From e560415fde967483573d42f628e52501768584e0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 10 Jul 2022 19:45:26 +0200 Subject: [PATCH 0001/1575] :rainbow: --- .gitignore | 4 + .vscode/settings.json | 15 + Cargo.toml | 22 + examples/rpi-pico-w/.cargo/config.toml | 8 + examples/rpi-pico-w/Cargo.toml | 59 ++ examples/rpi-pico-w/build.rs | 36 + examples/rpi-pico-w/memory.x | 5 + examples/rpi-pico-w/src/main.rs | 39 ++ rustfmt.toml | 3 + src/fmt.rs | 228 +++++++ src/lib.rs | 879 +++++++++++++++++++++++++ 11 files changed, 1298 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Cargo.toml create mode 100644 examples/rpi-pico-w/.cargo/config.toml create mode 100644 examples/rpi-pico-w/Cargo.toml create mode 100644 examples/rpi-pico-w/build.rs create mode 100644 examples/rpi-pico-w/memory.x create mode 100644 examples/rpi-pico-w/src/main.rs create mode 100644 rustfmt.toml create mode 100644 src/fmt.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ee8e6b4ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Cargo.lock +target/ +*.bin +notes.txt diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..cc926d04f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "editor.formatOnSave": true, + "rust-analyzer.cargo.buildScripts.enable": true, + "rust-analyzer.cargo.noDefaultFeatures": true, + "rust-analyzer.cargo.target": "thumbv6m-none-eabi", + "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.checkOnSave.noDefaultFeatures": true, + "rust-analyzer.imports.granularity.enforce": true, + "rust-analyzer.imports.granularity.group": "module", + "rust-analyzer.procMacro.attributes.enable": false, + "rust-analyzer.procMacro.enable": true, + "rust-analyzer.linkedProjects": [ + "examples/rpi-pico-w/Cargo.toml", + ], +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..d1672146c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "cyw43" +version = "0.1.0" +edition = "2021" + +[features] +defmt = ["dep:defmt", "embassy-rp/defmt", "embassy/defmt"] +log = ["dep:log"] +[dependencies] +embassy = { version = "0.1.0" } +embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac"] } +atomic-polyfill = "0.1.5" + +defmt = { version = "0.3", optional = true } +log = { version = "0.4.17", optional = true } + +cortex-m = "0.7.3" +cortex-m-rt = "0.7.0" +futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } + +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } +embedded-hal-async = { version = "0.1.0-alpha.1" } diff --git a/examples/rpi-pico-w/.cargo/config.toml b/examples/rpi-pico-w/.cargo/config.toml new file mode 100644 index 000000000..18bd4dfe8 --- /dev/null +++ b/examples/rpi-pico-w/.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/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml new file mode 100644 index 000000000..8dbcb20d4 --- /dev/null +++ b/examples/rpi-pico-w/Cargo.toml @@ -0,0 +1,59 @@ +[package] +name = "cyw43-example-rpi-pico-w" +version = "0.1.0" +edition = "2021" + + +[dependencies] +cyw43 = { path = "../../", features = ["defmt"]} +embassy = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +atomic-polyfill = "0.1.5" + +defmt = "0.3" +defmt-rtt = "0.3" +panic-probe = { version = "0.3", features = ["print-defmt"] } + +cortex-m = "0.7.3" +cortex-m-rt = "0.7.0" +futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } + +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } +embedded-hal-async = { version = "0.1.0-alpha.1" } + + +[patch.crates-io] +embassy = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } +#embassy = { path = "/home/dirbaio/embassy/embassy/embassy" } +#embassy-rp = { path = "/home/dirbaio/embassy/embassy/embassy-rp" } + +[profile.dev] +debug = 2 +debug-assertions = true +opt-level = 1 +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/examples/rpi-pico-w/build.rs b/examples/rpi-pico-w/build.rs new file mode 100644 index 000000000..3f915f931 --- /dev/null +++ b/examples/rpi-pico-w/build.rs @@ -0,0 +1,36 @@ +//! 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=-Tlink-rp.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/rpi-pico-w/memory.x b/examples/rpi-pico-w/memory.x new file mode 100644 index 000000000..eb8c1731d --- /dev/null +++ b/examples/rpi-pico-w/memory.x @@ -0,0 +1,5 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 1024K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} \ No newline at end of file diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs new file mode 100644 index 000000000..de8c5a2ba --- /dev/null +++ b/examples/rpi-pico-w/src/main.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait, concat_bytes)] + +use core::slice; + +use defmt::{assert, assert_eq, panic, *}; +use embassy::executor::Spawner; +use embassy_rp::gpio::{Flex, Level, Output, Pin}; +use embassy_rp::Peripherals; +use {defmt_rtt as _, panic_probe as _}; + + +macro_rules! forever { + ($val:expr) => {{ + type T = impl Sized; + static FOREVER: Forever = Forever::new(); + FOREVER.put_with(move || $val) + }}; +} + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello World!"); + + let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_25, p.PIN_29, p.PIN_24); + //let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_0, p.PIN_1, p.PIN_2); + + let mut driver = cyw43::Driver::new( + Output::new(pwr, Level::Low), + Output::new(cs, Level::High), + Output::new(clk, Level::Low), + Flex::new(dio), + ); + + driver.init().await; + + loop {} +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..3639f4386 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Module" +max_width=120 \ No newline at end of file diff --git a/src/fmt.rs b/src/fmt.rs new file mode 100644 index 000000000..f8bb0a035 --- /dev/null +++ b/src/fmt.rs @@ -0,0 +1,228 @@ +#![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); + } + } + } +} + +#[cfg(feature = "defmt-timestamp-uptime")] +defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } + +#[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/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..5caf19267 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,879 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait, concat_bytes)] + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +use core::slice; + +use embassy::time::{block_for, Duration, Timer}; +use embassy::util::yield_now; +use embassy_rp::gpio::{Flex, Output, Pin}; + +fn swap16(x: u32) -> u32 { + (x & 0xFF00FF00) >> 8 | (x & 0x00FF00FF) << 8 +} + +fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { + (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) +} + +const FUNC_BUS: u32 = 0; +const FUNC_BACKPLANE: u32 = 1; +const FUNC_WLAN: u32 = 2; +const FUNC_BT: u32 = 3; + +const REG_BUS_CTRL: u32 = 0x0; +const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status +const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask +const REG_BUS_STATUS: u32 = 0x8; +const REG_BUS_FEEDBEAD: u32 = 0x14; +const REG_BUS_TEST: u32 = 0x18; +const REG_BUS_RESP_DELAY: u32 = 0x1c; + +// SPI_STATUS_REGISTER bits +const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; +const STATUS_UNDERFLOW: u32 = 0x00000002; +const STATUS_OVERFLOW: u32 = 0x00000004; +const STATUS_F2_INTR: u32 = 0x00000008; +const STATUS_F3_INTR: u32 = 0x00000010; +const STATUS_F2_RX_READY: u32 = 0x00000020; +const STATUS_F3_RX_READY: u32 = 0x00000040; +const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080; +const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100; +const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00; +const STATUS_F2_PKT_LEN_SHIFT: u32 = 9; +const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000; +const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000; +const STATUS_F3_PKT_LEN_SHIFT: u32 = 21; + +const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005; +const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006; +const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007; +const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008; +const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009; +const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A; +const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B; +const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C; +const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D; +const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E; +const REG_BACKPLANE_PULL_UP: u32 = 0x1000F; +const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B; +const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C; +const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E; +const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F; + +const BACKPLANE_WINDOW_SIZE: usize = 0x8000; +const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; +const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; +const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; + +const AI_IOCTRL_OFFSET: u32 = 0x408; +const AI_IOCTRL_BIT_FGC: u8 = 0x0002; +const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; +const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020; + +const AI_RESETCTRL_OFFSET: u32 = 0x800; +const AI_RESETCTRL_BIT_RESET: u8 = 1; + +const AI_RESETSTATUS_OFFSET: u32 = 0x804; + +const TEST_PATTERN: u32 = 0x12345678; +const FEEDBEAD: u32 = 0xFEEDBEAD; + +// SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits +const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1" +const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002; +const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004; +const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1 +const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1 +const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020; +const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040; +const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests +const IRQ_MISC_INTR0: u16 = 0x0100; +const IRQ_MISC_INTR1: u16 = 0x0200; +const IRQ_MISC_INTR2: u16 = 0x0400; +const IRQ_MISC_INTR3: u16 = 0x0800; +const IRQ_MISC_INTR4: u16 = 0x1000; +const IRQ_F1_INTR: u16 = 0x2000; +const IRQ_F2_INTR: u16 = 0x4000; +const IRQ_F3_INTR: u16 = 0x8000; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Core { + WLAN = 0, + SOCSRAM = 1, + SDIOD = 2, +} + +impl Core { + fn base_addr(&self) -> u32 { + match self { + Self::WLAN => CHIP.arm_core_base_address, + Self::SOCSRAM => CHIP.socsram_wrapper_base_address, + Self::SDIOD => CHIP.sdiod_core_base_address, + } + } +} + +struct Chip { + arm_core_base_address: u32, + socsram_base_address: u32, + socsram_wrapper_base_address: u32, + sdiod_core_base_address: u32, + pmu_base_address: u32, + chip_ram_size: u32, + atcm_ram_base_address: u32, + socram_srmem_size: u32, + chanspec_band_mask: u32, + chanspec_band_2g: u32, + chanspec_band_5g: u32, + chanspec_band_shift: u32, + chanspec_bw_10: u32, + chanspec_bw_20: u32, + chanspec_bw_40: u32, + chanspec_bw_mask: u32, + chanspec_bw_shift: u32, + chanspec_ctl_sb_lower: u32, + chanspec_ctl_sb_upper: u32, + chanspec_ctl_sb_none: u32, + chanspec_ctl_sb_mask: u32, +} + +const WRAPPER_REGISTER_OFFSET: u32 = 0x100000; + +// Data for CYW43439 +const CHIP: Chip = Chip { + arm_core_base_address: 0x18003000 + WRAPPER_REGISTER_OFFSET, + socsram_base_address: 0x18004000, + socsram_wrapper_base_address: 0x18004000 + WRAPPER_REGISTER_OFFSET, + sdiod_core_base_address: 0x18002000, + pmu_base_address: 0x18000000, + chip_ram_size: 512 * 1024, + atcm_ram_base_address: 0, + socram_srmem_size: 64 * 1024, + chanspec_band_mask: 0xc000, + chanspec_band_2g: 0x0000, + chanspec_band_5g: 0xc000, + chanspec_band_shift: 14, + chanspec_bw_10: 0x0800, + chanspec_bw_20: 0x1000, + chanspec_bw_40: 0x1800, + chanspec_bw_mask: 0x3800, + chanspec_bw_shift: 11, + chanspec_ctl_sb_lower: 0x0000, + chanspec_ctl_sb_upper: 0x0100, + chanspec_ctl_sb_none: 0x0000, + chanspec_ctl_sb_mask: 0x0700, +}; + +#[derive(Clone, Copy)] +#[repr(C)] +struct SdpcmHeader { + len: u16, + len_inv: u16, + /// Rx/Tx sequence number + sequence: u8, + /// 4 MSB Channel number, 4 LSB arbitrary flag + channel_and_flags: u8, + /// Length of next data frame, reserved for Tx + next_length: u8, + /// Data offset + header_length: u8, + /// Flow control bits, reserved for Tx + wireless_flow_control: u8, + /// Maximum Sequence number allowed by firmware for Tx + bus_data_credit: u8, + /// Reserved + reserved: [u8; 2], +} + +#[derive(Clone, Copy)] +#[repr(C)] +struct CdcHeader { + cmd: u32, + out_len: u16, + in_len: u16, + flags: u16, + id: u16, + status: u32, +} + +#[derive(Clone, Copy)] +#[repr(C)] +struct BdcHeader { + flags: u8, + /// 802.1d Priority (low 3 bits) + priority: u8, + flags2: u8, + /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. + data_offset: u8, +} + +macro_rules! impl_bytes { + ($t:ident) => { + impl $t { + const SIZE: usize = core::mem::size_of::(); + + pub fn to_bytes(&self) -> [u8; Self::SIZE] { + unsafe { core::mem::transmute(*self) } + } + + pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { + unsafe { core::mem::transmute(*bytes) } + } + } + }; +} +impl_bytes!(SdpcmHeader); +impl_bytes!(CdcHeader); +impl_bytes!(BdcHeader); + +pub struct Driver<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { + pwr: Output<'a, PWR>, + + /// SPI chip-select. + cs: Output<'a, CS>, + + /// SPI clock + clk: Output<'a, CLK>, + + /// 4 signals, all in one!! + /// - SPI MISO + /// - SPI MOSI + /// - IRQ + /// - strap to set to gSPI mode on boot. + dio: Flex<'a, DIO>, + + backplane_window: u32, +} + +impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { + pub fn new(pwr: Output<'a, PWR>, cs: Output<'a, CS>, clk: Output<'a, CLK>, dio: Flex<'a, DIO>) -> Self { + Self { + pwr, + cs, + clk, + dio, + backplane_window: 0xAAAA_AAAA, + } + } + + pub async fn init(&mut self) { + // Set strap to select gSPI mode. + self.dio.set_as_output(); + self.dio.set_low(); + + // Reset + self.pwr.set_low(); + Timer::after(Duration::from_millis(20)).await; + self.pwr.set_high(); + Timer::after(Duration::from_millis(250)).await; + + info!("waiting for ping..."); + while self.read32_swapped(REG_BUS_FEEDBEAD) != FEEDBEAD {} + info!("ping ok"); + + self.write32_swapped(0x18, TEST_PATTERN); + let val = self.read32_swapped(REG_BUS_TEST); + assert_eq!(val, TEST_PATTERN); + + // 32bit, big endian. + self.write32_swapped(REG_BUS_CTRL, 0x00010033); + + let val = self.read32(FUNC_BUS, REG_BUS_FEEDBEAD); + assert_eq!(val, FEEDBEAD); + let val = self.read32(FUNC_BUS, REG_BUS_TEST); + assert_eq!(val, TEST_PATTERN); + + // No response delay in any of the funcs. + // seems to break backplane??? eat the 4-byte delay instead, that's what the vendor drivers do... + //self.write32(FUNC_BUS, REG_BUS_RESP_DELAY, 0); + + // Init ALP (no idea what that stands for) clock + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x08); + info!("waiting for clock..."); + while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x40 == 0 {} + info!("clock ok"); + + let chip_id = self.bp_read16(0x1800_0000); + info!("chip ID: {}", chip_id); + + // Upload firmware. + self.core_disable(Core::WLAN); + self.core_reset(Core::SOCSRAM); + self.bp_write32(CHIP.socsram_base_address + 0x10, 3); + self.bp_write32(CHIP.socsram_base_address + 0x44, 0); + + // I'm flashing the firmwares independently at hardcoded addresses, instead of baking them + // into the program with `include_bytes!` or similar, so that flashing the program stays fast. + // + // Flash them like this, also don't forget to update the lengths below if you change them!. + // + // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 + let fw = unsafe { slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + + let ram_addr = CHIP.atcm_ram_base_address; + + info!("loading fw"); + self.bp_write(ram_addr, fw); + + info!("verifying fw"); + let mut buf = [0; 1024]; + for (i, chunk) in fw.chunks(1024).enumerate() { + let buf = &mut buf[..chunk.len()]; + self.bp_read(ram_addr + i as u32 * 1024, buf); + assert_eq!(chunk, buf); + } + + info!("loading nvram"); + // Round up to 4 bytes. + let nvram_len = (NVRAM.len() + 3) / 4 * 4; + self.bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM); + + let nvram_len_words = nvram_len as u32 / 4; + let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; + self.bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic); + + // Start core! + info!("starting up core..."); + self.core_reset(Core::WLAN); + assert!(self.core_is_up(Core::WLAN)); + + while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x80 == 0 {} + + // "Set up the interrupt mask and enable interrupts" + self.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0); + + // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." + // Sounds scary... + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32); + + // wait for wifi startup + info!("waiting for wifi init..."); + while self.read32(FUNC_BUS, REG_BUS_STATUS) & STATUS_F2_RX_READY == 0 {} + + // Some random configs related to sleep. + // I think they're not needed if we don't want sleep...??? + /* + let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL); + val |= 0x02; // WAKE_TILL_HT_AVAIL + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val); + self.write8(FUNC_BUS, 0xF0, 0x08); // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02); // SBSDIO_FORCE_HT + + let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR); + val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val); + + // clear pulls + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0); + let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP); + */ + + let mut buf = [0; 8 + 12 + 1024]; + buf[0..8].copy_from_slice(b"clmload\x00"); + buf[8..20].copy_from_slice(b"\x02\x10\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00"); + buf[20..].copy_from_slice(&clm[..1024]); + self.send_ioctl(2, 263, 0, &buf); + + info!("init done "); + + let mut old_irq = 0; + let mut buf = [0; 2048]; + loop { + let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT); + if irq != old_irq { + info!("irq: {:04x}", irq); + } + old_irq = irq; + + if irq & IRQ_F2_PACKET_AVAILABLE != 0 { + let mut status = 0xFFFF_FFFF; + while status == 0xFFFF_FFFF { + status = self.read32(FUNC_BUS, REG_BUS_STATUS); + } + + if status & STATUS_F2_PKT_AVAILABLE != 0 { + let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; + info!("got len {}", len); + + let cmd = cmd_word(false, true, FUNC_WLAN, 0, len); + + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + self.spi_read(&mut buf[..len as usize]); + // pad to 32bit + let mut junk = [0; 4]; + if len % 4 != 0 { + self.spi_read(&mut junk[..(4 - len as usize % 4)]); + } + self.cs.set_high(); + + info!("rxd packet {:02x}", &buf[..len as usize]); + + self.rx(&buf[..len as usize]); + } + } + + yield_now().await; + } + } + + fn rx(&mut self, packet: &[u8]) { + if packet.len() < SdpcmHeader::SIZE { + warn!("packet too short, len={}", packet.len()); + return; + } + + let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); + + if sdpcm_header.len != !sdpcm_header.len_inv { + warn!("len inv mismatch"); + return; + } + if sdpcm_header.len as usize != packet.len() { + // TODO: is this guaranteed?? + warn!("len from header doesn't match len from spi"); + return; + } + + let channel = sdpcm_header.channel_and_flags & 0x0f; + + match channel { + 0 => { + if packet.len() < SdpcmHeader::SIZE + CdcHeader::SIZE { + warn!("control packet too short, len={}", packet.len()); + return; + } + + let cdc_header = + CdcHeader::from_bytes(packet[SdpcmHeader::SIZE..][..CdcHeader::SIZE].try_into().unwrap()); + + // TODO check cdc_header.id matches + // TODO check status + } + _ => {} + } + } + + fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) { + let mut buf = [0; 2048]; + + let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, + len_inv: !total_len as u16, + sequence: 0x02, // todo + channel_and_flags: 0, // control channle + next_length: 0, + header_length: SdpcmHeader::SIZE as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let cdc_header = CdcHeader { + cmd: cmd, + out_len: data.len() as _, + in_len: 0, + flags: kind as u16 | (iface as u16) << 12, + id: 1, // todo + status: 0, + }; + + buf[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); + buf[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); + + info!("txing {:02x}", &buf[..total_len]); + + let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + self.spi_write(&buf[..total_len]); + self.cs.set_high(); + } + + fn core_disable(&mut self, core: Core) { + let base = core.base_addr(); + + // Dummy read? + let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET); + + // Check it isn't already reset + let r = self.bp_read8(base + AI_RESETCTRL_OFFSET); + if r & AI_RESETCTRL_BIT_RESET != 0 { + return; + } + + self.bp_write8(base + AI_IOCTRL_OFFSET, 0); + let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); + + block_for(Duration::from_millis(1)); + + self.bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET); + let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET); + } + + fn core_reset(&mut self, core: Core) { + self.core_disable(core); + + let base = core.base_addr(); + self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN); + let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); + + self.bp_write8(base + AI_RESETCTRL_OFFSET, 0); + + block_for(Duration::from_millis(1)); + + self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN); + let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); + + block_for(Duration::from_millis(1)); + } + + fn core_is_up(&mut self, core: Core) -> bool { + let base = core.base_addr(); + + let io = self.bp_read8(base + AI_IOCTRL_OFFSET); + if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { + debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); + return false; + } + + let r = self.bp_read8(base + AI_RESETCTRL_OFFSET); + if r & (AI_RESETCTRL_BIT_RESET) != 0 { + debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); + return false; + } + + true + } + + fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { + // It seems the HW force-aligns the addr + // to 2 if data.len() >= 2 + // to 4 if data.len() >= 4 + // To simplify, enforce 4-align for now. + assert!(addr % 4 == 0); + + while !data.is_empty() { + // Ensure transfer doesn't cross a window boundary. + let window_offs = addr & BACKPLANE_ADDRESS_MASK; + let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; + + let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); + + self.backplane_set_window(addr); + + let cmd = cmd_word(false, true, FUNC_BACKPLANE, window_offs, len as u32); + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + + // 4-byte response delay. + let mut junk = [0; 4]; + self.spi_read(&mut junk); + + // Read data + self.spi_read(&mut data[..len]); + + // pad to 32bit + if len % 4 != 0 { + self.spi_read(&mut junk[..(4 - len % 4)]); + } + self.cs.set_high(); + + // Advance ptr. + addr += len as u32; + data = &mut data[len..]; + } + } + + fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { + // It seems the HW force-aligns the addr + // to 2 if data.len() >= 2 + // to 4 if data.len() >= 4 + // To simplify, enforce 4-align for now. + assert!(addr % 4 == 0); + + while !data.is_empty() { + // Ensure transfer doesn't cross a window boundary. + let window_offs = addr & BACKPLANE_ADDRESS_MASK; + let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; + + let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); + + self.backplane_set_window(addr); + + let cmd = cmd_word(true, true, FUNC_BACKPLANE, window_offs, len as u32); + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + self.spi_write(&data[..len]); + // pad to 32bit + if len % 4 != 0 { + let zeros = [0; 4]; + self.spi_write(&zeros[..(4 - len % 4)]); + } + self.cs.set_high(); + + // Advance ptr. + addr += len as u32; + data = &data[len..]; + } + } + + fn bp_read8(&mut self, addr: u32) -> u8 { + self.backplane_readn(addr, 1) as u8 + } + + fn bp_write8(&mut self, addr: u32, val: u8) { + self.backplane_writen(addr, val as u32, 1) + } + + fn bp_read16(&mut self, addr: u32) -> u16 { + self.backplane_readn(addr, 2) as u16 + } + + fn bp_write16(&mut self, addr: u32, val: u16) { + self.backplane_writen(addr, val as u32, 2) + } + + fn bp_read32(&mut self, addr: u32) -> u32 { + self.backplane_readn(addr, 4) + } + + fn bp_write32(&mut self, addr: u32, val: u32) { + self.backplane_writen(addr, val, 4) + } + + fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 { + self.backplane_set_window(addr); + + let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; + if len == 4 { + bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG + } + self.readn(FUNC_BACKPLANE, bus_addr, len) + } + + fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) { + self.backplane_set_window(addr); + + let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; + if len == 4 { + bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG + } + self.writen(FUNC_BACKPLANE, bus_addr, val, len) + } + + fn backplane_set_window(&mut self, addr: u32) { + let new_window = addr & !BACKPLANE_ADDRESS_MASK; + + if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 { + self.write8( + FUNC_BACKPLANE, + REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH, + (new_window >> 24) as u8, + ); + } + if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 { + self.write8( + FUNC_BACKPLANE, + REG_BACKPLANE_BACKPLANE_ADDRESS_MID, + (new_window >> 16) as u8, + ); + } + if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 { + self.write8( + FUNC_BACKPLANE, + REG_BACKPLANE_BACKPLANE_ADDRESS_LOW, + (new_window >> 8) as u8, + ); + } + self.backplane_window = new_window; + } + + fn read8(&mut self, func: u32, addr: u32) -> u8 { + self.readn(func, addr, 1) as u8 + } + + fn write8(&mut self, func: u32, addr: u32, val: u8) { + self.writen(func, addr, val as u32, 1) + } + + fn read16(&mut self, func: u32, addr: u32) -> u16 { + self.readn(func, addr, 2) as u16 + } + + fn write16(&mut self, func: u32, addr: u32, val: u16) { + self.writen(func, addr, val as u32, 2) + } + + fn read32(&mut self, func: u32, addr: u32) -> u32 { + self.readn(func, addr, 4) + } + + fn write32(&mut self, func: u32, addr: u32, val: u32) { + self.writen(func, addr, val, 4) + } + + fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { + let cmd = cmd_word(false, true, func, addr, len); + let mut buf = [0; 4]; + + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + if func == FUNC_BACKPLANE { + // 4-byte response delay. + self.spi_read(&mut buf); + } + self.spi_read(&mut buf); + self.cs.set_high(); + + u32::from_le_bytes(buf) + } + + fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { + let cmd = cmd_word(true, true, func, addr, len); + + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + self.spi_write(&val.to_le_bytes()); + self.cs.set_high(); + } + + fn read32_swapped(&mut self, addr: u32) -> u32 { + let cmd = cmd_word(false, true, FUNC_BUS, addr, 4); + let mut buf = [0; 4]; + + self.cs.set_low(); + self.spi_write(&swap16(cmd).to_le_bytes()); + self.spi_read(&mut buf); + self.cs.set_high(); + + swap16(u32::from_le_bytes(buf)) + } + + fn write32_swapped(&mut self, addr: u32, val: u32) { + let cmd = cmd_word(true, true, FUNC_BUS, addr, 4); + + self.cs.set_low(); + self.spi_write(&swap16(cmd).to_le_bytes()); + self.spi_write(&swap16(val).to_le_bytes()); + self.cs.set_high(); + } + + fn spi_read(&mut self, words: &mut [u8]) { + self.dio.set_as_input(); + for word in words { + let mut w = 0; + for _ in 0..8 { + w = w << 1; + + // rising edge, sample data + if self.dio.is_high() { + w |= 0x01; + } + self.clk.set_high(); + delay(); + + // falling edge + self.clk.set_low(); + delay(); + } + *word = w + } + self.clk.set_low(); + delay(); + } + + fn spi_write(&mut self, words: &[u8]) { + self.dio.set_as_output(); + for word in words { + let mut word = *word; + for _ in 0..8 { + // falling edge, setup data + self.clk.set_low(); + if word & 0x80 == 0 { + self.dio.set_low(); + } else { + self.dio.set_high(); + } + delay(); + + // rising edge + self.clk.set_high(); + delay(); + + word = word << 1; + } + } + self.clk.set_low(); + delay(); + self.dio.set_as_input(); + } +} + +fn delay() { + //cortex_m::asm::delay(5); +} + +macro_rules! nvram { + ($($s:literal,)*) => { + concat_bytes!($($s, b"\x00",)* b"\x00\x00") + }; +} + +static NVRAM: &'static [u8] = &*nvram!( + b"NVRAMRev=$Rev$", + b"manfid=0x2d0", + b"prodid=0x0727", + b"vendid=0x14e4", + b"devid=0x43e2", + b"boardtype=0x0887", + b"boardrev=0x1100", + b"boardnum=22", + b"macaddr=00:A0:50:86:aa:b6", + b"sromrev=11", + b"boardflags=0x00404001", + b"boardflags3=0x04000000", + b"xtalfreq=26000", + b"nocrc=1", + b"ag0=255", + b"aa2g=1", + b"ccode=ALL", + b"pa0itssit=0x20", + b"extpagain2g=0", + b"pa2ga0=-168,7161,-820", + b"AvVmid_c0=0x0,0xc8", + b"cckpwroffset0=5", + b"maxp2ga0=84", + b"txpwrbckof=6", + b"cckbw202gpo=0", + b"legofdmbw202gpo=0x66111111", + b"mcsbw202gpo=0x77711111", + b"propbw202gpo=0xdd", + b"ofdmdigfilttype=18", + b"ofdmdigfilttypebe=18", + b"papdmode=1", + b"papdvalidtest=1", + b"pacalidx2g=45", + b"papdepsoffset=-30", + b"papdendidx=58", + b"ltecxmux=0", + b"ltecxpadnum=0x0102", + b"ltecxfnsel=0x44", + b"ltecxgcigpio=0x01", + b"il0macaddr=00:90:4c:c5:12:38", + b"wl0id=0x431b", + b"deadman_to=0xffffffff", + b"muxenab=0x1", + b"spurconfig=0x3", + b"glitch_based_crsmin=1", + b"btc_mode=1", +); From 069a57fcf85c725d000e03163716edb4ae3922ca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Jul 2022 00:25:35 +0200 Subject: [PATCH 0002/1575] async ioctls working. --- examples/rpi-pico-w/src/main.rs | 21 +++- rust-toolchain.toml | 8 ++ src/lib.rs | 200 +++++++++++++++++--------------- src/structs.rs | 71 ++++++++++++ 4 files changed, 202 insertions(+), 98 deletions(-) create mode 100644 rust-toolchain.toml create mode 100644 src/structs.rs diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index de8c5a2ba..7545dfde8 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -6,11 +6,12 @@ use core::slice; use defmt::{assert, assert_eq, panic, *}; use embassy::executor::Spawner; +use embassy::util::Forever; use embassy_rp::gpio::{Flex, Level, Output, Pin}; +use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embassy_rp::Peripherals; use {defmt_rtt as _, panic_probe as _}; - macro_rules! forever { ($val:expr) => {{ type T = impl Sized; @@ -19,21 +20,29 @@ macro_rules! forever { }}; } +#[embassy::task] +async fn wifi_task(runner: cyw43::Runner<'static, PIN_23, PIN_25, PIN_29, PIN_24>) -> ! { + runner.run().await +} + #[embassy::main] -async fn main(_spawner: Spawner, p: Peripherals) { +async fn main(spawner: Spawner, p: Peripherals) { info!("Hello World!"); let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_25, p.PIN_29, p.PIN_24); //let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_0, p.PIN_1, p.PIN_2); - let mut driver = cyw43::Driver::new( + let state = forever!(cyw43::State::new()); + let (mut control, runner) = cyw43::new( + state, Output::new(pwr, Level::Low), Output::new(cs, Level::High), Output::new(clk, Level::Low), Flex::new(dio), - ); + ) + .await; - driver.init().await; + spawner.spawn(wifi_task(runner)).unwrap(); - loop {} + control.init().await; } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..03da4cf73 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,8 @@ +# Before upgrading check that everything is available on all tier1 targets here: +# https://rust-lang.github.io/rustup-components-history +[toolchain] +channel = "nightly-2022-07-09" +components = [ "rust-src", "rustfmt" ] +targets = [ + "thumbv6m-none-eabi", +] diff --git a/src/lib.rs b/src/lib.rs index 5caf19267..3609ecaeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,20 @@ #![no_std] #![no_main] -#![feature(type_alias_impl_trait, concat_bytes)] - +#![feature(type_alias_impl_trait, concat_bytes, const_slice_from_raw_parts)] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod structs; + +use core::cell::Cell; use core::slice; use embassy::time::{block_for, Duration, Timer}; use embassy::util::yield_now; use embassy_rp::gpio::{Flex, Output, Pin}; +use self::structs::*; + fn swap16(x: u32) -> u32 { (x & 0xFF00FF00) >> 8 | (x & 0x00FF00FF) << 8 } @@ -169,68 +173,73 @@ const CHIP: Chip = Chip { }; #[derive(Clone, Copy)] -#[repr(C)] -struct SdpcmHeader { - len: u16, - len_inv: u16, - /// Rx/Tx sequence number - sequence: u8, - /// 4 MSB Channel number, 4 LSB arbitrary flag - channel_and_flags: u8, - /// Length of next data frame, reserved for Tx - next_length: u8, - /// Data offset - header_length: u8, - /// Flow control bits, reserved for Tx - wireless_flow_control: u8, - /// Maximum Sequence number allowed by firmware for Tx - bus_data_credit: u8, - /// Reserved - reserved: [u8; 2], +enum IoctlState { + Idle, + + Pending { + kind: u32, + cmd: u32, + iface: u32, + buf: *const [u8], + }, + Sent, + Done, } -#[derive(Clone, Copy)] -#[repr(C)] -struct CdcHeader { - cmd: u32, - out_len: u16, - in_len: u16, - flags: u16, - id: u16, - status: u32, +pub struct State { + ioctl_id: Cell, + ioctl_state: Cell, } -#[derive(Clone, Copy)] -#[repr(C)] -struct BdcHeader { - flags: u8, - /// 802.1d Priority (low 3 bits) - priority: u8, - flags2: u8, - /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. - data_offset: u8, -} - -macro_rules! impl_bytes { - ($t:ident) => { - impl $t { - const SIZE: usize = core::mem::size_of::(); - - pub fn to_bytes(&self) -> [u8; Self::SIZE] { - unsafe { core::mem::transmute(*self) } - } - - pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { - unsafe { core::mem::transmute(*bytes) } - } +impl State { + pub fn new() -> Self { + Self { + ioctl_id: Cell::new(0), + ioctl_state: Cell::new(IoctlState::Idle), } - }; + } } -impl_bytes!(SdpcmHeader); -impl_bytes!(CdcHeader); -impl_bytes!(BdcHeader); -pub struct Driver<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { +pub struct Control<'a> { + state: &'a State, +} + +impl<'a> Control<'a> { + pub async fn init(&mut self) { + let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + + let mut buf = [0; 8 + 12 + 1024]; + buf[0..8].copy_from_slice(b"clmload\x00"); + buf[8..20].copy_from_slice(b"\x02\x10\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00"); + buf[20..].copy_from_slice(&clm[..1024]); + self.ioctl(2, 263, 0, &buf).await; + info!("IOCTL done"); + } + + async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &[u8]) { + // TODO cancel ioctl on future drop. + + while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) { + yield_now().await; + } + + self.state.ioctl_id.set(self.state.ioctl_id.get().wrapping_add(1)); + + self.state + .ioctl_state + .set(IoctlState::Pending { kind, cmd, iface, buf }); + + while !matches!(self.state.ioctl_state.get(), IoctlState::Done) { + yield_now().await; + } + + self.state.ioctl_state.set(IoctlState::Idle); + } +} + +pub struct Runner<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { + state: &'a State, + pwr: Output<'a, PWR>, /// SPI chip-select. @@ -249,18 +258,29 @@ pub struct Driver<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { backplane_window: u32, } -impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { - pub fn new(pwr: Output<'a, PWR>, cs: Output<'a, CS>, clk: Output<'a, CLK>, dio: Flex<'a, DIO>) -> Self { - Self { - pwr, - cs, - clk, - dio, - backplane_window: 0xAAAA_AAAA, - } - } +pub async fn new<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin>( + state: &'a State, + pwr: Output<'a, PWR>, + cs: Output<'a, CS>, + clk: Output<'a, CLK>, + dio: Flex<'a, DIO>, +) -> (Control<'a>, Runner<'a, PWR, CS, CLK, DIO>) { + let mut runner = Runner { + state, + pwr, + cs, + clk, + dio, + backplane_window: 0xAAAA_AAAA, + }; - pub async fn init(&mut self) { + runner.init().await; + + (Control { state }, runner) +} + +impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { + async fn init(&mut self) { // Set strap to select gSPI mode. self.dio.set_as_output(); self.dio.set_low(); @@ -306,6 +326,8 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { self.bp_write32(CHIP.socsram_base_address + 0x10, 3); self.bp_write32(CHIP.socsram_base_address + 0x44, 0); + let ram_addr = CHIP.atcm_ram_base_address; + // I'm flashing the firmwares independently at hardcoded addresses, instead of baking them // into the program with `include_bytes!` or similar, so that flashing the program stays fast. // @@ -314,9 +336,6 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 let fw = unsafe { slice::from_raw_parts(0x10100000 as *const u8, 224190) }; - let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; - - let ram_addr = CHIP.atcm_ram_base_address; info!("loading fw"); self.bp_write(ram_addr, fw); @@ -374,17 +393,20 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP); */ - let mut buf = [0; 8 + 12 + 1024]; - buf[0..8].copy_from_slice(b"clmload\x00"); - buf[8..20].copy_from_slice(b"\x02\x10\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00"); - buf[20..].copy_from_slice(&clm[..1024]); - self.send_ioctl(2, 263, 0, &buf); - info!("init done "); + } + pub async fn run(mut self) -> ! { let mut old_irq = 0; let mut buf = [0; 2048]; loop { + // Send stuff + if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { + self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get()); + self.state.ioctl_state.set(IoctlState::Sent); + } + + // Receive stuff let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT); if irq != old_irq { info!("irq: {:04x}", irq); @@ -419,6 +441,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { } } + // TODO use IRQs yield_now().await; } } @@ -453,14 +476,16 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { let cdc_header = CdcHeader::from_bytes(packet[SdpcmHeader::SIZE..][..CdcHeader::SIZE].try_into().unwrap()); - // TODO check cdc_header.id matches - // TODO check status + if cdc_header.id == self.state.ioctl_id.get() { + assert_eq!(cdc_header.status, 0); // todo propagate error + self.state.ioctl_state.set(IoctlState::Done); + } } _ => {} } } - fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) { + fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8], id: u16) { let mut buf = [0; 2048]; let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); @@ -469,7 +494,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { len: total_len as u16, len_inv: !total_len as u16, sequence: 0x02, // todo - channel_and_flags: 0, // control channle + channel_and_flags: 0, // control channel next_length: 0, header_length: SdpcmHeader::SIZE as _, wireless_flow_control: 0, @@ -482,7 +507,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { out_len: data.len() as _, in_len: 0, flags: kind as u16 | (iface as u16) << 12, - id: 1, // todo + id, status: 0, }; @@ -780,16 +805,13 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { w |= 0x01; } self.clk.set_high(); - delay(); // falling edge self.clk.set_low(); - delay(); } *word = w } self.clk.set_low(); - delay(); } fn spi_write(&mut self, words: &[u8]) { @@ -804,25 +826,19 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Driver<'a, PWR, CS, CLK, DIO> { } else { self.dio.set_high(); } - delay(); // rising edge self.clk.set_high(); - delay(); word = word << 1; } } self.clk.set_low(); - delay(); + self.dio.set_as_input(); } } -fn delay() { - //cortex_m::asm::delay(5); -} - macro_rules! nvram { ($($s:literal,)*) => { concat_bytes!($($s, b"\x00",)* b"\x00\x00") diff --git a/src/structs.rs b/src/structs.rs new file mode 100644 index 000000000..fe5e89a37 --- /dev/null +++ b/src/structs.rs @@ -0,0 +1,71 @@ +#[derive(Clone, Copy)] +#[repr(C)] +pub struct SdpcmHeader { + pub len: u16, + pub len_inv: u16, + /// Rx/Tx sequence number + pub sequence: u8, + /// 4 MSB Channel number, 4 LSB arbitrary flag + pub channel_and_flags: u8, + /// Length of next data frame, reserved for Tx + pub next_length: u8, + /// Data offset + pub header_length: u8, + /// Flow control bits, reserved for Tx + pub wireless_flow_control: u8, + /// Maximum Sequence number allowed by firmware for Tx + pub bus_data_credit: u8, + /// Reserved + pub reserved: [u8; 2], +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct CdcHeader { + pub cmd: u32, + pub out_len: u16, + pub in_len: u16, + pub flags: u16, + pub id: u16, + pub status: u32, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct BdcHeader { + pub flags: u8, + /// 802.1d Priority (low 3 bits) + pub priority: u8, + pub flags2: u8, + /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. + pub data_offset: u8, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct DownloadHeader { + pub flag: u16, + pub dload_type: u16, + pub len: u32, + pub crc: u32, +} + +macro_rules! impl_bytes { + ($t:ident) => { + impl $t { + pub const SIZE: usize = core::mem::size_of::(); + + pub fn to_bytes(&self) -> [u8; Self::SIZE] { + unsafe { core::mem::transmute(*self) } + } + + pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { + unsafe { core::mem::transmute(*bytes) } + } + } + }; +} +impl_bytes!(SdpcmHeader); +impl_bytes!(CdcHeader); +impl_bytes!(BdcHeader); +impl_bytes!(DownloadHeader); From 7ddcacac7bbfaed303dcda7d14ab29cad94fd570 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Jul 2022 03:07:39 +0200 Subject: [PATCH 0003/1575] clm download, country config. --- src/countries.rs | 481 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 98 ++++++++-- src/structs.rs | 56 ++++-- 3 files changed, 596 insertions(+), 39 deletions(-) create mode 100644 src/countries.rs diff --git a/src/countries.rs b/src/countries.rs new file mode 100644 index 000000000..fa1e8cace --- /dev/null +++ b/src/countries.rs @@ -0,0 +1,481 @@ +#![allow(unused)] + +pub struct Country { + pub code: [u8; 2], + pub rev: u16, +} + +/// AF Afghanistan +pub const AFGHANISTAN: Country = Country { code: *b"AF", rev: 0 }; +/// AL Albania +pub const ALBANIA: Country = Country { code: *b"AL", rev: 0 }; +/// DZ Algeria +pub const ALGERIA: Country = Country { code: *b"DZ", rev: 0 }; +/// AS American_Samoa +pub const AMERICAN_SAMOA: Country = Country { code: *b"AS", rev: 0 }; +/// AO Angola +pub const ANGOLA: Country = Country { code: *b"AO", rev: 0 }; +/// AI Anguilla +pub const ANGUILLA: Country = Country { code: *b"AI", rev: 0 }; +/// AG Antigua_and_Barbuda +pub const ANTIGUA_AND_BARBUDA: Country = Country { code: *b"AG", rev: 0 }; +/// AR Argentina +pub const ARGENTINA: Country = Country { code: *b"AR", rev: 0 }; +/// AM Armenia +pub const ARMENIA: Country = Country { code: *b"AM", rev: 0 }; +/// AW Aruba +pub const ARUBA: Country = Country { code: *b"AW", rev: 0 }; +/// AU Australia +pub const AUSTRALIA: Country = Country { code: *b"AU", rev: 0 }; +/// AT Austria +pub const AUSTRIA: Country = Country { code: *b"AT", rev: 0 }; +/// AZ Azerbaijan +pub const AZERBAIJAN: Country = Country { code: *b"AZ", rev: 0 }; +/// BS Bahamas +pub const BAHAMAS: Country = Country { code: *b"BS", rev: 0 }; +/// BH Bahrain +pub const BAHRAIN: Country = Country { code: *b"BH", rev: 0 }; +/// 0B Baker_Island +pub const BAKER_ISLAND: Country = Country { code: *b"0B", rev: 0 }; +/// BD Bangladesh +pub const BANGLADESH: Country = Country { code: *b"BD", rev: 0 }; +/// BB Barbados +pub const BARBADOS: Country = Country { code: *b"BB", rev: 0 }; +/// BY Belarus +pub const BELARUS: Country = Country { code: *b"BY", rev: 0 }; +/// BE Belgium +pub const BELGIUM: Country = Country { code: *b"BE", rev: 0 }; +/// BZ Belize +pub const BELIZE: Country = Country { code: *b"BZ", rev: 0 }; +/// BJ Benin +pub const BENIN: Country = Country { code: *b"BJ", rev: 0 }; +/// BM Bermuda +pub const BERMUDA: Country = Country { code: *b"BM", rev: 0 }; +/// BT Bhutan +pub const BHUTAN: Country = Country { code: *b"BT", rev: 0 }; +/// BO Bolivia +pub const BOLIVIA: Country = Country { code: *b"BO", rev: 0 }; +/// BA Bosnia_and_Herzegovina +pub const BOSNIA_AND_HERZEGOVINA: Country = Country { code: *b"BA", rev: 0 }; +/// BW Botswana +pub const BOTSWANA: Country = Country { code: *b"BW", rev: 0 }; +/// BR Brazil +pub const BRAZIL: Country = Country { code: *b"BR", rev: 0 }; +/// IO British_Indian_Ocean_Territory +pub const BRITISH_INDIAN_OCEAN_TERRITORY: Country = Country { code: *b"IO", rev: 0 }; +/// BN Brunei_Darussalam +pub const BRUNEI_DARUSSALAM: Country = Country { code: *b"BN", rev: 0 }; +/// BG Bulgaria +pub const BULGARIA: Country = Country { code: *b"BG", rev: 0 }; +/// BF Burkina_Faso +pub const BURKINA_FASO: Country = Country { code: *b"BF", rev: 0 }; +/// BI Burundi +pub const BURUNDI: Country = Country { code: *b"BI", rev: 0 }; +/// KH Cambodia +pub const CAMBODIA: Country = Country { code: *b"KH", rev: 0 }; +/// CM Cameroon +pub const CAMEROON: Country = Country { code: *b"CM", rev: 0 }; +/// CA Canada +pub const CANADA: Country = Country { code: *b"CA", rev: 0 }; +/// CA Canada Revision 950 +pub const CANADA_REV950: Country = Country { code: *b"CA", rev: 950 }; +/// CV Cape_Verde +pub const CAPE_VERDE: Country = Country { code: *b"CV", rev: 0 }; +/// KY Cayman_Islands +pub const CAYMAN_ISLANDS: Country = Country { code: *b"KY", rev: 0 }; +/// CF Central_African_Republic +pub const CENTRAL_AFRICAN_REPUBLIC: Country = Country { code: *b"CF", rev: 0 }; +/// TD Chad +pub const CHAD: Country = Country { code: *b"TD", rev: 0 }; +/// CL Chile +pub const CHILE: Country = Country { code: *b"CL", rev: 0 }; +/// CN China +pub const CHINA: Country = Country { code: *b"CN", rev: 0 }; +/// CX Christmas_Island +pub const CHRISTMAS_ISLAND: Country = Country { code: *b"CX", rev: 0 }; +/// CO Colombia +pub const COLOMBIA: Country = Country { code: *b"CO", rev: 0 }; +/// KM Comoros +pub const COMOROS: Country = Country { code: *b"KM", rev: 0 }; +/// CG Congo +pub const CONGO: Country = Country { code: *b"CG", rev: 0 }; +/// CD Congo,_The_Democratic_Republic_Of_The +pub const CONGO_THE_DEMOCRATIC_REPUBLIC_OF_THE: Country = Country { code: *b"CD", rev: 0 }; +/// CR Costa_Rica +pub const COSTA_RICA: Country = Country { code: *b"CR", rev: 0 }; +/// CI Cote_D'ivoire +pub const COTE_DIVOIRE: Country = Country { code: *b"CI", rev: 0 }; +/// HR Croatia +pub const CROATIA: Country = Country { code: *b"HR", rev: 0 }; +/// CU Cuba +pub const CUBA: Country = Country { code: *b"CU", rev: 0 }; +/// CY Cyprus +pub const CYPRUS: Country = Country { code: *b"CY", rev: 0 }; +/// CZ Czech_Republic +pub const CZECH_REPUBLIC: Country = Country { code: *b"CZ", rev: 0 }; +/// DK Denmark +pub const DENMARK: Country = Country { code: *b"DK", rev: 0 }; +/// DJ Djibouti +pub const DJIBOUTI: Country = Country { code: *b"DJ", rev: 0 }; +/// DM Dominica +pub const DOMINICA: Country = Country { code: *b"DM", rev: 0 }; +/// DO Dominican_Republic +pub const DOMINICAN_REPUBLIC: Country = Country { code: *b"DO", rev: 0 }; +/// AU G'Day mate! +pub const DOWN_UNDER: Country = Country { code: *b"AU", rev: 0 }; +/// EC Ecuador +pub const ECUADOR: Country = Country { code: *b"EC", rev: 0 }; +/// EG Egypt +pub const EGYPT: Country = Country { code: *b"EG", rev: 0 }; +/// SV El_Salvador +pub const EL_SALVADOR: Country = Country { code: *b"SV", rev: 0 }; +/// GQ Equatorial_Guinea +pub const EQUATORIAL_GUINEA: Country = Country { code: *b"GQ", rev: 0 }; +/// ER Eritrea +pub const ERITREA: Country = Country { code: *b"ER", rev: 0 }; +/// EE Estonia +pub const ESTONIA: Country = Country { code: *b"EE", rev: 0 }; +/// ET Ethiopia +pub const ETHIOPIA: Country = Country { code: *b"ET", rev: 0 }; +/// FK Falkland_Islands_(Malvinas) +pub const FALKLAND_ISLANDS_MALVINAS: Country = Country { code: *b"FK", rev: 0 }; +/// FO Faroe_Islands +pub const FAROE_ISLANDS: Country = Country { code: *b"FO", rev: 0 }; +/// FJ Fiji +pub const FIJI: Country = Country { code: *b"FJ", rev: 0 }; +/// FI Finland +pub const FINLAND: Country = Country { code: *b"FI", rev: 0 }; +/// FR France +pub const FRANCE: Country = Country { code: *b"FR", rev: 0 }; +/// GF French_Guina +pub const FRENCH_GUINA: Country = Country { code: *b"GF", rev: 0 }; +/// PF French_Polynesia +pub const FRENCH_POLYNESIA: Country = Country { code: *b"PF", rev: 0 }; +/// TF French_Southern_Territories +pub const FRENCH_SOUTHERN_TERRITORIES: Country = Country { code: *b"TF", rev: 0 }; +/// GA Gabon +pub const GABON: Country = Country { code: *b"GA", rev: 0 }; +/// GM Gambia +pub const GAMBIA: Country = Country { code: *b"GM", rev: 0 }; +/// GE Georgia +pub const GEORGIA: Country = Country { code: *b"GE", rev: 0 }; +/// DE Germany +pub const GERMANY: Country = Country { code: *b"DE", rev: 0 }; +/// E0 European_Wide Revision 895 +pub const EUROPEAN_WIDE_REV895: Country = Country { code: *b"E0", rev: 895 }; +/// GH Ghana +pub const GHANA: Country = Country { code: *b"GH", rev: 0 }; +/// GI Gibraltar +pub const GIBRALTAR: Country = Country { code: *b"GI", rev: 0 }; +/// GR Greece +pub const GREECE: Country = Country { code: *b"GR", rev: 0 }; +/// GD Grenada +pub const GRENADA: Country = Country { code: *b"GD", rev: 0 }; +/// GP Guadeloupe +pub const GUADELOUPE: Country = Country { code: *b"GP", rev: 0 }; +/// GU Guam +pub const GUAM: Country = Country { code: *b"GU", rev: 0 }; +/// GT Guatemala +pub const GUATEMALA: Country = Country { code: *b"GT", rev: 0 }; +/// GG Guernsey +pub const GUERNSEY: Country = Country { code: *b"GG", rev: 0 }; +/// GN Guinea +pub const GUINEA: Country = Country { code: *b"GN", rev: 0 }; +/// GW Guinea-bissau +pub const GUINEA_BISSAU: Country = Country { code: *b"GW", rev: 0 }; +/// GY Guyana +pub const GUYANA: Country = Country { code: *b"GY", rev: 0 }; +/// HT Haiti +pub const HAITI: Country = Country { code: *b"HT", rev: 0 }; +/// VA Holy_See_(Vatican_City_State) +pub const HOLY_SEE_VATICAN_CITY_STATE: Country = Country { code: *b"VA", rev: 0 }; +/// HN Honduras +pub const HONDURAS: Country = Country { code: *b"HN", rev: 0 }; +/// HK Hong_Kong +pub const HONG_KONG: Country = Country { code: *b"HK", rev: 0 }; +/// HU Hungary +pub const HUNGARY: Country = Country { code: *b"HU", rev: 0 }; +/// IS Iceland +pub const ICELAND: Country = Country { code: *b"IS", rev: 0 }; +/// IN India +pub const INDIA: Country = Country { code: *b"IN", rev: 0 }; +/// ID Indonesia +pub const INDONESIA: Country = Country { code: *b"ID", rev: 0 }; +/// IR Iran,_Islamic_Republic_Of +pub const IRAN_ISLAMIC_REPUBLIC_OF: Country = Country { code: *b"IR", rev: 0 }; +/// IQ Iraq +pub const IRAQ: Country = Country { code: *b"IQ", rev: 0 }; +/// IE Ireland +pub const IRELAND: Country = Country { code: *b"IE", rev: 0 }; +/// IL Israel +pub const ISRAEL: Country = Country { code: *b"IL", rev: 0 }; +/// IT Italy +pub const ITALY: Country = Country { code: *b"IT", rev: 0 }; +/// JM Jamaica +pub const JAMAICA: Country = Country { code: *b"JM", rev: 0 }; +/// JP Japan +pub const JAPAN: Country = Country { code: *b"JP", rev: 0 }; +/// JE Jersey +pub const JERSEY: Country = Country { code: *b"JE", rev: 0 }; +/// JO Jordan +pub const JORDAN: Country = Country { code: *b"JO", rev: 0 }; +/// KZ Kazakhstan +pub const KAZAKHSTAN: Country = Country { code: *b"KZ", rev: 0 }; +/// KE Kenya +pub const KENYA: Country = Country { code: *b"KE", rev: 0 }; +/// KI Kiribati +pub const KIRIBATI: Country = Country { code: *b"KI", rev: 0 }; +/// KR Korea,_Republic_Of +pub const KOREA_REPUBLIC_OF: Country = Country { code: *b"KR", rev: 1 }; +/// 0A Kosovo +pub const KOSOVO: Country = Country { code: *b"0A", rev: 0 }; +/// KW Kuwait +pub const KUWAIT: Country = Country { code: *b"KW", rev: 0 }; +/// KG Kyrgyzstan +pub const KYRGYZSTAN: Country = Country { code: *b"KG", rev: 0 }; +/// LA Lao_People's_Democratic_Repubic +pub const LAO_PEOPLES_DEMOCRATIC_REPUBIC: Country = Country { code: *b"LA", rev: 0 }; +/// LV Latvia +pub const LATVIA: Country = Country { code: *b"LV", rev: 0 }; +/// LB Lebanon +pub const LEBANON: Country = Country { code: *b"LB", rev: 0 }; +/// LS Lesotho +pub const LESOTHO: Country = Country { code: *b"LS", rev: 0 }; +/// LR Liberia +pub const LIBERIA: Country = Country { code: *b"LR", rev: 0 }; +/// LY Libyan_Arab_Jamahiriya +pub const LIBYAN_ARAB_JAMAHIRIYA: Country = Country { code: *b"LY", rev: 0 }; +/// LI Liechtenstein +pub const LIECHTENSTEIN: Country = Country { code: *b"LI", rev: 0 }; +/// LT Lithuania +pub const LITHUANIA: Country = Country { code: *b"LT", rev: 0 }; +/// LU Luxembourg +pub const LUXEMBOURG: Country = Country { code: *b"LU", rev: 0 }; +/// MO Macao +pub const MACAO: Country = Country { code: *b"MO", rev: 0 }; +/// MK Macedonia,_Former_Yugoslav_Republic_Of +pub const MACEDONIA_FORMER_YUGOSLAV_REPUBLIC_OF: Country = Country { code: *b"MK", rev: 0 }; +/// MG Madagascar +pub const MADAGASCAR: Country = Country { code: *b"MG", rev: 0 }; +/// MW Malawi +pub const MALAWI: Country = Country { code: *b"MW", rev: 0 }; +/// MY Malaysia +pub const MALAYSIA: Country = Country { code: *b"MY", rev: 0 }; +/// MV Maldives +pub const MALDIVES: Country = Country { code: *b"MV", rev: 0 }; +/// ML Mali +pub const MALI: Country = Country { code: *b"ML", rev: 0 }; +/// MT Malta +pub const MALTA: Country = Country { code: *b"MT", rev: 0 }; +/// IM Man,_Isle_Of +pub const MAN_ISLE_OF: Country = Country { code: *b"IM", rev: 0 }; +/// MQ Martinique +pub const MARTINIQUE: Country = Country { code: *b"MQ", rev: 0 }; +/// MR Mauritania +pub const MAURITANIA: Country = Country { code: *b"MR", rev: 0 }; +/// MU Mauritius +pub const MAURITIUS: Country = Country { code: *b"MU", rev: 0 }; +/// YT Mayotte +pub const MAYOTTE: Country = Country { code: *b"YT", rev: 0 }; +/// MX Mexico +pub const MEXICO: Country = Country { code: *b"MX", rev: 0 }; +/// FM Micronesia,_Federated_States_Of +pub const MICRONESIA_FEDERATED_STATES_OF: Country = Country { code: *b"FM", rev: 0 }; +/// MD Moldova,_Republic_Of +pub const MOLDOVA_REPUBLIC_OF: Country = Country { code: *b"MD", rev: 0 }; +/// MC Monaco +pub const MONACO: Country = Country { code: *b"MC", rev: 0 }; +/// MN Mongolia +pub const MONGOLIA: Country = Country { code: *b"MN", rev: 0 }; +/// ME Montenegro +pub const MONTENEGRO: Country = Country { code: *b"ME", rev: 0 }; +/// MS Montserrat +pub const MONTSERRAT: Country = Country { code: *b"MS", rev: 0 }; +/// MA Morocco +pub const MOROCCO: Country = Country { code: *b"MA", rev: 0 }; +/// MZ Mozambique +pub const MOZAMBIQUE: Country = Country { code: *b"MZ", rev: 0 }; +/// MM Myanmar +pub const MYANMAR: Country = Country { code: *b"MM", rev: 0 }; +/// NA Namibia +pub const NAMIBIA: Country = Country { code: *b"NA", rev: 0 }; +/// NR Nauru +pub const NAURU: Country = Country { code: *b"NR", rev: 0 }; +/// NP Nepal +pub const NEPAL: Country = Country { code: *b"NP", rev: 0 }; +/// NL Netherlands +pub const NETHERLANDS: Country = Country { code: *b"NL", rev: 0 }; +/// AN Netherlands_Antilles +pub const NETHERLANDS_ANTILLES: Country = Country { code: *b"AN", rev: 0 }; +/// NC New_Caledonia +pub const NEW_CALEDONIA: Country = Country { code: *b"NC", rev: 0 }; +/// NZ New_Zealand +pub const NEW_ZEALAND: Country = Country { code: *b"NZ", rev: 0 }; +/// NI Nicaragua +pub const NICARAGUA: Country = Country { code: *b"NI", rev: 0 }; +/// NE Niger +pub const NIGER: Country = Country { code: *b"NE", rev: 0 }; +/// NG Nigeria +pub const NIGERIA: Country = Country { code: *b"NG", rev: 0 }; +/// NF Norfolk_Island +pub const NORFOLK_ISLAND: Country = Country { code: *b"NF", rev: 0 }; +/// MP Northern_Mariana_Islands +pub const NORTHERN_MARIANA_ISLANDS: Country = Country { code: *b"MP", rev: 0 }; +/// NO Norway +pub const NORWAY: Country = Country { code: *b"NO", rev: 0 }; +/// OM Oman +pub const OMAN: Country = Country { code: *b"OM", rev: 0 }; +/// PK Pakistan +pub const PAKISTAN: Country = Country { code: *b"PK", rev: 0 }; +/// PW Palau +pub const PALAU: Country = Country { code: *b"PW", rev: 0 }; +/// PA Panama +pub const PANAMA: Country = Country { code: *b"PA", rev: 0 }; +/// PG Papua_New_Guinea +pub const PAPUA_NEW_GUINEA: Country = Country { code: *b"PG", rev: 0 }; +/// PY Paraguay +pub const PARAGUAY: Country = Country { code: *b"PY", rev: 0 }; +/// PE Peru +pub const PERU: Country = Country { code: *b"PE", rev: 0 }; +/// PH Philippines +pub const PHILIPPINES: Country = Country { code: *b"PH", rev: 0 }; +/// PL Poland +pub const POLAND: Country = Country { code: *b"PL", rev: 0 }; +/// PT Portugal +pub const PORTUGAL: Country = Country { code: *b"PT", rev: 0 }; +/// PR Pueto_Rico +pub const PUETO_RICO: Country = Country { code: *b"PR", rev: 0 }; +/// QA Qatar +pub const QATAR: Country = Country { code: *b"QA", rev: 0 }; +/// RE Reunion +pub const REUNION: Country = Country { code: *b"RE", rev: 0 }; +/// RO Romania +pub const ROMANIA: Country = Country { code: *b"RO", rev: 0 }; +/// RU Russian_Federation +pub const RUSSIAN_FEDERATION: Country = Country { code: *b"RU", rev: 0 }; +/// RW Rwanda +pub const RWANDA: Country = Country { code: *b"RW", rev: 0 }; +/// KN Saint_Kitts_and_Nevis +pub const SAINT_KITTS_AND_NEVIS: Country = Country { code: *b"KN", rev: 0 }; +/// LC Saint_Lucia +pub const SAINT_LUCIA: Country = Country { code: *b"LC", rev: 0 }; +/// PM Saint_Pierre_and_Miquelon +pub const SAINT_PIERRE_AND_MIQUELON: Country = Country { code: *b"PM", rev: 0 }; +/// VC Saint_Vincent_and_The_Grenadines +pub const SAINT_VINCENT_AND_THE_GRENADINES: Country = Country { code: *b"VC", rev: 0 }; +/// WS Samoa +pub const SAMOA: Country = Country { code: *b"WS", rev: 0 }; +/// MF Sanit_Martin_/_Sint_Marteen +pub const SANIT_MARTIN_SINT_MARTEEN: Country = Country { code: *b"MF", rev: 0 }; +/// ST Sao_Tome_and_Principe +pub const SAO_TOME_AND_PRINCIPE: Country = Country { code: *b"ST", rev: 0 }; +/// SA Saudi_Arabia +pub const SAUDI_ARABIA: Country = Country { code: *b"SA", rev: 0 }; +/// SN Senegal +pub const SENEGAL: Country = Country { code: *b"SN", rev: 0 }; +/// RS Serbia +pub const SERBIA: Country = Country { code: *b"RS", rev: 0 }; +/// SC Seychelles +pub const SEYCHELLES: Country = Country { code: *b"SC", rev: 0 }; +/// SL Sierra_Leone +pub const SIERRA_LEONE: Country = Country { code: *b"SL", rev: 0 }; +/// SG Singapore +pub const SINGAPORE: Country = Country { code: *b"SG", rev: 0 }; +/// SK Slovakia +pub const SLOVAKIA: Country = Country { code: *b"SK", rev: 0 }; +/// SI Slovenia +pub const SLOVENIA: Country = Country { code: *b"SI", rev: 0 }; +/// SB Solomon_Islands +pub const SOLOMON_ISLANDS: Country = Country { code: *b"SB", rev: 0 }; +/// SO Somalia +pub const SOMALIA: Country = Country { code: *b"SO", rev: 0 }; +/// ZA South_Africa +pub const SOUTH_AFRICA: Country = Country { code: *b"ZA", rev: 0 }; +/// ES Spain +pub const SPAIN: Country = Country { code: *b"ES", rev: 0 }; +/// LK Sri_Lanka +pub const SRI_LANKA: Country = Country { code: *b"LK", rev: 0 }; +/// SR Suriname +pub const SURINAME: Country = Country { code: *b"SR", rev: 0 }; +/// SZ Swaziland +pub const SWAZILAND: Country = Country { code: *b"SZ", rev: 0 }; +/// SE Sweden +pub const SWEDEN: Country = Country { code: *b"SE", rev: 0 }; +/// CH Switzerland +pub const SWITZERLAND: Country = Country { code: *b"CH", rev: 0 }; +/// SY Syrian_Arab_Republic +pub const SYRIAN_ARAB_REPUBLIC: Country = Country { code: *b"SY", rev: 0 }; +/// TW Taiwan,_Province_Of_China +pub const TAIWAN_PROVINCE_OF_CHINA: Country = Country { code: *b"TW", rev: 0 }; +/// TJ Tajikistan +pub const TAJIKISTAN: Country = Country { code: *b"TJ", rev: 0 }; +/// TZ Tanzania,_United_Republic_Of +pub const TANZANIA_UNITED_REPUBLIC_OF: Country = Country { code: *b"TZ", rev: 0 }; +/// TH Thailand +pub const THAILAND: Country = Country { code: *b"TH", rev: 0 }; +/// TG Togo +pub const TOGO: Country = Country { code: *b"TG", rev: 0 }; +/// TO Tonga +pub const TONGA: Country = Country { code: *b"TO", rev: 0 }; +/// TT Trinidad_and_Tobago +pub const TRINIDAD_AND_TOBAGO: Country = Country { code: *b"TT", rev: 0 }; +/// TN Tunisia +pub const TUNISIA: Country = Country { code: *b"TN", rev: 0 }; +/// TR Turkey +pub const TURKEY: Country = Country { code: *b"TR", rev: 0 }; +/// TM Turkmenistan +pub const TURKMENISTAN: Country = Country { code: *b"TM", rev: 0 }; +/// TC Turks_and_Caicos_Islands +pub const TURKS_AND_CAICOS_ISLANDS: Country = Country { code: *b"TC", rev: 0 }; +/// TV Tuvalu +pub const TUVALU: Country = Country { code: *b"TV", rev: 0 }; +/// UG Uganda +pub const UGANDA: Country = Country { code: *b"UG", rev: 0 }; +/// UA Ukraine +pub const UKRAINE: Country = Country { code: *b"UA", rev: 0 }; +/// AE United_Arab_Emirates +pub const UNITED_ARAB_EMIRATES: Country = Country { code: *b"AE", rev: 0 }; +/// GB United_Kingdom +pub const UNITED_KINGDOM: Country = Country { code: *b"GB", rev: 0 }; +/// US United_States +pub const UNITED_STATES: Country = Country { code: *b"US", rev: 0 }; +/// US United_States Revision 4 +pub const UNITED_STATES_REV4: Country = Country { code: *b"US", rev: 4 }; +/// Q1 United_States Revision 931 +pub const UNITED_STATES_REV931: Country = Country { code: *b"Q1", rev: 931 }; +/// Q2 United_States_(No_DFS) +pub const UNITED_STATES_NO_DFS: Country = Country { code: *b"Q2", rev: 0 }; +/// UM United_States_Minor_Outlying_Islands +pub const UNITED_STATES_MINOR_OUTLYING_ISLANDS: Country = Country { code: *b"UM", rev: 0 }; +/// UY Uruguay +pub const URUGUAY: Country = Country { code: *b"UY", rev: 0 }; +/// UZ Uzbekistan +pub const UZBEKISTAN: Country = Country { code: *b"UZ", rev: 0 }; +/// VU Vanuatu +pub const VANUATU: Country = Country { code: *b"VU", rev: 0 }; +/// VE Venezuela +pub const VENEZUELA: Country = Country { code: *b"VE", rev: 0 }; +/// VN Viet_Nam +pub const VIET_NAM: Country = Country { code: *b"VN", rev: 0 }; +/// VG Virgin_Islands,_British +pub const VIRGIN_ISLANDS_BRITISH: Country = Country { code: *b"VG", rev: 0 }; +/// VI Virgin_Islands,_U.S. +pub const VIRGIN_ISLANDS_US: Country = Country { code: *b"VI", rev: 0 }; +/// WF Wallis_and_Futuna +pub const WALLIS_AND_FUTUNA: Country = Country { code: *b"WF", rev: 0 }; +/// 0C West_Bank +pub const WEST_BANK: Country = Country { code: *b"0C", rev: 0 }; +/// EH Western_Sahara +pub const WESTERN_SAHARA: Country = Country { code: *b"EH", rev: 0 }; +/// Worldwide Locale Revision 983 +pub const WORLD_WIDE_XV_REV983: Country = Country { code: *b"XV", rev: 983 }; +/// Worldwide Locale (passive Ch12-14) +pub const WORLD_WIDE_XX: Country = Country { code: *b"XX", rev: 0 }; +/// Worldwide Locale (passive Ch12-14) Revision 17 +pub const WORLD_WIDE_XX_REV17: Country = Country { code: *b"XX", rev: 17 }; +/// YE Yemen +pub const YEMEN: Country = Country { code: *b"YE", rev: 0 }; +/// ZM Zambia +pub const ZAMBIA: Country = Country { code: *b"ZM", rev: 0 }; +/// ZW Zimbabwe +pub const ZIMBABWE: Country = Country { code: *b"ZW", rev: 0 }; diff --git a/src/lib.rs b/src/lib.rs index 3609ecaeb..698c52f49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait, concat_bytes, const_slice_from_raw_parts)] +#![deny(unused_must_use)] + // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod countries; mod structs; use core::cell::Cell; @@ -206,14 +209,67 @@ pub struct Control<'a> { impl<'a> Control<'a> { pub async fn init(&mut self) { + const CHUNK_SIZE: usize = 1024; + let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; - let mut buf = [0; 8 + 12 + 1024]; - buf[0..8].copy_from_slice(b"clmload\x00"); - buf[8..20].copy_from_slice(b"\x02\x10\x02\x00\x00\x04\x00\x00\x00\x00\x00\x00"); - buf[20..].copy_from_slice(&clm[..1024]); - self.ioctl(2, 263, 0, &buf).await; - info!("IOCTL done"); + info!("Downloading CLM..."); + + let mut offs = 0; + for chunk in clm.chunks(CHUNK_SIZE) { + let mut flag = DOWNLOAD_FLAG_HANDLER_VER; + if offs == 0 { + flag |= DOWNLOAD_FLAG_BEGIN; + } + offs += chunk.len(); + if offs == clm.len() { + flag |= DOWNLOAD_FLAG_END; + } + + let header = DownloadHeader { + flag, + dload_type: DOWNLOAD_TYPE_CLM, + len: chunk.len() as _, + crc: 0, + }; + let mut buf = [0; 8 + 12 + CHUNK_SIZE]; + buf[0..8].copy_from_slice(b"clmload\x00"); + buf[8..20].copy_from_slice(&header.to_bytes()); + buf[20..][..chunk.len()].copy_from_slice(&chunk); + self.ioctl(2, 263, 0, &buf[..8 + 12 + chunk.len()]).await; + } + + info!("Configuring misc stuff..."); + + self.set_iovar_u32("bus:txglom", 0).await; + self.set_iovar_u32("apsta", 1).await; + self.set_iovar("cur_etheraddr", &[02, 03, 04, 05, 06, 07]).await; + + let country = countries::WORLD_WIDE_XX; + let country_info = CountryInfo { + country_abbrev: [country.code[0], country.code[1], 0, 0], + country_code: [country.code[0], country.code[1], 0, 0], + rev: if country.rev == 0 { -1 } else { country.rev as _ }, + }; + self.set_iovar("country", &country_info.to_bytes()).await; + + info!("INIT DONE"); + } + + async fn set_iovar_u32(&mut self, name: &str, val: u32) { + self.set_iovar(name, &val.to_le_bytes()).await + } + + async fn set_iovar(&mut self, name: &str, val: &[u8]) { + info!("set {} = {:02x}", name, val); + + let mut buf = [0; 64]; + buf[..name.len()].copy_from_slice(name.as_bytes()); + buf[name.len()] = 0; + buf[name.len() + 1..][..val.len()].copy_from_slice(val); + + let total_len = name.len() + 1 + val.len(); + self.ioctl(2, 263, 0, &buf[..total_len]).await; } async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &[u8]) { @@ -255,6 +311,7 @@ pub struct Runner<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { /// - strap to set to gSPI mode on boot. dio: Flex<'a, DIO>, + ioctl_seq: u8, backplane_window: u32, } @@ -271,6 +328,8 @@ pub async fn new<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin>( cs, clk, dio, + + ioctl_seq: 0, backplane_window: 0xAAAA_AAAA, }; @@ -397,7 +456,6 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } pub async fn run(mut self) -> ! { - let mut old_irq = 0; let mut buf = [0; 2048]; loop { // Send stuff @@ -408,10 +466,6 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { // Receive stuff let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT); - if irq != old_irq { - info!("irq: {:04x}", irq); - } - old_irq = irq; if irq & IRQ_F2_PACKET_AVAILABLE != 0 { let mut status = 0xFFFF_FFFF; @@ -421,7 +475,6 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { if status & STATUS_F2_PKT_AVAILABLE != 0 { let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - info!("got len {}", len); let cmd = cmd_word(false, true, FUNC_WLAN, 0, len); @@ -435,7 +488,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } self.cs.set_high(); - info!("rxd packet {:02x}", &buf[..len as usize]); + info!("rx {:02x}", &buf[..(len as usize).min(48)]); self.rx(&buf[..len as usize]); } @@ -466,15 +519,17 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let channel = sdpcm_header.channel_and_flags & 0x0f; + let payload = &packet[sdpcm_header.header_length as _..]; + match channel { 0 => { - if packet.len() < SdpcmHeader::SIZE + CdcHeader::SIZE { - warn!("control packet too short, len={}", packet.len()); + if payload.len() < CdcHeader::SIZE { + warn!("payload too short, len={}", payload.len()); return; } let cdc_header = - CdcHeader::from_bytes(packet[SdpcmHeader::SIZE..][..CdcHeader::SIZE].try_into().unwrap()); + CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); if cdc_header.id == self.state.ioctl_id.get() { assert_eq!(cdc_header.status, 0); // todo propagate error @@ -490,10 +545,13 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); + let seq = self.ioctl_seq; + self.ioctl_seq = self.ioctl_seq.wrapping_add(1); + let sdpcm_header = SdpcmHeader { - len: total_len as u16, + len: total_len as u16, // TODO does this len need to be rounded up to u32? len_inv: !total_len as u16, - sequence: 0x02, // todo + sequence: seq, channel_and_flags: 0, // control channel next_length: 0, header_length: SdpcmHeader::SIZE as _, @@ -515,7 +573,9 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { buf[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); buf[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); - info!("txing {:02x}", &buf[..total_len]); + let total_len = (total_len + 3) & !3; // round up to 4byte + + info!("tx {:02x}", &buf[..total_len.min(48)]); let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); self.cs.set_low(); diff --git a/src/structs.rs b/src/structs.rs index fe5e89a37..bce9ab9ff 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,3 +1,19 @@ +macro_rules! impl_bytes { + ($t:ident) => { + impl $t { + pub const SIZE: usize = core::mem::size_of::(); + + pub fn to_bytes(&self) -> [u8; Self::SIZE] { + unsafe { core::mem::transmute(*self) } + } + + pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { + unsafe { core::mem::transmute(*bytes) } + } + } + }; +} + #[derive(Clone, Copy)] #[repr(C)] pub struct SdpcmHeader { @@ -18,6 +34,7 @@ pub struct SdpcmHeader { /// Reserved pub reserved: [u8; 2], } +impl_bytes!(SdpcmHeader); #[derive(Clone, Copy)] #[repr(C)] @@ -29,6 +46,7 @@ pub struct CdcHeader { pub id: u16, pub status: u32, } +impl_bytes!(CdcHeader); #[derive(Clone, Copy)] #[repr(C)] @@ -40,32 +58,30 @@ pub struct BdcHeader { /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. pub data_offset: u8, } +impl_bytes!(BdcHeader); #[derive(Clone, Copy)] #[repr(C)] pub struct DownloadHeader { - pub flag: u16, + pub flag: u16, // pub dload_type: u16, pub len: u32, pub crc: u32, } - -macro_rules! impl_bytes { - ($t:ident) => { - impl $t { - pub const SIZE: usize = core::mem::size_of::(); - - pub fn to_bytes(&self) -> [u8; Self::SIZE] { - unsafe { core::mem::transmute(*self) } - } - - pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { - unsafe { core::mem::transmute(*bytes) } - } - } - }; -} -impl_bytes!(SdpcmHeader); -impl_bytes!(CdcHeader); -impl_bytes!(BdcHeader); impl_bytes!(DownloadHeader); + +pub const DOWNLOAD_FLAG_NO_CRC: u16 = 0x0001; +pub const DOWNLOAD_FLAG_BEGIN: u16 = 0x0002; +pub const DOWNLOAD_FLAG_END: u16 = 0x0004; +pub const DOWNLOAD_FLAG_HANDLER_VER: u16 = 0x1000; + +pub const DOWNLOAD_TYPE_CLM: u16 = 2; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct CountryInfo { + pub country_abbrev: [u8; 4], + pub rev: i32, + pub country_code: [u8; 4], +} +impl_bytes!(CountryInfo); From 30b7800f9ae0a7f26e292dbe55cc67fbe2d1b131 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Jul 2022 05:19:31 +0200 Subject: [PATCH 0004/1575] add event printing, add join but not working yet. --- Cargo.toml | 1 + examples/rpi-pico-w/src/main.rs | 3 + src/events.rs | 281 ++++++++++++++++++++++++++++++++ src/lib.rs | 80 ++++++++- src/structs.rs | 55 ++++++- 5 files changed, 414 insertions(+), 6 deletions(-) create mode 100644 src/events.rs diff --git a/Cargo.toml b/Cargo.toml index d1672146c..bd27a48b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } +num_enum = { version = "0.5.7", default-features = false } diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 7545dfde8..d4aae8479 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -45,4 +45,7 @@ async fn main(spawner: Spawner, p: Peripherals) { spawner.spawn(wifi_task(runner)).unwrap(); control.init().await; + + let ssid = "MikroTik-951589"; + control.join(ssid).await; } diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 000000000..b35b12faa --- /dev/null +++ b/src/events.rs @@ -0,0 +1,281 @@ +#![allow(unused)] + +use core::num; + +#[derive(Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum Event { + #[num_enum(default)] + Unknown = 0xFF, + /// indicates status of set SSID + SET_SSID = 0, + /// differentiates join IBSS from found (START) IBSS + JOIN = 1, + /// STA founded an IBSS or AP started a BSS + START = 2, + /// 802.11 AUTH request + AUTH = 3, + /// 802.11 AUTH indication + AUTH_IND = 4, + /// 802.11 DEAUTH request + DEAUTH = 5, + /// 802.11 DEAUTH indication + DEAUTH_IND = 6, + /// 802.11 ASSOC request + ASSOC = 7, + /// 802.11 ASSOC indication + ASSOC_IND = 8, + /// 802.11 REASSOC request + REASSOC = 9, + /// 802.11 REASSOC indication + REASSOC_IND = 10, + /// 802.11 DISASSOC request + DISASSOC = 11, + /// 802.11 DISASSOC indication + DISASSOC_IND = 12, + /// 802.11h Quiet period started + QUIET_START = 13, + /// 802.11h Quiet period ended + QUIET_END = 14, + /// BEACONS received/lost indication + BEACON_RX = 15, + /// generic link indication + LINK = 16, + /// TKIP MIC error occurred + MIC_ERROR = 17, + /// NDIS style link indication + NDIS_LINK = 18, + /// roam attempt occurred: indicate status & reason + ROAM = 19, + /// change in dot11FailedCount (txfail) + TXFAIL = 20, + /// WPA2 pmkid cache indication + PMKID_CACHE = 21, + /// current AP's TSF value went backward + RETROGRADE_TSF = 22, + /// AP was pruned from join list for reason + PRUNE = 23, + /// report AutoAuth table entry match for join attempt + AUTOAUTH = 24, + /// Event encapsulating an EAPOL message + EAPOL_MSG = 25, + /// Scan results are ready or scan was aborted + SCAN_COMPLETE = 26, + /// indicate to host addts fail/success + ADDTS_IND = 27, + /// indicate to host delts fail/success + DELTS_IND = 28, + /// indicate to host of beacon transmit + BCNSENT_IND = 29, + /// Send the received beacon up to the host + BCNRX_MSG = 30, + /// indicate to host loss of beacon + BCNLOST_MSG = 31, + /// before attempting to roam + ROAM_PREP = 32, + /// PFN network found event + PFN_NET_FOUND = 33, + /// PFN network lost event + PFN_NET_LOST = 34, + RESET_COMPLETE = 35, + JOIN_START = 36, + ROAM_START = 37, + ASSOC_START = 38, + IBSS_ASSOC = 39, + RADIO = 40, + /// PSM microcode watchdog fired + PSM_WATCHDOG = 41, + /// CCX association start + CCX_ASSOC_START = 42, + /// CCX association abort + CCX_ASSOC_ABORT = 43, + /// probe request received + PROBREQ_MSG = 44, + SCAN_CONFIRM_IND = 45, + /// WPA Handshake + PSK_SUP = 46, + COUNTRY_CODE_CHANGED = 47, + /// WMMAC excedded medium time + EXCEEDED_MEDIUM_TIME = 48, + /// WEP ICV error occurred + ICV_ERROR = 49, + /// Unsupported unicast encrypted frame + UNICAST_DECODE_ERROR = 50, + /// Unsupported multicast encrypted frame + MULTICAST_DECODE_ERROR = 51, + TRACE = 52, + /// BT-AMP HCI event + BTA_HCI_EVENT = 53, + /// I/F change (for wlan host notification) + IF = 54, + /// P2P Discovery listen state expires + P2P_DISC_LISTEN_COMPLETE = 55, + /// indicate RSSI change based on configured levels + RSSI = 56, + /// PFN best network batching event + PFN_BEST_BATCHING = 57, + EXTLOG_MSG = 58, + /// Action frame reception + ACTION_FRAME = 59, + /// Action frame Tx complete + ACTION_FRAME_COMPLETE = 60, + /// assoc request received + PRE_ASSOC_IND = 61, + /// re-assoc request received + PRE_REASSOC_IND = 62, + /// channel adopted (xxx: obsoleted) + CHANNEL_ADOPTED = 63, + /// AP started + AP_STARTED = 64, + /// AP stopped due to DFS + DFS_AP_STOP = 65, + /// AP resumed due to DFS + DFS_AP_RESUME = 66, + /// WAI stations event + WAI_STA_EVENT = 67, + /// event encapsulating an WAI message + WAI_MSG = 68, + /// escan result event + ESCAN_RESULT = 69, + /// action frame off channel complete + ACTION_FRAME_OFF_CHAN_COMPLETE = 70, + /// probe response received + PROBRESP_MSG = 71, + /// P2P Probe request received + P2P_PROBREQ_MSG = 72, + DCS_REQUEST = 73, + /// credits for D11 FIFOs. [AC0,AC1,AC2,AC3,BC_MC,ATIM] + FIFO_CREDIT_MAP = 74, + /// Received action frame event WITH wl_event_rx_frame_data_t header + ACTION_FRAME_RX = 75, + /// Wake Event timer fired, used for wake WLAN test mode + WAKE_EVENT = 76, + /// Radio measurement complete + RM_COMPLETE = 77, + /// Synchronize TSF with the host + HTSFSYNC = 78, + /// request an overlay IOCTL/iovar from the host + OVERLAY_REQ = 79, + CSA_COMPLETE_IND = 80, + /// excess PM Wake Event to inform host + EXCESS_PM_WAKE_EVENT = 81, + /// no PFN networks around + PFN_SCAN_NONE = 82, + /// last found PFN network gets lost + PFN_SCAN_ALLGONE = 83, + GTK_PLUMBED = 84, + /// 802.11 ASSOC indication for NDIS only + ASSOC_IND_NDIS = 85, + /// 802.11 REASSOC indication for NDIS only + REASSOC_IND_NDIS = 86, + ASSOC_REQ_IE = 87, + ASSOC_RESP_IE = 88, + /// association recreated on resume + ASSOC_RECREATED = 89, + /// rx action frame event for NDIS only + ACTION_FRAME_RX_NDIS = 90, + /// authentication request received + AUTH_REQ = 91, + /// fast assoc recreation failed + SPEEDY_RECREATE_FAIL = 93, + /// port-specific event and payload (e.g. NDIS) + NATIVE = 94, + /// event for tx pkt delay suddently jump + PKTDELAY_IND = 95, + /// AWDL AW period starts + AWDL_AW = 96, + /// AWDL Master/Slave/NE master role event + AWDL_ROLE = 97, + /// Generic AWDL event + AWDL_EVENT = 98, + /// NIC AF txstatus + NIC_AF_TXS = 99, + /// NAN event + NAN = 100, + BEACON_FRAME_RX = 101, + /// desired service found + SERVICE_FOUND = 102, + /// GAS fragment received + GAS_FRAGMENT_RX = 103, + /// GAS sessions all complete + GAS_COMPLETE = 104, + /// New device found by p2p offload + P2PO_ADD_DEVICE = 105, + /// device has been removed by p2p offload + P2PO_DEL_DEVICE = 106, + /// WNM event to notify STA enter sleep mode + WNM_STA_SLEEP = 107, + /// Indication of MAC tx failures (exhaustion of 802.11 retries) exceeding threshold(s) + TXFAIL_THRESH = 108, + /// Proximity Detection event + PROXD = 109, + /// AWDL RX Probe response + AWDL_RX_PRB_RESP = 111, + /// AWDL RX Action Frames + AWDL_RX_ACT_FRAME = 112, + /// AWDL Wowl nulls + AWDL_WOWL_NULLPKT = 113, + /// AWDL Phycal status + AWDL_PHYCAL_STATUS = 114, + /// AWDL OOB AF status + AWDL_OOB_AF_STATUS = 115, + /// Interleaved Scan status + AWDL_SCAN_STATUS = 116, + /// AWDL AW Start + AWDL_AW_START = 117, + /// AWDL AW End + AWDL_AW_END = 118, + /// AWDL AW Extensions + AWDL_AW_EXT = 119, + AWDL_PEER_CACHE_CONTROL = 120, + CSA_START_IND = 121, + CSA_DONE_IND = 122, + CSA_FAILURE_IND = 123, + /// CCA based channel quality report + CCA_CHAN_QUAL = 124, + /// to report change in BSSID while roaming + BSSID = 125, + /// tx error indication + TX_STAT_ERROR = 126, + /// credit check for BCMC supported + BCMC_CREDIT_SUPPORT = 127, + /// psta primary interface indication + PSTA_PRIMARY_INTF_IND = 128, + /// Handover Request Initiated + BT_WIFI_HANDOVER_REQ = 130, + /// Southpaw TxInhibit notification + SPW_TXINHIBIT = 131, + /// FBT Authentication Request Indication + FBT_AUTH_REQ_IND = 132, + /// Enhancement addition for RSSI + RSSI_LQM = 133, + /// Full probe/beacon (IEs etc) results + PFN_GSCAN_FULL_RESULT = 134, + /// Significant change in rssi of bssids being tracked + PFN_SWC = 135, + /// a STA been authroized for traffic + AUTHORIZED = 136, + /// probe req with wl_event_rx_frame_data_t header + PROBREQ_MSG_RX = 137, + /// PFN completed scan of network list + PFN_SCAN_COMPLETE = 138, + /// RMC Event + RMC_EVENT = 139, + /// DPSTA interface indication + DPSTA_INTF_IND = 140, + /// RRM Event + RRM = 141, + /// ULP entry event + ULP = 146, + /// TCP Keep Alive Offload Event + TKO = 151, + /// authentication request received + EXT_AUTH_REQ = 187, + /// authentication request received + EXT_AUTH_FRAME_RX = 188, + /// mgmt frame Tx complete + MGMT_FRAME_TXSTATUS = 189, + /// highest val + 1 for range checking + LAST = 190, +} diff --git a/src/lib.rs b/src/lib.rs index 698c52f49..040caed86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; mod countries; +mod events; mod structs; use core::cell::Cell; @@ -253,9 +254,52 @@ impl<'a> Control<'a> { }; self.set_iovar("country", &country_info.to_bytes()).await; + // set country takes some time, next ioctls fail if we don't wait. + Timer::after(Duration::from_millis(100)).await; + + // self.set_iovar_u32("ampdu_ba_wsize", 8).await; + // self.set_iovar_u32("ampdu_mpdu", 4).await; + // self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes + + Timer::after(Duration::from_millis(100)).await; + + // evts + let mut evts = EventMask { + iface: 0, + events: [0xFF; 24], + }; + self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; + + // set wifi up + self.ioctl(2, 2, 0, &[]).await; + + Timer::after(Duration::from_millis(100)).await; + info!("INIT DONE"); } + pub async fn join(&mut self, ssid: &str) { + self.ioctl_set_u32(134, 0, 0).await; // wsec = open + self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; + self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 + self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) + + let mut i = SsidInfo { + len: ssid.len() as _, + ssid: [0; 32], + }; + i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + self.ioctl(2, 26, 0, &i.to_bytes()).await; // set_ssid + + info!("JOINED"); + } + + async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { + let mut buf = [0; 8]; + buf[0..4].copy_from_slice(&val1.to_le_bytes()); + buf[4..8].copy_from_slice(&val2.to_le_bytes()); + self.set_iovar(name, &buf).await + } async fn set_iovar_u32(&mut self, name: &str, val: u32) { self.set_iovar(name, &val.to_le_bytes()).await } @@ -272,6 +316,10 @@ impl<'a> Control<'a> { self.ioctl(2, 263, 0, &buf[..total_len]).await; } + async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { + self.ioctl(2, cmd, 0, &val.to_le_bytes()).await + } + async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &[u8]) { // TODO cancel ioctl on future drop. @@ -488,7 +536,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } self.cs.set_high(); - info!("rx {:02x}", &buf[..(len as usize).min(48)]); + //info!("rx {:02x}", &buf[..(len as usize).min(36)]); self.rx(&buf[..len as usize]); } @@ -528,14 +576,38 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { return; } - let cdc_header = - CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); + let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); if cdc_header.id == self.state.ioctl_id.get() { assert_eq!(cdc_header.status, 0); // todo propagate error self.state.ioctl_state.set(IoctlState::Done); } } + 1 => { + let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); + //info!("{}", bcd_header); + + let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; + if packet_start > payload.len() { + warn!("packet start out of range."); + return; + } + let packet = &payload[packet_start..]; + //info!("rx {:02x}", &packet[..(packet.len() as usize).min(36)]); + + let evt = EventHeader::from_bytes(&packet[24..][..EventHeader::SIZE].try_into().unwrap()); + let evt_num = evt.event_type.to_be() as u8; + let evt_data_len = evt.datalen.to_be() as u8; + let evt_data = &packet[24 + EventHeader::SIZE..][..evt_data_len as usize]; + info!( + "=== EVENT {} ({}) {} {:02x}", + events::Event::from(evt_num), + evt_num, + evt, + evt_data + ); + } + _ => {} } } @@ -575,7 +647,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let total_len = (total_len + 3) & !3; // round up to 4byte - info!("tx {:02x}", &buf[..total_len.min(48)]); + //info!("tx {:02x}", &buf[..total_len.min(48)]); let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); self.cs.set_low(); diff --git a/src/structs.rs b/src/structs.rs index bce9ab9ff..8a98d5227 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -15,6 +15,7 @@ macro_rules! impl_bytes { } #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct SdpcmHeader { pub len: u16, @@ -37,6 +38,7 @@ pub struct SdpcmHeader { impl_bytes!(SdpcmHeader); #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct CdcHeader { pub cmd: u32, @@ -49,8 +51,9 @@ pub struct CdcHeader { impl_bytes!(CdcHeader); #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] -pub struct BdcHeader { +pub struct BcdHeader { pub flags: u8, /// 802.1d Priority (low 3 bits) pub priority: u8, @@ -58,7 +61,36 @@ pub struct BdcHeader { /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. pub data_offset: u8, } -impl_bytes!(BdcHeader); +impl_bytes!(BcdHeader); + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct EventHeader { + /// version + pub version: u16, + /// see flags below + pub flags: u16, + /// Message (see below) + pub event_type: u32, + /// Status code (see below) + pub status: u32, + /// Reason code (if applicable) + pub reason: u32, + /// WLC_E_AUTH + pub auth_type: u32, + /// data buf + pub datalen: u32, + /// Station address (if applicable) + pub addr: [u8; 6], + /// name of the incoming packet interface + pub ifname: [u8; 16], + /// destination OS i/f index + pub ifidx: u8, + /// source bsscfg index + pub bsscfgidx: u8, +} +impl_bytes!(EventHeader); #[derive(Clone, Copy)] #[repr(C)] @@ -78,6 +110,7 @@ pub const DOWNLOAD_FLAG_HANDLER_VER: u16 = 0x1000; pub const DOWNLOAD_TYPE_CLM: u16 = 2; #[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct CountryInfo { pub country_abbrev: [u8; 4], @@ -85,3 +118,21 @@ pub struct CountryInfo { pub country_code: [u8; 4], } impl_bytes!(CountryInfo); + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct SsidInfo { + pub len: u32, + pub ssid: [u8; 32], +} +impl_bytes!(SsidInfo); + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct EventMask { + pub iface: u32, + pub events: [u8; 24], +} +impl_bytes!(EventMask); From 3ffdbd2ca3ed7cc3da95c391f1928342afb55e10 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Jul 2022 22:44:42 +0200 Subject: [PATCH 0005/1575] stuff --- examples/rpi-pico-w/src/main.rs | 1 + src/lib.rs | 32 ++++++++++++++++++++------------ src/structs.rs | 12 ++++++++++++ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index d4aae8479..bef820d31 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -6,6 +6,7 @@ use core::slice; use defmt::{assert, assert_eq, panic, *}; use embassy::executor::Spawner; +use embassy::time::{Duration, Timer}; use embassy::util::Forever; use embassy_rp::gpio::{Flex, Level, Output, Pin}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; diff --git a/src/lib.rs b/src/lib.rs index 040caed86..271917971 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -243,7 +243,7 @@ impl<'a> Control<'a> { info!("Configuring misc stuff..."); self.set_iovar_u32("bus:txglom", 0).await; - self.set_iovar_u32("apsta", 1).await; + //self.set_iovar_u32("apsta", 1).await; self.set_iovar("cur_etheraddr", &[02, 03, 04, 05, 06, 07]).await; let country = countries::WORLD_WIDE_XX; @@ -257,9 +257,13 @@ impl<'a> Control<'a> { // set country takes some time, next ioctls fail if we don't wait. Timer::after(Duration::from_millis(100)).await; - // self.set_iovar_u32("ampdu_ba_wsize", 8).await; - // self.set_iovar_u32("ampdu_mpdu", 4).await; - // self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes + self.ioctl_set_u32(64, 0, 0).await; // WLC_SET_ANTDIV + + self.set_iovar_u32("bus:txglom", 0).await; + //self.set_iovar_u32("apsta", 1).await; + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + self.set_iovar_u32("ampdu_mpdu", 4).await; + //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes Timer::after(Duration::from_millis(100)).await; @@ -275,6 +279,12 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; + self.ioctl_set_u32(86, 0, 0).await; // no power save + self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto + self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any + + Timer::after(Duration::from_millis(100)).await; + info!("INIT DONE"); } @@ -494,11 +504,11 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR); val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON self.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val); + */ // clear pulls self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0); let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP); - */ info!("init done "); } @@ -595,14 +605,12 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let packet = &payload[packet_start..]; //info!("rx {:02x}", &packet[..(packet.len() as usize).min(36)]); - let evt = EventHeader::from_bytes(&packet[24..][..EventHeader::SIZE].try_into().unwrap()); - let evt_num = evt.event_type.to_be() as u8; - let evt_data_len = evt.datalen.to_be() as u8; - let evt_data = &packet[24 + EventHeader::SIZE..][..evt_data_len as usize]; + let mut evt = EventHeader::from_bytes(&packet[24..][..EventHeader::SIZE].try_into().unwrap()); + evt.byteswap(); + let evt_data = &packet[24 + EventHeader::SIZE..][..evt.datalen as usize]; info!( - "=== EVENT {} ({}) {} {:02x}", - events::Event::from(evt_num), - evt_num, + "=== EVENT {}: {} {:02x}", + events::Event::from(evt.event_type as u8), evt, evt_data ); diff --git a/src/structs.rs b/src/structs.rs index 8a98d5227..beda9f364 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -92,6 +92,18 @@ pub struct EventHeader { } impl_bytes!(EventHeader); +impl EventHeader { + pub fn byteswap(&mut self) { + self.version = self.version.to_be(); + self.flags = self.flags.to_be(); + self.event_type = self.event_type.to_be(); + self.status = self.status.to_be(); + self.reason = self.reason.to_be(); + self.auth_type = self.auth_type.to_be(); + self.datalen = self.datalen.to_be(); + } +} + #[derive(Clone, Copy)] #[repr(C)] pub struct DownloadHeader { From d96ad248b33d8ca89daf88c5878cb24d6566cc6d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Jul 2022 22:53:57 +0200 Subject: [PATCH 0006/1575] Add LICENSEs --- LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++++ README.md | 14 ++++ 3 files changed, 240 insertions(+) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..ea4fa15c9 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2019-2022 Embassy project contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..87c052836 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019-2022 Embassy project contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 000000000..7df988b79 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# cyw43 + +Very WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. + +## 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. + From 18b11e7417e1338fb18e6ceda609f2a0841d7a57 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Jul 2022 03:34:27 +0200 Subject: [PATCH 0007/1575] check clmload_status. --- src/lib.rs | 85 +++++++++++++++++++++++++++++++++++++------------- src/structs.rs | 3 +- 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 271917971..6a1b7970c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,10 +184,14 @@ enum IoctlState { kind: u32, cmd: u32, iface: u32, - buf: *const [u8], + buf: *mut [u8], + }, + Sent { + buf: *mut [u8], + }, + Done { + resp_len: usize, }, - Sent, - Done, } pub struct State { @@ -237,9 +241,12 @@ impl<'a> Control<'a> { buf[0..8].copy_from_slice(b"clmload\x00"); buf[8..20].copy_from_slice(&header.to_bytes()); buf[20..][..chunk.len()].copy_from_slice(&chunk); - self.ioctl(2, 263, 0, &buf[..8 + 12 + chunk.len()]).await; + self.ioctl(2, 263, 0, &mut buf[..8 + 12 + chunk.len()]).await; } + // check clmload ok + assert_eq!(self.get_iovar_u32("clmload_status").await, 0); + info!("Configuring misc stuff..."); self.set_iovar_u32("bus:txglom", 0).await; @@ -275,7 +282,7 @@ impl<'a> Control<'a> { self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; // set wifi up - self.ioctl(2, 2, 0, &[]).await; + self.ioctl(2, 2, 0, &mut []).await; Timer::after(Duration::from_millis(100)).await; @@ -299,7 +306,7 @@ impl<'a> Control<'a> { ssid: [0; 32], }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.ioctl(2, 26, 0, &i.to_bytes()).await; // set_ssid + self.ioctl(2, 26, 0, &mut i.to_bytes()).await; // set_ssid info!("JOINED"); } @@ -310,10 +317,18 @@ impl<'a> Control<'a> { buf[4..8].copy_from_slice(&val2.to_le_bytes()); self.set_iovar(name, &buf).await } + async fn set_iovar_u32(&mut self, name: &str, val: u32) { self.set_iovar(name, &val.to_le_bytes()).await } + async fn get_iovar_u32(&mut self, name: &str) -> u32 { + let mut buf = [0; 4]; + let len = self.get_iovar(name, &mut buf).await; + assert_eq!(len, 4); + u32::from_le_bytes(buf) + } + async fn set_iovar(&mut self, name: &str, val: &[u8]) { info!("set {} = {:02x}", name, val); @@ -323,14 +338,28 @@ impl<'a> Control<'a> { buf[name.len() + 1..][..val.len()].copy_from_slice(val); let total_len = name.len() + 1 + val.len(); - self.ioctl(2, 263, 0, &buf[..total_len]).await; + self.ioctl(2, 263, 0, &mut buf).await; + } + + async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { + info!("get {}", name); + + let mut buf = [0; 64]; + buf[..name.len()].copy_from_slice(name.as_bytes()); + buf[name.len()] = 0; + + let total_len = name.len() + 1 + res.len(); + let res_len = self.ioctl(2, 262, 0, &mut buf[..total_len]).await - name.len() - 1; + res[..res_len].copy_from_slice(&buf[name.len() + 1..][..res_len]); + res_len } async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { - self.ioctl(2, cmd, 0, &val.to_le_bytes()).await + let mut buf = val.to_le_bytes(); + self.ioctl(2, cmd, 0, &mut buf).await; } - async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &[u8]) { + async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { // TODO cancel ioctl on future drop. while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) { @@ -343,11 +372,16 @@ impl<'a> Control<'a> { .ioctl_state .set(IoctlState::Pending { kind, cmd, iface, buf }); - while !matches!(self.state.ioctl_state.get(), IoctlState::Done) { + let resp_len = loop { + if let IoctlState::Done { resp_len } = self.state.ioctl_state.get() { + break resp_len; + } yield_now().await; - } + }; self.state.ioctl_state.set(IoctlState::Idle); + + resp_len } } @@ -519,7 +553,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { // Send stuff if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get()); - self.state.ioctl_state.set(IoctlState::Sent); + self.state.ioctl_state.set(IoctlState::Sent { buf }); } // Receive stuff @@ -546,7 +580,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } self.cs.set_high(); - //info!("rx {:02x}", &buf[..(len as usize).min(36)]); + trace!("rx {:02x}", &buf[..(len as usize).min(48)]); self.rx(&buf[..len as usize]); } @@ -564,7 +598,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); - + trace!("rx {:?}", sdpcm_header); if sdpcm_header.len != !sdpcm_header.len_inv { warn!("len inv mismatch"); return; @@ -587,15 +621,21 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); + trace!(" {:?}", cdc_header); - if cdc_header.id == self.state.ioctl_id.get() { - assert_eq!(cdc_header.status, 0); // todo propagate error - self.state.ioctl_state.set(IoctlState::Done); + if let IoctlState::Sent { buf } = self.state.ioctl_state.get() { + if cdc_header.id == self.state.ioctl_id.get() { + assert_eq!(cdc_header.status, 0); // todo propagate error instead + + let resp_len = cdc_header.len as usize; + (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); + self.state.ioctl_state.set(IoctlState::Done { resp_len }); + } } } 1 => { let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); - //info!("{}", bcd_header); + trace!(" {:?}", bcd_header); let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; if packet_start > payload.len() { @@ -603,7 +643,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { return; } let packet = &payload[packet_start..]; - //info!("rx {:02x}", &packet[..(packet.len() as usize).min(36)]); + trace!(" {:02x}", &packet[..(packet.len() as usize).min(36)]); let mut evt = EventHeader::from_bytes(&packet[24..][..EventHeader::SIZE].try_into().unwrap()); evt.byteswap(); @@ -642,12 +682,13 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let cdc_header = CdcHeader { cmd: cmd, - out_len: data.len() as _, - in_len: 0, + len: data.len() as _, flags: kind as u16 | (iface as u16) << 12, id, status: 0, }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", cdc_header); buf[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); buf[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); @@ -655,7 +696,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let total_len = (total_len + 3) & !3; // round up to 4byte - //info!("tx {:02x}", &buf[..total_len.min(48)]); + trace!(" {:02x}", &buf[..total_len.min(48)]); let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); self.cs.set_low(); diff --git a/src/structs.rs b/src/structs.rs index beda9f364..91df616ad 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -42,8 +42,7 @@ impl_bytes!(SdpcmHeader); #[repr(C)] pub struct CdcHeader { pub cmd: u32, - pub out_len: u16, - pub in_len: u16, + pub len: u32, pub flags: u16, pub id: u16, pub status: u32, From e1fd7dfc40bbb1ccbab511fb1e0d7a1120ae68a0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Jul 2022 04:17:07 +0200 Subject: [PATCH 0008/1575] wpa2 join... still nothing. --- examples/rpi-pico-w/src/main.rs | 4 +-- src/lib.rs | 59 ++++++++++++++++++++++++++++++--- src/structs.rs | 10 ++++++ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index bef820d31..6d1614147 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -47,6 +47,6 @@ async fn main(spawner: Spawner, p: Peripherals) { control.init().await; - let ssid = "MikroTik-951589"; - control.join(ssid).await; + //control.join_open("MikroTik-951589").await; + control.join_wpa2("MikroTik-951589", "fasdfasdfasdf").await; } diff --git a/src/lib.rs b/src/lib.rs index 6a1b7970c..70fa7edee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -250,8 +250,8 @@ impl<'a> Control<'a> { info!("Configuring misc stuff..."); self.set_iovar_u32("bus:txglom", 0).await; - //self.set_iovar_u32("apsta", 1).await; - self.set_iovar("cur_etheraddr", &[02, 03, 04, 05, 06, 07]).await; + self.set_iovar_u32("apsta", 1).await; + //self.set_iovar("cur_etheraddr", &[02, 03, 04, 05, 06, 07]).await; let country = countries::WORLD_WIDE_XX; let country_info = CountryInfo { @@ -267,9 +267,13 @@ impl<'a> Control<'a> { self.ioctl_set_u32(64, 0, 0).await; // WLC_SET_ANTDIV self.set_iovar_u32("bus:txglom", 0).await; - //self.set_iovar_u32("apsta", 1).await; + Timer::after(Duration::from_millis(100)).await; + //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? + Timer::after(Duration::from_millis(100)).await; self.set_iovar_u32("ampdu_ba_wsize", 8).await; + Timer::after(Duration::from_millis(100)).await; self.set_iovar_u32("ampdu_mpdu", 4).await; + Timer::after(Duration::from_millis(100)).await; //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes Timer::after(Duration::from_millis(100)).await; @@ -281,12 +285,20 @@ impl<'a> Control<'a> { }; self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; + Timer::after(Duration::from_millis(100)).await; + // set wifi up self.ioctl(2, 2, 0, &mut []).await; Timer::after(Duration::from_millis(100)).await; - self.ioctl_set_u32(86, 0, 0).await; // no power save + // power save mode 2 + self.set_iovar_u32("pm2_sleep_ret", 0xc8).await; + self.set_iovar_u32("bcn_li_bcn", 1).await; + self.set_iovar_u32("bcn_li_dtim", 1).await; + self.set_iovar_u32("assoc_listen", 10).await; + self.ioctl_set_u32(0x86, 0, 2).await; + self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any @@ -295,7 +307,9 @@ impl<'a> Control<'a> { info!("INIT DONE"); } - pub async fn join(&mut self, ssid: &str) { + pub async fn join_open(&mut self, ssid: &str) { + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + self.ioctl_set_u32(134, 0, 0).await; // wsec = open self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 @@ -311,6 +325,38 @@ impl<'a> Control<'a> { info!("JOINED"); } + pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) { + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + + self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 + self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; + self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; + self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; + + Timer::after(Duration::from_millis(100)).await; + + let mut pfi = PassphraseInfo { + len: passphrase.len() as _, + flags: 1, + passphrase: [0; 64], + }; + pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); + self.ioctl(2, 268, 0, &mut pfi.to_bytes()).await; // WLC_SET_WSEC_PMK + + self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 + self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) + self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth + + let mut i = SsidInfo { + len: ssid.len() as _, + ssid: [0; 32], + }; + i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + self.ioctl(2, 26, 0, &mut i.to_bytes()).await; // set_ssid + + info!("JOINED"); + } + async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { let mut buf = [0; 8]; buf[0..4].copy_from_slice(&val1.to_le_bytes()); @@ -362,6 +408,9 @@ impl<'a> Control<'a> { async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { // TODO cancel ioctl on future drop. + // snail mode 🐌 + Timer::after(Duration::from_millis(100)).await; + while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) { yield_now().await; } diff --git a/src/structs.rs b/src/structs.rs index 91df616ad..dd2c0cfe9 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -139,6 +139,16 @@ pub struct SsidInfo { } impl_bytes!(SsidInfo); +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct PassphraseInfo { + pub len: u16, + pub flags: u16, + pub passphrase: [u8; 64], +} +impl_bytes!(PassphraseInfo); + #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] From f60407feb3c2b2f3a364bd075dee2840220a5314 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Jul 2022 05:06:29 +0200 Subject: [PATCH 0009/1575] ITS DOING SOMETHING --- src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 70fa7edee..e0c2c9312 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -593,6 +593,12 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0); let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP); + // start HT clock + //self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10); + //info!("waiting for HT clock..."); + //while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x80 == 0 {} + //info!("clock ok"); + info!("init done "); } @@ -1084,18 +1090,18 @@ static NVRAM: &'static [u8] = &*nvram!( b"boardtype=0x0887", b"boardrev=0x1100", b"boardnum=22", - b"macaddr=00:A0:50:86:aa:b6", + b"macaddr=00:A0:50:b5:59:5e", b"sromrev=11", b"boardflags=0x00404001", b"boardflags3=0x04000000", - b"xtalfreq=26000", + b"xtalfreq=37400", b"nocrc=1", b"ag0=255", b"aa2g=1", b"ccode=ALL", b"pa0itssit=0x20", b"extpagain2g=0", - b"pa2ga0=-168,7161,-820", + b"pa2ga0=-168,6649,-778", b"AvVmid_c0=0x0,0xc8", b"cckpwroffset0=5", b"maxp2ga0=84", @@ -1118,7 +1124,7 @@ static NVRAM: &'static [u8] = &*nvram!( b"il0macaddr=00:90:4c:c5:12:38", b"wl0id=0x431b", b"deadman_to=0xffffffff", - b"muxenab=0x1", + b"muxenab=0x100", b"spurconfig=0x3", b"glitch_based_crsmin=1", b"btc_mode=1", From ce7353fba4e2a133087f0312ae47184aa180642e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Jul 2022 07:52:16 +0200 Subject: [PATCH 0010/1575] Hook up embassy-net. IT WORKS. --- Cargo.toml | 1 + examples/rpi-pico-w/Cargo.toml | 9 ++ examples/rpi-pico-w/src/main.rs | 78 ++++++++++++++- src/lib.rs | 168 ++++++++++++++++++++++++++++++-- src/structs.rs | 9 ++ 5 files changed, 255 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd27a48b8..31a14f96f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ log = ["dep:log"] [dependencies] embassy = { version = "0.1.0" } embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac"] } +embassy-net = { version = "0.1.0" } atomic-polyfill = "0.1.5" defmt = { version = "0.3", optional = true } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 8dbcb20d4..9e1d75470 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" cyw43 = { path = "../../", features = ["defmt"]} embassy = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } atomic-polyfill = "0.1.5" defmt = "0.3" @@ -20,13 +21,21 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +heapless = "0.7.15" [patch.crates-io] embassy = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } #embassy = { path = "/home/dirbaio/embassy/embassy/embassy" } #embassy-rp = { path = "/home/dirbaio/embassy/embassy/embassy-rp" } +#embassy-net = { path = "/home/dirbaio/embassy/embassy/embassy-net" } +#smoltcp = { path = "./smoltcp" } + +#[patch."https://github.com/smoltcp-rs/smoltcp"] +#smoltcp = { path = "./smoltcp" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 6d1614147..e08ee8e95 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -8,9 +8,13 @@ use defmt::{assert, assert_eq, panic, *}; use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; use embassy::util::Forever; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output, Pin}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embassy_rp::Peripherals; +use embedded_io::asynch::{Read, Write}; +use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; macro_rules! forever { @@ -26,6 +30,11 @@ async fn wifi_task(runner: cyw43::Runner<'static, PIN_23, PIN_25, PIN_29, PIN_24 runner.run().await } +#[embassy::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + #[embassy::main] async fn main(spawner: Spawner, p: Peripherals) { info!("Hello World!"); @@ -45,8 +54,71 @@ async fn main(spawner: Spawner, p: Peripherals) { spawner.spawn(wifi_task(runner)).unwrap(); - control.init().await; + let net_device = control.init().await; - //control.join_open("MikroTik-951589").await; - control.join_wpa2("MikroTik-951589", "fasdfasdfasdf").await; + control.join_open("MikroTik-951589").await; + //control.join_wpa2("MikroTik-951589", "asdfasdfasdfasdf").await; + + let config = embassy_net::ConfigStrategy::Dhcp; + //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), + //}); + + // Generate random seed + let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. + + // Init network stack + let stack = &*forever!(Stack::new( + net_device, + config, + forever!(StackResources::<1, 2, 8>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } } diff --git a/src/lib.rs b/src/lib.rs index e0c2c9312..dde9d9c34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,12 +12,19 @@ mod structs; use core::cell::Cell; use core::slice; +use core::sync::atomic::Ordering; +use core::task::Waker; +use atomic_polyfill::AtomicBool; +use embassy::blocking_mutex::raw::NoopRawMutex; +use embassy::channel::mpmc::Channel; use embassy::time::{block_for, Duration, Timer}; use embassy::util::yield_now; +use embassy_net::{PacketBoxExt, PacketBuf}; use embassy_rp::gpio::{Flex, Output, Pin}; use self::structs::*; +use crate::events::Event; fn swap16(x: u32) -> u32 { (x & 0xFF00FF00) >> 8 | (x & 0x00FF00FF) << 8 @@ -197,6 +204,10 @@ enum IoctlState { pub struct State { ioctl_id: Cell, ioctl_state: Cell, + + tx_channel: Channel, + rx_channel: Channel, + link_up: AtomicBool, } impl State { @@ -204,6 +215,10 @@ impl State { Self { ioctl_id: Cell::new(0), ioctl_state: Cell::new(IoctlState::Idle), + + tx_channel: Channel::new(), + rx_channel: Channel::new(), + link_up: AtomicBool::new(true), // TODO set up/down as we join/deassociate } } } @@ -213,7 +228,7 @@ pub struct Control<'a> { } impl<'a> Control<'a> { - pub async fn init(&mut self) { + pub async fn init(&mut self) -> NetDevice<'a> { const CHUNK_SIZE: usize = 1024; let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; @@ -253,6 +268,15 @@ impl<'a> Control<'a> { self.set_iovar_u32("apsta", 1).await; //self.set_iovar("cur_etheraddr", &[02, 03, 04, 05, 06, 07]).await; + // read MAC addr. + let mut mac_addr = [0; 6]; + assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + info!("mac addr: {:02x}", mac_addr); + + // TODO get_iovar is broken, it returns all zeros. + // Harcdode our own MAC for now. + let mac_addr = [0x28, 0xCD, 0xC1, 0x00, 0x3F, 0x05]; + let country = countries::WORLD_WIDE_XX; let country_info = CountryInfo { country_abbrev: [country.code[0], country.code[1], 0, 0], @@ -283,6 +307,15 @@ impl<'a> Control<'a> { iface: 0, events: [0xFF; 24], }; + + // Disable spammy uninteresting events. + evts.unset(Event::RADIO); + evts.unset(Event::IF); + evts.unset(Event::PROBREQ_MSG); + evts.unset(Event::PROBREQ_MSG_RX); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::PROBRESP_MSG); + self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; Timer::after(Duration::from_millis(100)).await; @@ -305,6 +338,11 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; info!("INIT DONE"); + + NetDevice { + state: self.state, + mac_addr, + } } pub async fn join_open(&mut self, ssid: &str) { @@ -387,6 +425,7 @@ impl<'a> Control<'a> { self.ioctl(2, 263, 0, &mut buf).await; } + // TODO this is not really working, it always returns all zeros. async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { info!("get {}", name); @@ -395,7 +434,7 @@ impl<'a> Control<'a> { buf[name.len()] = 0; let total_len = name.len() + 1 + res.len(); - let res_len = self.ioctl(2, 262, 0, &mut buf[..total_len]).await - name.len() - 1; + let res_len = self.ioctl(0, 262, 0, &mut buf[..total_len]).await - name.len() - 1; res[..res_len].copy_from_slice(&buf[name.len() + 1..][..res_len]); res_len } @@ -408,9 +447,6 @@ impl<'a> Control<'a> { async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { // TODO cancel ioctl on future drop. - // snail mode 🐌 - Timer::after(Duration::from_millis(100)).await; - while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) { yield_now().await; } @@ -434,6 +470,50 @@ impl<'a> Control<'a> { } } +pub struct NetDevice<'a> { + state: &'a State, + mac_addr: [u8; 6], +} + +impl<'a> embassy_net::Device for NetDevice<'a> { + fn register_waker(&mut self, waker: &Waker) { + // loopy loopy wakey wakey + waker.wake_by_ref() + } + + fn link_state(&mut self) -> embassy_net::LinkState { + match self.state.link_up.load(Ordering::Relaxed) { + true => embassy_net::LinkState::Up, + false => embassy_net::LinkState::Down, + } + } + + fn capabilities(&self) -> embassy_net::DeviceCapabilities { + let mut caps = embassy_net::DeviceCapabilities::default(); + caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header + caps.medium = embassy_net::Medium::Ethernet; + caps + } + + fn is_transmit_ready(&mut self) -> bool { + true + } + + fn transmit(&mut self, pkt: PacketBuf) { + if self.state.tx_channel.try_send(pkt).is_err() { + warn!("TX failed") + } + } + + fn receive(&mut self) -> Option { + self.state.rx_channel.try_recv().ok() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } +} + pub struct Runner<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { state: &'a State, @@ -576,7 +656,10 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { while self.read32(FUNC_BUS, REG_BUS_STATUS) & STATUS_F2_RX_READY == 0 {} // Some random configs related to sleep. - // I think they're not needed if we don't want sleep...??? + // These aren't needed if we don't want to sleep the bus. + // TODO do we need to sleep the bus to read the irq line, due to + // being on the same pin as MOSI/MISO? + /* let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL); val |= 0x02; // WAKE_TILL_HT_AVAIL @@ -606,11 +689,16 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let mut buf = [0; 2048]; loop { // Send stuff + // TODO flow control if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get()); self.state.ioctl_state.set(IoctlState::Sent { buf }); } + if let Ok(p) = self.state.tx_channel.try_recv() { + self.send_packet(&p); + } + // Receive stuff let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT); @@ -646,6 +734,52 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } } + fn send_packet(&mut self, packet: &[u8]) { + trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); + + let mut buf = [0; 2048]; + + let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); + + let seq = self.ioctl_seq; + self.ioctl_seq = self.ioctl_seq.wrapping_add(1); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, // TODO does this len need to be rounded up to u32? + len_inv: !total_len as u16, + sequence: seq, + channel_and_flags: 2, // data channel + next_length: 0, + header_length: SdpcmHeader::SIZE as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let bcd_header = BcdHeader { + flags: 0x20, + priority: 0, + flags2: 0, + data_offset: 0, + }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", bcd_header); + + buf[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); + buf[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); + + let total_len = (total_len + 3) & !3; // round up to 4byte + + trace!(" {:02x}", &buf[..total_len.min(48)]); + + let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); + self.cs.set_low(); + self.spi_write(&cmd.to_le_bytes()); + self.spi_write(&buf[..total_len]); + self.cs.set_high(); + } + fn rx(&mut self, packet: &[u8]) { if packet.len() < SdpcmHeader::SIZE { warn!("packet too short, len={}", packet.len()); @@ -683,6 +817,8 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { assert_eq!(cdc_header.status, 0); // todo propagate error instead let resp_len = cdc_header.len as usize; + info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); + (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); self.state.ioctl_state.set(IoctlState::Done { resp_len }); } @@ -703,14 +839,32 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let mut evt = EventHeader::from_bytes(&packet[24..][..EventHeader::SIZE].try_into().unwrap()); evt.byteswap(); let evt_data = &packet[24 + EventHeader::SIZE..][..evt.datalen as usize]; - info!( + debug!( "=== EVENT {}: {} {:02x}", events::Event::from(evt.event_type as u8), evt, evt_data ); } + 2 => { + let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); + trace!(" {:?}", bcd_header); + let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; + if packet_start > payload.len() { + warn!("packet start out of range."); + return; + } + let packet = &payload[packet_start..]; + trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); + + let mut p = unwrap!(embassy_net::PacketBox::new(embassy_net::Packet::new())); + p[..packet.len()].copy_from_slice(packet); + + if let Err(_) = self.state.rx_channel.try_send(p.slice(0..packet.len())) { + warn!("failed to push rxd packet to the channel.") + } + } _ => {} } } diff --git a/src/structs.rs b/src/structs.rs index dd2c0cfe9..060c2b060 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,3 +1,5 @@ +use crate::events::Event; + macro_rules! impl_bytes { ($t:ident) => { impl $t { @@ -157,3 +159,10 @@ pub struct EventMask { pub events: [u8; 24], } impl_bytes!(EventMask); + +impl EventMask { + pub fn unset(&mut self, evt: Event) { + let evt = evt as u8 as usize; + self.events[evt / 8] &= !(1 << (evt % 8)); + } +} From 31410aa5b7d570184b0abcb78d04654d939660db Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 13 Jul 2022 21:22:52 +0200 Subject: [PATCH 0011/1575] update rust nightly to match embassy. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 03da4cf73..0fa7cf7bf 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-07-09" +channel = "nightly-2022-07-13" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", From 7dfdea87971bf7a951b2b1d3fc2e50e245005d07 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 15 Jul 2022 18:33:32 +0200 Subject: [PATCH 0012/1575] Switch to embedded-hal SPI, GPIO traits. --- .vscode/settings.json | 2 +- Cargo.toml | 3 +- examples/rpi-pico-w/src/main.rs | 125 ++++++-- src/lib.rs | 490 +++++++++++++++++--------------- 4 files changed, 364 insertions(+), 256 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index cc926d04f..748816bb9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,7 +8,7 @@ "rust-analyzer.imports.granularity.enforce": true, "rust-analyzer.imports.granularity.group": "module", "rust-analyzer.procMacro.attributes.enable": false, - "rust-analyzer.procMacro.enable": true, + "rust-analyzer.procMacro.enable": false, "rust-analyzer.linkedProjects": [ "examples/rpi-pico-w/Cargo.toml", ], diff --git a/Cargo.toml b/Cargo.toml index 31a14f96f..d35e865bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,10 @@ version = "0.1.0" edition = "2021" [features] -defmt = ["dep:defmt", "embassy-rp/defmt", "embassy/defmt"] +defmt = ["dep:defmt", "embassy/defmt"] log = ["dep:log"] [dependencies] embassy = { version = "0.1.0" } -embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac"] } embassy-net = { version = "0.1.0" } atomic-polyfill = "0.1.5" diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index e08ee8e95..655535f9d 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -1,8 +1,9 @@ #![no_std] #![no_main] -#![feature(type_alias_impl_trait, concat_bytes)] +#![feature(generic_associated_types, type_alias_impl_trait)] -use core::slice; +use core::convert::Infallible; +use core::future::Future; use defmt::{assert, assert_eq, panic, *}; use embassy::executor::Spawner; @@ -13,8 +14,9 @@ use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output, Pin}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embassy_rp::Peripherals; +use embedded_hal_1::spi::ErrorType; +use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite}; use embedded_io::asynch::{Read, Write}; -use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; macro_rules! forever { @@ -26,7 +28,9 @@ macro_rules! forever { } #[embassy::task] -async fn wifi_task(runner: cyw43::Runner<'static, PIN_23, PIN_25, PIN_29, PIN_24>) -> ! { +async fn wifi_task( + runner: cyw43::Runner<'static, Output<'static, PIN_23>, ExclusiveDevice>>, +) -> ! { runner.run().await } @@ -39,25 +43,25 @@ async fn net_task(stack: &'static Stack>) -> ! { async fn main(spawner: Spawner, p: Peripherals) { info!("Hello World!"); - let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_25, p.PIN_29, p.PIN_24); - //let (pwr, cs, clk, dio) = (p.PIN_23, p.PIN_0, p.PIN_1, p.PIN_2); + let pwr = Output::new(p.PIN_23, Level::Low); + let cs = Output::new(p.PIN_25, Level::High); + let clk = Output::new(p.PIN_29, Level::Low); + let mut dio = Flex::new(p.PIN_24); + dio.set_low(); + dio.set_as_output(); + + let bus = MySpi { clk, dio }; + let spi = ExclusiveDevice::new(bus, cs); let state = forever!(cyw43::State::new()); - let (mut control, runner) = cyw43::new( - state, - Output::new(pwr, Level::Low), - Output::new(cs, Level::High), - Output::new(clk, Level::Low), - Flex::new(dio), - ) - .await; + let (mut control, runner) = cyw43::new(state, pwr, spi).await; spawner.spawn(wifi_task(runner)).unwrap(); let net_device = control.init().await; - control.join_open("MikroTik-951589").await; - //control.join_wpa2("MikroTik-951589", "asdfasdfasdfasdf").await; + //control.join_open("MikroTik-951589").await; + control.join_wpa2("DirbaioWifi", "HelloWorld").await; let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -122,3 +126,92 @@ async fn main(spawner: Spawner, p: Peripherals) { } } } + +struct MySpi { + /// SPI clock + clk: Output<'static, PIN_29>, + + /// 4 signals, all in one!! + /// - SPI MISO + /// - SPI MOSI + /// - IRQ + /// - strap to set to gSPI mode on boot. + dio: Flex<'static, PIN_24>, +} + +impl ErrorType for MySpi { + type Error = Infallible; +} + +impl SpiBusFlush for MySpi { + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } + } +} + +impl SpiBusRead for MySpi { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> { + async move { + self.dio.set_as_input(); + for word in words { + let mut w = 0; + for _ in 0..8 { + w = w << 1; + + // rising edge, sample data + if self.dio.is_high() { + w |= 0x01; + } + self.clk.set_high(); + + // falling edge + self.clk.set_low(); + } + *word = w + } + + Ok(()) + } + } +} + +impl SpiBusWrite for MySpi { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, words: &'a [u8]) -> Self::WriteFuture<'a> { + async move { + self.dio.set_as_output(); + for word in words { + let mut word = *word; + for _ in 0..8 { + // falling edge, setup data + self.clk.set_low(); + if word & 0x80 == 0 { + self.dio.set_low(); + } else { + self.dio.set_high(); + } + + // rising edge + self.clk.set_high(); + + word = word << 1; + } + } + self.clk.set_low(); + + self.dio.set_as_input(); + Ok(()) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index dde9d9c34..fb693c323 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] #![no_main] -#![feature(type_alias_impl_trait, concat_bytes, const_slice_from_raw_parts)] +#![feature(type_alias_impl_trait, concat_bytes)] #![deny(unused_must_use)] // This mod MUST go first, so that the others see its macros. @@ -21,7 +21,8 @@ use embassy::channel::mpmc::Channel; use embassy::time::{block_for, Duration, Timer}; use embassy::util::yield_now; use embassy_net::{PacketBoxExt, PacketBuf}; -use embassy_rp::gpio::{Flex, Output, Pin}; +use embedded_hal_1::digital::blocking::OutputPin; +use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; use self::structs::*; use crate::events::Event; @@ -514,41 +515,26 @@ impl<'a> embassy_net::Device for NetDevice<'a> { } } -pub struct Runner<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> { +pub struct Runner<'a, PWR, SPI> { state: &'a State, - pwr: Output<'a, PWR>, - - /// SPI chip-select. - cs: Output<'a, CS>, - - /// SPI clock - clk: Output<'a, CLK>, - - /// 4 signals, all in one!! - /// - SPI MISO - /// - SPI MOSI - /// - IRQ - /// - strap to set to gSPI mode on boot. - dio: Flex<'a, DIO>, + pwr: PWR, + spi: SPI, ioctl_seq: u8, backplane_window: u32, } -pub async fn new<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin>( - state: &'a State, - pwr: Output<'a, PWR>, - cs: Output<'a, CS>, - clk: Output<'a, CLK>, - dio: Flex<'a, DIO>, -) -> (Control<'a>, Runner<'a, PWR, CS, CLK, DIO>) { +pub async fn new<'a, PWR, SPI>(state: &'a State, pwr: PWR, spi: SPI) -> (Control<'a>, Runner<'a, PWR, SPI>) +where + PWR: OutputPin, + SPI: SpiDevice, + SPI::Bus: SpiBusRead + SpiBusWrite, +{ let mut runner = Runner { state, pwr, - cs, - clk, - dio, + spi, ioctl_seq: 0, backplane_window: 0xAAAA_AAAA, @@ -559,52 +545,53 @@ pub async fn new<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin>( (Control { state }, runner) } -impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { +impl<'a, PWR, SPI> Runner<'a, PWR, SPI> +where + PWR: OutputPin, + SPI: SpiDevice, + SPI::Bus: SpiBusRead + SpiBusWrite, +{ async fn init(&mut self) { - // Set strap to select gSPI mode. - self.dio.set_as_output(); - self.dio.set_low(); - // Reset - self.pwr.set_low(); + self.pwr.set_low().unwrap(); Timer::after(Duration::from_millis(20)).await; - self.pwr.set_high(); + self.pwr.set_high().unwrap(); Timer::after(Duration::from_millis(250)).await; info!("waiting for ping..."); - while self.read32_swapped(REG_BUS_FEEDBEAD) != FEEDBEAD {} + while self.read32_swapped(REG_BUS_FEEDBEAD).await != FEEDBEAD {} info!("ping ok"); - self.write32_swapped(0x18, TEST_PATTERN); - let val = self.read32_swapped(REG_BUS_TEST); + self.write32_swapped(0x18, TEST_PATTERN).await; + let val = self.read32_swapped(REG_BUS_TEST).await; assert_eq!(val, TEST_PATTERN); // 32bit, big endian. - self.write32_swapped(REG_BUS_CTRL, 0x00010033); + self.write32_swapped(REG_BUS_CTRL, 0x00010033).await; - let val = self.read32(FUNC_BUS, REG_BUS_FEEDBEAD); + let val = self.read32(FUNC_BUS, REG_BUS_FEEDBEAD).await; assert_eq!(val, FEEDBEAD); - let val = self.read32(FUNC_BUS, REG_BUS_TEST); + let val = self.read32(FUNC_BUS, REG_BUS_TEST).await; assert_eq!(val, TEST_PATTERN); // No response delay in any of the funcs. // seems to break backplane??? eat the 4-byte delay instead, that's what the vendor drivers do... - //self.write32(FUNC_BUS, REG_BUS_RESP_DELAY, 0); + //self.write32(FUNC_BUS, REG_BUS_RESP_DELAY, 0).await; // Init ALP (no idea what that stands for) clock - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x08); + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x08).await; info!("waiting for clock..."); - while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x40 == 0 {} + while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x40 == 0 {} info!("clock ok"); - let chip_id = self.bp_read16(0x1800_0000); + let chip_id = self.bp_read16(0x1800_0000).await; info!("chip ID: {}", chip_id); // Upload firmware. - self.core_disable(Core::WLAN); - self.core_reset(Core::SOCSRAM); - self.bp_write32(CHIP.socsram_base_address + 0x10, 3); - self.bp_write32(CHIP.socsram_base_address + 0x44, 0); + self.core_disable(Core::WLAN).await; + self.core_reset(Core::SOCSRAM).await; + self.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; + self.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; let ram_addr = CHIP.atcm_ram_base_address; @@ -618,42 +605,44 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let fw = unsafe { slice::from_raw_parts(0x10100000 as *const u8, 224190) }; info!("loading fw"); - self.bp_write(ram_addr, fw); + self.bp_write(ram_addr, fw).await; info!("verifying fw"); let mut buf = [0; 1024]; for (i, chunk) in fw.chunks(1024).enumerate() { let buf = &mut buf[..chunk.len()]; - self.bp_read(ram_addr + i as u32 * 1024, buf); + self.bp_read(ram_addr + i as u32 * 1024, buf).await; assert_eq!(chunk, buf); } info!("loading nvram"); // Round up to 4 bytes. let nvram_len = (NVRAM.len() + 3) / 4 * 4; - self.bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM); + self.bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) + .await; let nvram_len_words = nvram_len as u32 / 4; let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; - self.bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic); + self.bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) + .await; // Start core! info!("starting up core..."); - self.core_reset(Core::WLAN); - assert!(self.core_is_up(Core::WLAN)); + self.core_reset(Core::WLAN).await; + assert!(self.core_is_up(Core::WLAN).await); - while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x80 == 0 {} + while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} // "Set up the interrupt mask and enable interrupts" - self.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0); + self.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." // Sounds scary... - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32); + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32).await; // wait for wifi startup info!("waiting for wifi init..."); - while self.read32(FUNC_BUS, REG_BUS_STATUS) & STATUS_F2_RX_READY == 0 {} + while self.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} // Some random configs related to sleep. // These aren't needed if we don't want to sleep the bus. @@ -661,25 +650,25 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { // being on the same pin as MOSI/MISO? /* - let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL); + let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; val |= 0x02; // WAKE_TILL_HT_AVAIL - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val); - self.write8(FUNC_BUS, 0xF0, 0x08); // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02); // SBSDIO_FORCE_HT + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; + self.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT - let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR); + let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val); + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; */ // clear pulls - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0); - let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP); + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; + let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; // start HT clock - //self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10); + //self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; //info!("waiting for HT clock..."); - //while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR) & 0x80 == 0 {} + //while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} //info!("clock ok"); info!("init done "); @@ -691,21 +680,22 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { // Send stuff // TODO flow control if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { - self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get()); + self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get()) + .await; self.state.ioctl_state.set(IoctlState::Sent { buf }); } if let Ok(p) = self.state.tx_channel.try_recv() { - self.send_packet(&p); + self.send_packet(&p).await; } // Receive stuff - let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT); + let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; if irq & IRQ_F2_PACKET_AVAILABLE != 0 { let mut status = 0xFFFF_FFFF; while status == 0xFFFF_FFFF { - status = self.read32(FUNC_BUS, REG_BUS_STATUS); + status = self.read32(FUNC_BUS, REG_BUS_STATUS).await; } if status & STATUS_F2_PKT_AVAILABLE != 0 { @@ -713,15 +703,23 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let cmd = cmd_word(false, true, FUNC_WLAN, 0, len); - self.cs.set_low(); - self.spi_write(&cmd.to_le_bytes()); - self.spi_read(&mut buf[..len as usize]); - // pad to 32bit - let mut junk = [0; 4]; - if len % 4 != 0 { - self.spi_read(&mut junk[..(4 - len as usize % 4)]); - } - self.cs.set_high(); + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&cmd.to_le_bytes()).await?; + bus.read(&mut buf[..len as usize]).await?; + // pad to 32bit + let mut junk = [0; 4]; + if len % 4 != 0 { + bus.read(&mut junk[..(4 - len as usize % 4)]).await?; + } + + Ok(()) + } + }) + .await + .unwrap(); trace!("rx {:02x}", &buf[..(len as usize).min(48)]); @@ -734,7 +732,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } } - fn send_packet(&mut self, packet: &[u8]) { + async fn send_packet(&mut self, packet: &[u8]) { trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); let mut buf = [0; 2048]; @@ -774,10 +772,17 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { trace!(" {:02x}", &buf[..total_len.min(48)]); let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); - self.cs.set_low(); - self.spi_write(&cmd.to_le_bytes()); - self.spi_write(&buf[..total_len]); - self.cs.set_high(); + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&cmd.to_le_bytes()).await?; + bus.write(&buf[..total_len]).await?; + Ok(()) + } + }) + .await + .unwrap(); } fn rx(&mut self, packet: &[u8]) { @@ -869,7 +874,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } } - fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8], id: u16) { + async fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8], id: u16) { let mut buf = [0; 2048]; let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); @@ -908,60 +913,69 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { trace!(" {:02x}", &buf[..total_len.min(48)]); let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); - self.cs.set_low(); - self.spi_write(&cmd.to_le_bytes()); - self.spi_write(&buf[..total_len]); - self.cs.set_high(); + + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&cmd.to_le_bytes()).await?; + bus.write(&buf[..total_len]).await?; + Ok(()) + } + }) + .await + .unwrap(); } - fn core_disable(&mut self, core: Core) { + async fn core_disable(&mut self, core: Core) { let base = core.base_addr(); // Dummy read? - let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET); + let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; // Check it isn't already reset - let r = self.bp_read8(base + AI_RESETCTRL_OFFSET); + let r = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; if r & AI_RESETCTRL_BIT_RESET != 0 { return; } - self.bp_write8(base + AI_IOCTRL_OFFSET, 0); - let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); + self.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; + let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await; block_for(Duration::from_millis(1)); - self.bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET); - let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET); + self.bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET).await; + let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; } - fn core_reset(&mut self, core: Core) { - self.core_disable(core); + async fn core_reset(&mut self, core: Core) { + self.core_disable(core).await; let base = core.base_addr(); - self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN); - let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); + self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) + .await; + let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await; - self.bp_write8(base + AI_RESETCTRL_OFFSET, 0); + self.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; - block_for(Duration::from_millis(1)); + Timer::after(Duration::from_millis(1)).await; - self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN); - let _ = self.bp_read8(base + AI_IOCTRL_OFFSET); + self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN).await; + let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await; - block_for(Duration::from_millis(1)); + Timer::after(Duration::from_millis(1)).await; } - fn core_is_up(&mut self, core: Core) -> bool { + async fn core_is_up(&mut self, core: Core) -> bool { let base = core.base_addr(); - let io = self.bp_read8(base + AI_IOCTRL_OFFSET); + let io = self.bp_read8(base + AI_IOCTRL_OFFSET).await; if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); return false; } - let r = self.bp_read8(base + AI_RESETCTRL_OFFSET); + let r = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; if r & (AI_RESETCTRL_BIT_RESET) != 0 { debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); return false; @@ -970,7 +984,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { true } - fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { + async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { // It seems the HW force-aligns the addr // to 2 if data.len() >= 2 // to 4 if data.len() >= 4 @@ -984,24 +998,32 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); - self.backplane_set_window(addr); + self.backplane_set_window(addr).await; let cmd = cmd_word(false, true, FUNC_BACKPLANE, window_offs, len as u32); - self.cs.set_low(); - self.spi_write(&cmd.to_le_bytes()); - // 4-byte response delay. - let mut junk = [0; 4]; - self.spi_read(&mut junk); + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&cmd.to_le_bytes()).await?; - // Read data - self.spi_read(&mut data[..len]); + // 4-byte response delay. + let mut junk = [0; 4]; + bus.read(&mut junk).await?; - // pad to 32bit - if len % 4 != 0 { - self.spi_read(&mut junk[..(4 - len % 4)]); - } - self.cs.set_high(); + // Read data + bus.read(&mut data[..len]).await?; + + // pad to 32bit + if len % 4 != 0 { + bus.read(&mut junk[..(4 - len % 4)]).await?; + } + Ok(()) + } + }) + .await + .unwrap(); // Advance ptr. addr += len as u32; @@ -1009,7 +1031,7 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } } - fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { + async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { // It seems the HW force-aligns the addr // to 2 if data.len() >= 2 // to 4 if data.len() >= 4 @@ -1023,18 +1045,26 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); - self.backplane_set_window(addr); + self.backplane_set_window(addr).await; let cmd = cmd_word(true, true, FUNC_BACKPLANE, window_offs, len as u32); - self.cs.set_low(); - self.spi_write(&cmd.to_le_bytes()); - self.spi_write(&data[..len]); - // pad to 32bit - if len % 4 != 0 { - let zeros = [0; 4]; - self.spi_write(&zeros[..(4 - len % 4)]); - } - self.cs.set_high(); + + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&cmd.to_le_bytes()).await?; + bus.write(&data[..len]).await?; + // pad to 32bit + if len % 4 != 0 { + let zeros = [0; 4]; + bus.write(&zeros[..(4 - len % 4)]).await?; + } + Ok(()) + } + }) + .await + .unwrap(); // Advance ptr. addr += len as u32; @@ -1042,51 +1072,51 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { } } - fn bp_read8(&mut self, addr: u32) -> u8 { - self.backplane_readn(addr, 1) as u8 + async fn bp_read8(&mut self, addr: u32) -> u8 { + self.backplane_readn(addr, 1).await as u8 } - fn bp_write8(&mut self, addr: u32, val: u8) { - self.backplane_writen(addr, val as u32, 1) + async fn bp_write8(&mut self, addr: u32, val: u8) { + self.backplane_writen(addr, val as u32, 1).await } - fn bp_read16(&mut self, addr: u32) -> u16 { - self.backplane_readn(addr, 2) as u16 + async fn bp_read16(&mut self, addr: u32) -> u16 { + self.backplane_readn(addr, 2).await as u16 } - fn bp_write16(&mut self, addr: u32, val: u16) { - self.backplane_writen(addr, val as u32, 2) + async fn bp_write16(&mut self, addr: u32, val: u16) { + self.backplane_writen(addr, val as u32, 2).await } - fn bp_read32(&mut self, addr: u32) -> u32 { - self.backplane_readn(addr, 4) + async fn bp_read32(&mut self, addr: u32) -> u32 { + self.backplane_readn(addr, 4).await } - fn bp_write32(&mut self, addr: u32, val: u32) { - self.backplane_writen(addr, val, 4) + async fn bp_write32(&mut self, addr: u32, val: u32) { + self.backplane_writen(addr, val, 4).await } - fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 { - self.backplane_set_window(addr); + async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 { + self.backplane_set_window(addr).await; let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; if len == 4 { bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG } - self.readn(FUNC_BACKPLANE, bus_addr, len) + self.readn(FUNC_BACKPLANE, bus_addr, len).await } - fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) { - self.backplane_set_window(addr); + async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) { + self.backplane_set_window(addr).await; let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; if len == 4 { bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG } - self.writen(FUNC_BACKPLANE, bus_addr, val, len) + self.writen(FUNC_BACKPLANE, bus_addr, val, len).await } - fn backplane_set_window(&mut self, addr: u32) { + async fn backplane_set_window(&mut self, addr: u32) { let new_window = addr & !BACKPLANE_ADDRESS_MASK; if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 { @@ -1094,138 +1124,124 @@ impl<'a, PWR: Pin, CS: Pin, CLK: Pin, DIO: Pin> Runner<'a, PWR, CS, CLK, DIO> { FUNC_BACKPLANE, REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH, (new_window >> 24) as u8, - ); + ) + .await; } if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 { self.write8( FUNC_BACKPLANE, REG_BACKPLANE_BACKPLANE_ADDRESS_MID, (new_window >> 16) as u8, - ); + ) + .await; } if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 { self.write8( FUNC_BACKPLANE, REG_BACKPLANE_BACKPLANE_ADDRESS_LOW, (new_window >> 8) as u8, - ); + ) + .await; } self.backplane_window = new_window; } - fn read8(&mut self, func: u32, addr: u32) -> u8 { - self.readn(func, addr, 1) as u8 + async fn read8(&mut self, func: u32, addr: u32) -> u8 { + self.readn(func, addr, 1).await as u8 } - fn write8(&mut self, func: u32, addr: u32, val: u8) { - self.writen(func, addr, val as u32, 1) + async fn write8(&mut self, func: u32, addr: u32, val: u8) { + self.writen(func, addr, val as u32, 1).await } - fn read16(&mut self, func: u32, addr: u32) -> u16 { - self.readn(func, addr, 2) as u16 + async fn read16(&mut self, func: u32, addr: u32) -> u16 { + self.readn(func, addr, 2).await as u16 } - fn write16(&mut self, func: u32, addr: u32, val: u16) { - self.writen(func, addr, val as u32, 2) + async fn write16(&mut self, func: u32, addr: u32, val: u16) { + self.writen(func, addr, val as u32, 2).await } - fn read32(&mut self, func: u32, addr: u32) -> u32 { - self.readn(func, addr, 4) + async fn read32(&mut self, func: u32, addr: u32) -> u32 { + self.readn(func, addr, 4).await } - fn write32(&mut self, func: u32, addr: u32, val: u32) { - self.writen(func, addr, val, 4) + async fn write32(&mut self, func: u32, addr: u32, val: u32) { + self.writen(func, addr, val, 4).await } - fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { + async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { let cmd = cmd_word(false, true, func, addr, len); let mut buf = [0; 4]; - self.cs.set_low(); - self.spi_write(&cmd.to_le_bytes()); - if func == FUNC_BACKPLANE { - // 4-byte response delay. - self.spi_read(&mut buf); - } - self.spi_read(&mut buf); - self.cs.set_high(); + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&cmd.to_le_bytes()).await?; + if func == FUNC_BACKPLANE { + // 4-byte response delay. + bus.read(&mut buf).await?; + } + bus.read(&mut buf).await?; + Ok(()) + } + }) + .await + .unwrap(); u32::from_le_bytes(buf) } - fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { + async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { let cmd = cmd_word(true, true, func, addr, len); - self.cs.set_low(); - self.spi_write(&cmd.to_le_bytes()); - self.spi_write(&val.to_le_bytes()); - self.cs.set_high(); + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&cmd.to_le_bytes()).await?; + bus.write(&val.to_le_bytes()).await?; + Ok(()) + } + }) + .await + .unwrap(); } - fn read32_swapped(&mut self, addr: u32) -> u32 { + async fn read32_swapped(&mut self, addr: u32) -> u32 { let cmd = cmd_word(false, true, FUNC_BUS, addr, 4); let mut buf = [0; 4]; - self.cs.set_low(); - self.spi_write(&swap16(cmd).to_le_bytes()); - self.spi_read(&mut buf); - self.cs.set_high(); + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&swap16(cmd).to_le_bytes()).await?; + bus.read(&mut buf).await?; + Ok(()) + } + }) + .await + .unwrap(); swap16(u32::from_le_bytes(buf)) } - fn write32_swapped(&mut self, addr: u32, val: u32) { + async fn write32_swapped(&mut self, addr: u32, val: u32) { let cmd = cmd_word(true, true, FUNC_BUS, addr, 4); - self.cs.set_low(); - self.spi_write(&swap16(cmd).to_le_bytes()); - self.spi_write(&swap16(val).to_le_bytes()); - self.cs.set_high(); - } - - fn spi_read(&mut self, words: &mut [u8]) { - self.dio.set_as_input(); - for word in words { - let mut w = 0; - for _ in 0..8 { - w = w << 1; - - // rising edge, sample data - if self.dio.is_high() { - w |= 0x01; + self.spi + .transaction(|bus| { + let bus = unsafe { &mut *bus }; + async { + bus.write(&swap16(cmd).to_le_bytes()).await?; + bus.write(&swap16(val).to_le_bytes()).await?; + Ok(()) } - self.clk.set_high(); - - // falling edge - self.clk.set_low(); - } - *word = w - } - self.clk.set_low(); - } - - fn spi_write(&mut self, words: &[u8]) { - self.dio.set_as_output(); - for word in words { - let mut word = *word; - for _ in 0..8 { - // falling edge, setup data - self.clk.set_low(); - if word & 0x80 == 0 { - self.dio.set_low(); - } else { - self.dio.set_high(); - } - - // rising edge - self.clk.set_high(); - - word = word << 1; - } - } - self.clk.set_low(); - - self.dio.set_as_input(); + }) + .await + .unwrap(); } } From 931e3d7ee0cb1ff9f320b22aab3f4ea375a1c624 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 16 Jul 2022 18:06:57 +0200 Subject: [PATCH 0013/1575] Switch to 32bit SPI. --- examples/rpi-pico-w/src/main.rs | 14 ++-- src/lib.rs | 112 ++++++++++++++------------------ 2 files changed, 55 insertions(+), 71 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 655535f9d..475c69067 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -153,17 +153,17 @@ impl SpiBusFlush for MySpi { } } -impl SpiBusRead for MySpi { +impl SpiBusRead for MySpi { type ReadFuture<'a> = impl Future> where Self: 'a; - fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> { + fn read<'a>(&'a mut self, words: &'a mut [u32]) -> Self::ReadFuture<'a> { async move { self.dio.set_as_input(); for word in words { let mut w = 0; - for _ in 0..8 { + for _ in 0..32 { w = w << 1; // rising edge, sample data @@ -183,20 +183,20 @@ impl SpiBusRead for MySpi { } } -impl SpiBusWrite for MySpi { +impl SpiBusWrite for MySpi { type WriteFuture<'a> = impl Future> where Self: 'a; - fn write<'a>(&'a mut self, words: &'a [u8]) -> Self::WriteFuture<'a> { + fn write<'a>(&'a mut self, words: &'a [u32]) -> Self::WriteFuture<'a> { async move { self.dio.set_as_output(); for word in words { let mut word = *word; - for _ in 0..8 { + for _ in 0..32 { // falling edge, setup data self.clk.set_low(); - if word & 0x80 == 0 { + if word & 0x8000_0000 == 0 { self.dio.set_low(); } else { self.dio.set_high(); diff --git a/src/lib.rs b/src/lib.rs index fb693c323..8c235b174 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,13 +28,18 @@ use self::structs::*; use crate::events::Event; fn swap16(x: u32) -> u32 { - (x & 0xFF00FF00) >> 8 | (x & 0x00FF00FF) << 8 + x.rotate_left(16) } fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) } +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +} + const FUNC_BUS: u32 = 0; const FUNC_BACKPLANE: u32 = 1; const FUNC_WLAN: u32 = 2; @@ -529,7 +534,7 @@ pub async fn new<'a, PWR, SPI>(state: &'a State, pwr: PWR, spi: SPI) -> (Control where PWR: OutputPin, SPI: SpiDevice, - SPI::Bus: SpiBusRead + SpiBusWrite, + SPI::Bus: SpiBusRead + SpiBusWrite, { let mut runner = Runner { state, @@ -549,7 +554,7 @@ impl<'a, PWR, SPI> Runner<'a, PWR, SPI> where PWR: OutputPin, SPI: SpiDevice, - SPI::Bus: SpiBusRead + SpiBusWrite, + SPI::Bus: SpiBusRead + SpiBusWrite, { async fn init(&mut self) { // Reset @@ -566,8 +571,8 @@ where let val = self.read32_swapped(REG_BUS_TEST).await; assert_eq!(val, TEST_PATTERN); - // 32bit, big endian. - self.write32_swapped(REG_BUS_CTRL, 0x00010033).await; + // 32bit, little endian. + self.write32_swapped(REG_BUS_CTRL, 0x00010031).await; let val = self.read32(FUNC_BUS, REG_BUS_FEEDBEAD).await; assert_eq!(val, FEEDBEAD); @@ -607,14 +612,6 @@ where info!("loading fw"); self.bp_write(ram_addr, fw).await; - info!("verifying fw"); - let mut buf = [0; 1024]; - for (i, chunk) in fw.chunks(1024).enumerate() { - let buf = &mut buf[..chunk.len()]; - self.bp_read(ram_addr + i as u32 * 1024, buf).await; - assert_eq!(chunk, buf); - } - info!("loading nvram"); // Round up to 4 bytes. let nvram_len = (NVRAM.len() + 3) / 4 * 4; @@ -675,7 +672,7 @@ where } pub async fn run(mut self) -> ! { - let mut buf = [0; 2048]; + let mut buf = [0; 512]; loop { // Send stuff // TODO flow control @@ -707,14 +704,8 @@ where .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&cmd.to_le_bytes()).await?; - bus.read(&mut buf[..len as usize]).await?; - // pad to 32bit - let mut junk = [0; 4]; - if len % 4 != 0 { - bus.read(&mut junk[..(4 - len as usize % 4)]).await?; - } - + bus.write(&[cmd]).await?; + bus.read(&mut buf[..(len as usize + 3) / 4]).await?; Ok(()) } }) @@ -723,7 +714,7 @@ where trace!("rx {:02x}", &buf[..(len as usize).min(48)]); - self.rx(&buf[..len as usize]); + self.rx(&slice8_mut(&mut buf)[..len as usize]); } } @@ -735,7 +726,8 @@ where async fn send_packet(&mut self, packet: &[u8]) { trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); - let mut buf = [0; 2048]; + let mut buf = [0; 512]; + let buf8 = slice8_mut(&mut buf); let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); @@ -763,21 +755,21 @@ where trace!("tx {:?}", sdpcm_header); trace!(" {:?}", bcd_header); - buf[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); - buf[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); + buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); + buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); let total_len = (total_len + 3) & !3; // round up to 4byte - trace!(" {:02x}", &buf[..total_len.min(48)]); + trace!(" {:02x}", &buf8[..total_len.min(48)]); let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); self.spi .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&cmd.to_le_bytes()).await?; - bus.write(&buf[..total_len]).await?; + bus.write(&[cmd]).await?; + bus.write(&buf[..(total_len + 3 / 4)]).await?; Ok(()) } }) @@ -875,7 +867,8 @@ where } async fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8], id: u16) { - let mut buf = [0; 2048]; + let mut buf = [0; 512]; + let buf8 = slice8_mut(&mut buf); let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); @@ -904,13 +897,13 @@ where trace!("tx {:?}", sdpcm_header); trace!(" {:?}", cdc_header); - buf[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); - buf[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); + buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); + buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); let total_len = (total_len + 3) & !3; // round up to 4byte - trace!(" {:02x}", &buf[..total_len.min(48)]); + trace!(" {:02x}", &buf8[..total_len.min(48)]); let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); @@ -918,8 +911,8 @@ where .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&cmd.to_le_bytes()).await?; - bus.write(&buf[..total_len]).await?; + bus.write(&[cmd]).await?; + bus.write(&buf[..(total_len + 3) / 4]).await?; Ok(()) } }) @@ -984,7 +977,7 @@ where true } - async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { + async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u32]) { // It seems the HW force-aligns the addr // to 2 if data.len() >= 2 // to 4 if data.len() >= 4 @@ -1006,19 +999,14 @@ where .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&cmd.to_le_bytes()).await?; + bus.write(&[cmd]).await?; // 4-byte response delay. - let mut junk = [0; 4]; + let mut junk = [0; 1]; bus.read(&mut junk).await?; // Read data - bus.read(&mut data[..len]).await?; - - // pad to 32bit - if len % 4 != 0 { - bus.read(&mut junk[..(4 - len % 4)]).await?; - } + bus.read(&mut data[..len / 4]).await?; Ok(()) } }) @@ -1027,7 +1015,7 @@ where // Advance ptr. addr += len as u32; - data = &mut data[len..]; + data = &mut data[len / 4..]; } } @@ -1038,12 +1026,15 @@ where // To simplify, enforce 4-align for now. assert!(addr % 4 == 0); + let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; + while !data.is_empty() { // Ensure transfer doesn't cross a window boundary. let window_offs = addr & BACKPLANE_ADDRESS_MASK; let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); + slice8_mut(&mut buf)[..len].copy_from_slice(&data[..len]); self.backplane_set_window(addr).await; @@ -1053,13 +1044,8 @@ where .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&cmd.to_le_bytes()).await?; - bus.write(&data[..len]).await?; - // pad to 32bit - if len % 4 != 0 { - let zeros = [0; 4]; - bus.write(&zeros[..(4 - len % 4)]).await?; - } + bus.write(&[cmd]).await?; + bus.write(&buf[..(len + 3) / 4]).await?; Ok(()) } }) @@ -1172,13 +1158,13 @@ where async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { let cmd = cmd_word(false, true, func, addr, len); - let mut buf = [0; 4]; + let mut buf = [0; 1]; self.spi .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&cmd.to_le_bytes()).await?; + bus.write(&[cmd]).await?; if func == FUNC_BACKPLANE { // 4-byte response delay. bus.read(&mut buf).await?; @@ -1190,7 +1176,7 @@ where .await .unwrap(); - u32::from_le_bytes(buf) + buf[0] } async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { @@ -1200,8 +1186,7 @@ where .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&cmd.to_le_bytes()).await?; - bus.write(&val.to_le_bytes()).await?; + bus.write(&[cmd, val]).await?; Ok(()) } }) @@ -1211,13 +1196,13 @@ where async fn read32_swapped(&mut self, addr: u32) -> u32 { let cmd = cmd_word(false, true, FUNC_BUS, addr, 4); - let mut buf = [0; 4]; + let mut buf = [0; 1]; self.spi .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&swap16(cmd).to_le_bytes()).await?; + bus.write(&[swap16(cmd)]).await?; bus.read(&mut buf).await?; Ok(()) } @@ -1225,7 +1210,7 @@ where .await .unwrap(); - swap16(u32::from_le_bytes(buf)) + swap16(buf[0]) } async fn write32_swapped(&mut self, addr: u32, val: u32) { @@ -1235,8 +1220,7 @@ where .transaction(|bus| { let bus = unsafe { &mut *bus }; async { - bus.write(&swap16(cmd).to_le_bytes()).await?; - bus.write(&swap16(val).to_le_bytes()).await?; + bus.write(&[swap16(cmd), swap16(val)]).await?; Ok(()) } }) From 4205eef3ec46840fff77eb45cdadcbd51938b798 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 16 Jul 2022 19:25:35 +0200 Subject: [PATCH 0014/1575] Fix iovar_get, unhardcode MAC addr. --- src/lib.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8c235b174..e42bae687 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ mod events; mod structs; use core::cell::Cell; +use core::cmp::{max, min}; use core::slice; use core::sync::atomic::Ordering; use core::task::Waker; @@ -272,17 +273,12 @@ impl<'a> Control<'a> { self.set_iovar_u32("bus:txglom", 0).await; self.set_iovar_u32("apsta", 1).await; - //self.set_iovar("cur_etheraddr", &[02, 03, 04, 05, 06, 07]).await; // read MAC addr. let mut mac_addr = [0; 6]; assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); info!("mac addr: {:02x}", mac_addr); - // TODO get_iovar is broken, it returns all zeros. - // Harcdode our own MAC for now. - let mac_addr = [0x28, 0xCD, 0xC1, 0x00, 0x3F, 0x05]; - let country = countries::WORLD_WIDE_XX; let country_info = CountryInfo { country_abbrev: [country.code[0], country.code[1], 0, 0], @@ -439,10 +435,12 @@ impl<'a> Control<'a> { buf[..name.len()].copy_from_slice(name.as_bytes()); buf[name.len()] = 0; - let total_len = name.len() + 1 + res.len(); - let res_len = self.ioctl(0, 262, 0, &mut buf[..total_len]).await - name.len() - 1; - res[..res_len].copy_from_slice(&buf[name.len() + 1..][..res_len]); - res_len + let total_len = max(name.len() + 1, res.len()); + let res_len = self.ioctl(0, 262, 0, &mut buf[..total_len]).await; + + let out_len = min(res.len(), res_len); + res[..out_len].copy_from_slice(&buf[..out_len]); + out_len } async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { @@ -712,7 +710,7 @@ where .await .unwrap(); - trace!("rx {:02x}", &buf[..(len as usize).min(48)]); + trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); self.rx(&slice8_mut(&mut buf)[..len as usize]); } From 13c88a9ca3e4a192677a184baa7e6ac12646797d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 17 Jul 2022 00:33:30 +0200 Subject: [PATCH 0015/1575] Obtain the firmware blobs from the user instead of hardcoding magic flash addrs. --- .gitignore | 2 - examples/rpi-pico-w/src/main.rs | 15 +++++- firmware/43439A0.bin | Bin 0 -> 224190 bytes firmware/43439A0_clm.bin | Bin 0 -> 4752 bytes .../LICENSE-permissive-binary-license-1.0.txt | 49 ++++++++++++++++++ firmware/README.md | 5 ++ src/lib.rs | 26 ++++------ 7 files changed, 77 insertions(+), 20 deletions(-) create mode 100755 firmware/43439A0.bin create mode 100755 firmware/43439A0_clm.bin create mode 100644 firmware/LICENSE-permissive-binary-license-1.0.txt create mode 100644 firmware/README.md diff --git a/.gitignore b/.gitignore index ee8e6b4ca..1e7caa9ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ Cargo.lock target/ -*.bin -notes.txt diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 475c69067..633c1b2b3 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -43,6 +43,17 @@ async fn net_task(stack: &'static Stack>) -> ! { async fn main(spawner: Spawner, p: Peripherals) { info!("Hello World!"); + // Include the WiFi firmware and CLM. + let fw = include_bytes!("../../../firmware/43439A0.bin"); + let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); + + // To make flashing faster for development, you may want to flash the firmwares independently + // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: + // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 + //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); let clk = Output::new(p.PIN_29, Level::Low); @@ -54,11 +65,11 @@ async fn main(spawner: Spawner, p: Peripherals) { let spi = ExclusiveDevice::new(bus, cs); let state = forever!(cyw43::State::new()); - let (mut control, runner) = cyw43::new(state, pwr, spi).await; + let (mut control, runner) = cyw43::new(state, pwr, spi, fw).await; spawner.spawn(wifi_task(runner)).unwrap(); - let net_device = control.init().await; + let net_device = control.init(clm).await; //control.join_open("MikroTik-951589").await; control.join_wpa2("DirbaioWifi", "HelloWorld").await; diff --git a/firmware/43439A0.bin b/firmware/43439A0.bin new file mode 100755 index 0000000000000000000000000000000000000000..b46b3beff05de7dfcbf41d53a12bf3a74381881b GIT binary patch literal 224190 zcmeFZdwi2c_AoxrCCQUCZBtrGdZW2eXd4P?%cWe@G=ZlrMaxB9ZmWT!n<8ojFQBL? zpcc{HwgJsW1VQ(UAZyc#OV!#c?tXDycM~X@-s9q~*t)M1ddUMQdCxp47ya)3-rrxp z-}}e5pLw1$GiT16IWu$S%$a$D5JLAPAyl9E|4%@rsD#x2|JC0D^O7ZMD|M~9Kcpe_ z??NZY$q&)8hkm?HV?V%#vGl9uADc#@)rAyFn@OPuXHcjg z;LL)b^4tn}SMmREd2dZds0KnxCPFi(f!rbd^A?0ILa17bkl%|?3&_uV8KIAW|0=+J z5yHL+5Y!0$9s2&6bc9AAY|TI@3+TKCH1+vF-5w5rX74 zgjPei8^Zk%_HK;kKLd3WJjwkC-LwaIJc!WBLoj9_{NYbfFNClD0z3h4ehAb5hEUnp zF#paYv;%nE1tDVsg~moHry--2Mu>%1gh6Ntyh$Dj3K=J&aU>21B*MZ!ceOOc|MT}B z3H(O_|B=AIl)&)HCXyZr-?d-NAFc$gN#e!(qVM}(A@8B^@0{fQZTMHOlK0=kPBH)M z@OH7>nea9bN&jp3I~RF>8V-nfr^2hm{8Qm~L^>abZx`e3;RRy6JzU@>_-)}AM0#!E zJH`Bu!xP2$SK*IEytXha*8gGnw<6uv@HP?e{cx1OgW;P+{NIQFCerH-7l{0{glk3o zrtrODyeT{?<~N16iF|s)&x-M`@V#O^jo}AG{Exy{#Pr{V=ZWPGghxgC2g1oB-qA1? z@eYQAV*FsZPsBeEzAW;&Km0q9zgNTWiuLRZZxhRV!Uf{n9Tvp=1L2@Z=Ri1KjJvMU zafo!k3}=h!Uxw2~`d^OK-x+>h%y)#V#dJsbQL!FJ_;ZoIBmAVuw0gEyi19DS(!U5hMSfohFBj{7A?y(8?GBfV z^j`@7S}eCK{IG~e`u0D6|B=9dB=8>z{6_-+_er25%`WaMV8ejk(M4en;4S(ntOEFx zDN$GrurWIdYXA;Rs1eI%1B`r8*a&cX(snVu2;l6ZC~N|Fbr>90?E5J@&6t)38_4z1V4RGMYD7*#W_O>W&2UvA73fBU>< zE;<+GCmZ0615wxraCTM{E&>>j@oWNk{>f;1DZuKBQFsZ!>n}y&6#y@gKPR@)3a~0M z3fl-=7=^0=UNbKWZvpr~XSA#x;1yj_xEA0O)+kOL!0KQ$-3jnpUysS3;C~;54+H#l zC<-?M+#X1rzZ-Z2Ur+`F~GIG z(R2aeZGVfxNCEwFE(&u15C1(1s{pQ>Tr0{%4e;wR(Q&H*cw1Z)&IXuEiNZ#Jm#m25 z7XhqU8HG&%cdU!Tr2y}&io#0(?r}!p6#(1#9aY6uF~^yzRs2#u?wv(^F)w}Rq;0#c z*mmU|#`ba5_d`rg+&kOhEvb>d!<14slyk*r<7-Qj8d2+f@KvpQx4L8#1h$Mbp=`#- zlqqbqMPaQj0lPi|WArN&jOMXoUb}@hkzA5zhdjDWZmZMPt|0gefF6mzqN`nUHQxK) zvG@O(tfb7$!<%8*X6gtFbaNMX9OBHnb1S?XWHBQxzY!86By<(nV0 z#VZuHczvQN-VpLp2Fl0gkAB1DmzO7+$`>C3e1ty|>^#DM5^DHwzbS#VfB0iE(`@`= zy-7Xkoaq(FYZSJ_vqjj!2fisT)9TDiW-l_8?OGAhr73q`o zCIlQJ+#s0vX=R+Xl+T0b67gx}rvf~+EZ#cx*1WPIqrS0R$P>9Ntu7%n5xjpGdt0s1I0tVAFM5yC?>6Oq8?I;`L7CZY z6~iczH59e6dd5gOG?-x{;b3HDIntdGP~&)h51+_S3Hk+e;NU{t87jods8?l!3hs{~ zB>!ZPlgbCA(htq!&-4mxojeqolQJNS|6D+Iv@e_{n=bGCWKbcyd!gjCnV-o&=zX8x z$;a>OsU))wKZqyO50NFDyxo9 zvF%Eg+;BR2w%p)fx$} ziqJ5AnbT_CVnc?D{yPBkP4#ZG#GhhYZCj@13Kl|2rIoQk9rSkfL9P`Uhj0kOISBf75QlIG!Z`@~ zN{B-^1mRqzsui`t$UwquN(}Wdey)zIYhw$>*AMT~Vt8Q#gMJ_d$S@oG!^iV(t#7pP zk#JKaXA8=i?dP2GS0<%OeLCP#=;oYSUXCB=)$x-G@@%H-F!qT%Kc7 z+bEqBf7hKHOu8+9i>pQ9jJZ~8YqvtIwXl~fjV&X5baf-BkJ1-b%kheyght%@1N5W& zaxJ=J4uz7S9~VG>)f&yDWKq`6!EnAklXu)aq zss%|YlhpD({leF;tzW#pLTBp|(2~m1W9qp!&hdvqTKTz<-Y&sUb#os1$Wsqd4rb)3 z&9Xv|LY5yh^3QM?x$&9i>y8>tw$Y#B(Xi(5~tfREFl1DE|a9Ow9B>^13i`G*Bv>iFT+*1sc=XRUQ ziz`xW1fJ17$7U*5*m&M;W_&k8ilI9zm|7phv%&GUIi-mi&zKp1DxY@LZ5};SXiGhY z9J5Ldd}@OPCwI@YrMkaOEi65gs^F1Bf@6W_RQJlX>7|~r6t+9BJk3_#oVsOh^U7Vl zyHa;imnEY`GjFv>qr8oK@q9J{UJ0M$XotSE5PJCAEu8hXk^Y}NYjm&8>*T`J2g|YE6gfAeZ z&85V-Z-e|h0WL4TefdN1-V5Q+5JC{h7@9B--Vj(aj^M2Ve4;D3;}7u+)U>yY(zKrE zOmaNwRX5}lT}#fo^1dA^mhnp9tDslaBF8g(=LXSa4IdklQXBYVg4Bv$l7$#ffj4!c zec71d7A`Q27BTzGlLL9p*?c~X#rz*xUhwAg$SuM1x{&9;YW|t}H}$DO2}cid%puSx z)J^M#l9)z^iRCpj_1kngG)BzKxWSd9RP?ZJ(@T42-`7v_w4qzEf{I6jIUgxom(XW2r=r z|LS23@qW{ya~pM-q8(!uGsyaD{6a}`K zFivVZ4{g-ikfU;=j;9=lZ26_MEp_pza=fN}1=Nr0i>c!HN${M>2j*H~r#wW-+1V|j_;@Zvaa6#! z@N0je4QVXH_R43^tI<^nG`&J(=5M#7Y8c~xgz?@3Az?nu5eS`X|=2`u`S`_M5FwT0gM9zrF~@Ca$S2 zQ5_$T%Om6QN(jwDGL{G|KOS!sG<+KF{2>y4?gui8RWN^l=SF%eNEMnmlzrKMGNeJ( zCG8WcODq%h1H?Cfv8@!wy3nQ@Ks0AkS*lC8;}DOuH*u|b-wKvqL1UNlv3wHm;?WdY z@Y`Sh?UzPq?{^VSkRfVz#ljIkjL99$R%%{%)1kqoiD+;KvfF$iusYhvdZfY>Wjvnh zqxF<-1F%{=A>#jjbdU z9hdNEWJ2|oq1G^(qzJO1N0DUEjx4rBFKdhpv0-Y)>rKan75RtEI8X$9<6>!syOHJD zd?U{mWbjvpc8? zBXUqAau6Bn9G3D~%^JPp2&E--+D45$sppnRZibYf!xsUU`J>nCd3%&ThOgGMHxe!y zt0z~i=kcyIun$N}Pk;q0BO#D5#k^K9m`MxmyGK z$Hy{!M~QM#DW<2rl%nJ~H%|5o$6DN_m6l71*B|4o8OG9x?J$#a5Hu$!UQ- zAlrKPrROi!3#>hfMw|7A_RYX9su1JUAeif7(ndXvEBWb&qk-}@;iJEw37dvn>sem{cs|H^NOE!o;w%kwUupsZ77 zJY!?_AlqUa7h);4UJg%5y{s;Q&*oFbzTOjY!gzAQ$eXs6Yd~5#>`B~+n<;fjLy@^G zw)8Ph$>8&fH;^f%V&Ib)McrqEikPMR>par@jyD8jY%;fE|8h$lFLz`?8eJb3{Dr{I z$+yKgklw70amrnU@2@T+Jr8`V`SGF1%zvJyU2mXU(`3PkHl*L4LNFGJxknco_~A3` zob*s+zVEceE%iLY(}O#tp9u*4Wl*8e0oP1$mB6S%OW1kT+?Ue@Khk!BI}PUlj}jN- zmhr3;=Klp+Yv4`1@(9fTVb;mH&|nS21n-1eED(}Hkr~2iTAO}^U`2-f!&Bc9n$q7? zLLBXZ9oCN~p4RGkh~a$!q`8TV0a3avzq?V*%NsIzuAb0-!p4rgwMp6lx3H3{s(nIM2QqszE?iikw2wqV@ zufs^ZL{{QdY>)rcN6q|a6SQe5HEXnKkFX%W+1wDIG%?rYv4cN0GW|(4pU&^%8$gn) zhJR(Fvh4ha2x%wjUIU$_1&bKz3PH}V?Trkbx)6;Wts`>(Xe{=Y7<&(T?TM7#rek%l z9U-|*7mm~&bt23KCU0d!oP@f0wCN6EGJ+KfG`GWE#vgI(`L{rld0{<&)OEyt4B#nY zf)fdQen46YUokv!69ZiI^+@y^%v#=k_(ef!yfU5|j4=`_1gLdYO(33#wyJHCEFo&}8 zg2!Bx{zYM9f%`D&2QrRkUwMCGS`%}4Kz&lZI>jNd`T0CG%Vqwy8x3v;n)P0!UkMy- zEVwfCBG6Wk(LUxvdw{;yeK-bmWYQJtriBYDFp_@hcOPEKA9Ek|K$&%<2I&fH zZ-KU2F?0-`cMKhc=dz(A@T?f3G_lY>S4VGiBU4BE>UrJ)^4&Sq0eXnag5H+3Av+IR zN{X~|c?rmi@44D*66m#mhN7`)Vn6+3EH+(?^^e7jVk{7%we$G=I*HhW{!m`sG_g;9 z+JjvJTOIAi-S(w?9`xdLz*;mq%|&1%zzasBy?EmC)07$dZ8Avz;1G2S75r^TBm1yv zg}66)t1Lc?k7sKa!T@4^4;O zv0pI4^T`X-;Q9E4Ja|5KAs?O(U!XLF*JfCo*k>7I^Xtfk2W()OO?nrhDK83Pqe4I5 zL00ZU-pq#wHSgte-A z{pD%S>23k;1bN{%E`uEtUUw-6e0?+A(}B|G0@of1vzMp2rn?P%KHxlfWf7Dx zx(F@F$Ta9r8f5qIg{Ay7z?ccCmxh-C2FZzpzqwQjyi9jThAJ;yt8M7p;pxsoS3clJ z!h0@7Y0rX^y~7&4md`jssCYsI&N@QU_lA0bkJ!-dF?U~j0cX6Kb)*nDLyeJe%B4cE zy;h0+Hx1|V6hE8Hq;MIuZFaP6LrueF{A{R;%$>J}uW=Fn#ieEZ9OpC_;bQ3}A|Jwa zWXL@XZ`W*h5x{$fi$Ge0q6R1yjZxeM6pO|v?zn;CG@v+ljAHhsLXjSk!g`P{u@K8a zUZi*CfX(&UkA&)Xm$ZD1SnJytw7h9R4s-J0MUs{Qb7l^3Fd;=jNlX z1u4SL`3zJp;A2;4FZkUC{Ps43w2A!uoUs>kS3ZCY?x?@doa-*>oCz2 zcVnVK*5CqouEd4#yc5rW=W;v~o=fm7c;1R(ar z*L(|mW#Tugy(#)ujlCC-y+46Bl}-7ozs(yl#QNx#)h+j>YpP$X zuB^`TCh)=ZT%N@%0?f?yDH^`0wSCBLB6WaQYS>heXkpAsaW6^yvcCG;ftCb%97l@0 zG35qRVaxd8I#%7Lo#}!mf$_9T$Yrn?JKrS+<+f?A)DFVOuCw_1sK)Y?wZt~u; z4BoJ(CVTXuNbweW3#Ctl-E`t5`hvnzvt_r*JitIZV0U35D-p}b!yd}TFXSmbbBP;D z9FWHwe3exiUT0ABo~v3Vm7O(>PN9%44!_++!OjKtW!j42Pu9iZlFoJk*{|cSz5&;D z4cByG65!@{GPVp0@v@wNz2YhVJ|Wv!?p^4m^vD4_C)gJjK>Oc`NG#dlm$)mFFuWZz0ahoUjmA9gx^49CTth|Aj zZ#m4f=$o0bqH$;saz$z*Xtj|$ZW6{SgV0I3T=-gKDrFvQdI}mtPcE1yLh^RX^{7x zth!q&6?-KP|Gks+$yFXnuWWAAw-Rhcq>0iWH4(pV8E71`tNrqdgr{`lE=h+BAP>|K%>mtfC2Igl zs9BlQz6aURZ!$+B;YT902TjfzU=%Z9)EzY?4k+R|@VgLNgH4g}j>xm-qBbc{&CJaE zU;Y#6_LQiHQS{op3Vtj;6Qum>D=53#ry7uQXh0^L`ftYU9oP6bT}WRSi~reQStW(O z*6?I>-#B7TKf?_4Gd9Zr#}r!8^c!cyjk274txU!6Nud7WPpIE0$Aq6@z6sPbEwimM zkDHNn5Py(D!{JliRM2#x_x7_wFyF&a9n}0G@SWkB@bQT-4#*gdht|iy2v{ZMuFcDH z0rWB%1#}R-ERIziDXbX&6ZGxmp2{knK`FLWF5*MJE)M@GfLnv$*JJ;(u0KOv?_H~F z)lcfW>wna>UaadN)McqkJWgg&EJ$y)RB=su3&5*tucY7o8C6 zJ<2nC-{hzB(|97KSg}W}6$>9&0VlJ^m60#Oo zM8Zs)2*KIWnxz!F2f_mo9)nM8_L-CZrG|*b3x0;((<`xM z!`fPOJe|+5fn^q9c)it>M!d#h3(AVFm`)y6usRAXsNY7Yy6fxCb&V2>CtRakG5j%b z{C00kh>Axx%FngL1(&p(7+jbs?<79a0-ib=3I8#Yr|$d)iT>pn&;3+}m8^th^r6Ym zG}^`e81oX9#k_3-UAc_6j5@&+Nx^Qg3C?h9TV6b0gG^xakv#CjfKC6~2;(Vg@hrG& zfo1e%{j266oNIxL}bYS-Sy7eP!2mZm!!K zu{hF8BpqFu{{oVGxkq9fkJ0kv8%FMpJbm*s{HJ>)Rw@exGl0sz^HLOt7ejyFEZQJ( zFk7p7h<}RAyQHps*gKJ#S#voD&IU-D0(|e)t#B4)B{u0Euycjur4=kc*M@RUc$eJ* zeNAkS)nm3tEdDRC{;2H{ooP3N{I1W*8zo(Tjn^n<4#@APbMnTPJ^nN7aUcov1ywbN zX9up!?#3~)_Gcw7g2t3y(+)Sz%Edp!{JNX5z4{aFa3kL{e}?^ex77AX+WN_lSZ=Zw z*@RXiC*rAx`N!9TT@s7W_wRtwZNi;)IQ7PFcdz8Pi}GF739{ky3`)OG7&f+qP(1C^ zW3-tHTHyHyNIL;DGZI!`I=O|K{Hh6Iubp7s^)GsV6{)A^`e-4xhaK8c%+Ir02Bfm6 zRXtCfRf#F4%i)*iyiq2|HH*M41zLI&gv$cF62#!wm{u)sA7ig%bN$Kw@^On zmSM0-!7D4SfN^-2Q-pqlkMDwYK9-jrk%@CQ4nH3N{~SnV)fJG+qe$0yd2}=p{PiSl z-(>_R`ibDTkC!lF%AU&=qxQ}7O#O10@r(vPZAK382)|j#Gk9O0?~76y0q zNm}x(5;x$1)d9B0a)S4s+HrYgh{4bG(IT!=&)`468M4$RbI0O!{j^gKCwnT;DY5t) zQ7@5`+(`IPTXeS(35P(FO8$kF8;d{hzCLRH41IM^7uU#>#sw3eSgd279jpMlTTAQNd?{eqR7D}6N zi5a5dMnvk^*dhElMSjT*s7T3<2N2vpixu}&m0q9lXBC{-P=28UX&pbPv>U51jt6c^CX} z=5MTSjXod5$v}zxBe+{Zc7g4JCf{O1x^`hw{tjEakez=Lo>TLgs&>JcZ-M8G{2lNt z%0CIu`T1z1Zu9cn(yRKb_O3i5B(*;YXN&Jlcy3^L%5wt`|LUJ9`>Uo5urhW)$!;@E zO?gqBAkgjKZ($2c29&?rD^Tsnwg6O8G9bxXpVGUw*JjGiTF`5CoIjv?Za~R#0}@U- zAXC;3C}kA`$CWP(JTm`iS8EI&~1i?u{YSkSAThUo{SJhf| z#XwWi&VdV)b`ES>FHd=;>Wr|aebNBU7MRwVUcGP4hIF>~mw$G^PpwriQ+KZIwCz%N0yoRh&H<@XIY22Vn_d`@vK0ef>9Ye5%>wMD zYt_5dz)Qya<`4ah&1_q#wrrrRXVez!cH52(XVg2aAKOmC`=phwV(vSmX4aakEbz9h z-Cnf=-aFQQTy+xOC)bkwC~9lJ9qy5Vr|czFJdd(e0d+qAamYj?$fa>TRd zXb<3x(R{e?6XC6;;Fm#E4$-@CBAzDkE6QWaJ^_kJIyNXTSBS6%?pXb@4`~3O`b21i z@D2sHeA;3y2({lqp@k;+jse;x02MTHhTtypce6=qU{ zcrCVR254oYO$9xQkX#LC+Ud3}dqKvCH4P}Odj$rh+(ZM6_JX9@nE+{u)%(rHwFGyQ zbt2z!h8@}dh_R|dq`X{UyfRfrl*&h^2$g3CWD<#Kw&|Gx znRHjm*{bCG2(3pWlVR%@x!Gq+<)i#$-q$RBYZgZA!W3jZVXpAb7 zX1m+ya`m0WD~#GYU<`_5&qiicB>a5@WiRrPnPk@?(O52d&x}sS($~{>YMrQeGCaOA@=Za0i9>W{Y8W zOrZ6VY+m!`B7p9V+_vzYwkG~haGq%AkX{~C3yfwvo<(iPYv@?G`*8`h)AEHw-MIq; zkIRCy^s$1%xJEC7ry@itnih_9N2#k|A70dw4Woc~vkN0s5c-ML@J3=`KBw~9jgfFR z+`zngZ;tpfUHn>aoOpAtpf(lmK|KUMhk*z+o168vd8C`{0OY92w109`|ASzRnP z2EdyqLWThB{$U4V8ckmZmQx|xtHl4+mq1z}rmDqMCG0_pFVKlsQx}*Ct`axE#S{(P zUb$;6#0!*oVjqIIX;fGmjVF)AzaERn^>JeUcVltsSpNC3_?6ynk=`%I=#BO&by149 z1OMOmW|t!^@wA#oKUfy!U}@i^a=7*JleCgPO*#EDyqo%R;lzlPoU~Sn|I}-OF+p0B zd>cu7XN+1d_$EpGgcwIJDY3S%oOsM&j9yPy;$)Cy#qb)D%R-)po~kw}@&0|NO_eY% zF=>4A1a(SknyTRTM-#zzrCZ9pTv?(w#+Ghk;5J&iWzqn{>P!gkvQ_K!&`(%olT&A~(J_$4li%d~}Y&zf(sCx9CuCn~rcy@DGB|AKGse{mgKyQHh`EM_L8k zArWsNhChKj<0MABG?DPx2+~Q!oo6D%{u1dBw%Er90|IB<;UzcgP;Nj_YLso@!Q{~Z zjmG&3Om%R#kbJk{EC~oIJJBWofPF97UmHNz;^Ij7NE^!jr~mtJU*~H43GF-0j=| z+Em7n`<7C^-3xp0@njD!#dr39A#~)cWzr7nrcpm5rY-I#=N@_ENzO_cc+hSd0uB+> zu)AlUATy=`W#o#*Cj#5VeXk6E4x=7!V?-#JBltM+%g6E)pms+9Wh1=D{@Vv0Ap0I8 z3pc6G2pt;z1Mrmx?0XtZK&yyfBxrg1NNgw`ID8J)z)BxCeQlusybMc{(x=QcExIEX zeq@R8P%-?9ayIRtbiC(=Cuo^1g#SRIkLp?}D>=$(&%8 z7w+qbSWJ)>p&Yl53e~}x{NKUe;2_Sy$f-qO*XO8uDs!}5i*ho3k{(%3W;d6U+B-jI zTyRNFvQOQ;W)72+5ImW~_I#WZ*L8Rf%$cBfWarbIkLmG_*q@fchLR#>K z&Jg2LcxoU%8_xIZWcsr&Y*048X46$w+T@axL&%cw_<}YIrkU=h1n+5qqo^R<`K6xL zy?9;;_dAP>Y-m1}$UlN>y2N`S9gY@Wu01 zDT$TA#aXare~b?@c`===jy6yd6DiBYRgHX8qlB{_jxQ~v z@tLmDGFXrBU%N`g_h((a`2O#%h2r~U*8=g~26w~Ffd78iSz!h>8(X?GEoX%yY7Xw} zQiByVAM%ck<;@gR=0M8pkV0_&ET+ta6c3~rskxZyA*Bi+r9n)Q;I}T4lxdK%3+l>) z_cM^Qn7Rj7^pl)iNZHn9usMWz#x#gef%wB+lSS^TyRyXh#xBDKb(I`ze%LOPoz;qI z>wuaDaPNY5CcIa{dm_B=gm(tKm%%$7-nYR!4c?2oQlT9AVu;2I+!-(zv=AmkFhH0A z;U);h5O@f;Lih!QwGcKy*bHGigy$gq8p2)(uR%Bh;r9?efbd5MpF!|J2tY7`K9sK9k+^}Y_lJRh~foqF#B|2i<%C?V&+7pq!yD8N{51Te08ZkX` zpV=}fVe7YQ9D(jEins*(^e6;Wl z_(~+Bp}B^v+>KUs9fjHc=G~nv-O*IFOhTVclcdFJf^bV(a{#^!f8QJ9qVNw)tzn#B z?oQkqki~TrIxp-ho=9UEUEo|%ARjc-wdOOmQvb?yznuvtx~LqfKOvatq_X3DNGq$S zgDRb@0qF~zX_Ti4JjS%#m1y~FJMabRa?b%<*YnN0^}|n9>TxDx85oz?yaO~-7X^B& zdADU4EpOg2!^viIehtsLW(v7eld!$-nBYVy!2(yIlLCtG2&)QC2tRLN*?bE1g?eiJQF%IFflG|p>@ansN)yMgv`&?{*zGFLIyP_p{U@_BIA z$b`4sW!SW-DP(^d;Tn4)$l2K0T-7Db>p%uW_e&QuLB~w#)@}Gj`Dvll&J8e(zJbkx9HN_h7XYq-A`o z;M7PQ+i*vh#51lg7JBFeKdvE>=Yl7|wl9J20uVgMIZ$w1gU;3=>|`5+3QZlHyNVtz z{7TLCEmW?&Q@f%5;;pLJLR4H|2vNm@aTM4eS}OPMOEs4!bm}%VT)b676^!$faW)Ry z&2hdsBdwJFq&-j5;N_g#w>}Qm0jFK|>#HS{<=B#^`S7Y!o?!txKv zxv2EnTroy}2<=jxVhm?t{Ho;ADQBsIEV0G$gyRbbL%*OX_@=)SdTV{I#CK0F9m%7@XWRS%>1g!+jv zwn*t0+K`UY$)U7F9A(isSed%hO$hF-(LC%y8@OO>gM&|KV1p}?cZuUPwhm;rX|gXB zsAY+5`+q}E-NtI-f~3bt?}2QJNMETMqSQfqHo#rTI)|5Y7dlT1#sqrsBj)3#lR}Y} z%IDk^zJ)p|%&7Soay}I_2SHAcF@BK5P@01Yn)rwDioUeEL?@k%e6=m_2(}qum*0^}5DE)_$>(l%SvGjz%8YrCvr8hw7=b^OnYH97bP^5rp z85Q(ivPT8wlbx!%L>G)_BaE-MPlXjViNp57MZP^kYymRx@J@iY**D(Jz;7MId&Dt^J{|qQGInZ2{?zE6o*tIf`+=a50ZED>_on`od zMdsMON{_8f8k?rHU=6c-8Kr|8RC*<}i3`TN6!q|QD;gxhcn{om(&67<9-#|b4$^|rkr1qDkzN}K zimvg36mEORGf58MxQ)yMM60qPb!t*WjOTSjk|>dGnW{tKs%dGi((rdnih26%1o&Qw z3C6l-0`FX>+`Yt}6jXSYdy8SeAL}-a<|bz1o<6zD-b5Qq?J@4xb79qVT57X|x${$k zWc}=vQ5_0*E_~S|fi#IQXMQZKb{c+_WT{9$)WZZ-BK<%BN-3MF91{82<`=Sr8S{tW zSukJbA-09gQQ%aIt3L;;e{|g**WqJT9eV^-L5z2)G^S&jMADJ!O7W(+8{h^n*a{vC z^s&s*P(H(%=u)qtbH>-Di~XDuAmgRko91B+iJcu_< zPa-*+;7%oUU_{8^J8*T?y;$9YhOJ8)VeMf2GN)VnO2joJ(>4ILy&nKLb{#bj->;<0Up`Qec+bu~la~Eu7Fe%<-q2s$NF%9kZPD z6U1&KRuU7O<&xDyuYnClY(^Qe6E(6rm8*PWcN1j*JxJnS|B6D$>DmXJHYLs&p@E-Tm#GX>*(dy{1V>9YVLd}afGQy@%+kOg591U&>D1T6#& zgv|L9peNo3JAv|l74*POQDRHwVz>jKlh!R2;j%8NhpS5j>rU;E`lYV%<)@m`Koc@u zNkOUeVflCb;Wo-hcn?!{9u1CyeGZAj_47vuhGky_@4ebJkcMe z@SVj&0i`n*_A76M)J%*6Zfn-l4QR?rUh1~MTx9@zV}F*AtW5BzoN+GXpz7#)4fu^Y z_i{1!mym0Qnv)RWMechL&toKl8YDkqo7_!@6jWUBL0o<{?Sn+$USd_;t&J* zBt9*Wjk{<3-#>Y(%<1UsW3r{`R13)p7jE`s1uV|bNgcxlvrz|&08Z;)At z&bZRq-c&Mxreq20w#B=NovZW}h$~aFfezdetmhA1h>&V@pKbd=>ZE? zF(giAWUbL%MHRT$5)IrcxT~MScCZm-mX@Y83UE|$XUVGnm2tV3XscggSGRscY{*Ti ztH!y1h2QFiGVs-2mhixQVg-X|0lPc1gpJCjK#!zM!3_}Kpq$`J5NBJwPwKjg`3&UP zlpL&RQOqYHey=j#y~rC2dQ=MgzG7I}GVx^FGQj)cM zh13-Kojui!auGgazpizW;R#=J)k?UFu-^{9hYU8X#~ugcy%<)TOqjnh9(mnSuhgY< zYxoJcIKaV|@ChP3FA(GM!X4-L7t9pQ+tNf^kF0SD_j@g*v;wGP;@|elJxh5s$i%4J zgl;B&vU@4`vQyPoQs@<+u21%W9 zz^kS^)-3@JG@ubO@tj_B3ZXRzey3;&#$d;Q=0z5~lWAU?Bl^N%7dR;!Ec%9viJDxn z*{R&8hlk#bgfF)comPE3rfQt`_ASg*Du8t3g16ch4%;7IY`t?UHSNW=N$?vm3x^Nf zbE|c+_4X~2sna{9QyD*{+abKJuWn6xJPEV$i)~453$T6tBnXSGNsqIbW3T=y4B%P7 zXNy^8HFWTK>R_+8{PaZ9tj^~*adfU zZr*Qt#dP5xcI7-4b{kK^Dat9G1a2U~?>O}3gU%q(cfFS$fB*3^-ZDVL-86=#a$gtL z8PC;nZ9CySj_fp(V1H)c1h>B|=j_@1YV7H^KJ)nF^B$Lllrf}~?Co>UAZ@lTHnf0) zlL}nYm-jmoHvwGSC;Ha4N}Z8ECj7;Sa8X}u=r7E42kRlUi59nlWnHa!@q8&9`6BHy zel?%>^5?<{dpiE2i`>3^)-Lgw`M#!XI6W_7=D;ZQ!Myy75zY@cIm{D$t0p!y_2oVi z0IOqJoZ$uF)?`8Yv%(4rxTBU~OJD`~BiHA9YCRHZ$J*fLiJqoD18`$q9UydGh0}z; z)tWI)|IH5PlW@jZ#_tr?YO?udO{?*#K7w=LnV(Wmv{XI|{wJOBFv5TB%`A@%ZRCuO ziEs`^+MVyndb?mR!XNjNvT&N9E8<@iHW}0QAbg^i^_&&Z*a^MxRB!xY5`*9IfV2FKh44!%Bde>qAelqcjhkEa z8&*FlF|husQpuZ}@%R0bAPc`D1YX8BGx5E>lDFsa@xLSY;S_KmAU3pKj$XP=G&c$<<%u>K?^^g!07e`12Ym0}`<^h(co;lSN4M(Xo3vUu*@s`t2#(P&V0|Q| zR){gOuyTySn%w(M7iK3Wnk?SE!V06odjfFCI&v7^L`p{EeITVY8*PyL--G$#luFJ< zaWqjJkf6o8N7yuDtn9G8@@m;PjjO#-yKyBe>o~ggTK;B|uLD0A7g915ti^kzNS_0@ z4d|Ee#CP<+AF_jw=d4g?On0wZJI!+vz9h+tYNps-Vqf28`%U8n{*OcLW+m?zxC3+p zoEd4^!`V8x=ZJUq=&SxXgvux5XW{pkCJzrJ=i(jAz1BnU4HUv{_IX=g7AQM>6_x$E zfNI{~vQwb*dDt6PT8SUMSkvTn2xg`%0g>QphNfesbBh?Wo58u^)&^8XEbvoGff~Q$U8TJTQ&>rwjNPUb!?z6N|x}Cxm z4Hbl^aiNbS^+orAYA6+Dh2FHp)Q&_?3fj`K0@;!za^voPN@ z%9&UHngM;(JYuRyYKwup#cv2b1u>$uq`Ex(8Qhp965QW?Gk=0l0iW{4^w>}q{6dAM z`>e2Y27B1GWi$S|@2p@Om(5SYd;21Kst#_u!u<>isBOlp`iKRZ05TyvdzDXq1nIw0 zj}xf)dSR)m7H%R?g(|~{AHl3!P0kec>umI}J^$k&WOgh>{z@+!T8eVKP%EW^c7EMO zJlhDb@1`b4TTG;c^p#fHV*z{~#`4Y?4&e#dF%sUNfU}tRs3sbbu7x$ISf0=Fi|`ly zFdK?sL>eCUkuO9xXf;)(_^&-Yl7^BvScf5Ys)yXSCU6;MyNg(9hp{xjY_52cjc}O< z*5I9F2H&6WgnOa$ya(Vn3yld0cmdo`sD%`wla%=5i%uaUf4MlY^RyJU_X|ycJfHvw~{vYP<)|y;tKeyN_<&4XZ?w_AK<DUaO^lUgKUx5D(zsk`3&>lgl`$E95+mpiYC+!rJ1?22~5?BGP#zn+oG0^{F2p46Gc!Ik?H(VRUfFvy`~sILZ=HW8=zMa1YK41ZJ0~%&1|dCv zxA(KA?E;@qQW7?ir>3ewcuPg;Py^L~bY-}``xf|~p{(gi7=MKCh24NPg8?frb<F^Av7jU#7nz0Fj9-U3WAJhRXUqHH9!?oeg=3KOSv3Ex^|QI)21E5F%?Nc zI)|_@UsDCO>8OS>ysMiH>NkAaq^`OLKhYJfsp}!|gmQ9N_b5&Z&t#5)yG3LjOy^9z z_)TdI`MpB!6>_7A{N5V*y^Nb9s?hX!rB4-_5Fg_&62DEd@Z279GXxi_aifQveTuP~ zM7a4d6(>L+rQ;J~`zYY+m`YRS0ND^pl;Qc^nks^=GTn*q^~^rbz%OvE25FjbY7K%O z{NH?id0E0;CHx3rpLyk~V;45z-cpWl<`iQz$b8icU&FHf2~fX&G%r zxus!g6A=`h0V*?1!5Jz##dTc9xrtCsHyU7ObY$+GFQmX>|2^LcRpob@~3 z`YzA=yt8_!yprOkD~7>pL#sYW)}1qh7qnI|7z|Z1l5~e2tIALI8~E9zxL15TJm(QB z)95lWmXr=e9{_73HI9a^Zg%!Oc@cNPxan6}hXqZ}GfyTHi7Y8d{mf^cK%{ZO!iDhf zZ?d(|#%ib8O7A<1{H#|x-jMfC*iL$$r2f!U@_g6FVS1lyy@slW>*Eq1%78p-EG7Td z%Z5u)4%S?`foeA`eP+aiEv%-PE2CeO`;3T=5G8alp%7U&?V)m<#hTJK>=ynre?9J> zOFY2iF0kRsofgsoYsz*Up}k-{tVPpD(^!g;;OH~(L&>*2CxmfBFcQd~E?T08`#G}# z2pS-(Fv3QIuQ4W-q{DZkmrW)|(XWO>w>9vaNuZA%h1dr4(Qgx3Gc8FfYVc)tyO1-_ zMOdJJwvoR;!r4B~z4uR=AN<9LTch8@@tuv|j=qad@E1LH26F^Z_QWpv09)o2 z@W-IXmE!r^g%=NHAXLJzRy*^$ zP*(W5Fi&|xDAOBJH<)$CZGA&kUlIX+qV6ylSMPZ>UQ@G;<%ad0d@ zH*DLuCnL`S^&-Stk^?1YI7?C>$6^nrgdeaB56GZH7Qm*S4+g6V;aQgSK%b=`m>E{= z9UE@j2wS&W+n5tRo{{WHX&j9?gW|Z$$)s*|L+1DYRNe6n`g{L=LSF7lh5ePwN$6_L zkeYVQyc_+GYl%K4OW9%J6Fn-Qvcm>`Fea?oyCLEn-jg|#QV_^tBGcd}%4ulB(6#dh*eZT3m^69@ayaN_M} zvZe*>%~O3}3(}<=I5yc`)ThImfyi)4fF+!GoyoEWe+Rn@U7ha>8duO>S(d+u_idC% z3Yqm}UYETY+S0B#FLW=ygpBLLwXoDkTPJTd@MrG%H6@;Hrinnk_HsaY;Ha*31bL~8 zHE9~p_T}TemhQOlZeb_vpA4B;I~8@IfZZzp=w|3;a$e_O z?+hsnXO#!W_&08B&lp3t^p&?O4*cBTp0RP`81ht~4qmL77ldjp)E5F4;MOPnv_=%c z^^n?FZoPvZWA6D$zVNqf>S?!Z)*Pp+aK%oAhZ}IrKXm2vy!k*PNH|54(!)Yl(jIs( zy%a1UETd|O4=eX5_Nex#@$SlfihZhmGT7|zfWK4eQFv6I6XF{@8=yMQ2Y&4V;3TB+ zV?`~om@&b>`AE`%#1aKDaD#QudXl{E{PZ<%7FU7q>V|?s<}Y?fz2D=kGdqPMym5`g1Q6U zZEu5HLeI<5p1QP&*bFY+uzcy;9(smzwUk>$4$_Ms9)JzTmY%Y8pzp9 z&ouCfU?Lht9zAzA3z%{8jT_b|%q_G}FS)TZI)v1n3t-lz|MSSlT`6H&g2a1I*xao> zAoratX2|Dm+S9-&yMzlIhp)m;-gg%rV9k@)n4O)1{?(XgaA@E+m}~C$r=lj9efhi^ zmL+xAvUPs@dS@4>=v3d^KM6A4F!FR49b;72ntC7(*0XG&w4vd%Lr6|Pt^3K=5`P@7 zpfcfX@yO3ea<^Ad0mDQsAr_L=~6682X_We0h-MGW_426yvtL#`lhMtA!nFk7vpx(O* z`2w%4*#!(0eQJj!&G~j(L$Ri7k@&0la$4FF2lZ-XgIi&jV0a~b(P;>iZLf89mxvdhVpbBY5efwRUK0Z-v5vgzC`J`VZ;y&w9xYYBN0G28#J zdZp!^XMqf((Q|fRCArpL24n>EHmnDKjM|f_{?pSo)xQo=7R5v=VZ$bTuXC7I%-c50 zPqpx^(ro^R@jnWZPHgx6P+XOYl&G<24@InuVmk&PW8y$ZN8#FW~@1Ww60QNQend>&VV?40)E3WWf)f z1wVL}Ja8~FRNEV282cq)Aj4o}ZGiN`vAF_{*S;Yp}mlIceARA=X!0DSN{U{D?i7d)E8Y$=7}5ezWs!A;Umz{%3?w z5?|1NAbetE$f0-M#+x~u98qvGi>5*CVJ^SEy|PN(JYO&}biLcZ-7Dzvj^HpQ_1o71 zqux_Z<3$T0Q>dF$!1?SJ*Twqnbd66uY9@EY@%yrsg{L2PH@qv9LKn1?_1oKpc9bTG$3xM@Gx=dU*5obt78h8>pn4*`g=AMLCzQEW-kchttXHy@hy#Sm|Fk zu>~Ipubb%pqd(gJrnvu2QDNoIZ_BDVGgF%d? zCNAzQ9ZQwubZ-{=?i@C=xsqh`>HL{Zv&iM{L=nR^j-Aibr;dXiz>JI&)SKe*k7#!L z|F_hdHF`v0<)B698I&$ViB6|W}kJ)c~}ynduh7`G;!*n82cj=S*+!YUFP z-WJkaf4f&t-A;B=V`{f!gwN;yx&MriYoh-@z^^8(`*j`jc4r+!ZS`k_!h1&wP1K?x z55(%3USB-D+Y#S1lV@PB8(jZ2O$rZ#SOU~?nfH3C^`q$NYC4ZU2VH$cQ>=v!8)*w2 zHln8)V^96WnC0iH(hWBDMpcNjv_vAA|NW4Du{WXhl z&7w-y3=D-yNgfw)<+v^1_lFbW*(Z!6d=`It>=`TYjAisJ){=srG+3^a;+ZGEzi%4u zn^vi6)|yzel1vmv_$vHkQCC@FPs&2H;~8O`xINA^!k6raMRtXES*9p652^8PVsfC* z)}(G_vKjoI5p?l8EFX|@wF;)$GN-1)nX7TRQn#Cyv?4|eFS>hOE3uZaa2nS6gnrJ&8G^v__l{PC# z(_Lt3M5x({Qv~6E1q-Q;_lKP;9JYGX* zj(K42okG05v%|_)9ttbH@|@OyBIl7{CVB=#=GV|McYv`+9{4nyC^u$Kq z_%1r488WFR)92e%;opU)><6ZKhqGKR?PTHAN#>`J6Fqd6SSSM~eZaN;Q*6`!<^iUJ zZfE+=(AAQ&M~0znbo`DYr>I_l@i@uNjKa)^_{DT$?4k9yAv)hJ&K~vXTI;JgMHvf{Zt$a3b8*WqqPQm%m3aNXz!=B{MS3oeGIux$J_qcczaXq z*N99+&41y}tD?W3x-U6nWqJQR?H8p1+Fxe`(;;;g2jZHzt$m8Aom2n6($SH~kU#xs zH2>eFc||PE@BU3`o^xBO=Zy2CbIb-;G61-9$|Q*>Nq5er20*NZ*LTq8%?mtuQICr zwZbZTMhfC~uGFm6kkae)d}?FLLX_tAaRmedogZ4wi`m7yj2Q*2F7ldSy=qP|rTZj%xINN6!^uP4wy@dB zjG`^m4exsQ(m8))M>Qet0M;48C|Ug0iTqgRFgs=l-@s2rEW>bY4~JcDD(~Zmfk}%U za~RtNe~XRJcto!N-^>M0?O)rnL*k$+^<5W9x$@p&fNc&pN=|ePkW$; zMKn(?`DahOi0D>Y*gzdH0}7uRt8Z+b92FYT{Hb5FEu=@7>hcp0>5dpSF1& zX83;?8l~#9bjGfLbXR;_Mmq0Ih=KJTUdS-$>=&VpDtt7+GX|NKqCXTfAE1I&v z=|I;lcwWFmq}UHzzi8!WNB{G$gXg`{&yKqM4SW-b;{OQW4EWL5F;n?5-vop?*moM= zfBqX?gQ?ucSl((?c(lmOr8cWP3g3{bxJa%_-(fH#cJ{Y*=pzjD<5K8^oq~30T;!zs zPSkjf?xWcfGO(pJm0@exHCwd-`jQfoy4)LG0bg;$*nKG^3M-AB2(FWut3rw)oyHZj zw|V^PRocS~@->ZjqQAIGH3Zo6?`a6L_)4+^w#p$MWq1f=87fOD;o;P@j3&n3=!#l@ z7QCi<6L`L_MpxwR&hpr*vpqkbZNVo8pIlE?OXhE>7qpO9dhSF&zp81Wo=~GF|L~9& zljjRzD=aoH*PZAeu9^|;4L!mzu7!F#^&LGxh*LRS-$DH_oS+37NA6Yn3LuOBy-Vej z16RkXZ7mX%su&XY1@5J98JU?(Lk6%x)c5T6`Sv9M6H(N zihR_C4Z78CIg9v8S}OGWgO3tTmz&S>JDfmYqK_OrR~)7ZCxDDXUME_2KQYZl57QkS zPNsy`RO|g5S#j=TVO#_4vq%#ak*npCZMd@_;c1&D%)EtWno+M zJhbp@S3ZV?@+ADM%$^URwXul*m3TN0jd(sQyfdl-mS}GmwGlF7;3FPW$PelXU;Nq4 z^x<Xd`w)#pr(C*_RP<&jPPZ8 zl^&B%)tHFBql7&tt5xHX?LH=0?RTPYU-?2PJviM@rIm!>oZPschjQ7zXL40RDob~v z#`F1BMAklvxni1L;VVKfuVa$Y3ot6Ya`XZdncDBdDm^doG%%v@0eW)yWM3)lb>$?k zchB{BYqIxl+L!v|#u2_gSa!W_M+L@^={t)4Aj7v5QSNds*)zq@3PlkEF(ITAx7+uy}ZrSa2ik8eu$N1c$H5lQn7ViR8kHj1_lhw{OaLibUkG&#R7+3BtgoO* z0xJgu>YzM7^0x{>`#?5tN6m^BGK{hNLwP>jyd=xL`A@sI>viH^%*&hHlLux4JkQSz<2a*S6*0`MY~z>6_5vH-mwO$`~Ub8A9x` zxFDE$$!S8SYkmg&FM8;D+sD-k*;t9xa_9>c+m1uF z^{$vgOLT?1Ib_t+Xu0JD^0yly=)>m(hrNn z3WHH!#h8|pNqx*{>UX98S<}^T=$-a2ybT-BVJmH?kMP?mytyXR&rbUYze(X1=sK2F z&5F3rJ;LiiP)fjo)+w~=>|`E#F3w-`OX2l3(g}w9LP2=L?oY>E;2pbPg7IM@8}PjG zyauiDVSz8TVucy4_K9hpFp6niY@&7SfH&Aj^_i7dvm}TK6F&6DnGu<;1_(_~MP2Fsy*X3*c=^NAi(gH)yH|X^Tof2aL83P|_Dq?==Y$JsX4L$~wg_QM+ z@xQe(3}K^?V@=`PgpAgguzJSv0YQ2&p=w^0#qV+M7ECTKlBVLqjm{=s649#;ITe<; zE-Pv@89at|Xw`D--0zaJXMV?JXM&@jgAo@l^r~e4wvP-3sHsN*M3E>&T zF7UgZ5#AB?!*`6ox_D~S=TVo1fmf1_wzMscXn|bAW)YpM4ek*>(=A)^2pH4@3fb90 z{?#24&oHvMN8&4R&Z~akpZ@G|oS!4Mc@}sW7$fpu5oPz+qU5OTd~HV(;tQq0uQ1RJ+uRBPDy3u}yxZ7Km^fr@&j9l)Dt*pgP7P}>6P08_y zV=hA;bAQ=(7iVc|%g)p1DAA%i=tFjL$R)c}FXkZH{6ZNePO#* z8JQ*d6eDMbV6svzj3HL{Mdz|A&Vw_!MNhn1o$r@j$yg(^rsV6;!Y4^(mDgGBd?fC4 zG$p>NzNc=cZ0Gb3qO+3oovclAiP5f~{%O^pkZtJCmn8A^PVQ2?M5nJS9@kyQ-@LX< zpe3Uv!ZSsyZClMU@FMMU&UYHgvk%`l;D~Rm{{WdJKe!aneVXRg--$j;mR9X>&H|41 z*%I@BgL^-kBJrkCJav=PrEhXH)z_qX-F5YhsmWaijq(1juY~XrPJF|Kdhi2q6>0g- zS4%&ws&`iC={qlVD)A=MNVQYfUIMHa8#d#++Rh^&ks~)lcsGd*tbUAnzC#%?h}-iW#_+4vGykOyC4`QW z@ijO{tcycVyo~`@YdyuoyJVMMRFv_Xz@^6_*C1ow;-Kw=c2dqjd{1mNr9BCI;1e;6 zMPf~lbI~fSY>#U$P;Cm=YkLJLd<)3+C8>l|r97QXCiIu_OR++5q#(}2 zb7yhOmLhj?>kPr%>Tx#Ka}hm@XSObNyoRk;9lM2Wo!N20Ve}*Zg&i>o`>`*_Fv-6R z7z>k2Qn1YNno~0AHAmXUij9chF(0a}tgi4Ms-ErlT#q*_b-tiq=D?QqHRqW$p!ljQ z5qmBLQkCYG+e~u0rD2Ulf^{toSbbVDJNeMPtjYss+vLtuSOej))fMvk{3ppyu+93a z^gz<@aeoD#fo$YiWyxFDTh5!EO?6#$$H13xOi0q{>lfA^6H>6P*EQ95VLJ`m89Mz7 z3tu>f{1e9nldkCn`eu}osK}qXqkM;N$1x!b|8sCO*Lr9Nka*8>1I&79PLS#j$9<@}8nr}YbvQ<E%(C%x@xp z1RDnI+Bj~*aS0H`A*cHEe#+=I zlgPW4t-knVVqL1MtghU(tFGB)T&=7Nxk~Hw?)tig?xmQQOyN)JwEaV?yOtITI(L)CoI%01@d;J?i0E4Y;(O*ZU2rnt}Ruy zx#3lL*Y%aql(=c}Ch?3<5xM58kG<=PndxmifN~o>aR<&j;yShI5-eqm3%F3FUY!36 zp>;onnCx17)4rh3Uq$NsmsXWQ${KZCT(wjAeX)`b`TU z*RO;h-3f9vkJs#jYPit!Tay(aR8V6zukR% zkF`BEz87J9FM7fpURJ%a3Q^XG1uX(IPzgES{Rz;GyYqX{3h297S$=5kVd9fiLAs)3 z8yJrjx_ss8(PT#N@URWm@}vHxRl3s8>R~kpXB@Sb$Cv(caT%{2&t3XRR_*)^=I?3; za#2NGsfUrxp3W8$$hUE#=ErpGth;;n1U`c{QL2|iKUDt~){$1=j!qo>>V+m==ke7` zd83#Wf#Q^25+25}GW`Aop9T25h)?;!#70Xa^u2>@h;z=OY)K6|$R2CSm&gDs=adbf z2)T^LL#2>ZV5my1EYV8z80my#2kdKKy<&f+QJJn9jNgaXuVe0aXVk}pSq2)leN32R zeMxv!_mc3K;VnTLjwf9FA0m z)i)K(zl?bxVlDB^#b%(qIUrq7cB=}JYFbaCUlkdNU&j2Bu+Tcouk|h3n7$FQ3Z@>F zqLJ={y{)l`GQLI65M8sXVTZGjm)mwYcGSx!?{M$O8R`Bz(Z{c_I7f=Q-rhG9c%S{k zqhO0d&5D>G#+>JooT8pd@OuC?tqQUcog5k+Fi3eV8;Q0%7hD=48QU2@92VP%TAt@K zL+8ooONr2x*O2Fn_0yMx#nwDua)eRu#~X-v9(75(rWqK;{8FOomQ(E$*la3|OAFH5 zsq~v`t%ldfBTNN{m1ql4KSN7m_!99KxQijpPf-|laXa}oNqRf=5gf09eG{&gZS7xIgbwMe{4Oz@fo?xp@Cf^f>Daixtf&jV#egk`mGD z3|i!0L$5E;HtKv`^-cAVvgheLjtgZ5ZL`l=qyIcD9xe4{S|K0TD2EO3<~2ETuYZu1 z;PGq=Zllfbfuc7F64)lV}!#3~>(%Ju$3$(3iOG|@!KI#LR6qKW; z9z{*X{ue;r&ufm|>7lON27P<-g0+kSx59UpOCYMRy-XY@3-{ zWL~R-#8|tjMU=>s0t@*)*M}QyXzBk%u19EtqYc!S)rUL^JkDhDn#zgpT&~X}G#ZoI z&jW-NP2pIqd^%(YMX>sr@~xri8tla+{f){J`w$QJ&e(Me_7c^d!Uk z@M#}4=oBfzUBcIf7yN2?`Qp!tH6vAbdO0Pc@^=ZJT9b=<5c^`_Cy@k1^wL&wEy?vQ zLc|whj|)urEOKbVgY)66>QRkpThrQmR)rKy=O|Aem|G6Db6cU|p`ULN`?MC8|5`zQ zkRs1fYIqK~0L7gfiLQs9b<>~Myh9u#Id09?62GSDLO#{3Zb!epOjrF(x28#o%#~e{ zvi7{D24M!!$(m+5f;V6VvXcZi-ps1>9u_jJ8Xx9RL;BX-X2kp_s9wQ1QYc`fTL8`E zJ$y(0z6|j+L7`0iUxxnI_ec! z@xn&(aZj#K*^FGJiOttJjCloYJTYt4VbN=Q*wid=u;y746yJNyTg4--CVGFyvNnkO zF=nR@_!C^|V1uXI*zg+iP~Xt3Ndaw0lQk+J4N0n=$AY87)~qQw zqQ`1HGV9@>HSEZ;2Ua3ea%t9sK*xV26v8tG>vMq}+tf?;F}^2Z+b(X~3&vr4y0|^P z;67}Zirb|HS=gQF;%WfUW_0z2R`3N=-30c}-lSv;lYu{aonX3rIpS%APrPH?nF0Sv8Nt% z6!JRwbSZC_Hr&SF|JT@Pus=I(;Frg+!dRXic<{f9ovwm*Ae#fYyA-MI-5icMmRwe(qun0{DD6WIGnGe`M6X>*wE< z@nKvqyI$s>0d3+ShsYf0pD7)jfdO|fhYw|d-Sw?fB6IduS-`*L5tgkz3yE?J)#g!# z{JRRiOIXZYzji^W^FQw=&N_ebMiwkATC;gOEd}L&bu6Q2Bi{jXx--~Mb25vpuyZr` zMuBbcL3Unzy1tbeV;(qNNwd$m#7GBtTu#gO$@d*{*e^fe{&Hgrl_bnm&!6Bv5}|B# zoFzV#LF`x;^W0Lz99;pLlObc=c5`K=6t;<+^J9k>@_T1;m@R^&2Hpib(Yx)C1issp zhupXHY@HjimzqLEz~7AyCr2P@QQvA}Na|Yxjfxe|ruY7%c=mPI;Jtss-wdzVY}q_m zrhm9(z#yi#}_`mOOG;yt>;(p97!NwIhg(qtB7!IpuiH7vg>YGyL6_ zmMw#Kd=GcjF;9s1TPNP{r0Wvw9Be=a%~0xnL_X}ST--{MC!Ga%rUCuKnB!MM|B-r= z23%uhVF_w$zYG76wU{AIu0@$NlAP^Qf`iJoszN3`EA=H0G>&CRgA37SzzQ95*j6o( zah->p`ttL-q@r+4(1A ze;0edA&X{qRtII^pOJ%|gasES&16|f3c9%P&u6fKxih{FK05t7^riXeOTz*(-zmQw z$Y5>9_%Y0wq%b|3VqBShg+$*41nd}PSUW}@&Kn~QXO4kH2Cv+Bc;!^$xHds=r6V+p zQ8+7{Bb<{V87?bY49hni9k1O+d=nqrK6slldXDD5T_s$bbJyx0>1XbXjWhwwivmVk zv}hONgz0^>{27cf@m~2A_wKdzb`jVMWB1y8TLBp`6YRzWcN=y0pnt!ApC2(zM)vTn z=#^{iVVUtx^wTJ7I#e}aV2|96Ccu;QT2+NJZ5bVVx7_P7A80Y3JoGO!$}E(TmhaY` zll~FVsSetj8C|bjU8v}8DSrY?1EFKi8jJ@k_MQ0;{uVoEm{w?hO{{^%WV?`z$Ajzu`VO9#u zK@pIQ7q>PQXLaZiGbbGveo7YJjo)Pd#i#IJ`r6iuTbah)!dOEo_((g20o^3N0QM(X z%?r{XO{P6g^C8iklDDWottcRTi>&E1-s>=Q+%MsIkQOl6`VPc}+4PpU=~seTM4`0u z`kBgzLU}t{9g&?fg})ZQEs#cXCG&w;Uv8dXV&##;EUKG%J6aLZ%{GH24;+1yE1?sx zHn187r_bPELs8@S>|r@jWVfQWtMR!Or#uJ?gTW&N4N6~{gGRCqWv0F>y!Cz+SZiQ) zAHfI9r8b8pe)b0fnr@ z*#m?fr_cQc*UIqkzX)C7kPq%&IS0!~S5FU2$`+^Y0<@H^&P z_~~o~uXp~lbw$Gv-dne_>KNGO;lW9G&AGC{S0~+d46O27oJV%Rzu8*tl!ll=1V^81zt4mmHt;ux=>7I}Tg3Fu@Uii7pd^|HW&{LLrRR-(r& zuLvjU`w9f2jU$8hl!7nDDRNCLXH`xL8uj zJLfLe??L}EqgAMO0pA$;@6 zMJV~A<>h=8>?q)8?He9Oc80NxaaRp;t*iVqt_4T>%P|AfqkVnMNKIJYf*xVrBU*0? zolF}^4z^$pzIB&nzU5-=;BW22*aXhioX|KH5$J`INj^;@qR5RE zb=pRSHy$jZZp;I>xI@TuvIWsM3Qo&UdF7W?N{_`q$p=0(jnt+F7n{;Mm*_NVaAs$j zbeLI7ucV1w1Gl0jS31EpVDak?%dS+c%|cK4-GJ5?7qP3kNTDJw;?z#!=VLq@sr~IV zjeHr297Z&{cp{>tM*fyDX_{7(Kb)g+Gb3?z`W0p5A&$npi~tVZQ4pL6umxL2mnhBcnLF|^_w{Jd~6p|vEiUAr08JIlO?mf0e zT_Eu&AahWLi3^;n(Li>b+s^CT6HSK&r{VA6u|CwbF?G$NH90H5kqp+Z-Pd!C>UN_k zZH@l0@xR*Wg`tt5;{qmYmcyQWPv{iaRuVO0oSrLSQ4EElm@>BgV#0Imv>Zd zd+FO3nXy1T?UvJ0(ps1YUTyl8R1RxKR{18N(=R%KvVkwS8~F@v{8D%?(?az9&9KT! zj2p-sJ+&yMeDYY&ac7fE+qqZtx6ypn@?a;V4di0ye@Z^?t_|ba)_nd~;M30HH<0#z ztycn!zrz~uweqpm;a9>9>jol4MCU)OshmoT@lPX#eQXwo*~G~6k4LeF^jE)#y6pX{6DWj$!MMv9y<@(ErAFihHmQg9`wxF(PVz-Z9@3Pkgb zC)r*3;uxWJ$zN7oY;6Qealdc^k>JbowY;J+9jIPveOlqBPaA6mwgO)7B-l}32kL<# zORz$<>ZX#fMf>_nH_fAQ*os^lU^RH%dT3L+-;C=SGRyt8z_g|hxMn{dP)F1pEpr;s zZX1ZJpXR$jl$|>u1VkIyI5#438ulZT#@z3kQ)xWh%kXq(JO1XkRh{wMHh%z4InGpp z@urPd9Huo?P{mnqMPsCcFR10AH_4w1ORyGy! zDK;f^fz$1*87Kn}?O#%4NzdYkndH7SvZ(KCApnM{x*o>-N0f>)CybVRV1HA685ZxW zR8Csim@t|R|BPS4pM;iw67q-KL+fZ6qJrBa93s6|qUND}qJKM-G&^|-m>Yi*`Y=qv0})JVmu+VeD=I|0|1lL)u>>B3}aC_wBtp zYjc%2yQRv`97t(a*c+)IzzpeI+=V!1YK1&KY1n00a4`x4!hy-nr?7e+$)h+ra zI3n3V9ysUp9|E4d0e9{Ko}7;H+GSigestG2?C7t*sT4;E(~+36of(1;9WP$K_Q_*c z6x-bVb5)<02L&y=>*YAuLi+@JO_E33z~q9ls)jMw&L&2UegtM**yJ3Mw%>1m->%yNHoMAxBUp`|zCj7-P2fQXa&#kEba$FO} zk%5E9E*Da*xI!@{SfLoM3wHSjt)4cuXEqz}p&TU%yQnP7LDxIvYz9x(v>I4f@&`xs zPik!UMzm#>7bBYDSm=n}&_{7BQ^|je`sl10#!S&KK)qOjXt5?V8T=CK8*ULy;ucKxt~}58s2}ZFKr+vfb9H27-(fJQ_ol;Gjq;;}gqYkQhv;v`}X#8y~o>r2A7`SISb zu-atvwJq~VPw%^kV2N*3`iJoy=ih||{;Uv247*@0^dsYZ{_Uvg+F&*~O@pTC(yEt) zzcFV2AYV>$yJb~{TmwXH7#5@!ai04RH^s#@N$rsr+*bavBaWkUTGEj7gsx}5bng{1 zfE=8jPvfPYanm+x7JX7*i)$I3#U}!%X5jB&RhtyrbA3AI!_$}7if48smSUxww%HNb z^S=_-usP)6J_*((b@S)Y!xy+$nTA06z&sdX%zc9Od>S}fpF8{>vcUZ%1FUoG%4}6A zz0|(g>L2o3d)uZ31>YGYS@4pvw)Hc<3odoqRv3wbUFs;`mK0&MqPIh<_b0U%!G65d zQL)YDS2vf+xU=9y0Ruy&Lk*jOL6Y1UV~>$ykIvUr&6raVgX~Y-&U}%)UF1L1HGigW z1hZ5kGOkjVydg(kSj^mM4RM$ zdB$n-vFp24CV#ewPtn*1+pebI@*ipJX0&HADS{Q`q)Xj&TCVRLJloekQ_gm3 znm0k>hugo43tKub^DO3ZLoKo@}B_?d$;fvBIOc;mDOAPh#iDof+8)k z;*6o5MiXSN6i%{lG1VE}9ABA*?Sy;JWc#zS;?mmUFBepowkF$VNuB()Xi{Vwj8GZPg z*89Ft=wji?Bl&m_7EDv7ZHJEum-BB%r(L5+YFehPN6nBk9_bqy9x+B6jvJ#64~4}f z2^N!NSWHr3F-e5QL^~$Gg(7L``Eghua@SHR7K{Kc3Aju~-k>r|HW&!8LG zx+ijc(^=R0*7WK*es}dm%-&OgTL#L0lzP;WV(v@EnCM7#Sl=BrdBV z9Vw19r*u*pjxmg6HW=)g`M@`I?|lwaP}I!CoES z$%*r2IYurUSYNAGn8(;Xi~CFcgWCl?kOUCh zY=W&N_I@7m9*e|#c#Ac-M;7S^E7TMlaJWD<)r$Aqeoh69*e5^|<%1{VW>k8WT3(I{ z1uhA4G&BoCTp?t&DijgfqEA#AtxE>mK^#0Q{~j~l(*~|Ljly$RjF{AeVV|PI%b_DN zrV@TN$?T#S;d03PtjV|WNpcB{821I+1*HK`Z=mQx8T_|Jyd1jJLB>wvdnjl;9+e*uXn|`g39^yO^Cen%*GRsit*h|Qf(*A z`D7rvuDcZf-ms7_N0}t%)ua>ku@Lp#)xG!nID>&_3bLsTX@b7ei#^thu+}N_X`GY; zSP&XHUP1ODr$#6v~d(oDzMCkv~9ELsmZr35&`9NLbw9 z<u0bV*MQNB$>(6RKP&W# z}uZ&dQFo@fTZYP=7W6v*!roA3HXspmY zl*U-gP9d`n;9jqS_XVh9Ny6|T^_r#Oj)OD7C!r>cTP1rdIySlnGekfv2`%Rq=ny}g zH?-$7UTd@?AJE)$bpA01>5QYA%vwf-Jcun}RLT6%u-d@fRH3bviOb;4m1BG}19G1g zcG$h^T3}6}y}8Ul?Lr#Jo82pS&G_pXdN*6p0#+Lln$7hM0FLuUI`W>S*6E*iM%#pvsk>CAA zY}@~MqhU~|Nyf^jU3#dMY8NV4RCe>4$uxEzo=RlWWLOQQYk~SRWZtL|G$Ac~ zP!qwZN=Ng}=zURYzu|a?Uj-ekEu!P7F3Ug@gEz*gg?3OHwyuO_199e|9=UJ*a>N(` zKW1H?<^Nci4u0-gkS;Q#RF|HPbq?Ayi|6ZRk^6fQ$-|tCXcBZ_(4h=X$DLY>Eh6o+m6=RF#z@y1e*xWuNf?TZlOqz61M3mv@qKmy&pynDlfGveVm+*}gr7)$)AL@~ z;zZw5`c}ZlC;1%u0F4(?c$AIMdK*hbYh5|sl5tVF1PD=ADw)w&1g$0W@XhF~=)BoQ ze5Pj}v=)OW4qA)cW88ByIyL$(YAP1-i+o)!9ngF0qs-*D*^7? zKrLZLUggbfd4#-p4r@P{XWkVGG1^8XBnFdwS$uRkv?;8|mscKk<~v6el%O9Bp}2PR z!!3Od%pTNABp0nj3t{6?`j%k-+k({ax^UI7M_6C@hEQ&<^*2hhsy;L}YNx|4Cc7ec zsI0UFZ$>LG@55^K2G|1s?>b|Qvr%U$kP8-~JfmT`bQZi^YzSOj6>T>I%aPuB@yUf5 zdc=|~5X^?x{E3^{Y&O7UCj`st&un@LrdSEagJc&tJ9o);ar>wyo`krc5Bkiarro(< zJF|3YtWUlHtCZFQdyoM=Ge53T<)Qr%eK4PLJK^0Lq`YRA%&BZF#JVCkQA-oh_ktQT z#y7y$-xs8JpM=?F7UG+jtkKBP<8jicQnkepT<0tX^XfVWwV^BnH{U7Ib^}|jm)bO9 zwi^wY&)1Pn{f%KIys1hb@S|ZhBn+ijnj;naAJ#UbOYC#<0G*|2UcpHi^}hfIvlfwC zH=+MeM*lE+CZm6tJPGI@$P$45ao=U77s$H?$}Ot{p2~nI&@Y53t%-YYMh%x0KJZt2 zXg{P@5y_8wNvp|-Ub2a@bD@vSfu=YqNcq9;N2w$^>-l#JP(Q}Ar_v@>~ zs6j-m#UZ=YT*84to{4|yTeeKvC_@yZ4V*r=u3flkM2^NRwCdPy%6C~OO_+J&-dxlc zN(`&Khnj0sjdky(TA}GpF@^A~+eRzjC4Z-cb`DJ5qonr}2cb!ODd`=S_d}lN0?mXOmM4 zj1}%{@;SXv&?9Gr)4HO2N0@s@J`heDDi;dQX6KFQYnSQSUPtA^h0dk^li1TN?pfqm zP0|?A3nu!)zW>+p=pVK-``?N6-vfvw*}Hju`D46#otMOUM zz9;z$JD050N?)!-M%cAZXkrrT@7{(uYU=Bzvj{`ptfBpy+QS1@>WdeVi5ehC?QgVLsxH0yBn7@{#R;VlmINX?=Pw5$qR)-}IJS>XkT=bZ^JfeaI1Tv?JpEA~o*4-UNY zlLv`X!G)z+68!tiFFM+&{7*gX3;b8F9l2{qwgrb$O8)z{-v5?x7A!ml{>?gC2h!Bn z+gui~3E3r779Tq*I1KWnIq*AV7NrPkWzfn`BU9n)oQ53GEV#Qhu$QexPuVK3Aj|rG zCv3I8D{L@~wtb*af|twJCJVeDQA&Jz%F54}*N#h1mA*XK9zL*#Zh)jrTjCq{b|qMw;c zq{lH%1I*QH*1L80-uUU;4u11*_PPpCQFu*^KN7Hm3=wgRSzlPMpbAkX-P4|MrVe^{V1CA}z zSOu{vW`Czppr%iEv@zzl6$?7F=v~^#CiHdmv0G?8Ybjs(FMa-lt{?h*0cv`Qnf54J z*XZXG^HU4qeNSvH!|b!}TCC1#OUfgE_#d_2e^m#o&Q4Yuna13RhOR2{?+*UW#lPTH zTw{<&7UBO|L_KUko&NDEtqtov>^p6nLtC;@AK*QK4R#uIEbwEJy{;mlp`^?rAc>y2 z7UIK*qNOcCdDmQH?W7Yk+naTr7(9_nAi}khF+k9=@_r2|+{$Kl9aaXLL z9d~V~(u{VAY4*U&KcO{%H$S};tePhDIXc2`M9I~E-|v2QH7>GIbHo|zeK(?yU7ajQ z9)Qr2Q4X(;~hbzPpjc(=U4&F^>#v7&^+jO4pgC zU3exKc>6F%aADLf@bHMA<}*c!#m95zi|b?Uc|3UX8>WN$2xNSHtB6iHm?=izKku#u ze&=TSV$38;7c?Y{+Q)Cxd10%YN~{do>^=otmE!EFY7SObiY^9cJl>8o)3X&G7m$v0 z{*wUrNF&^A+x7~{4JF^HvQyF1BFU}!@rl7q@fsf1|+?}VYs3A6xeR?_&|3kqb1;TkW%E|?*?73Hs(!c|V} z(>nMI`N;YMIVJ`DPk%*d7xr!=yJ-BElRQGW0H&)1!RKjw42 zw>T*%$7nw9lt+?OH=>^p)cG&g!LqMwp00{4VQ>~k`RM_x-$~ztO*CyIqq=^LnSQ(Q zh1CLX?$bIdS3Dgmhfa7q`csm{E0AAlGIEyQzUc|8212M|7ixH?i@pPmO&TooaIwra zuK#(jxgis+N4a@N8MqLpGgVmI-;`_<GP+3e6GYr)W99JVYkz<+f%3!|$a z_MK^j!OSy15Gz%ZSSqPlsR~h3)0*!ILmAz~s&g)I!Xwa9%9`sW^JT&q0RYRbeim z@`x2uL=9Q*EcGv|?pa0qrnZ9{a3QaUi<5+VkbyzUeT28s`m9a~^u?SQ@1#^t^mnbt z*H^KS?9q!E<~U>iCEC<>uT3>%N52}+IOMFrsC>1WB@&iKzpzHc4~ID;Rz2iPzk5x6 zlSOVIC2TQ>4xuwbjQ8MM2lEvyZ#(&lox^y_hQE$KbjAKKVsN;6@FCQs5XPjij+u#&AXr=9{jYbq34$ zpr`|@&IsQ!(C1-~%xI7`+>J(})JvZikhM@ha28)?M2=;{Wd8cK;yYkUlp&)EaF%zj z-G2+2Q8?J)V6)RsW<3|3s3)+yD8O;r)n!2}5AJN0dG1ELqw???@YAQV1<#b=eHL_! zEniD?z;54 zmyM84OpR?NWYWF8i|W(sU-^FTSZKWOe!IqQC;$Dj05s!HqZDtneL*n^gA2mWq&uH$ z^T5k0kCd_U$Zkb_TSr?m#=tMZxqpKypT6u?9`yJ;JvJ)}TW?Hu7%dH@4k{ z9H0XdF|DMr&49SI>)klR%9^}(%7W0?mV%!3mXKpCA^(8IowDEnhePjj?;Kj{e(?zG z)TA3%XBdmy=S-M;_0C$vHrauxwwI7Udx3>x0>W;c?ky#<6%>(Zh?_~@N@p)3olQ)~ z%DF6ZVV7)$y{9;imj@<-EmYQ1*iKuf@Lh`h9^8Tq&piGV_EDcn$&S{f15Lp3aAPU+ zfATBH-rb-C3$eCKYEm99g3LAUvI4CBs>48Luu}B9+{=sdke>{kGzB4J9zz1Ip`m#E z{@SGs$?_@!UXTKC!TJp9XXFA%?{W8WtCH{lidx70l}v>d;7w<`2g{66QZ#CN)TG0yq9drR1f ze-quzpx9?ftwM)kcjjw7Fu8k}LV^Z$&0GWbuztLYWz&)x5M z+R^H`gK|j8$#c_S6>>3kGbQ5h^D}Qo8;AkX{c5zxn5$MuGIwn@$KN4uW#nHB$Fiqx z7cvshLD!!(gNcS+)HYxzRiPPS#aR30kc-Jajc-ULp73g9RrN{9@cuuBIUM^;JocG* zj4_2*C*v&>`mK(WVGWL+5|5s8sSy3}W(QE8^3{ZuzboGLn0V}%O9i&W)0dC9z`V65 zyxM~6Z=SO{?&FW*ZGe_05kLiwsSHd*ibg3 zj7UQ^XLvQ^S?^R%pm-3;Ud~elzfzsy5HzY+ftpAB7P&it8NCWuWa9~!`jukM{TyEV z6qIr}N;y<+2KN(vmcH+C)T1N+VSF#opB>IeK04{)8_|!6Y`=DV79{(Mh!k_>tN`VI zzY%?#nCrk`qoI)(ccV)p^jXuxccY6UQ&4)3pr4S932E$13KMBN z7v|d>%xE2uwZr9c_>PmS@y*H=_)e2c@jX;7W{{9uV}6@jrsKxSdPcIUo*{2hrXD%0 zyme(1%_G6v^$^BvfMxz}{+d}bPcg}dpDrDxk7aW}Sx|-&k`e-<`hHrriM`A|so7dJ z(kMGxM2;d~678K*;_h0CxyDXjh0XHrHSKN55&G|Em{ncCF_w^LLu!+OUyM0!Gwyg+ zFeKW^e|0@N%fLT6vxr>nhtys2kx-Y9-}N5}F9N~s!v8G#YpY(y!sb;(x-fdA$ZPhx zki@Fj2(dj2+wWuhNo-HYw%#a>ye>hX-d99k>6YMaGqC4Z z68e8{_g+|y9+ZyP=UokEKIEusS=IDl^=d~|(}QSpV=;LbtI3b+ zH!gH&HEgXr@Sf}blwXWKTDg@&juK?6`BC1CBJy;N2mA^C87A zIxFGxdg|=O)!wQ`M$*|`MYFK=RMD)hixf`SdwtB~@QBq};mdgXxNu^0Bl;vKIfN}b z+S-JZ6U^lFy+|f(!%Cbg-g7AK*;BO$7$ka+1o0lM@sL1<(z_iOF5^zgYRXh7A=4zu z=-qWz#)RYazE;*q&lo)W{irMn|0jG9<&veyey#CK?~?@P4%~cN9bRs@6WubvKJZ@f z>No5I;AqGH^#ieI{BCqWF>v;{a8fMwX_Wfp=rxFn+ObK-Yx&7Q7VI=JImTdv@E`a! z1XlW!e(d8ENalRA;8`bmmQ5<_ChBQQxVRw(kuAa#3sk z-{%IcGxK}@Klt3-d(J)goO91T%X6OP`)tcOMb3)~q@%#ne_wgTW783{N=kzrc)0!p>UH zSj9J*U2$ZMMb|r<3pLzC#nB(?x%O@Rj9ZLFF-bKxT?!BX)}!;iubV5UW8dZ9;oi22 zYntcUoV`yLHP43cInEMFVHJOU#Z>UFl{?p(-aMG`N-=o_@5rGlgnTiF3f`7OixIjN zbI3j_x6-;I_k&#iJdb@rsu{Rajw-AA+;uzfAkV?Kl$rB($@_p14IMbCeH#?TSlS6_ z-XhGmFi;bAv}TXLPq%t3Sr!(j=pQy$vf-!TE{jE4ok z4CIw7g%ocpg$!GzmFBVD#e4Qb>w;4Mh|~x1w8;y5T`tOfTzUzf;MU^20h*nw&|A~e z_ulE~P$f7nAD32}*~I7EgebJan_N6a&XF~3m5Ly}4CT#uA8TMtDRRjqDJ>F6wp^P8 z997_btAUljO5@ZyskPyOTKGZaI$?LPA3;{P6^b zMS3WfgI}rf;^Xk8L$YelI38GAp}bdQ-7f=Ypt3$(M~c}hJ#VHQR3kq5mK*WWx0(^h zzNOjdwYqTFGcgAKu+BKt-s7+CIt|%u$iHd0xdQr#he%S2ou`o0tnLeoV=MfhaF`Rx z+iAF&G3bqH@bN<{!txf@@TxRfp8~xYRy=!KRabmkL>W6!2KF^_8ADKpT`t2U`%n!x zCpv0YJ-dra0uKj}yV z9TXa)6tapJ+Y$@j#M{%m+in+bN_WMhzrDZOXbT55aS3Y{Q35)KR)@_e=Srzl;SC6@ zzdae+%ljakH1c~D`v%)8jcqA|#BZ*s>ln<83asC?KUH81^Pp^KHE;L{-wXJV6vs~G z6W2Chy$K#xqs`u?U0{~g*^{Fb>`mv^6!^p%Da{u(OdrY z+|<+e>vcWXn8&01`}GRQ>nHioq#5Kl*p)k=Cqg@!X=343F5col2r7D-$U|3%5#MrR z#KX5G=>l9@3AD2{^weHwEc|IP;vQ!O5C9Whq%rjzH1;gwb|;OY#f9_Md(`0#wqc;C2gWKWvKLIB0c|X)9CGhFz}jmE z_MVCG_L$rLXFBQ{Dx^KhY+`>8A-xe2U9@r{^ir}K?mU0Ubsx{`CFopd1J5hWOR1kh zs@=vRvm-6U)6#pUzP#2oG5w=@y8C+aR5_0c@IN8T8&R(zu+sqb*vYL)gx8;8Vs_g^ z>tO7QKtUTl3wOFoH*9RsZ7gu$zO~ZC6Ih)>vX|m@0;aj;xnKNNZO>+JY>N_bMtiZWNTsJGe9rrp+6rC zEDlb>ZeqH)L|zXs0Y4ditG`Ez>Zf(gwC=U4F0UENCJb$Pb}h-Y`+BQ4Zv3|t>Pw1o zecg0lVVEusZ`ODhy_AIBJ6#+q=gAls;~wS88evBU4H(qVz$DCd8)b{4CqPZ@0snZ9 zq!yk$t{6AUO+Gk{$Kiuh&>cq~0Se&04c8igsbc;M%C1eK)w!x9v_A7Ki}v&bpI_-_ zA7{sDX*L62PJld14S%gFJ{Vx`zAdTsZ%Zokdp?$%Ydz#+nY4C}O_%lNIVv06pGbJA zRqMx#ZO8#>^&HUH$BP$beMCo+$z>{tw+)6yT=>iOe^v3YRuvv%hNVv1dpVVQ6hQLLsNi`YrmY<5#BvFEi9X&fDHuQ%>>02eoR?0SNy6it6{j^Hce5(jQQTfSF=J*^w;3O zV(bfjWv(MITETFvKmu+TSqq7o-G#@M2av)6N&;rYa&ZZ1-~ycD6=59B=tKp7#znE0TKr$v)yBVMof)?{NH5nAQ`$t#ti>A;+u{ z=fi+g5aEeRh;NKCMwzx2pD9-OhO-KDk3aH1o~|5Cyh8Fc3A|o&f7fmQ(k|B90rSox zj@dF=_8k$wrCMV3+~{GR8t9(IdB~!awAxglTHh3F8-QrekVons@MY{+iAJMOXbfvb zf14SjXf*gkYq935Z35^y2D)S_L#p>v_*&WZ8STyo9q#dm`(B0@<|yr;@|R>=n?Alr zH5s<(^1&57u#d%4;2c<>eKZ^`*e&Xylf{+~hcyP{)KBHgf!3=UZF3t3!OmtOdf|EC zb%DZlo<{ma7g1E?|1;V42S_XRpdUNUerS_MC&g|&kF}#LIo`HNJx%t-P5Q~KVYpb= zMk$HH>=ZIPZx{dMZ|r(lhxxbAJ~I`ZmlSmxaKyn6x1RGcx>@a@_@ z(;yK%*Q=TQpW4lW_$YV)L&V20bEtf&tRN1&e17_H%)G>qU{+F7tBNiu136D*>5IJbFSH^m2Z6C6-3|8%IvP z%Jlj4RVHkwc=GHUyeO&^gC}VxNCUI1Z`4VTdf7?1F8&5cO|MPcQ8#aJ-}XQD9pie^ zakgC*{<>quG(sDACHA9w^aC%uzX?{B8oQ!`FH;`Kunz_wH&fgWY*Z1Ha=1;q^N;Wj zD5`e6J{@IDlm?prJUCMf@7wnd|Kg51>+9?!=1s(AZH@jR=A(Lcztes$nky5apTn2c zIuvDIhq5di^5`^BkN@u+U;TgNNI7+1Q6eWoMW~bdJ9m`+sdW1W=d?c$er<4P!w6`y zM(~rQNIjc^U!m06*Bw54UQG00X9?AsDce`Cfd{?o3;W(-S7(ZUyUI?~Oz1QAx`z=$ z*64(RU|+;C{Q6)b3y*2VGt$>bx}57tHi6l=xOm21d?)SBdG@-|oTtB?Ptdr_B&*5{ z?3Cpx3)kyi9L=vn@eCCX-j4Me@{HjNE=X}vmGH-$b7`daM@QIaK#889^!3FiThj<$ z&2qtRKyT^3#MvysrXU{_I)>sH(;D;xLed9{s8|BrWdVrG+v(TKPCb@0(Kf_&%&|D` zf@dJOipX_%C4S?grZnipH!q!);uaw70uK8gYlj|G7wpG3_RG*|jI`_&fQUw{jH7OP&MMl%(0@bW2fb zKly*z(F_|~m5oMI6|~mLli@b&A`e+OXd#^+IN$w`qBSwq z*38xlSeD1SjE-l3!I**2G*GOg9RK4A#xzT3C#@3y%)Mzn)i}r&>3SFEVYU;%$0XE( zn2cnb>RRAJr*Eo_NL9(va30Itzl=>~6>5$K2-d=~!eZT}#f z-5V*C=1?KqnLQUc2Foh#O80y4JvrA(loE6+kpJplADQE}OqACps#yy1P+!JXSq5g; ztPX8w_Ad_={;oHN54)P?FG3D{Zw^^)hYFQ8eM2R1{`%65oj!L5E4@>inSU(!3o zLBMzG;~T^=(4kC+PWLkLtu{jSalkIXf;9P&4%i)}p?^?9Q?JnayF3;){= z@a^e03g7Fw6t%5}skE+wwO+jKRO8iphd8t?SikJHv*%FZc4<@bc4>3jHjJ+4#5Gq> z$zPtn8|rk0jgO$dqG#ZVg1)&!yojAa=-V@p_&dbYsONTRZ{2q30CqM`8JUj|Gv?yn zVAN|Xb|GOd9v&+ydNv`?KVRa#6+N4A^(5%h8~kXb^ISSqNTsJ-t$e8TAIU!6rt?s$ z_X?S^57+g!0QJL**yf zRMCzk+jGElgst6U9UhD|_*|x#=O7DV#r_fp?#X*b`i6pSUOh=`gMpVIe~M-#P|6&B z^;kugia%WD3}4iTB(}l8$BpKbT+v@j_DQ2{k2Y*CT<*|~;aldEMnO{}%DufX-!W<| z?+HhkSrzWtUD8?~CGruh`|U)f+cZ|~d7^ZHD8NRXHSKzlG3mTzJ!AM-sFO@<+}AU7 zWKA?<{L71_FNWdX>=(_a&o~rQHNd1I&Q7#atJW&RlzkXl=LX?W;85czlOE?;ctPYz zw${0=IJ0vXR!kOmLRZ@gBTvQ@C~QC}$t{CL5?O=(##sjp`~>)F5+mP(Z=(04DQJst z5OYC}iAT(E#C-iA#)z1aM#Cygx5}uqBw&9Cdr9=6rJ{sB7gG?s@;XxS^Zbw2=p(|0B`=3z!a`Qf^4()S6)Gqx4# zPgXk=wtYd-R%vCj9$D0og(T+S={S1AyMAC|A^PdlX_Vv1*&Pb#qj z?x>3ghlp97))Ch~TbzMC2=VFF&{h{r%5o=s;J|{UOi>Q6t5ew&XX0(FU+j^5@#dAx zPBZZ)jws6WA^R4J(mKZX%Z82zeWm5%YoOvEpE=36R1CCSl+MCCx~03bALMA(LRTc( z|3u44Y2omb($lzeKHAa|KG)I_>|ayARQ#}IsL>2*QP-Iv>-+s;mPvSqg-lm%+EJ$}f=?xrqMU`-sXnlbGuxEMLT$b_nJLc}cawdv z`|r}JeBg(H<~(j4)A)|D+_-?)7e-!wpa+{>msd27zI`y{Su-g;a;{^Tude`lg{ zUz(%96dP$Pquq9 zJN0-Uhy2{V%O-rqci|82^QVNOY;nb<(5^vU1PnmA?x}KLfuwPsvRnmU*V*DYSY$Er z6WmW=R4I>;Hqx_Vd@H~I5gRi??|oBBnZzVY?h`%nQ&i&d*elh5qFyZSXq&j(wWqO|TS$=Twy7Qzc#?bc&Im94ufd7Xs2qc}z2%0Gqtk*Pm%T(%cgaAp+;4xQO0qq zPi{DJz-j~gEJGjPK-djvYM&Kf1%AHLds-SOjKJP0RAS&V(sz0{A-#o4q7p-Oe_1Y# zOUo9^TU0U!>aT_!OwP<5ppO->U#mp_r164w&RGJxMQ{lpgt`%ewr=me|G6qc_8*Ba zODS(&rC$DX*o(BGlr%b)h|LH?lhkDCRwpeHJqV>5HNBy)<+OAMbzZYX`~qRKQIES} zNc-7Ui(F19bOQJ2Dzj6hF_pJOJoa;p6{BPR67k?w_%NA__mHuEo%7LG=UckNlZY?3 z1iHeMLnwqCkiJxo=_B9I?957EB5sjm`u4`GN6dP}P>mvbb16nRvok4&^33d{6wk@e z^y_`*6~sJ?mV({vC^X3yK(0@RhL3sKcLg7Zb#`s&i z1iDm|cPK0(`~|{I4^n@IkbDn$XLfEzyVn04gZy7c%n`&;uLW&^{109k*4ty02-RGn zZ$v%Q(yc(a{GqT0;azfWTKT)p=nkU%t%#w%sq6jL8xj5m!ayLDd(aw$g9!7z;esm> zz2&jUFYiidTv)m(wB!ma-wTbBr>-1=Kfsoiq`kLH0iul;zAwf!2W|>T<ez?Ps=R^%+ho+hLN8se=ohBxZeMP<^wtdFE*Jurjx zpu-K?!{NpN_Uw%5BWaTPO=-X+@e)MsL5C&e3meJPt zISP&_R=OPVC0I^Oz^tEaj+=5W=65~O10(CJtPyQ}kEl%i9aSVf$xbp!QE?`z{}_{` z5C3gFED*HN+ZcP^!bjy)NX2- zh}$K%sol6cx{grm+`0U_o|uu7d-^|GZDm1Ws=UAJi5p4REVMT`VVT(33OxX%>TI?;pj{7hnV<5xaL zzMFY_r$6&nhGtb~9O}TAFo~xcpRsa@i!LKCKs9FJi!W@$Q+IaVYF~S(y?t$^6%sEw zeoh-_05<_CX0(z0$5tEVrn|%>wl<>VJ7GSHR_hB+4fFjXOzZvEST-!omlj6pscqHe zx#G95dYXhjTschv43$aFeDQEg=Ivb%?*E_O`_-ZQwfR{8R9>Br)xpEU?N5EGhz~s{ zt-;PGK0(cSb{zF0`a+qa60$;+_v6vjrb+vtTwD$G6iYrwjpc<4Q%P2%#yFJmEbcxpF-h@Y?#Yw zgsjYnIR+^*J0Zcq^-?+Y1a`AA1?h*&ci0@vCFz)rtr-6ga+t(Mx)8sH@mla)n*5xN zjlep*MAT5KJSqp9TjSHNGQ`~@Diz@n{i@H?c&P;cL$MTYm%}M2KOLbPt(mv9=-Za= z6g+p&%F8o$ODj84uuYx095WJOe+2_ue?m=q-}!@nOT;f*lSyl}yQClV31MFjTCw=9 z|0Q2C(bCk0F5IegjCN!pt6DzR4(P^0 zX8pZjFue#XWg6)YDS-6J@_lUEpdB0AK)jz7;t7ntQAN*!ZnSi4PWw>e6ck<}{{B*v zvZ)UgCV|*%T$R?XqP=>&>JNvd-3<2k#hS6K`zElmcmwIeW{a*Cia7%R=7Mdx z=x(D>fgH*eS6`t}yHa)0R8%ev!dgfrW#6UeK9fsLkSIkwaLPs$?U$BMG$F+b(bxJ% zX@OF}YP}A-+9l%r)}g2u)pe3Q^8^DKJ(Oe0q_s#-+zv|*#z4m-m*FkR&aIBpO0a^t?4z<@E>U^w=(Cs&q`O8TZf(Rr0@;a;BYOn%a4$LRt~YAVvdjW6jDhNXyep9P|5}bK)d13_BX~-9&9O zJ5@CkT8!LW_|{k)$Ge-NLy{NbO0C1*HwvpM%YbL$xMVN2MmM%(7tbJh^9ToHp9mjg z>eU0pByeKy`pY`JMa+m1IU3hpe`&{mFHZ&kZ@WVYU1)Cs#W_2^bJW?HQ5tc}WyV0- z6BmQ=uqb`3I2ibQ5Pt5&p5{?opUeF)_uJf2+lXJ61%8%=ONDTkWW}^~U>S64;3Fwchc$U5tw6hxNOOf6PkJX)3`}vZQx$kr6tHiwXrQYW zFXs{rqy$-5cdKnFrQ4uo5$#wazTApytJY1(Q8z%JCY2h+{@{kiLgyWLqsm~QCP@BK z{{{=UU|?)8)h0orBB6gsr-Ok%K*~})gEWLLOM+8D$i&4Y-Kw%hGz~FBo!nIqBc9B1-?>%8&;0qL!z74eInTO9553PDRZWtt^X@y%{io@Va z4PnX#LxkZF`uDrs@ws);BIW&!V zd0$FVW)u7q1_O1$3g|$QWYKKSD-46yk=abo(q3Nao$biW3#EH2m{Z7%?)Komp6VE! zF%&BtUtWgv(Bw!gfxa#<2+Kg>W@%f}fXz0`9tMPS-A;Dj*C?kVm{-V`lf`Skn;D-6 zgiXqs!ma z4gD9U4#<3W{qKukOW{m3-o6Xijp4xOxeNS+uVGi5CC5bvF5&)N|65`f?o^2h-n_y@ zV8GH_o|Puls-eGzw@;xk5UQo9T1rnIXrZxF+wH#);A>{uhOS#^Pq20S|B6tWZO9g? z-5;>`5_vqu}a;D zULfm-2eGd)bdsIjcb<8?t%+8P31_RAO^5SvGXW-7&3kmS@Qq4|{ zEauG`^nH!YjZ)Za>wt~oRM|Ko!oJhi8OXVf-2pf@f@!NQyI|OUA9N`~;UQ(&1wMaG zZ)m6-s^|^H$e}%50`@uywpcmzT36H~qiu0z^K22Y;ku^mM*mg81N>;TMl!5e2OU;l zCLEb=;Q#gUS0>Z0q$>}=E??)DFcCj`3g_D2Q!%Nopos8fFwj*-?6vPBN@ z+q+rt@rH5&G%G)6e11+2KQ4#ALHIVlZ6DvWa@dVO%DHF z4u6U8r7pt4i)X$t3M06EsUIug=@K?;0#JANcLwI)J{3ncPu>18-S=@*(?D~XAw%1p zvAD~lCtZ&&m&y&~5oErFbg8k=HnuzL z=%pE*?G9ZTg(;NQ$r{4Sbk*^g0giwd5C$Y}E!y_fJxx_ub*N=qx^!s2g03+9KG#Kb z(V!qk0?ow}Z%k15YVk(TbSZpq;rvUS-^Y1A&cDG|;c>2GLan_N`T>2wO~)(;T?fXV zEU8w7%Q_Fiz+*tgPjNf!?@P4Xk(^_9)<-moUj#hukWP zJ=r?Qs|QcP1WYgIsHhaSTmu9Z*tYpJzyi$@_g~3qFxc((sH7>-1lw)?DKIWbyJ|*| zXOt~ekH43=q~&I+&2ej}E>oMqQ(60t^$`?KvZ?>SdS(A#>*e@=)NAH{)a%;4hwHWW zB=P3Msu(+4KG3FlJ57vhRo6f}yx03fw|_!c_~ZPzI7kmwqAr3{94gRw*_y>eMjo9%!OEI7Pa-S7B+ z-Gy-q?=uW28}FKK|JJUN+4DoA5d!UldxZ$KFn8sHu1Wy zM;dVuEF~#iY1fFafHEHr?^_yR45OKNmp%UU4$NBp zEE&qZ-Ttml<53>%C1{v^s!x*_4)e-7TQ*dgCN~4 zVUIX`{S=XWm#g4! zV;k@~v37&2rmCh^4PqsUZg>JV!WL1tB{W8fPMP5gU0Y>Z1fI9#(VH%ZCijM>$)V)l z&~!PJ*c-CRq2Y4qx?_eM8sbwLINQuJq9kASUw2TRR8~Kq0`VDg{02YW-R+wthe~|2 z<@0u*<|D@7G|nlD#~aS}PeGrXiK{R0l^LViM7jJZhORAgS>KAx$Q0-+J_(Nax#~#~}8AoR!+!q3bo{>Mn}**i z-*o&Q@MYk)+&2@yWj+gj_xRHATk4yAxXVA)HwV9|zFGL4=*yJ*+$rqs&*5k)V6Yw( z&oH>Q+5fVzT>6|Xo}q!pCase*pqI~>NIx+ZIL-%sb|qgP4E!@#=1PTDZ!qxRLA#4~ z9R}<;Qi1)F%>@H9Zu4)m&{BKb#lX7z1E0VPiG7++U=4ZOs`V&*MUXIb`@z$Wk7`s3 z3|3}k@5&sS8!K(6Af=vIKB;^&JpP2+%pdbUeP80$O_X_cYp0I`ukJA*V+bbBr%~Ap z7$ZO!0u#$$co%nVeW25_!~Xq1?-ai2A^w^zZCTHDdhdRQ0SOg=?lElBbdN9H{jY|D$3UQ(5LT)-n$t&Bz6cR zEQBn40G=N|9&IxpO|@a%9gzH%0H{~!-P5*h^3nXLz}0M@&`X=!e2_#_|J-u#zjw;>;Sk$4`_8)m zS(t8j+WxYzFK{2DZF?ap>t7vNgT8o=kH;$HVc?ONiw6!*{6G&+5+_;SM0;vQIU&fv zR~HavmB1b%6k26D?U4h4usiu)Ha-`YJ~^J>;l9PjrhJHVFtF~P1zNVF(R0><7L01# zB==O31<_6w=#OEnoG->_9pO&P^G|JW8R1k$jxAJ*8DluQDG<1Lo6=HUfT|ycm938S z1s;BqkYE4x$w1)K+bz)gZCLqdX_2`>QkXxKmYPWqxYDkMzugjHJ`mDQwC<1=)-h;x z_~w+yHmxJr#n$eqtA=$B*=dy9)bIkyl%*dpla2rbpPlN&8M;tOsX$3UpGcYu$>L(5 z`UC^h;Q11FSUXTIaL3T?e@Ue99sj!`>4Tm~0=<_+{`Ii$6vletZ935QQ!3_!nYd~N z9#^Qnc(fI(AuS7LBv(ehMK0m(wfF|qPK$fdeX~$zAYi*a)&AbfN>ETG(27-yvs$-H z3xP}((q~jA>QC~pl+{D0Z*OBdMmA|A{`?+XNFO+@(v}XIwq^ry237CwlG8xj2`N)S zgG~@ocb2qV(lC!>O$y*WRx_X{v(4|LM%aY_x9BWd`gqCvXy;*s z?jp+QUZ5S^vtXc1B7R~r?9z!pXlo?NUJ-ms2n}Q-xzJ2~m=ow1Un6}--2nI!QrXyq zaPW{W1Nk4{r-{DEB2P_;dhG7T+a*C`m}^twA`fHO1l`Qf`|%#4O+HUjcw-y#?5zcr z>e&|~69&3@XhA)Jon&7o)W6%%s@Zb?rnU|BZ>!v@H9g7R1JYqe4U5+U(vdO?xHYfA z9|`e`;yw1Ux)z0(uKUZ^0!H)mV#KB9hQ9mBXM`0fD}8`RWk9U8IRkc8q3cJRwO%XI zSVLbYJs3!%p5SE!`D<;HcY5%Dc*v(wlUG5^vc<&j<@eHj9Sr>GZY}0wT0j1HcfGuR zB%>s>GTJsiNLIlr8|^vZWx654OHX6<5DsD`si_ypqbJsoBz(W$bd$XS@doej?8MBn zn@!BlI`zKDYNy=R)oAOCx;}QYDowHPL=RJo)=S8NXP{$7D8pU;aLh<3MX#m=$HW9h=k2JfaY`tA$g z7G8b%#KjV4xg_a}hj{R{?zmCvT$I1#&S4i(CFSrp#YRMUPWk3QulLgp^BRsif&#N| zDey~*xYl}KpzlxA&uH%)@*gRshsq+{2u;~C`_R%@`yz>R@`*r)$cjWMPu;1--j8O( z1$WlFPD#IEvR$xf^~@Q^x{qy0Y~V3Fz!nv=!~I{Hu|v5w)DgDfv-V-g6@K&*7ozq+ z3#;AZ@JMlW7V%vtJ1)cXi0eb&9E=Bet8Ljdo^;n&m{XyHXTa`&14S^ji<=nX+BBZ? z&fY$=a&R-`aUvx?)cKWR&#fW5hd) zk!dE%aRGAdE_)|ITMtDpodZ6YD zj4N)U8SfL#3Xp$ebU4{Jd3cDTy}?1r0t9&ajx<+ImRCyYWEg1N#rAFX1NOQ0u7#1M z{q2u9c<7?DqelY&WD{)QKHE^(Kr?;;kp3Y}!Ik5-&~Or8RRcRE($~m_H|Qr42e{|J zS4}YR^n-Ly-CiiC%R)NoG&efD(TFh`lC*F*(=k}4ySGW78wj^896eSAOe7%T8M^$9 zons))U<@7LkvDWIx`wYk3yU{J*O;{^_7{ocwp&lGEcRil;b?G5?JhcvuD)A&1w6f8}?MIw@@`8!Sd>SytTQ4`U}&1?*d*Le;|ZOT+v$p+BG%_dPJ zBVj~t4cn+lT;vV|Hp0j7>|zm{oS&_K6YIvClFFHWOPBD{hRj=<4A^nTPu)P)oOd>S zxDsdkAdywmx=D1-VsKt)tpVK%4#Qc{#0+L4^4bN|H()yxg5`Os&A>wcz0H945{$6r zJ7J{o2_sMoCi$r*qr*ED^<*E^^UnQx z+C%kxiE104_;Mt?hqS7HdEvvARO9)c|EzBj>N^87FV$B6pq&4`wxV2H@jq(2^FeKY zfG@GoJAWzH_Df@bFZaloNqWe0y8PS7m+hrJ8Iv)RyZqmGzgM^`gYI?t|Jluq`rAL` zxyp7xs!J}1*Q{achzqra{6-EKn%({+pQRzsQRAp&6c-LTVoL|B8Q&cz#Vl5N5ff8t z6*XMU*7pj-HnLN7iA=}eoL6r~iW8YIW+GF>eDl!%$BYJhGL28;N_=@XtQ=^^P&QrB z9N{jPb_P?Gy)gZ0>A1}NxI~8C>xz=`8J>RE>F3h|6ZZOH%q>A?fLn*!Vs{Cnv%MySj`K2+u(;QQ_1 z4?CH|IIatyL1QR~T2LsB&dtVIU)~de>-X%XF$W(C8yuxE#+-wW{-vB4%|$0H_fSvY z-1+s#@PS8`sL+u!+mgVe)$}VCKfLtsHGvhi+z#Ix{{iA((j!Qes6x_w*uz1}C-J5G@ zC(Mj7*RyqAWhM0~^abnuzM zkC4-mcR#_a93R)Rc1Rp@S42$qq|~QU?!H}+w<*V`A_tv!bp0-?0y|zRi|AtI__26` zJP^_zPZ1tlFQ>dvlX*29fMY$p3bsUi#Mo5t3UhQ2v8TsEx{U69VvUYs#f=xCb? zy9COQ_K82!+acN;EjS_aRf)QD~7FBH_>Em9{wO7VyAfXl6D*t6*hs%1 zIHSsz?r=k|W-HJF6_7o1j>S@RSYJ@&7Z>D`+lZ%%HhIzpx>mDxoN6o*^!-FIrty z2Yso6Qb;`w4)`oDUj@BY_HnLEHIC^>-E#HjKvB(1Vn4^xiX+JUXlKSTz~LZN_Q+1> z>~OaUPhxbccyNki*T=eMflspyW2VNI2Skb7q(i{%12rDlb+5!_!upu4xfl4ay9;s6 zRfR&|+!f$UqRq*9fpzP30)UX-!rj<$QOe9)ai;WJ;bXME{^}!=zKMK7q=G{_GBB%i``Q@YPT1b;gkBE$-FOt< z33f^Y5FTiTE`V~y8t2WDi@-cjjV50aZ9=F2nQm&kg$j#rkMm#Kj?!;) zPO;4rgDso9z$n05uQt6XV&vd$S3}B=ouv`_`gp&cQa{vPuc!C>ZTBo#u08yILmqni zIhwR^6?)&cPA1;&Mybp4-N->W(CguYkio((r`ik-*0@g0 zZxbA}r)7;ViaT4ry_VVOEbj6DscS3R_Yj`TS?k0 zOn5i^sBD2nK##vt&SQUj$I(wZ~tfccdLgx4*Pg-w=%*$w>H> z>k|>?=9@po3OT^j$NQ;IA2A5ALPTFrKQGc?Pe-|M#?{v-6d{LrW5G3wRTx)X;|!24 z7?t}VUw~~Z>jfWTFH&k5apcy-Qe}0ym6xAU8ndpcWjW}doxCYA8?`YnhDXF)*$V@c z@Q8aMKk*`FwwcM;ImsRl%nEoaS&cfg;Cf2|4x=f>4eS!kzoF8PU&|{nI5JWg_%j!@ zkhvza@Jma(BKR?E-fHtU6@D?Nzy+Vw zRu>Bd`LkFnW_IQk9G7nBTby|XttE-zGyu)0dX0GX>L=IWGe}FGBm^UktVdOy{^U-< zC_n-|7{2G__fhCB8ZmpE!>r8NOBeQE-`_P;3|^h;H8^z7>y?@dk)8X-D?J`?{K}}?=!0{AnFurVs*NX6KEy{4EVf9+eqwHM|oK-#Yk~Cc+{L zTNCa4R0VcGv0LGN(Uc~|O+{-l0%}}}8UrI`E52(jjj5k|q6v3?;l@`ETL?a&85Ele z{rpRs=grpj&tR^;4KE+^IyWg+Ugv187Ps`DvakO8e;l@JeH_b~JN=9N(U5o;9BShL zudY3et&&*bt9F*%Szi{n#Ts{cm8_kZG3~2pGwO38Q0X!_QlZVaPTX`!>pfO7f4a(Z z7&6n<>^#g3$;d4<#;x*v3f;ht^)Bj*bJ40Fc4x@rT#KidUAjLPjKhr0%j<65aeOVU z!7&Sz!-nQ<(0ig)_{-bO@%yWAc#j*t*%*v8lYJ5Rmb5afdYrDgz+76*bfSja0xa}D zf`R>a;^9esrmdgx1z_ep{|`$?7*^KM_vGwiCH!r?AbPHZzUXt1rA~13a%x!N{3BFyw|{cCb-mN3LhIhzx^08f zSEMDs>#xGcJNB(?2HNIS$A0*8S5~!`LNmmd$-&brG#*p}<_J;2@%sem>b@Wvu7tkY z%g`QMAb#Il>X?oumj+{>>d2t=np#Yb#qiPrY^CxI29>E3K2c3}BT+T@+UFNC#+YKi zSA{1;Z=ziZ?7`+AHqCy)jQLfFcnZkkv|@bJK`R558;)|P zw6xeZduhdZuR~Rq)j%u4#~svCbT<*SS9IAb*rCwMQP#2D=0plwF?;+q9V*QEl@HPm zKw5p%KWq!Vsv5ilr-mm!S~alqEK}ELE9ohces=qdJGa2=ZjXPXoWqM9^jtQ=YdSuV z2G-Jip!D3Y->qh|S5vJK&3tS>CA2a)(ZaKPGbaJl0Pm=VS3&N;3*rcQmDGq~oD#h= zswMuR@6BNr#BL66a~AZoU=Mj`K`-U#%PS<}W@dyC+}}7-QXW@j-P}3gIfS%F3)4>h_V1Pxx33}>>C0o`Ro$NW6w%=^LqTZd_peujwIU+jJv=3ZsH4gec6b61aUVJ zcg6?(G3QM?o+}$X(}DHh6p`k43;?wj@jy>U60v-57cZrc0zh8JBv3)`An7tj_9 z*z&vr2W0+S`5s88{r<>K@WsLXRxuCB%ytlV(;Bg=wd>n|{IZ1kZea=Y6^_)CLu_R( z-oCx`FALYY;s*BvN8QX2<<unQcq(Sr2rnV?`h@X6b2SUO#7?K z<2}0UPwZvVcb4DBz7YL!_Qqcy8|x*E6OR zeP@oaY2nN9gd_^2aX}s(RJON!vg+QJ7Q)M;YA>w>$F|U%ODJ4Z#V3IHz=#LI3nZ^4 zi7wde0Yil>te#J*0B-zLR~;w-b3Y~Q`6xC%>2-tI@u=vyl+HYvLdXJxEfmhV1YDz} zEw*5y#$6$MBW7bGFa0PtHoT7az_gzeRb_mTXIhn%SrBA>$ z|Hgb)`qEsUP4=RU_{%No>Mt6&@fQEQEq`v;u_t;u1Z};deLBAvyJwy4kaSDraa;l?=d-uY2@X861fY=XY! zM9+^-Jf}I+t^vOT{@O2}^2J3Q#yTCRp+3fI{tld7$m$z3)iIE|X{&+(ZSej(l-nW2 zOexQ%dS8IY1?V57oW)t`dy-cGReiCYOa>XrF^)I#VYtW^9@uIbG|fU8JIY3!7ngMrUNR zUVa~ARQeW#DScxYMY|KnY}VDIYr>wX_M3KH_;b^CWGgGxX_;F{)BQxxZ}rM|(_+%5 zLEBY7IqiMvw|YhUtE@+I>reEYWvZ;b&+FqGw@a?966>^9(i&2|d&(N#g7;ui7XkQZ zhJNsOvv>M94K$AA9J1Psc+g`|A+Q)R^9#wR^>g>bD}r=74ucB;$0W4 zn<85)t^KG~{w+5Ra(i6vS92k&otw>g6I|dSjD_b&1JT;d*ziVh>jg-d4LtaDp9tHf znK%zIEOy}w8~1IP(g0bm0~$s^ynYNmgcaWC2Gn1uwnm;qx}b5v}N4ca+ViI3+hsNK0jK4k2Jl3;1 zXpKzh7iCmJ*R}`}Y8s0vuKgH;u^5ZwJ1EEdcj?g-E>Y*k{#>{y-7r@}Mub&YgV|c{ ztBhtp>d7R|0e%bBlu4Z7NdZ470{o!GV%X)ku|s_oU-*8ffvsFO{!y3(|F&Qz6YgxP}&Q)P%0}&DJUhSTA!MATOFXZNLBo*G!q#2 zEcRI847KHBqOqAuB@e)C@-+OOz8T!2hh^CIX*4sLT#ByORtEzsfYnViiynxqbWdrc zyZmu#e@JQZrr_Xpl_KR_SWblI2>K>3f#Rh%eWsxTGo{sa7&Fxb*xlYIJ^jV;*fCM#lav>fkT5M z9ETo96pmDGvjo&$juTfSOcOPBdc;IBs=@$zfr>uTJC2o8iaz{l%RAWEvIk!t6 z>8Z779kunNk<6eMJ}O=Xk{fA1#4-0ylieuc9ajMFm@M)B;3~0eT)=?r^2+BoV1?2Q zsW|iuapje-UWM%lW4Hl&L2vQ4NJGW%Rq33R#a`5e|~2? zH18H+XV&ST?Bl(3-R1Xogt&msPST(3@>`H9L*BoE=Z92u-R1vl2iboy;-8!$-y#g8 z*p&W%0@k|>u$joK$WMXSZtIVS`hFtpGqW{p)lUKE?It-t+6mpmeo$W5otE_+w8Abh zl3(8yvPEFTH=Q%#59fm5_ZM}B@_Y8S7h@@u&yw49a_uRfOCnjPZgPimX+thQbjnr- z@$1~9kw+WynAORdOk4A|hTiI;Uyr@=&*8uNv#;1)gYW=2GM*RIzk zMgXDEou%^iWj=uK+^4M5+c_nrOh8Ngwp-JXMxHcmhSCJ=3rc_%I$3UuF8@Kl*<~IA zpQhj^bwRg-a?>XEy9~bw%=CtijckI-ebA@m7XvSo=fF!QZ@bVB+1=gbT8|c~bxQt{ z&f4q}*JRtl62JeZzZkJz#J&Peb#J!Wm1Mhy_%=UD>(#KrJAL!s8*DPmCqKrN%}HU7 z9_7)GN=`5=PAq;{CaVLdtPX~;9AKD;-_W7zO(&<8C{n3lzG_s8%-o*|Y86~4(EGmtB)-W$3REmQ|m<3!0oh>T8cBpokcDQzgcBEF&8nmOd@!HW^qt>J~ zYm>Ce+Wy)Iz?HCC6+r$pT29MrwOXAvOsmi;wc*-6+P>O;+I(&Tx1CwXJk8J*+zR2J z@BQac4z&&gxc-O#80L|2lP8XwXcppEayP8YUn{IzC9JdO3-(Y$n~KF^Vj9%6(VN&Y&@Q}CVyoPN?4auL{agG zMIN>_`S}k8XRbtf4@YGD@`(qxkxA&jkSMHrA%6|3x%|P+F~So$>xJh-b#~m(=6+@I z=o2|bMXU0J6*-QgU#-a(@>cz7CB4pa+>+}la?DS~Z$I8hc+!!PLd%CQ`bu*U>3+{iF zpFaq$&{Fww1M8UQUu2$N&OG(x%qOzuOcB=PJT?LyWI=kG{BfuxIh~NdJ})1a)Tlb! zFK12@%t>QMtkwNnxxPvIdn=1ohF1%6GNv-ILVJZ5(=k=PLo5Yr2855=9ENPp<03^q{ zfBT`#aF3q+zueR@xHls7jE=FdT%NP$`DHlP2sz7_hk69wFmKI-Wsc?RL)|U+S8LHB z)~#jc&;R99XoNM6=ktZ-tNuUs-UKYFBl{b!dwXROnni6Ewdr;NQNp6)me{hi0xE8q zm~CvprE$eA0o>X(Zb<}9f{;bUB&{YB(Kr#8VD@e1KM<1{jd9XSCNUUeL(_J%_V+ut zZzDR%%zWSXJn!>9?>o&?RGm|&mV0VDRrl687=BBZu0;2H0&T3hUz4O+ymXFc7|nh7 z;hzTXz<4vOo?&KDL(Ht`Av0TiznK|d+Q#<3yp7F#WgFX7v5o0p+{QXEC0s1u#&{$( zBV9b)6{P(K(qwDQFYb_!`}`u0KLX(+b^<#IF4Y&*(zAni@0-G;O_q&HJfS1JUvj>;V znL96^rp39-moG(EoP%dEa#EVzC3E02Hi*Gmg210>0cLE7=*NrxF!;XG0!IF=cfS&R z8Q4iM=jPo+r$$a5I;%)G@t^1@o~hIv`LI^4B46FuWGDX&u1``vBgK5+3Gl3YU~hb& zhAD*n*j}L=knJXT<*Fy1^x!@9VR%x4X;q>8O=wJlmuyZF{BKWB6@1*oJWBY}-<Mv{-N5kP**^wi1Z&z_k+gZ!DZX9#{q8Yd-qo zIr-G5g?fqk`bwjD!hd_(EaXcoBEeLD^1;7ftyk?^-skP|J$(NES}$RofnRb@e*Zhw zixD3%i{OhG^4Wg)jdtjDfPbrlF$;W{{SkbMKL{9)!;Zsm1OA8@Pd@z)!T(D*V{eG? zSxoUJjq~BcFc$@TvcM;faKYVFn()2K?h?9y&(8-UACy&<)14 zgsyrg9?9>5&jU0gp32`S`kO_6tLSeN{T-seOZ1-+{oSHpD*Agx|9R0b6a8}0e_8bR zi~fu7Z75$4pP4b2!j}i$fd%{q51uC5A&4h=*NYeYjh^_v(VlobmM0!~A%2r5J|^J~ zEZ{eL@QLF@JYWI8#e>(6zXJ!$J?#?*|Iak9z!&%lUutITdHBTda`@lF-v*z4FT$sg z0Arxv1#9ksFEh5o@3_)AM>D1BTPJu?$r%6a%Q2Q2WU*9-phU_8aYAjXHIfeEe= zVOj?WR!TkT6<{^NDiJ23gy2Yj4*7Y;8Nq$-fxGy?H2xH?>#E;tLcJL~=u7y4BHlN^ z&xh(6AeNWdMZeq6Q~&+Kb2GL^PU&We@wARmJgp<-(>g+aMw+MIMv-om*uGOmAI&2? zM}ZjsuH;>zAN`ikqzyv+S0bIC)FHs%Nk03R>Ad;DH-s_L< zqWk}sp0R#H|8Pq@K8-ZuBl*Pdvaa^Hn-0TOpd+8?_Wy@;mj96MyhxYaKe<2EhkPpM zwAf!crr-&$;1xnU(7ZzN--+=W_JuHRFpCKFg!m$kTb01q&Eh=3Sf~*H7xZb$58X%b z-%@&DPzx{*hETkUeJO7@wUZ@d6AcKEJ*$1+|;=`cGA@qZTc9U|uYW>>z$JoyHD@|ChrMLrA>^R?W? zhj_+^dGhtp|4HP7H{Un9_~5A*4>^G7&qLxRzd$U1H0Ob%`61^C-^6=hL<{8`S*Soy z@+rmhoCgl&JoJATVOlsTUdxgN`ko^FcOpEOdFbOfPdvkA0iq9r#7aJ`E98GA!bu{1 zy9hH#A{5WF9WRmJCbri{Q(8TE-%Y~^-zoAtcTkH5ADj|S_zsa@RUs}9K72zE;oBJ> zN&b6*?E;=5Y0T9^esyc z`I;$xMLtLb-&>!lA9%`nRLmFSPe%0DkO7rHTf}Q17Z4ste}do4Nrd>hUFkyZ${+KA z0DlU3fzoXY@%Te}f2XH>7j>l@B94dsfrMY$h4+ki+TBt5^WyWIPxIt6M})tX>HB%= zWf01l%BBjww>=yf&y;Vm_c>Jh7m42cJg3k>DSlm7xtb}S{A5Bt-^#`b{xWgCiC^WR z-zw67=;NvP_OA4`3d(P%x4bEuDFi>`g_&9J$=_GV_qyb|&`(EZdh*@VmCqcZUayES z&-U_^&jAs>isvWzkO+J0^|}au3pt6(nHAxodrQQ7+wW5`{qZimm*1Xp&xm+SPC;)f z=R*;GS4=ljlrsPd^^l13cRvxP5l!X0(a|Wq?4EeJ2z%EFg$Pp*BYLGbJ>+tNeMHz> zp6@-d-#xJZJ#deE;DCGJzG>s?IY`bs54;zi2iPm`(#%5fp<=v3gg0U0B6`TRLVU9b ze>MpZVkGEBW>Et03PDdLs8<)A(a`N$vPm8fzTKakMGG z@6$w{<1!fA{?bLJABmHv6i9VPjxaV>%5fMc7%^<(IOr4~!&vR_Q`r9Ta*o5sBBQiF zV_6E0OC%b`W=&Rd96G3?-x|#KK0=uA=O6dwIO=HlS#tb1ZXjdx=FMZf=lXLTviNYy z&n$jb5029i1!MY60UQ^}SpSStrY;TSxg?ycKJYti%vHR%Ed7f?Je5U1^O<1Yo4@9Z z5Z;@oC)V>ncWKV@Il|F=5)QFfEzeuJWM2OKI}}ym`Ozc3%Ak=|9q{kJbjVLbk8EC<`R(Cj zkA1VP<%#Lj9w=MEiuNH4lOR<^v`RJiP8PM1D^`_Vt&>iqU}*%LDpRko{Neh{jO*GFnp zPvYF34Tv9B$S8!@A$TjaLW5U%D?INcHQinsv9!5)nuOt+#Q4MnmY6g;DQR><0?R4T zFuXm{uqW}-gpG_(j7vzE;|-WJZ24jh%gf2ru$;vOb5@Do?3IPy&~hQPV$Q0r0L3lO znUlYCcEOX&SJ2xRR*;BN7U!9AmT1_VdCOO>(6E(-h(*SDJBspi^M+C~A+Wq~Xu=9m zJh=o#=i=4Q@|-0A7tf=b=72nZPR^>8sM^YfOF^|LkIgU0U%GVFN^~oB^>-XrWoD-7 z_5P*X*){mo2fP0ej~^Zr&IGrL&77r~oH|3J8O=1FH-Q5N#0c-OH1rz{>H|-0hxBXy zr@W?jrOYWPSiy2;KUt9D`Jd0|m9S~)ij}kRo;Fu_F^Z?jn>U~3uFNAhd(P4ov+2#% zsuk?Hhd361a%dUp{>xj;X3u6>(+PKW7RRhNw{=Ay15Cdo@DGV$g3SmId6;9{;IBVU znE&jzyIUUrS&ILRifUQaEpO_8LlY8*QeR9Ooj782VlpT^0+jGyw}fD~ty}h~lO}7@ z#?Ks2huSN*~RD=osDL#CSjdlnI8Uar)E|qsNXJdjG`C^$+Ky=Pq2d zw4mnv2OoZt)_f&@?qZCH#Hjx9afT6NhE81n@b4Gae3G`M`3jflKYY9aS&te$Qb_v9 z>{)Y`)SRbOo`Pg|l@v$ipt!E0e)n*C_mVvMjY#gwtt&6F^yE>V@*m0*YVaaeZaG`D zjHP8vPfwjWlognY=*wvcFNDnm2sLLOOT(P*n;RuADtDf6+=j0)7yS(1Y*%3YITjIeR`6 zJnG#)<1n7aI=K#nY5ktb_Y6AP!m_*IgM4BLH zFU~2%)H`?fqNPg<1Ox%5B?ZDfv}i8VKgQvWtEcQ)2uF+IB?v=o`lmT)#mafJSI)&q zSe(CP_MBBKi)QB)gxPw|#m7SEacL|6Rd_j05whJExMyUw9CK{v*u!wY_WMZ0_vsty zXFd($_c_)C!VPdFik*LRG*7snMVuD?lZ)`L+wc6pc-G<9IM(Nx4eY1w8(7Sn5lInexCiEefKNncBi>Job#*C z=+FMh!chAl@bPZ??)cvd-{F-{w|tZ=xm#Ix(spmx|Azm|f&X&gza0242mZ@}|5G?X z+H_>lx!z`fpou|aX`6kcT?(DAOY+~!x7%%Y-?$BiUY7#ndchh~D|9S|#*vlJN)zc? zt29=cLgP|#>$}nz3S6py3xr<8K-gJowXedBh(plDtFyT!WRc-RK)g-@?RcD3H91~) z(DbE3@s$D^oRow8YfiS=9ofxSd(!<5Hqg&1b?8^nog-OoJg-(~;NAw)ou9yscsHf- zvY^$bhJE~ijOyPDH!8^9QLBBWgGDKD1E9^`&(5?PU~x4hW1CuUI#%tkoss&v`hTWA zXnY^KCW+$X#!IgL@&1$hrp7_zV4MBjMrfWH@ELGY*sS(Xl*L->*Z{I`+Gck(MGG2r zSk2kl9Qz(;$sHi-`>g$moZ=1OCv@nCRH-ik<4PN7`}Q zUssuj|C0JVMV2N{H%E~*H}7)(+&PuTk+8dtjk`wwiONwDqu<2M*L|79=sS@o=U~%$qfcgtvBCLqXPPncT-NxR zGn&DhP~<-mPKxI+!KvVEz$;I(s8CIV^IWGai$$r8q0nOfiTxb3!#eC-6|0Fy8r)i) zFgY^Duo3rv5443$(iqu^?1nA4v&^CzoclWiv(1K)#=t42s!U*P_SfyW*A9Q2bBqZ$ z*^R@Eq^oc$^q$LXU$=b>ZI~az5suZkP>1Vq^mf2@Qk}i!SYXsh(0$Mv7%kcTfT4fg zW(}3HxHA~#)>g5o&8XoMcG!T?NspvJ&nQ_x9SPmYu>G8e{9Czr=p(32jQZI*tyN}i za9->jV2nqOr&?vOV?%sSgR62_m%@%3^tALe$m=BetOPeMOZu98VdJR>T1t(Uf<{(@ zxk`r{`>;+~+86qqVhzV8jI5Gp?av9vy#Q@)q>*kVkxr*IXhUpxnG1c9`oBu!4_=gD z6f`&kJ8=W%MCg=wQ>*=^-KRof&{fAn&u}Js@l%b2eayk>HtCh+H;hM&ewiP+q~;(~ zPqbrO-tYGdy#<|r_#jd}Un z*x)Pm`LpDKU+ZrV!tEUmMr4qKbS$*lA8HH`+ML3$Tc*Ki$l(pXS!_24?TRnDqpLU# zS%XvIhV_$w!T$;Ub;Zf#tmH}Do@C^tLGFD_1M_Y6*P1w;&X|-n zFek!PpOfyAPGYu+?Qc3dr;Q!uYlxc&{r8>C_WSM4_E(+Ew!-ChMs?zoOxsr-(UW5) zMH>~iz*dcsER4CGf9OPSvcGBO^g8U3PRx-5fdHui#84PY4=3#Zl_xKAJZ9a(l$m)*R7+D+$TH|82Z<(L;}20UEDYW{K7EaX8p9eWt2RjD)%*q4x^FKepI z*W($D?oF7P-#|GbSv>TXF%!yZE*Sr9Umri|5amULjm$S7p3{Z2qPL~0&M9L3FEZ$4 zPi;1@49y8$#3e~>>!sYVIT9AeS(#34*qLIhQ=OX{ic z52Bf~WSMHy?v(HvhTCo7Mh5JD^aVP^AiaFMuTNx1B~=a!u_<`I?xnCdQZj3Y8_byb zB-W=M>*3-HxWwZrEp;!eUrUi*VlnsEj)-_JfS9Z~G1eN>=rs^doZ4>P1+fRtc+YIg|65BPVKUUuT}I5dlD5@8CN zTww~ymO#(%`Up-JoGEGbX*r{Ut!Nh;mELEl9Po)J?gtGrB-0hFmANLYqI=KhPkz1@fpOmm)!cy@mYsW7sBVpsV6M5 zQy<9b9Etg5K_{d<0h?n-4;w^Mnj7o33ad!7{UQ5zt~L7)n6xIcB=PwTT>8X#`AiiM z`+N=G;KxJhYeOl&9o(cgejT$9(Gkt(Hx!&QktoXq%I(K@)p)3hwx?U!$SM7YaTR6h zZ(Sx#AGcs{Ios`g_FWkTK;r7|21>Gp9`H0_%BTiHa(x-SbJAoo)G6rX>M?y zawS0Xx-oySNi{$lEkg;9A`cFEb5>p`A;MHfc?fa8zbB5nALW~`)ipTxx<;EmyTRng zTQ}Ov^L1HhuQbwF4n<4$RUwxfA;sbqiQ*|Ey1I zmfF^*gbVVAzrY)xubGA<5Z=&NlrI*w$*dYfc!oyHU>WxxEli`%u-Q3>dUCX}Q0Oy@ zVW)nmsmZ?Bak9;3Lv2^3rv_!c>S&$Y2P;SCrl0!ajR)r9aVddJ5BE0YNRmrPMzJ9L z2HYXI{cy+N!m7<|*A|{_M!Xpga$Z!pDa8ieyvA^owwWytC)pW$Ci4oc8A%~VMV3Mv zZcKGaBOf+BVw7gVdX}+ZLg-CqZHMcRv4M*U)&fC}Y_-qFXlSvw-d<)cs=S8A($;@i+_QN=V2>qbiC zqsq2AXojC4fh_>lNfm6l^tsqt#m7R2UTi2V150Y%&L7=9vgB5`v)=8W#l!#3?T7Un z+?(`>NveGWYo`j9eZO+cv+U3wi#^CW&wt|MFt{|hnQ&99Qu|6$6woxotRaR+BK0Vn z)0F8wGE{@JzD0`X|E8tal>X|0&7FpUHn&{vV1`2xeQUV^Z#!6a;03ax2YEKd?R?FB zteP8s^SE^AAqPt%x|?+c=U^|p%I*BzonYXxvnKwNO&5`_M58T#tp6oMJPhN0ZN^ zheIN8S)+3E2Mv~8fi~o*6q}wWjK1m0?|7>(=IFmbwnR)qg@pTxd%#i0qWsY7z{Om* z^LVER(%%l|-Oi(3Pz<0$owW0HJ6A)7kHAx6pMQdC@}z_M>0jIK)LLb)g?>i`>~5Bs ziDI6x?@4X&k*v;{h%<{%pmmot54PHU?0A9&DbO$xo=g4F?c{4-z}R)K=c0~520SyC zW2fLB1Wplk-gPKQkN*~E4zcB&cYPY92K1*OWfZ49@2U-wNBNQz3*0fdZ-YM7M#3*k zIPZEpN~2SO#v0@g%zj|b!kvfv5b#&LTR_O$TdZF%|Oi`ii5H{7_}(2_~=gS_03 zHiAFiXkKryF@vOTa3S@ZKdSrAMY<85bnhcw#9|gJxg1*{SRUBGJuRJiH3cOQE|- ziimWVWc7tk5Or?fsu6{31T+W_lr}~y^#(p_JoZQ%!7q;%ak5pMX$Yy1+xoXzjg(_6 zM%~C}jnQgqfz6Kj`R2^kCUXYaIM@XD6{KYrtA>7&&934E{p}dZi|Ixq^4(0M*j-?d+NQJ{ zp)ZZuPD3AvI(SZYb(LS?(W<|>j>VTv$b~jEV0oK;D0D%o`PqP_w#L>t=yZk!6mUjw zd)drK`5OM_Iu&nRP&R=ylw32x+Ocg(izbKieFh&{(iohUX}6Dn?amLO*L|~VZNe1n zU!^wHiCgvw!U)Qm_#tdgHrrdBs;XP|`yI+E==SDd!GcTAU>ckKca0MNL$EJ-$n^|| z59uIF>NEWecDq0HiR|W><^hb_v_=kkE;qZhyau`+OQ7RpcrA8818_o!G6zo|0h+`{ zP1RU99|itIO>DONH)^y`=($sy^X*}kud!LpkaJQ6fFZQ6!)oz8cvF)w@pr+R%GoA#rb z{TZ&rW@c94e7E8@9^B0z%B3XAqyei82RPesIo$J@n>$7Bvj#mXYk1y|Yo0wcnJN z+ducVeXyMshG`Mq5GIckFI5~Zg}QYt z_K>x_?_A0%<(_X48ky;{=Q8}iVP^8(GdwHU6lU@-^(z^c5#PthBq{Gv zk^>!=%JbZwTlO`!D8pbAmC|M#n#oms?qZtgjF?PmS)?)4=x@Z_l$a1c*7%u=>4zAh zt5N?2+%4FZlkEd_>h6>)$SI~yQZC;&n5a}yrpU}sP;!t_QLfylHTskzm5TDEwnu7J zxG^xpzd};(XY5fCm|-;d858>4vM1XHo8tRK83IXPcJ?j%iDR8VcK#?p&yu|KB4qJ0 zxQ%FM+H*~WqkYzu({G*`37^i;(%=t+)5DQG_ayE8vm;C;$E1RO`IT8}v`mw`)A?lw zooKe$hhfLdqFU_CDKpH%=xMQ^aKvJlA^V2HZ#u9yO^wa{$@vA&-&^co*a8kK(GMD% zNcR59r9b9Z+G5{j+X$$|z70B+$lq);3;r)QlB=N$6B0G~KR{QK#?Xi|>4en3q|tt# zb2CDJb`3kn*4AY6by$jh9F`)5!}6ruVR?dcSe8|_ zH8pHeHMR0>>V5CDH97gVp7Z!N)%(!yZ2it%~Z|iKTZtHCMV_WBKWrLU+qwP&|LjuCxuDsK4EVgH)2(>DoCR5l?tKiWXPz`y1gSkLPNc`!)Osg5&3v@`Vu+MlgOzZeI%4lWJOg8uLo+%C-5 zd-1Igy=&ROm4meyvioYwu5-1=mmKrkBXJmUX5+JG+0_8-6*1EvzHYzl2#lt-5 zS2z}3Wm><(A%^SF>8`Q`Kr3_3hMiMJ_jo|BZyJ+2!k0C>$Sy)D^y{mob$Y?ZIpb$-&zDWP@#N4Y&cj`Di~j!O0f z9+hJ4q7qr@b^CDG*J+UuwBSrxNg7Itf{It48l1d=fkWU40^~+-D}RYcj|uKei5tuPjzH@E}*uDpriaF zbD-eAXAUSoj#*A`J8Rx&xcsAkxu<@iQ32)LAk;Ied#-^;afT^y1hoK1-e@LEbG~%} z6*N=I>-_g2HUGYAZ_xX$&_phoMSoY93@h85GS1K&J&z>2yYutpmV!4f*X>)Hl~vsE zpMPXSSJl#K&&BkA)D1U0U|@s#SN|<7>ou z4VU~-`jt91Xq1(?1DW}s@;U>~_}7d!6h~5hn2vhh1I@~E>Q&__J*Uhvv6)_IA!pG zl}uU@f3aH~Tr$0LJ7#zyEIZwP+4JtrwW;7_51d0@tg5RDFnwHgV*3}>D|V2kXYA$c zZ23w^H8&h9c4+$OY*NXJjR}UE_8g~fJ0!v}x((6B0%7k?{{tEAHk{f1uW%*33}oqY z*s*i2k-KT1{E)!=nYAotdMz6~wU%^$Pp++?^)WW1mNiNt-QTp2cao%%R!jZRukN90 zd7H1zZZCI0XInK{xV&la?;MUEjq^>b8Bf(8qvIF6SLP~|+Dc>I4h`OBOC+!kU(0PAolBQtXDn3cPQH zhCz;zoY_4Ht8nbl^)Bg7vizEaI`nrBF?!$WaVXKX$xB~wQf@F- zNli1W25f)4T4S=?`y;L09_1Krj5gfOeYZ2Lrr$}Z3xWn=UxUtoE0lZP&f#t;-q^XF zel^poBr*I&sV>AQu}Tu6V7DR&r4~$}XBB4J@mr6Pb>6iSs8 z4;O5i$&R?4-{34Iu1Z$1I<+i4@WKg2kGdWe(a^xp!Qx>+MHKY*%gQkWJnwei?j$a9 zOit?n^o2^H$@v45u1XI(U;doi`KlY=D^wW)DFMBVwVU1r+3i(tQ(tzER~S^99Jw`$D!kVbas1)0RW zh8{X@tMlx1h@&++KeJmGVlJ4#jgrUofsH55CiRVsR7Lh?8eQr}y>8P+vrc8zBfM#2 zHmtSyT8$z;yZQVIiB%;+l8w|CxKX|_VL~o)rLTX0TBTO2LzrIYk5s-^vrcKAMk!1hNx+ARhn)^kFa zUn6&z=M8uDV&>xo=Uf71FL2If$Ge>}!o=?~)sZ@tiG% z-R{1IfS6^%(<;>k(FFws!{G26!*;vkG@Ew%R(-UKSwGr&GrThYk}MAA`bSfeF1)^T z1hjz1X(YQm@#(uLzFgKSs6Ev^$vXEx;(vaFD6Bnz#^wa`AIo$fxu8($Y z%D)*A(u}L$)~9xQA^m!3-EiYlTOS(z&r&(kFA44;!27|$uk0*plkjBg8)**Qh`C*P zLxEj7*^e~|?>}N(yq_iAUbi1s6?*{Z_|KQ5pawy{!==6J@(k$g5dF4HqU9>Vx|ur@aT;DX8=Ng}nS z+Zo`FhNV5`BhiPX0t;I}OpCM6^_#(4Ua&SD7hg$Muy|=wpe)o;q%d~kDR{$-i(*Od3c1vqu?WIpEOtjfBnekZe5?$mAm1Lk74 z^JHgbKD$v{pjR0;m&ts-KDdBVaEY)4jTFYs`!{n|-LA^bYtGTQrOUalld1VKLr*+$ zuh_9yqS4FrPB~Vp5tt8M_OY4bJymleCa7vCjMe%XB?sV~tdE{t~@QG8q?|E>yi? z)S$j0&g+7I`8IYvD4C1mj=1c;&V~5GB_h9Ug3K@i*aQP%-M5*w59+dp*7($Y270p} zi1!oR9+&+L-gMka-*B7quDV@_5$m!ay**2KrekiL=9@+texFaaz>iIk3)=tHa?e@i z0MMWfP|g>(Y0uudX`^wxku!Q$rJ3Q}kU)%8`AY#;XzeR$G#d(_?Ny5>Y_xg$_z)w_ zhIn^mj21$k_0zT?;BEWK4d~}wO!_6|QN2k~&h7cdZfQ!5lp7?ql_q{nSqguJY4#gO zSdFIpqkge}-l#t@-!#DVNkkOhG>=9ZM{UQdZ#;f~YzW%>7yI?bT+@We6_J-Crbqk~ z_NAI|0gbST9+8J{?!F6q-Kez28bVxLY###~+*+4odOGEs3zepV$cS)ADW(W@9__L; zI2X}Nj?vzi_Bdybh3^h4i6~X?VX?d|@+r)&Q|WH9s;{-l8d2MZZwY)(#m+#PBrQ=DY81m#gApNO|_I} zmMv}H$Z|i^1@*{1u%U!+I_Tt}=bjL(-a)0kVAsO(ZrDCfJIy6s$aG;Py-P#)^xl_I z)%4ixNL48}`pe@Tg99%*9)BgWhqdvs%)SJxN+cJ}+8zc~DYN!|X?7%@Z@}s>vP816 zY28*;g(fz-R(7!`-V#N`rq~L$&atZYX=8@h#$PbUq}WVb=UeSzj}+P;59TkZ75Gx= zY~-*ot>J1$vc>J}3z^^-`_RVL$YAx~fjO>rJ44;PZSuR#VYxz@FV)GGi}=RG$6a4| zurPv8w){1t98}PKXdC@*)8nC~yluq0p%wn%?9i<*;fWb)F?6f_aVh+mt%^ooDZinV zQaRPPS#*l6-_~9(W&C9Y-x$7i&|$(4um|nq5>jlzTkT=xMVE`Hra|D9@4NP}Z;C34 zk}an*QX6jpxR+x+_i?PToMT^?aclihgIPFA-I!ppTRwJmSbZ;cLwp!7dV#lBJS$IWg(xd@NdBdy~MG8aJ_-MRO(67 zfxLzS7xbJ5rhLey0{0YL1>CupIksW{o%jIFUB;Y=8-=Xo+_CZ}H9Ky50oq%hEyYE$wErvS^_Y+(P9MOMt zPrd(!XHU2XM|8`?a{R!b*w;DM1~=_6cyQdl}$2tMKf{QT7S63*z4TBhtc`o#dDfZamx!xVdmo!fl7M!@1%7KzkqR z<9*hD)hV=ZHTVNp1NRf$)zfI#GaSoqc+Pir>yB1h`8nIz zQ`rp<`pkwk4i7B53ud+(H45yHy?r_x=MpKllbfkeUqqiiwpn8sbodwhnZ}?BE-A%k z*^JSLKJ4w&`_EPu=3*R`tAj9d_MQFv9-hgz^DOyUo~_)$v(!@PIw?%Hyo!_hUf5ww z?%-`(&(;@eiz1{L19`cK3rRg#__8=6?nnF+XAc(U6$bNPs>_8@oz$T%st25Gc_HIV zjMdl<`OuUmtl~v^s7JD;RKRLFl)%o~tSi1&MC1AyVO;m@@CA1AW<{f~Qqkxm#dB$r zFn7>Auod%wtb?93@o`GANl4M&&fA8c-H;{M;^eI+TXcN z;nkVJPeJ=;#a_=M<+A1)5&{XKN6FBjjT z%K+VKl==3VWXlAhRx1I$*;T92Lai16S9yjT&Z2G}?_I*%UJ~MyEn_oM8YGz49zpz` zGkaZ<{W@5__RY*}kSxe6JlQDoL&$ex&8v;PUu9u-!%>0<&u-n>MqCDu(^OIY1-{B| z6PHv`scBIH&pRB`4fh=;4i9Pn=icMtKj!~--$tn~arrw}+HUEvi|Lfhh07Ib+hN0r zw`H91%W6=1+jKH)77y7Ay)Cp1WQ%9s|GcffNLwt90IUay>*^(E z+o=9Zv6s}Mm;7@2w>c>jIeFtQPL2^cxl5>r(c$If4xx^-y5OfWlhA{wc5!z7UA0Q0 z8l!HW8ZT?BFV?OWYg{bUxY*0}yf)uPrNDLNu1$IlUyy|E|&WfH0co z2<58;p5+O3^A*B5LcOJT@lU8t7yky|RnzF%xMnTt}z zxyaed+u~2tcucm86MObe;0B(~w#j@>wzaf)XVqTTyh2q}vQTeJv#uytoB^oCLsG95 zdS`(DX{EN64f{0x2b9g9PPW7fW9oyd%OVD`64f%)qO2{7smU6I@QLMx{Y!L0m0)$|0x7}?LX zCR>7V_keQPhBDr(PPPOHHP`^?bTz(57yEiI_*yvX|LGVHF&GbVfES?OCzSB)Mc^Jm z|3BI{0Fui~8@L10Ai1!boPBrJEAP5>3Aoqco`PH2E&g77vgNQ)mocczO4RJNjND?k zb6BhY&|Ub-#s?N}u$~ELdiJnn%gY(zh#i1f#U(m%g1tqXQjAwLDtyW^dbxZwG-Ke~ zUhw2Og5%vz|GQEs#1zQehi2Jb8Nqx;TC(L!wNImu58k@A@WZMbBLZ!8{%E0?4c?OE z%1x6_r5#I?Hum=^bMcxo7Z)GQM;R!k|1h`nQp==tC1gcg9d8S$4jmd++k2>_He_gu zO|c=&D!-vnuFNQN^%Cm$LCbQ4dkW!mEyBH=Q}O*oQpHQS>twAwH!7a#OXIv z4lo=kTv%vAJ;w|6R5to3-Od9o{P0skJtqrP+T!{mV>cQ<&^(K0In@}2XJP%zT>+YM zSC9Bvpz%Q|U#k63>vb(rLwp-~-&?5liBnh6^D7HkNmNm?g)7M^;!6$`NlLC3NlT)N zWhGhYPxD%M+bgFIi}mX1;>NjSrM1bH^b9&dd#Hta`zhGXZp4YTf8b<8O4By8^oCOM zXrYI0j6jzv()puiDscIUyJ!*xngo$X1sdKq_Z0PL->S;Oi0W9>L)*ez2gC0xa4b6G z8}x5A>Nf3^XY>vbM{fXd6Hfi}i0>${*o4Dnw8|2o#4>u_r#6CY2uJdr{6aPa+ui=beivysd3WK{yR3~>s?9TA`}l}i9i z03UR5w#zl1^b5rFWnJm#A$^Ge%@xz{5Yy+1={F+%nVMwFY~i_A1A4oLIQ$q=u5{5X z_6R~tUCEY*Gp>OX3qVl``iBJiJU}l3nkhh!0eY@RkXYo&mZ>7=rvbY|$a6As$`taX z9+d9V62Z+%_(o-Q}ocZ48G2Dp?q7yqSlm;fmN$@%HZ1OZ}ry7&yGjpI9= zt#0Yi@lA59HbS&?Cfj$iXlw!~y~I&ZqALVn4XL4ba6ONIQBE&Ty~2Z-`ZwrBxe zbkkfd74rMi&Ds2Fn$gxN+t4@AcSgalL7R^R?m_g0RN(fYZ~TBhH39wNEBNog{RsRU za8kHG0Uv<=l#0IAgtQe%=RiJxLU=Lq`5xg*2tR>*wxR#kqi;4Modx+YZC>GgJZGnK zmz!phx#-aZZE?f8rP(82Ur@v9rPi2{idn1^-+5AuN5=NVaJq+u92R=gV1}Vx0I$VO z9>|v=hEC`2-F?Bg=eOoULdXUWe&;@j&}4+BxvwEK386`D?doJpCPEWDC9KX{P5Hnk zua-*IBWARxgf;bDF`O>hG72$6J!#hDt?{Pe5tD=%jSz!3-e&FEdLfoaj*E2Jc&nLg zxepY9M6sJF4zA5xE7V=2;L*C&o}Hbbz`Jndv97)wMY5$IVsBBbSxCPwZ=E;23FWI1 zYo%BrckTN6uGnOYNvA?=U02SP>vPvf@D*wvT=UJ4X-&FL=lh+Ut?eAmB}34LuVKFU z9&7CTXh%KTdo|j0Ai^Bl@I|2oj&$Owl;vU@Rtoj%-iA!KSD<@di0R&j&xozN-IM07 zHhfBG!!@1MhHr17Hr$Mwuk55Y+=$T9PHMvq2rcNOHe8R;Tu%wz+K_=Cs}M8GQ$n{k zq}E-6m@H44yV{V}-(<^t#Ezv{4ZP3>-WI;M4bu^Oi(-YGySL#uu??*qoGt8}cjr1<;QwDc1m0s8_N7{fRWt(o&icY( z*spdvKkWzxpV^j!#d*cS9tv<*pePzDP`uM|kof9}1HXm1;z2^(;f{mEKg3;7Yu8|% zK##h%Itp`Qr?Ubz`AXbFUr@vPtT3)3GH5l%`0wB$f8M&n+1~F);~9IwB294@&9Sqr zhWh0es_NjfU)6S&HZ}>=Qj-vG1!q09gvq$^LmREn*dSF}@zyV9;*T`#of1vQNuYM0#NEkeG3LB2|n=QNI}7kt!CD^15H z?<%-Ppgavq`!73ru5$yQKH(lH0tq00NJ%qA~mIyiQLk?e_`ObB=gk}m$`i7=2 zHg(T0!{B)$lKhl-r{3u_w@V=%h6Vb-9`_G*ob3cso4ZoK@o!S+2&sD@b%>C9FH%qJ zO8xY|NuAP_I#fu#3aS0OQZM;8sde|H{vA?(h3{`Y&z=2mQcLeio!oWS5h-x8xs9_8 z?ZT-ATmx{ruJ!@<3+Lc`szUAAT}6a-Ixm4D_zd<_7#qzP5AR?eH)9+<4A*^}yomU> z;0|N#oQJE%I7`O38iVjYjGDRLv!I)_~o!bN`*|JNJ7t#K067;E48?T|aZ^LX?*rsTd zDYfWVTL3K+sY{ic04)%q4TAjG>CA1T)!<-Jr}J@?`aI4n?v}?q(%6$oIk`>QY|i7g z6HeGwXB?Y`a@R zn+sfIn-nF;gc4{y2nEbm$b=Fe2J8>m1(-_s9bjpj6eS=YCCmWa-b&JpObf|P(&=>I zc2nGG@2u{TWp1?Y#dme}kT(P3n05trVHrqgZH>aoqYuABz1JnT<`uEh1I4+;K0HzF zP_IbiN|P;2I}Vgzw9*{GN^>!%bEOAYkICR+leg1Z(|TY{?ix=HDr@~}RvNYTngDa9 z2iE4Ur5L_6YTY#bR=gp-%ESVWAaJ!i7dh<#P)4`X%E?MeCgDo8t6u^aIdCzuHEPYZ)t(#zta;#~ zm#bVCKlv_xO1k(d?c%4bi=Xl?ei9G8{3H(E;U{shKRBqni-W~DWg!lJ-NIr2f9FYg z;YkZn|4&=~{gd+Ad7wPea`%&B*H+5oNndaI-{DF1n0@F;jd;?1!oGYg>ilpEoiZg` zMk73$^SZFH7&wwD9}z7a_9qWPl0zNJFq=dg&as$_ifRwzE(_9ID-@(k}(yz zbUNMGOO6!lLGK3wK6HmlF}b>Rj!|dT~4yan1Ot-HcfLe z?EmkKgB58e?9U$oZJO&&8=J6SOSU`+>?qfrHuhkr0UPJK)5a$4V$jk)Qs)w6XrYZg zSOc(XmmrA&Ytjm>Y@oTfhf9#DyJ5!w%i}3T4#urWTZEb=Wpp^5Zb8Nd?IJOU`+)t~ ztrhnrUhH6CueuM4drG!vkyg>DR#NSBi22G*tFC7^NpXvHipFrPJ$(@OG2(nQp1mZ+ zk!;%w_|tCh{=1_Em5iZ8taXMK(D8kF#xkb7n z-qw$2r6g%mh%3ETRDt!sAHJERT$r{4`~TcxU9m`q{wdNGmvp1!K$q#xU9DR!(xI1% zbgQ50M#qD01fFB*yPmpkKwZY6u0zDSt`+OnU#x492uF%_9oJRYaY9{}BkyrSUB?M^ zRsKd@P2K81b#3na^}7DP8y(fv+R1ET@6tI-vSk)p=3=L327D0VFFQRm;BY!o3FE4dA@IBiupY zt^()b9bsMp=3+;8-f`V{SN1Esd#}U8yV5S+o$4UoJ==|UZ-It*w@u{T>wt-OTSVTy z(n0UAyYue(4rcqy+27&?t(?nI(@l78Z$Bd365xuu@q%y*fLki?g4a?{!m4S=18%Os z3*gX`2sazJM>|MHCvFhtF<_?O#S6Y0FCP3AUW^fWk=@0ML=iJ7!`mxjI_Sv_cv??C ziV}H~BJv^(ywKmp3l(^gLA(%U&S+r{9f{gWI=WWrI1hFhu&#EG98c@rRDpIVus7Rv z#or6x7sNtppt<(@c4q4%?CK$l77mDlELzmZ!k6Y2NibJRF-yuywZ-z%++s!P!D1!k zRUb&JzL5U?Ap855UR&LxRJ$giRGoRlzTEyFVv0n}xb{L3 z^Mr^Q(Y{o~EEh3D+vnBqb#c3AH|+Cawwz5gD;+4JeHY%5TgNx><7uVxO{G?w(jb|F z_gAGk#Rstuqc|Dgiw+*P*7GFgvL3lq-r3R(u79p2_%0k~%^08i_g|7-Zg9c@SqL|9n zuDQAz7`WWkROWc>vpSp~v}xB}U8`G*n6-OdA(Y=*A-^?Zeru55`m5_GpM&eDzMxn~ z`C;wXTwM?Bdf(I>IKFfU_8nON+b&$I#F>QW-G$T^h_M(g%>4tU`DEedaHJo_pdLnI00;0=Ih*GRitm9T5X z_;1^L?E>WAahRJQf?EnV3v=>fxQ%cxz?m>#_ecNx3jMMO`^J56H_<0&U=F{x9Pd5} z?I!N9v40cx);rYLzd@3xT4DbtwCWBu_HW`25c@ZAN3H1E`zgEjei(<~yY~rb-@96T zg4Wige|p;gb+vdzti?O6wyXc^c;Pu$pmDQ9?OVDcjpRd;9#*z`PZvD$Klan&8H3RO z#4`r|u064Y_QYLh3{pS?0SypN9Ato^0Syu$I;R*6C`N#2mpueftN_tII}XrL0iu0& zyk@WKzPt9>Dq)}9;f!j1t;4BqopzPpu+W$dZp{*6lPwBy9`R{SxtfhW zx(ofU6Z0XRPjGo{=8r6)nZy3e5B(W zSdOtcrNLjzrS30IZ3xf-6U9oduGXv(V^^j0qORlcRh-V^xJ@D%ZFDs$b2XI`B!ATF}QgKGXC{~WxL6C={ zRZOaH;EZ(EL$L#eSValF+rjv6Ic@#F?YYzO+_IJjtbRks)_M94?fOZwZWVX^1U~?{ z$yG_;^ax)Kjse`@@_c1T@gzZ96L+A=mf=F*`v$Z4Kd+R73Wt z_N@+c$EO&Xl#yGY$upPm7e`YYHt&sB+P_$hxXzizZ_baphI;7mK>u~OKjV#AaCl@y+ts=aP?Frtet{7R`;au#J z8iLDg_TEjxT`Q@^Fh;m_%SDBhRO8P3`xpmqxNPhd^WdI@+X?py+z`y$eK2n~W6T)f z=o=vV9{uTKvQn90he2+EUQlhB!LQ_oy{7TutjIz(L~pA!FU;4)`d^9EM&n){-86I9 zw>7b7`8V7^eo&P`W|a=&269&aL6U(7aBIL}_iN&!HyQX^K02gMhVSa98E)W?wx0PR z-0nQ!mKsJ@wLAB?-o^pskgeV?GgU3=K~|^{H%Tz7=L^C z-Lmp%+~Z><64Y?N&EJZ<*+-`

1d=ZlpzNk=JW(zlukh-yUe%sMBuCzTL5c8(n(5 zJVkPeG{mlHjI`3-OG&Nt#sr1@g)nP|1SgGv+z> z`Qt1)u@<^S3tqy>$o>MB1g$W-`hu~#v2kzLRN-5tu|9`f0p|Gyl^fW=p4O=bx>;$n z@3zHP=b2cnKlG4=GqiveXCfm$D9AIV=IFK?ja-s$!<6(NFEfp9d)|gvx;gR`)t-D8 zZeBunE4a8iKi+^l8@kmwZ3UwY*9vW@7dQOP_Z2g$z#zVaanMo@pXoo*-tjPbyeBT>vPMEsS9P+O$)O`Xw$+nQ*9$O zq(E0D?hnFA47f>a<>JpD_h<|Ie;9l7_$Z3>f4q8nu7m^z5+K|?GX#bV;3VM)0`8DN zLlQLsVFd)89N;8e;gCfamkCKUDDDOV7%q*1y8(4K!=XgQ#06Y*-OpwO+;Hj0AxA_z z)00edB>BCoW(d*Ge!j2YAJtvYQ%_gdeLVHl^K@~h`0a^Z9C~LLC-K{DW(HL@+vBG} zKN7ursy{qwQCLmkBBR@dn_Jh~IRQ7DL_O{rNmDN>f@@G|*lvTkL5#*Npm8F-4gRzI zNB?^9I@(m!9bEuAN5-G;bMS6JekLVn;Q2n!*n8g$!(DdTdXBW)T$!ME`Qu9k_AP36 z>d*4hLz&2(^l|}i#BswSYO57ds8$vWFIB6{tCh*N9vnAJExxbxvPFun{7JZlIn1N} zD|G)D$ToG;JT13vJ2WF?_+9bPj5S@|l&Otu>cy*Y=XEdD=~I6cUtU_$^A0ahANWO# z`>x!Sp0a1b&b~Vj)Wz&o95(e%Z%)oat5^O0Y2_;e*{W_T)^J;Y8Rr@ei*svx;Dz=h zZW<#OOJjsqAK^i)#}$|De!QiKSI9lCY_wTKE%ZM@LJ&7g0~xPDxpLRPdWC4*%#M6> z+jV>RMdeY~&vGN~Zc(iT&qEiJ*K||a`gL9nMC`1SiS6B- z@^s#4br4gDVk0M%QI!_l%T}88CJpXXbK7sq#Q|b2QHI*c&L6Yi&HlKl{WsCIPl;dQ z7WP-&N^6fbhJ~GgcCVu-;dd0V-~?vC-O4oh7duWUm9~hT?T+{zXyF2A%qh z8RrU74nU4~7tz+e1Fh08PYKvtCh!%>Jv`U*{qo}hjU}v}`ceaWi<esM~aRMCzO+&QuRU5 zzhZM(iSl8m6dvi6!WvsPZWnWLC!odmgz}y(#`CuGK(lHY51kVexL1-@9_*>1ZxGs+ zl0kz%RN2miXwSfZ0-CC5sqR1?hM`pTxRL0W_p}I@3Hn`u^JfL~7}+ z^k#V@bnH_detx-U%Q3YMs}RUv-XWHVT0yX59u(l{c%&AGG<%5D{zf7ePo}d7$)>hCemitgS2!`^|cndiU_9q zIMTANS?B0^J7T!W&BgVp=MtXtOb+EYjA=5}vi$mS@ALk5cHdxpm(x5;(NEwV^E;K! zoKAT@_$&JK=A1kiN)a{qt8#qfSCnH|%qnM8%7{*JcP`J8Uwtv)7jbt^MZvEq^)&9` zywiMB#baG(s@hVMmRPl+rb(t9q=(K0@}akS6}YNm)vOj<-nT5%`dF`4tg``n@FdT zDjL%VlD;E7_n@Cn3ymXmyl8>d2paz9PJilLe)UWPeXzCEA&pMI#z}@~Bu;ZbjT<_B z*}0t^{MDD~_>vn#zZ~HHfTdC8rEhw-T5E*Ji+30_Sh{*xS^2$nk77BKRb1*_$rYTT zAyK%2OxM5V_qdL{Yzw7dl++!#jmlO6Aj`RvL$J$FEYU$gB<|PJ2Ip zeL8)4zhr0~r2Oj3^Y{{VfG_XYfyztkAecY3DU6L1qzBa!_yaVA()`hrA*wIIFDD$P z>v88391(qGPi#-7BVJg2sxQX8Jy}=pZr+v=Q9Ho6@?0B!i=oAN~ zSut{}Wg_HZ=QpN_SVm%18xGu8XurCSo_oIxh{-R1=D$BC1V_L*a_14Sb8C37gEBY* zPL_2JZZsYF>P}H>52W@Cm9;_|kt$g05cBzqT1i>>L8Qv7m88|HBY&CBYF{v&m!t>j zT=R=`cK-X4j=yL4|A|(t`9(TAk1Vr0DDA#^pp<;SPG`jSU#640*wU4y@wcnH-cRE| zR+dkP(pc6hjYEEs#?F84*YW2J|CjjlL0{*x>|E;F4obUkCMe%vX*`_#nTM8SKctUL zE0{{{_F*=}9MVlJsU|{MYER52jdHByz+L`vVozw*ZEV+h+XL6zsmA8&yUoqSOGmet zG1B#rsE$bJnFK!|tiC9{IE{x!^i9xNM0J}{E()~eZKnMZdV8S_0s7f7J5yb4%`(=i z=_-+oO!si*JoL?s12zFW0%!dm8NN(u04$VP4Z3M)Ili^;^?!clm4KejjF?MJ z&=Jf2$YD07s{xwsQ%x%8!8IzR_+aDaZco;aeqLTPLfcQHkqQ>@{sJC!(zb0lI0bOybSvn=2UX&dAMUV)=+ZoBwuH8JbT_2psZeEw86>gfDydh7gBZ>sCI zOk5X1hD2c8;j0qU5;4Y{lwe0;-%a^W3pCS@X*uv|vUKWkul`+99tLf0RQE)(sC)N^ zK;2!=JPbNzX}c>!yQ5l4Hn*DdIqUF?4@#(W-j@9;Z#rn)(EiuaZ0@fpeG-p-u`4Ih zq2GzpDG%ET4H)G}$Ic%DrxhLQnx=&6L{D!cmMTwBuPGhJPzD(!_GOH(T}Aa)%~rqs zxim~HLaJetq2;aA_29T+AJ#GryW?WTkvI$%18&MO&<#9~=e)r1biHv?Udbqku~=~? z(vesPid;tFG#t?XZ&=ud!tfixQ{&?@|mcaRZZd8T^xMpe}^jCwcZ z%K;*&P?J5Or-AA;rRjcGHs`y<2&?X5HFVIZzdm*#segN7FY# z>O2;9&WWoNB~4ijwJPJ{MjuT+jCDo4n4UHW^)u%ViG2iVoQ4#ts&2@eLTNnKT!@j# z_FqOK+AC&siu-uBEZ+=tPDSVRPVAthYhzhH6QE;ZxHt`YA0bozAx+VzNRfJ( ziBZ2f19|}C!M#E~Y`q$o(Uz2=E~D*Zs$a81y_3XEsPBv3ic{=gt(fVmMbqhj{qeuu z0Z?|d>CQDLN%w2o?xU2Y0nJX86T6$*6SUu=|JG9buG?_u32=YFmnhz94V4Mq1I*Fh z-vfN{tJt!B@!!t%a_Co-j|SBEZ&Zraq2Bqf`$4UDPV0`2Z!-MHkk$lz!#gy;pzmDj z>pSR!dFzY3@s4tskry6H?I%b&JjKLTlKE3D=Wzj#0P&N+?zU$O7~MeGksUX}wLd(B|%e?sh- zRaopn?qAYjszVNTqwdnP0csOPZl^w~=eRvkSVQfz(NUTR@fd^R{ZwNw?ZqdXhKa`x zP>q*78~Zqu#(XvKDtOQq&<*fU3(*~7bYY;o8d!@^6GFRogucTFcFQi-zoM=N1!HcFluB(AhA=O30qu_@{ zD2&a){G9oS)b>ETK96?Y18bu%@ebBym^H7Xv>&uXS^h06!OnGgt6AmjfjfmyU^PJZ zW3*Sy5A_OJex#$s?Y_mR&u^HfTi!u;RzJZG)PvUkpiX7k{Hw1%Zq_(+@@}bbMf|$Q z6BG1*|B8BV^Aes~hoX9IwbQ5uTUg#`&J~;~u!$#xTrJhn3cc9Sw`LG72P#pX)Y{Vx z`Cb14hX76XdiRyUfFlJ{>2BjnKzrNa@WSrYwLq^sInK&v;t;Z8VmNc9!@SyXl(rf_ zbT9SC4&yDq9`^hCn*_%V==>C%8V7W6w5wrrigIj{u;Uo=Nf~s-p8XBgoup($;LG=$ zCiysW)%g?E4{uouoswJxv|4$&ggymdA4n0$t(hotV?T3G9L6_sdfAguFiaHDI(blx z1;s+KyTYwI)~L}}PuAjF@{rHdw*ayunrqHI&bu+YlQmyk*YtBgBg}g+f6_tAb_C+go)YkIjVk?ydAddw zI7Jh~bc(7B|LB5H9oGa+tw>LdqNRz4vWPh!x<7a59=DlO z9Er}C9lRsBzJ9uWG|plaXz=Vyl%rcvuKL5;^nT9qHJbE3PNQ3c)0=Rt=D$I2(z9@? z+nMTkRt_7|bz*<#K=BjzW#>+JC_OWgI!S2lu^i&M&JfnSO;la6`TU8y(bE(ygY;OP_-F`>zCb<9gNC z%4vgX=_b^VW9cw(wAM16fs*_Y_D&h~?<`$HDQ)z`g#Ope{_FU~e|ti&Ak{Mrz2!|D zG#L_%A@qB24!VZ}FUi{^JH|2Udq)DFb$QVLDsqjrWP7)C&iJ{a9-ZO8xAJpEKROt? z1EJK~)3*O-YH2z7$T&MaTRFC9vTt@md2(dEx?!^KxA4VydU(@XMw-f@X*jLrA-ubJ z>XhQdw3Y;%4+VYk=3_b7vjIEVH#3p?lMYi$6>5>si4=#q^!cx82)R)+#Pj%!_-^iw7WfsABP&)!+pBXL}E(Pv!II7#s zcDSUPD}yPXD(E|XjN7Ni_<(cj-G|*)@szSr=;5aJNHz-GD0QL%?Sijp3%q`tuV@d{ zxAEz_VQGx&SKIH?RNIbT0l%1kmbMUBd_!HScA>UxlNS~){QE+Fq2VYo7cD%tkbkyd zsw=Nfu5(cBaX~Jpes|cLcLG_=2AyC2PNF+WSx%J0EhlRml;Ji<*2&sExh6}4qP9`Y zd%_-IE@hn~{m+;z(Tr2s{S2*hYP(35SFt@ZcxqC;q8>wxIoY&j9E6TO#4si&8*&>q zGv*a<&K?KOED|p}x|_{eZ!w(;y}fkbvl}%p%a_nvW%*~{K=)ZCI{uAhzWyvuZr)7h zJSVWuq>^i4P3kbJ6BvBhDVI(SLqBp!)bfT>6gW&wQS_t`qwdu}(p_S-m~9XS8sv9n11# z>>G-k2HbeWOwV7gLK8WqwU}tm$TnGi)z^ku;if#an&2Ec0=&(3$l1FNl-8V9Tttu7 z)K_vH$YhWPP?SX5l?!LOdYoY!jxIr4`8{nnc{cVoE>7zV^u1Dz{(2i3=n`<-GIb7Bab9l}QIu}8^=k6nr8K7E3i2jEB zOcSjISi^v=k$Gj%(G6|DY8O50vP&mibU*qqbQ1>mTd*JL%8=@w=pu1_Phj48!8|zpab_g~x=-YrjbYcOG@%v_r*b_1Q27ivi7;;1I#nu_nY|>^C4G2jH ztU;P0;R_3eTy53Da)jF3UsS=G2Y8Wabxcq1%cRze{{INt$y0DE;YPs?I^6A1m1th` zagCvdhrU|fSkd0sj8gc6gF+L&#;`u#zuQ7xWyN9-KOcIfHg=#Wa)HXi4)pD}B9bY`0^ zO%Qcnt$Pe?*SH+CW?+jY^M2~79_N>;GyI=m%@&W?u46R|XvGaVT8c~`;vnU)LKe%F z+nf=p5ou;owUysW_x40;XtUL1__yU8R~+%hN-jsd!%-&mZ1i1{v?Mpo2`@vyn{&^q{JoMl5d6dZDTiSx-QoT)aD$= zA#S{Dv}{xz5Pw9yV~RlSyj${sfGk%z9TP@ld}6mce}No8mr%K7X-)z9S36+KzSkF7Vg>v|FYb??j z=^d6oEEKzrt~s)=LvQ&l`A-3EO#jg|g+JjBloNhSF8Q$FD2LbD{;57{pd%tl@p32IS zNR=2hNEe)-qeO=WhB%6qjnzr$C5!HKdH1+dRUIdCW!myWW^+Y=?r*dt*lN&Mf=B35 zZ-K>zYEGJe2kRZnbM>&pA}u>-%`+G;J0_$%9D8ynfSWZmD>pX@+VGzaSo0c|`LLfz zZK;T=NT!gs!H2VOy^$9a(!#1VULVpGpT>)NV_4O7oSDaqVf#m>kDPWL`dpzaTj;fY zI4r!lI3=(TcBCL_;(=wyp+(A?0zKvcOP)TQ~B7H3b^8dkJsVtqlR1m!r& zS8#WqMzy-{gEb*ROhp`{m|pIn6cnjFT3SfX&q9NTdn|PWp>`n((dS!VDeJ=)m5N!dcFt_AKTgj z#zGFTfzL0$-6}-Kb-54>ha66RIq+!*4q&vu?zw!8+912$owod8ut9c5Ufx743tW#s zt&kqWVBP1A>|_{{fF3dGlw#{Xj$!9FQJAjNY{t_{W>k=N2BXbPJgv;^8N{SA%*@fJ z6@Lh0XBa;)$9o59#xl(Du?9#-p*K5qQiFnCrxn~<$*9ouxW=4T)-pKt4u#hO-o*)S zy|kf?!n=^(jet2RmcbhVV`=PGNqOxw+zxmq;5KS64e(6BHWuFJ9))dyM=|)b+Y}xJ zIElgi?ov1@9xEl-4?nFu3anQ8h^69DP)!4jTSBdrY8v3lEIfh5G8ynZ7T$}|&jUO> zKFUpLhr{ncm|8l-XdU>2UfPe@QzN~J(I*pO8tD#8eI{rN0CUnCEVTuI*RXG^7`z7X zTEIGKG0Xj0z^ef3r4=k)s{j|X@B1_QV!);B`%5f$rGVF{>G%F%e4`K_gGaLO*8+Ai zxQ@Xtz*|`SZ?pex!S}g(`o9(i=O&$2po$x?o#joTwFOu$U1H(809OL$BnOMH67W|n z9QWZ+HeUfg&EQ~qP6Mt7te1*eIo1Pq;D0*l8%FQI|1Kb0C$+KsTtN6IT0P3Iuoaft zm~Sn7l}Ix$DeRj{q*=l@TIugBjS{}G=>*IWe_*!vY-3L=dl9aY9%gCWi|{Rg)lvbg zlP!P;vocuBTIgWJXJPUGnbBJSm!izHQWZ;cDa!1+hJODw3%`!`pM>~s2mXh}moz3g zm#C%F9~Z!$NAW0}U7OV69;Hk;qco5SSP#A9vqPGtfs9KHW=9=~> zX^burbelqSwh-L{(4}?IJ<90fK=)#ZZfJ;ZI_MtlpxeyojG)U4(G3jIO$6O$Mu(e^ z$~Hz91G-rux}G7rVW8W_=+vNlgVA*Z-J}p*M2M~@=-%k~?-qf^S|Jo1)$PFk5$-a^ z_flkYG{-7zF6-mr6Dt#tt@XeMWw!`s3CKN zGlC}EJXAEyf3b9{TwDQ$w$iCIMD!7eyvQCadZk12mMN=k1Td|_w4$J(R* zhSgACX6;)4sx_kiH7n`;SqlFoxv+`cF1<5U-VV?MekUUjxEt`Qkt|K<=93iJgYL`!3Pv&#$$#Xv!k^!xSaglXw1I zh&4ewdK%q}tsGpeoHv1M$kDnWL-^HumD$m`0&gr9OIm62NDbBYxqd#^Hl zVX-nQyI3*98R2}n#Y#2aLl+pGbe~(OJYk!bV}us|LS>T8=qyx{+0)~o{uDfmm9C)g zmW`5Hh+FHpu+z8#xz=eb_bQ`C7b}?y_9~CZ0zXS5ZM&#J=?P=ZNQID9tjzvxk!x}NUjKNBrptSD6DMEm6HGVxspXW?W5B&W4cD8p<|OzG)C zXhO>5`HQ%T^K)+1#DH6$gq7bjO`uZ^6J34OfMz#QZnE?;v{Z%Zo09Z&Y&m4xKE-oB zXoW+1`Tkph?MGOBcMccz-fx2c?Q>l|-jp;t$xUV7>36%5o9K6HDp5~U{fgKfHfuJC zYA3aS%n#I@!zyqHD237Dc%=TMDM?6H%r2zVMDFrJ$|5c?B_}AGM^mqDO5)y}U(sjK z$Eo5Vr1mV*emo&!`x|}qJ3m*1%7fWqJCnqf+2K3o>@GVKb6{C?Y>G14z&D;yMecmU zu@dpE1omV?iW2Lh`Q%1-6}KkWBb7Y~g^|ihI%fn*ItBL!VbU4Try`IWu?ls-17a zJ{OnQ#oY(F`MdH}k6ZFONCg;<@+`MMd1!4Ljt1@b>M>Sp({{{#C0SFiN<7%Sab3W3 zU%H*t&cnEd(o3mVWaY>$B{{9k=D;7GK2Az*Xor>vIwK_g`45|gXEaSoVfy+Ho9mv@ z;puuNx;A`h^iY+zawzdyh7x1DnaY^Z`9oF4zuxcpWKmr{4g1Nbq!-ZA%9RY;4jo_L zSehaUe4~qTixjrQJS=9M z&nv0kvBVB*!xMU)Uve89My!psR9^AhLU1)C=WUGTm}H4`7O+N2!@dw!R{@^}%uCY{ zZh^94+*SvylPd82Ur8;7V#-1=OAs zX%eV->0^B5U|)?dEM@5!9K+}H->8WJ+H$lfsr;(no7`&aquOR$1gUbPH zr9UEn7a05+;wi^GKOX7jrCs=6CCdkuHPc8Yq(=f>MD1#U2+w=LODtIJwfjaGR>J>! zK>_>^8J56bQIPNLGip7#nz)`MVT}9v)Azrf)Luy6k#=9a;VIB;FPIJbNrqW?PBqK` zW^X};cd+o7H&#gZ4iQqlal%w@yfDR^Af$k%*jRH>f z9rz8r-ucYd*y^Vb0{W3syL?}EsSW! z+GIAzO9wH6v<1du_aB3uA}7VO-FVDsid_MVH!3Olj@kKAAFR80Id+ORvA{Q;-)fg*CA&<2qxeM?Zl3*e3BTkKOLj)V%(0UHNK%p z$_0V$+S-jLl+(C@)L>h$I1I`8#QgUx-Hyoe^^R?6C%})TH)WP)buH&iE+u(Lz2Y_1 zDSO>Ha&Nr7CjE{{Vu@lDT*|-Wd9zEIG#Y1?x6MwJPxkpdhqKSphy*2Y3FiZJj7un@ zXE$g^I2qDyr?*74(iHUYTuxup&3y0D3%EP^+VxWMeEJKi>hc%TUr5uG;}qa^{`L02 zf9~kr2J_8J`knoEP@d|5ozTvNv!Jl;)M_^@{lW^yfIAUo^gnIu&ENw=o8z6mid}n1 z+0R8v|2`Ys7lqqqg6|%_&zvpfJtW=xj#wso!guP>N~+_>iQV?o(fj=iRG#*zG66PG z)zbb8+J~Av8L-TDLb+~>GoOHk`UE^bv5hfj_+QLEp&Yl_eW#yzNBL5CN2#-A_~);n z_kcbqcn5cwij^&6&)|z<*d8lp-)8vL1wYDe87)nvuZcFhE&+qHx@+<#*@XSz*Gi=d zEwYC?VG``&QXec37+zhD`;Ar!`{oj*+(y?>KjFqOkzh$jnMs21_I8~30-i;vRD({s zb=#Us`6t)q3r%OxXY9VE)64U9;;T3%@39EGX55OJ>+xWxd&AlTeX&CLb;0)r|4*Cx#I9&FyQu6x75=F)Wu4b4_vgWo#x>nqmb^*>wV>c6oL#p&P6 zD>>_w1~Vvktn2;t!RCr}0~+kU-aT+i51F_k?1@RjGdeD3J^2xRMgI+K(r3UfHvAh9 zM!yL}*`4`SLX#Vyn`vqDy>%eg>9%ah!v&N8jZq4J82Yq|Z80c0FE5iBd_jy8|I-ZyQ z1G!hw9g4|&TxvM`PsL8u%~yUeIR}nE z#&4mli!}ctF|9L)HDUEp&Xq-pVNLzXTuHbnh`G{3-`Ra%2IVA_Lgqy6Z?vpGYJ-FG9gFEEgG!#PpB7%i>4NU@EH z-M+`FQ-d|8CLZlFT3QKwhX3~~`R_T5x5qUlxP3=snsU$Jw)<`+2E1C_hI8OPWVUT9 z?nZL3x@b26gZ`Bbu3D)r+|<{V;h(u;uImhB1-K!{O#@wXU6jW?NIH2@@20$gL(2CF zDgL@AQlx?WUigD$rfd5QcT;HHbEX}u=)2u?ZWU-v8NPPNzXLOvQh&XMivxehPjbvs z1m92HFM?v*hoI)b8}Nej@EzQ@ka^&xqvttTU8TMw=gs-(IZ4Nr==HRR*0&lYwH7Oo zdfwP3A8RRA`ma}e0Y{;K!d)$oTXzK1@3nlY>k7+^1g-ip z+ogQ^qB&P!Ck;)+pF&0k^{Gab&|k&nmA4; z*;SbFjbAr$XoK*XAW6_p+i`?6?z><+CQaemnaYrY(~7ePXwt@u4r$qiBU!*1O7m#l zKAK|mX2x4g6KvOD?=f2VQBH5dN?u399!rVxzKb(*YKQpU1zn{zXg%$L<%$7!rtjov zFsJg;w@M6Z+_N&G-0U?wg;YAHUz7I*3f#OH!|Vt4b@xwwW!?;3;0z=rP1oeH%~;`T zE1i&mfJ`anVO5Q#H&c7p6Z4n2Z5HK?te|swR1+~rmUk_ux(3<iUSCWH6;7 z-kGScpfzb5<|OE^Zo72N@FYj3Va*p_S<2)y3)~v)A1qiq%u_5l%`&0wCt=2?@^?SV zm#V4EPcM9fGq(Ic!fJ35^8ZhqYZJ>u_5IQ{IM4p&X>jk<7@Vy^=Wx4H(<>d^0h|!` zOrsh)KW^8ERW-xJ{w8%(7nHz8$(9%I)a`#%ialo*b^Fi0uzwymIy~jv3NEq#h!ziB zk+cWiXD5YSaUus<HR!V+Yk5i@C`c?r+IL(JoLpkHpm{n zTpfWN#gaX4#_8IsNl=gWty^{sliiFYIC5VCMw@~RMk5T?TtmxHf!L(!q+&J0=xq-kn4DTkqZ{hv@Q=pk&L>f?T15tjIPyIl40@fQ^gJ(32^5PFE zKl#7WRCmzKWi;C{hZmqNy;QBo&CN$b5&<=?XV4#LnbG!SVW+G zY5b-q5q6S=?Q+fP2n#p*O_>P!n1%ewmC+GGzp6Anim-7gQ@d2?O6v&I7;UC$2&u$Z zt2|;Syx%k#_$|OYJ;|N$l_opz(THiDht>^^VM03DLif|eG9!fZo01qV4!CI{T%~Cg z!{JQK@(9D#;aeNS^#X2G2v=zu&TyMsXMarpP=}liV;H{Gz%Vqgu?%zPo>r`@dF^_4 z!Wkh$o@PSJT>(Ci!x4F2Ko6p;;X$c`FtR`zhe;Z=1?;UcY}~W3{zK2~=j0SYM6Za9 ze~XYay9l{jNyrkw6XCXJBP^?k{0_KyxEp|%1KtN0xtovyfF~|Qn}VBv~6*ScB=a1sY@c;J-Vj zWR+wkXRBlDlp_MS&}9Pka&QV4sqP(5#~1D9Ey zJ$d2r+>`|+&(y(WMDTCVuR|)r(9kbH{E$I9x(J6L9+l z=}^Pf+v~7*s>7*R9d=W7%5}h39CbJ^s#6pL|Iy4&!yz#+ ziovxE9tAi?XLFlj2fk50%y2O{kv*=80j>*h?_!^QQbl61UYcP6t{-r3VJ+2Gl0E} z-G3=$TpHzcMpKGa`9=n(FnA-@eU#(cD33o(_3Nl1b#|nn7e=(9&6p9fQYeN79AoqK ztTk?G4f=m@A7LgiG0i6suf=^FV@wbD5hrF(I%9m=PIVfvQ!4=Y{Xlfaw;W+qR+^d&VLtRm`KuvIZ1?dgxuBxC z`T$hJ*_S^j&JE$-18x*>8UD2^Grc_!{~lnX8Ro^6Q@w|P*#XRHme!WUNAO)P;wl5K zQU&RrrYk%+$O)wR%1Y8~Er7ID!g1w2)euqLWYC$N-zb&q&*y=kLhM{4?9rb@-2=zk z08L#Wv8Pu(=F{XG*zeLb<|5t;EGKsp_4wCIz|92?^$zOxM5ZmMFbS#30CtU*ro1pm zzDA{UrNX4ck&wQpGIH&KwV|7Y&$m_P)7F;a&&hwvU6D`Q#zgpuX-BpLPtrmnc@-(eDb66YoN&KdqAoH$?NrE7MdocP@}y3jJKkARL9 z*TIjv$NKO-2H#6Ghol?Z0!<-_yheHFLBr9dFK!?%r7nufoJ)sa#)`B(@C9xj7djf{ z=j9*==(LNTDtnb_<)sxGNKs=qlt}k5Lk-2yY&gkh$tr5cxkN5O6|cA6@{l3iQ0zc`mg&XHLrA%;AV%_>Q(=EwqMTdNSL*(4vC@#E#w?kT zmXJ=iaa%a);=Sr*oUW@MKTq)^(?&A9M(W*Oo$PR=oX_w-f||bRLpdY&TuVb+f+oZmKBk=@GgYQ+K&19!y+;db8gT5MTFLQCFWk07~>{R6?DE|!)+r{JAXq^ z9`B{ zkw-5F_eAwLm+1+*e}bQq#077F5o!1lx^^U%!S$2!^4w)Of$rz#A?X^BXMsDLl}CM_ zV8qYKs)ST(qdeK?WpjkxN9TwK?Rf*_r-SoB;sL|a)i2TZY%$RG99&b5LY+kWf-QM7 z+UY2?)0QT?nYQCNTP8v^zRE0O`8{sMW+Lo*6Wt*SHpm#jnc&QDgW+P~hQJNAnO7Lh zIV+&8)7H&eQJb^Enoowr@G0&QDE(}srH+@8DD$wJOJ*5b@*gF`Lu+g94*MxL+2KG%d($^hm>N0#kihW{h9 zzTOb3s=jm>MwyI>q0BOmE zVr7^^=M9$cmL}SYHZ{>!L|1>LR`VU!{NJMe;5`X$`K2Oq5$`Eii%2QzcG&ktgqHVY z>;q!=5Q0VdXu-0a~={{j; z>4wlAtN=AKfoU!rg&HY9jSOnCI%6FM`i;TJZJr$^|amor9HS~+}E*VB()ctipbWV zuy2%#$OYt)ri-pIuDeU}KF^~vxV>&?EuQ4ALhI!GRnDx%0XdJ(Ke;B%5?W}doP<5I z5v4tkwaWr~qkIjexjvuCkf%(*8d4J$KC~Y9RInn%Jfq)Qrx>22XW^B|O|3*vV2R{L zORY2!jJTE3$b$E>s6H|5sHMMsr#4n)&q|{*&={?@a?(q8Ad|JQ&R5|A4@mkRbxe0N zn_QL7M^ocin~C>3b7`C}yHeBb;2=y(Pelt!O;1fzm0QK&`oA?`Z^wx~b~~gDt0HGg zT5I4H$Pf_ABCPI8)g0U3Ou)Huj^~kdD(6S{xZr|dm!py*aJKrS=i&4OXdsUvG<2Mp z1U$K?LJVn$VT(3Zr28C|^d8QqCxtK^+vlhx$VghohHwdNpQDm0+6o}=K)>6|_Bkr4 zXS*eYv9Wy)tf#kmA>%;zLL*Ej7W<2BR0?8?whWko4D-#s%$f%9i2A4gAg_Q7^9w7> z?#l9_LLR3+?SbxW?H4@f=UUOedli!~yx;FuOscMwk`3L939Y*Y+G28BS4^6tipe{` zoyB`A-kW+9lP}u_gr!#SQ_EmXnz~t04 z3B1)ohUm?UvPNbp##6YD^82*4p|PV5BT7}4<{WM5pR_Ln*Z&9aYG}N~jTx&?H|Wf& zRBmYan#efa9aS5D;p;xV@4yB-)@#K6b|{f;US88`K2SKZW`SbbcW5d(~mg(s_0 z5XbOpo%u`-;z(QbgrfeD#dGhM@x0P0o{0Y%Px8&KDjL^v#Fa_Y^Zfr6SK9Lr()9n1 zD*;TDtKjfJ@zo7IVSV_ws`BIrQQwg4n+4y9uakY5cn|aFy~#cY ze0}R5_cbS%Ckvjw-hXbO{Pbngc(k`}xE;AnngU<6Inlx6Ox0yOtL|~e>Qs%lI=Uh( zjZx|0+X~+{Mo)2+{`ZhU z#pz1K_*2RRZ6MI&_MQ+%AHsx%R>rs&{{fwow!KNme#)0J;Fg}RnXVzmVoow3?Pr?L zAeJk}K56PEJs${sr`W)QnV`LH>;+5&FyCM#_A(v}o$0%$^?(#s3*#PkKW|_&b+>eA z>@GK?8#=8FTLWwF>1&{^mhm!ob?{;qVO&@ackyU3*$wyH2gPLir^O`m2b_JvPt!^{ z2i&j%++dCtb)MePH)+6K(Id8S+}tfV+UMgQ>8F&sOsZdq>QxZLs;TsrV*-1tLT?^t z6%>#4{_^`i*YY5Z-N$vOKDstA{H2t*77Iy(ESd(FIq?pW=(!?RT}Z=c%c_;fao6;f z{DbaxYp=60UAQF965Rm(It~&ZX6e?j250{gDYJw(@VJW@X2Cr_ffLl;7~Edtz^5ca z1kM_-Oxl>-)AM%o`YhczZW!P28?3dcj;`l`J-X%y>`KEyC$FdxdqHPzIBrT_m4EiE?MZR(DG-@rBu6& z8K*>BY_w1G*|;fL<9TYM#=5H(`pmQoUC2MFS6qa{jI!h)#_e4}dEE%KlJBsmjo^~p z5iI7v`_VSz4W=C{g7-G+5hl2A%HOg*Dnh`&&>}5lurn_SckKE(E2i3*_J{G(!{6d= z?`ZAtCTNWdmN%81Q$xB;BmOA-dD%^QpWl{E?>iY7^tQhM(9(J`WUSZ?n!}QC=jp0^ z4JmMuJn;uPMuwEY*xCc!7`v}2@r~pkJ!3Iby2wMwMYv2XDfLH))^-!J{U?-pT4G5B z_q)hNhS0cTSzMoW#O3o>v*KX+Q4f2#g2FDOS@Dt4&~Gj3t&UVwB*mr-5S zcFBOzqSWxPlWQi+y~{ANREBN-kD{)=6R2q8#46{#z-sVZiKHnEGZv?HsbV+H%C3U< za0%3<272pNRgbS%5^staR>3vvmj|`JB6VeC6Yiu{*HD?>aXkEnvfIw@lnaHLO=lEu z&rDIfsms29VI_Ab;JFuE=XqRl;vy0)CPI|s{Dul6{r2b#wmaY zu0Kf6Sci#G>Ft3Pt<*mbF}`+Th**$kb9YVehSS51wu-5rD|OHXq#mOk;X^CkVu1V_ zW}NoG%~lR_qdGB3?8dazklIU_N2U2V_rUmfI(`E=&J&!0(4EY^z>6Psck7`y-H6>Z z;nGtV%<0;{iqH>1Lm+nr zY0lx&t_x~2^prrtg+i$p)H^jhb|lAn`d~ezh9r&J9HcA#1>NWnT`v#)8ta?=^y}3Z zi1{DN#`ymzLj1ud^+RFx`e}8Ie8hz&LhOwZM1@hGVDzV}8dAG|)~x-r_V1rX)TG#y zUT`==B3aBuf^*%IdVY^x!Bb6=&9tQ_2Nf8tS&#=6#mdup}ELNL~_`c-`S$2EE(9 z%3RuJTX!~>yAy_4D1ML8dg5xz#eZ7Y6qxDj;Ym&nP};) zfl;^hH4)}>mo}vCRVG*Nh2CfzD1Q#Tlu9L8ssBa*+-Z)s$Z+Y&&>8*^8OKHN47ctNygw+A?^QX*i_X^g&6nn6_ z>KU!~XKNPjgw zh0riSL$1mrLU<$ad;pqOLU>~c-yiru3oQ|g@uP#LC7U(sGjt~8>UD0%$HZiXd{r#w z&_i&K!aWK1F5E|OTDX_s-h}%K3o{l-hn-chPJDy8}M;=Wm^Z`{*bD_CK| z5@k(}L3BBL$sXke_=*J@Du`X;ifh3EH6Bx@VF!luK-|BcSTHwxpEC|pB34KkZ6x}d zKIW&sH}1H+Otty##p*KcX8LdX*Y}k;;oKs=yvydki{Do)Bx3X2#j2{v%`|R$o4o7h z9r&toRZ;)Uv!#@n>Vc!pdoSt{|37i^0 zpAJT)r1)T;djF!KiG;=A<^>m)iuq@xbgzMyKjr~jM6JriK{8cc*4JUpk4EXJLXdaa z8Kr;O&+=;A@|_p|1vJ%}C6e-ow95<4&+b_qVdU=eX50AVkieufnpJx5BH}Bfv&SUy ztU`oDl)%EPJj;iSk9e28ixe~es zX6Ng(Dqc-1)ED;MID6wOg(nNUnQ#JRYgoSn{I_xF{U-GODDx{`)=uJyH37RTj4x8l zcQ$cyD(xTJo6Sq=4xG~^IfOGgOZsUCoW8tlM_w6GojI51flm4JdFSxI*}Cp$E-&3q zx}LF#W6gQrH9Min!-TK?fxg%stBwlr`)@<{x&!MF zEB4S8hDhwrs<9TKT&eIo26sPm&+?uXy+i8`@-#&jf(ESdv zSn7FaG~4ao3$#Nzu%BDqB#?JQ(6K*i>Yp#G^07$OX^O*4lKXuH3lS1wcZn<2MQza+% z!AV+mQ#5x)PL}Adm_jjt#!E^~5b6(TlvEdj1VAICQk>$HlV47A5{5H!Z;(W(hmuFv6lHC34YtE6} zkv=KVSjdeo!mXW;rXO=SobNb}^`8@Clz1-7Awh>%Pa++~m+>)qm*rz0!p9lhB=3qm z2={s@z5)0TDIUHna&$GL&A%+a&u}sMSD-6oC3poRrP&wM;t|v-Rs}OoC{FaQa^)cb zdiOU&dds9v94VbTs}-p}2c!6qiD#QE(9k0cLX7r*ayayp7d5u^5`fav7h5NM9G~91{S~Yj&Zn5b4Xp@p;@3my+agDdQX|j`8_w zXoZZ)`!Vo`J5+zv2j~?vN0l;#PGXEX5W>73!o-ljg)n~%VfvGILzt2fMo+3jn6)8{ zf$R-oR?2(QPbv{u)wo=w{H^r2zeBqwcaHFs&M?b5PBoJf-K zdAmY!OqYw5Wh4RllKeW+kwbGfzi<*b3EHYF@|XSy^J_{U+jczr+Ww4ZKbs!Ufwm9v z9AvwMr(k;}?Ks{>+iOaU<27Y~!{D6m411S9)?-6SYF}4L`XdE8}a;R9$KkM#^h*i05u$i6ir zuIUVZp1~Hx^*Do=I0`rx0DIjYs)CL#BJL>{byE z)WaG!VQ2M0k<4(CTJJHt%iFU?kI2{A^p$Tf?P(fjh^{M z(3z&thH(IT$zg}5UJ<&-vlH27S!toC6hge&+1I%lqnYX8Dcniq_7T(5(%a{{c~hKo zwGn3^OFex`A9@@1gkp%lAup28CFuNW2k)N?X88YHa6^8G$s|07bpqfOSLE*Ug!EWQ&5lo>kjCMZ z8hVu2nV-cuE_EE6HBN))I^Sc>r;_pD8|zd@E-hSNg~ARVEmTSv1jf?^PZFPxrO!7LwtAWjU3v<0U=L z#^{a2q2~v6k_)Uh4M-;l^Van5ggs!w4q9q$E5!^*8$V^mr+ z!)QWtUSg*?Z&PLo>As+ZEO@+x#Nh4=Eu&ef!2Py_+?rKFw$3jhH_}RoXI=@Jj`!9l z(9S@+9geOQZ0~s&V2KnfK8<=4%BiA}KS_i@n8m%=Q(sBL&ORaeC|7IBpRppQ4{_JQAzzr2sQ zMa&5e7@4O^xG4i}1Kmq{9h;K7Vkh}3Zpf5b!#paB&ZDcEn5xG2%Y6D8-hA2dfO->d zp+GwXG)w4UEs@g9_X#zZooZ;5(Ef@t#3bkv8I+@-M`|nhTnx2gmr1lWboQ}X%VN9t-T~9i^f&3NzEjLPu8}GjZ=WHw` zYXH08is71;l#mH{)3ykc}jepfh7bR$L>XqPPTtiZVJ0 zi^>QRm2Jjxo}lB%BG`Zkh%?in1KrSpe!i#f?M^@j-{<|k@8|vFHBeo*mRoh|+&ZUD zRh>E&#$`gEMmX$hT0=nF9@mdJRpQfVz5x50q|wawe)?J^8@vJZ0r&I2YFO$+B*RjV zmMy$_Tm@VspL&1KMz)vI<$WH4C4lQ5n3|!BKTxQpy_=M&*ty32WQVcSZZJH=bu_e7 zllVR>=26hms53lpJV8GU628~<9dv^yL))7un|8xV&p19V0WDd9UGg!92i=r%PaJeT z9Jxpt#`SyyV_(J2A%p$8k>Dp99%qY<$G#z3$8cV^;Om5R-kqM$wnYevcwC z`!wb~t~ne(vi+?K&U%N8pBeSw6>tx|UCAcFeTnC@?^LoD_}$*EWIZ=mvZPJmA8;*j zo!+cur;wiT?;4i5i}thwan3ysryNun9@k&6k4@(sU=a>yGwGbe1!sN(<)d;%y_mtQ z9@ht-u)Sk|XYJ0gX_!TTQ^T5w-|Ahb43h9E-*spLa61JfSiD-F=2&_JI#C!0Ha1kU ziFlvNpnVV(cF5Dz;jk339{gYc*Vj+OsE`I43UYjlow)SaUf79KOP5`d9_*o`4_|P` z0R!>4dlLNI_O<~#HMIkcU15O_HVD}m3m#8W5TiU40R5@ znS5hoj4~ZAY?yY1i_k%7z zu4E>_CHMCj^?7HNqHlizQ-heeGfpdR1!QschOJhM;f!I4H6T`F4A9iCO3>%Q=3O-N zy6(q_sx~xT`WrM-a!{MsRfd>!$3BD?@$h^e-i@#cVVrToo=Q6I%QD_X#|uEm3pgE% z+-AXM+anng=zh2A9>?fo^BAtKa-Q1UUNrUvN6&*45ivtv;5SQekD*C z30;5Y*pIZ|A8yz5Prni%jDn^=b9{u9LeSK1l{R)F_9!DMK zch})ufUL)#ekBA@LZc9|gRW95jt-p8Ag)hNV;7-B$_SVniE-raJf%ewV zI0s!}1LF-sEN&TueUP40!Rerv30)m`!vwn_7FILm6Aaxlx}(Lu6AT{L_igKUMJfFu z0q2Z9fn^j}bht(`73m0l0?R1GSzEYFb>7+g3iIo_J<|;N8t&Bps$^S$i@l(?M-d(i z_dFcYUQMI4LIVqc=bbm8m#A6l(?RsOCS%=*B%kxn7U-nYyM&vIJ7wGi05@M>rWQWf zJPc!8`lWHWZ(gV!#>Y5(cSkPhW%A(!e_x*7`ohw&q~qjRh?Fsh2i%l0H7*t@Gp`nY ztoFxugYF_pnqOay;r%BN`cJ)h|7rEeV+QF+_24lVJ+j={6gw8SWT@Aj_V{p=NjO8y zLPhS}z~#<+_w4@);{@;!`899^KMei}_)8JL6aM(al}x~MEZpz$O!%t9?7@besrg32 zh8Zgf>p5N)UWQH}VjjNi!`d8RjaCszm;ZypFzn5*2j?4iIf!dzUrOm`4=_|34j2-E z$<)gqWs|P9Go`D29>PN}A4Iw7jYG8uA(3hkqq$sWFhJ)TC#{PGXkK4VNz>DcT`>ZlLw#Nw(XH)D_5|=j~q_EKk(vb%vPhaKj==b?dcY3Pffyp zbJEHQSH503VS4kqo}=7R4Y0lMj>_CLandBs#9m+RzZe{hyGJ^OTnrQ@=tC}cVpzd$ zQmsAXT+pi85DGiAowTASFiG?TUpnKQ-FkvjH{h0&xFCDbMLpBHMSGVPp^F;6kR&FD z4R#)ldijfsfg*dp>&qb*J2T;VT66B4v}RintPTV>F~>%a=1J^$r)XA@UQ#!%m(-o> zCG};Ulr>8$AFG|PF`WQw21DzDaK8sxO=1J)Tnlg>4+{AvR!9TKatK3k9{-v1OyzZ1 zys%S{8a5P~+c*!<`E zs~g8ocLh5*gYaMQa_qKvdHfD|(RRQKi*0Zx6JDM@`0W1-FX^|z%gFyn@KSzF^DM_p z)$Q=&n2u8j{ClfAwCfPQcO13r>4Q(-$G`V|clh2tZ%Y%QO#z^Ze_RUKF#V@PqIB^u zxYK#Ra0c_h6Ks&kE&bVIU2OXJn9M%Yue23~q_0966DW*F&S5lrY`eapqMR-Ju>#L=UpW>Vd<>{4vx|+og znm*S(LThRqIBl>%^R^Rlq(9*hG|(OC1(|N-abZN*u#1OTJms6!I9TxI=#3H6>mnWI z0X)yY564Vv;OBPfHpuRH{~G3Z-H$i$+aYPh^}3!y?y%#L(4!DxK{*0)4C$3hT(8vQ zdQ6ew{?JCqC<=yMzvO_czep5Td(h4nFfgqq~@vTj?&m<$p$Z1#ZnVobD=rNq4u$ z#jRtGjEj{w@m7wD$-!^Df^%rExd#{|+y)kE@9GvhS4{|M?%UYaE&lw$B;l*73DQxz z&HjZ6j>GRx2=WBRbUC{JqS$G?KFHGvx^O}4jPrGewU0W+*mEiW^`51fy>ZgFUOrEm(;}9c_oOKSlPmpWqTA);@07l$iB#;k*Yw;7wCv zw#0?FHyvjS5f^iew@?l>gWV1Hb4oCZcQbxy4Tef5 z{Ct49*qFmU{7{O(@$;^y9X}MC%<=PvryV~OOV${@u63S6ySgdRP^>C03bCs_g}?}D zd6Nw7b-moCRl=BeApCsWy?X@UWc3BiE8ss5|ILe7gMq&SeiHmI;Tz$<2GA9gV$=YQs#?Gj_ZuLJ(&H3_~WFwV8m^|cJH2NE}=fS zH{r&@ErpAP%Yci78?`f}L%j{CH-h8*g=^HN2F%j}c$)^HO_uZc=D_)T9Pi2(t!?td zy8XHX?=%5Qx#w)X-oV2urQg(FFXZ7Z_|k-@*c((wQ|x-wNa3D0?%i{h;B!{8qwvSV z{}lesGWa!Dg|g;x&UqJK^LQ6Lk>9GOKIiO;RgN@hN`=OqhCWQF2DdfXXjXaxD}iZy z5*o{`$7+Nz$86`E5g*e1H;(BW^wLw+4f|sb%-`t_u<4#W04aO@q`>Fb>8qb?aCfgu zK9*Z^u;;`{t0s0owhw6mkibXx#66T;{*Gumjr-rZ>|G~%0ydKDecJirl@soWrX@9- zdJ8E_Y+Gv}Ri|vVMQr$}CuYkV1dnQ?xOUAplb-ZFy{`XU2Y2I=qby7KpjxNjAM?i= zty{89c=FK20I9lR?S#PRWvGT9dv!w4#8ngXYq}ph)WU89)$CtDHH|~Hl|x0)04hN9 zQBQF#LDUY<+qZ}3>B@F2JtbpFA}l@C;OrZiA&@U@7!U2qdwPi~9tU%1%22 z08MZQ9051Lk+dP?;sHVEfF0P5Q?SGJfpK>i;YYGoHb`rGTf4yy=Vvuv{VNl? z=pR1Tsquk6A8ZS2B%ZBm86-gtcue{!BqUTLc~td~1ImOH>8Ie};FeC(MUoO8mwxKp zxijp-3du-?%pkR>I`zM{`sx(Dxb~d$CqARsV$E@?{rW;5m~1LA^P zRKY?@tl@3LX6WW_TD2KHW;0eDH@o{aELvqL$M$mL3Fx|nPL+AX0muU($Mbj`t%Z7B znOIv2JjQUV3$5n7&sTHgmR4TId>d{L+!H%Pa8CtW1G}kfVBKs@z?#4{s*Iqi%zDi= z7bAhs<|~aSFZZirWsVy5KIlSTm%Zlt>0`E+!bD3`Fkg?w9Sql8HPTFBdl24Rr?AQY%XOh?KPxbnb|IuEDA-EG}s2 z&Lh(Qru`|>SHOB4upek02fZe>ew-umh$$xpYhNsOyrbJOLr$7w;&=5tMnFvHu{y-u z{hfx#h=`GnsS%U>-I)SVx^-QsCinj*%ytE?1;mwSs+|BlU#6`pk2% zH0F3hI#WH)G4#avOEt-Hmb;(2Zce{|PiKSzii=dc`w6>VKTv?|Y7k~Nw|vfj`Z;gmDxirlkq zaEhS4frnD7SmVqpHX802W`ZI^wzQKy_&a2t~f-}kz!m5(p_d0&xtZeWigo{Yz)ah%+-t#^S#nS)Kb2!aL_Sx)`|{l?_U4)@u6sApNH!$qp}q5!6@<0mZ}Y(4?tylIIyPf!x?BD5Y3GJZ z!XTm&yAirKg`1(@t<~#zeP3ZDEJJS}&qIe1T8hxUJk*TPVubGDAz1dnY2*a`XoRl2 zhT^;jOL&;~0-~-FQ%9f|xWKExhd^DUai)QvnO=9@^)q4(#z}l9ci7Zne1Ac33_^Kz z#xck{gqKL~reIZGx_CgG{NMnrGT*sBP}DEoGcCP%lci(Qj5 z%RK@s@aLSbw+JI3S;U{gEn?2&>YQ`V7g~IyRc#|^QP%0x$KX5uj_-%3G$&dE4Oq!i zAI}j|=IPEk3tQ?8>Dv>nF^D;q^Kd~Hb_>tpt^l?1d}CBb49>2D?>R>4yV$$n1Z;$D zL(iBMHg?WA8_-OF;pYtx1&`F>MA{ka8@LH=iB&cAJHso=xyL$=2U?vU`i97M?IxLF zA1u*#J_ZZ>7e5Ucuwv7M;A5HYqzI-S4;u|67j%PMApcHwiN5&u#B^=@SO87 zQum=0I;)D9qSMZ8SY9HjhNw3EN?Al0X#W!j>WBT1B*-?1i^4p@vBN!6%^@Aw3#Vco zDQa-X4-B8X8w?aGckeLNFDZ9d;J07)>)H391-rEo_-)d{ZjpgGUh&p15r>8D?N#tf z0aG8t9TuNwRk%OPD&+E1A-;Em`#`b_P;}9IaE9qL@DQZ$I_0!8tYutG1Xcyp0NGO5 zCitjv2XcUHp7oQ%5w_saK<(VBQ4?1>ym4${*tf(LR*Bb=c5t7DZu4$vHM zteP0o5)sn>+LPBZQCk8aB1IwrDyg{9OfR5=b+I&9 zY`vGhX9(xSr=7wMHNGdwrz|c^AI$Icyy$J(mY~@(p~AgLs&Idk^tZiu!W|;t2x)u% z3491~X*+GOR2rgLwS>jn+^=)H$ZsUs+y%32?xk?WaLeFI;r={hmHSEU^ecZau5*_q zM`uLGVNYr%_4W_FQ8?YQNTO4Ov`+K{FehZ4Ob5gH~@%BMGK8j~|%gJp@=^_*9mq0N?`SA#+tvVAmi zY|)Sm7DwmT>7DJ~L3lq^5>Oh&d0p>%#dw@Kl9+~Y-5>4V=r#2eMg$InC1A;kHDZeI zh4^=Pd>6#em0Ayz< z?9Zgo*>mH#NHwfxpHaa<-iu2n|iwh^0FL@ajrew+a-sb&z!r1*5Hf%Iz zJ_{fL(@9F`gyZJB`XwDy*9;NyiEG`{_Ca?_Ho)_U9-U7y<({QQ;NO%wVa ze$5Om!7-g7-K?KX@6B+m!`Y@Ex~dz;BW|+exg!Dgo+xp>b*AG>%)frb{eKKnp`bYQ z4crZbQ-|0=Y_f*vm+N1KwVLm)*z_zY^qWD}xs4w$Iq1HxHYVrl;u(&iM?85#ipTPI z>;Vrq{OF7(?zD^Mj!F6{j`|b(?ZknHXPt7kT=wK0bWg0K+#2g{)5m53rxfq29b~A6 zZtv+_X!S`r<#pJUyB2z*;MUL^jnRn1-UbZ@9*%Jc;eGbPX2vW}*2hb(!oqf@K-pz#>*UL28fcb1^3 zhK-@=KgFcnZK8WG-+=VG4C97v*4xl8zW>qykxny;!(#S!)xV7!htl+;(1Ye*Pisum zr)GTXPP2JkgKivhTN%CmA>N+TEOWu!8x-^9^)$>?zk)bF9X+XoaSJ5uH+t0XIIgh-mRCtr}JW8LhcY3%TXq)}X19^{R{R?C7}S0vk$a zs+hI4hV6npCuE4olk_n?<3QKWKKda0-(yBt560}zTxJzge{Tp*SvTSGVT_d|zvYr7 zGGJt_J8xE}X`QB`FBllc@NT#{^+)GctOE>yy*u2>1TKBVOLVg6J&`SHB)s*=z?Vx7 z$R%rTe{74!Y0u2S9&vQC=uMn0#(BuVh>QY9yoiy&Lb0#3h2ezgl)z++hhEp(w$2Cz zgu?cSNa3g2cDSdE_&jE_TXhfGzqfCl$n;x8HXQENKZ+~}^``9{#QE^qHjH&-&w=0D zoFi4Z{|c9f5kigr`p>rD1lSlq(huC#>*{^owv9n6wT<-#2lTqSUhf4+nZWsUTTBMD z_7J({o{#{P5sLa=Z>xa45t>`} z+l+fT0Bba09t(FL+K>iUi?|VheRJ0yT|Pi_0CQ9x-asl)XXaHd+VuLks`ZCiUbZ7X*V4A|)oNfkym#k}nf z4G>3#-SzX~FuN^d0df^7#{m<83wf%=vjC}w0%5;Kq|`?}4QA#@Vy=ZXSf4Y5AfWhJhp98AB=Yz<`7w> zd+LaBah2|Afs=V^g2#-|eSwpBDAqF(B|qYCX;067w;@pA_uy5!&4FVT42F4{wn39a zVx8oB6QV~}x_hWbA$1_{1#fy%0a1)fh?~H}uX!?IhrXBUF6b^&dtUI+Df2$6L|*HU z*DBrpR1}|mW1Tw>Z?GZvAC3nuU+@e7&8^}x2JQs*s1f>uq}Jb$^SOu6M~l%vtKrXv zdjiiT@EhUIzy+fpKM3c-vk_^1;Qox~`_QlB@%#+jR|p@4i$=bMh#vx%jqvyA^Y0=3 z8vOHc?wLQ9iSWMhfYLV8txO_?6ygLsBhyY0E+A7iIDsA$QKZo zxV@W#%{WgK${89WQ%rA9I6?^_GNz(Ep$H8Lk^9<=Yau8vNtM8{I2HGUR=Sf_Bt6lo zHMTbdFuw^nj{(m4fHQ4pmkgQTZN3%?X>G8IIM?gfypXVlsBnf5p+VPzk)q`(&*SXw zP!(}aws!%_c@gC$qU|%_ljIYaJH0i#@JFYsWxrdk(&T=P--bq5v(`A6qB zE$ZAoSj+j*+1yx&wDc>%c>bsbJdoRNE5|Ad;wE7IuSJNjUsB;-1>Jo|<`L^f=bo0k zr@`hb+vlD#W;E<);J&FbX^6?>zq7}T!gC$}ePql?Je&D%?wApHF66(z8ww@47KsJU8>-e~cN9=Og_0yD@j+**?bWdiZ+!cIxL3 zdDYy1sf}^Ju}zapv)88CURS(#0PhzAbCK_ATt<~URV9D@nOFN~RJdzJlAeVXu z2o~3 zA6rCrJ%aS1YSK{r(Rl${=UTp}c*^;uV~}By;Z1{D@7@#K_@nb*I5%puUUELXgXSr8 zpLVVq-xhB)Bn@Ieq!2G?Ko4I%Ls{$R+o> z7m)r)E3F?AtPz0qF=`zwbwvQy1&$Q!Kv-ZbwzBwoLqM!n-(@QHa&g~Vn@fLf3$${> zj)yAeM>=P8rqx+kr3rW;@CCOk+AFWuf979X9$bR&2lsop-*sNis9r5(z3PT&)O#=L zO+&rWs5j2B#F_xig)<$GRqV$ef`r(FQSBOSa9Ii1Hn>F>tqUKDoN~0+u zdmXf@_M>wU@FkxTfgb$h)odN=3_T;Sr?%t5;>SfeaN$dz2dhl6`rb94&x--Rij;FB zXPwuv!~744F*@@bswO)c0s2FY5z_*2%ZL~|$pHDMHVk(Lu(fTjozUS`8$yl?xH-L( zvP%|>I~?4uqc>#S{FUS8ci=@-=I<-3SsP${2R`v4TVp2s+^gPV;aM7obv3v<#gJTk})g0dTF9kh^17h(vsDXzOD z&k??wZi^kl^^mW-qS|VA?ZjOnDu?ETtv5z#wQNtMw^hVBDFL(G5dBQ(ipwjZ<^GjW zxm}O0u4Wl1nsF~L0`U>X+)P~gKB5)NL z7pghEQnQLyfo;&DrqemsT^l_KANQIjVh5(zG^tkLr`Kb!+d(&YxLvi-dXU>+{~g&o zn6B>CC=H3;E5QZ??Z?b=n6Qf}#*%C{%kk(j84oY}sfO((n3L9kx4~U_zM8$fwwehq zRI_y8fZ9TRWj)_xNx;feo9k6K-9+Mc^|&G5;^B5V`MxgonAbi?+tc^;YW5e80a3%%@(hGAog0Lg^*x%;f z^-tXB(=TpASsZrkP8W%Bn$oypK@$>pX1S)wWXvkAUq1cHpNi^BE8H(*Rkg0zP*hhG zS6<p2gr^)pKQh6AN(r#5$t=M!w+Z~fN)W(yr1TFsZi2c z=QhlRX+JG1miWPAKXRM1SW~OQSt;1jxs0+Cp6GD0ZT!^n-~%3)nOn9l;`<@@PQ91! zhcL$*;0|?$?Bl|qbA~kin2a$~msr1d-h~x@oQ`Q$!D8Sy(2V)sx#V(ch6Ib|NmGtH zd$by|Si=3yK}c2Sd=ESSqxD!5Hzw;1i;p|cUm6X|gLOtq$-I0fhxTMB{<}+Jtf)8e z_=%Ter@@j~Fyu2Ezo!EHCSYy9Y5g+-*ZOX(aKmoUkq#$n8)7Hud!aw+-L_pl{r#*V z_EBhSU3cYNO~9RL^;wCydopXOOjpwsx}x>OZeKx;P~k4IK!M$Fhb+)s%>oaDzP`p- z0lMmm_!xXo!e@_e0(-8zz#fbi*r#w#1{no2D~7x4SYePPs9C&F;m^H@g521-B@j!H5T^^xm{0t5_Vx{ zE(<5(v((loJMK4(#QGfUbGJBKTxwY4W{z=gHTIXzI!j?m<*c)$mBDrw@L6uT0vYQ+ z;Med+d#hP4aL3NWhAty};DLGv)+K*{EbYema=ApDal=}NJE{08dZ6T7D;vv*&-1}#2d*T$)9JCJ(AB2cB{`8$tl<(K@ZS6xXw)sdd-K}{U&KB20dj1 zcFV~QHe@1wy#Z$>Ja$7(Tqxom0A!1d1RKq>#$K;8d<3{~w;hKoff1&|0oQWCWjAy>KF87ZSZIdY7&^7i zN*bTJ*E`wkIE)1tr_Z81HbB0uh-9KK0p~k#pT^d(opA5;lVQ%p{w8kbyl%)j>m1?q z^#Ls)Cz*TVv?_kiR6b=oep&!-dLBP*Jh%w6j_2dl!K<`;RB#w~mZV0<*1OFS^^1jp z<*+|?W--lC?lWv$yufk($SH&tIDUXEJy`#!>?D;8YrVCe!boK6ztVd&SM?kB`d&a zTkNqivj1B#`+Ofe_)P*mr(%a?#Vbo6b<8@p!#yur<`0iLHox*LV0{seX!8hKLuYtB zu0+30r0<=ft(qq^xE)qLD;wNar%2Q$SZSpB&{<~~G%60y!9A0+YuF!vGg(F&fS&(v zz8?-5DRuyQ{$bdrzxA{%=5;tN+moM`#mQ%^39Aipkes@4IR1tk&M*@wP0&a2@G(yv zBxT|>YH*r&oYq@TyLX0MMKdb5t3WvwuvU?cNjCZm;3F6b)(0P~VSW0q=I}PVn@7eh z#`wG4_w?uOZt$ZDbAUb!C(w?tPc!UJSO;+Tf#s$n>=X2{AP2B&PHUv_pOJbTsppw! z7d7yquXTidfY?@7d#-H=A40Bqf@t?5*Sw_nVISrQ^+&Va`LXMd83$lVqdjJuYfHvk z?japw-f_K&n4ukF>RcNUb61C$mr%n6-pALvFQ}t-iRkGU)KuzvSi7T|s$6fm5Aa$) zxOza_75GJ%LtG08OI-&PnEnZiX}x#dcPTRkG1?d17qqY;osQTDD(%S$HG2`S7UQC> zNzQV>{+$;54sZX!%l@bP2fU4YpoS(~8D*(#W zweH;5s9k}8JvWx@?f9^iK=@33%k5xm-F;$nGRob3Vb6~8_l+&*w(UR>E9m|&2(m*I79Sn%&}j+qoaM zMcjLaeju{Va3yea;l2Rh_%|HG^NVn=<2f6Cf4EQZ{3ZMo@K?f*f_op&d*M%l8v*wx zxN#}qv2gO-opjzv*D(k>S1a0NT|$rR?_75)E&T`Q5k7bCTL_yO`8CkHCtcegoS$Mg z+N9{(k~R}gSgi55YO#`FFoc4akjAOU_10A_cn6hD8c>rJo!iejf4FzgLj`h~R5rol zyVud>>Z081=z@h?nQ#8!T*u+4@bkJ*K=B7YC)PI)>R_2e%}*`Uj5rdUEe&@RcAVE~ z;`$>c04a%w`}$MTV{33zVVf()BcE=!#j0`JBg5)ZP}@hC8Pc4&yMNA1nwrtzcrMqK z>**bCd=xY;SGuxd@=^yL7`wcNT_^_sgCAA`Ibm51`v&1XaE~Iq2jMdCMQU3i{3OKD z*kEW3niiDNyCzOgt2-{2Sy>~1JsG}6a7<#X2Jbkq=0|H&KRB;k!FufWZqp$93Ti( zTMh2pBWdZ+$_*5bJyhS(QtTDN4KAzUb<67$F{t|^KA)WrJuIx^#6f;E1cN`k2dmSx zYe$-GIO78gC2Hfe;}PgFzPn{Su4Onkzu&!H7eb>M5_T&_=>XKzDRsUTB~zm zUyrP`kOZQJRUQLQ-Mh}MScz`~cftmJHF9_kgnJOrV=HUe{#7;XOZawtucz@1WI0=2 zw{*FlQ|8S-I7h=qA*IWB(gIKYIG#v*N*njRQ@GL#Ylre}Ft81!=;U?hmIGXvU#noF z->a!#CTPwqOSH;TmEF0jRgJsjlEM9YUaCWU7?*JD&gHFBxUP@g8FtCOjBXGyI@X;a zd74`H&eIrw)vMVY&uVrCeBfVjd_JF`ZZu6XnC4!`$Lc`Sr66wC4pOhqRAmgFp2=<|zN!6I{BG9VwTEsHN> zg~oVuc4@pBo-ZQFnvw(R=k*^U6!;gSq>I2%UW*9H|Ho|8MA!=7FhDe#%q(y z?&LVidNk$!dKde?5xsM?JpbOj=TR0Pb|+=Om&aQ&h@p3`Qn!4c8KNq zQg6-&q%JGT%UNhD$(2F1l$8`3=?nYNZp<$!E=960Tv$|KUS=xEH|AN!#uu}avW4bl zB`X%1R+w@v3rnm;mSXF|c=JN}W6PbzQdHF5FPJe~%cy^Kh{`H8FDvu`pI?wuY{^{? z$mL!&G~O)#XM`+Yu0kUrFYSMfyjyyezt;hNPEA;LG@8x$+Gr~JN%2DKisBLrTA5SK zdy9!Ep|HT$`LYU(MP}Z=m2TP|ks=oeK9XhiWxtu2a*7M&CNm@T7nz!YZeJfK0AvhP z)h26BVUcAyJ?2_?AN)lx=n#KXPv{UoA{(7wu8MUmhw}SVZpv@{ulfBcMB^MiGRg`q zML7kWK@?}9yXTwJwDG2*5nv#`qD&@ghB}~GeCr*rS0O9(31stSL8tU#t)vK;@_is) zg(bh(vamR}pb&i$JPW-`?md1U)J{jl19M71)tuT3janaP)cW|MHj8sb>S4Yr+h6}b zdXmX74J6mzRPe1VpkPF{!b3PqFfNzrMIq&OHqwET+anc1ZjWHB$dXNcfp-~`pO}55 zB$FC2!+SgO7R!w3|Ew|omy>us|7S0Q$@{(}KA+rL#F=xl7v0;Kh55_!6hfk*uYFuADl=KZ<>a(>BK7C>^@=24 zuSk-6MM=l^I+8Dqgv2)$3eGMxD}|$rwO5OgKyJG+JIg;}0nlQMy(`dzEtW!M%(yvH zMmaU9Lr#Uo+#K1?X3QpPjW)H6x?xJP5B*AqlgFR}!~((cXr)wAR8~mTg=qg^fFSQ$ zG}4X8_S1O@L?jxbiJZU&>6PNFWNl7?i>VJ>eZJtuW|PF9{}kTI0xo0CTVf-{-TzIO0l!h0&m z1s@o5N=()ceTaG+kWggK^)!YoXltnz(R8Uk@My-k?xGt*=3%zthl5gZ@JMgM&2wpP#Nt~!?I;)8wOmIWwsQX7DBczEC9o{ z$Yr&2{DQ)goPvA;qR?1qHW>?*PS|0*>c}KJL@z_f^MPDwCc0H#mA{ei6Dc93=Yfon zi&(jDBm&W*4=*H$%$D2|qYMhq)KLOAnJ|k0@LJ;2r8)WLg3{v0e7MWKMRF`HD9yz@ zBnPC)nV(Mp^P|$Vq#btfQkjs!OK%oH8B?MmwTOG;stHD59-8@&|!QC zBYq|$NlD;>g7UrjV8o|A#HP5IN5d1xyfPYhV0ayd;|{UPc-$cdXJm>B@@|&SQnej~ zTpue?n%aS!JL0!pAe8gx`aYd6sJw2GMRui0rG>@ReW-u=`w|7@$;Tp~1pzKY`8ctH z7hezsKuV=t#F%_&lqK#|?f+cn_T$wrVBUmRf2<#`{ut`iL`w0dc#|w#fn#@+t}^m@ zR~`Ofelg7`ib^bIHL+kdqL*1PwPrC6IpjS#sXLv3xY4O5EiV2F*)b)VLU#F_2A(E)|>kR>!ZZOLm9Ar!o#?q zM+pZg;XuV#DSjvJ2eQseScXH(x+vjr#g9<@NX3tWFX3NTB^<5z-4wsO;>W-jk*lZT z_fq`6N_s!V_tjg3ZilF8EU4DPLSxpj@Z3Ue)|0QQ}bWf z*kj83=}MR>={6;tu7qz7z_&j9D4*b4>ZI}?2cW$^#sP4R{i{-+3;@E^4d^eOeq3X( z-Ag_OGLQ|Yqr%cWJ{hGoF29V#Xq_Mj@?fc1UOi(Z5zFa(e$28;Ol76UB01nEH{T$F zr94PDEN@vEU%@NQ)gn|1QthzbCNG+$X_N0tN>b0w!D5u9xLB<#$S)}>$j!ABVQnph z&)Tsr0amauuK=n;1w{)>a`G&+Z~;z`lea9d!ve-F%NZC!v0Rab1q=F3;=jqf#4Ka6 z#oXaVtTSTLuD#`b8l-@$$dpwqK6MqLjsHC`XXg0(XIhrCVj~tt_++1{b@4h$7pohf zi_!Je_0mP^y6U2I(YkKB?z-N(KDxfT1l?fW5M82fsBV~Uye?grshgpjshg#nt9$L8 z<$<$x3w7gk({;mjsk#xmk-Aa3G~H-j_sLTx51Opg8FYHxWZe{9k}g@7qI*|2Pxp{+ zzV02}gSt67lg_4FsavHp>nyrOx)NQsu1>d6w@LTD?oYY~-B#Tnb?@oU=zh?h)t%Ng z>kjL_(*0TYv2M4nQMXOEU01K$tb0@Uq3$EyE?txEQ{88}uXX>>eWUxQ?pxgv-BH~! z-ErObI=k+a?q}Vy}sFIkM%oce|0#6Nr6|>}yhcLaGcTrg>N*{yaKpY^7z{fVg@EK zm~z_bm^H7sbYW3Jsn++Z5>C1$%$vrGF3vBe7m9ruOa&Hv;?e>R5N6jPDo`k2NAzV; z%1pT>>@NZfZfF-3oNHMwe=4dftAr#TWx!0;$$srd=WMj;fxiqA4~4PgojmDv6o@z^47mw<9+Yn>eG8+ zDw0qz@TjzWloeI!DuR=yj`;;ec`Q5EXv#6>5ar@f-wuWtjux)QF;+5uBa?4x4^U{CqJ8{sa%+g zUoLcI$=dFarN;jO(m=>jX%LGCo}Y)nm`g$<3zQx{7tF@Zi;5Z^-5-DI8H0?Bbux@A z#O=7hQo>SRLXZ@;>h>i_Z27O0u$-6BN!r`z_9Xzf!^uUP51K zfpGg0XiiRBelnG&vH&5BX|&UCqEeb+Z9+LRm2L|jw4BQ7(c_+;M5TAn=-Giv?_7dR zr5^@2cD;QGM5T8wL8j7=gYRzaSi(Ji7>G*mT!KucO~LyfxP1vkrFSktrqX@E4{#du z;h`M{qS8B;AXDjIf{(1eJq$#pcP>Gu(!U31@t$!r3_dEoa|tq)eii)lzF)#XB@mU~ zxdfR?zYSiVb=wj+mEO4onM%LIobL7|5S8A!1er?h!K+{ESi)G|AAD4L=MrQpJsaHh z?%S6@RC?zUWGcN7{MwP*mq1i{=MrQpZM~&OwYLIdV4-iJlwmL~S_lz@6_Rv7BRH4v zA&KOe%P@?W7>lsEfpOR$L*fL&h1kR}YO#Mn!#9oXAe1y+d@*rXJ@+^hL^464J zNdwVxX(&dP5@>t9D3@lCG+*qnBbH@hP4S7XO z)a`z?5AZw2wAJC`8yqaXh( z9*7^^xdfRX{d`+I`1sMCOOW}|rCabYlYa{zKe}@XGC#U{3m*I>aDH^>5@dely#){L zB@jQla|tp(3JkruKgcEc_|ct9koi&P(3|^1dkMsk?p%V*kD{;u+ObEqmq7gJ&LznF zDE5{TX8E})-?gC0h(gEU0v6iMF0q=Ewaaobr^zWPlgXZ$i;EG@ryqO?3k<=}%7}Rp z>+!LsWXy{EFB9uPSms1pg{dqwVY!NyR+h?>7|yeZ$0#-WwlHqxN}RLMi&;6wVwP*l zPu4EviU#0O3rn&%BSRDNc?)to>XdLsDYH&_+9m@;{FGLf`BZ8tQw5Eao&;YapVn^3 zw<-SPieIYuMT(!V_=^=EQzibMN%0|8@$e&xKVR|ZD*kN6pP~5E75{$4pQiXz6ko6S z6BU1g;-@SAIK>~U_;)M*7{yOh{E>=3Lh(}-KUwjU6dyt*;J;TI&i#87|8BoOjK`0W zeEv`+oT&JNxj$Nx{WK|ohtn``q;k`wfr?MFND2?&{wTk%QNnSGAItp_lFwK3aH{0< z`}1(JB>O26Eg`FdQY6}0Pz5C^ezHV6BdQ>+;tyB+VTzxq_(K$bu;M59ecEAB1r3sX zKFQarpn;O?4^VuK;>Rg|tly`d4^@y_lKuXQ-|sh{Nj{7byNiivOVE zb2>#jr%(7(mH5eu&*>8J22y?oFe;1D*tN5e6 zoDRdscsad=kM?qU3m>KUBfXsN!bf_C@b^Y|Io*Yi@N&8fPf`3N#n*Z{9fl8A{9%fJ zmzUFH_)x`9RQ$n;pWx+m8a_}75Abq24v$m(Sidiq6X)f09IjUU{(hg+b$EX-r{{3o zjl<#Y;}5HqFsEmvb9xT%<(0#|lyFbK&*?lo#_RJroriZCgjIKK#s%D&9<%q!1WyhMpXg~-uC9#bY~*e${GEnn1ak4aKuZmz&m zQi5rqoEOrZ{7_W35b8WRCLA*0g_tb4Snci@(>@E7bKKMl+OonzvhuEJ375ApL2YkJ zZb5dkak+0w$+x(aa&D=aBuConL5paKn%3jZIV5kA=61WnNc$6@L@z4dsmtk(+hm6) zk}V}+P4J17L{)QF*zi21k)uCDk-3y?1DDQLRrg2=5HREewHf zVLjP3W6UeLVTXk52A*}=E~j6K!rdQ1urT+dvp4LSopt^J_T)w*6z3GDr5L8S9x^2w z+tPqX zFbVbiWdYFoi z1`u6YgpLUmf`$XUn4xkFJH&tmUIvwyB@-=kY`ujFgXnVYu zvV%&QZ`dJJ*j1NHxAGdgZ4d)&Xv0u(C3$xVwhllXW3`EA*teUQJ@I7g+!bHoXu6lNUU(SNkP0V`oDT>TH`OE?~My%TUH9~kn1>nUX zf@K^*J%1E|yLvtwCsgx>$!$N(tV0VJn%2ml+%PV^A0l?W?xV_+eNQa2x%hH02#XC z3cR<0JT&^jZ#OdgkPX7zO)Q^s7EupDPGO}%Fn!TaZrD$<_m=FP<-G^5=fC?(tGbMx zAM#4;sEw=^W#V(>4|%;cy%bG(g8&OkwDl_bF4~SROl_|ONG_(9 zJsWx7!^AX2iuP4NmMhp$LEz3oMR^M<=h%0MjJp1-qRoch^Q!#Co{~}b6ws5%dVI|C z6GfL{hg|!sVj$uASt>KQJ+gwK_F9pX;Tog?pRbB5hYq4L<@!Vwb-owb`AL77@7g36 z_7Xy?5t2LREA0G&s@Az1(b~$ncrSOKWsYG7cmcIfCX!F&_Yg^iy(yPj&*|N@ky$4l zXjQ)|XL++dvI>!Mx7jKewpA1eTuz2_Y&)3x%#CPN#QDn(Q6#9}mqYLS8qIM{S*X{^ z$f8~cE``Vi=nM*g|0cQ(tId@jA~y4ZbcDPQr6VWdeFQoxXOV+h1AClBCIUc-MQWrd zWC?AMUV;~!fD9pc*TE11~g7dLEIg_0o$JdQ`0Ww3rDRX&)kkBcy}mrApr+ zt@{Y+T?%cMst^j@C{{O(Vxb$Qazut?iYn?M&7##VmhrgPV+wBy`;Iq^o!~8ECz)ONo~cU(n>|pjU1FhQ_$Q7DlD*zy7pm4F*WeHZ zs@;M2&MRkg&?2>J%}B!x=eA`V1c+EQDhsYNYfJ`W(BYdU#Bf4lNKJ zfgUjcT~UY-1$+P@0wald`K3Sd7gVD}VLu|Hh?o0@mo9(eX_2L3%w2<+0DVxD68WUG z37(*q=8)G{sNz6K{dtHjUVu{iptlZUJ!(Lk8aAp%qH$VyR(_pKjCTn>wL{u>65u|@ z)SpQ#b~kU47$&WxobeJv8v9(Rv5TE+AK_`z^I~!lS7HD zVZl=@q9?M1Ad8X)^q5%0iboJFodE=8-9 za?gLu@=2>N%lP&zl4w(5L=R%Z{lYPW=;&b+gBKuVq|ex|2S$ViVyo6dp+3HlnL;cf zQvUAQLc{_Zodk(IjPEi7tid3KF+d5~h54;o6L{;) zLbasG$w4w1Gn% z!{ir&BU!_Is+~2=gJZ~c4j>w+W-Z|QdYa*IX0?Z#!?wVXao4Xp?AP20_oXSgdF2vk_UFY@U&R{ z=}Szo32A@~_`HWad6W@W$)Sx%LyIulqlT5$>;X!b_%a+EpmO2bxfPP zk)1k87!(`;0tlh;-S~_=ylht5Aaj&jP%-f(@W5v~fl~XSLkllTZxA882;g-BUf(i4 zu2nmQSHwgyczk~*tOY*{G_TRh2EdRfAoA8|AvZ33sVNEiC zVZ&wMqeS7e=K!Y=%}mraNVqikvIy%8;S9Zss$A*a^ykR18p?#RgS=FC2F@j`86~n|2Y$h13Re#LH z?sB);#BwL*)NJa8Iaf|iT@=W7b)a3zc^7)!C+KyYe8pbkN*c=1>9{A=;=^HPN<*YL zlz+xZ6Q3}OiCpv(HC*-<6M0&<93>=wX|x@3t2mI$<#@q{PV37V#^qLVh!{9j!^9!{ z1u;>1j~7N$-wFMhsAJX%P9_d#dGNAy9eWSB8;5L^6ion5V)^^#*s>;xD-+15!M}Ia>)a>&jVP zdz4&QCTOKAXhkWSa~p9MddT|DpUn~laE)u6^ZL7JjcX&@_R)} zPPq*W#TcZ8$IDQEssJyn3#@LhNMzm6k*qi$gZ{FFHAM3P#d@-0Tbvc44VpBKmg$`mfrdVv%ZVGss` zWQf5FkFy91v$Bg!e2mr1?@EKl`H72YZkT_vB51;6yrQ6O7z3h+d#^KqRmKRx^TSz> zm0-B4m9s;HR0`R6qXnI>%!WA{Y6HZIx|dMqf(q6u$sTA017>trctI>W8eV4>-3_e` zViVS|^Xw%y7jQ19azytA7p`Wl5DV3lQ6H}%qC%-b0Rp2;HH+>iBjg20EIn9sZ+N{~ zG~S!t6=|#?6RR<}mY%)MR&5Y!<)KcNUwE0_MNoeti{>In44}^Y^l7#DIw4C0@b(22 zQYmjrLu6lGku(b3?I05y#ZZg^VxvMUVkJ1H*vM=rs|8GOAVI`nM8G^Jtib|#=_MAO zOlj;xp4RPKCC!YqwJbV?S&wvO;$GZY*PDskiJM~V;Puh64^^}sP1(Srhl?wl4>D;9 z2VPn#bVeG6ta~ysgWe=O4Uq#FoPuwG5TCEqqbEPd;&uy9+gV5tSt^;Z7o$^@uLfmU z5}ZC{HDvaHK)}gf>MS?&q}=Ee$nphs8yal7uNS3EVB!n3Rzs52mQ37(!Q{_zS`5JC zNBj~TMc-tL!k6yq?D9C6ZVTxmEm5mN@GX z`4n**V7C<>xD81VB*kW~R%%DJQ!8vyf+~}05T7Otp5o)5cv@I>vZ^XIi5Lq!rf!x3 z#Pev_bQXyrD{~f$Buva@k*(AV*z*oPGessLQ)2LFo+<^%=m`*Qn1M+F_+m7}WOm^k zQ?I~#y5QO$IhKUQR7%f?A9!K1Xe)fXO8DwzaH+%28gPYV1PwiqR3VCm_Xp;A&QVIA zFJ%9_wl@!qvby@m&-2WhnaN}(D*}?CK?4a(AdsPAHEg1yC4d@>b_lWskwsC`SNqn4 ztbjs7l&Dx_6%?yc)ZkVFXmP7XZH>kEO$W7tOEtbsO?_+S`#I-4lZD#1zQ5o1n?JbR zJ9jzv+;h)%&%IBxV+#cq(1|Pi5B+i~a3()5&vinYC~!AXkB$<3CK3Ny=loK}0C52C^I{dD}CV0*pa5dM9TX z=WwnZC*r%L<_x~Y0y|RCxePB3_s_#8ymoF!W9b3`KSqtZ=DWw zK0reAuraXG1eR44;G`Uzuh=9Dm zpb*=|HnU+WRAQz1DT_V69~rlm_%l`t)tPy5EuCqQdgt;1gs@86B9{HkHKGgFz<8zQ zSdOeku-~2k6>iWyb}eQ!-GOhf^yx)mxfLAD-^0O>V~(k$|2P{epk1lyJZuas+6Y_= zYi6}_lSd9S&Lj#|VW&J`{MfCYW7{T)ED-h7< zJ6Jok922^^YT@w4=BgE_F%=ghx!QR48XI*rV015qCTa6J0XH-W8wk6FV@pakmfyg^fwtLtbWS(^_ z@v)&FTu0H{Td+5H<*~cy8f@4o8mLzqmYeN>KxTtw3bM&~nsd&q1JFkI z<2O+}Z4a3C7lDZbI@<^FF@zP|&hiVN$l1(DUJ|KwqaAx~ZI98?hbCoZJxb9ji;d|4 zqcUpdHdxM_16Gc4)^Fy|U}$~-MTfy1HTvHG=lHMNAxR=t->&?zQL7HA(h4tSJtOKl zO@;@OjaSs%AE>_$%-=b*VG78N-9<8o_6LK z%wi8vT@gG8PbuSk4ZC0V^v{cy^TgOB#cwoiC)wJhLGTl*pLCoWKBZ<9zIg(_0a3^5JsB-VK3?d!hD26`{F=6JCA`^ zaB&&|t}(==Id1Ot7M(T+ZI>b>JY^6y=Pjl{m05jwBiUKy$fodG<0z8A=jvv9-Rg|W zqk}795J#7+NoW zzCoc?R?ZgUeFP>KW%CigwNvOOah2I`mWcs_?0L4&hP;J**kM#h*0G&a9(4xtDqwlf zCi@&HW@RipURKWIxYH_9XH+6pRkV(`#uVO8%!ZE(Fb zD5#JISFGvX0EOJ(iZw=E37o}ybsIM8HhdIk6FbSi8th^x)$?!Il>8eu<)a3Ualw~P z-xkangbHj$_XA7$;5Ldg)GR5rHDkpl-DS_?-@^Ef5d!9c-N9}ccxuF`XWyZHgIPW$ zlk5%P-se+rD&tdKh%jQRQ3wjD*Qsz79}4EX)jBpqfG@{+c0ez-KQP8-o;gs&zhYvT135 zM$}g%o&A>$^cP+k>8$T{2c*q4WP2a%hF zoTBcaXt?%+Xg-$$usez%A7FPBf%CoJyU)%lleqb-#=58<>ka_po^>{9)G^3Vo9eIA zMP-&J*v;6WI6HzJcZ5RM_ig~69GatnR=G?xPWxLFHTO{H0mBAG3DZ1)O%mR2)JAFo zmQG}k+H#ptq9)NvvLA(`Ad6y`bie$F&UWJensMD({IwzkBZWD{vege5{Dp0i^q#+dfm~0DgrJ51g;d7j^yeNGB zVh;SAbH|v^GtR1`5N;o6a#+j=`(Je({-*2jcbvHAvjK1b%0eD5ho}w*athE3*kKCc zQ%TK5aM8q98l5=@=3&L~+sZ17h-4~`6l*Ft(|G007j39%_FH)v8}?Ny*Z|-MWVS7Y zJ+}F)BDf-onkXGk=8jj%;l*!RM$rK*)kzBdoJvO~(OG&*;~0dk2K2~^xmP}JR>BkP zNMuYHE1~*5jG7W{fSma(h{&Pd5ywD--69#=MU8Qsary{Y+l2#l`Qy!@#^4jZ@f`E( zi~x?X!2~*YbHaY%sL@5Cf468!lFqnu8vM*{11G|!jUK1cZPUqd zuEjs$Ge)jZNh3}nyr@Q?Dz0)^3>Sq1QOKBp?68<>Gkj#dxhZcv1>yn24OVnU^2 zfx^w>-Vx|14$%iG_~CauN!U1_x*i7)Se3Pydv36cJy_%o?^MlYxI#_FBu zgWm#KXt&4MRdPY>`Y2%8LNDG8(=vrxwwq3{LkCVmVVbmTH#QyW9}DlW4P$ZEghH^q z&(+}g!q|ipa4bhN@EQC}F}O#%Ubt0~-CMCAMp2MOK|T_z3P{8JQSgKeyBj>Y#EG1C z9k*pL>;P)n0nK9_ev7wLA*?mA4X0F6>Tn4xa6Bi483^o&On2=56@(2C(qcfgvYOUQ zGY(r-o`rnaNuj;m&fc;?*Y;U%Za+&DWa!X3EYxgBW(2yamY^8CQ^&haliO`s9YD_! zai=(bqpBvTyaRmcH-;N(DAPQ0_)TI}wj1y(B38v%BZ?ENvkEI#yL2ohnm1B_P4kX^g)NBa|EUlXAA_ ztrI=O@ik-K(XvTKunho!?3+&O z81*nau~#y7!{N4hkQ$+xoo}FU0Cggwhhse7RQ2vl&Ap{s9vikcyZv1)Jj}U)U{=!L z1!|s!u3;^4^B0jD1;L=X-yjEd`6JC8K@JM?am~GiTm}WO$GF|g$iZp&vF2Vu4l9P} zKK|`hOmi9qKGoc7$W5n!dI36ri`)z`vQwbmT_f`w8r1!8qa^TWfd%y|+b%hT$dnX9 zQEH`V;estpis0o`3P=^Cqo zDse_;pvs6U{i&lQE|>^YWlBHPfZr-9CebOLHqZYAgp+rE#!h}CLAt*FD=!dMQTFspHZ=VT9-m{8jo zFDp;$lWkw;YBu%QqUCjPk7kb9Z<~o7Fx^#ildQW_Z6F|otAv#p{F^lbELrRT=NkDf z0JphVM6v)lHDRgkEVr}-OL#*~W4m9bVkSbEdU=ho4c*Se@cJ!qm*HqJQsqATF!L-h zYLf%6olO#9iiCqZolWY=7cOj~kfSA$a5%1WvssB~G$b5g?A)ZkaTw@vRKY-_jE|at zI(FMZL{6|{baor$JRl(9jGeVZID~n&!Hdi_;GfRNcw!lwYaV_K?NykD@XAK=i(Tf&<^%kz`UTn>*;tj@jDC`GJ^o&i4(1>Ibn@9x)ppM@Ut~+>}}`HN)x*NQae$ zBdyfiltK`|a%dVkKkdsWq(N$)(h?*P_o-XPfXdI{W*gQejfq5HBiA_siIvtzXkKpht7^9%)M-tUMwIC zCNP9lIm)qA4!AQXPyndQgIw8wD(w^idWznryxWjGBfqic&%bTKZ`ji?UT=fNaD-ez zj4)ZM%sOAH%tB|k8~t_IyEl~vBaKcfpBOqq|d|v)#IiJTuCREg0D700Ydl2J+=4q<9U<28}vs@R) z_gFOa1`W+O4Fq$$E`}FC#=NcWRW``o!v^I`$W-oT?ir7KaN4$auLa0-He)b zeRSQc3UYvxRq11ihlWjF3Fd(_OpQ2m>1~K$T70KQ7jfLEfE&1nSCiY7Mke&zb2*51 zg3EPB)ziWs_|;Kyn_ILQ2N+KRs;naxDMzWLp6zB^KabNGD(yL|fsBW%GrJZjqdQuO*Uu zZ06~X?EA2&wUoW>^oj9GtF@bPn@PO3>RQdXT`_Ov;7tRZW}?j9Ozh1IpdJW%9%bIc zyYGvwbFI4&e$FOL0%9lBX~hDMH7T7K|MeN_U}Tz zGrJG6Z2^1QVLO1XT`yR}aszt{TjdC(g*am!`gJf>R_l=rfIg2z6?u#h_)sFQUGGa! z47y&@v97-M_jF=ba;ykvFWaV(>wN)&U4fTni$Ci!SQEz-*DwI!i)|9Q*{>dk1NRAw zfs;?P%KNrm0MJupdq5KY&1h%u!C`WJh`{o>r+~5~34Pm_Fab3UpGFVwX14U}; z15q>w@W*rnKs_rC3IW1(2 zQyDCH*lq>ckVNIptG;f7Y67a$Q*_BPo~yjlhw7z zI5#3QQJB$cR^y@tZ2PJbC-*`fr^tO4Tn@Hzl8y>%pxoxfDjM19I-^ z(lv6;#$;^R-a&9fXJ`=IcH`xaDo6Gyt{Eu1(`bmA970)B2ozk75uTxtUdZz8lFm|< z7o=7g2TDBl`XO9scS}MSW=WN{9Ake8HjrI%Znq{0nkb#Qy`lo(#vgo0nU`eht*NcD z0LcY9`eNH*EQecf;Q7FQTl49{GQP%Hc>Z`tUs_f|khL2o>O3&bi)_+=<9iLT)P(pXvAE@tvNm}ZCh(TIP zi7BSNbppBNssq!6s}&nzm+4~C*a6a5Mb^bX-!5z6v`-LiD2seho!+}#MmwY&v z35_BjWJlyy@}*(HA0gjXa}8+Mx7E0^@Da+m2NZHXU*TESacyk)RtYw7!~QFQ^Jp@X zQ~cZV2>*t4&l<|-(-Qe^z~X~uCA|c4sI+h&4c}@!6S;!w?WQeKOzwjcdgA^y5IN{v z>vRqgD9xB=@KfWil0k6SN(74gpca0CK$O=Jh$2kR{em?=UNQSi2W=1X+JgB*oPDq# zkYxti79g=v3q%hJk7GF|)p&eJqK z7aPN7L62-S5;%DfK+Lf1{RD+sT=+m_28A2JKjo%NbYiX1iP>@l!*&`9!YuvLz-ffs zK)|v8N&pdRq?d*mPFyy}q_;r~0wI`~{lZg5O;lTA@<>e%qrMF9EbW2s*Ux_k<+dekn8w+DroC zwj=9LeVhhWEP|^|rZHz47d8)?K^b>rbVj+>oNOD-?IuGOJWmOejq<30Ye@RrAV#u1 z;Y^r9?LrzIYcEe2ZJPT=QNnpt&G}tH!EU*<;oD|XRbX@ zwo?WI5+J-WY&7=><$Ni?Crm_wVJ8AFay^SQjwl|L<4q`SX%;2280XYP>B+RDhv|5^Z4Ez(F zX|CazY=l=XW)wKqUZId3Qj+?KuGk@{6(=bnf+|g?KE4VYy@}wLLb%SUk0apMAdN$& zi&y#(#=}jz9aN8j@)lxK${jciYq- zIWG9;C3q*{CI#Xq+i*t6Apo%$RRF|dYPuWAqFCMnBM0aGpg}?eQO0g5Y~kM)Q}}Kp zjeomeX`&j1n|m7Q#HwHhnkU0|>p)dT`n%jff!JmEILDA}iVZ90KFy4~wX?NGVz1v0 z@o)B?-vTl$+HIki*BTYr)93|$lva>bMF(i<5F(xMx<2}psA}WEjs>S`zmWzP?KE8# zsInOZZSy5;HmiC0H=B3CHy=GAS4D;p3prUr4VJ19`KIIIPc`{BdMM&wX z3`CT;s394Y*KOGl!n>r$VY6&f!8%XnCGmGWIyGqhI zVGP!OW1(DE^9aC|pjQFuLsTJVxaxyP0ysZ2DYJ_baK;zfaPq<|Nc%<#;CYVF2K(MD zx^~+}zKjF1`m$3bWqHJ^oP zd2=P@3`Bz&cF#atJ}AU9kbkRs=q!rn?Wf_xZJPj@XCQKoG<*m=iIq&P%kn_;pGs$* z{%GJp0|1fjZtAuzVA4-Zy(N29MvbKP^$(S6pl36e`(Z(&|0<{Q%INrkv^V6({@ zsXcwREI2F%R!TjCzoUVFCoM0`Ghy(vbJJ5Kq~QsR2=NSu7Mn{Uz2o4?6MpT%F@!mp zLK4UG<+(cko2<$$wme?cTL5Qf zTG2vQSpNd$d3<3mA>wmHp}3jqaFH^+!>lXJCeO77R4PP+LRU0nSCpHMRunE{Ujm1S zf|3*41i29|zW;Z_D;Ds-w!jtg zPM-n6mxVc40NVI=f~Vbd@l;;7)?FL40|j%*^E!!oE(PqPKwFJrN)BPe zIkCKTXvd_XsB4E%oCbFhlzgZhx~RDhLAf_U)HDyjz_O1n{!dWkYzp0B)?jpRSWFri z^;OVtJ@4Bd;Iu?x-Q@FhS7w%^CclT<~wQ*f0b#}cGL`M z7(%6aWv4#*Gxj^~Vhlo{TP=D1%zn4ZhX!#krdN;Cy+cG(_54Mw9?xGCvcH_(b<{-H z`rCg4>hmeIQ6c*~I>S`{4vsmB=U`3*vbb=D-HY9Uf!2pbgZG*=G&(4TWZRS$+c6v> zI}iqg?Hj2Yy8QyP3hlgM?6F>(OIWk^;@F27ftTkFkzu(I7@VF=%Qn10x$H}aqwWbl z6~gJ9$@j{=0dY2Z;d~coBXUbmy^37!Qp!0hCWaT*pgEsHcUgVPk)1bF&-8Js-m%e< zn`!c~oVP!8BJmd1;@%4atg3{6Vx>WY@OeaSc4VXAHigzQ)R-72hBcM- z;Xc@1NTH**rUG^4(=br2a1HMW!|Y)0ty^XXjfeI-JzAz)p9hhWR9t6rKh}iX>V|xPR=A8i4 z&4yzMJUO37ci-0~iZ!<3(#d#}WVF2r1^{@gcd{su?D9et&PC?S2$3oi{$$3BD~3Z5 z!nQEY))eyMq6MxePvb(~E2hD{tic;Ea5o@WucL}Lkg}(^AgX4NF5%Y`SnYKkVZ9Ff zk87F+4|};`I|8Fg3t?0=hXm>ksIuqpwj6H$Q|Ziaq4jE=?bWQ|LK@1tyLSoobW2~X zt#~V3a4VzhYJm(0$tKrVNS2c8uSiyq>uV&rxldr4Gbj{qpfaFEykg#`&B}>jl||aw)4~8fMZFmyUMH)?sh78vO5ef;YE?3| zbj3CC+noH6j`syQF!YauGj%q>c9P5VN({L#8)Wd_RFMT%0_|z~QwqzgB9FXJDfj-Z zs4?iJ-v0VH zg9>~M1Wu3dhqS+yS~#+5bL=%Q7y6nQOLur*%Y`X>3r7>OV894`z6lMfbmAS}&(axL zZI>jY=DN1w3U2d4S_WhCwIu1KUavsF2`Ww*;(d)b!~?q+Kd%cQavuordOFJj^2D{0hUI`LuWJBkPuiHuQV54 zBm6cjz5`fAb?Lh*FbbpQMhaO(^pu#nKD_@@m~Xpnge2>q5^m$y>vd?yyvG+tyL<}W zCYSqs1>gmI5l;Yg9m8+aDYS{|D2tui(MTP2A))@UnQPhM9ao8At4U|$5!5@IhWt*O zA2sc>rt$9Q!-Y;J4fs61_u&HC^*UU5^$F0+KOQca^bQ^q1y{rw0~){@mNii1L>aOQ_lD;ljkp(ZF@$;O3S4q8$Ip4UEyB zp>V&|0G=pIT2QEDP=fDzjQ2MR{Xz$-<_7-u?HcsMtAckju&c0h3N&)XS0=GWzMq>7 z2t4!MV)1!xeoJrQ@lqO}@%gG*28eX}heO?r4I*=V95LTp=0J~R%e#%6*Vy>Vx(_GI z(u)SKBDxRgv2K=jfqBp#^4)G!Pr|K+zCwSuo@sPrzOKY&6z^hZYOKksnJX`Q`R=xh!0Yg!bF0$Dg<#$G|F84GVNQz%rW2w~j@C%u!z z62>uL)GVXWFJ+(%kNOgdY`@`Y?y==?@-X~o)5s0`C~rEs6Ohaxw+G2=a(j^!!y1&X zS?KrpCt$Ylsk(2!DbGbcXIADNCwflbk9>|UzAkkiysvz88XG#A&x@VI zpQX?-Gl4YXQ{ugGjD+V-KETIw;=PDg35BNt%R$e{!=Ic;PG*Ex;`%-hz-ttoCQDU< za~We5`Pz|7>3Qx1Om3A#zN02Nr6yg?$^Yur6U*qeOj$t>0w8$wyh*G<47ol_08D)^ z{-UY8Zz@hvDrClq9j+wBF&XnJDR>q%_**C_Tpv+FsDfajxkfpd0B&=-AT1zC5S=L& zbct>?*@iNZ4@&90H+%t^3I%@3IXu;eUM6@t=NdI7-7o8=Wb`EixW{1EQ8i9hICd7)5w=Vh8)j^z2w&MP>V)LCqi8!+JnCZw|f zYNG1^YI1^_oD?I0J1oO+>inSVo6!uzHIZitY|CgMvm{oq!N?0#Xgi!3$H3N7$bF>Y?#9Se)iZdQ)gZ%)z827xb9e|vPoXo)$ zlHiua(I^xS#=$hWHQYSO2H?dq9WmBc#NdYeJO^?IxNxr+7iJ~)&I{RsX2+lt)@R~* z!t0RmO`gPa8F;WfvkHb-;zZ=UxTL_=nZSuEcw0my$#dqjlQ3I87(<{wvmje$*UmUg zSBH%NpE!e<9cG}){8Owe%9DYjtr;xi#I=Lwcn^@(&;k957I0^*2!;;T#BToyYhCcO zv({yFE`YRXqVR6W#zqWFp~$A#vY+Wdv!HTpid~9hK*B|+i1@4$BNRHAm7X|T78T56 zSw~|i@kZGJiKWOj(e0%&Ub^m%bPbV7*ASU>R=yCv?bAfAn^L+Tr}&2{ag}-QocoaI zc&lvHjGpI&Y>+6HMBXCDj}n6Ou?!ECK%9AcMbKiU-$`%Ay5q10SGvWoDC6Z#)``tV zAW9{bJSREdd)6C~aaPF!%=sg3U=K`+2NnJgH`gspNM{7+N@2cZ{nO%!N?Zp)1kd`X z*j$S#bhnQFsa1%FrT$2X8}Qp)3h@Ij4nDf!P3eCGlx*VB4W=w+U5qo&69P-(HrrqT zn79q?R#0e*H99u{4KhUMj5|Eg5U0iKY0a$wiKjCU==Li^y9%GlI5E&<=hRMMsn_kO(QtB zdXt}Xt2ZS+0t≫q$>JSAzW#r380%0>2$qMp~GuIgVDDg#c#<>v{ zkFZSla3m|p4V@h!W9}Tt#wW8d!pET}PquHRkGDN`vgMaZ9zNNG*w<|jo-EtKz&CV3 zlLJQyo&n%jZwmHDa>J(412Bi>n`ZJcSxwdm^>||LAJ01PWczmd_^nANTXrD1;A9gX z!GG)GlV!W8&i@9VWpOw}^8)%fQuFej3i`NX-OESTAldNp@k%<;vFR08mFoIXV@fhz zeBc`~`IWdj=$Ku*0ZWq`m(j)^g~oY5dby($`|m`@U7f}E8@F`epo&eC&27s*1s~kp zR)#Al*rMr(x`H;E2><7t^w>Mq$PJ*xKO#3M_D(If?-m5T8e?(TCVmuFLxpTIcF__P zCLR|l1C9c&k=eD&I1Dqe81wr{L9F0X`uGp$#IE=;lJjD#XV8g1Op0wT5+A33u)I2p z^-mkvG(c%#rt|1ovs@Qs=hX;%8oA`|yz0at%tIz5B z^om5{DeD{$37=w!?dv;R)(-|_ALlI!zo7J?mJb)s+f}DNN6ZOU+V7^iyj67l*;Jpm z%6N3BICekx2`yQUUBAzA>{`BG=(H!vAHXRvdu>GlX&53i!rY^IEi`;6uNL|dkMdp4P39U6f8p`qxx}7k=cd&Q zj-uIA7mu!g406+XDlBg|jO#l(3TK%9@nUHD$Fof3gB&$9<+TR?dE%<}pNBJs%6Kmi zpNC<+=FCZ!uSAB0r-nfaR%)_FX=$o)chngQl2q|uWSmhrN_xRE!hpNGqlSOu=py)u zwnZjT(+&u!AIGM{xD%Eq?O=}yUW#Nnyw6&N47`2hWk3&noO~dIsOnG63M8&qz(4m< z=t0SO8^sUipCf)S|D4nOU~X;@H>(Bsz+z1uY7FFL zm{ci(_RF2^(PXbC4`|Xo0-U!TvFkke0r|@y|Mrky-~aUE{yTCT_!uz7EFmOW{z3yl zoa|Qug?!%hG-zBr0zT#p_0|(JnLC){WNYIapNuc9|K&rCQtXfcY;`Gm{ExjXa<{ zjc`0;0tn?`IPc&~kKLW&_8J2yqyH7S9uyn-k&ga1h11k_-)N4vT5nuKSvTqz0wEGb z%AV4C=mNpzP?>h4&CGn=jDgGvqJ}!pOM=~21!+X;wNU_vdPf3HQ9!c2_ zwuspAvK(HO8G}SJCk0gcss}46sGeu2KX9isaUv%%*CJUlh}xLDvHh@~S}^Mn*~c48-efC+h}Hyu~tMY za>pr9CWV33oCHBjtYjEtEs)Ie7bjx`qw~dS6uiQ4A%UXQ`QpW~(U;@Z#9}l@ausQF zKNe1#%WuAb{N`i-HAYuoLJ&dadFRt>Nlnxq~&<4 z6A2XgDV|n%M^3x|NwN!k@bg#`g0G%Sp2-7AJ5^)CyLIkOrpd7U(&T4#(7*zT8E*I)sNi2v(mG+dVW_>v z3d$(QRDg+kW4x8a_2i^R>@h&5QEc&CIYQ0MwlJ7@uJD_ z&!l_6Al#Z57P3+<*^ly*zO0la`VU*a#hpEDa&Jv!D&&& z9VZmT^7Gyl00Tb(5rGu5Xh~=SB|Tw4rHTg&CAA>O!T3^>H`l~$O8SlL@T3DGnICyp zk~ff)?=g~|SE%7bf~Pbr7ZGU2bMmRUq=Q1j_*5JRKl+Z#wn)_^_#?>$$7{kCH2Vj| z21-&Du8ma`rJh1Urf=^%@ zb}FCfz0s#D6PRW(fh24^WdZXjpI>4GPAp~> z=M%<5CAXUO`jMNYFQB-*N47_i{E^>w6*&KiseWwAf^Vu z(y*sN42Ya3D?q9*kvOi>t&B$0jeu95Q)T>ZW9IyZR|v z)V>RaULKt(1Uv+;k1Q~c6RfG~XHfzZ>DUxc?0N{r7yPjX8`wn_KJx_5mqmdBJm{Cr zAARCOmp}oF37tD5gg(x@Ryu25&Ykgx1_Nv<&S1gEU&~vmc4MGWFbKyyf(N{vbEyp+ z?E10Bv5s|TH;e zo3f}=YG%X1t{d+v+{1(tHQ!=E${8jo5QRFG2s&GkP^>BrY6?pz1Xmm-0~tB7(I7YE zf^dWW^TP%0dWt5~HJPXY;>LUV9=h!~23o9_i$Gb+Wpv+h{z!ua)**%*Qd`69x>YIy zUDp`y5Y&STEB0LD8Sa-6(hwPk-->YmUEXw(!oW%?L^wF#OAg#BfqZyfTm$)Z(rXN7 zD*_zZ?^wFvDD#Q%;K`Pz%1t-zS zP73dZsN3dfdytw(%2Ek)#A6+Wka$dkJ5P1)fdE^3eG^0o?VWidWSh35Pw7n~0u)9%YK-_>VNyB4&22kknocH%L!a-iM+p%*0vxY(G7 zK!!`7+Wfz1+ZE>!HbpKOwa`UTK7BR8H}bRD+iwdI|fBJ zZ79^5m&9u_y9i#wZUe4j;jAv|=N#VwhxBcxTnfNym97QJ_(bCxxpp7#MaIKMMgtJ^ z|AR~8@zGff;(i`6N@TW2(_#?4lZD0vZ48eKGnVeI0Xf;Q8GstE&9^-<3R?5N~ z`n)JgJWW2<84%B%n6ktMp2dM74R{TvqmgFfeVZwZZHTFI(iFaO~x6q`K_^lo}7@I6GlM=DE%Mo!l`VKla z#~fBrPSK+-O0SSfcLzXNT|=XWfm(q$acGCUVlKPvBkM32(g|-!bztt$*|E_x*a)o} zJ{pWn9#t0M7Q`nFu)~9RGyEtx(H^7UmFRN~`4bLc^53GMP`e3%#bp61Ki};lTKi%(F`nLWXXXx_-7bT*aG-4e?X;{8t#>LRyd2X z%)*is8rNyUMC9@amf-lrKANjn_#2r|tRI=>~DSnyS}+epU3?sa!;;g1G!^WapBzJ-iykTj7o4~1CiY9ubo zs6+xqzDgJMHeJ8ldwPMuTmW)w%n5!aE7k&pU|3!;puE6ojUXpK_=?i9iH$>pC!ytm zq~HsZBOhsCtiS_UDEtb!3@ktMx}J~+`Pgi-As(}2gZC}pKrrwTyWqDJ9iB_M9yE|E zBch&b(vI}qe!gq0&AQ@gb{@ikMl39hz z4ap89sNyi!;D$CQxuYmK5swRQkt&#jRKa+lGhUVLae4vYMHnHRPH1uY>_a(+tfVu0}*Gv z6LEa1Sv{Z#tQqTPoG0J3e5!YES<-m~!?j zEJd{Jg~i6KAIp~U1{9GR*^xZcET0NQK4-cNP{C|=cqg=>Cj1&*utHv}iH*5J+IgNc z`8ICqrA8H28gUm@vQ#F2H$M4B9SB~r2fJv~PjM#(%MsC~nhzD#k1yDmwRo>G`5LBV zj1d0~q7FHwPknVgc`@QqoM>PbVpy#EjiAV9kmv~48P0uRa`5xPX_&mytSSeg=x<|I zC<-9|7#0*VEH~=M&vAV>Qe86ZMx!Rm%V<{O0lMUy6?l9Up1+o^!aErsn&+>jv+KY5 zvd5Yr5kT{}UXa^FIe$`w9LMtq(Q2RdSUz7(PF`Nrs%djwm}=+6m0SC5UC+yLgdHZn^uktR1P zNO*fgdoe+@sklA*;HR84=Nuu=(}HrWA9gAaRKpaBhi#g#qU0^`>(AvJ1pAtH+y#DN z$(pWb=$QZg*x>^d-SAP@{HI`O00#xM>#N5a(6OEkvArG$Qu*Mk15Xf17~X&jM)#IRdSVO zxm#mnp3q7ENmlx?13D}mZqS!JC%MoV^Q;tl@yRy^7rEiL7~Vk^CY0u%2hB4L3VA2L zWLDyl)#O90w1F9e>h=SJ^71Oar)Yh(3F~+HCLkNAMmG?D^eMSrr_xT#rXIhQ-Fix{KC@LKdH1N3R+E^sV=q zFfV`XBP_>!%6S*-0Grw3A%EcpM#Il~nALIeydZ^-_APu-3g7Ho_!}wwvTxxLnNfe@%&vO_yWnl3$ zX1`oi@nVlbt>Bya_jHv#16`v9b2r9v;ST)`=Q256l;2OttuZJk0IT}z98j3kW#nhb zZ?n_Dm)6s&GxRqtT-9F+-#$ayB&9#-%z;A#(+2uX`2qK0zI`V5V($Mz9Kf;Q zbtykEI5{0a(;S{nDMP?}93}f4v>ZU397=U1j^Y*^&vg9x>e;yp#7H`UuCY^bh|Kc~ zMgDyCs*$4IxBLpOq{Eal5-g~vmvP;_M7}T@^D%t|Sw_z2*pDU(=IVm!smk3rDJ zoCkle=z+n==t0cogU2c568NmLkcfOh;^Ni3#2Q@zjL4o&iHJZ+`?b{^eLA?$=&mK9o%< z_$Y)d6|TL2Y}EPg!O6(*QONP0o>wNKZeHV!V0rlrXEd^QUJH~3IE!bmHVK>v?W%i? zS7wyUmXU!{ZpQq;g5ER`T+PzL1+kR!a|2(V5-Q-0P$4vbvcjxp+J%{v(`8FRA!ggS z31XhxX)jN4p)I8jvKYhG!EjH({=@ukB~Po)wjthbU~UEqyX8A}YRX1I>$QiOd((8~ z-YE^VS90>(I{e1qz65pT8A`d&tYUEQ6k(cjAAW0R6hL5Jp0X9gZsA<7myA;G6)BVQ zOOed^I61IhGTOH}Hv2wi9!!e--gXKCHF$I3lVKgz^BZyE$GZyOxwAf&7^$AWe;+zq zK`Hwz<^c*>Z#Ke<3UG0mm)-j*`vmyhK|E>mJ?ni@5kt;woa zc)=NJ)>PAejnSF6#!*n-v96n`Vjwi1-Oh<=C95Pynz*&lZRHx}H{09oI_Zxf&RD8p z=K$PeutL_8=YFh_j9xk%g&ndfXLrCpa?F2q5X2eYMEHaYOHz&jP9b4Sl*{?8aCUd& zpCD*A#Bv@6^AHWGuk3Hm*{5W)UykUR%D)mVC|n*=XdrTmxB zr{`J43-m1Je2D+d1?%E=$EDBQPo+g_6{w>X{)}%^nR&kYq(Bvek0_~;_5Pcp= z1$HXFaLv|4**-NxrE=GZ*;rtyb2;aQKHEvflT-f^=fbu4eu?i}v_NzM2` z(pjN)HsQNEJFib?=lALC0&CdsF)_he@|m#Mf!0g04w%Emva6t*vg!x4Msu?%=VgH` ztu}6|Tl%1-B(mX-_<6MocsW=}WR1-8wk9uW(yGZP&{&MQ*XmKUPkr0I3=pQlTdkMZ zA@wn{5?na-D3*#v0SiU%%Bv*7v7=yV5%*~Zmx0N_pM@BgsaTww6_HEyx1Z{7cvEfa zQv9avRw;IueHV6D_QCG;7Iuxg690&q*J{@{e}5!%yWE*`pbsKybq25tqAdr5kQw2q zJp+CzJ}kZVaYc_Zz|gs&pUez_us_E>Oov;Q%_+H z3bp3e-d1_)y?to#mwhm}%c{x1_u6Xfb4T|5=IqzaKbFlbt^slER*OEN#J5R@)s3Ef zrmn-Utes;~vJkB_7t>2-^eAx6N|e=$al0N`%WqHiS=1J5smu!A;VnrogmX>6Iklg5X3bt>&T-kUn!Ydz!j@Q^q9jL_C+bRLgu zevk3`B3V!ea?_2cy5hIulu13VApHqQ?wK?`{N9UoePDs43!wg&)ZM88&7OtBdOW{sd+uFRsiJl4%6IP^6ZK4P3Gmrn}pkR9%f2Tz?Y!(>$}V``6IlgIbTrPk64A{ z@5iBvt!a(WS=tQ}4uR+0dw*f~bM5SP__n{GoM+@1sqB7*1sXgs;lsJ_Je%z^wYAf( z5owrKimtPI-^Pu+f!nxZVvL(*Iv91cumG<^2?nk=47_s|I>P}vsxW%M?sMKlPU);z z9Hwb=bedO+!xUAPM1ZV0(9iyK1->WD-pMrBjZkfo9P=L?SotJ;L4PEhoXgjN1eY-Zz59*oj9QSW#HB%&xZgRsyZV!GQ8z<2|2F|3Kb0<7PA6s zMcOUAR7RNw1mQeK+|iSbw>muA@}gKcFW$n}8P%BOOp5;|4T`k^pB}?1FeXbBO(2T+ zNH48C7Ux-|IUu)9q84HYu-e?}B12Ik7E1(1GrY*R-ni`3?5p zBb3IEg0gNCZ%~@M7+VcoW8qafZ$kxNN@@3S4o}9%cQ{;uY_$qV-6Lq%SdN~1t{Waq_WbDFEds(|aVliW7qc8Mf zoG)5TRaP@urA69%eWiuY?{!7N>q@4tE0cTOv_n%(dtV3oeV>8e?laKutbugl-m_{l z%Kw!Dg17e}t7PZ&=pnLH&&1bHwGu>p4fx@KCMt>6uEVWs6N+A4<1B27xc)}`< z5>Xs7)dyiI!b5-{etm04suZomzQuvI2^+nZ3QQ?%*1`f4ya%VpT3EjhN;l^yTH=Yd zS?xTAev43gDUjT@z&axVHR0CU7+DvT5!Mr~AU8z)oU?$CDu|Gp z7!4-2oa(wCmrrXs+K2lrLs(`$?b|IZ z$qTr|2$@ak6QG8gcgnXt~}=`rh70)5uQ9E!Q8Lju9k11e{K&&mb*`XS;6IV5hrK8XmM zq+}9DcGMw2t^lFPcq4gp1XbjM4G!MfPXw*ROve=oR4Lwx6L4p2wV`Oe0u46BHebU> z^r$K8o~~CJ(<8cmaYV8J!-oy=MXpy7!59C&A%7950Fk$I_}kR99G^K1B_jaP%d{)! zlo(GwtR}w|tiW%OY79gQA|`Y^JCXsJ?N1tJ+Yw%OeuO)3Aw@Azn}U>~23$s5#u?TZ z%Ik42vWd}CYqa1HEAm$76`QdfN587a3xDsLodxUUlVR!gn7fU?bH@)#H{si4dUFVG z_Pf*TEwma(wJ;iahhq}sPD4g(J-|h0glXnd!8&MUZ^c&ghn?Q)L-cB7nDeDSPIblvBfVT_1h+B@SGVB0)P!hHA@)kb%4?|ZG8<~*K8Y<)o`wZ4(14(H zizX_ym&1T%Ln~Hu`N0jX1-S8tT;6BVo`e@WlE^Wsu03wd3O}s0#C|z zvI?6YMq`86`*g# zdT_Q%_10b^(6+)wxHN!)2F047WJGSS>y6L8Rm#9|Z0#-?~i4?!&Q8i zHBrA>8sIXoEdN;f4XYx#QBbggwZ+&Sks5zzpDI@K=6}`P5pUjRh2Y5utX%2{e3;?y z^IIIUy#KLlT(;ZM7o`6yeVX61&ow3dX}UOeu}R#FYu?4Y!~1R^(Soesa2CRwq_p`{ z>KA~WkY`ZV;eX%rP zK=<551@PgFFWPH<5Jb)%R*BBClfrRiX@m2D&)yACC9SD%l|o}nNjlKVT)0nXDSy5>Ajo=yp6Kow}`={!o}2Ym10+KC-hs@x4BkX1W*2gZ^qB1=x7%WpNPau!zW42 zu0EJBs-&>JZ(%hGb5pt}Eoe1ZuS*GL^sUMANU+;HTDYkYE#pj9r8)0ZA4JO9Hs}4V zPhnqT=(}(|7=&*W+BT0abcPY|9fz}UO%=>|E0Q<(wXo*Awlt>35GKdtk<+K14*Flb08<>>~M*Ok4XCurl{tzmYm1M94j>?ooD?X zFP~>bQ@DLzcRj5?(8`lZ_$%)}&|{T*Lefv>D}7T7u0aW0oyrGELgmsL`R>%+_xC~Z zc0sEg{kHOW@pP`0Z(hI^%Kdmzh|}bFeJ*|JtVz|J0tMP#uN3VIQGN#clT<4do_Ttz za2KALc}VLead#y7R&GDw5W`h?M(E$AJdr4b{}c)s`SwX`xfJ<-7$5Blxm~>fcs@S9 zG}CdUUc5YBKMs#qe)vatTFbbHLcqg#vEUi_&*4FTtqe~*1wCN>3)8q4r2hcF(nn!h ziq&qOQ7-j87CiBCw_Q5;tNi;N{hVHoc7+T_d|vT>POoR{8As|9F$EO{<8Yjw#_@G} z8XqT~#^)bj*Tda-)czTcl!24#IQ=fX#Md#tP6JM(vv_$Nj(5y`;|M*a*#GzA z>cW9OEI4$3^yE?A%J@D#ookir{$e`P`N!KcR0qzQRK@9c;Q{Rnx!=>r$2d~Xt?>Jq z13jFle({t`@fXm92I@K@O3A8+Jop%;2CO`Fr&NgYeiNWL9d_b1*W57_ z`t0d>bXC^$sb>wDhR@XS-(7iwe~@B4H^yzy*!;2iKb)60;)WH=t{b&#!IBjt7LQoD zV#ReMR*qRYV#$J4H}SvoZo08_#LA^NE?RQkjSFuWv2@|WmGiD!vU=f&c`FuZ+F C^%uGT literal 0 HcmV?d00001 diff --git a/firmware/43439A0_clm.bin b/firmware/43439A0_clm.bin new file mode 100755 index 0000000000000000000000000000000000000000..6e3ba786b2b496ef3d347d8f8150cb433f684692 GIT binary patch literal 4752 zcmd5=&2Jo85wG`p#$%7i<96cRusN(+3P=bXGc$G^+e9l*_k4HvxINu7p0RR?hy({h zP{bjpAntoY95`}CLIUx3gTWD{Ngg`9-o{YpXg-u9lm+};^mKCzk1v`?tGju z#x%{YX_-CKw$?W7u5Ecc&F1cItJT_TwfFYg?VB#$^qA!@2Si6~`O}pFMxf_WY1-HEpv$?9jST>m~JvroS-# z!t|Fm9GY-!!nuk1Hkz1dW1>4V7OTpeSlh%JosDYhDHoVIZQtQvjtjK%Upyw^K`Zzoyk4?!M zUeaPAo~@PUux3qC!yXgTST^;r#=2_ckuisLTZh`38MVBm?VTMMH%wd4D$Q5hUZ-u2 z+TPWN8Sl#TuIlF4_nchvt}MAl%KHU*VGUM)(elE!_dW2k-nz9#&70RNhaFn7Lu;}yG#Cukq_AP=P=c>b!tjmWgYB^j-*VmK9G731TO7i`2fVNvaeO(op6N!2%E!@3E&C=hes$>IK10sjll6kg~L@qrY6cYWrDro zhcicX!bFsYsn{F>Dg4Y8Dc7y8Sv!D!1QZqfMzw30xV*Zlfd+-Et&G6W$d&;t1Upw@Atbf@fw|o}!6L=5x_W zo=9TT1W5v+E&3--0M#j*A&_Td8!fQBU$7~ z1Oq;y-N+0{;w##iSmij}9)Ky9}dlNl464@Zc-A^TllGw$I5=4i0oS#T^ zDAAyBpX8J#nk3vd$p}gc(uTVzA&`grEGLB-EHL2iN(kBSl#0iKR6Ukr6Crz3t%vDE zvNzS*k|NoV>Z!Ueh3j}b1gS)j(oE`nimbz~OeN>Ym& zQ&9%zT28Ow4TK=;%ht=(tC^lM8MMP2nVvEkyb9N4Y6f?`B1heu%_RicLaI$Bg@`is zAvemxB?qFP$x%YwKs%*;C$Xhv?YNnslc5_au$_gNN(;r#J+~9M&?>PI$i%(4bLK*d zUMH7S4|1eB+>&dm=g2XHJ6CUTAMYgRV{ufjIhJcsxY3t2Gr^u~`tb!orZf+8bySW# zLSf;XgnNz*2 zag_OT(kh!3@sPWI5F2TzmMUi@5u}v{Zlx))(xhEYMe<5e&tVEyr4cJn{C;wX|r8B0moI19|<0yYR|}CZQG+g zd*t(_>B_Um)wi`r{E7qXzVWEotSKPinw8tPHT(8}Z+s#-!^9eFFDo6qgzsSXYR8_l zV8ROpzMB#Knjaiu`T^7b;(Hkfm+-3P1=IXTf8+Nf-0+ge^qL2_HJ~Qb-@Qki*G|Tl z{JTZQdbRpiV|%B$%Qr#mZMXK?`~Jbb``bDU8GqpSCfw6-H^Y6*?1W~gG;&16< literal 0 HcmV?d00001 diff --git a/firmware/LICENSE-permissive-binary-license-1.0.txt b/firmware/LICENSE-permissive-binary-license-1.0.txt new file mode 100644 index 000000000..cbb51f9c9 --- /dev/null +++ b/firmware/LICENSE-permissive-binary-license-1.0.txt @@ -0,0 +1,49 @@ +Permissive Binary License + +Version 1.0, July 2019 + +Redistribution. Redistribution and use in binary form, without +modification, are permitted provided that the following conditions are +met: + +1) Redistributions must reproduce the above copyright notice and the + following disclaimer in the documentation and/or other materials + provided with the distribution. + +2) Unless to the extent explicitly permitted by law, no reverse + engineering, decompilation, or disassembly of this software is + permitted. + +3) Redistribution as part of a software development kit must include the + accompanying file named �DEPENDENCIES� and any dependencies listed in + that file. + +4) Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +Limited patent license. The copyright holders (and contributors) grant a +worldwide, non-exclusive, no-charge, royalty-free patent license to +make, have made, use, offer to sell, sell, import, and otherwise +transfer this software, where such license applies only to those patent +claims licensable by the copyright holders (and contributors) that are +necessarily infringed by this software. This patent license shall not +apply to any combinations that include this software. No hardware is +licensed hereunder. + +If you institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the software +itself infringes your patent(s), then your rights granted under this +license shall terminate as of the date such litigation is filed. + +DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS." ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/firmware/README.md b/firmware/README.md new file mode 100644 index 000000000..7381fdc56 --- /dev/null +++ b/firmware/README.md @@ -0,0 +1,5 @@ +# WiFi firmware + +Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439 + +Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e42bae687..0e4a862c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -235,11 +235,9 @@ pub struct Control<'a> { } impl<'a> Control<'a> { - pub async fn init(&mut self) -> NetDevice<'a> { + pub async fn init(&mut self, clm: &[u8]) -> NetDevice<'a> { const CHUNK_SIZE: usize = 1024; - let clm = unsafe { slice::from_raw_parts(0x10140000 as *const u8, 4752) }; - info!("Downloading CLM..."); let mut offs = 0; @@ -528,7 +526,12 @@ pub struct Runner<'a, PWR, SPI> { backplane_window: u32, } -pub async fn new<'a, PWR, SPI>(state: &'a State, pwr: PWR, spi: SPI) -> (Control<'a>, Runner<'a, PWR, SPI>) +pub async fn new<'a, PWR, SPI>( + state: &'a State, + pwr: PWR, + spi: SPI, + firmware: &[u8], +) -> (Control<'a>, Runner<'a, PWR, SPI>) where PWR: OutputPin, SPI: SpiDevice, @@ -543,7 +546,7 @@ where backplane_window: 0xAAAA_AAAA, }; - runner.init().await; + runner.init(firmware).await; (Control { state }, runner) } @@ -554,7 +557,7 @@ where SPI: SpiDevice, SPI::Bus: SpiBusRead + SpiBusWrite, { - async fn init(&mut self) { + async fn init(&mut self, firmware: &[u8]) { // Reset self.pwr.set_low().unwrap(); Timer::after(Duration::from_millis(20)).await; @@ -598,17 +601,8 @@ where let ram_addr = CHIP.atcm_ram_base_address; - // I'm flashing the firmwares independently at hardcoded addresses, instead of baking them - // into the program with `include_bytes!` or similar, so that flashing the program stays fast. - // - // Flash them like this, also don't forget to update the lengths below if you change them!. - // - // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 - // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 - let fw = unsafe { slice::from_raw_parts(0x10100000 as *const u8, 224190) }; - info!("loading fw"); - self.bp_write(ram_addr, fw).await; + self.bp_write(ram_addr, firmware).await; info!("loading nvram"); // Round up to 4 bytes. From 54269a07614105b30e8ea52d424ff417fd9e6e87 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 17 Jul 2022 00:34:27 +0200 Subject: [PATCH 0016/1575] Switch default log to debug. Trace is very VRYY verbose. --- examples/rpi-pico-w/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/.cargo/config.toml b/examples/rpi-pico-w/.cargo/config.toml index 18bd4dfe8..6183e70f7 100644 --- a/examples/rpi-pico-w/.cargo/config.toml +++ b/examples/rpi-pico-w/.cargo/config.toml @@ -5,4 +5,4 @@ runner = "probe-run --chip RP2040" target = "thumbv6m-none-eabi" [env] -DEFMT_LOG = "trace" +DEFMT_LOG = "debug" From 726d68a706507d3c0a1b7c5a9c76231ccf1dcbcf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 17 Jul 2022 00:34:41 +0200 Subject: [PATCH 0017/1575] Add status and instructions in README. --- README.md | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7df988b79..8ee0c235d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,49 @@ # cyw43 -Very WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. +WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver). + +## Current status + +Working: + +- Station mode (joining an AP). +- Sending and receiving Ethernet frames. +- Using the default MAC address. +- [`embassy-net`](https://embassy.dev) integration. + +TODO: + +- AP mode (creating an AP) +- GPIO support (used for the Pico W LED) +- Scanning +- Setting a custom MAC address. +- RP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W. Probably porting [this](https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/cyw43_driver). (Currently bitbanging is used). +- Using the IRQ pin instead of polling the bus. +- Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) + +## Running the example + +- `cargo install probe-run` +- `cd examples/rpi-pico-w` +- Edit `src/main.rs` with your Wifi network's name and password. +- `cargo run --release` + +After a few seconds, you should see that DHCP picks up an IP address like this + +``` +11.944489 DEBUG Acquired IP configuration: +11.944517 DEBUG IP address: 192.168.0.250/24 +11.944620 DEBUG Default gateway: 192.168.0.33 +11.944722 DEBUG DNS server 0: 192.168.0.33 +``` + +The example implements a TCP echo server on port 1234. You can try connecting to it with: + +``` +nc 192.168.0.250 1234 +``` + +Send it some data, you should see it echoed back and printed in the firmware's logs. ## License From 92505f53e239bf6004dec1140b2d5a15b762bb66 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Jul 2022 23:50:40 +0200 Subject: [PATCH 0018/1575] Get wifi credentials from envvars in example. --- .vscode/settings.json | 4 ++++ README.md | 3 +-- examples/rpi-pico-w/src/main.rs | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 748816bb9..082b286da 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,4 +12,8 @@ "rust-analyzer.linkedProjects": [ "examples/rpi-pico-w/Cargo.toml", ], + "rust-analyzer.server.extraEnv": { + "WIFI_NETWORK": "foo", + "WIFI_PASSWORD": "foo", + } } \ No newline at end of file diff --git a/README.md b/README.md index 8ee0c235d..5d4347e99 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,7 @@ TODO: - `cargo install probe-run` - `cd examples/rpi-pico-w` -- Edit `src/main.rs` with your Wifi network's name and password. -- `cargo run --release` +- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release` After a few seconds, you should see that DHCP picks up an IP address like this diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 633c1b2b3..3e966d212 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -71,8 +71,8 @@ async fn main(spawner: Spawner, p: Peripherals) { let net_device = control.init(clm).await; - //control.join_open("MikroTik-951589").await; - control.join_wpa2("DirbaioWifi", "HelloWorld").await; + //control.join_open(env!("WIFI_NETWORK")).await; + control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { From 5ef40acd1d9bf3b9c94787f3901ef32bd0d3a248 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 22 Jul 2022 00:05:39 +0200 Subject: [PATCH 0019/1575] Fix set iovar buffer length. --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0e4a862c5..b06eb36e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -422,7 +422,7 @@ impl<'a> Control<'a> { buf[name.len() + 1..][..val.len()].copy_from_slice(val); let total_len = name.len() + 1 + val.len(); - self.ioctl(2, 263, 0, &mut buf).await; + self.ioctl(2, 263, 0, &mut buf[..total_len]).await; } // TODO this is not really working, it always returns all zeros. @@ -904,7 +904,7 @@ where let bus = unsafe { &mut *bus }; async { bus.write(&[cmd]).await?; - bus.write(&buf[..(total_len + 3) / 4]).await?; + bus.write(&buf[..total_len / 4]).await?; Ok(()) } }) From ddfbfa0132285963382a4d7290d506186da31369 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 28 Jul 2022 18:43:17 +0200 Subject: [PATCH 0020/1575] move ioctl_id from State to Runner. --- src/lib.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b06eb36e6..133ce3161 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -209,7 +209,6 @@ enum IoctlState { } pub struct State { - ioctl_id: Cell, ioctl_state: Cell, tx_channel: Channel, @@ -220,7 +219,6 @@ pub struct State { impl State { pub fn new() -> Self { Self { - ioctl_id: Cell::new(0), ioctl_state: Cell::new(IoctlState::Idle), tx_channel: Channel::new(), @@ -453,8 +451,6 @@ impl<'a> Control<'a> { yield_now().await; } - self.state.ioctl_id.set(self.state.ioctl_id.get().wrapping_add(1)); - self.state .ioctl_state .set(IoctlState::Pending { kind, cmd, iface, buf }); @@ -522,7 +518,8 @@ pub struct Runner<'a, PWR, SPI> { pwr: PWR, spi: SPI, - ioctl_seq: u8, + ioctl_id: u16, + sdpcm_seq: u8, backplane_window: u32, } @@ -542,7 +539,8 @@ where pwr, spi, - ioctl_seq: 0, + ioctl_id: 0, + sdpcm_seq: 0, backplane_window: 0xAAAA_AAAA, }; @@ -669,8 +667,7 @@ where // Send stuff // TODO flow control if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { - self.send_ioctl(kind, cmd, iface, unsafe { &*buf }, self.state.ioctl_id.get()) - .await; + self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; self.state.ioctl_state.set(IoctlState::Sent { buf }); } @@ -723,8 +720,8 @@ where let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); - let seq = self.ioctl_seq; - self.ioctl_seq = self.ioctl_seq.wrapping_add(1); + let seq = self.sdpcm_seq; + self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); let sdpcm_header = SdpcmHeader { len: total_len as u16, // TODO does this len need to be rounded up to u32? @@ -802,7 +799,7 @@ where trace!(" {:?}", cdc_header); if let IoctlState::Sent { buf } = self.state.ioctl_state.get() { - if cdc_header.id == self.state.ioctl_id.get() { + if cdc_header.id == self.ioctl_id { assert_eq!(cdc_header.status, 0); // todo propagate error instead let resp_len = cdc_header.len as usize; @@ -858,19 +855,20 @@ where } } - async fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8], id: u16) { + async fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) { let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); - let seq = self.ioctl_seq; - self.ioctl_seq = self.ioctl_seq.wrapping_add(1); + let sdpcm_seq = self.sdpcm_seq; + self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); + self.ioctl_id = self.ioctl_id.wrapping_add(1); let sdpcm_header = SdpcmHeader { len: total_len as u16, // TODO does this len need to be rounded up to u32? len_inv: !total_len as u16, - sequence: seq, + sequence: sdpcm_seq, channel_and_flags: 0, // control channel next_length: 0, header_length: SdpcmHeader::SIZE as _, @@ -883,7 +881,7 @@ where cmd: cmd, len: data.len() as _, flags: kind as u16 | (iface as u16) << 12, - id, + id: self.ioctl_id, status: 0, }; trace!("tx {:?}", sdpcm_header); From 3388b5cecf95d6b0bc9cf5c952e9f0aa1e019b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Gr=C3=B6nlund?= Date: Tue, 9 Aug 2022 00:53:32 +0200 Subject: [PATCH 0021/1575] Improve data checks for VHD events For some reason I got strange events on channel 1 (ASYNCEVENT_HEADER): 0.647329 WARN unexpected ehternet type 0x0508, expected Qualcom ether type 0x886c This patch improves the validation of BCD WHD events to minimize the risk for panic. --- src/events.rs | 1 + src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++-------- src/structs.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/src/events.rs b/src/events.rs index b35b12faa..a828eec98 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,4 +1,5 @@ #![allow(unused)] +#![allow(non_camel_case_types)] use core::num; diff --git a/src/lib.rs b/src/lib.rs index 133ce3161..3d08370c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -815,20 +815,55 @@ where trace!(" {:?}", bcd_header); let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; - if packet_start > payload.len() { - warn!("packet start out of range."); + + if packet_start + EventPacket::SIZE > payload.len() { + warn!("BCD event, incomplete header"); return; } - let packet = &payload[packet_start..]; - trace!(" {:02x}", &packet[..(packet.len() as usize).min(36)]); + let bcd_packet = &payload[packet_start..]; + trace!(" {:02x}", &bcd_packet[..(bcd_packet.len() as usize).min(36)]); - let mut evt = EventHeader::from_bytes(&packet[24..][..EventHeader::SIZE].try_into().unwrap()); - evt.byteswap(); - let evt_data = &packet[24 + EventHeader::SIZE..][..evt.datalen as usize]; + let mut event_packet = EventPacket::from_bytes(&bcd_packet[..EventPacket::SIZE].try_into().unwrap()); + event_packet.byteswap(); + + const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h + if event_packet.eth.ether_type != ETH_P_LINK_CTL { + warn!( + "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", + event_packet.eth.ether_type, ETH_P_LINK_CTL + ); + return; + } + const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; + if event_packet.hdr.oui != BROADCOM_OUI { + warn!( + "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", + event_packet.hdr.oui, BROADCOM_OUI + ); + return; + } + const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; + if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { + warn!("unexpected subtype {}", event_packet.hdr.subtype); + return; + } + + const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; + if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { + warn!("unexpected user_subtype {}", event_packet.hdr.subtype); + return; + } + + if event_packet.msg.datalen as usize >= (bcd_packet.len() - EventMessage::SIZE) { + warn!("BCD event, incomplete data"); + return; + } + + let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; debug!( "=== EVENT {}: {} {:02x}", - events::Event::from(evt.event_type as u8), - evt, + events::Event::from(event_packet.msg.event_type as u8), + event_packet.msg, evt_data ); } diff --git a/src/structs.rs b/src/structs.rs index 060c2b060..7a7c25b26 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -64,10 +64,44 @@ pub struct BcdHeader { } impl_bytes!(BcdHeader); +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct EthernetHeader { + pub destination_mac: [u8; 6], + pub source_mac: [u8; 6], + pub ether_type: u16, +} + +impl EthernetHeader { + pub fn byteswap(&mut self) { + self.ether_type = self.ether_type.to_be(); + } +} + #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct EventHeader { + pub subtype: u16, + pub length: u16, + pub version: u8, + pub oui: [u8; 3], + pub user_subtype: u16, +} + +impl EventHeader { + pub fn byteswap(&mut self) { + self.subtype = self.subtype.to_be(); + self.length = self.length.to_be(); + self.user_subtype = self.user_subtype.to_be(); + } +} + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct EventMessage { /// version pub version: u16, /// see flags below @@ -91,9 +125,9 @@ pub struct EventHeader { /// source bsscfg index pub bsscfgidx: u8, } -impl_bytes!(EventHeader); +impl_bytes!(EventMessage); -impl EventHeader { +impl EventMessage { pub fn byteswap(&mut self) { self.version = self.version.to_be(); self.flags = self.flags.to_be(); @@ -105,6 +139,24 @@ impl EventHeader { } } +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct EventPacket { + pub eth: EthernetHeader, + pub hdr: EventHeader, + pub msg: EventMessage, +} +impl_bytes!(EventPacket); + +impl EventPacket { + pub fn byteswap(&mut self) { + self.eth.byteswap(); + self.hdr.byteswap(); + self.msg.byteswap(); + } +} + #[derive(Clone, Copy)] #[repr(C)] pub struct DownloadHeader { From f76815d642064b5ed5b1673e4a386e2747813f20 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 13 Aug 2022 15:37:30 +0200 Subject: [PATCH 0022/1575] Update Embassy. --- Cargo.toml | 6 ++++-- examples/rpi-pico-w/Cargo.toml | 13 ++++++++----- examples/rpi-pico-w/src/main.rs | 17 ++++++++--------- src/lib.rs | 8 ++++---- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d35e865bf..cea7d7801 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,12 @@ version = "0.1.0" edition = "2021" [features] -defmt = ["dep:defmt", "embassy/defmt"] +defmt = ["dep:defmt"] log = ["dep:log"] + [dependencies] -embassy = { version = "0.1.0" } +embassy-executor = { version = "0.1.0", features = [ "time" ] } +embassy-util = { version = "0.1.0" } embassy-net = { version = "0.1.0" } atomic-polyfill = "0.1.5" diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 9e1d75470..af558d8cd 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt"]} -embassy = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-executor = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-util = { version = "0.1.0" } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } atomic-polyfill = "0.1.5" @@ -26,10 +27,12 @@ heapless = "0.7.15" [patch.crates-io] -embassy = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "5f43c1d37e9db847c7861fe0bd821db62edba9f6" } -#embassy = { path = "/home/dirbaio/embassy/embassy/embassy" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } +embassy-util = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } +#embassy-executor = { path = "/home/dirbaio/embassy/embassy/embassy-executor" } +#embassy-util = { path = "/home/dirbaio/embassy/embassy/embassy-util" } #embassy-rp = { path = "/home/dirbaio/embassy/embassy/embassy-rp" } #embassy-net = { path = "/home/dirbaio/embassy/embassy/embassy-net" } #smoltcp = { path = "./smoltcp" } diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 3e966d212..91f087262 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -5,15 +5,14 @@ use core::convert::Infallible; use core::future::Future; -use defmt::{assert, assert_eq, panic, *}; -use embassy::executor::Spawner; -use embassy::time::{Duration, Timer}; -use embassy::util::Forever; +use defmt::*; +use embassy_executor::executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; -use embassy_rp::gpio::{Flex, Level, Output, Pin}; +use embassy_net::{Stack, StackResources}; +use embassy_rp::gpio::{Flex, Level, Output}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embassy_rp::Peripherals; +use embassy_util::Forever; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite}; use embedded_io::asynch::{Read, Write}; @@ -27,19 +26,19 @@ macro_rules! forever { }}; } -#[embassy::task] +#[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, ExclusiveDevice>>, ) -> ! { runner.run().await } -#[embassy::task] +#[embassy_executor::task] async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } -#[embassy::main] +#[embassy_executor::main] async fn main(spawner: Spawner, p: Peripherals) { info!("Hello World!"); diff --git a/src/lib.rs b/src/lib.rs index 3d08370c7..3932ce41b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,11 +17,11 @@ use core::sync::atomic::Ordering; use core::task::Waker; use atomic_polyfill::AtomicBool; -use embassy::blocking_mutex::raw::NoopRawMutex; -use embassy::channel::mpmc::Channel; -use embassy::time::{block_for, Duration, Timer}; -use embassy::util::yield_now; +use embassy_executor::time::{block_for, Duration, Timer}; use embassy_net::{PacketBoxExt, PacketBuf}; +use embassy_util::blocking_mutex::raw::NoopRawMutex; +use embassy_util::channel::mpmc::Channel; +use embassy_util::yield_now; use embedded_hal_1::digital::blocking::OutputPin; use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; From d35a1c9790e32477aac2fd6abe38a8c76263fcc8 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 19:39:13 +0200 Subject: [PATCH 0023/1575] Preliminary DMA support for RP2040 --- embassy-rp/src/dma.rs | 143 ++++++++++++++++++++++-------- embassy-rp/src/uart.rs | 197 ++++++++++++++++++++++++++++++++--------- 2 files changed, 260 insertions(+), 80 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 42c4fd13e..dfa047a2f 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,39 +1,71 @@ +use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; -use embassy_hal_common::impl_peripheral; +use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use futures::Future; use crate::pac::dma::vals; use crate::{pac, peripherals}; -pub struct Dma { - _inner: T, +pub fn copy<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: &[W], to: &mut [W]) -> Transfer<'a, C> { + assert!(from.len() == to.len()); + + into_ref!(ch); + + unsafe { + let p = ch.regs(); + + p.read_addr().write_value(from.as_ptr() as u32); + p.write_addr().write_value(to.as_mut_ptr() as u32); + p.trans_count().write_value(from.len() as u32); + + compiler_fence(Ordering::SeqCst); + + p.ctrl_trig().write(|w| { + w.set_data_size(W::size()); + w.set_incr_read(true); + w.set_incr_write(true); + w.set_chain_to(ch.number()); + w.set_en(true); + }); + + // FIXME: + while p.ctrl_trig().read().busy() {} + + compiler_fence(Ordering::SeqCst); + } + Transfer::new(ch) } -impl Dma { - pub fn copy(inner: T, from: &[u32], to: &mut [u32]) { - assert!(from.len() == to.len()); +pub(crate) struct Transfer<'a, C: Channel> { + channel: PeripheralRef<'a, C>, +} - unsafe { - let p = inner.regs(); +impl<'a, C: Channel> Transfer<'a, C> { + pub(crate) fn new(channel: impl Peripheral

+ 'a) -> Self { + into_ref!(channel); + Self { channel } + } +} - p.read_addr().write_value(from.as_ptr() as u32); - p.write_addr().write_value(to.as_mut_ptr() as u32); - p.trans_count().write_value(from.len() as u32); +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + // self.channel.request_stop(); + // while self.channel.is_running() {} + } +} - compiler_fence(Ordering::SeqCst); - - p.ctrl_trig().write(|w| { - w.set_data_size(vals::DataSize::SIZE_WORD); - w.set_incr_read(true); - w.set_incr_write(true); - w.set_chain_to(inner.number()); - w.set_en(true); - }); - - while p.ctrl_trig().read().busy() {} - - compiler_fence(Ordering::SeqCst); - } +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // self.channel.set_waker(cx.waker()); + // if self.channel.is_running() { + // Poll::Pending + // } else { + Poll::Ready(()) + // } } } @@ -42,38 +74,77 @@ pub struct NoDma; impl_peripheral!(NoDma); mod sealed { - use super::*; + pub trait Channel {} - pub trait Channel { - fn number(&self) -> u8; + pub trait Word {} +} - fn regs(&self) -> pac::dma::Channel { - pac::DMA.ch(self.number() as _) +pub trait Channel: Peripheral

+ sealed::Channel + Into + Sized + 'static { + fn number(&self) -> u8; + + fn regs(&self) -> pac::dma::Channel { + pac::DMA.ch(self.number() as _) + } + + fn degrade(self) -> AnyChannel { + AnyChannel { + number: self.number(), } } } -pub trait Channel: sealed::Channel {} +pub trait Word: sealed::Word { + fn size() -> vals::DataSize; +} + +impl sealed::Word for u8 {} +impl Word for u8 { + fn size() -> vals::DataSize { + vals::DataSize::SIZE_BYTE + } +} + +impl sealed::Word for u16 {} +impl Word for u16 { + fn size() -> vals::DataSize { + vals::DataSize::SIZE_HALFWORD + } +} + +impl sealed::Word for u32 {} +impl Word for u32 { + fn size() -> vals::DataSize { + vals::DataSize::SIZE_WORD + } +} pub struct AnyChannel { number: u8, } -impl Channel for AnyChannel {} -impl sealed::Channel for AnyChannel { +impl_peripheral!(AnyChannel); + +impl sealed::Channel for AnyChannel {} +impl Channel for AnyChannel { fn number(&self) -> u8 { self.number } } macro_rules! channel { - ($type:ident, $num:expr) => { - impl Channel for peripherals::$type {} - impl sealed::Channel for peripherals::$type { + ($name:ident, $num:expr) => { + impl sealed::Channel for peripherals::$name {} + impl Channel for peripherals::$name { fn number(&self) -> u8 { $num } } + + impl From for crate::dma::AnyChannel { + fn from(val: peripherals::$name) -> Self { + crate::dma::Channel::degrade(val) + } + } }; } diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index 6c5ab3515..c1596960f 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -2,6 +2,7 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; +use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; @@ -76,26 +77,27 @@ pub enum Error { Framing, } -pub struct Uart<'d, T: Instance> { - tx: UartTx<'d, T>, - rx: UartRx<'d, T>, +pub struct Uart<'d, T: Instance, M: Mode> { + tx: UartTx<'d, T, M>, + rx: UartRx<'d, T, M>, } -pub struct UartTx<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, +pub struct UartTx<'d, T: Instance, M: Mode> { + tx_dma: Option>, + phantom: PhantomData<(&'d mut T, M)>, } -pub struct UartRx<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, +pub struct UartRx<'d, T: Instance, M: Mode> { + rx_dma: Option>, + phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> UartTx<'d, T> { - fn new() -> Self { - Self { phantom: PhantomData } - } - - pub async fn write(&mut self, _buffer: &[u8]) -> Result<(), Error> { - todo!() +impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { + fn new(tx_dma: Option>) -> Self { + Self { + tx_dma, + phantom: PhantomData, + } } pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { @@ -116,13 +118,29 @@ impl<'d, T: Instance> UartTx<'d, T> { } } -impl<'d, T: Instance> UartRx<'d, T> { - fn new() -> Self { - Self { phantom: PhantomData } +impl<'d, T: Instance> UartTx<'d, T, Async> { + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + if let Some(ch) = &mut self.tx_dma { + unsafe { + T::regs().uartdmacr().modify(|reg| { + reg.set_txdmae(true); + }); + } + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + let transfer = crate::dma::copy(ch, buffer, unsafe { T::regs().uartdr().ptr() }); + transfer.await; + } + Ok(()) } +} - pub async fn read(&mut self, _buffer: &mut [u8]) -> Result<(), Error> { - todo!(); +impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { + fn new(rx_dma: Option>) -> Self { + Self { + rx_dma, + phantom: PhantomData, + } } pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { @@ -150,25 +168,42 @@ impl<'d, T: Instance> UartRx<'d, T> { } } -impl<'d, T: Instance> Uart<'d, T> { +impl<'d, T: Instance> UartRx<'d, T, Async> { + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + if let Some(ch) = &mut self.rx_dma { + unsafe { + T::regs().uartdmacr().modify(|reg| { + reg.set_rxdmae(true); + }); + } + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + let transfer = crate::dma::copy(ch, unsafe { T::regs().uartdr().ptr() }, buffer); + transfer.await; + } + Ok(()) + } +} + +impl<'d, T: Instance> Uart<'d, T, Blocking> { /// Create a new UART without hardware flow control - pub fn new( + pub fn new_blocking( uart: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(tx, rx); - Self::new_inner(uart, rx.map_into(), tx.map_into(), None, None, config) + Self::new_inner(uart, rx.map_into(), tx.map_into(), None, None, None, None, config) } /// Create a new UART with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( + pub fn new_with_rtscts_blocking( uart: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, - cts: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(tx, rx, cts, rts); @@ -176,18 +211,72 @@ impl<'d, T: Instance> Uart<'d, T> { uart, rx.map_into(), tx.map_into(), - Some(cts.map_into()), Some(rts.map_into()), + Some(cts.map_into()), + None, + None, + config, + ) + } +} + +impl<'d, T: Instance> Uart<'d, T, Async> { + /// Create a new DMA enabled UART without hardware flow control + pub fn new( + uart: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + rx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(tx, rx, tx_dma, rx_dma); + Self::new_inner( + uart, + rx.map_into(), + tx.map_into(), + None, + None, + Some(tx_dma.map_into()), + Some(rx_dma.map_into()), config, ) } + /// Create a new DMA enabled UART with hardware flow control (RTS/CTS) + pub fn new_with_rtscts( + uart: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(tx, rx, cts, rts, tx_dma, rx_dma); + Self::new_inner( + uart, + rx.map_into(), + tx.map_into(), + Some(rts.map_into()), + Some(cts.map_into()), + Some(tx_dma.map_into()), + Some(rx_dma.map_into()), + config, + ) + } +} + +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>, - cts: Option>, rts: Option>, + cts: Option>, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { into_ref!(_uart); @@ -246,15 +335,13 @@ impl<'d, T: Instance> Uart<'d, T> { } Self { - tx: UartTx::new(), - rx: UartRx::new(), + tx: UartTx::new(tx_dma), + rx: UartRx::new(rx_dma), } } +} - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.write(buffer).await - } - +impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write(buffer) } @@ -263,26 +350,31 @@ impl<'d, T: Instance> Uart<'d, T> { self.tx.blocking_flush() } - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.read(buffer).await - } - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.blocking_read(buffer) } - /// Split the Uart into a transmitter and receiver, which is - /// particuarly useful when having two tasks correlating to - /// transmitting and receiving. - pub fn split(self) -> (UartTx<'d, T>, UartRx<'d, T>) { + /// Split the Uart into a transmitter and receiver, which is particuarly + /// useful when having two tasks correlating to transmitting and receiving. + pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { (self.tx, self.rx) } } +impl<'d, T: Instance> Uart<'d, T, Async> { + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.write(buffer).await + } + + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.rx.read(buffer).await + } +} + mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::serial::Read for UartRx<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, T, M> { type Error = Error; fn read(&mut self) -> Result> { let r = T::regs(); @@ -306,7 +398,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for UartTx<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) @@ -316,14 +408,14 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::serial::Read for Uart<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { type Error = Error; fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } - impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for Uart<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) @@ -419,6 +511,8 @@ cfg_if::cfg_if! { mod sealed { use super::*; + pub trait Mode {} + pub trait Instance { fn regs() -> pac::uart::Uart; } @@ -428,6 +522,21 @@ mod sealed { pub trait RtsPin {} } +pub trait Mode: sealed::Mode {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Blocking; +pub struct Async; + +impl_mode!(Blocking); +impl_mode!(Async); + pub trait Instance: sealed::Instance {} macro_rules! impl_instance { From 3bbfc11f45149c5624e2ff6370f8e71b956402af Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 20:30:24 +0200 Subject: [PATCH 0024/1575] Stop active DMA transfer on drop --- embassy-rp/src/dma.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index dfa047a2f..bf15e1f4e 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -30,9 +30,6 @@ pub fn copy<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: &[W] w.set_en(true); }); - // FIXME: - while p.ctrl_trig().read().busy() {} - compiler_fence(Ordering::SeqCst); } Transfer::new(ch) @@ -51,8 +48,11 @@ impl<'a, C: Channel> Transfer<'a, C> { impl<'a, C: Channel> Drop for Transfer<'a, C> { fn drop(&mut self) { - // self.channel.request_stop(); - // while self.channel.is_running() {} + let p = self.channel.regs(); + unsafe { + p.ctrl_trig().write(|w| w.set_en(false)); + while p.ctrl_trig().read().busy() {} + } } } @@ -64,7 +64,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { // if self.channel.is_running() { // Poll::Pending // } else { - Poll::Ready(()) + Poll::Ready(()) // } } } @@ -87,9 +87,7 @@ pub trait Channel: Peripheral

+ sealed::Channel + Into + S } fn degrade(self) -> AnyChannel { - AnyChannel { - number: self.number(), - } + AnyChannel { number: self.number() } } } From 55a63a5417ccfd2ca2792215920de14519a3d27b Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 20:30:50 +0200 Subject: [PATCH 0025/1575] Attempt to implement future for DMA transfer --- embassy-rp/src/dma.rs | 49 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index bf15e1f4e..ec09a7699 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,8 +1,9 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::{Context, Poll}; +use core::task::{Context, Poll, Waker}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; use futures::Future; use crate::pac::dma::vals; @@ -60,15 +61,41 @@ impl<'a, C: Channel> Unpin for Transfer<'a, C> {} impl<'a, C: Channel> Future for Transfer<'a, C> { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // self.channel.set_waker(cx.waker()); - // if self.channel.is_running() { - // Poll::Pending - // } else { - Poll::Ready(()) - // } + self.channel.set_waker(cx.waker()); + + if self.channel.is_running() { + Poll::Pending + } else { + Poll::Ready(()) + } } } +struct ChannelState { + waker: AtomicWaker, +} + +impl ChannelState { + const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } +} + +struct State { + channels: [ChannelState; 12], +} + +impl State { + const fn new() -> Self { + const CH: ChannelState = ChannelState::new(); + Self { channels: [CH; 12] } + } +} + +static STATE: State = State::new(); + pub struct NoDma; impl_peripheral!(NoDma); @@ -86,6 +113,14 @@ pub trait Channel: Peripheral

+ sealed::Channel + Into + S pac::DMA.ch(self.number() as _) } + fn is_running(&self) -> bool { + self.regs().ctrl_trig().read().en() + } + + fn set_waker(&self, waker: &Waker) { + STATE.channels[self.number() as usize].waker.register(waker); + } + fn degrade(self) -> AnyChannel { AnyChannel { number: self.number() } } From 9c9b7b1a66dc90a9183886867811d2db57df714c Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 20:34:55 +0200 Subject: [PATCH 0026/1575] Remove unneeded NoDma struct --- embassy-rp/src/dma.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index ec09a7699..531ed8020 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -96,10 +96,6 @@ impl State { static STATE: State = State::new(); -pub struct NoDma; - -impl_peripheral!(NoDma); - mod sealed { pub trait Channel {} From 1d49b3444f2bd3e049f19a72da63804192ee0402 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 21:09:50 +0200 Subject: [PATCH 0027/1575] Add DMA read + write functions --- embassy-rp/src/dma.rs | 48 +++++++++++++++++++++++++++++++++++------- embassy-rp/src/uart.rs | 4 ++-- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 531ed8020..cfaa6dd34 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -5,26 +5,45 @@ use core::task::{Context, Poll, Waker}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_util::waitqueue::AtomicWaker; use futures::Future; +use pac::dma::vals::DataSize; use crate::pac::dma::vals; use crate::{pac, peripherals}; -pub fn copy<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: &[W], to: &mut [W]) -> Transfer<'a, C> { - assert!(from.len() == to.len()); +pub(crate) fn read<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: *const W, to: *mut [W]) -> Transfer<'a, C> { + let (ptr, len) = crate::dma::slice_ptr_parts_mut(to); + copy(ch, from as *const u32, ptr as *mut u32, len, W::size()) +} +pub(crate) fn write<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: *const [W], + to: *mut W, +) -> Transfer<'a, C> { + let (from_ptr, len) = crate::dma::slice_ptr_parts(from); + copy(ch, from_ptr as *const u32, to as *mut u32, len, W::size()) +} + +fn copy<'a, C: Channel>( + ch: impl Peripheral

+ 'a, + from: *const u32, + to: *mut u32, + len: usize, + data_size: DataSize, +) -> Transfer<'a, C> { into_ref!(ch); unsafe { let p = ch.regs(); - p.read_addr().write_value(from.as_ptr() as u32); - p.write_addr().write_value(to.as_mut_ptr() as u32); - p.trans_count().write_value(from.len() as u32); + p.read_addr().write_value(from as u32); + p.write_addr().write_value(to as u32); + p.trans_count().write_value(len as u32); compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { - w.set_data_size(W::size()); + w.set_data_size(data_size); w.set_incr_read(true); w.set_incr_write(true); w.set_chain_to(ch.number()); @@ -60,7 +79,7 @@ impl<'a, C: Channel> Drop for Transfer<'a, C> { impl<'a, C: Channel> Unpin for Transfer<'a, C> {} impl<'a, C: Channel> Future for Transfer<'a, C> { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.channel.set_waker(cx.waker()); if self.channel.is_running() { @@ -110,13 +129,15 @@ pub trait Channel: Peripheral

+ sealed::Channel + Into + S } fn is_running(&self) -> bool { - self.regs().ctrl_trig().read().en() + unsafe { self.regs().ctrl_trig().read().en() } } fn set_waker(&self, waker: &Waker) { STATE.channels[self.number() as usize].waker.register(waker); } + fn on_irq() {} + fn degrade(self) -> AnyChannel { AnyChannel { number: self.number() } } @@ -177,6 +198,17 @@ macro_rules! channel { }; } +// TODO: replace transmutes with core::ptr::metadata once it's stable +#[allow(unused)] +pub(crate) fn slice_ptr_parts(slice: *const [T]) -> (usize, usize) { + unsafe { core::mem::transmute(slice) } +} + +#[allow(unused)] +pub(crate) fn slice_ptr_parts_mut(slice: *mut [T]) -> (usize, usize) { + unsafe { core::mem::transmute(slice) } +} + channel!(DMA_CH0, 0); channel!(DMA_CH1, 1); channel!(DMA_CH2, 2); diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index c1596960f..09cadf030 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -128,7 +128,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { } // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - let transfer = crate::dma::copy(ch, buffer, unsafe { T::regs().uartdr().ptr() }); + let transfer = crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _); transfer.await; } Ok(()) @@ -178,7 +178,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { } // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - let transfer = crate::dma::copy(ch, unsafe { T::regs().uartdr().ptr() }, buffer); + let transfer = crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer); transfer.await; } Ok(()) From debff0980d6a4c5527ebdaea6f91f32b63477bc5 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 21:14:57 +0200 Subject: [PATCH 0028/1575] Don't increment read address in DMA copy from peripherals --- embassy-rp/src/dma.rs | 12 +++++++++--- embassy-rp/src/uart.rs | 14 +++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index cfaa6dd34..8cf8c394d 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -10,7 +10,11 @@ use pac::dma::vals::DataSize; use crate::pac::dma::vals; use crate::{pac, peripherals}; -pub(crate) fn read<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: *const W, to: *mut [W]) -> Transfer<'a, C> { +pub(crate) fn read<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: *const W, + to: *mut [W], +) -> Transfer<'a, C> { let (ptr, len) = crate::dma::slice_ptr_parts_mut(to); copy(ch, from as *const u32, ptr as *mut u32, len, W::size()) } @@ -44,7 +48,7 @@ fn copy<'a, C: Channel>( p.ctrl_trig().write(|w| { w.set_data_size(data_size); - w.set_incr_read(true); + w.set_incr_read(false); w.set_incr_write(true); w.set_chain_to(ch.number()); w.set_en(true); @@ -136,7 +140,9 @@ pub trait Channel: Peripheral

+ sealed::Channel + Into + S STATE.channels[self.number() as usize].waker.register(waker); } - fn on_irq() {} + fn on_irq() { + // FIXME: + } fn degrade(self) -> AnyChannel { AnyChannel { number: self.number() } diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index 09cadf030..03623a9f8 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -441,15 +441,15 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for Uart<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for Uart<'d, T, M> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UartTx<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartTx<'d, T, M> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UartRx<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } } @@ -458,7 +458,7 @@ cfg_if::cfg_if! { if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] { use core::future::Future; - impl<'d, T: Instance> embedded_hal_async::serial::Write for UartTx<'d, T> + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> { type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -473,7 +473,7 @@ cfg_if::cfg_if! { } } - impl<'d, T: Instance> embedded_hal_async::serial::Read for UartRx<'d, T> + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> { type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -482,7 +482,7 @@ cfg_if::cfg_if! { } } - impl<'d, T: Instance> embedded_hal_async::serial::Write for Uart<'d, T> + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> { type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -497,7 +497,7 @@ cfg_if::cfg_if! { } } - impl<'d, T: Instance> embedded_hal_async::serial::Read for Uart<'d, T> + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> { type ReadFuture<'a> = impl Future> + 'a where Self: 'a; From aa586fe1dee309808fca34500812bf33f2a5c558 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Aug 2022 21:27:37 +0200 Subject: [PATCH 0029/1575] Simplify waker storage for DMA state --- embassy-rp/src/dma.rs | 45 ++++++------------------------------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 8cf8c394d..a56d77c12 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,6 +1,6 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::{Context, Poll, Waker}; +use core::task::{Context, Poll}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_util::waitqueue::AtomicWaker; @@ -84,9 +84,9 @@ impl<'a, C: Channel> Unpin for Transfer<'a, C> {} impl<'a, C: Channel> Future for Transfer<'a, C> { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.channel.set_waker(cx.waker()); + CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); - if self.channel.is_running() { + if unsafe { self.channel.regs().ctrl_trig().read().en() } { Poll::Pending } else { Poll::Ready(()) @@ -94,30 +94,9 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { } } -struct ChannelState { - waker: AtomicWaker, -} - -impl ChannelState { - const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } -} - -struct State { - channels: [ChannelState; 12], -} - -impl State { - const fn new() -> Self { - const CH: ChannelState = ChannelState::new(); - Self { channels: [CH; 12] } - } -} - -static STATE: State = State::new(); +const CHANNEL_COUNT: usize = 12; +const NEW_AW: AtomicWaker = AtomicWaker::new(); +static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; mod sealed { pub trait Channel {} @@ -132,18 +111,6 @@ pub trait Channel: Peripheral

+ sealed::Channel + Into + S pac::DMA.ch(self.number() as _) } - fn is_running(&self) -> bool { - unsafe { self.regs().ctrl_trig().read().en() } - } - - fn set_waker(&self, waker: &Waker) { - STATE.channels[self.number() as usize].waker.register(waker); - } - - fn on_irq() { - // FIXME: - } - fn degrade(self) -> AnyChannel { AnyChannel { number: self.number() } } From a29972413bcfb30e6e28766491229c6ac38eed22 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 08:48:52 +0200 Subject: [PATCH 0030/1575] Fix uart rp2040 blocking example --- examples/rp/src/bin/uart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/uart.rs b/examples/rp/src/bin/uart.rs index c63b31cae..05177a6b4 100644 --- a/examples/rp/src/bin/uart.rs +++ b/examples/rp/src/bin/uart.rs @@ -10,7 +10,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let config = uart::Config::default(); - let mut uart = uart::Uart::new_with_rtscts(p.UART0, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, config); + let mut uart = uart::Uart::new_with_rtscts_blocking(p.UART0, p.PIN_0, p.PIN_1, p.PIN_3, p.PIN_2, config); uart.blocking_write("Hello World!\r\n".as_bytes()).unwrap(); loop { From 140ef4febf2a74a14be0b4421f7114c68ecba571 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 09:48:58 +0200 Subject: [PATCH 0031/1575] Add DMA_IRQ0 handling to Transfer --- embassy-rp/src/dma.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index a56d77c12..23cd8532f 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -2,13 +2,28 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_util::waitqueue::AtomicWaker; use futures::Future; use pac::dma::vals::DataSize; use crate::pac::dma::vals; -use crate::{pac, peripherals}; +use crate::{interrupt, pac, peripherals}; + +#[interrupt] +unsafe fn DMA_IRQ_0() { + let ints0 = pac::DMA.ints0().read().ints0(); + + critical_section::with(|_| { + for channel in 0..CHANNEL_COUNT { + if ints0 & (1 << channel) == 1 { + CHANNEL_WAKERS[channel].wake(); + } + } + pac::DMA.ints0().write(|w| w.set_ints0(ints0)); + }); +} pub(crate) fn read<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, @@ -66,6 +81,17 @@ pub(crate) struct Transfer<'a, C: Channel> { impl<'a, C: Channel> Transfer<'a, C> { pub(crate) fn new(channel: impl Peripheral

+ 'a) -> Self { into_ref!(channel); + + unsafe { + let irq = interrupt::DMA_IRQ_0::steal(); + irq.disable(); + irq.set_priority(interrupt::Priority::P6); + + pac::DMA.inte0().write(|w| w.set_inte0(1 << channel.number())); + + irq.enable(); + } + Self { channel } } } @@ -84,6 +110,8 @@ impl<'a, C: Channel> Unpin for Transfer<'a, C> {} impl<'a, C: Channel> Future for Transfer<'a, C> { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // We need to register/re-register the waker for each poll because any + // calls to wake will deregister the waker. CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); if unsafe { self.channel.regs().ctrl_trig().read().en() } { From 331a64a4eac4accd91f6f9de8f77837181d8cab0 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 10:11:03 +0200 Subject: [PATCH 0032/1575] Add back public dma::copy, and correct dma incr settings for read/write --- embassy-rp/src/dma.rs | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 23cd8532f..820ce0051 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -25,30 +25,39 @@ unsafe fn DMA_IRQ_0() { }); } -pub(crate) fn read<'a, C: Channel, W: Word>( - ch: impl Peripheral

+ 'a, - from: *const W, - to: *mut [W], -) -> Transfer<'a, C> { +pub fn read<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: *const W, to: &mut [W]) -> Transfer<'a, C> { let (ptr, len) = crate::dma::slice_ptr_parts_mut(to); - copy(ch, from as *const u32, ptr as *mut u32, len, W::size()) + copy_inner(ch, from as *const u32, ptr as *mut u32, len, W::size(), false, true) } -pub(crate) fn write<'a, C: Channel, W: Word>( - ch: impl Peripheral

+ 'a, - from: *const [W], - to: *mut W, -) -> Transfer<'a, C> { +pub fn write<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: &[W], to: *mut W) -> Transfer<'a, C> { let (from_ptr, len) = crate::dma::slice_ptr_parts(from); - copy(ch, from_ptr as *const u32, to as *mut u32, len, W::size()) + copy_inner(ch, from_ptr as *const u32, to as *mut u32, len, W::size(), true, false) } -fn copy<'a, C: Channel>( +pub fn copy<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: &[W], to: &mut [W]) -> Transfer<'a, C> { + let (from_ptr, from_len) = crate::dma::slice_ptr_parts(from); + let (to_ptr, to_len) = crate::dma::slice_ptr_parts_mut(to); + assert_eq!(from_len, to_len); + copy_inner( + ch, + from_ptr as *const u32, + to_ptr as *mut u32, + from_len, + W::size(), + true, + true, + ) +} + +fn copy_inner<'a, C: Channel>( ch: impl Peripheral

+ 'a, from: *const u32, to: *mut u32, len: usize, data_size: DataSize, + incr_read: bool, + incr_write: bool, ) -> Transfer<'a, C> { into_ref!(ch); @@ -63,8 +72,8 @@ fn copy<'a, C: Channel>( p.ctrl_trig().write(|w| { w.set_data_size(data_size); - w.set_incr_read(false); - w.set_incr_write(true); + w.set_incr_read(incr_read); + w.set_incr_write(incr_write); w.set_chain_to(ch.number()); w.set_en(true); }); @@ -74,7 +83,7 @@ fn copy<'a, C: Channel>( Transfer::new(ch) } -pub(crate) struct Transfer<'a, C: Channel> { +pub struct Transfer<'a, C: Channel> { channel: PeripheralRef<'a, C>, } @@ -114,7 +123,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { // calls to wake will deregister the waker. CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); - if unsafe { self.channel.regs().ctrl_trig().read().en() } { + if unsafe { self.channel.regs().ctrl_trig().read().busy() } { Poll::Pending } else { Poll::Ready(()) From 295af2a057beec74bb765f6e3dad28b44e30cb19 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 14:16:19 +0200 Subject: [PATCH 0033/1575] Fix bit checking in DMA irq --- embassy-rp/src/dma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 820ce0051..75ad640ca 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -17,7 +17,7 @@ unsafe fn DMA_IRQ_0() { critical_section::with(|_| { for channel in 0..CHANNEL_COUNT { - if ints0 & (1 << channel) == 1 { + if ints0 & (1 << channel) == (1 << channel) { CHANNEL_WAKERS[channel].wake(); } } From 6b4555a6a70f8c10755198b69e7cf085c1a53fce Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 20 Aug 2022 10:49:27 +0200 Subject: [PATCH 0034/1575] Add comments about Country Locale Matrix (CLM) This commit add comments about what CLM stands for. The motivation of this is that I think it helps understanding the code for users who are new to the codebase (like me). --- examples/rpi-pico-w/src/main.rs | 2 +- src/structs.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 91f087262..569c9bf4c 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -42,7 +42,7 @@ async fn net_task(stack: &'static Stack>) -> ! { async fn main(spawner: Spawner, p: Peripherals) { info!("Hello World!"); - // Include the WiFi firmware and CLM. + // Include the WiFi firmware and Country Locale Matrix (CLM) blobs. let fw = include_bytes!("../../../firmware/43439A0.bin"); let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); diff --git a/src/structs.rs b/src/structs.rs index 7a7c25b26..355470971 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -172,6 +172,7 @@ pub const DOWNLOAD_FLAG_BEGIN: u16 = 0x0002; pub const DOWNLOAD_FLAG_END: u16 = 0x0004; pub const DOWNLOAD_FLAG_HANDLER_VER: u16 = 0x1000; +// Country Locale Matrix (CLM) pub const DOWNLOAD_TYPE_CLM: u16 = 2; #[derive(Clone, Copy)] From 945449b10fe815dd10875f55482d4777d6d801b7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Aug 2022 17:24:43 +0200 Subject: [PATCH 0035/1575] Update Embassy. --- Cargo.toml | 2 +- examples/rpi-pico-w/Cargo.toml | 15 +++++++++------ examples/rpi-pico-w/src/main.rs | 21 +++++++++++---------- src/lib.rs | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cea7d7801..c6120dac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ defmt = ["dep:defmt"] log = ["dep:log"] [dependencies] -embassy-executor = { version = "0.1.0", features = [ "time" ] } +embassy-time = { version = "0.1.0" } embassy-util = { version = "0.1.0" } embassy-net = { version = "0.1.0" } atomic-polyfill = "0.1.5" diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index af558d8cd..98a3d105d 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -6,17 +6,19 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt"]} -embassy-executor = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } +embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-util = { version = "0.1.0" } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } atomic-polyfill = "0.1.5" +static_cell = "1.0" defmt = "0.3" defmt-rtt = "0.3" panic-probe = { version = "0.3", features = ["print-defmt"] } -cortex-m = "0.7.3" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]} cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } @@ -27,10 +29,11 @@ heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } -embassy-util = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "6ffca81a38d2c7f57da667ff49b4296c4eba78e2" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } +embassy-util = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } #embassy-executor = { path = "/home/dirbaio/embassy/embassy/embassy-executor" } #embassy-util = { path = "/home/dirbaio/embassy/embassy/embassy-util" } #embassy-rp = { path = "/home/dirbaio/embassy/embassy/embassy-rp" } diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 569c9bf4c..986474ce3 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -6,23 +6,22 @@ use core::convert::Infallible; use core::future::Future; use defmt::*; -use embassy_executor::executor::Spawner; +use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; -use embassy_rp::Peripherals; -use embassy_util::Forever; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite}; use embedded_io::asynch::{Read, Write}; +use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -macro_rules! forever { +macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; - static FOREVER: Forever = Forever::new(); - FOREVER.put_with(move || $val) + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) }}; } @@ -39,9 +38,11 @@ async fn net_task(stack: &'static Stack>) -> ! { } #[embassy_executor::main] -async fn main(spawner: Spawner, p: Peripherals) { +async fn main(spawner: Spawner) { info!("Hello World!"); + let p = embassy_rp::init(Default::default()); + // Include the WiFi firmware and Country Locale Matrix (CLM) blobs. let fw = include_bytes!("../../../firmware/43439A0.bin"); let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); @@ -63,7 +64,7 @@ async fn main(spawner: Spawner, p: Peripherals) { let bus = MySpi { clk, dio }; let spi = ExclusiveDevice::new(bus, cs); - let state = forever!(cyw43::State::new()); + let state = singleton!(cyw43::State::new()); let (mut control, runner) = cyw43::new(state, pwr, spi, fw).await; spawner.spawn(wifi_task(runner)).unwrap(); @@ -84,10 +85,10 @@ async fn main(spawner: Spawner, p: Peripherals) { let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. // Init network stack - let stack = &*forever!(Stack::new( + let stack = &*singleton!(Stack::new( net_device, config, - forever!(StackResources::<1, 2, 8>::new()), + singleton!(StackResources::<1, 2, 8>::new()), seed )); diff --git a/src/lib.rs b/src/lib.rs index 3932ce41b..af2821e52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,8 +17,8 @@ use core::sync::atomic::Ordering; use core::task::Waker; use atomic_polyfill::AtomicBool; -use embassy_executor::time::{block_for, Duration, Timer}; use embassy_net::{PacketBoxExt, PacketBuf}; +use embassy_time::{block_for, Duration, Timer}; use embassy_util::blocking_mutex::raw::NoopRawMutex; use embassy_util::channel::mpmc::Channel; use embassy_util::yield_now; From 9218aff498aa4f9fca5d0aa3134fda6462801a2e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Aug 2022 01:06:14 +0200 Subject: [PATCH 0036/1575] Update Embassy. --- Cargo.toml | 3 ++- examples/rpi-pico-w/Cargo.toml | 20 ++++++-------------- src/lib.rs | 6 +++--- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c6120dac3..cb6aa0b29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,8 @@ log = ["dep:log"] [dependencies] embassy-time = { version = "0.1.0" } -embassy-util = { version = "0.1.0" } +embassy-sync = { version = "0.1.0" } +embassy-futures = { version = "0.1.0" } embassy-net = { version = "0.1.0" } atomic-polyfill = "0.1.5" diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 98a3d105d..53e72498b 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" cyw43 = { path = "../../", features = ["defmt"]} embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-util = { version = "0.1.0" } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } atomic-polyfill = "0.1.5" @@ -29,19 +28,12 @@ heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } -embassy-util = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "53fbd0efb3e77e1e3de948afde2b5bf1a5a9735f" } -#embassy-executor = { path = "/home/dirbaio/embassy/embassy/embassy-executor" } -#embassy-util = { path = "/home/dirbaio/embassy/embassy/embassy-util" } -#embassy-rp = { path = "/home/dirbaio/embassy/embassy/embassy-rp" } -#embassy-net = { path = "/home/dirbaio/embassy/embassy/embassy-net" } -#smoltcp = { path = "./smoltcp" } - -#[patch."https://github.com/smoltcp-rs/smoltcp"] -#smoltcp = { path = "./smoltcp" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } [profile.dev] debug = 2 diff --git a/src/lib.rs b/src/lib.rs index af2821e52..1c49771b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,11 +17,11 @@ use core::sync::atomic::Ordering; use core::task::Waker; use atomic_polyfill::AtomicBool; +use embassy_futures::yield_now; use embassy_net::{PacketBoxExt, PacketBuf}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::channel::Channel; use embassy_time::{block_for, Duration, Timer}; -use embassy_util::blocking_mutex::raw::NoopRawMutex; -use embassy_util::channel::mpmc::Channel; -use embassy_util::yield_now; use embedded_hal_1::digital::blocking::OutputPin; use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; From f6c2e26372961747a0bc148d1675b1bc5dfb7e75 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 23 Aug 2022 12:28:17 +0200 Subject: [PATCH 0037/1575] Address code review comments --- embassy-rp/src/dma.rs | 50 +++++++++++++++++++++++++----------------- embassy-rp/src/lib.rs | 1 + embassy-rp/src/uart.rs | 34 ++++++++++++++-------------- 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 75ad640ca..25e3ec8fc 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -15,27 +15,47 @@ use crate::{interrupt, pac, peripherals}; unsafe fn DMA_IRQ_0() { let ints0 = pac::DMA.ints0().read().ints0(); - critical_section::with(|_| { - for channel in 0..CHANNEL_COUNT { - if ints0 & (1 << channel) == (1 << channel) { - CHANNEL_WAKERS[channel].wake(); - } + for channel in 0..CHANNEL_COUNT { + if ints0 & (1 << channel) == (1 << channel) { + CHANNEL_WAKERS[channel].wake(); } - pac::DMA.ints0().write(|w| w.set_ints0(ints0)); - }); + } + pac::DMA.ints0().write(|w| w.set_ints0(ints0)); } -pub fn read<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: *const W, to: &mut [W]) -> Transfer<'a, C> { +pub(crate) unsafe fn init() { + let irq = interrupt::DMA_IRQ_0::steal(); + irq.disable(); + irq.set_priority(interrupt::Priority::P6); + + pac::DMA.inte0().write(|w| w.set_inte0(0xFFFF)); + + irq.enable(); +} + +pub unsafe fn read<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: *const W, + to: &mut [W], +) -> Transfer<'a, C> { let (ptr, len) = crate::dma::slice_ptr_parts_mut(to); copy_inner(ch, from as *const u32, ptr as *mut u32, len, W::size(), false, true) } -pub fn write<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: &[W], to: *mut W) -> Transfer<'a, C> { +pub unsafe fn write<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: &[W], + to: *mut W, +) -> Transfer<'a, C> { let (from_ptr, len) = crate::dma::slice_ptr_parts(from); copy_inner(ch, from_ptr as *const u32, to as *mut u32, len, W::size(), true, false) } -pub fn copy<'a, C: Channel, W: Word>(ch: impl Peripheral

+ 'a, from: &[W], to: &mut [W]) -> Transfer<'a, C> { +pub unsafe fn copy<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: &[W], + to: &mut [W], +) -> Transfer<'a, C> { let (from_ptr, from_len) = crate::dma::slice_ptr_parts(from); let (to_ptr, to_len) = crate::dma::slice_ptr_parts_mut(to); assert_eq!(from_len, to_len); @@ -91,16 +111,6 @@ impl<'a, C: Channel> Transfer<'a, C> { pub(crate) fn new(channel: impl Peripheral

+ 'a) -> Self { into_ref!(channel); - unsafe { - let irq = interrupt::DMA_IRQ_0::steal(); - irq.disable(); - irq.set_priority(interrupt::Priority::P6); - - pac::DMA.inte0().write(|w| w.set_inte0(1 << channel.number())); - - irq.enable(); - } - Self { channel } } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 8c053a4f7..6db77b8cd 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -105,6 +105,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); timer::init(); + dma::init(); } peripherals diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index 03623a9f8..9d94dcf21 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -120,17 +120,16 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { impl<'d, T: Instance> UartTx<'d, T, Async> { pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - if let Some(ch) = &mut self.tx_dma { - unsafe { - T::regs().uartdmacr().modify(|reg| { - reg.set_txdmae(true); - }); - } + let ch = self.tx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().uartdmacr().modify(|reg| { + reg.set_txdmae(true); + }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - let transfer = crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _); - transfer.await; - } + crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _) + }; + transfer.await; Ok(()) } } @@ -170,17 +169,16 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { impl<'d, T: Instance> UartRx<'d, T, Async> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - if let Some(ch) = &mut self.rx_dma { - unsafe { - T::regs().uartdmacr().modify(|reg| { - reg.set_rxdmae(true); - }); - } + let ch = self.rx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().uartdmacr().modify(|reg| { + reg.set_rxdmae(true); + }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - let transfer = crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer); - transfer.await; - } + crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer) + }; + transfer.await; Ok(()) } } From 7e3ce2c90befe19ea30c6a784710f6cebf5a48f2 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 23 Aug 2022 13:20:36 +0200 Subject: [PATCH 0038/1575] Abort DMA operation when dropping a Transfer, and panic on DMA errors --- embassy-rp/src/dma.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 25e3ec8fc..6423d1938 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -14,8 +14,18 @@ use crate::{interrupt, pac, peripherals}; #[interrupt] unsafe fn DMA_IRQ_0() { let ints0 = pac::DMA.ints0().read().ints0(); - for channel in 0..CHANNEL_COUNT { + let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read(); + if ctrl_trig.ahb_error() { + panic!("DMA: ahb error on DMA_0 channel {}", channel); + } + if ctrl_trig.read_error() { + panic!("DMA: read error on DMA_0 channel {}", channel); + } + if ctrl_trig.write_error() { + panic!("DMA: write error on DMA_0 channel {}", channel); + } + if ints0 & (1 << channel) == (1 << channel) { CHANNEL_WAKERS[channel].wake(); } @@ -119,7 +129,9 @@ impl<'a, C: Channel> Drop for Transfer<'a, C> { fn drop(&mut self) { let p = self.channel.regs(); unsafe { - p.ctrl_trig().write(|w| w.set_en(false)); + pac::DMA + .chan_abort() + .modify(|m| m.set_chan_abort(1 << self.channel.number())); while p.ctrl_trig().read().busy() {} } } From 594a64b3bfd5757f98ea4118fcaf62f59fc2157b Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 23 Aug 2022 13:28:14 +0200 Subject: [PATCH 0039/1575] Change to using embassy-sync --- embassy-rp/src/dma.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 6423d1938..137e9a0c3 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -4,7 +4,7 @@ use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; -use embassy_util::waitqueue::AtomicWaker; +use embassy_sync::waitqueue::AtomicWaker; use futures::Future; use pac::dma::vals::DataSize; @@ -36,7 +36,7 @@ unsafe fn DMA_IRQ_0() { pub(crate) unsafe fn init() { let irq = interrupt::DMA_IRQ_0::steal(); irq.disable(); - irq.set_priority(interrupt::Priority::P6); + irq.set_priority(interrupt::Priority::P3); pac::DMA.inte0().write(|w| w.set_inte0(0xFFFF)); From b88ef032141e87411dabb5d90ef488f5d6af2285 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 23 Aug 2022 13:46:48 +0200 Subject: [PATCH 0040/1575] Only check for ahb error in DMA --- embassy-rp/src/dma.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 137e9a0c3..694823742 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -17,13 +17,7 @@ unsafe fn DMA_IRQ_0() { for channel in 0..CHANNEL_COUNT { let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read(); if ctrl_trig.ahb_error() { - panic!("DMA: ahb error on DMA_0 channel {}", channel); - } - if ctrl_trig.read_error() { - panic!("DMA: read error on DMA_0 channel {}", channel); - } - if ctrl_trig.write_error() { - panic!("DMA: write error on DMA_0 channel {}", channel); + panic!("DMA: error on DMA_0 channel {}", channel); } if ints0 & (1 << channel) == (1 << channel) { From bb76a29ff160580a934343a1ab10313e7b408da0 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Wed, 24 Aug 2022 15:58:44 +0200 Subject: [PATCH 0041/1575] Add comment for AI constants This commit adds a comment about the AI_* constants. The motivation for using this definition is from looking in the following file: https://github.com/seemoo-lab/bcm-public/blob/master/firmware_patching/examples/ioctl/bcmdhd/include/aidmp.h#L2 https://github.com/seemoo-lab/bcm-public/blob/master/firmware_patching/examples/ioctl/bcmdhd/include/aidmp.h#L307-L361 --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1c49771b9..b8c003297 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,8 @@ const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; +// Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect (AI) +// constants const AI_IOCTRL_OFFSET: u32 = 0x408; const AI_IOCTRL_BIT_FGC: u8 = 0x0002; const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; From f11aa9720be6b5f608becb3472e4fd13de324080 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Aug 2022 23:43:28 +0200 Subject: [PATCH 0042/1575] rp: update PAC --- embassy-rp/Cargo.toml | 4 ++-- embassy-rp/src/gpio.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index cfd95b7b4..871e7dd0a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -43,8 +43,8 @@ cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="9ad7223a48a065e612bc7dc7be5bf5bd0b41cfc4", features = ["rt"] } -#rp2040-pac2 = { path = "../../rp/rp2040-pac2", features = ["rt"] } +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.8", optional = true} diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 428855c7f..a0328302a 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -433,7 +433,7 @@ impl<'d, T: Pin> Flex<'d, T> { }); pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); + w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0); }); } @@ -586,7 +586,7 @@ impl<'d, T: Pin> Drop for Flex<'d, T> { unsafe { self.pin.pad_ctrl().write(|_| {}); self.pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0); + w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0); }); } } From a730e2cd0f4f282a78cc5d9897c584ec4f5a44a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 24 Aug 2022 23:46:07 +0200 Subject: [PATCH 0043/1575] rp: add usb device support. --- embassy-rp/Cargo.toml | 4 +- embassy-rp/src/lib.rs | 37 ++ embassy-rp/src/usb.rs | 846 ++++++++++++++++++++++++++++ examples/rp/Cargo.toml | 6 + examples/rp/src/bin/usb_ethernet.rs | 264 +++++++++ examples/rp/src/bin/usb_serial.rs | 103 ++++ 6 files changed, 1259 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/usb.rs create mode 100644 examples/rp/src/bin/usb_ethernet.rs create mode 100644 examples/rp/src/bin/usb_serial.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 871e7dd0a..f76e439ab 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -12,6 +12,7 @@ flavors = [ ] [features] +defmt = ["dep:defmt", "embassy-usb?/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. @@ -20,7 +21,7 @@ flavors = [ unstable-pac = [] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -33,6 +34,7 @@ embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 8c053a4f7..bc31b4798 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -10,6 +10,8 @@ pub mod interrupt; pub mod spi; pub mod timer; pub mod uart; +#[cfg(feature = "nightly")] +pub mod usb; mod clocks; mod reset; @@ -80,6 +82,8 @@ embassy_hal_common::peripherals! { DMA_CH9, DMA_CH10, DMA_CH11, + + USB, } #[link_section = ".boot2"] @@ -109,3 +113,36 @@ pub fn init(_config: config::Config) -> Peripherals { peripherals } + +/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes. +trait RegExt { + unsafe fn write_xor(&self, f: impl FnOnce(&mut T) -> R) -> R; + unsafe fn write_set(&self, f: impl FnOnce(&mut T) -> R) -> R; + unsafe fn write_clear(&self, f: impl FnOnce(&mut T) -> R) -> R; +} + +impl RegExt for pac::common::Reg { + unsafe fn write_xor(&self, f: impl FnOnce(&mut T) -> R) -> R { + let mut val = Default::default(); + let res = f(&mut val); + let ptr = (self.ptr() as *mut u8).add(0x1000) as *mut T; + ptr.write_volatile(val); + res + } + + unsafe fn write_set(&self, f: impl FnOnce(&mut T) -> R) -> R { + let mut val = Default::default(); + let res = f(&mut val); + let ptr = (self.ptr() as *mut u8).add(0x2000) as *mut T; + ptr.write_volatile(val); + res + } + + unsafe fn write_clear(&self, f: impl FnOnce(&mut T) -> R) -> R { + let mut val = Default::default(); + let res = f(&mut val); + let ptr = (self.ptr() as *mut u8).add(0x3000) as *mut T; + ptr.write_volatile(val); + res + } +} diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs new file mode 100644 index 000000000..82eafdefd --- /dev/null +++ b/embassy-rp/src/usb.rs @@ -0,0 +1,846 @@ +use core::marker::PhantomData; +use core::slice; +use core::sync::atomic::Ordering; +use core::task::Poll; + +use atomic_polyfill::compiler_fence; +use embassy_hal_common::into_ref; +use embassy_sync::waitqueue::AtomicWaker; +use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; +use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use futures::future::poll_fn; +use futures::Future; + +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::{pac, peripherals, Peripheral, RegExt}; + +pub(crate) mod sealed { + pub trait Instance { + fn regs() -> crate::pac::usb::Usb; + fn dpram() -> crate::pac::usb_dpram::UsbDpram; + } +} + +pub trait Instance: sealed::Instance + 'static { + type Interrupt: Interrupt; +} + +impl crate::usb::sealed::Instance for peripherals::USB { + fn regs() -> pac::usb::Usb { + pac::USBCTRL_REGS + } + fn dpram() -> crate::pac::usb_dpram::UsbDpram { + pac::USBCTRL_DPRAM + } +} + +impl crate::usb::Instance for peripherals::USB { + type Interrupt = crate::interrupt::USBCTRL_IRQ; +} + +const EP_COUNT: usize = 16; +const EP_MEMORY_SIZE: usize = 4096; +const EP_MEMORY: *mut u8 = pac::USBCTRL_DPRAM.0; + +const NEW_AW: AtomicWaker = AtomicWaker::new(); +static BUS_WAKER: AtomicWaker = NEW_AW; +static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; +static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; + +struct EndpointBuffer { + addr: u16, + len: u16, + _phantom: PhantomData, +} + +impl EndpointBuffer { + const fn new(addr: u16, len: u16) -> Self { + Self { + addr, + len, + _phantom: PhantomData, + } + } + + fn read(&mut self, buf: &mut [u8]) { + assert!(buf.len() <= self.len as usize); + compiler_fence(Ordering::SeqCst); + let mem = unsafe { slice::from_raw_parts(EP_MEMORY.add(self.addr as _), buf.len()) }; + buf.copy_from_slice(mem); + compiler_fence(Ordering::SeqCst); + } + + fn write(&mut self, buf: &[u8]) { + assert!(buf.len() <= self.len as usize); + compiler_fence(Ordering::SeqCst); + let mem = unsafe { slice::from_raw_parts_mut(EP_MEMORY.add(self.addr as _), buf.len()) }; + mem.copy_from_slice(buf); + compiler_fence(Ordering::SeqCst); + } +} + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +struct EndpointData { + ep_type: EndpointType, // only valid if used + max_packet_size: u16, + used: bool, +} + +impl EndpointData { + const fn new() -> Self { + Self { + ep_type: EndpointType::Bulk, + max_packet_size: 0, + used: false, + } + } +} + +pub struct Driver<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + ep_in: [EndpointData; EP_COUNT], + ep_out: [EndpointData; EP_COUNT], + ep_mem_free: u16, // first free address in EP mem, in bytes. +} + +impl<'d, T: Instance> Driver<'d, T> { + pub fn new(_usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { + into_ref!(irq); + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + let regs = T::regs(); + unsafe { + // zero fill regs + let p = regs.0 as *mut u32; + for i in 0..0x9c / 4 { + p.add(i).write_volatile(0) + } + + // zero fill epmem + let p = EP_MEMORY as *mut u32; + for i in 0..0x100 / 4 { + p.add(i).write_volatile(0) + } + + regs.usb_muxing().write(|w| { + w.set_to_phy(true); + w.set_softcon(true); + }); + regs.usb_pwr().write(|w| { + w.set_vbus_detect(true); + w.set_vbus_detect_override_en(true); + }); + regs.main_ctrl().write(|w| { + w.set_controller_en(true); + }); + } + + // Initialize the bus so that it signals that power is available + BUS_WAKER.wake(); + + Self { + phantom: PhantomData, + ep_in: [EndpointData::new(); EP_COUNT], + ep_out: [EndpointData::new(); EP_COUNT], + ep_mem_free: 0x180, // data buffer region + } + } + + fn on_interrupt(_: *mut ()) { + unsafe { + let regs = T::regs(); + //let x = regs.istr().read().0; + //trace!("USB IRQ: {:08x}", x); + + let ints = regs.ints().read(); + + if ints.bus_reset() { + regs.inte().write_clear(|w| w.set_bus_reset(true)); + BUS_WAKER.wake(); + } + if ints.dev_resume_from_host() { + regs.inte().write_clear(|w| w.set_dev_resume_from_host(true)); + BUS_WAKER.wake(); + } + if ints.dev_suspend() { + regs.inte().write_clear(|w| w.set_dev_suspend(true)); + BUS_WAKER.wake(); + } + if ints.setup_req() { + regs.inte().write_clear(|w| w.set_setup_req(true)); + EP_OUT_WAKERS[0].wake(); + } + + if ints.buff_status() { + let s = regs.buff_status().read(); + regs.buff_status().write_value(s); + + for i in 0..EP_COUNT { + if s.ep_in(i) { + EP_IN_WAKERS[i].wake(); + } + if s.ep_out(i) { + EP_OUT_WAKERS[i].wake(); + } + } + } + } + } + + fn alloc_endpoint( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result, driver::EndpointAllocError> { + trace!( + "allocating type={:?} mps={:?} interval={}, dir={:?}", + ep_type, + max_packet_size, + interval, + D::dir() + ); + + let alloc = match D::dir() { + UsbDirection::Out => &mut self.ep_out, + UsbDirection::In => &mut self.ep_in, + }; + + let index = alloc.iter_mut().enumerate().find(|(i, ep)| { + if *i == 0 { + return false; // reserved for control pipe + } + !ep.used + }); + + let (index, ep) = index.ok_or(EndpointAllocError)?; + assert!(!ep.used); + + if max_packet_size > 64 { + warn!("max_packet_size too high: {}", max_packet_size); + return Err(EndpointAllocError); + } + + // ep mem addrs must be 64-byte aligned, so there's no point in trying + // to allocate smaller chunks to save memory. + let len = 64; + + let addr = self.ep_mem_free; + if addr + len > EP_MEMORY_SIZE as _ { + warn!("Endpoint memory full"); + return Err(EndpointAllocError); + } + self.ep_mem_free += len; + + let buf = EndpointBuffer { + addr, + len, + _phantom: PhantomData, + }; + + trace!(" index={} addr={} len={}", index, buf.addr, buf.len); + + ep.ep_type = ep_type; + ep.used = true; + ep.max_packet_size = max_packet_size; + + let ep_type_reg = match ep_type { + EndpointType::Bulk => pac::usb_dpram::vals::EpControlEndpointType::BULK, + EndpointType::Control => pac::usb_dpram::vals::EpControlEndpointType::CONTROL, + EndpointType::Interrupt => pac::usb_dpram::vals::EpControlEndpointType::INTERRUPT, + EndpointType::Isochronous => pac::usb_dpram::vals::EpControlEndpointType::ISOCHRONOUS, + }; + + match D::dir() { + UsbDirection::Out => unsafe { + T::dpram().ep_out_control(index - 1).write(|w| { + w.set_enable(false); + w.set_buffer_address(addr); + w.set_interrupt_per_buff(true); + w.set_endpoint_type(ep_type_reg); + }) + }, + UsbDirection::In => unsafe { + T::dpram().ep_in_control(index - 1).write(|w| { + w.set_enable(false); + w.set_buffer_address(addr); + w.set_interrupt_per_buff(true); + w.set_endpoint_type(ep_type_reg); + }) + }, + } + + Ok(Endpoint { + _phantom: PhantomData, + info: EndpointInfo { + addr: EndpointAddress::from_parts(index, D::dir()), + ep_type, + max_packet_size, + interval, + }, + buf, + }) + } +} + +impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { + type EndpointOut = Endpoint<'d, T, Out>; + type EndpointIn = Endpoint<'d, T, In>; + type ControlPipe = ControlPipe<'d, T>; + type Bus = Bus<'d, T>; + + fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval) + } + + fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval) + } + + fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + let regs = T::regs(); + unsafe { + regs.inte().write(|w| { + w.set_bus_reset(true); + w.set_buff_status(true); + w.set_dev_resume_from_host(true); + w.set_dev_suspend(true); + w.set_setup_req(true); + }); + regs.int_ep_ctrl().write(|w| { + w.set_int_ep_active(0xFFFE); // all EPs + }); + regs.sie_ctrl().write(|w| { + w.set_ep0_int_1buf(true); + w.set_pullup_en(true); + }) + } + trace!("enabled"); + + ( + Bus { + phantom: PhantomData, + inited: false, + ep_out: self.ep_out, + }, + ControlPipe { + _phantom: PhantomData, + max_packet_size: control_max_packet_size, + }, + ) + } +} + +pub struct Bus<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + ep_out: [EndpointData; EP_COUNT], + inited: bool, +} + +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> { + poll_fn(move |cx| unsafe { + BUS_WAKER.register(cx.waker()); + + if !self.inited { + self.inited = true; + return Poll::Ready(Event::PowerDetected); + } + + let regs = T::regs(); + let siestatus = regs.sie_status().read(); + + if siestatus.resume() { + regs.sie_status().write(|w| w.set_resume(true)); + return Poll::Ready(Event::Resume); + } + + if siestatus.bus_reset() { + regs.sie_status().write(|w| { + w.set_bus_reset(true); + w.set_setup_rec(true); + }); + regs.buff_status().write(|w| w.0 = 0xFFFF_FFFF); + regs.addr_endp().write(|w| w.set_address(0)); + + for i in 1..EP_COUNT { + T::dpram().ep_in_control(i - 1).modify(|w| w.set_enable(false)); + T::dpram().ep_out_control(i - 1).modify(|w| w.set_enable(false)); + } + + for w in &EP_IN_WAKERS { + w.wake() + } + for w in &EP_OUT_WAKERS { + w.wake() + } + return Poll::Ready(Event::Reset); + } + + if siestatus.suspended() { + regs.sie_status().write(|w| w.set_suspended(true)); + return Poll::Ready(Event::Suspend); + } + + // no pending event. Reenable all irqs. + regs.inte().write_set(|w| { + w.set_bus_reset(true); + w.set_dev_resume_from_host(true); + w.set_dev_suspend(true); + }); + Poll::Pending + }) + } + + #[inline] + fn set_address(&mut self, addr: u8) { + let regs = T::regs(); + trace!("setting addr: {}", addr); + unsafe { regs.addr_endp().write(|w| w.set_address(addr)) } + } + + fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) { + todo!(); + } + + fn endpoint_is_stalled(&mut self, _ep_addr: EndpointAddress) -> bool { + todo!(); + } + + fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { + trace!("set_enabled {:?} {}", ep_addr, enabled); + if ep_addr.index() == 0 { + return; + } + + let n = ep_addr.index(); + match ep_addr.direction() { + UsbDirection::In => unsafe { + T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled)); + T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| { + w.set_pid(0, true); // first packet is DATA0, but PID is flipped before + }); + EP_IN_WAKERS[n].wake(); + }, + UsbDirection::Out => unsafe { + T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled)); + + T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| { + w.set_pid(0, false); + w.set_length(0, self.ep_out[n].max_packet_size); + }); + cortex_m::asm::delay(12); + T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| { + w.set_pid(0, false); + w.set_length(0, self.ep_out[n].max_packet_size); + w.set_available(0, true); + }); + EP_OUT_WAKERS[n].wake(); + }, + } + } + + type EnableFuture<'a> = impl Future + 'a where Self: 'a; + + 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) } + } +} + +trait Dir { + fn dir() -> UsbDirection; + fn waker(i: usize) -> &'static AtomicWaker; +} + +pub enum In {} +impl Dir for In { + fn dir() -> UsbDirection { + UsbDirection::In + } + + #[inline] + fn waker(i: usize) -> &'static AtomicWaker { + &EP_IN_WAKERS[i] + } +} + +pub enum Out {} +impl Dir for Out { + fn dir() -> UsbDirection { + UsbDirection::Out + } + + #[inline] + fn waker(i: usize) -> &'static AtomicWaker { + &EP_OUT_WAKERS[i] + } +} + +pub struct Endpoint<'d, T: Instance, D> { + _phantom: PhantomData<(&'d mut T, D)>, + info: EndpointInfo, + buf: EndpointBuffer, +} + +impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { + fn info(&self) -> &EndpointInfo { + &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_OUT_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"); + } + } +} + +impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { + fn info(&self) -> &EndpointInfo { + &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"); + } + } +} + +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); + } + 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(()) + } + } +} + +pub struct ControlPipe<'d, T: Instance> { + _phantom: PhantomData<&'d mut T>, + max_packet_size: u16, +} + +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); + }); + } + + 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(()) + } + } + + fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { + async move { + trace!("control: accept"); + + unsafe { + let bufcontrol = T::dpram().ep_in_buffer_control(0); + 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); + }); + } + } + } + + fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { + async move { + 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/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index d804a660b..72a3a057d 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,6 +9,10 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.3" @@ -25,3 +29,5 @@ byte-slice-cast = { version = "1.2.0", default-features = false } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +static_cell = "1.0.0" diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..2cb0010f1 --- /dev/null +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -0,0 +1,264 @@ +#![no_std] +#![no_main] +#![feature(generic_associated_types)] +#![feature(type_alias_impl_trait)] + +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Waker; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; +use embassy_rp::usb::Driver; +use embassy_rp::{interrupt, peripherals}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use embassy_usb::{Builder, Config, UsbDevice}; +use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; +use embedded_io::asynch::{Read, Write}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +type MyDriver = Driver<'static, peripherals::USB>; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +#[embassy_executor::task] +async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { + device.run().await +} + +#[embassy_executor::task] +async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { + loop { + warn!("WAITING for connection"); + LINK_UP.store(false, Ordering::Relaxed); + + class.wait_connection().await.unwrap(); + + warn!("Connected"); + LINK_UP.store(true, Ordering::Relaxed); + + loop { + let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); + let n = match class.read_packet(&mut p[..]).await { + Ok(n) => n, + Err(e) => { + warn!("error reading packet: {:?}", e); + break; + } + }; + + let buf = p.slice(0..n); + if RX_CHANNEL.try_send(buf).is_err() { + warn!("Failed pushing rx'd packet to channel."); + } + } + } +} + +#[embassy_executor::task] +async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { + loop { + let pkt = TX_CHANNEL.recv().await; + if let Err(e) = class.write_packet(&pkt[..]).await { + warn!("Failed to TX packet: {:?}", e); + } + } +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + // Create the driver, from the HAL. + let irq = interrupt::take!(USBCTRL_IRQ); + let driver = Driver::new(p.USB, irq); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-Ethernet example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for Windows support. + config.composite_with_iads = true; + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + + struct Resources { + device_descriptor: [u8; 256], + config_descriptor: [u8; 256], + bos_descriptor: [u8; 256], + control_buf: [u8; 128], + serial_state: State<'static>, + } + let res: &mut Resources = singleton!(Resources { + device_descriptor: [0; 256], + config_descriptor: [0; 256], + bos_descriptor: [0; 256], + control_buf: [0; 128], + serial_state: State::new(), + }); + + // Create embassy-usb DeviceBuilder using the driver and config. + let mut builder = Builder::new( + driver, + config, + &mut res.device_descriptor, + &mut res.config_descriptor, + &mut res.bos_descriptor, + &mut res.control_buf, + None, + ); + + // WARNINGS for Android ethernet tethering: + // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. + // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), + // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. + // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 + // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 + + // Our MAC addr. + let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; + // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. + let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; + + // Create classes on the builder. + let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); + + // Build the builder. + let usb = builder.build(); + + unwrap!(spawner.spawn(usb_task(usb))); + + let (tx, rx) = class.split(); + unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); + unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); + + let config = embassy_net::ConfigStrategy::Dhcp; + //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Generate random seed + let seed = 1234; // guaranteed random, chosen by a fair dice roll + + // Init network stack + let device = Device { mac_addr: our_mac_addr }; + let stack = &*singleton!(Stack::new( + device, + config, + singleton!(StackResources::<1, 2, 8>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} + +static TX_CHANNEL: Channel = Channel::new(); +static RX_CHANNEL: Channel = Channel::new(); +static LINK_UP: AtomicBool = AtomicBool::new(false); + +struct Device { + mac_addr: [u8; 6], +} + +impl embassy_net::Device for Device { + fn register_waker(&mut self, waker: &Waker) { + // loopy loopy wakey wakey + waker.wake_by_ref() + } + + fn link_state(&mut self) -> embassy_net::LinkState { + match LINK_UP.load(Ordering::Relaxed) { + true => embassy_net::LinkState::Up, + false => embassy_net::LinkState::Down, + } + } + + fn capabilities(&self) -> embassy_net::DeviceCapabilities { + let mut caps = embassy_net::DeviceCapabilities::default(); + caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header + caps.medium = embassy_net::Medium::Ethernet; + caps + } + + fn is_transmit_ready(&mut self) -> bool { + true + } + + fn transmit(&mut self, pkt: PacketBuf) { + if TX_CHANNEL.try_send(pkt).is_err() { + warn!("TX failed") + } + } + + fn receive<'a>(&mut self) -> Option { + RX_CHANNEL.try_recv().ok() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } +} diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs new file mode 100644 index 000000000..74be1f598 --- /dev/null +++ b/examples/rp/src/bin/usb_serial.rs @@ -0,0 +1,103 @@ +#![no_std] +#![no_main] +#![feature(generic_associated_types)] +#![feature(type_alias_impl_trait)] + +use defmt::{info, panic}; +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::usb::{Driver, Instance}; +use embassy_usb::driver::EndpointError; +use embassy_usb::{Builder, Config}; +use embassy_usb_serial::{CdcAcmClass, State}; +use futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello there!"); + + let p = embassy_rp::init(Default::default()); + + // Create the driver, from the HAL. + let irq = interrupt::take!(USBCTRL_IRQ); + let driver = Driver::new(p.USB, irq); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From 3826b4f7130366c92015f61566b4bb0783e0fee3 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Thu, 25 Aug 2022 05:43:38 +0200 Subject: [PATCH 0044/1575] Rename REG_BUS_FEEDBEAD to REG_BUS_TEST_RO This commit renames the REG_BUS_FEEDBEAD to REG_BUS_TEST_RO (Read-Only) which is the name used in the specification, section 4.2.3 Table 6. It also adds a constant named REG_BUS_TEST_RW (Read-Write) to represent the dummy register which the host can use to write data and read back to check that the gSPI interface is working properly. --- src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1c49771b9..e287ca12a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,8 +50,8 @@ const REG_BUS_CTRL: u32 = 0x0; const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask const REG_BUS_STATUS: u32 = 0x8; -const REG_BUS_FEEDBEAD: u32 = 0x14; -const REG_BUS_TEST: u32 = 0x18; +const REG_BUS_TEST_RO: u32 = 0x14; +const REG_BUS_TEST_RW: u32 = 0x18; const REG_BUS_RESP_DELAY: u32 = 0x1c; // SPI_STATUS_REGISTER bits @@ -563,19 +563,19 @@ where Timer::after(Duration::from_millis(250)).await; info!("waiting for ping..."); - while self.read32_swapped(REG_BUS_FEEDBEAD).await != FEEDBEAD {} + while self.read32_swapped(REG_BUS_TEST_RO).await != FEEDBEAD {} info!("ping ok"); - self.write32_swapped(0x18, TEST_PATTERN).await; - let val = self.read32_swapped(REG_BUS_TEST).await; + self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await; + let val = self.read32_swapped(REG_BUS_TEST_RW).await; assert_eq!(val, TEST_PATTERN); // 32bit, little endian. self.write32_swapped(REG_BUS_CTRL, 0x00010031).await; - let val = self.read32(FUNC_BUS, REG_BUS_FEEDBEAD).await; + let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await; assert_eq!(val, FEEDBEAD); - let val = self.read32(FUNC_BUS, REG_BUS_TEST).await; + let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await; assert_eq!(val, TEST_PATTERN); // No response delay in any of the funcs. From 045ae2c29f2ca358a501e664ed2a7863b9a6bd59 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 23 Aug 2022 14:57:45 +0200 Subject: [PATCH 0045/1575] Ensure interrupt::take works without embassy-executor Add "rtos-trace-interrupt" feature flag on embassy-macros and enable it for embassy-executor, to ensure that the interrupt::take! macro can be used without depending on embassy-executor. --- embassy-executor/Cargo.toml | 4 +-- embassy-macros/Cargo.toml | 3 +++ .../src/macros/cortex_m_interrupt_take.rs | 27 ++++++++++++++----- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 1a611720c..409a341f0 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,7 +31,7 @@ nightly = [] integrated-timers = ["dep:embassy-time"] # Trace interrupt invocations with rtos-trace. -rtos-trace-interrupt = ["rtos-trace"] +rtos-trace-interrupt = ["rtos-trace", "embassy-macros/rtos-trace-interrupt"] [dependencies] defmt = { version = "0.3", optional = true } @@ -39,7 +39,7 @@ log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.1.0", path = "../embassy-macros"} +embassy-macros = { version = "0.1.0", path = "../embassy-macros" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 19a3e9de2..03fa79dd8 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -15,3 +15,6 @@ proc-macro = true [features] std = [] wasm = [] + +# Enabling this cause interrupt::take! to require embassy-executor +rtos-trace-interrupt = [] diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index f6e41bcb4..d30189ce3 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs @@ -6,6 +6,23 @@ pub fn run(name: syn::Ident) -> Result { let name_interrupt = format_ident!("{}", name); let name_handler = format!("__EMBASSY_{}_HANDLER", name); + #[cfg(feature = "rtos-trace-interrupt")] + let (isr_enter, isr_exit) = ( + quote! { + ::embassy_executor::rtos_trace_interrupt! { + ::embassy_executor::export::trace::isr_enter(); + } + }, + quote! { + ::embassy_executor::rtos_trace_interrupt! { + ::embassy_executor::export::trace::isr_exit(); + } + }, + ); + + #[cfg(not(feature = "rtos-trace-interrupt"))] + let (isr_enter, isr_exit) = (quote! {}, quote! {}); + let result = quote! { { #[allow(non_snake_case)] @@ -19,13 +36,11 @@ pub fn run(name: syn::Ident) -> Result { let func = HANDLER.func.load(interrupt::_export::atomic::Ordering::Relaxed); let ctx = HANDLER.ctx.load(interrupt::_export::atomic::Ordering::Relaxed); let func: fn(*mut ()) = ::core::mem::transmute(func); - ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::export::trace::isr_enter(); - } + #isr_enter + func(ctx); - ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::export::trace::isr_exit(); - } + #isr_exit + } static TAKEN: interrupt::_export::atomic::AtomicBool = interrupt::_export::atomic::AtomicBool::new(false); From f2ac14b86f21c31221bba1e1653e2a9020d60d8e Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Wed, 24 Aug 2022 13:35:48 +0200 Subject: [PATCH 0046/1575] Add WORD_LENGTH_32/HIGH_SPEED constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds two constants which are intended to be used for setting the `Word Length` and `High Speed` fields in the gSPR register (address: 0x0000, bit: 0 and bit 4). Currently, this field is being set by the following line: ```rust // 32bit, little endian. self.write32_swapped(REG_BUS_CTRL, 0x00010031).await; ``` Assuming that we are sending these bits using the gSPI write protocol and using 16-bit word operation in little endian (which I think might be the default) then the data bytes should be packed like this: ``` +--+--+--+--+ |D1|D0|D3|D2| +--+--+--+--+ val (hex): 0x00010031 val (bin): 00000000000000010000000000110001 rotated(16): 00000000001100010000000000000001 ``` If we split val into bytes and rotated the bits we get: ``` Split into bytes: D3 D2 D1 D0 00000000 00000001 00000000 00110001 Rotate 16 and split into bytes: D1 D0 D3 D2 00000000 00110001 00000000 00000001 ``` Looking at the write procotol it seems to me that the above will indeed set the `Word Length` to 1 but will also set other values. ``` Status enable (1=default) D1 D0 D3 D2 ↓ 00000000 00110001 00000000 00000001 ↑↑ ↑↑ ↑ || |Word Length (1=32-bit) || | || Endianess (0=Little) || |High-speed mode (1=High speed (default)) | Interrupt polarity (1=high (default)) ``` This commit suggests adding the above mentioned constants for setting the only the word length field and the high speed field. --- src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3932ce41b..ef586c8f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,8 @@ const REG_BUS_STATUS: u32 = 0x8; const REG_BUS_FEEDBEAD: u32 = 0x14; const REG_BUS_TEST: u32 = 0x18; const REG_BUS_RESP_DELAY: u32 = 0x1c; +const WORD_LENGTH_32: u32 = 0x1; +const HIGH_SPEED: u32 = 0x10; // SPI_STATUS_REGISTER bits const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; @@ -570,8 +572,8 @@ where let val = self.read32_swapped(REG_BUS_TEST).await; assert_eq!(val, TEST_PATTERN); - // 32bit, little endian. - self.write32_swapped(REG_BUS_CTRL, 0x00010031).await; + // 32-bit word length, little endian (which is the default endianess). + self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await; let val = self.read32(FUNC_BUS, REG_BUS_FEEDBEAD).await; assert_eq!(val, FEEDBEAD); From 9a873d1dbf301bf6416fec675a40ebd3aa3f1174 Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 26 Aug 2022 14:40:20 +1000 Subject: [PATCH 0047/1575] Ensure that the sampling is stopped Ensures that nRF saadc sampling is stopped and is awaited prior to exiting the two sampling methods. Not doing so causes a potential power drain and the potential for dropped buffer writes when having finished continuous sampling. --- embassy-nrf/src/saadc.rs | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 7dc66349e..43c98a888 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -183,6 +183,11 @@ impl<'d, const N: usize> Saadc<'d, N> { r.intenclr.write(|w| w.started().clear()); WAKER.wake(); } + + if r.events_stopped.read().bits() != 0 { + r.intenclr.write(|w| w.stopped().clear()); + WAKER.wake(); + } } fn regs() -> &'static saadc::RegisterBlock { @@ -251,6 +256,8 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; + + Self::stop_sampling().await; } /// Continuous sampling with double buffers. @@ -403,6 +410,42 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; + + Self::stop_sampling().await; + } + + // Stop sampling and wait for it to stop + async fn stop_sampling() { + let r = Self::regs(); + + // Reset and enable the events + + compiler_fence(Ordering::SeqCst); + + r.events_stopped.reset(); + r.intenset.write(|w| { + w.stopped().set(); + w + }); + + // Stop + + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + + // Wait for 'stopped' event. + poll_fn(|cx| { + let r = Self::regs(); + + WAKER.register(cx.waker()); + + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; } } From bd27b9080fd9019e69e84fd30894a71db9fd61b5 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 12:50:12 +0200 Subject: [PATCH 0048/1575] Add HIL tests of DMA & UART, and correctly set DREQ for uart DMA --- embassy-rp/src/dma.rs | 31 +++++++++++-- embassy-rp/src/uart.rs | 71 +++++++++++++++++++++--------- tests/rp/src/bin/dma_copy_async.rs | 41 +++++++++++++++++ tests/rp/src/bin/uart.rs | 32 ++++++++++++++ tests/rp/src/bin/uart_dma.rs | 32 ++++++++++++++ 5 files changed, 184 insertions(+), 23 deletions(-) create mode 100644 tests/rp/src/bin/dma_copy_async.rs create mode 100644 tests/rp/src/bin/uart.rs create mode 100644 tests/rp/src/bin/uart_dma.rs diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 694823742..75d7492e0 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -41,18 +41,38 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const W, to: &mut [W], + dreq: u8, ) -> Transfer<'a, C> { - let (ptr, len) = crate::dma::slice_ptr_parts_mut(to); - copy_inner(ch, from as *const u32, ptr as *mut u32, len, W::size(), false, true) + let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(to); + copy_inner( + ch, + from as *const u32, + to_ptr as *mut u32, + len, + W::size(), + false, + true, + dreq, + ) } pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: &[W], to: *mut W, + dreq: u8, ) -> Transfer<'a, C> { let (from_ptr, len) = crate::dma::slice_ptr_parts(from); - copy_inner(ch, from_ptr as *const u32, to as *mut u32, len, W::size(), true, false) + copy_inner( + ch, + from_ptr as *const u32, + to as *mut u32, + len, + W::size(), + true, + false, + dreq, + ) } pub unsafe fn copy<'a, C: Channel, W: Word>( @@ -71,6 +91,7 @@ pub unsafe fn copy<'a, C: Channel, W: Word>( W::size(), true, true, + vals::TreqSel::PERMANENT.0, ) } @@ -82,6 +103,7 @@ fn copy_inner<'a, C: Channel>( data_size: DataSize, incr_read: bool, incr_write: bool, + dreq: u8, ) -> Transfer<'a, C> { into_ref!(ch); @@ -95,6 +117,9 @@ fn copy_inner<'a, C: Channel>( compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { + // TODO: Add all DREQ options to pac vals::TreqSel, and use + // `set_treq:sel` + w.0 = ((dreq as u32) & 0x3f) << 15usize; w.set_data_size(data_size); w.set_incr_read(incr_read); w.set_incr_write(incr_write); diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index 9d94dcf21..4a3c7a0ce 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -113,7 +113,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { pub fn blocking_flush(&mut self) -> Result<(), Error> { let r = T::regs(); - unsafe { while r.uartfr().read().txff() {} } + unsafe { while !r.uartfr().read().txfe() {} } Ok(()) } } @@ -127,7 +127,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _) + crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _, T::TX_DREQ) }; transfer.await; Ok(()) @@ -147,6 +147,10 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { unsafe { for b in buffer { *b = loop { + if r.uartfr().read().rxfe() { + continue; + } + let dr = r.uartdr().read(); if dr.oe() { @@ -157,7 +161,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { return Err(Error::Parity); } else if dr.fe() { return Err(Error::Framing); - } else if dr.fe() { + } else { break dr.data(); } }; @@ -176,7 +180,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer) + crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer, T::RX_DREQ) }; transfer.await; Ok(()) @@ -282,6 +286,30 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { 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) = &cts { + pin.io().ctrl().write(|w| w.set_funcsel(2)); + 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); + }); + } + let clk_base = crate::clocks::clk_peri_freq(); let baud_rate_div = (8 * clk_base) / config.baudrate; @@ -302,10 +330,14 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), - Parity::ParityEven => (true, true), Parity::ParityOdd => (true, false), + Parity::ParityEven => (true, true), }; + // PL011 needs a (dummy) line control register write to latch in the + // divisors. We don't want to actually change LCR contents here. + r.uartlcr_h().modify(|_| {}); + r.uartlcr_h().write(|w| { w.set_wlen(config.data_bits.bits()); w.set_stp2(config.stop_bits == StopBits::STOP2); @@ -321,15 +353,6 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_ctsen(cts.is_some()); w.set_rtsen(rts.is_some()); }); - - tx.io().ctrl().write(|w| w.set_funcsel(2)); - rx.io().ctrl().write(|w| w.set_funcsel(2)); - if let Some(pin) = &cts { - pin.io().ctrl().write(|w| w.set_funcsel(2)); - } - if let Some(pin) = &rts { - pin.io().ctrl().write(|w| w.set_funcsel(2)); - } } Self { @@ -377,6 +400,10 @@ mod eh02 { fn read(&mut self) -> Result> { let r = T::regs(); unsafe { + if r.uartfr().read().rxfe() { + return Err(nb::Error::WouldBlock); + } + let dr = r.uartdr().read(); if dr.oe() { @@ -387,10 +414,8 @@ mod eh02 { Err(nb::Error::Other(Error::Parity)) } else if dr.fe() { Err(nb::Error::Other(Error::Framing)) - } else if dr.fe() { - Ok(dr.data()) } else { - Err(nb::Error::WouldBlock) + Ok(dr.data()) } } } @@ -512,6 +537,9 @@ mod sealed { pub trait Mode {} pub trait Instance { + const TX_DREQ: u8; + const RX_DREQ: u8; + fn regs() -> pac::uart::Uart; } pub trait TxPin {} @@ -538,8 +566,11 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($inst:ident, $irq:ident) => { + ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$inst { + const TX_DREQ: u8 = $tx_dreq; + const RX_DREQ: u8 = $rx_dreq; + fn regs() -> pac::uart::Uart { pac::$inst } @@ -548,8 +579,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0); -impl_instance!(UART1, UART1); +impl_instance!(UART0, UART0, 20, 21); +impl_instance!(UART1, UART1, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} diff --git a/tests/rp/src/bin/dma_copy_async.rs b/tests/rp/src/bin/dma_copy_async.rs new file mode 100644 index 000000000..c53f644bd --- /dev/null +++ b/tests/rp/src/bin/dma_copy_async.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::dma::copy; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + // Check `u8` copy + { + let data: [u8; 2] = [0xC0, 0xDE]; + let mut buf = [0; 2]; + unsafe { copy(p.DMA_CH0, &data, &mut buf).await }; + assert_eq!(buf, data); + } + + // Check `u16` copy + { + let data: [u16; 2] = [0xC0BE, 0xDEAD]; + let mut buf = [0; 2]; + unsafe { copy(p.DMA_CH1, &data, &mut buf).await }; + assert_eq!(buf, data); + } + + // Check `u32` copy + { + let data: [u32; 2] = [0xC0BEDEAD, 0xDEADAAFF]; + let mut buf = [0; 2]; + unsafe { copy(p.DMA_CH2, &data, &mut buf).await }; + assert_eq!(buf, data); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs new file mode 100644 index 000000000..92f61464e --- /dev/null +++ b/tests/rp/src/bin/uart.rs @@ -0,0 +1,32 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::uart::{Config, Uart}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let config = Config::default(); + let mut uart = Uart::new_blocking(uart, tx, rx, config); + + // We can't send too many bytes, they have to fit in the FIFO. + // This is because we aren't sending+receiving at the same time. + + let data = [0xC0, 0xDE]; + uart.blocking_write(&data).unwrap(); + + let mut buf = [0; 2]; + uart.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs new file mode 100644 index 000000000..963c22707 --- /dev/null +++ b/tests/rp/src/bin/uart_dma.rs @@ -0,0 +1,32 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::uart::{Config, Uart}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let config = Config::default(); + let mut uart = Uart::new(uart, tx, rx, p.DMA_CH0, p.DMA_CH1, config); + + // We can't send too many bytes, they have to fit in the FIFO. + // This is because we aren't sending+receiving at the same time. + + let data = [0xC0, 0xDE]; + uart.write(&data).await.unwrap(); + + let mut buf = [0; 2]; + uart.read(&mut buf).await.unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 61c666212fa04571a74c27f649526459a376aa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 24 Jun 2022 14:31:30 +0200 Subject: [PATCH 0049/1575] stm32wl: Do not require external SPI pins for SUBGHZ For the Seeed Studio Lora-E5 those pins conflict with the radio frontend control GPIOS (PA4 and PA5). --- embassy-stm32/src/spi/mod.rs | 12 ++++++++++++ embassy-stm32/src/subghz/mod.rs | 7 ++----- examples/stm32wl/src/bin/lorawan.rs | 2 +- examples/stm32wl/src/bin/subghz.rs | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 26fb392ef..1a00f4187 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -179,6 +179,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { ) } + /// Useful for on chip peripherals like SUBGHZ which are hardwired. + /// The bus can optionally be exposed externally with `Spi::new()` still. + pub fn new_internal( + peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ 'd, + freq: Hertz, + config: Config, + ) -> Self { + Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + } + fn new_inner( peri: impl Peripheral

+ 'd, sck: Option>, diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs index a74f9a6d5..33398fa1d 100644 --- a/embassy-stm32/src/subghz/mod.rs +++ b/embassy-stm32/src/subghz/mod.rs @@ -81,7 +81,7 @@ pub use value_error::ValueError; use crate::dma::NoDma; use crate::peripherals::SUBGHZSPI; use crate::rcc::sealed::RccPeripheral; -use crate::spi::{BitOrder, Config as SpiConfig, MisoPin, MosiPin, SckPin, Spi, MODE_0}; +use crate::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -212,9 +212,6 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { /// clock. pub fn new( peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - mosi: impl Peripheral

> + 'd, - miso: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, ) -> Self { @@ -227,7 +224,7 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { let mut config = SpiConfig::default(); config.mode = MODE_0; config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new(peri, sck, mosi, miso, txdma, rxdma, clk, config); + let spi = Spi::new_internal(peri, txdma, rxdma, clk, config); unsafe { wakeup() }; diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 7e8a8946d..fdd7eb8cf 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -31,7 +31,7 @@ async fn main(_spawner: Spawner) { let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High); let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); - let radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); + let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); let irq = interrupt::take!(SUBGHZ_RADIO); static mut RADIO_STATE: SubGhzState<'static> = SubGhzState::new(); diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index c5e9bb597..8f674d796 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -72,7 +72,7 @@ async fn main(_spawner: Spawner) { unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); }); - let mut radio = SubGhz::new(p.SUBGHZSPI, p.PA5, p.PA7, p.PA6, NoDma, NoDma); + let mut radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); defmt::info!("Radio ready for use"); From 8e8106ef555373f8b0f04fe1f67684efef435a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 25 Jun 2022 07:01:31 +0200 Subject: [PATCH 0050/1575] lora: Improve IRQ handling * Interrupt handler only triggers a waker: Do the actual interrupt processing which involves SUBGHZ SPI coms in the task. * Do not require a static state for the constructor. * Remove unsafe from construcor. --- embassy-lora/src/stm32wl/mod.rs | 126 ++++++++++++---------------- examples/stm32wl/src/bin/lorawan.rs | 5 +- 2 files changed, 54 insertions(+), 77 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 7822d0153..5e1773d55 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,18 +1,18 @@ //! A radio driver integration for the radio found on STM32WL family devices. use core::future::Future; -use core::mem::MaybeUninit; +use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{AnyPin, Output}; -use embassy_stm32::interrupt::{InterruptExt, SUBGHZ_RADIO}; +use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; use embassy_stm32::subghz::{ CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; -use embassy_stm32::Peripheral; -use embassy_sync::signal::Signal; +use embassy_sync::waitqueue::AtomicWaker; +use futures::future::poll_fn; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; @@ -28,65 +28,43 @@ pub enum State { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RadioError; -static IRQ: Signal<(Status, u16)> = Signal::new(); - -struct StateInner<'d> { - radio: SubGhz<'d, NoDma, NoDma>, - switch: RadioSwitch<'d>, -} - -/// External state storage for the radio state -pub struct SubGhzState<'a>(MaybeUninit>); -impl<'d> SubGhzState<'d> { - pub const fn new() -> Self { - Self(MaybeUninit::uninit()) - } -} +static IRQ_WAKER: AtomicWaker = AtomicWaker::new(); /// The radio peripheral keeping the radio state and owning the radio IRQ. -pub struct SubGhzRadio<'d> { - state: *mut StateInner<'d>, - _irq: PeripheralRef<'d, SUBGHZ_RADIO>, +pub struct SubGhzRadio<'d, RS> { + radio: SubGhz<'d, NoDma, NoDma>, + switch: RS, + irq: PeripheralRef<'d, SUBGHZ_RADIO>, } -impl<'d> SubGhzRadio<'d> { +#[derive(Default)] +#[non_exhaustive] +pub struct SubGhzRadioConfig { + pub reg_mode: RegMode, + pub calibrate_image: CalibrateImage, +} + +impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// Create a new instance of a SubGhz radio for LoRaWAN. - /// - /// # Safety - /// Do not leak self or futures - pub unsafe fn new( - state: &'d mut SubGhzState<'d>, - radio: SubGhz<'d, NoDma, NoDma>, - switch: RadioSwitch<'d>, + pub fn new( + mut radio: SubGhz<'d, NoDma, NoDma>, + switch: RS, irq: impl Peripheral

+ 'd, - ) -> Self { + config: SubGhzRadioConfig, + ) -> Result { into_ref!(irq); - let mut inner = StateInner { radio, switch }; - inner.radio.reset(); - - let state_ptr = state.0.as_mut_ptr(); - state_ptr.write(inner); + radio.reset(); irq.disable(); - irq.set_handler(|p| { - // This is safe because we only get interrupts when configured for, so - // the radio will be awaiting on the signal at this point. If not, the ISR will - // anyway only adjust the state in the IRQ signal state. - let state = &mut *(p as *mut StateInner<'d>); - state.on_interrupt(); + irq.set_handler(|_| { + IRQ_WAKER.wake(); + unsafe { SUBGHZ_RADIO::steal().disable() }; }); - irq.set_handler_context(state_ptr as *mut ()); - irq.enable(); - Self { - state: state_ptr, - _irq: irq, - } + Self { radio, switch, irq } } -} -impl<'d> StateInner<'d> { /// Configure radio settings in preparation for TX or RX pub(crate) fn configure(&mut self) -> Result<(), RadioError> { trace!("Configuring STM32WL SUBGHZ radio"); @@ -151,8 +129,7 @@ impl<'d> StateInner<'d> { self.radio.set_tx(Timeout::DISABLED)?; loop { - let (_status, irq_status) = IRQ.wait().await; - IRQ.reset(); + let (_status, irq_status) = self.irq_wait().await; if irq_status & Irq::TxDone.mask() != 0 { let stats = self.radio.lora_stats()?; @@ -214,8 +191,8 @@ impl<'d> StateInner<'d> { trace!("RX started"); loop { - let (status, irq_status) = IRQ.wait().await; - IRQ.reset(); + let (status, irq_status) = self.irq_wait().await; + trace!("RX IRQ {:?}, {:?}", status, irq_status); if irq_status & Irq::RxDone.mask() != 0 { let (status, len, ptr) = self.radio.rx_buffer_status()?; @@ -238,17 +215,24 @@ impl<'d> StateInner<'d> { } } - /// Read interrupt status and store in global signal - fn on_interrupt(&mut self) { - let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); - self.radio - .clear_irq_status(irq_status) - .expect("error clearing irq status"); - if irq_status & Irq::PreambleDetected.mask() != 0 { - trace!("Preamble detected, ignoring"); - } else { - IRQ.signal((status, irq_status)); - } + async fn irq_wait(&mut self) -> (Status, u16) { + poll_fn(|cx| { + self.irq.unpend(); + self.irq.enable(); + IRQ_WAKER.register(cx.waker()); + + let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); + self.radio + .clear_irq_status(irq_status) + .expect("error clearing irq status"); + trace!("IRQ status: {=u16:b}", irq_status); + if irq_status == 0 { + Poll::Pending + } else { + Poll::Ready((status, irq_status)) + } + }) + .await } } @@ -257,18 +241,12 @@ impl PhyRxTx for SubGhzRadio<'static> { type TxFuture<'m> = impl Future> + 'm; fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { - async move { - let inner = unsafe { &mut *self.state }; - inner.do_tx(config, buf).await - } + async move { self.do_tx(config, buf).await } } type RxFuture<'m> = impl Future> + 'm; fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { - async move { - let inner = unsafe { &mut *self.state }; - inner.do_rx(config, buf).await - } + async move { self.do_rx(config, buf).await } } } @@ -278,7 +256,7 @@ impl From for RadioError { } } -impl<'d> Timings for SubGhzRadio<'d> { +impl<'d, RS> Timings for SubGhzRadio<'d, RS> { fn get_rx_window_offset_ms(&self) -> i32 { -200 } diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index fdd7eb8cf..9c390bdf0 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -32,10 +32,9 @@ async fn main(_spawner: Spawner) { let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); - let irq = interrupt::take!(SUBGHZ_RADIO); - static mut RADIO_STATE: SubGhzState<'static> = SubGhzState::new(); - let radio = unsafe { SubGhzRadio::new(&mut RADIO_STATE, radio, rfs, irq) }; + + let radio = SubGhzRadio::new(radio, rfs, irq); let mut region: region::Configuration = region::EU868::default().into(); From 6ee29ff0bd4d8cbd2da547dfdf631332cda551d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 25 Jun 2022 10:06:00 +0200 Subject: [PATCH 0051/1575] lora: Propagate "defmt" feature to lora crates --- embassy-lora/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 9d5e7aed2..25d8c667c 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -18,6 +18,7 @@ flavors = [ sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] +defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] [dependencies] From 69d80c086d2f22f66aa25fafb55918cb1c5e3bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 25 Jun 2022 10:10:59 +0200 Subject: [PATCH 0052/1575] lora: Use a trait for RF frontend switching The Seeed Studio Lora-E5 module only has two control pins. With the `RadioSwitch` trait the user can implement any method required by the module/board to control the TX/RX direction of the radio frontend. --- embassy-lora/src/stm32wl/mod.rs | 42 +++++------------------------ examples/stm32wl/src/bin/lorawan.rs | 28 ++++++++++++++++++- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 5e1773d55..7502dc379 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -4,7 +4,6 @@ use core::task::Poll; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::dma::NoDma; -use embassy_stm32::gpio::{AnyPin, Output}; use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; use embassy_stm32::subghz::{ CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams, @@ -100,7 +99,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { //trace!("TX Request: {}", config); trace!("TX START"); - self.switch.set_tx_lp(); + self.switch.set_tx(); self.configure()?; self.radio @@ -236,15 +235,15 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { } } -impl PhyRxTx for SubGhzRadio<'static> { +impl PhyRxTx for SubGhzRadio<'static, RS> { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm; + type TxFuture<'m> = impl Future> + 'm where RS: 'm; fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { async move { self.do_tx(config, buf).await } } - type RxFuture<'m> = impl Future> + 'm; + type RxFuture<'m> = impl Future> + 'm where RS: 'm; fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { async move { self.do_rx(config, buf).await } } @@ -265,36 +264,9 @@ impl<'d, RS> Timings for SubGhzRadio<'d, RS> { } } -/// Represents the radio switch found on STM32WL based boards, used to control the radio for transmission or reception. -pub struct RadioSwitch<'d> { - ctrl1: Output<'d, AnyPin>, - ctrl2: Output<'d, AnyPin>, - ctrl3: Output<'d, AnyPin>, -} - -impl<'d> RadioSwitch<'d> { - pub fn new(ctrl1: Output<'d, AnyPin>, ctrl2: Output<'d, AnyPin>, ctrl3: Output<'d, AnyPin>) -> Self { - Self { ctrl1, ctrl2, ctrl3 } - } - - pub(crate) fn set_rx(&mut self) { - self.ctrl1.set_high(); - self.ctrl2.set_low(); - self.ctrl3.set_high(); - } - - pub(crate) fn set_tx_lp(&mut self) { - self.ctrl1.set_high(); - self.ctrl2.set_high(); - self.ctrl3.set_high(); - } - - #[allow(dead_code)] - pub(crate) fn set_tx_hp(&mut self) { - self.ctrl2.set_high(); - self.ctrl1.set_low(); - self.ctrl3.set_high(); - } +pub trait RadioSwitch { + fn set_rx(&mut self); + fn set_tx(&mut self); } fn convert_spreading_factor(sf: SpreadingFactor) -> SF { diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 9c390bdf0..0ef13354d 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -9,7 +9,7 @@ use embassy_executor::Spawner; use embassy_lora::stm32wl::*; use embassy_lora::LoraTimer; use embassy_stm32::dma::NoDma; -use embassy_stm32::gpio::{Level, Output, Pin, Speed}; +use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; use embassy_stm32::rng::Rng; use embassy_stm32::subghz::*; use embassy_stm32::{interrupt, pac}; @@ -17,6 +17,32 @@ use lorawan::default_crypto::DefaultFactory as Crypto; use lorawan_device::async_device::{region, Device, JoinMode}; use {defmt_rtt as _, panic_probe as _}; +struct RadioSwitch<'a> { + ctrl1: Output<'a, AnyPin>, + ctrl2: Output<'a, AnyPin>, + ctrl3: Output<'a, AnyPin>, +} + +impl<'a> RadioSwitch<'a> { + fn new(ctrl1: Output<'a, AnyPin>, ctrl2: Output<'a, AnyPin>, ctrl3: Output<'a, AnyPin>) -> Self { + Self { ctrl1, ctrl2, ctrl3 } + } +} + +impl<'a> embassy_lora::stm32wl::RadioSwitch for RadioSwitch<'a> { + fn set_rx(&mut self) { + self.ctrl1.set_high(); + self.ctrl2.set_low(); + self.ctrl3.set_high(); + } + + fn set_tx(&mut self) { + self.ctrl1.set_low(); + self.ctrl2.set_high(); + self.ctrl3.set_high(); + } +} + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); From f31116cafaeea7746aec19903fb1c73adaea9ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 25 Jun 2022 11:59:07 +0200 Subject: [PATCH 0053/1575] lora: Make some options configurable Call `config()` only once at construction not with every RX and TX operation. The Lora-E5 only supports HP mode, use that instead. The nucleo board supports both HP and LP and should continue to work. --- embassy-lora/src/stm32wl/mod.rs | 75 ++++++++++++++--------------- examples/stm32wl/src/bin/lorawan.rs | 4 +- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 7502dc379..5170b261a 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -6,9 +6,9 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; use embassy_stm32::subghz::{ - CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams, - LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, - Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, + CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, + LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, + StandbyClk, Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; @@ -61,46 +61,17 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { unsafe { SUBGHZ_RADIO::steal().disable() }; }); - Self { radio, switch, irq } - } + configure_radio(&mut radio, config)?; - /// Configure radio settings in preparation for TX or RX - pub(crate) fn configure(&mut self) -> Result<(), RadioError> { - trace!("Configuring STM32WL SUBGHZ radio"); - self.radio.set_standby(StandbyClk::Rc)?; - let tcxo_mode = TcxoMode::new() - .set_txco_trim(TcxoTrim::Volts1pt7) - .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(40))); - - self.radio.set_tcxo_mode(&tcxo_mode)?; - self.radio.set_regulator_mode(RegMode::Ldo)?; - - self.radio.calibrate_image(CalibrateImage::ISM_863_870)?; - - self.radio.set_buffer_base_address(0, 0)?; - - self.radio - .set_pa_config(&PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp))?; - - self.radio.set_pa_ocp(Ocp::Max140m)?; - - // let tx_params = TxParams::LP_14.set_ramp_time(RampTime::Micros40); - self.radio - .set_tx_params(&TxParams::new().set_ramp_time(RampTime::Micros40).set_power(0x0A))?; - - self.radio.set_packet_type(PacketType::LoRa)?; - self.radio.set_lora_sync_word(LoRaSyncWord::Public)?; - trace!("Done initializing STM32WL SUBGHZ radio"); - Ok(()) + Ok(Self { radio, switch, irq }) } /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form /// the upcoming RX window start. async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - //trace!("TX Request: {}", config); - trace!("TX START"); + trace!("TX Request: {}", config); + //trace!("TX START"); self.switch.set_tx(); - self.configure()?; self.radio .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; @@ -164,7 +135,6 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { trace!("RX START"); // trace!("Starting RX: {}", config); self.switch.set_rx(); - self.configure()?; self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; @@ -180,7 +150,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { let irq_cfg = CfgIrq::new() .irq_enable_all(Irq::RxDone) - .irq_enable_all(Irq::PreambleDetected) + //.irq_enable_all(Irq::PreambleDetected) .irq_enable_all(Irq::HeaderErr) .irq_enable_all(Irq::Timeout) .irq_enable_all(Irq::Err); @@ -235,6 +205,35 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { } } +fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> { + trace!("Configuring STM32WL SUBGHZ radio"); + + radio.set_regulator_mode(config.reg_mode)?; + radio.set_standby(StandbyClk::Rc)?; + + let tcxo_mode = TcxoMode::new() + .set_txco_trim(TcxoTrim::Volts1pt7) + .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100))); + radio.set_tcxo_mode(&tcxo_mode)?; + // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control". + // The STM32CUBE C driver also does this. + radio.set_hse_in_trim(HseTrim::MIN)?; + + // Re-calibrate everything after setting the TXCO config. + radio.calibrate(0x7F)?; + radio.calibrate_image(config.calibrate_image)?; + + radio.set_pa_config(&PaConfig::HP_14)?; + radio.set_tx_params(&TxParams::HP.set_ramp_time(RampTime::Micros40))?; + radio.set_pa_ocp(Ocp::Max140m)?; + + radio.set_packet_type(PacketType::LoRa)?; + radio.set_lora_sync_word(LoRaSyncWord::Public)?; + + trace!("Done initializing STM32WL SUBGHZ radio"); + Ok(()) +} + impl PhyRxTx for SubGhzRadio<'static, RS> { type PhyError = RadioError; diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 0ef13354d..83df0188a 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -60,7 +60,9 @@ async fn main(_spawner: Spawner) { let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); let irq = interrupt::take!(SUBGHZ_RADIO); - let radio = SubGhzRadio::new(radio, rfs, irq); + let mut radio_config = SubGhzRadioConfig::default(); + radio_config.calibrate_image = CalibrateImage::ISM_863_870; + let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap(); let mut region: region::Configuration = region::EU868::default().into(); From 84240d49eaf208691d0785e060ee13a7a14f0671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 26 Jun 2022 22:59:39 +0200 Subject: [PATCH 0054/1575] stm32wl: Fix RCC * `MSIRGSEL = 1` was required for MSI accept the updated MSI range * Reorder enable and clock switching to properly handle the jump from the default 4MHz MSI to a higher MSI freuquency --- embassy-stm32/src/rcc/wl.rs | 116 ++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 69c192c67..347674918 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -202,54 +202,11 @@ impl Default for Config { pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw, vos) = match config.mux { - ClockSrc::HSI16 => { - // Enable HSI16 - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - - (HSI_FREQ.0, 0x01, VoltageScale::Range2) - } - ClockSrc::HSE32 => { - // Enable HSE32 - RCC.cr().write(|w| { - w.set_hsebyppwr(true); - w.set_hseon(true); - }); - while !RCC.cr().read().hserdy() {} - - (HSE32_FREQ.0, 0x02, VoltageScale::Range1) - } - ClockSrc::MSI(range) => { - RCC.cr().write(|w| { - w.set_msirange(range.into()); - w.set_msion(true); - }); - - while !RCC.cr().read().msirdy() {} - - (range.freq(), 0x00, range.vos()) - } + ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2), + ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Range1), + ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()), }; - RCC.cfgr().modify(|w| { - w.set_sw(sw.into()); - if config.ahb_pre == AHBPrescaler::NotDivided { - w.set_hpre(0); - } else { - w.set_hpre(config.ahb_pre.into()); - } - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); - }); - - RCC.extcfgr().modify(|w| { - if config.shd_ahb_pre == AHBPrescaler::NotDivided { - w.set_shdhpre(0); - } else { - w.set_shdhpre(config.shd_ahb_pre.into()); - } - }); - let ahb_freq: u32 = match config.ahb_pre { AHBPrescaler::NotDivided => sys_clk, pre => { @@ -288,16 +245,6 @@ pub(crate) unsafe fn init(config: Config) { } }; - let apb3_freq = shd_ahb_freq; - - if config.enable_lsi { - let csr = RCC.csr().read(); - if !csr.lsion() { - RCC.csr().modify(|w| w.set_lsion(true)); - while !RCC.csr().read().lsirdy() {} - } - } - // Adjust flash latency let flash_clk_src_freq: u32 = shd_ahb_freq; let ws = match vos { @@ -319,6 +266,61 @@ pub(crate) unsafe fn init(config: Config) { while FLASH.acr().read().latency() != ws {} + match config.mux { + ClockSrc::HSI16 => { + // Enable HSI16 + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + } + ClockSrc::HSE32 => { + // Enable HSE32 + RCC.cr().write(|w| { + w.set_hsebyppwr(true); + w.set_hseon(true); + }); + while !RCC.cr().read().hserdy() {} + } + ClockSrc::MSI(range) => { + let cr = RCC.cr().read(); + assert!(!cr.msion() || cr.msirdy()); + RCC.cr().write(|w| { + w.set_msirgsel(true); + w.set_msirange(range.into()); + w.set_msion(true); + }); + while !RCC.cr().read().msirdy() {} + } + } + + RCC.extcfgr().modify(|w| { + if config.shd_ahb_pre == AHBPrescaler::NotDivided { + w.set_shdhpre(0); + } else { + w.set_shdhpre(config.shd_ahb_pre.into()); + } + }); + + RCC.cfgr().modify(|w| { + w.set_sw(sw.into()); + if config.ahb_pre == AHBPrescaler::NotDivided { + w.set_hpre(0); + } else { + w.set_hpre(config.ahb_pre.into()); + } + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + + // TODO: switch voltage range + + if config.enable_lsi { + let csr = RCC.csr().read(); + if !csr.lsion() { + RCC.csr().modify(|w| w.set_lsion(true)); + while !RCC.csr().read().lsirdy() {} + } + } + set_freqs(Clocks { sys: Hertz(sys_clk), ahb1: Hertz(ahb_freq), @@ -326,7 +328,7 @@ pub(crate) unsafe fn init(config: Config) { ahb3: Hertz(shd_ahb_freq), apb1: Hertz(apb1_freq), apb2: Hertz(apb2_freq), - apb3: Hertz(apb3_freq), + apb3: Hertz(shd_ahb_freq), apb1_tim: Hertz(apb1_tim_freq), apb2_tim: Hertz(apb2_tim_freq), }); From 60ca5e8479e951b220299ea32bfc48b1909edd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 27 Jun 2022 20:52:37 +0200 Subject: [PATCH 0055/1575] lora: Improve TX/RX parameters Match the settings used in the C driver --- embassy-lora/src/stm32wl/mod.rs | 82 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 5170b261a..c142353b6 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -69,8 +69,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form /// the upcoming RX window start. async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - trace!("TX Request: {}", config); - //trace!("TX START"); + trace!("TX request: {}", config); self.switch.set_tx(); self.radio @@ -87,33 +86,26 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio.set_lora_packet_params(&packet_params)?; - let irq_cfg = CfgIrq::new() - .irq_enable_all(Irq::TxDone) - .irq_enable_all(Irq::RxDone) - .irq_enable_all(Irq::Timeout); + let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout); self.radio.set_irq_cfg(&irq_cfg)?; self.radio.set_buffer_base_address(0, 0)?; self.radio.write_buffer(0, buf)?; - self.radio.set_tx(Timeout::DISABLED)?; + // The maximum airtime for any LoRaWAN package is 2793.5ms. + // The value of 4000ms is copied from C driver and gives us a good safety margin. + self.radio.set_tx(Timeout::from_millis_sat(4000))?; + trace!("TX started"); loop { let (_status, irq_status) = self.irq_wait().await; if irq_status & Irq::TxDone.mask() != 0 { - let stats = self.radio.lora_stats()?; - let (status, error_mask) = self.radio.op_error()?; - trace!( - "TX done. Stats: {:?}. OP error: {:?}, mask {:?}", - stats, - status, - error_mask - ); - + trace!("TX done"); return Ok(0); - } else if irq_status & Irq::Timeout.mask() != 0 { - trace!("TX timeout"); + } + + if irq_status & Irq::Timeout.mask() != 0 { return Err(RadioError); } } @@ -121,10 +113,15 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> { let mod_params = LoRaModParams::new() - .set_sf(convert_spreading_factor(config.spreading_factor)) - .set_bw(convert_bandwidth(config.bandwidth)) + .set_sf(convert_spreading_factor(&config.spreading_factor)) + .set_bw(convert_bandwidth(&config.bandwidth)) .set_cr(CodingRate::Cr45) - .set_ldro_en(true); + .set_ldro_en(matches!( + (config.spreading_factor, config.bandwidth), + (SpreadingFactor::_12, Bandwidth::_125KHz) + | (SpreadingFactor::_12, Bandwidth::_250KHz) + | (SpreadingFactor::_11, Bandwidth::_125KHz) + )); self.radio.set_lora_mod_params(&mod_params) } @@ -132,8 +129,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// be able to hold a single LoRaWAN packet. async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { assert!(buf.len() >= 255); - trace!("RX START"); - // trace!("Starting RX: {}", config); + trace!("RX request: {}", config); self.switch.set_rx(); self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; @@ -144,41 +140,41 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { .set_preamble_len(8) .set_header_type(HeaderType::Variable) .set_payload_len(0xFF) - .set_crc_en(true) + .set_crc_en(false) .set_invert_iq(true); self.radio.set_lora_packet_params(&packet_params)?; let irq_cfg = CfgIrq::new() .irq_enable_all(Irq::RxDone) - //.irq_enable_all(Irq::PreambleDetected) + .irq_enable_all(Irq::PreambleDetected) + .irq_enable_all(Irq::HeaderValid) .irq_enable_all(Irq::HeaderErr) - .irq_enable_all(Irq::Timeout) - .irq_enable_all(Irq::Err); + .irq_enable_all(Irq::Err) + .irq_enable_all(Irq::Timeout); self.radio.set_irq_cfg(&irq_cfg)?; - self.radio.set_rx(Timeout::DISABLED)?; + self.radio.set_buffer_base_address(0, 0)?; + + self.radio + .set_rx(Timeout::from_millis_sat(self.get_rx_window_duration_ms()))?; trace!("RX started"); loop { - let (status, irq_status) = self.irq_wait().await; + let (_status, irq_status) = self.irq_wait().await; - trace!("RX IRQ {:?}, {:?}", status, irq_status); if irq_status & Irq::RxDone.mask() != 0 { - let (status, len, ptr) = self.radio.rx_buffer_status()?; - + let (_status, len, ptr) = self.radio.rx_buffer_status()?; let packet_status = self.radio.lora_packet_status()?; let rssi = packet_status.rssi_pkt().to_integer(); let snr = packet_status.snr_pkt().to_integer(); - trace!( - "RX done. Received {} bytes. RX status: {:?}. Pkt status: {:?}", - len, - status.cmd(), - packet_status, - ); self.radio.read_buffer(ptr, &mut buf[..len as usize])?; self.radio.set_standby(StandbyClk::Rc)?; + + trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); return Ok((len as usize, RxQuality::new(rssi, snr as i8))); - } else if irq_status & (Irq::Timeout.mask() | Irq::TxDone.mask()) != 0 { + } + + if irq_status & Irq::Timeout.mask() != 0 { return Err(RadioError); } } @@ -194,7 +190,9 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio .clear_irq_status(irq_status) .expect("error clearing irq status"); - trace!("IRQ status: {=u16:b}", irq_status); + + trace!("SUGHZ IRQ 0b{=u16:b}, {:?}", irq_status, status); + if irq_status == 0 { Poll::Pending } else { @@ -268,7 +266,7 @@ pub trait RadioSwitch { fn set_tx(&mut self); } -fn convert_spreading_factor(sf: SpreadingFactor) -> SF { +fn convert_spreading_factor(sf: &SpreadingFactor) -> SF { match sf { SpreadingFactor::_7 => SF::Sf7, SpreadingFactor::_8 => SF::Sf8, @@ -279,7 +277,7 @@ fn convert_spreading_factor(sf: SpreadingFactor) -> SF { } } -fn convert_bandwidth(bw: Bandwidth) -> LoRaBandwidth { +fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth { match bw { Bandwidth::_125KHz => LoRaBandwidth::Bw125, Bandwidth::_250KHz => LoRaBandwidth::Bw250, From 308ca4b8e3c4a4ef6c1711528dd7561b75c61aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 8 Aug 2022 19:25:50 +0200 Subject: [PATCH 0056/1575] Use `pub(crate)` visibility for internal SPI SubGhz provides a public interface for the radio connected to internal SPI `#[allow(dead_code)]` is required for CI to succeed --- embassy-stm32/src/spi/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 1a00f4187..77609bd0f 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -181,7 +181,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { /// Useful for on chip peripherals like SUBGHZ which are hardwired. /// The bus can optionally be exposed externally with `Spi::new()` still. - pub fn new_internal( + #[allow(dead_code)] + pub(crate) fn new_internal( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, From af845b7d44228665dffc4432ba509fd80e8400b2 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 9 Aug 2022 12:17:30 +0200 Subject: [PATCH 0057/1575] Add impl for offset radio interface --- embassy-lora/src/lib.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 1b2dd45c2..2483dcb2e 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -11,13 +11,35 @@ pub mod stm32wl; #[cfg(feature = "sx127x")] pub mod sx127x; +#[cfg(feature = "time")] +use embassy_time::{Duration, Instant, Timer}; + /// A convenience timer to use with the LoRaWAN crate -pub struct LoraTimer; +#[cfg(feature = "time")] +pub struct LoraTimer { + start: Instant, +} + +#[cfg(feature = "time")] +impl LoraTimer { + pub fn new() -> Self { + Self { start: Instant::now() } + } +} #[cfg(feature = "time")] impl lorawan_device::async_device::radio::Timer for LoraTimer { + fn reset(&mut self) { + self.start = Instant::now(); + } + + type AtFuture<'m> = impl core::future::Future + 'm; + fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> { + Timer::at(self.start + Duration::from_millis(millis)) + } + type DelayFuture<'m> = impl core::future::Future + 'm; fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { - embassy_time::Timer::after(embassy_time::Duration::from_millis(millis)) + Timer::after(Duration::from_millis(millis)) } } From 1f36da5ca6d8bf70bba5d237b1c699ae8eae48e4 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 9 Aug 2022 12:18:07 +0200 Subject: [PATCH 0058/1575] Make settings configurable --- embassy-lora/src/stm32wl/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index c142353b6..b428e81b1 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -41,6 +41,8 @@ pub struct SubGhzRadio<'d, RS> { pub struct SubGhzRadioConfig { pub reg_mode: RegMode, pub calibrate_image: CalibrateImage, + pub pa_config: PaConfig, + pub tx_params: TxParams, } impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { @@ -155,8 +157,10 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio.set_buffer_base_address(0, 0)?; + // NOTE: Upper layer handles timeout by cancelling the future self.radio - .set_rx(Timeout::from_millis_sat(self.get_rx_window_duration_ms()))?; + .set_rx(Timeout::DISABLED)?; + trace!("RX started"); loop { @@ -221,8 +225,8 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf radio.calibrate(0x7F)?; radio.calibrate_image(config.calibrate_image)?; - radio.set_pa_config(&PaConfig::HP_14)?; - radio.set_tx_params(&TxParams::HP.set_ramp_time(RampTime::Micros40))?; + radio.set_pa_config(&config.pa_config)?; + radio.set_tx_params(&config.tx_params)?; radio.set_pa_ocp(Ocp::Max140m)?; radio.set_packet_type(PacketType::LoRa)?; From 5d114479ff1b123441725ff202744f80387dc731 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 9 Aug 2022 12:18:20 +0200 Subject: [PATCH 0059/1575] Adjust timings after offset calculation fix --- embassy-lora/src/stm32wl/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index b428e81b1..6013c2d99 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -258,10 +258,10 @@ impl From for RadioError { impl<'d, RS> Timings for SubGhzRadio<'d, RS> { fn get_rx_window_offset_ms(&self) -> i32 { - -200 + -500 } fn get_rx_window_duration_ms(&self) -> u32 { - 800 + 3000 } } From 6dab322c5819f89b41977bf4bad66bb8445af5f9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 9 Aug 2022 12:18:39 +0200 Subject: [PATCH 0060/1575] Use LP as default --- examples/stm32wl/src/bin/lorawan.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 83df0188a..9143e64da 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -37,7 +37,7 @@ impl<'a> embassy_lora::stm32wl::RadioSwitch for RadioSwitch<'a> { } fn set_tx(&mut self) { - self.ctrl1.set_low(); + self.ctrl1.set_high(); self.ctrl2.set_high(); self.ctrl3.set_high(); } @@ -69,7 +69,7 @@ async fn main(_spawner: Spawner) { // NOTE: This is specific for TTN, as they have a special RX1 delay region.set_receive_delay1(5000); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); // Depending on network, this might be part of JOIN device.set_datarate(region::DR::_0); // SF12 From 2636a8dc2e82afde0fb4e8952bb7f7014e11e5de Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 26 Aug 2022 12:44:33 +0200 Subject: [PATCH 0061/1575] Use released rust-lorawan with radio fixes --- embassy-lora/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 25d8c667c..a80557a89 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -35,5 +35,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } +lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } lorawan = { version = "0.7.1", default-features = false } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 11751a21d..6358fe865 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -14,7 +14,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} -lorawan-device = { version = "0.7.1", default-features = false, features = ["async"], 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" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5f6679f4b..e2e7d4078 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -10,7 +10,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } -lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } +lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } defmt = "0.3" From c30b38586a7663ad4c4654739364e36139c7217e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 26 Aug 2022 15:34:54 +0200 Subject: [PATCH 0062/1575] lora: Fix unused import warning --- embassy-lora/src/stm32wl/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 6013c2d99..9307cdebb 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -7,7 +7,7 @@ use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; use embassy_stm32::subghz::{ CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, - LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, + LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_sync::waitqueue::AtomicWaker; From 47069dfbe18df268966bc3526f515dc944d43021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 26 Aug 2022 15:37:20 +0200 Subject: [PATCH 0063/1575] lora: Fix for stm32l0 exampe to build --- examples/stm32l0/src/bin/lorawan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 303558b96..00ff67f3f 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); let region = region::EU868::default().into(); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer, Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); defmt::info!("Joining LoRaWAN network"); From 1ee2dfa03e6d3578ffaac810b49b726e7da9cf36 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Aug 2022 21:15:07 +0200 Subject: [PATCH 0064/1575] futures: derive defmt for Eithers --- embassy-futures/src/select.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-futures/src/select.rs b/embassy-futures/src/select.rs index 8cecb7fa0..53fa1da61 100644 --- a/embassy-futures/src/select.rs +++ b/embassy-futures/src/select.rs @@ -4,6 +4,7 @@ use core::task::{Context, Poll}; /// Result for [`select`]. #[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Either { /// First future finished first. First(A), @@ -60,6 +61,7 @@ where /// Result for [`select3`]. #[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Either3 { /// First future finished first. First(A), @@ -118,6 +120,7 @@ where /// Result for [`select4`]. #[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Either4 { /// First future finished first. First(A), From 3763baf8fa4cf332a81214eb1610afdaf5173824 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Aug 2022 21:15:30 +0200 Subject: [PATCH 0065/1575] futures: add select_slice, rename select_all to select_array. --- embassy-futures/README.md | 3 +- embassy-futures/src/select.rs | 62 +++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/embassy-futures/README.md b/embassy-futures/README.md index 971f4c835..3810bcafd 100644 --- a/embassy-futures/README.md +++ b/embassy-futures/README.md @@ -5,5 +5,6 @@ Utilities for working with futures: - [`select`](select::select) - waiting for one out of two futures to complete. - [`select3`](select::select3) - waiting for one out of three futures to complete. - [`select4`](select::select4) - waiting for one out of four futures to complete. -- [`select_all`](select::select_all) - waiting for one future in a list of futures to complete. +- [`select_array`](select::select_array) - waiting for one future in an array of futures to complete. +- [`select_slice`](select::select_slice) - waiting for one future in a slice of futures to complete. - [`yield_now`](yield_now::yield_now) - yielding the current task. diff --git a/embassy-futures/src/select.rs b/embassy-futures/src/select.rs index 53fa1da61..facc2f60b 100644 --- a/embassy-futures/src/select.rs +++ b/embassy-futures/src/select.rs @@ -186,28 +186,76 @@ where // ==================================================================== -/// Future for the [`select_all`] function. +/// Future for the [`select_array`] function. #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct SelectAll { +pub struct SelectArray { inner: [Fut; N], } -/// Creates a new future which will select over a list of futures. +/// Creates a new future which will select over an array of futures. /// -/// The returned future will wait for any future within `iter` to be ready. Upon +/// The returned future will wait for any future to be ready. Upon /// completion the item resolved will be returned, along with the index of the /// future that was ready. /// /// # Panics /// /// This function will panic if the array specified contains no items. -pub fn select_all(arr: [Fut; N]) -> SelectAll { +pub fn select_array(arr: [Fut; N]) -> SelectArray { assert!(N > 0); - SelectAll { inner: arr } + SelectArray { inner: arr } } -impl Future for SelectAll { +impl Future for SelectArray { + type Output = (Fut::Output, usize); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move, + // its elements also cannot move. Therefore it is safe to access `inner` and pin + // references to the contained futures. + let item = unsafe { + self.get_unchecked_mut() + .inner + .iter_mut() + .enumerate() + .find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) { + Poll::Pending => None, + Poll::Ready(e) => Some((i, e)), + }) + }; + + match item { + Some((idx, res)) => Poll::Ready((res, idx)), + None => Poll::Pending, + } + } +} + +// ==================================================================== + +/// Future for the [`select_slice`] function. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct SelectSlice<'a, Fut> { + inner: &'a mut [Fut], +} + +/// Creates a new future which will select over a slice of futures. +/// +/// The returned future will wait for any future to be ready. Upon +/// completion the item resolved will be returned, along with the index of the +/// future that was ready. +/// +/// # Panics +/// +/// This function will panic if the slice specified contains no items. +pub fn select_slice<'a, Fut: Future>(slice: &'a mut [Fut]) -> SelectSlice<'a, Fut> { + assert!(!slice.is_empty()); + SelectSlice { inner: slice } +} + +impl<'a, Fut: Future> Future for SelectSlice<'a, Fut> { type Output = (Fut::Output, usize); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { From 973f3b513fb85b7587312196d8f3aef75be2615f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Aug 2022 21:28:26 +0200 Subject: [PATCH 0066/1575] futures: make `select_(slice|array)` hang intead of panicking if empty. --- embassy-futures/src/select.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/embassy-futures/src/select.rs b/embassy-futures/src/select.rs index facc2f60b..c0dd7ecd3 100644 --- a/embassy-futures/src/select.rs +++ b/embassy-futures/src/select.rs @@ -199,11 +199,8 @@ pub struct SelectArray { /// completion the item resolved will be returned, along with the index of the /// future that was ready. /// -/// # Panics -/// -/// This function will panic if the array specified contains no items. +/// If the array is empty, the resulting future will be Pending forever. pub fn select_array(arr: [Fut; N]) -> SelectArray { - assert!(N > 0); SelectArray { inner: arr } } @@ -247,11 +244,8 @@ pub struct SelectSlice<'a, Fut> { /// completion the item resolved will be returned, along with the index of the /// future that was ready. /// -/// # Panics -/// -/// This function will panic if the slice specified contains no items. +/// If the slice is empty, the resulting future will be Pending forever. pub fn select_slice<'a, Fut: Future>(slice: &'a mut [Fut]) -> SelectSlice<'a, Fut> { - assert!(!slice.is_empty()); SelectSlice { inner: slice } } From 764ee3b72cc8770f7c398ba1aee41fbd34f07764 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Aug 2022 22:51:52 +0200 Subject: [PATCH 0067/1575] futures: add block_on --- embassy-futures/src/block_on.rs | 31 +++++++++++++++++++++++++++++++ embassy-futures/src/lib.rs | 2 ++ 2 files changed, 33 insertions(+) create mode 100644 embassy-futures/src/block_on.rs diff --git a/embassy-futures/src/block_on.rs b/embassy-futures/src/block_on.rs new file mode 100644 index 000000000..749fa67f3 --- /dev/null +++ b/embassy-futures/src/block_on.rs @@ -0,0 +1,31 @@ +use core::future::Future; +use core::pin::Pin; +use core::ptr; +use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + +static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| RawWaker::new(ptr::null(), &VTABLE), |_| {}, |_| {}, |_| {}); + +/// Run a future to completion using a busy loop. +/// +/// This calls `.poll()` on the future in a busy loop, which blocks +/// the current thread at 100% cpu usage until the future is done. The +/// future's `Waker` mechanism is not used. +/// +/// It's suitable for systems with no or limited concurrency and without +/// strict requirements around power consumption. For more complex use +/// cases, prefer using a "real" executor like `embassy-executor`, which +/// supports multiple tasks, and putting the core to sleep when no task +/// needs to do work. +pub fn block_on(mut fut: F) -> F::Output { + // safety: we don't move the future after this line. + let mut fut = unsafe { Pin::new_unchecked(&mut fut) }; + + let raw_waker = RawWaker::new(ptr::null(), &VTABLE); + let waker = unsafe { Waker::from_raw(raw_waker) }; + let mut cx = Context::from_waker(&waker); + loop { + if let Poll::Ready(res) = fut.as_mut().poll(&mut cx) { + return res; + } + } +} diff --git a/embassy-futures/src/lib.rs b/embassy-futures/src/lib.rs index 45bea2529..41e27047d 100644 --- a/embassy-futures/src/lib.rs +++ b/embassy-futures/src/lib.rs @@ -5,8 +5,10 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod block_on; mod select; mod yield_now; +pub use block_on::*; pub use select::*; pub use yield_now::*; From 2a0df652f37ac00ba3f568b457eabe4e352bc3c3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Aug 2022 22:52:31 +0200 Subject: [PATCH 0068/1575] futures: add joins --- embassy-futures/src/join.rs | 252 ++++++++++++++++++++++++++++++++++++ embassy-futures/src/lib.rs | 2 + 2 files changed, 254 insertions(+) create mode 100644 embassy-futures/src/join.rs diff --git a/embassy-futures/src/join.rs b/embassy-futures/src/join.rs new file mode 100644 index 000000000..39a78ccd3 --- /dev/null +++ b/embassy-futures/src/join.rs @@ -0,0 +1,252 @@ +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::{fmt, mem}; + +#[derive(Debug)] +enum MaybeDone { + /// A not-yet-completed future + Future(/* #[pin] */ Fut), + /// The output of the completed future + Done(Fut::Output), + /// The empty variant after the result of a [`MaybeDone`] has been + /// taken using the [`take_output`](MaybeDone::take_output) method. + Gone, +} + +impl MaybeDone { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool { + let this = unsafe { self.get_unchecked_mut() }; + match this { + Self::Future(fut) => match unsafe { Pin::new_unchecked(fut) }.poll(cx) { + Poll::Ready(res) => { + *this = Self::Done(res); + true + } + Poll::Pending => false, + }, + _ => true, + } + } + + fn take_output(&mut self) -> Fut::Output { + match &*self { + Self::Done(_) => {} + Self::Future(_) | Self::Gone => panic!("take_output when MaybeDone is not done."), + } + match mem::replace(self, Self::Gone) { + MaybeDone::Done(output) => output, + _ => unreachable!(), + } + } +} + +impl Unpin for MaybeDone {} + +macro_rules! generate { + ($( + $(#[$doc:meta])* + ($Join:ident, <$($Fut:ident),*>), + )*) => ($( + $(#[$doc])* + #[must_use = "futures do nothing unless you `.await` or poll them"] + #[allow(non_snake_case)] + pub struct $Join<$($Fut: Future),*> { + $( + $Fut: MaybeDone<$Fut>, + )* + } + + impl<$($Fut),*> fmt::Debug for $Join<$($Fut),*> + where + $( + $Fut: Future + fmt::Debug, + $Fut::Output: fmt::Debug, + )* + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct(stringify!($Join)) + $(.field(stringify!($Fut), &self.$Fut))* + .finish() + } + } + + impl<$($Fut: Future),*> $Join<$($Fut),*> { + #[allow(non_snake_case)] + fn new($($Fut: $Fut),*) -> Self { + Self { + $($Fut: MaybeDone::Future($Fut)),* + } + } + } + + impl<$($Fut: Future),*> Future for $Join<$($Fut),*> { + type Output = ($($Fut::Output),*); + + fn poll( + self: Pin<&mut Self>, cx: &mut Context<'_> + ) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let mut all_done = true; + $( + all_done &= unsafe { Pin::new_unchecked(&mut this.$Fut) }.poll(cx); + )* + + if all_done { + Poll::Ready(($(this.$Fut.take_output()), *)) + } else { + Poll::Pending + } + } + } + )*) +} + +generate! { + /// Future for the [`join`](join()) function. + (Join, ), + + /// Future for the [`join3`] function. + (Join3, ), + + /// Future for the [`join4`] function. + (Join4, ), + + /// Future for the [`join5`] function. + (Join5, ), +} + +/// Joins the result of two futures, waiting for them both to complete. +/// +/// This function will return a new future which awaits both futures to +/// complete. The returned future will finish with a tuple of both results. +/// +/// Note that this function consumes the passed futures and returns a +/// wrapped version of it. +/// +/// # Examples +/// +/// ``` +/// # embassy_futures::block_on(async { +/// +/// let a = async { 1 }; +/// let b = async { 2 }; +/// let pair = embassy_futures::join(a, b).await; +/// +/// assert_eq!(pair, (1, 2)); +/// # }); +/// ``` +pub fn join(future1: Fut1, future2: Fut2) -> Join +where + Fut1: Future, + Fut2: Future, +{ + Join::new(future1, future2) +} + +/// Joins the result of three futures, waiting for them all to complete. +/// +/// This function will return a new future which awaits all futures to +/// complete. The returned future will finish with a tuple of all results. +/// +/// Note that this function consumes the passed futures and returns a +/// wrapped version of it. +/// +/// # Examples +/// +/// ``` +/// # embassy_futures::block_on(async { +/// +/// let a = async { 1 }; +/// let b = async { 2 }; +/// let c = async { 3 }; +/// let res = embassy_futures::join3(a, b, c).await; +/// +/// assert_eq!(res, (1, 2, 3)); +/// # }); +/// ``` +pub fn join3(future1: Fut1, future2: Fut2, future3: Fut3) -> Join3 +where + Fut1: Future, + Fut2: Future, + Fut3: Future, +{ + Join3::new(future1, future2, future3) +} + +/// Joins the result of four futures, waiting for them all to complete. +/// +/// This function will return a new future which awaits all futures to +/// complete. The returned future will finish with a tuple of all results. +/// +/// Note that this function consumes the passed futures and returns a +/// wrapped version of it. +/// +/// # Examples +/// +/// ``` +/// # embassy_futures::block_on(async { +/// +/// let a = async { 1 }; +/// let b = async { 2 }; +/// let c = async { 3 }; +/// let d = async { 4 }; +/// let res = embassy_futures::join4(a, b, c, d).await; +/// +/// assert_eq!(res, (1, 2, 3, 4)); +/// # }); +/// ``` +pub fn join4( + future1: Fut1, + future2: Fut2, + future3: Fut3, + future4: Fut4, +) -> Join4 +where + Fut1: Future, + Fut2: Future, + Fut3: Future, + Fut4: Future, +{ + Join4::new(future1, future2, future3, future4) +} + +/// Joins the result of five futures, waiting for them all to complete. +/// +/// This function will return a new future which awaits all futures to +/// complete. The returned future will finish with a tuple of all results. +/// +/// Note that this function consumes the passed futures and returns a +/// wrapped version of it. +/// +/// # Examples +/// +/// ``` +/// # embassy_futures::block_on(async { +/// +/// let a = async { 1 }; +/// let b = async { 2 }; +/// let c = async { 3 }; +/// let d = async { 4 }; +/// let e = async { 5 }; +/// let res = embassy_futures::join5(a, b, c, d, e).await; +/// +/// assert_eq!(res, (1, 2, 3, 4, 5)); +/// # }); +/// ``` +pub fn join5( + future1: Fut1, + future2: Fut2, + future3: Fut3, + future4: Fut4, + future5: Fut5, +) -> Join5 +where + Fut1: Future, + Fut2: Future, + Fut3: Future, + Fut4: Future, + Fut5: Future, +{ + Join5::new(future1, future2, future3, future4, future5) +} diff --git a/embassy-futures/src/lib.rs b/embassy-futures/src/lib.rs index 41e27047d..ea135b3ab 100644 --- a/embassy-futures/src/lib.rs +++ b/embassy-futures/src/lib.rs @@ -6,9 +6,11 @@ pub(crate) mod fmt; mod block_on; +mod join; mod select; mod yield_now; pub use block_on::*; +pub use join::*; pub use select::*; pub use yield_now::*; From 598689ef43a5dec1aeb8948dd8b5d0e46a62ef81 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Aug 2022 22:57:35 +0200 Subject: [PATCH 0069/1575] futures: move select* and join* to separate modules. --- embassy-futures/src/join.rs | 10 ++++++---- embassy-futures/src/lib.rs | 7 +++---- embassy-futures/src/select.rs | 2 ++ embassy-usb/src/lib.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/embassy-futures/src/join.rs b/embassy-futures/src/join.rs index 39a78ccd3..7600d4b8a 100644 --- a/embassy-futures/src/join.rs +++ b/embassy-futures/src/join.rs @@ -1,3 +1,5 @@ +//! Wait for multiple futures to complete. + use core::future::Future; use core::pin::Pin; use core::task::{Context, Poll}; @@ -131,7 +133,7 @@ generate! { /// /// let a = async { 1 }; /// let b = async { 2 }; -/// let pair = embassy_futures::join(a, b).await; +/// let pair = embassy_futures::join::join(a, b).await; /// /// assert_eq!(pair, (1, 2)); /// # }); @@ -160,7 +162,7 @@ where /// let a = async { 1 }; /// let b = async { 2 }; /// let c = async { 3 }; -/// let res = embassy_futures::join3(a, b, c).await; +/// let res = embassy_futures::join::join3(a, b, c).await; /// /// assert_eq!(res, (1, 2, 3)); /// # }); @@ -191,7 +193,7 @@ where /// let b = async { 2 }; /// let c = async { 3 }; /// let d = async { 4 }; -/// let res = embassy_futures::join4(a, b, c, d).await; +/// let res = embassy_futures::join::join4(a, b, c, d).await; /// /// assert_eq!(res, (1, 2, 3, 4)); /// # }); @@ -229,7 +231,7 @@ where /// let c = async { 3 }; /// let d = async { 4 }; /// let e = async { 5 }; -/// let res = embassy_futures::join5(a, b, c, d, e).await; +/// let res = embassy_futures::join::join5(a, b, c, d, e).await; /// /// assert_eq!(res, (1, 2, 3, 4, 5)); /// # }); diff --git a/embassy-futures/src/lib.rs b/embassy-futures/src/lib.rs index ea135b3ab..8c769bdfc 100644 --- a/embassy-futures/src/lib.rs +++ b/embassy-futures/src/lib.rs @@ -6,11 +6,10 @@ pub(crate) mod fmt; mod block_on; -mod join; -mod select; mod yield_now; +pub mod join; +pub mod select; + pub use block_on::*; -pub use join::*; -pub use select::*; pub use yield_now::*; diff --git a/embassy-futures/src/select.rs b/embassy-futures/src/select.rs index c0dd7ecd3..97a81a86d 100644 --- a/embassy-futures/src/select.rs +++ b/embassy-futures/src/select.rs @@ -1,3 +1,5 @@ +//! Wait for the first of several futures to complete. + use core::future::Future; use core::pin::Pin; use core::task::{Context, Poll}; diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 5a3f8ba88..b165804ef 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -12,7 +12,7 @@ mod descriptor_reader; pub mod driver; pub mod types; -use embassy_futures::{select, Either}; +use embassy_futures::select::{select, Either}; use heapless::Vec; pub use self::builder::{Builder, Config}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index ba2159c72..7fdb0b685 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -8,7 +8,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; use embassy_executor::Spawner; -use embassy_futures::{select, Either}; +use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; From 4c0f1b6354b1f1b2f87a6876bfa5d3803804cbb9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Aug 2022 23:32:46 +0200 Subject: [PATCH 0070/1575] futures: add join_array. --- embassy-futures/src/join.rs | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/embassy-futures/src/join.rs b/embassy-futures/src/join.rs index 7600d4b8a..bc0cb5303 100644 --- a/embassy-futures/src/join.rs +++ b/embassy-futures/src/join.rs @@ -1,6 +1,7 @@ //! Wait for multiple futures to complete. use core::future::Future; +use core::mem::MaybeUninit; use core::pin::Pin; use core::task::{Context, Poll}; use core::{fmt, mem}; @@ -252,3 +253,70 @@ where { Join5::new(future1, future2, future3, future4, future5) } + +// ===================================================== + +/// Future for the [`join_array`] function. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct JoinArray { + futures: [MaybeDone; N], +} + +impl fmt::Debug for JoinArray +where + Fut: Future + fmt::Debug, + Fut::Output: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JoinArray").field("futures", &self.futures).finish() + } +} + +impl Future for JoinArray { + type Output = [Fut::Output; N]; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let mut all_done = true; + for f in this.futures.iter_mut() { + all_done &= unsafe { Pin::new_unchecked(f) }.poll(cx); + } + + if all_done { + let mut array: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + for i in 0..N { + array[i].write(this.futures[i].take_output()); + } + Poll::Ready(unsafe { (&array as *const _ as *const [Fut::Output; N]).read() }) + } else { + Poll::Pending + } + } +} + +/// Joins the result of an array of futures, waiting for them all to complete. +/// +/// This function will return a new future which awaits all futures to +/// complete. The returned future will finish with a tuple of all results. +/// +/// Note that this function consumes the passed futures and returns a +/// wrapped version of it. +/// +/// # Examples +/// +/// ``` +/// # embassy_futures::block_on(async { +/// +/// async fn foo(n: u32) -> u32 { n } +/// let a = foo(1); +/// let b = foo(2); +/// let c = foo(3); +/// let res = embassy_futures::join::join_array([a, b, c]).await; +/// +/// assert_eq!(res, [1, 2, 3]); +/// # }); +/// ``` +pub fn join_array(futures: [Fut; N]) -> JoinArray { + JoinArray { + futures: futures.map(MaybeDone::Future), + } +} From 6392f067172541a0cb9b3757a9c7a40d3b7c5e25 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 29 Aug 2022 00:54:14 +0200 Subject: [PATCH 0071/1575] futures: readme, docs improvements. --- embassy-futures/Cargo.toml | 5 ++++- embassy-futures/README.md | 32 +++++++++++++++++++++++++------- embassy-futures/src/block_on.rs | 2 ++ embassy-futures/src/yield_now.rs | 17 +++++++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/embassy-futures/Cargo.toml b/embassy-futures/Cargo.toml index e564f5a96..7123b7c61 100644 --- a/embassy-futures/Cargo.toml +++ b/embassy-futures/Cargo.toml @@ -6,9 +6,12 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-futures-v$VERSION/embassy-futures/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-futures/src/" -features = ["nightly"] +features = ["defmt"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["defmt"] + [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-futures/README.md b/embassy-futures/README.md index 3810bcafd..7add22c7b 100644 --- a/embassy-futures/README.md +++ b/embassy-futures/README.md @@ -1,10 +1,28 @@ # embassy-futures -Utilities for working with futures: +An [Embassy](https://embassy.dev) project. + +Utilities for working with futures, compatible with `no_std` and not using `alloc`. Optimized for code size, +ideal for embedded systems. + +- Future combinators, like [`join`](join) and [`select`](select) +- Utilities to use `async` without a fully fledged executor: [`block_on`](block_on::block_on) and [`yield_now`](yield_now::yield_now). + +## Interoperability + +Futures from this crate can run on any executor. + +## Minimum supported Rust version (MSRV) + +Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. + +## 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. -- [`select`](select::select) - waiting for one out of two futures to complete. -- [`select3`](select::select3) - waiting for one out of three futures to complete. -- [`select4`](select::select4) - waiting for one out of four futures to complete. -- [`select_array`](select::select_array) - waiting for one future in an array of futures to complete. -- [`select_slice`](select::select_slice) - waiting for one future in a slice of futures to complete. -- [`yield_now`](yield_now::yield_now) - yielding the current task. diff --git a/embassy-futures/src/block_on.rs b/embassy-futures/src/block_on.rs index 749fa67f3..da90351ec 100644 --- a/embassy-futures/src/block_on.rs +++ b/embassy-futures/src/block_on.rs @@ -11,6 +11,8 @@ static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| RawWaker::new(ptr::null( /// the current thread at 100% cpu usage until the future is done. The /// future's `Waker` mechanism is not used. /// +/// You can use this to run multiple futures concurrently with [`join`][crate::join]. +/// /// It's suitable for systems with no or limited concurrency and without /// strict requirements around power consumption. For more complex use /// cases, prefer using a "real" executor like `embassy-executor`, which diff --git a/embassy-futures/src/yield_now.rs b/embassy-futures/src/yield_now.rs index 1ebecb916..13b103778 100644 --- a/embassy-futures/src/yield_now.rs +++ b/embassy-futures/src/yield_now.rs @@ -3,6 +3,23 @@ use core::pin::Pin; use core::task::{Context, Poll}; /// Yield from the current task once, allowing other tasks to run. +/// +/// This can be used to easily and quickly implement simple async primitives +/// without using wakers. The following snippet will wait for a condition to +/// hold, while still allowing other tasks to run concurrently (not monopolizing +/// the executor thread). +/// +/// ```rust,no_run +/// while !some_condition() { +/// yield_now().await; +/// } +/// ``` +/// +/// The downside is this will spin in a busy loop, using 100% of the CPU, while +/// using wakers correctly would allow the CPU to sleep while waiting. +/// +/// The internal implementation is: on first poll the future wakes itself and +/// returns `Poll::Pending`. On second poll, it returns `Poll::Ready`. pub fn yield_now() -> impl Future { YieldNowFuture { yielded: false } } From 782751dfb9809a9de8530893b9ffa584e3173bae Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 29 Aug 2022 10:27:51 +0200 Subject: [PATCH 0072/1575] Add missing std feature for embassy-sync Fixes #930 --- embassy-sync/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 0d14bba55..14ab1d003 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -11,6 +11,7 @@ target = "thumbv7em-none-eabi" [features] nightly = ["embedded-io/async"] +std = [] [dependencies] defmt = { version = "0.3", optional = true } From c0b7fd910e5663242d9320dd9893dbc1844b2729 Mon Sep 17 00:00:00 2001 From: huntc Date: Tue, 30 Aug 2022 09:49:04 +1000 Subject: [PATCH 0073/1575] Additional doco --- embassy-nrf/src/saadc.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 43c98a888..69926ec03 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -224,6 +224,8 @@ impl<'d, const N: usize> Saadc<'d, N> { } /// One shot sampling. The buffer must be the same size as the number of channels configured. + /// The sampling is stopped prior to returning in order to reduce power consumption (power + /// consumption remains higher if sampling is not stopped explicitly). pub async fn sample(&mut self, buf: &mut [i16; N]) { let r = Self::regs(); From dcd8c62169fc92224e2672c621b7d0b846b59cdf Mon Sep 17 00:00:00 2001 From: huntc Date: Tue, 30 Aug 2022 20:56:56 +1000 Subject: [PATCH 0074/1575] Permits the future to be cancelled Includes documentation --- embassy-nrf/src/saadc.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 69926ec03..5f2ac64ef 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -3,6 +3,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; @@ -225,8 +226,12 @@ impl<'d, const N: usize> Saadc<'d, N> { /// One shot sampling. The buffer must be the same size as the number of channels configured. /// The sampling is stopped prior to returning in order to reduce power consumption (power - /// consumption remains higher if sampling is not stopped explicitly). + /// consumption remains higher if sampling is not stopped explicitly). Cancellation will + /// also cause the sampling to be stopped. pub async fn sample(&mut self, buf: &mut [i16; N]) { + // In case the future is dropped, stop the task and wait for it to end. + let on_drop = OnDrop::new(Self::stop_sampling_immediately); + let r = Self::regs(); // Set up the DMA @@ -259,6 +264,7 @@ impl<'d, const N: usize> Saadc<'d, N> { }) .await; + on_drop.defuse(); Self::stop_sampling().await; } @@ -279,6 +285,12 @@ impl<'d, const N: usize> Saadc<'d, N> { /// taken to acquire the samples into a single buffer. You should measure the /// time taken by the callback and set the sample buffer size accordingly. /// Exceeding this time can lead to samples becoming dropped. + /// + /// The sampling is stopped prior to returning in order to reduce power consumption (power + /// consumption remains higher if sampling is not stopped explicitly), and to + /// free the buffers from being used by the peripheral. Cancellation will + /// also cause the sampling to be stopped. + pub async fn run_task_sampler( &mut self, timer: &mut T, @@ -330,6 +342,9 @@ impl<'d, const N: usize> Saadc<'d, N> { I: FnMut(), S: FnMut(&[[i16; N]]) -> SamplerState, { + // In case the future is dropped, stop the task and wait for it to end. + let on_drop = OnDrop::new(Self::stop_sampling_immediately); + let r = Self::regs(); // Establish mode and sample rate @@ -413,10 +428,24 @@ impl<'d, const N: usize> Saadc<'d, N> { }) .await; + on_drop.defuse(); Self::stop_sampling().await; } - // Stop sampling and wait for it to stop + // Stop sampling and wait for it to stop in a blocking fashion + fn stop_sampling_immediately() { + let r = Self::regs(); + + compiler_fence(Ordering::SeqCst); + + r.events_stopped.reset(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + + while r.events_stopped.read().bits() == 0 {} + r.events_stopped.reset(); + } + + // Stop sampling and wait for it to stop in a non-blocking fashino async fn stop_sampling() { let r = Self::regs(); From 4781feafc43b640b870fbde5f9f9c78934628dd6 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Tue, 30 Aug 2022 15:27:25 +0200 Subject: [PATCH 0075/1575] Add split() method to BufferedUarte in embassy-nrf --- embassy-nrf/src/buffered_uarte.rs | 179 ++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 12 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 62af544ae..1bb75414d 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -13,6 +13,7 @@ //! //! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. +use core::cell::RefCell; use core::cmp::min; use core::future::Future; use core::sync::atomic::{compiler_fence, Ordering}; @@ -71,7 +72,7 @@ struct StateInner<'d, U: UarteInstance, T: TimerInstance> { /// Interface to a UARTE instance pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { - inner: PeripheralMutex<'d, StateInner<'d, U, T>>, + inner: RefCell>>, } impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} @@ -169,7 +170,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { ppi_ch2.enable(); Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { _peri: peri, timer, _ppi_ch1: ppi_ch1, @@ -182,13 +183,13 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { tx: RingBuffer::new(tx_buffer), tx_state: TxState::Idle, tx_waker: WakerRegistration::new(), - }), + })), } } /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { - self.inner.with(|state| { + self.inner.borrow_mut().with(|state| { let r = U::regs(); let timeout = 0x8000_0000 / (baudrate as u32 / 40); @@ -198,12 +199,35 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { r.baudrate.write(|w| w.baudrate().variant(baudrate)); }); } + + pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) { + ( + BufferedUarteRx { inner: &self.inner }, + BufferedUarteTx { inner: &&self.inner }, + ) + } +} + +pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { + inner: &'u RefCell>>, +} + +pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { + inner: &'u RefCell>>, } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { type Error = core::convert::Infallible; } +impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { + type Error = core::convert::Infallible; +} + +impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { + type Error = core::convert::Infallible; +} + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { type ReadFuture<'a> = impl Future> where @@ -212,7 +236,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { let mut do_pend = false; - let res = self.inner.with(|state| { + let res = self.inner.borrow_mut().with(|state| { compiler_fence(Ordering::SeqCst); trace!("poll_read"); @@ -232,7 +256,43 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe Poll::Pending }); if do_pend { - self.inner.pend(); + self.inner.borrow().pend(); + } + + res + }) + } +} + +impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.borrow_mut().with(|state| { + compiler_fence(Ordering::SeqCst); + trace!("poll_read"); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len()); + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + state.rx.pop(len); + do_pend = true; + return Poll::Ready(Ok(len)); + } + + trace!(" empty"); + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + if do_pend { + self.inner.borrow().pend(); } res @@ -247,7 +307,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for Bu fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { poll_fn(move |cx| { - self.inner.with(|state| { + self.inner.borrow_mut().with(|state| { compiler_fence(Ordering::SeqCst); trace!("fill_buf"); @@ -269,13 +329,53 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for Bu } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { + let signal = self.inner.borrow_mut().with(|state| { let full = state.rx.is_full(); state.rx.pop(amt); full }); if signal { - self.inner.pend(); + self.inner.borrow().pend(); + } + } +} + +impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { + compiler_fence(Ordering::SeqCst); + trace!("fill_buf"); + + // We have data ready in buffer? Return it. + let buf = state.rx.pop_buf(); + if !buf.is_empty() { + trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + trace!(" empty"); + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.borrow_mut().with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + self.inner.borrow().pend(); } } } @@ -287,7 +387,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let res = self.inner.with(|state| { + let res = self.inner.borrow_mut().with(|state| { trace!("poll_write: {:?}", buf.len()); let tx_buf = state.tx.push_buf(); @@ -308,7 +408,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff Poll::Ready(Ok(n)) }); - self.inner.pend(); + self.inner.borrow_mut().pend(); res }) @@ -320,7 +420,62 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { poll_fn(move |cx| { - self.inner.with(|state| { + self.inner.borrow_mut().with(|state| { + trace!("poll_flush"); + + if !state.tx.is_empty() { + trace!("poll_flush: pending"); + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} + +impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + poll_fn(move |cx| { + let res = self.inner.borrow_mut().with(|state| { + trace!("poll_write: {:?}", buf.len()); + + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + trace!("poll_write: pending"); + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + let n = min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + trace!("poll_write: queued {:?}", n); + + compiler_fence(Ordering::SeqCst); + + Poll::Ready(Ok(n)) + }); + + self.inner.borrow_mut().pend(); + + res + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { trace!("poll_flush"); if !state.tx.is_empty() { From b2720117c455bd0d3781f287446f86db2d94f8e4 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Tue, 30 Aug 2022 15:48:50 +0200 Subject: [PATCH 0076/1575] Deduplicate IO methods --- embassy-nrf/src/buffered_uarte.rs | 320 ++++++++++++------------------ 1 file changed, 126 insertions(+), 194 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 1bb75414d..385dc7e4e 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -201,19 +201,129 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) { - ( - BufferedUarteRx { inner: &self.inner }, - BufferedUarteTx { inner: &&self.inner }, - ) + (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) + } + + async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.borrow_mut().with(|state| { + compiler_fence(Ordering::SeqCst); + trace!("poll_read"); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len()); + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + state.rx.pop(len); + do_pend = true; + return Poll::Ready(Ok(len)); + } + + trace!(" empty"); + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + if do_pend { + self.inner.borrow().pend(); + } + + res + }) + .await + } + + async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { + poll_fn(move |cx| { + let res = self.inner.borrow_mut().with(|state| { + trace!("poll_write: {:?}", buf.len()); + + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + trace!("poll_write: pending"); + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + let n = min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + trace!("poll_write: queued {:?}", n); + + compiler_fence(Ordering::SeqCst); + + Poll::Ready(Ok(n)) + }); + + self.inner.borrow_mut().pend(); + + res + }) + .await + } + + async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { + trace!("poll_flush"); + + if !state.tx.is_empty() { + trace!("poll_flush: pending"); + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + .await + } + + async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { + compiler_fence(Ordering::SeqCst); + trace!("fill_buf"); + + // We have data ready in buffer? Return it. + let buf = state.rx.pop_buf(); + if !buf.is_empty() { + trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + trace!(" empty"); + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + .await + } + + fn inner_consume(&self, amt: usize) { + let signal = self.inner.borrow_mut().with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + self.inner.borrow().pend(); + } } } pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { - inner: &'u RefCell>>, + inner: &'u BufferedUarte<'d, U, T>, } pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { - inner: &'u RefCell>>, + inner: &'u BufferedUarte<'d, U, T>, } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { @@ -234,33 +344,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe Self: 'a; fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("poll_read"); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len()); - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - state.rx.pop(len); - do_pend = true; - return Poll::Ready(Ok(len)); - } - - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::Pending - }); - if do_pend { - self.inner.borrow().pend(); - } - - res - }) + self.inner_read(buf) } } @@ -270,33 +354,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read f Self: 'a; fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("poll_read"); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len()); - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - state.rx.pop(len); - do_pend = true; - return Poll::Ready(Ok(len)); - } - - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::Pending - }); - if do_pend { - self.inner.borrow().pend(); - } - - res - }) + self.inner.inner_read(buf) } } @@ -306,37 +364,11 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for Bu Self: 'a; fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("fill_buf"); - - // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); - if !buf.is_empty() { - trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) - }) + self.inner_fill_buf() } fn consume(&mut self, amt: usize) { - let signal = self.inner.borrow_mut().with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - self.inner.borrow().pend(); - } + self.inner_consume(amt) } } @@ -346,37 +378,11 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRea Self: 'a; fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("fill_buf"); - - // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); - if !buf.is_empty() { - trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) - }) + self.inner.inner_fill_buf() } fn consume(&mut self, amt: usize) { - let signal = self.inner.borrow_mut().with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - self.inner.borrow().pend(); - } + self.inner.inner_consume(amt) } } @@ -386,32 +392,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff Self: 'a; fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - poll_fn(move |cx| { - let res = self.inner.borrow_mut().with(|state| { - trace!("poll_write: {:?}", buf.len()); - - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - trace!("poll_write: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - let n = min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - trace!("poll_write: queued {:?}", n); - - compiler_fence(Ordering::SeqCst); - - Poll::Ready(Ok(n)) - }); - - self.inner.borrow_mut().pend(); - - res - }) + self.inner_write(buf) } type FlushFuture<'a> = impl Future> @@ -419,19 +400,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - trace!("poll_flush"); - - if !state.tx.is_empty() { - trace!("poll_flush: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + self.inner_flush() } } @@ -441,32 +410,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write Self: 'a; fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - poll_fn(move |cx| { - let res = self.inner.borrow_mut().with(|state| { - trace!("poll_write: {:?}", buf.len()); - - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - trace!("poll_write: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - let n = min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - trace!("poll_write: queued {:?}", n); - - compiler_fence(Ordering::SeqCst); - - Poll::Ready(Ok(n)) - }); - - self.inner.borrow_mut().pend(); - - res - }) + self.inner.inner_write(buf) } type FlushFuture<'a> = impl Future> @@ -474,19 +418,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - trace!("poll_flush"); - - if !state.tx.is_empty() { - trace!("poll_flush: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + self.inner.inner_flush() } } From 171077bacf2a289dae115e0db00c37f3a721df53 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Tue, 30 Aug 2022 15:57:38 +0200 Subject: [PATCH 0077/1575] Avoid double-borrow --- embassy-nrf/src/buffered_uarte.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 385dc7e4e..c3cba2470 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -207,7 +207,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { poll_fn(move |cx| { let mut do_pend = false; - let res = self.inner.borrow_mut().with(|state| { + let mut inner = self.inner.borrow_mut(); + let res = inner.with(|state| { compiler_fence(Ordering::SeqCst); trace!("poll_read"); @@ -227,7 +228,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { Poll::Pending }); if do_pend { - self.inner.borrow().pend(); + inner.pend(); } res @@ -237,7 +238,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { poll_fn(move |cx| { - let res = self.inner.borrow_mut().with(|state| { + let mut inner = self.inner.borrow_mut(); + let res = inner.with(|state| { trace!("poll_write: {:?}", buf.len()); let tx_buf = state.tx.push_buf(); @@ -258,7 +260,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { Poll::Ready(Ok(n)) }); - self.inner.borrow_mut().pend(); + inner.pend(); res }) @@ -307,13 +309,14 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } fn inner_consume(&self, amt: usize) { - let signal = self.inner.borrow_mut().with(|state| { + let mut inner = self.inner.borrow_mut(); + let signal = inner.with(|state| { let full = state.rx.is_full(); state.rx.pop(amt); full }); if signal { - self.inner.borrow().pend(); + inner.pend(); } } } From 92ed95780df854b136eaf0095b51badd34f7581b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 30 Aug 2022 19:29:59 +0200 Subject: [PATCH 0078/1575] futures: cargo.toml metadata --- embassy-futures/Cargo.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-futures/Cargo.toml b/embassy-futures/Cargo.toml index 7123b7c61..89bb3af0b 100644 --- a/embassy-futures/Cargo.toml +++ b/embassy-futures/Cargo.toml @@ -2,6 +2,16 @@ name = "embassy-futures" version = "0.1.0" edition = "2021" +description = "no-std, no-alloc utilities for working with futures" +repository = "https://github.com/embassy-rs/embassy" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", + "concurrency", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-futures-v$VERSION/embassy-futures/src/" From 464ae67108c74efe38326b6ac8fedd29857e9412 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 30 Aug 2022 19:25:36 +0200 Subject: [PATCH 0079/1575] net: feature-gate nightly-only async traits to allow building on stable. --- ci.sh | 5 + ci_stable.sh | 2 + embassy-net/Cargo.toml | 8 +- embassy-net/src/lib.rs | 3 +- embassy-net/src/tcp.rs | 120 +++++++++++++--------- examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/usb_ethernet.rs | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 3 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 7 +- examples/stm32l5/src/bin/usb_serial.rs | 1 - 16 files changed, 99 insertions(+), 66 deletions(-) diff --git a/ci.sh b/ci.sh index 77a8a7e27..0e2af1e4c 100755 --- a/ci.sh +++ b/ci.sh @@ -36,6 +36,10 @@ 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-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ @@ -94,6 +98,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \ --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ --- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \ + --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- 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 \ diff --git a/ci_stable.sh b/ci_stable.sh index d388cfee3..02e630520 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -13,6 +13,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2143f36d3..64d78566f 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -16,6 +16,9 @@ std = [] defmt = ["dep:defmt", "smoltcp/defmt"] +nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"] +unstable-traits = [] + udp = ["smoltcp/socket-udp"] tcp = ["smoltcp/socket-tcp"] dns = ["smoltcp/socket-dns"] @@ -30,7 +33,6 @@ pool-16 = [] pool-32 = [] pool-64 = [] pool-128 = [] -unstable-traits = [] [dependencies] @@ -39,7 +41,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.0", features = [ "async" ] } +embedded-io = { version = "0.3.0", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } @@ -49,7 +51,7 @@ 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 = "0.2.0" +embedded-nal-async = { version = "0.2.0", optional = true } [dependencies.smoltcp] version = "0.8.0" diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 83d364715..8eebc798e 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::new_without_default)] -#![feature(generic_associated_types, type_alias_impl_trait)] +#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 910772c7d..0fa873602 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,5 +1,4 @@ use core::cell::UnsafeCell; -use core::future::Future; use core::mem; use core::task::Poll; @@ -55,6 +54,18 @@ pub struct TcpWriter<'a> { io: TcpIo<'a>, } +impl<'a> TcpReader<'a> { + pub async fn read(&mut self, buf: &mut [u8]) -> Result { + self.io.read(buf).await + } +} + +impl<'a> TcpWriter<'a> { + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.io.write(buf).await + } +} + 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. @@ -129,6 +140,14 @@ impl<'a> TcpSocket<'a> { .await } + pub async fn read(&mut self, buf: &mut [u8]) -> Result { + self.io.read(buf).await + } + + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.io.write(buf).await + } + pub fn set_timeout(&mut self, duration: Option) { unsafe { self.io.with_mut(|s, _| s.set_timeout(duration)) } } @@ -241,6 +260,7 @@ impl<'d> TcpIo<'d> { .await } + #[allow(unused)] async fn flush(&mut self) -> Result<(), Error> { poll_fn(move |_| { Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? @@ -249,88 +269,96 @@ impl<'d> TcpIo<'d> { } } -impl embedded_io::Error for ConnectError { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other +#[cfg(feature = "nightly")] +mod embedded_io_impls { + use core::future::Future; + + use super::*; + + impl embedded_io::Error for ConnectError { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } } -} -impl embedded_io::Error for Error { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other + impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } } -} -impl<'d> embedded_io::Io for TcpSocket<'d> { - type Error = Error; -} + impl<'d> embedded_io::Io for TcpSocket<'d> { + type Error = Error; + } -impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { - type ReadFuture<'a> = impl Future> + impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { + type ReadFuture<'a> = impl Future> where Self: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.io.read(buf) + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.io.read(buf) + } } -} -impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { - type WriteFuture<'a> = impl Future> + impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { + type WriteFuture<'a> = impl Future> where Self: 'a; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.io.write(buf) - } + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.io.write(buf) + } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.io.flush() + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + self.io.flush() + } } -} -impl<'d> embedded_io::Io for TcpReader<'d> { - type Error = Error; -} + impl<'d> embedded_io::Io for TcpReader<'d> { + type Error = Error; + } -impl<'d> embedded_io::asynch::Read for TcpReader<'d> { - type ReadFuture<'a> = impl Future> + impl<'d> embedded_io::asynch::Read for TcpReader<'d> { + type ReadFuture<'a> = impl Future> where Self: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.io.read(buf) + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.io.read(buf) + } } -} -impl<'d> embedded_io::Io for TcpWriter<'d> { - type Error = Error; -} + impl<'d> embedded_io::Io for TcpWriter<'d> { + type Error = Error; + } -impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { - type WriteFuture<'a> = impl Future> + impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { + type WriteFuture<'a> = impl Future> where Self: 'a; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.io.write(buf) - } + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.io.write(buf) + } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - self.io.flush() + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + self.io.flush() + } } } -#[cfg(feature = "unstable-traits")] +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] pub mod client { + use core::future::Future; use core::mem::MaybeUninit; use core::ptr::NonNull; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index b0af0c86e..b0190acc8 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index ca6c7e0d1..352660b59 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -18,7 +18,7 @@ use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; -use embedded_io::asynch::{Read, Write}; +use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 72a3a057d..18a92b094 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } defmt = "0.3" diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 2cb0010f1..2df7f62f6 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -16,7 +16,7 @@ use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; -use embedded_io::asynch::{Read, Write}; +use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index c7cec6b19..dbfd9d625 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 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", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } +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.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a446fe3fb..53ab2f5e7 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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-32768hz"] } 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", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embedded-io = { version = "0.3.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 0f76f3226..18b61120b 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "unstable-traits", "tick-32768hz"] } 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", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } embedded-io = { version = "0.3.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 624c73c26..ea3d5ab88 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -14,7 +14,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } usbd-hid = "0.5.2" defmt = "0.3" diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 3286f5c4d..2c8706e41 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -12,14 +12,13 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; use embassy_stm32::rcc::*; use embassy_stm32::rng::Rng; -use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use embassy_usb::{Builder, UsbDevice}; use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; -use embedded_io::asynch::{Read, Write}; +use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index f7e3d93e3..7d763e7fd 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -6,9 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::*; -use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; -use embassy_stm32::{interrupt, Config, Peripherals}; +use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; @@ -111,11 +110,11 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } - fn set_idle(&self, id: Option, dur: Duration) { + fn set_idle_ms(&self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } - fn get_idle(&self, id: Option) -> Option { + fn get_idle_ms(&self, id: Option) -> Option { info!("Get idle rate for {:?}", id); None } diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 323db6557..b576a7353 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -5,7 +5,6 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_stm32::rcc::*; -use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_usb::driver::EndpointError; From acaa8b3e8b80ccd49e04a6dc7d595d3a52d1ad0d Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 30 Aug 2022 20:36:57 +0000 Subject: [PATCH 0080/1575] Fix calculation of slice index total_len is already rounded up, so the `+ 3` is not needed. And even if it was, the calculation should have been `((total_len + 3) / 4)`. `(total_len + 3 / 4)` is equivalent to `total_len` and can overflow the slice, leading to a panic which can easily be triggered by sending large ICMP ECHO packets to the device. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8f439cf2f..34170a266 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -762,7 +762,7 @@ where let bus = unsafe { &mut *bus }; async { bus.write(&[cmd]).await?; - bus.write(&buf[..(total_len + 3 / 4)]).await?; + bus.write(&buf[..(total_len / 4)]).await?; Ok(()) } }) From 30641d05640a7a3cd4d71c70c09694a3eef825e0 Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 31 Aug 2022 08:44:28 +1000 Subject: [PATCH 0081/1575] Avoid context switch and wait for stop Should be more efficient given the sub 100 cycles to wait. --- embassy-nrf/src/saadc.rs | 49 ++-------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 5f2ac64ef..9bc89eb38 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -184,11 +184,6 @@ impl<'d, const N: usize> Saadc<'d, N> { r.intenclr.write(|w| w.started().clear()); WAKER.wake(); } - - if r.events_stopped.read().bits() != 0 { - r.intenclr.write(|w| w.stopped().clear()); - WAKER.wake(); - } } fn regs() -> &'static saadc::RegisterBlock { @@ -230,7 +225,7 @@ impl<'d, const N: usize> Saadc<'d, N> { /// also cause the sampling to be stopped. pub async fn sample(&mut self, buf: &mut [i16; N]) { // In case the future is dropped, stop the task and wait for it to end. - let on_drop = OnDrop::new(Self::stop_sampling_immediately); + OnDrop::new(Self::stop_sampling_immediately); let r = Self::regs(); @@ -263,9 +258,6 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; - - on_drop.defuse(); - Self::stop_sampling().await; } /// Continuous sampling with double buffers. @@ -343,7 +335,7 @@ impl<'d, const N: usize> Saadc<'d, N> { S: FnMut(&[[i16; N]]) -> SamplerState, { // In case the future is dropped, stop the task and wait for it to end. - let on_drop = OnDrop::new(Self::stop_sampling_immediately); + OnDrop::new(Self::stop_sampling_immediately); let r = Self::regs(); @@ -427,9 +419,6 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; - - on_drop.defuse(); - Self::stop_sampling().await; } // Stop sampling and wait for it to stop in a blocking fashion @@ -444,40 +433,6 @@ impl<'d, const N: usize> Saadc<'d, N> { while r.events_stopped.read().bits() == 0 {} r.events_stopped.reset(); } - - // Stop sampling and wait for it to stop in a non-blocking fashino - async fn stop_sampling() { - let r = Self::regs(); - - // Reset and enable the events - - compiler_fence(Ordering::SeqCst); - - r.events_stopped.reset(); - r.intenset.write(|w| { - w.stopped().set(); - w - }); - - // Stop - - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - - // Wait for 'stopped' event. - poll_fn(|cx| { - let r = Self::regs(); - - WAKER.register(cx.waker()); - - if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); - return Poll::Ready(()); - } - - Poll::Pending - }) - .await; - } } impl<'d> Saadc<'d, 1> { From 8ba421f324f0971b2394f497d8fbbee65847f583 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 Aug 2022 02:46:52 +0200 Subject: [PATCH 0082/1575] Do not use cfg_if for embedded-hal-async feature gates. Old code used `cfg_if!` because rustc still parses code inside disabled cfg's, and Rust stable at that time couldn't parse the new GAT where-clause location. This is not the case anymore. --- embassy-nrf/src/gpiote.rs | 105 +++++++++++++++--------------- embassy-nrf/src/spim.rs | 74 +++++++++++---------- embassy-nrf/src/twim.rs | 60 ++++++++--------- embassy-nrf/src/uarte.rs | 115 +++++++++++++++++---------------- embassy-rp/src/uart.rs | 77 +++++++++++----------- embassy-stm32/src/exti.rs | 49 +++++++------- embassy-stm32/src/i2c/v2.rs | 67 ++++++++++--------- embassy-stm32/src/spi/mod.rs | 82 +++++++++++------------ embassy-stm32/src/usart/mod.rs | 97 ++++++++++++++------------- embassy-time/src/delay.rs | 32 ++++----- 10 files changed, 380 insertions(+), 378 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index b52035705..d99f592b0 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -469,72 +469,73 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use futures::FutureExt; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use futures::FutureExt; - impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; + use super::*; - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) - } + impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> { + type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - 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) - } - - 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) - } - - 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) - } - - 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) - } + fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { + self.wait_for_high().map(Ok) } - impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; + type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) - } + fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { + self.wait_for_low().map(Ok) + } - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; + type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) - } + fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { + self.wait_for_rising_edge().map(Ok) + } - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; + type WaitForFallingEdgeFuture<'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) - } + fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { + self.wait_for_falling_edge().map(Ok) + } - type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; + type WaitForAnyEdgeFuture<'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) - } + fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { + self.wait_for_any_edge().map(Ok) + } + } - type WaitForAnyEdgeFuture<'a> = impl Future> + 'a where Self: 'a; + 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_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(Ok) - } + fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { + self.wait_for_high().map(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) + } + + 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) + } + + 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) + } + + 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) } } } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index be2fc02fc..2955182e4 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -475,49 +475,47 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use core::future::Future; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use core::future::Future; - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + use super::*; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + 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(()) } + } + } + + 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) + } + } + + 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) + } + } + + 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) } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spim<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(words) - } - } - - 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) - } - } - - 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) - } - - 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) - } + fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { + self.transfer_in_place(words) } } } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 850f6d0fa..3d4af753a 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -838,43 +838,43 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +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) - } + fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(address, buffer) + } - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + 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) - } + fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(address, bytes) + } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; + type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - 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) - } + 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) + } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; + type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; - fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { - let _ = address; - let _ = operations; - async move { todo!() } - } + fn transaction<'a, 'b>( + &'a mut self, + address: u8, + operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], + ) -> Self::TransactionFuture<'a, 'b> { + let _ = address; + let _ = operations; + async move { todo!() } } } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 4347ea558..c250e24ca 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1073,78 +1073,79 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] { - use core::future::Future; +#[cfg(all( + feature = "unstable-traits", + feature = "nightly", + feature = "_todo_embedded_hal_serial" +))] +mod eha { + use core::future::Future; - impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + use super::*; - fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buffer) - } + impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, 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, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, 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) } - impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buffer) - } + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } + } + } - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buffer) } - impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buffer) - } + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } + } + } - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + 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::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) } - impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + type FlushFuture<'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::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<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } } } } diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index 4a3c7a0ce..987b716b4 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -477,56 +477,57 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] { - use core::future::Future; +#[cfg(all( + feature = "unstable-traits", + feature = "nightly", + feature = "_todo_embedded_hal_serial" +))] +mod eha { + use core::future::Future; - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + use super::*; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buf) } - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(buf) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buf) } - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } } + } - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(buf) } } } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 935149b13..07c96ead0 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -165,40 +165,41 @@ mod eh1 { } } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use futures::FutureExt; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use futures::FutureExt; - impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for ExtiInput<'d, T> { - type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; + use super::*; - fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { - self.wait_for_high().map(Ok) - } + impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for ExtiInput<'d, T> { + type WaitForHighFuture<'a> = impl Future> + 'a where Self: 'a; - type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; + fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> { + self.wait_for_high().map(Ok) + } - fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { - self.wait_for_low().map(Ok) - } + type WaitForLowFuture<'a> = impl Future> + 'a where Self: 'a; - type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; + fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> { + self.wait_for_low().map(Ok) + } - fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> { - self.wait_for_rising_edge().map(Ok) - } + type WaitForRisingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - type WaitForFallingEdgeFuture<'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) + } - fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> { - self.wait_for_falling_edge().map(Ok) - } + type WaitForFallingEdgeFuture<'a> = impl Future> + 'a where Self: 'a; - type WaitForAnyEdgeFuture<'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) + } - fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> { - self.wait_for_any_edge().map(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) } } } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 07a3105da..db4924461 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -927,46 +927,45 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use super::{RxDma, TxDma}; - use core::future::Future; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use core::future::Future; - 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; + use super::super::{RxDma, TxDma}; + use super::*; - fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(address, buffer) - } + 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; - 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) - } + fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(address, buffer) + } - type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; - 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) - } + 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) + } - type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; + type WriteReadFuture<'a> = impl Future> + 'a where Self: 'a; + 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) + } - fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Self::TransactionFuture<'a, 'b> { - let _ = address; - let _ = operations; - async move { todo!() } - } + type TransactionFuture<'a, 'b> = impl Future> + 'a where Self: 'a, 'b: 'a; + + fn transaction<'a, 'b>( + &'a mut self, + address: u8, + operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], + ) -> Self::TransactionFuture<'a, 'b> { + let _ = address; + let _ = operations; + async move { todo!() } } } } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 26fb392ef..acc29d87e 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -870,54 +870,48 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use core::future::Future; - 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; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use core::future::Future; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async { Ok(()) } - } + 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(()) } + } + } + + 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) + } + } + + 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) + } + } + + 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) } - 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; + type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, data: &'a [W]) -> Self::WriteFuture<'a> { - self.write(data) - } - } - - 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) - } - } - - 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) - } - - 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) - } + fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a> { + self.transfer_in_place(words) } } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 2ad85c675..6c2668748 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -361,64 +361,69 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] { - use core::future::Future; +#[cfg(all( + feature = "unstable-traits", + feature = "nightly", + feature = "_todo_embedded_hal_serial" +))] +mod eha { + use core::future::Future; - impl<'d, T: BasicInstance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma> - where - TxDma: crate::usart::TxDma, - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + use super::*; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } + impl<'d, T: BasicInstance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma> + where + TxDma: crate::usart::TxDma, + { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buf) } - impl<'d, T: BasicInstance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma> - where - RxDma: crate::usart::RxDma, - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } + } + } + + impl<'d, T: BasicInstance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma> + where + RxDma: crate::usart::RxDma, + { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(buf) + } + } + + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma> + where + TxDma: crate::usart::TxDma, + { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(buf) } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma> - where - TxDma: crate::usart::TxDma, - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async move { Ok(()) } } + } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Read for Uart<'d, T, TxDma, RxDma> - where - RxDma: crate::usart::RxDma, - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Read for Uart<'d, T, TxDma, RxDma> + where + RxDma: crate::usart::RxDma, + { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(buf) } } } diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs index d010fff98..0a7982963 100644 --- a/embassy-time/src/delay.rs +++ b/embassy-time/src/delay.rs @@ -31,26 +31,28 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use crate::Timer; - use core::future::Future; - use futures_util::FutureExt; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use core::future::Future; - impl embedded_hal_async::delay::DelayUs for Delay { - type Error = core::convert::Infallible; + use futures_util::FutureExt; - type DelayUsFuture<'a> = impl Future> + 'a where Self: 'a; + use super::*; + use crate::Timer; - fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { - Timer::after(Duration::from_micros(micros as _)).map(Ok) - } + impl embedded_hal_async::delay::DelayUs for Delay { + type Error = core::convert::Infallible; - type DelayMsFuture<'a> = impl Future> + 'a where Self: 'a; + type DelayUsFuture<'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) - } + fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> { + Timer::after(Duration::from_micros(micros as _)).map(Ok) + } + + 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) } } } From e7d4bf258a8cc30d650d7babc80c79a672052549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 29 Aug 2022 00:30:50 +0200 Subject: [PATCH 0083/1575] dma --- embassy-rp/src/spi.rs | 91 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 16 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index d0261598e..c8589dd75 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -1,7 +1,10 @@ +use core::marker::PhantomData; + use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; +use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::{pac, peripherals, Peripheral}; @@ -30,8 +33,11 @@ impl Default for Config { } } -pub struct Spi<'d, T: Instance> { +pub struct Spi<'d, T: Instance, M: Mode> { inner: PeripheralRef<'d, T>, + tx_dma: Option>, + rx_dma: Option>, + phantom: PhantomData<(&'d mut T, M)>, } fn div_roundup(a: u32, b: u32) -> u32 { @@ -57,9 +63,11 @@ fn calc_prescs(freq: u32) -> (u8, u8) { ((presc * 2) as u8, (postdiv - 1) as u8) } -impl<'d, T: Instance> Spi<'d, T> { +impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { pub fn new( inner: impl Peripheral

+ 'd, + tx_dma: Option>, + rx_dma: Option>, clk: impl Peripheral

+ 'd> + 'd, mosi: impl Peripheral

+ 'd> + 'd, miso: impl Peripheral

+ 'd> + 'd, @@ -68,6 +76,8 @@ impl<'d, T: Instance> Spi<'d, T> { into_ref!(clk, mosi, miso); Self::new_inner( inner, + tx_dma, + rx_dma, Some(clk.map_into()), Some(mosi.map_into()), Some(miso.map_into()), @@ -78,26 +88,48 @@ impl<'d, T: Instance> Spi<'d, T> { pub fn new_txonly( inner: impl Peripheral

+ 'd, + tx_dma: Option>, clk: impl Peripheral

+ 'd> + 'd, mosi: impl Peripheral

+ 'd> + 'd, config: Config, ) -> Self { into_ref!(clk, mosi); - Self::new_inner(inner, Some(clk.map_into()), Some(mosi.map_into()), None, None, config) + Self::new_inner( + inner, + tx_dma, + None, + Some(clk.map_into()), + Some(mosi.map_into()), + None, + None, + config, + ) } pub fn new_rxonly( inner: impl Peripheral

+ 'd, + rx_dma: Option>, clk: impl Peripheral

+ 'd> + 'd, miso: impl Peripheral

+ 'd> + 'd, config: Config, ) -> Self { into_ref!(clk, miso); - Self::new_inner(inner, Some(clk.map_into()), None, Some(miso.map_into()), None, config) + Self::new_inner( + inner, + None, + rx_dma, + Some(clk.map_into()), + None, + Some(miso.map_into()), + None, + config, + ) } fn new_inner( inner: impl Peripheral

+ 'd, + tx_dma: Option>, + rx_dma: Option>, clk: Option>, mosi: Option>, miso: Option>, @@ -134,7 +166,12 @@ impl<'d, T: Instance> Spi<'d, T> { pin.io().ctrl().write(|w| w.set_funcsel(1)); } } - Self { inner } + Self { + inner, + tx_dma, + rx_dma, + phantom: PhantomData, + } } pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { @@ -228,16 +265,25 @@ impl<'d, T: Instance> Spi<'d, T> { mod sealed { use super::*; + pub trait Mode {} + pub trait Instance { + const TX_DREQ: u8; + const RX_DREQ: u8; + fn regs(&self) -> pac::spi::Spi; } } +pub trait Mode: sealed::Mode {} pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident) => { + ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$type { + const TX_DREQ: u8 = $tx_dreq; + const RX_DREQ: u8 = $rx_dreq; + fn regs(&self) -> pac::spi::Spi { pac::$type } @@ -246,8 +292,8 @@ macro_rules! impl_instance { }; } -impl_instance!(SPI0, Spi0); -impl_instance!(SPI1, Spi1); +impl_instance!(SPI0, Spi0, 16, 17); +impl_instance!(SPI1, Spi1, 18, 19); pub trait ClkPin: GpioPin {} pub trait CsPin: GpioPin {} @@ -281,12 +327,25 @@ impl_pin!(PIN_17, SPI0, CsPin); impl_pin!(PIN_18, SPI0, ClkPin); impl_pin!(PIN_19, SPI0, MosiPin); +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Blocking; +pub struct Async; + +impl_mode!(Blocking); +impl_mode!(Async); + // ==================== mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Transfer for Spi<'d, T, M> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { self.blocking_transfer_in_place(words)?; @@ -294,7 +353,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Write for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Write for Spi<'d, T, M> { type Error = Error; fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { @@ -313,29 +372,29 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::spi::ErrorType for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T, M> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T, M> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T, M> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -346,7 +405,7 @@ mod eh1 { } } -impl<'d, T: Instance> SetConfig for Spi<'d, T> { +impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> { type Config = Config; fn set_config(&mut self, config: &Self::Config) { let p = self.inner.regs(); From 07c64d902e001ab0943382e9da35f9280a5533d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 30 Aug 2022 00:30:47 +0200 Subject: [PATCH 0084/1575] example --- embassy-rp/src/spi.rs | 84 ++++++++++++++++++++++++++++++-- examples/rp/src/bin/spi.rs | 4 +- examples/rp/src/bin/spi_async.rs | 31 ++++++++++++ 3 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 examples/rp/src/bin/spi_async.rs diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index c8589dd75..a91a1fd19 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -3,6 +3,7 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; +use futures::future::join; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin as _; @@ -64,10 +65,8 @@ fn calc_prescs(freq: u32) -> (u8, u8) { } impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { - pub fn new( + pub fn new_blocking( inner: impl Peripheral

+ 'd, - tx_dma: Option>, - rx_dma: Option>, clk: impl Peripheral

+ 'd> + 'd, mosi: impl Peripheral

+ 'd> + 'd, miso: impl Peripheral

+ 'd> + 'd, @@ -76,8 +75,8 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { into_ref!(clk, mosi, miso); Self::new_inner( inner, - tx_dma, - rx_dma, + None, + None, Some(clk.map_into()), Some(mosi.map_into()), Some(miso.map_into()), @@ -262,6 +261,81 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { } } +impl<'d, T: Instance> Spi<'d, T, Async> { + pub fn new( + inner: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd> + 'd, + mosi: impl Peripheral

+ 'd> + 'd, + miso: impl Peripheral

+ 'd> + 'd, + config: Config, + ) -> Self { + into_ref!(tx_dma, rx_dma, clk, mosi, miso); + Self::new_inner( + inner, + Some(tx_dma.map_into()), + Some(rx_dma.map_into()), + Some(clk.map_into()), + Some(mosi.map_into()), + Some(miso.map_into()), + None, + config, + ) + } + + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let ch = self.tx_dma.as_mut().unwrap(); + let transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_txdmae(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + }; + transfer.await; + Ok(()) + } + + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let ch = self.rx_dma.as_mut().unwrap(); + let transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_rxdmae(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + }; + transfer.await; + Ok(()) + } + + pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> { + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_txdmae(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::write(tx_ch, tx_buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + }; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_rxdmae(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_buffer, T::RX_DREQ) + }; + join(tx_transfer, rx_transfer).await; + Ok(()) + } +} + mod sealed { use super::*; diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index 88003ee17..e50297ae4 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::spi::Spi; +use embassy_rp::spi::{Blocking, Spi}; use embassy_rp::{gpio, spi}; use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { // create SPI let mut config = spi::Config::default(); config.frequency = 2_000_000; - let mut spi = Spi::new(p.SPI1, clk, mosi, miso, config); + let mut spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); // Configure CS let mut cs = Output::new(touch_cs, Level::Low); diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs new file mode 100644 index 000000000..f21377ede --- /dev/null +++ b/examples/rp/src/bin/spi_async.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::spi::{Async, Spi}; +use embassy_rp::{gpio, spi}; +use embassy_time::{Duration, Timer}; +use gpio::{Level, Output}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let miso = p.PIN_12; + let mosi = p.PIN_11; + let clk = p.PIN_10; + + let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI1, p.DMA_CH0, p.DMA_CH1, clk, mosi, miso, spi::Config::default()); + + loop { + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + info!("{:?}", rx_buf); + Timer::after(Duration::from_secs(1)).await; + } +} From 44150c483017c18979e58d8557aac3df031ba47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 30 Aug 2022 01:18:28 +0200 Subject: [PATCH 0085/1575] impl embedded-hal-async --- embassy-rp/src/spi.rs | 54 ++++++++++++++++++++++++++++++++ examples/rp/src/bin/spi_async.rs | 6 ++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index a91a1fd19..be639504f 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -479,6 +479,60 @@ mod eh1 { } } +cfg_if::cfg_if! { + if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { + use core::future::Future; + 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(()) } + } + } + + 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) + } + } + + 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) + } + } + + 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) + } + + type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; + + fn transfer_in_place<'a>( + &'a mut self, + words: &'a mut [u8], + ) -> Self::TransferInPlaceFuture<'a> { + let (ptr, len) = crate::dma::slice_ptr_parts(words); + let tx_buffer = unsafe { core::slice::from_raw_parts(ptr as *const _, len) }; + self.transfer(words, tx_buffer) + } + } + } +} + impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> { type Config = Config; fn set_config(&mut self, config: &Self::Config) { diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs index f21377ede..359ad50e7 100644 --- a/examples/rp/src/bin/spi_async.rs +++ b/examples/rp/src/bin/spi_async.rs @@ -4,10 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::spi::{Async, Spi}; -use embassy_rp::{gpio, spi}; +use embassy_rp::spi::{Async, Config, Spi}; use embassy_time::{Duration, Timer}; -use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -19,7 +17,7 @@ async fn main(_spawner: Spawner) { let mosi = p.PIN_11; let clk = p.PIN_10; - let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI1, p.DMA_CH0, p.DMA_CH1, clk, mosi, miso, spi::Config::default()); + let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI1, p.DMA_CH0, p.DMA_CH1, clk, mosi, miso, Config::default()); loop { let tx_buf = [1_u8, 2, 3, 4, 5, 6]; From c8ecc557109bd14bea0564e4a1505a26947e1851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 30 Aug 2022 01:39:08 +0200 Subject: [PATCH 0086/1575] Fix example --- examples/rp/src/bin/spi_display.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index f0e54d87f..d0fbd6847 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; -use embassy_rp::spi::Spi; +use embassy_rp::spi::{Spi, Blocking}; use embassy_time::Delay; use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -48,7 +48,8 @@ async fn main(_spawner: Spawner) { config.phase = spi::Phase::CaptureOnSecondTransition; config.polarity = spi::Polarity::IdleHigh; - let spi_bus = RefCell::new(Spi::new(p.SPI1, clk, mosi, miso, config)); + let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); + let spi_bus = RefCell::new(spi); let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); From 6d347af9fae3a9c594e13900796ec57537c402e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 30 Aug 2022 06:50:58 +0200 Subject: [PATCH 0087/1575] transfer_in_place --- embassy-rp/src/spi.rs | 15 ++++++++------- examples/rp/src/bin/spi_display.rs | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index be639504f..11aad6305 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -334,6 +334,12 @@ impl<'d, T: Instance> Spi<'d, T, Async> { join(tx_transfer, rx_transfer).await; Ok(()) } + + pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { + let (ptr, len) = crate::dma::slice_ptr_parts(words); + let tx_buffer = unsafe { core::slice::from_raw_parts(ptr as *const _, len) }; + self.transfer(words, tx_buffer).await + } } mod sealed { @@ -521,13 +527,8 @@ cfg_if::cfg_if! { type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - fn transfer_in_place<'a>( - &'a mut self, - words: &'a mut [u8], - ) -> Self::TransferInPlaceFuture<'a> { - let (ptr, len) = crate::dma::slice_ptr_parts(words); - let tx_buffer = unsafe { core::slice::from_raw_parts(ptr as *const _, len) }; - self.transfer(words, tx_buffer) + fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { + self.transfer_in_place(words) } } } diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index d0fbd6847..23cd4355e 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; -use embassy_rp::spi::{Spi, Blocking}; +use embassy_rp::spi::{Blocking, Spi}; use embassy_time::Delay; use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::mono_font::ascii::FONT_10X20; From 99dd2a9386c83f4e625f4c849c8b5e519e12a87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 30 Aug 2022 22:55:53 +0200 Subject: [PATCH 0088/1575] Reorder args --- embassy-rp/src/spi.rs | 4 ++-- examples/rp/src/bin/spi_async.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 11aad6305..6f68777b2 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -264,11 +264,11 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { impl<'d, T: Instance> Spi<'d, T, Async> { pub fn new( inner: impl Peripheral

+ 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, clk: impl Peripheral

+ 'd> + 'd, mosi: impl Peripheral

+ 'd> + 'd, miso: impl Peripheral

+ 'd> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(tx_dma, rx_dma, clk, mosi, miso); diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs index 359ad50e7..b2df0dd11 100644 --- a/examples/rp/src/bin/spi_async.rs +++ b/examples/rp/src/bin/spi_async.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let mosi = p.PIN_11; let clk = p.PIN_10; - let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI1, p.DMA_CH0, p.DMA_CH1, clk, mosi, miso, Config::default()); + let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); loop { let tx_buf = [1_u8, 2, 3, 4, 5, 6]; From 7954cbc4e7f44ff5292052da00b1ced857d3183a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 30 Aug 2022 23:04:58 +0200 Subject: [PATCH 0089/1575] Add HIL tests --- tests/rp/src/bin/spi.rs | 28 ++++++++++++++++++++++++++++ tests/rp/src/bin/spi_async.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 tests/rp/src/bin/spi.rs create mode 100644 tests/rp/src/bin/spi_async.rs diff --git a/tests/rp/src/bin/spi.rs b/tests/rp/src/bin/spi.rs new file mode 100644 index 000000000..3ce85a95c --- /dev/null +++ b/tests/rp/src/bin/spi.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::spi::{Blocking, Config, Spi}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let clk = p.PIN_2; + let mosi = p.PIN_3; + let miso = p.PIN_4; + + let mut spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI0, clk, mosi, miso, Config::default()); + + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap(); + assert_eq!(rx_buf, tx_buf); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs new file mode 100644 index 000000000..41c711f70 --- /dev/null +++ b/tests/rp/src/bin/spi_async.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::spi::{Async, Config, Spi}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let clk = p.PIN_2; + let mosi = p.PIN_3; + let miso = p.PIN_4; + + let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + assert_eq!(rx_buf, tx_buf); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 27905f1be1e2404952b1a5c333d4a07f2e4c18f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Wed, 31 Aug 2022 19:54:38 +0200 Subject: [PATCH 0090/1575] Change DMA write/read to use raw pointers --- embassy-rp/src/dma.rs | 12 ++++++------ embassy-rp/src/spi.rs | 39 +++++++++++++++++++++++++++++---------- embassy-rp/src/uart.rs | 6 ++++-- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 75d7492e0..526c83822 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -40,14 +40,14 @@ pub(crate) unsafe fn init() { pub unsafe fn read<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const W, - to: &mut [W], + to: *mut W, + len: usize, dreq: u8, ) -> Transfer<'a, C> { - let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(to); copy_inner( ch, from as *const u32, - to_ptr as *mut u32, + to as *mut u32, len, W::size(), false, @@ -58,14 +58,14 @@ pub unsafe fn read<'a, C: Channel, W: Word>( pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, - from: &[W], + from: *const W, to: *mut W, + len: usize, dreq: u8, ) -> Transfer<'a, C> { - let (from_ptr, len) = crate::dma::slice_ptr_parts(from); copy_inner( ch, - from_ptr as *const u32, + from as *const u32, to as *mut u32, len, W::size(), diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 6f68777b2..720aad0e5 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -285,6 +285,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let (from_ptr, len) = crate::dma::slice_ptr_parts(buffer); let ch = self.tx_dma.as_mut().unwrap(); let transfer = unsafe { self.inner.regs().dmacr().modify(|reg| { @@ -292,13 +293,14 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(ch, from_ptr as *const u32, self.inner.regs().dr().ptr() as *mut _, len, T::TX_DREQ) }; transfer.await; Ok(()) } pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(buffer); let ch = self.rx_dma.as_mut().unwrap(); let transfer = unsafe { self.inner.regs().dmacr().modify(|reg| { @@ -306,13 +308,24 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, to_ptr as *mut u32, len, T::RX_DREQ) }; transfer.await; Ok(()) } pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> { + self.transfer_inner(rx_buffer, tx_buffer).await + } + + pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { + self.transfer_inner(words, words).await + } + + async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> { + let (from_ptr, from_len) = crate::dma::slice_ptr_parts(tx_ptr); + let (to_ptr, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); + assert_eq!(from_len, to_len); let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { self.inner.regs().dmacr().modify(|reg| { @@ -320,7 +333,13 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(tx_ch, tx_buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write( + tx_ch, + from_ptr as *const u32, + self.inner.regs().dr().ptr() as *mut _, + from_len, + T::TX_DREQ, + ) }; let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { @@ -329,17 +348,17 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_buffer, T::RX_DREQ) + crate::dma::read( + rx_ch, + self.inner.regs().dr().ptr() as *const _, + to_ptr as *mut u32, + to_len, + T::RX_DREQ, + ) }; join(tx_transfer, rx_transfer).await; Ok(()) } - - pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { - let (ptr, len) = crate::dma::slice_ptr_parts(words); - let tx_buffer = unsafe { core::slice::from_raw_parts(ptr as *const _, len) }; - self.transfer(words, tx_buffer).await - } } mod sealed { diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index 987b716b4..f8a10bd9d 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -120,6 +120,7 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { impl<'d, T: Instance> UartTx<'d, T, Async> { pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let (from_ptr, len) = crate::dma::slice_ptr_parts(buffer); let ch = self.tx_dma.as_mut().unwrap(); let transfer = unsafe { T::regs().uartdmacr().modify(|reg| { @@ -127,7 +128,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(ch, from_ptr as *const u32, T::regs().uartdr().ptr() as *mut _, len, T::TX_DREQ) }; transfer.await; Ok(()) @@ -173,6 +174,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { impl<'d, T: Instance> UartRx<'d, T, Async> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(buffer); let ch = self.rx_dma.as_mut().unwrap(); let transfer = unsafe { T::regs().uartdmacr().modify(|reg| { @@ -180,7 +182,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, to_ptr as *mut u32, len, T::RX_DREQ) }; transfer.await; Ok(()) From 3fce6ec649953fac52b731ea0aa7587ed60e55c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Wed, 31 Aug 2022 21:54:42 +0200 Subject: [PATCH 0091/1575] Rearrange new:s --- embassy-rp/src/spi.rs | 176 ++++++++++++++++++++++++++--------------- embassy-rp/src/uart.rs | 16 +++- 2 files changed, 128 insertions(+), 64 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 720aad0e5..9bf6a9119 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -65,66 +65,6 @@ fn calc_prescs(freq: u32) -> (u8, u8) { } impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { - pub fn new_blocking( - inner: impl Peripheral

+ 'd, - clk: impl Peripheral

+ 'd> + 'd, - mosi: impl Peripheral

+ 'd> + 'd, - miso: impl Peripheral

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

+ 'd, - tx_dma: Option>, - clk: impl Peripheral

+ 'd> + 'd, - mosi: impl Peripheral

+ 'd> + 'd, - config: Config, - ) -> Self { - into_ref!(clk, mosi); - Self::new_inner( - inner, - tx_dma, - None, - Some(clk.map_into()), - Some(mosi.map_into()), - None, - None, - config, - ) - } - - pub fn new_rxonly( - inner: impl Peripheral

+ 'd, - rx_dma: Option>, - clk: impl Peripheral

+ 'd> + 'd, - miso: impl Peripheral

+ 'd> + 'd, - config: Config, - ) -> Self { - into_ref!(clk, miso); - Self::new_inner( - inner, - None, - rx_dma, - Some(clk.map_into()), - None, - Some(miso.map_into()), - None, - config, - ) - } - fn new_inner( inner: impl Peripheral

+ 'd, tx_dma: Option>, @@ -261,6 +201,66 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { } } +impl<'d, T: Instance> Spi<'d, T, Blocking> { + pub fn new_blocking( + inner: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd> + 'd, + mosi: impl Peripheral

+ 'd> + 'd, + miso: impl Peripheral

+ 'd> + 'd, + config: Config, + ) -> Self { + into_ref!(clk, mosi, miso); + Self::new_inner( + inner, + None, + None, + Some(clk.map_into()), + Some(mosi.map_into()), + Some(miso.map_into()), + None, + config, + ) + } + + pub fn new_blocking_txonly( + inner: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd> + 'd, + mosi: impl Peripheral

+ 'd> + 'd, + config: Config, + ) -> Self { + into_ref!(clk, mosi); + Self::new_inner( + inner, + None, + None, + Some(clk.map_into()), + Some(mosi.map_into()), + None, + None, + config, + ) + } + + pub fn new_blocking_rxonly( + inner: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd> + 'd, + miso: impl Peripheral

+ 'd> + 'd, + config: Config, + ) -> Self { + into_ref!(clk, miso); + Self::new_inner( + inner, + None, + None, + Some(clk.map_into()), + None, + Some(miso.map_into()), + None, + config, + ) + } +} + impl<'d, T: Instance> Spi<'d, T, Async> { pub fn new( inner: impl Peripheral

+ 'd, @@ -284,6 +284,46 @@ impl<'d, T: Instance> Spi<'d, T, Async> { ) } + pub fn new_txonly( + inner: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd> + 'd, + mosi: impl Peripheral

+ 'd> + 'd, + config: Config, + ) -> Self { + into_ref!(tx_dma, clk, mosi); + Self::new_inner( + inner, + Some(tx_dma.map_into()), + None, + Some(clk.map_into()), + Some(mosi.map_into()), + None, + None, + config, + ) + } + + pub fn new_rxonly( + inner: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd> + 'd, + miso: impl Peripheral

+ 'd> + 'd, + config: Config, + ) -> Self { + into_ref!(rx_dma, clk, miso); + Self::new_inner( + inner, + None, + Some(rx_dma.map_into()), + Some(clk.map_into()), + None, + Some(miso.map_into()), + None, + config, + ) + } + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { let (from_ptr, len) = crate::dma::slice_ptr_parts(buffer); let ch = self.tx_dma.as_mut().unwrap(); @@ -293,7 +333,13 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, from_ptr as *const u32, self.inner.regs().dr().ptr() as *mut _, len, T::TX_DREQ) + crate::dma::write( + ch, + from_ptr as *const u32, + self.inner.regs().dr().ptr() as *mut _, + len, + T::TX_DREQ, + ) }; transfer.await; Ok(()) @@ -308,7 +354,13 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, to_ptr as *mut u32, len, T::RX_DREQ) + crate::dma::read( + ch, + self.inner.regs().dr().ptr() as *const _, + to_ptr as *mut u32, + len, + T::RX_DREQ, + ) }; transfer.await; Ok(()) diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index f8a10bd9d..87d5fbd46 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -128,7 +128,13 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, from_ptr as *const u32, T::regs().uartdr().ptr() as *mut _, len, T::TX_DREQ) + crate::dma::write( + ch, + from_ptr as *const u32, + T::regs().uartdr().ptr() as *mut _, + len, + T::TX_DREQ, + ) }; transfer.await; Ok(()) @@ -182,7 +188,13 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, to_ptr as *mut u32, len, T::RX_DREQ) + crate::dma::read( + ch, + T::regs().uartdr().ptr() as *const _, + to_ptr as *mut u32, + len, + T::RX_DREQ, + ) }; transfer.await; Ok(()) From b934f3f12e0414ac92ce812b73aca44d0f47a5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Wed, 31 Aug 2022 22:03:34 +0200 Subject: [PATCH 0092/1575] Remove cfg_if --- embassy-rp/src/spi.rs | 77 ++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 9bf6a9119..cd40a4a31 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -556,51 +556,46 @@ mod eh1 { } } -cfg_if::cfg_if! { - if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { - use core::future::Future; - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> { - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use super::*; + use core::future::Future; - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async { Ok(()) } - } + 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(()) } + } + } + + 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) + } + } + + 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) + } + } + + 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) } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite - for Spi<'d, T, Async> - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(data) - } - } - - 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) - } - } - - 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) - } - - 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) - } + fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { + self.transfer_in_place(words) } } } From e2181cb4396710c8dfef5d06ee5c498510524ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Wed, 31 Aug 2022 22:07:03 +0200 Subject: [PATCH 0093/1575] rustfmt --- embassy-rp/src/spi.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index cd40a4a31..8ae1c8b03 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -558,9 +558,10 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { - use super::*; 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; From 71c130488b465012d1cb076deb182c5946c011d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Wed, 31 Aug 2022 22:12:14 +0200 Subject: [PATCH 0094/1575] Reorder args --- embassy-rp/src/spi.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 8ae1c8b03..c5d9647db 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -67,12 +67,12 @@ fn calc_prescs(freq: u32) -> (u8, u8) { impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { fn new_inner( inner: impl Peripheral

+ 'd, - tx_dma: Option>, - rx_dma: Option>, clk: Option>, mosi: Option>, miso: Option>, cs: Option>, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { into_ref!(inner); @@ -212,12 +212,12 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { into_ref!(clk, mosi, miso); Self::new_inner( inner, - None, - None, Some(clk.map_into()), Some(mosi.map_into()), Some(miso.map_into()), None, + None, + None, config, ) } @@ -231,12 +231,12 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { into_ref!(clk, mosi); Self::new_inner( inner, - None, - None, Some(clk.map_into()), Some(mosi.map_into()), None, None, + None, + None, config, ) } @@ -250,12 +250,12 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { into_ref!(clk, miso); Self::new_inner( inner, - None, - None, Some(clk.map_into()), None, Some(miso.map_into()), None, + None, + None, config, ) } @@ -274,52 +274,52 @@ impl<'d, T: Instance> Spi<'d, T, Async> { into_ref!(tx_dma, rx_dma, clk, mosi, miso); Self::new_inner( inner, - Some(tx_dma.map_into()), - Some(rx_dma.map_into()), Some(clk.map_into()), Some(mosi.map_into()), Some(miso.map_into()), None, + Some(tx_dma.map_into()), + Some(rx_dma.map_into()), config, ) } pub fn new_txonly( inner: impl Peripheral

+ 'd, - tx_dma: impl Peripheral

+ 'd, clk: impl Peripheral

+ 'd> + 'd, mosi: impl Peripheral

+ 'd> + 'd, + tx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(tx_dma, clk, mosi); Self::new_inner( inner, - Some(tx_dma.map_into()), - None, Some(clk.map_into()), Some(mosi.map_into()), None, None, + Some(tx_dma.map_into()), + None, config, ) } pub fn new_rxonly( inner: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, clk: impl Peripheral

+ 'd> + 'd, miso: impl Peripheral

+ 'd> + 'd, + rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rx_dma, clk, miso); Self::new_inner( inner, - None, - Some(rx_dma.map_into()), Some(clk.map_into()), None, Some(miso.map_into()), None, + None, + Some(rx_dma.map_into()), config, ) } From 9ff5c507741cfadc8a1fc125a353d8438433494f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Wed, 31 Aug 2022 22:28:47 +0200 Subject: [PATCH 0095/1575] Cleanup examples --- examples/rp/src/bin/spi.rs | 4 ++-- examples/rp/src/bin/spi_async.rs | 4 ++-- tests/rp/src/bin/spi.rs | 4 ++-- tests/rp/src/bin/spi_async.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index e50297ae4..a830a17a2 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::spi::{Blocking, Spi}; +use embassy_rp::spi::Spi; use embassy_rp::{gpio, spi}; use gpio::{Level, Output}; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { // create SPI let mut config = spi::Config::default(); config.frequency = 2_000_000; - let mut spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); + let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); // Configure CS let mut cs = Output::new(touch_cs, Level::Low); diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs index b2df0dd11..671a9caaf 100644 --- a/examples/rp/src/bin/spi_async.rs +++ b/examples/rp/src/bin/spi_async.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::spi::{Async, Config, Spi}; +use embassy_rp::spi::{Config, Spi}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let mosi = p.PIN_11; let clk = p.PIN_10; - let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); loop { let tx_buf = [1_u8, 2, 3, 4, 5, 6]; diff --git a/tests/rp/src/bin/spi.rs b/tests/rp/src/bin/spi.rs index 3ce85a95c..478d62ee0 100644 --- a/tests/rp/src/bin/spi.rs +++ b/tests/rp/src/bin/spi.rs @@ -4,7 +4,7 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_rp::spi::{Blocking, Config, Spi}; +use embassy_rp::spi::{Config, Spi}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let mosi = p.PIN_3; let miso = p.PIN_4; - let mut spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI0, clk, mosi, miso, Config::default()); + let mut spi = Spi::new_blocking(p.SPI0, clk, mosi, miso, Config::default()); let tx_buf = [1_u8, 2, 3, 4, 5, 6]; let mut rx_buf = [0_u8; 6]; diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index 41c711f70..6c85ef60a 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs @@ -4,7 +4,7 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_rp::spi::{Async, Config, Spi}; +use embassy_rp::spi::{Config, Spi}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let mosi = p.PIN_3; let miso = p.PIN_4; - let mut spi: Spi<'_, _, Async> = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + let mut spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); let tx_buf = [1_u8, 2, 3, 4, 5, 6]; let mut rx_buf = [0_u8; 6]; From efe456ab1414a19a4e0469a6bb7686a04029a44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Thu, 1 Sep 2022 12:00:11 +0200 Subject: [PATCH 0096/1575] Fix dma --- embassy-rp/src/dma.rs | 12 ++++++------ embassy-rp/src/spi.rs | 38 ++++++-------------------------------- embassy-rp/src/uart.rs | 18 ++---------------- 3 files changed, 14 insertions(+), 54 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 526c83822..acf338225 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -40,14 +40,14 @@ pub(crate) unsafe fn init() { pub unsafe fn read<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const W, - to: *mut W, - len: usize, + to: *mut [W], dreq: u8, ) -> Transfer<'a, C> { + let (to_ptr, len) = crate::dma::slice_ptr_parts(to); copy_inner( ch, from as *const u32, - to as *mut u32, + to_ptr as *mut u32, len, W::size(), false, @@ -58,14 +58,14 @@ pub unsafe fn read<'a, C: Channel, W: Word>( pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, - from: *const W, + from: *const [W], to: *mut W, - len: usize, dreq: u8, ) -> Transfer<'a, C> { + let (from_ptr, len) = crate::dma::slice_ptr_parts(from); copy_inner( ch, - from as *const u32, + from_ptr as *const u32, to as *mut u32, len, W::size(), diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index c5d9647db..74f0b04de 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,7 +325,6 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let (from_ptr, len) = crate::dma::slice_ptr_parts(buffer); let ch = self.tx_dma.as_mut().unwrap(); let transfer = unsafe { self.inner.regs().dmacr().modify(|reg| { @@ -333,20 +332,13 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write( - ch, - from_ptr as *const u32, - self.inner.regs().dr().ptr() as *mut _, - len, - T::TX_DREQ, - ) + crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; transfer.await; Ok(()) } pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(buffer); let ch = self.rx_dma.as_mut().unwrap(); let transfer = unsafe { self.inner.regs().dmacr().modify(|reg| { @@ -354,13 +346,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read( - ch, - self.inner.regs().dr().ptr() as *const _, - to_ptr as *mut u32, - len, - T::RX_DREQ, - ) + crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) }; transfer.await; Ok(()) @@ -375,8 +361,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> { - let (from_ptr, from_len) = crate::dma::slice_ptr_parts(tx_ptr); - let (to_ptr, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); + let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); + let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); assert_eq!(from_len, to_len); let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { @@ -385,13 +371,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write( - tx_ch, - from_ptr as *const u32, - self.inner.regs().dr().ptr() as *mut _, - from_len, - T::TX_DREQ, - ) + crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { @@ -400,13 +380,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read( - rx_ch, - self.inner.regs().dr().ptr() as *const _, - to_ptr as *mut u32, - to_len, - T::RX_DREQ, - ) + crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) }; join(tx_transfer, rx_transfer).await; Ok(()) diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart.rs index 87d5fbd46..987b716b4 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart.rs @@ -120,7 +120,6 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { impl<'d, T: Instance> UartTx<'d, T, Async> { pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let (from_ptr, len) = crate::dma::slice_ptr_parts(buffer); let ch = self.tx_dma.as_mut().unwrap(); let transfer = unsafe { T::regs().uartdmacr().modify(|reg| { @@ -128,13 +127,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write( - ch, - from_ptr as *const u32, - T::regs().uartdr().ptr() as *mut _, - len, - T::TX_DREQ, - ) + crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _, T::TX_DREQ) }; transfer.await; Ok(()) @@ -180,7 +173,6 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { impl<'d, T: Instance> UartRx<'d, T, Async> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(buffer); let ch = self.rx_dma.as_mut().unwrap(); let transfer = unsafe { T::regs().uartdmacr().modify(|reg| { @@ -188,13 +180,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read( - ch, - T::regs().uartdr().ptr() as *const _, - to_ptr as *mut u32, - len, - T::RX_DREQ, - ) + crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer, T::RX_DREQ) }; transfer.await; Ok(()) From 5327b9c289ee69bf07ed384253d03d29af291285 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 2 Sep 2022 00:58:31 +0200 Subject: [PATCH 0097/1575] time: add more tick rates, use 1mhz as default. --- ci.sh | 46 ++--- ci_stable.sh | 70 +++---- embassy-lora/Cargo.toml | 4 +- embassy-net/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/time_driver.rs | 4 +- embassy-time/Cargo.toml | 84 ++++++++- embassy-time/gen_tick.py | 50 +++++ embassy-time/src/duration.rs | 24 +-- embassy-time/src/instant.rs | 14 +- embassy-time/src/lib.rs | 19 +- embassy-time/src/tick.rs | 182 +++++++++++++++++++ examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 38 files changed, 418 insertions(+), 135 deletions(-) create mode 100644 embassy-time/gen_tick.py create mode 100644 embassy-time/src/tick.rs diff --git a/ci.sh b/ci.sh index 0e2af1e4c..fa24d5268 100755 --- a/ci.sh +++ b/ci.sh @@ -36,10 +36,10 @@ 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-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,unstable-traits \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,unstable-traits,nightly \ + --- 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 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ @@ -58,25 +58,25 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ + --- 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/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 \ diff --git a/ci_stable.sh b/ci_stable.sh index 02e630520..0332c3faf 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -13,8 +13,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,embassy-time/tick-1mhz,unstable-traits \ + --- 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-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ @@ -32,38 +32,38 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \ --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \ diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 9d5e7aed2..15f9237e9 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -8,8 +8,8 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-time/tick-32768hz"] }, - { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-time/tick-32768hz"] }, + { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, + { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, ] [lib] diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 64d78566f..d5b13204e 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = [ "pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "embassy-time/tick-1mhz"] +features = ["pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] target = "thumbv7em-none-eabi" [features] diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 186c73a58..6fbbc8d9b 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -57,7 +57,7 @@ _nrf5340-net = ["_nrf5340", "nrf5340-net-pac"] _nrf5340 = ["_gpio-p1", "_dppi"] _nrf9160 = ["nrf9160-pac", "_dppi"] -_time-driver = ["dep:embassy-time", "embassy-time?/tick-32768hz"] +_time-driver = ["dep:embassy-time", "embassy-time?/tick-hz-32_768"] _ppi = [] _dppi = [] diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index f76e439ab..ccb2f6525 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -30,7 +30,7 @@ unstable-traits = ["embedded-hal-1"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } -embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-1mhz" ] } +embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 7a8e5c59b..b4c19f32e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -10,7 +10,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32 # TODO: sdmmc # TODO: net # TODO: subghz -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "embassy-time/tick-32768hz"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 6989a43d3..ed3225c51 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -7,7 +7,7 @@ use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time::driver::{AlarmHandle, Driver}; -use embassy_time::TICKS_PER_SECOND; +use embassy_time::TICK_HZ; use stm32_metapac::timer::regs; use crate::interrupt::{CriticalSection, InterruptExt}; @@ -153,7 +153,7 @@ impl RtcDriver { r.cr1().modify(|w| w.set_cen(false)); r.cnt().write(|w| w.set_cnt(0)); - let psc = timer_freq.0 / TICKS_PER_SECOND as u32 - 1; + let psc = timer_freq.0 / TICK_HZ as u32 - 1; let psc: u16 = match psc.try_into() { Err(_) => panic!("psc division overflow: {}", psc), Ok(n) => n, diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index f0f622aba..6d71e100f 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -11,8 +11,8 @@ features = ["nightly", "defmt", "unstable-traits", "std"] target = "x86_64-unknown-linux-gnu" [features] -std = ["tick-1mhz"] -wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-1mhz"] +std = ["tick-hz-1_000_000"] +wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] # Enable nightly-only features nightly = ["embedded-hal-async"] @@ -26,13 +26,79 @@ unstable-traits = ["embedded-hal-1"] defmt-timestamp-uptime = ["defmt"] # Set the `embassy_time` tick rate. -# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. -# If you're not writing your own driver, check the driver documentation to customize the tick rate. -# If you're writing a driver and your tick rate is not listed here, please add it and send a PR! -tick-32768hz = [] -tick-1000hz = [] -tick-1mhz = [] -tick-16mhz = [] +# +# At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. +# +# If the time driver in use supports using arbitrary tick rates, you can enable one `tick-*` +# feature from your binary crate to set the tick rate. The driver will use configured tick rate. +# If the time driver supports a fixed tick rate, it will enable one feature itself, so you should +# not enable one. Check the time driver documentation for details. +# +# When using embassy-time from libraries, you should *not* enable any `tick-*` feature, to allow the +# end user or the driver to pick. + +# BEGIN TICKS +# Generated by gen_tick.py. DO NOT EDIT. +tick-hz-1 = [] +tick-hz-10 = [] +tick-hz-100 = [] +tick-hz-1_000 = [] +tick-hz-10_000 = [] +tick-hz-100_000 = [] +tick-hz-1_000_000 = [] +tick-hz-10_000_000 = [] +tick-hz-100_000_000 = [] +tick-hz-1_000_000_000 = [] +tick-hz-2 = [] +tick-hz-4 = [] +tick-hz-8 = [] +tick-hz-16 = [] +tick-hz-32 = [] +tick-hz-64 = [] +tick-hz-128 = [] +tick-hz-256 = [] +tick-hz-512 = [] +tick-hz-1_024 = [] +tick-hz-2_048 = [] +tick-hz-4_096 = [] +tick-hz-8_192 = [] +tick-hz-16_384 = [] +tick-hz-32_768 = [] +tick-hz-65_536 = [] +tick-hz-131_072 = [] +tick-hz-262_144 = [] +tick-hz-524_288 = [] +tick-hz-1_048_576 = [] +tick-hz-2_097_152 = [] +tick-hz-4_194_304 = [] +tick-hz-8_388_608 = [] +tick-hz-16_777_216 = [] +tick-hz-2_000_000 = [] +tick-hz-3_000_000 = [] +tick-hz-4_000_000 = [] +tick-hz-6_000_000 = [] +tick-hz-8_000_000 = [] +tick-hz-9_000_000 = [] +tick-hz-12_000_000 = [] +tick-hz-16_000_000 = [] +tick-hz-18_000_000 = [] +tick-hz-24_000_000 = [] +tick-hz-32_000_000 = [] +tick-hz-36_000_000 = [] +tick-hz-48_000_000 = [] +tick-hz-64_000_000 = [] +tick-hz-72_000_000 = [] +tick-hz-96_000_000 = [] +tick-hz-128_000_000 = [] +tick-hz-144_000_000 = [] +tick-hz-192_000_000 = [] +tick-hz-256_000_000 = [] +tick-hz-288_000_000 = [] +tick-hz-384_000_000 = [] +tick-hz-512_000_000 = [] +tick-hz-576_000_000 = [] +tick-hz-768_000_000 = [] +# END TICKS [dependencies] defmt = { version = "0.3", optional = true } diff --git a/embassy-time/gen_tick.py b/embassy-time/gen_tick.py new file mode 100644 index 000000000..651ebbd72 --- /dev/null +++ b/embassy-time/gen_tick.py @@ -0,0 +1,50 @@ +import os +import toml +from glob import glob + +abspath = os.path.abspath(__file__) +dname = os.path.dirname(abspath) +os.chdir(dname) + +ticks = [] +for i in range(10): + ticks.append(10**i) +for i in range(1, 25): + ticks.append(2**i) +for i in range(1, 10): + ticks.append(2**i * 1000000) + ticks.append(2**i * 9 // 8 * 1000000) + ticks.append(2**i * 3 // 2 * 1000000) + +seen = set() +ticks = [x for x in ticks if not (x in seen or seen.add(x))] + +# ========= Update Cargo.toml + +things = {f'tick-hz-{hz:_}': [] for hz in ticks} + +SEPARATOR_START = '# BEGIN TICKS\n' +SEPARATOR_END = '# END TICKS\n' +HELP = '# Generated by gen_tick.py. DO NOT EDIT.\n' +with open('Cargo.toml', 'r') as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + toml.dumps(things) + SEPARATOR_END + after +with open('Cargo.toml', 'w') as f: + f.write(data) + +# ========= Update src/tick.rs + +with open('src/tick.rs', 'w') as f: + + f.write('// Generated by gen_tick.py. DO NOT EDIT.\n\n') + for hz in ticks: + f.write(f'#[cfg(feature = "tick-hz-{hz:_}")] pub const TICK_HZ: u64 = {hz:_};\n') + f.write('#[cfg(not(any(\n') + for hz in ticks: + f.write(f'feature = "tick-hz-{hz:_}",\n') + f.write(')))] pub const TICK_HZ: u64 = 1_000_000;') + + +os.system('rustfmt src/tick.rs') \ No newline at end of file diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index dc4f16bd4..d3c6f42a9 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -1,7 +1,7 @@ use core::fmt; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; -use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND}; +use super::{GCD_1K, GCD_1M, TICK_HZ}; #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -23,17 +23,17 @@ impl Duration { /// Convert the `Duration` to seconds, rounding down. pub const fn as_secs(&self) -> u64 { - self.ticks / TICKS_PER_SECOND + self.ticks / TICK_HZ } /// Convert the `Duration` to milliseconds, rounding down. pub const fn as_millis(&self) -> u64 { - self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) + self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K) } /// Convert the `Duration` to microseconds, rounding down. pub const fn as_micros(&self) -> u64 { - self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) + self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M) } /// Creates a duration from the specified number of clock ticks @@ -43,15 +43,13 @@ impl Duration { /// Creates a duration from the specified number of seconds, rounding up. pub const fn from_secs(secs: u64) -> Duration { - Duration { - ticks: secs * TICKS_PER_SECOND, - } + Duration { ticks: secs * TICK_HZ } } /// Creates a duration from the specified number of milliseconds, rounding up. pub const fn from_millis(millis: u64) -> Duration { Duration { - ticks: div_ceil(millis * (TICKS_PER_SECOND / GCD_1K), 1000 / GCD_1K), + ticks: div_ceil(millis * (TICK_HZ / GCD_1K), 1000 / GCD_1K), } } @@ -59,21 +57,19 @@ impl Duration { /// NOTE: Delays this small may be inaccurate. pub const fn from_micros(micros: u64) -> Duration { Duration { - ticks: div_ceil(micros * (TICKS_PER_SECOND / GCD_1M), 1_000_000 / GCD_1M), + ticks: div_ceil(micros * (TICK_HZ / GCD_1M), 1_000_000 / GCD_1M), } } /// Creates a duration from the specified number of seconds, rounding down. pub const fn from_secs_floor(secs: u64) -> Duration { - Duration { - ticks: secs * TICKS_PER_SECOND, - } + Duration { ticks: secs * TICK_HZ } } /// Creates a duration from the specified number of milliseconds, rounding down. pub const fn from_millis_floor(millis: u64) -> Duration { Duration { - ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), + ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K), } } @@ -81,7 +77,7 @@ impl Duration { /// NOTE: Delays this small may be inaccurate. pub const fn from_micros_floor(micros: u64) -> Duration { Duration { - ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), + ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M), } } diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs index 6a4925f47..44f226f62 100644 --- a/embassy-time/src/instant.rs +++ b/embassy-time/src/instant.rs @@ -1,7 +1,7 @@ use core::fmt; use core::ops::{Add, AddAssign, Sub, SubAssign}; -use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND}; +use super::{driver, Duration, GCD_1K, GCD_1M, TICK_HZ}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -29,21 +29,21 @@ impl Instant { /// Create an Instant from a microsecond count since system boot. pub const fn from_micros(micros: u64) -> Self { Self { - ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), + ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M), } } /// Create an Instant from a millisecond count since system boot. pub const fn from_millis(millis: u64) -> Self { Self { - ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), + ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K), } } /// Create an Instant from a second count since system boot. pub const fn from_secs(seconds: u64) -> Self { Self { - ticks: seconds * TICKS_PER_SECOND, + ticks: seconds * TICK_HZ, } } @@ -54,17 +54,17 @@ impl Instant { /// Seconds since system boot. pub const fn as_secs(&self) -> u64 { - self.ticks / TICKS_PER_SECOND + self.ticks / TICK_HZ } /// Milliseconds since system boot. pub const fn as_millis(&self) -> u64 { - self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) + self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K) } /// Microseconds since system boot. pub const fn as_micros(&self) -> u64 { - self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) + self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M) } /// Duration between this Instant and another Instant diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index a6c5d78cc..5b2620986 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -11,6 +11,7 @@ mod delay; pub mod driver; mod duration; mod instant; +mod tick; mod timer; #[cfg(feature = "std")] @@ -23,25 +24,13 @@ pub use duration::Duration; pub use instant::Instant; pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; -#[cfg(feature = "tick-1000hz")] -const TPS: u64 = 1_000; - -#[cfg(feature = "tick-32768hz")] -const TPS: u64 = 32_768; - -#[cfg(feature = "tick-1mhz")] -const TPS: u64 = 1_000_000; - -#[cfg(feature = "tick-16mhz")] -const TPS: u64 = 16_000_000; - /// Ticks per second of the global timebase. /// /// This value is specified by the `tick-*` Cargo features, which /// should be set by the time driver. Some drivers support a fixed tick rate, others /// allow you to choose a tick rate with Cargo features of their own. You should not /// set the `tick-*` features for embassy yourself as an end user. -pub const TICKS_PER_SECOND: u64 = TPS; +pub const TICK_HZ: u64 = tick::TICK_HZ; const fn gcd(a: u64, b: u64) -> u64 { if b == 0 { @@ -51,8 +40,8 @@ const fn gcd(a: u64, b: u64) -> u64 { } } -pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000); -pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000); +pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000); +pub(crate) const GCD_1M: u64 = gcd(TICK_HZ, 1_000_000); #[cfg(feature = "defmt-timestamp-uptime")] defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() } diff --git a/embassy-time/src/tick.rs b/embassy-time/src/tick.rs new file mode 100644 index 000000000..a79006506 --- /dev/null +++ b/embassy-time/src/tick.rs @@ -0,0 +1,182 @@ +// Generated by gen_tick.py. DO NOT EDIT. + +#[cfg(feature = "tick-hz-1")] +pub const TICK_HZ: u64 = 1; +#[cfg(feature = "tick-hz-10")] +pub const TICK_HZ: u64 = 10; +#[cfg(feature = "tick-hz-100")] +pub const TICK_HZ: u64 = 100; +#[cfg(feature = "tick-hz-1_000")] +pub const TICK_HZ: u64 = 1_000; +#[cfg(feature = "tick-hz-10_000")] +pub const TICK_HZ: u64 = 10_000; +#[cfg(feature = "tick-hz-100_000")] +pub const TICK_HZ: u64 = 100_000; +#[cfg(feature = "tick-hz-1_000_000")] +pub const TICK_HZ: u64 = 1_000_000; +#[cfg(feature = "tick-hz-10_000_000")] +pub const TICK_HZ: u64 = 10_000_000; +#[cfg(feature = "tick-hz-100_000_000")] +pub const TICK_HZ: u64 = 100_000_000; +#[cfg(feature = "tick-hz-1_000_000_000")] +pub const TICK_HZ: u64 = 1_000_000_000; +#[cfg(feature = "tick-hz-2")] +pub const TICK_HZ: u64 = 2; +#[cfg(feature = "tick-hz-4")] +pub const TICK_HZ: u64 = 4; +#[cfg(feature = "tick-hz-8")] +pub const TICK_HZ: u64 = 8; +#[cfg(feature = "tick-hz-16")] +pub const TICK_HZ: u64 = 16; +#[cfg(feature = "tick-hz-32")] +pub const TICK_HZ: u64 = 32; +#[cfg(feature = "tick-hz-64")] +pub const TICK_HZ: u64 = 64; +#[cfg(feature = "tick-hz-128")] +pub const TICK_HZ: u64 = 128; +#[cfg(feature = "tick-hz-256")] +pub const TICK_HZ: u64 = 256; +#[cfg(feature = "tick-hz-512")] +pub const TICK_HZ: u64 = 512; +#[cfg(feature = "tick-hz-1_024")] +pub const TICK_HZ: u64 = 1_024; +#[cfg(feature = "tick-hz-2_048")] +pub const TICK_HZ: u64 = 2_048; +#[cfg(feature = "tick-hz-4_096")] +pub const TICK_HZ: u64 = 4_096; +#[cfg(feature = "tick-hz-8_192")] +pub const TICK_HZ: u64 = 8_192; +#[cfg(feature = "tick-hz-16_384")] +pub const TICK_HZ: u64 = 16_384; +#[cfg(feature = "tick-hz-32_768")] +pub const TICK_HZ: u64 = 32_768; +#[cfg(feature = "tick-hz-65_536")] +pub const TICK_HZ: u64 = 65_536; +#[cfg(feature = "tick-hz-131_072")] +pub const TICK_HZ: u64 = 131_072; +#[cfg(feature = "tick-hz-262_144")] +pub const TICK_HZ: u64 = 262_144; +#[cfg(feature = "tick-hz-524_288")] +pub const TICK_HZ: u64 = 524_288; +#[cfg(feature = "tick-hz-1_048_576")] +pub const TICK_HZ: u64 = 1_048_576; +#[cfg(feature = "tick-hz-2_097_152")] +pub const TICK_HZ: u64 = 2_097_152; +#[cfg(feature = "tick-hz-4_194_304")] +pub const TICK_HZ: u64 = 4_194_304; +#[cfg(feature = "tick-hz-8_388_608")] +pub const TICK_HZ: u64 = 8_388_608; +#[cfg(feature = "tick-hz-16_777_216")] +pub const TICK_HZ: u64 = 16_777_216; +#[cfg(feature = "tick-hz-2_000_000")] +pub const TICK_HZ: u64 = 2_000_000; +#[cfg(feature = "tick-hz-3_000_000")] +pub const TICK_HZ: u64 = 3_000_000; +#[cfg(feature = "tick-hz-4_000_000")] +pub const TICK_HZ: u64 = 4_000_000; +#[cfg(feature = "tick-hz-6_000_000")] +pub const TICK_HZ: u64 = 6_000_000; +#[cfg(feature = "tick-hz-8_000_000")] +pub const TICK_HZ: u64 = 8_000_000; +#[cfg(feature = "tick-hz-9_000_000")] +pub const TICK_HZ: u64 = 9_000_000; +#[cfg(feature = "tick-hz-12_000_000")] +pub const TICK_HZ: u64 = 12_000_000; +#[cfg(feature = "tick-hz-16_000_000")] +pub const TICK_HZ: u64 = 16_000_000; +#[cfg(feature = "tick-hz-18_000_000")] +pub const TICK_HZ: u64 = 18_000_000; +#[cfg(feature = "tick-hz-24_000_000")] +pub const TICK_HZ: u64 = 24_000_000; +#[cfg(feature = "tick-hz-32_000_000")] +pub const TICK_HZ: u64 = 32_000_000; +#[cfg(feature = "tick-hz-36_000_000")] +pub const TICK_HZ: u64 = 36_000_000; +#[cfg(feature = "tick-hz-48_000_000")] +pub const TICK_HZ: u64 = 48_000_000; +#[cfg(feature = "tick-hz-64_000_000")] +pub const TICK_HZ: u64 = 64_000_000; +#[cfg(feature = "tick-hz-72_000_000")] +pub const TICK_HZ: u64 = 72_000_000; +#[cfg(feature = "tick-hz-96_000_000")] +pub const TICK_HZ: u64 = 96_000_000; +#[cfg(feature = "tick-hz-128_000_000")] +pub const TICK_HZ: u64 = 128_000_000; +#[cfg(feature = "tick-hz-144_000_000")] +pub const TICK_HZ: u64 = 144_000_000; +#[cfg(feature = "tick-hz-192_000_000")] +pub const TICK_HZ: u64 = 192_000_000; +#[cfg(feature = "tick-hz-256_000_000")] +pub const TICK_HZ: u64 = 256_000_000; +#[cfg(feature = "tick-hz-288_000_000")] +pub const TICK_HZ: u64 = 288_000_000; +#[cfg(feature = "tick-hz-384_000_000")] +pub const TICK_HZ: u64 = 384_000_000; +#[cfg(feature = "tick-hz-512_000_000")] +pub const TICK_HZ: u64 = 512_000_000; +#[cfg(feature = "tick-hz-576_000_000")] +pub const TICK_HZ: u64 = 576_000_000; +#[cfg(feature = "tick-hz-768_000_000")] +pub const TICK_HZ: u64 = 768_000_000; +#[cfg(not(any( + feature = "tick-hz-1", + feature = "tick-hz-10", + feature = "tick-hz-100", + feature = "tick-hz-1_000", + feature = "tick-hz-10_000", + feature = "tick-hz-100_000", + feature = "tick-hz-1_000_000", + feature = "tick-hz-10_000_000", + feature = "tick-hz-100_000_000", + feature = "tick-hz-1_000_000_000", + feature = "tick-hz-2", + feature = "tick-hz-4", + feature = "tick-hz-8", + feature = "tick-hz-16", + feature = "tick-hz-32", + feature = "tick-hz-64", + feature = "tick-hz-128", + feature = "tick-hz-256", + feature = "tick-hz-512", + feature = "tick-hz-1_024", + feature = "tick-hz-2_048", + feature = "tick-hz-4_096", + feature = "tick-hz-8_192", + feature = "tick-hz-16_384", + feature = "tick-hz-32_768", + feature = "tick-hz-65_536", + feature = "tick-hz-131_072", + feature = "tick-hz-262_144", + feature = "tick-hz-524_288", + feature = "tick-hz-1_048_576", + feature = "tick-hz-2_097_152", + feature = "tick-hz-4_194_304", + feature = "tick-hz-8_388_608", + feature = "tick-hz-16_777_216", + feature = "tick-hz-2_000_000", + feature = "tick-hz-3_000_000", + feature = "tick-hz-4_000_000", + feature = "tick-hz-6_000_000", + feature = "tick-hz-8_000_000", + feature = "tick-hz-9_000_000", + feature = "tick-hz-12_000_000", + feature = "tick-hz-16_000_000", + feature = "tick-hz-18_000_000", + feature = "tick-hz-24_000_000", + feature = "tick-hz-32_000_000", + feature = "tick-hz-36_000_000", + feature = "tick-hz-48_000_000", + feature = "tick-hz-64_000_000", + feature = "tick-hz-72_000_000", + feature = "tick-hz-96_000_000", + feature = "tick-hz-128_000_000", + feature = "tick-hz-144_000_000", + feature = "tick-hz-192_000_000", + feature = "tick-hz-256_000_000", + feature = "tick-hz-288_000_000", + feature = "tick-hz-384_000_000", + feature = "tick-hz-512_000_000", + feature = "tick-hz-576_000_000", + feature = "tick-hz-768_000_000", +)))] +pub const TICK_HZ: u64 = 1_000_000; diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index f143d1e8d..ce1e6fe4a 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 29c87eee1..2fc7ae834 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5669527fe..553edf0aa 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 48624d5ec..470eca52a 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 00b638ca5..2b4b29357 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 51ba730d5..40bddd194 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 182acf694..5b4a61e8f 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index cd2995d2c..c82b79c86 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,6 +13,6 @@ defmt-rtt = "0.3" 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-32768hz"] } +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"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 8660e743d..33ac63db1 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index b4bff4d85..60cd54bd9 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index d152b145f..208f39080 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 9bfdda92d..ea5c47a43 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" [dependencies] 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", "unstable-traits", "tick-32768hz"] } +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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 53ab2f5e7..6c2f846fe 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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.0", features = ["async"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 30f2b86f8..6baf17f36 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index f81df0b70..d8c05a979 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 18b61120b..fc60d7a88 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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", "unstable-traits", "tick-32768hz"] } +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.0", features = ["async"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 11751a21d..086a8f790 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -10,7 +10,7 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", [dependencies] 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-32768hz"] } +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 = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 18b35b305..a943c73d2 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "stm32l151cb-a", "time-driver-any", "memory-x"] } defmt = "0.3" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index cb7238e4c..35a9c20f0 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 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"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index ea3d5ab88..9ed5ea7e5 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index ff0ec9f42..164940586 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } defmt = "0.3" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 3b10da0ad..923833e46 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "stm32wb55cc", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5f6679f4b..2cd8d692b 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" [dependencies] 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-32768hz"] } +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", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index f1441d00c..d9cd3f120 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -15,7 +15,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] 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", "tick-32768hz"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } defmt = "0.3.0" From 3ca73144765411994759194a2279b567f4508be5 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 30 Aug 2022 13:07:35 +0200 Subject: [PATCH 0098/1575] Remove generic const expressions from embassy-boot * Remove the need for generic const expressions and use buffers provided in the flash config. * Extend embedded-storage traits to simplify generics. * Document all public APIs * Add toplevel README * Expose AlignedBuffer type for convenience. * Update examples --- embassy-boot/README.md | 30 + embassy-boot/boot/src/lib.rs | 544 ++++++++++-------- embassy-boot/nrf/src/lib.rs | 35 +- embassy-boot/stm32/src/lib.rs | 35 +- examples/boot/application/nrf/src/bin/a.rs | 3 +- .../boot/application/stm32f3/src/bin/a.rs | 7 +- .../boot/application/stm32f7/src/bin/a.rs | 7 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- .../boot/application/stm32h7/flash-boot.sh | 3 +- .../boot/application/stm32h7/src/bin/a.rs | 16 +- .../boot/application/stm32l0/src/bin/a.rs | 7 +- .../boot/application/stm32l1/src/bin/a.rs | 7 +- .../boot/application/stm32l4/src/bin/a.rs | 7 +- .../boot/application/stm32wl/src/bin/a.rs | 7 +- examples/boot/bootloader/nrf/src/main.rs | 6 +- examples/boot/bootloader/stm32/src/main.rs | 8 +- 16 files changed, 431 insertions(+), 293 deletions(-) create mode 100644 embassy-boot/README.md diff --git a/embassy-boot/README.md b/embassy-boot/README.md new file mode 100644 index 000000000..414405377 --- /dev/null +++ b/embassy-boot/README.md @@ -0,0 +1,30 @@ +# embassy-boot + +An [Embassy](https://embassy.dev) project. + +A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks. + +The bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts. + +By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself. + +## Hardware support + +The bootloader supports different hardware in separate crates: + +* `embassy-boot-nrf` - for the nRF microcontrollers. +* `embassy-boot-stm32` - for the STM32 microcontrollers. + +## Minimum supported Rust version (MSRV) + +`embassy-boot` 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/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 51e1056cf..e8ebe628d 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,53 +1,54 @@ #![feature(type_alias_impl_trait)] #![feature(generic_associated_types)] -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] #![no_std] -///! embassy-boot is a bootloader and firmware updater for embedded devices with flash -///! storage implemented using embedded-storage -///! -///! The bootloader works in conjunction with the firmware application, and only has the -///! ability to manage two flash banks with an active and a updatable part. It implements -///! a swap algorithm that is power-failure safe, and allows reverting to the previous -///! version of the firmware, should the application crash and fail to mark itself as booted. -///! -///! This library is intended to be used by platform-specific bootloaders, such as embassy-boot-nrf, -///! which defines the limits and flash type for that particular platform. -///! +#![warn(missing_docs)] +#![doc = include_str!("../../README.md")] mod fmt; -use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use embedded_storage_async::nor_flash::AsyncNorFlash; const BOOT_MAGIC: u8 = 0xD0; const SWAP_MAGIC: u8 = 0xF0; +/// A region in flash used by the bootloader. #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Partition { + /// Start of the flash region. pub from: usize, + /// End of the flash region. pub to: usize, } impl Partition { + /// Create a new partition with the provided range pub const fn new(from: usize, to: usize) -> Self { Self { from, to } } + + /// Return the length of the partition pub const fn len(&self) -> usize { self.to - self.from } } -#[derive(PartialEq, Debug)] +/// The state of the bootloader after running prepare. +#[derive(PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum State { + /// Bootloader is ready to boot the active partition. Boot, + /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. Swap, } -#[derive(PartialEq, Debug)] +/// Errors returned by bootloader +#[derive(PartialEq, Eq, Debug)] pub enum BootError { + /// Error from flash. Flash(NorFlashErrorKind), + /// Invalid bootloader magic BadMagic, } @@ -60,19 +61,39 @@ where } } -pub trait FlashConfig { - const BLOCK_SIZE: usize; - const ERASE_VALUE: u8; - type FLASH: NorFlash + ReadNorFlash; +/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. +#[repr(align(32))] +pub struct AlignedBuffer(pub [u8; N]); - fn flash(&mut self) -> &mut Self::FLASH; +impl AsRef<[u8]> for AlignedBuffer { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsMut<[u8]> for AlignedBuffer { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +/// Extension of the embedded-storage flash type information with block size and erase value. +pub trait Flash: NorFlash + ReadNorFlash { + /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase + /// size of the flash, but for external QSPI flash modules, this can be lower. + const BLOCK_SIZE: usize; + /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. + const ERASE_VALUE: u8 = 0xFF; } /// Trait defining the flash handles used for active and DFU partition -pub trait FlashProvider { - type STATE: FlashConfig; - type ACTIVE: FlashConfig; - type DFU: FlashConfig; +pub trait FlashConfig { + /// Flash type used for the state partition. + type STATE: Flash; + /// Flash type used for the active partition. + type ACTIVE: Flash; + /// Flash type used for the dfu partition. + type DFU: Flash; /// Return flash instance used to write/read to/from active partition. fn active(&mut self) -> &mut Self::ACTIVE; @@ -84,9 +105,7 @@ pub trait FlashProvider { /// BootLoader works with any flash implementing embedded_storage and can also work with /// different page sizes and flash write sizes. -/// -/// The PAGE_SIZE const parameter must be a multiple of the ACTIVE and DFU page sizes. -pub struct BootLoader { +pub struct BootLoader { // Page with current state of bootloader. The state partition has the following format: // | Range | Description | // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | @@ -98,15 +117,16 @@ pub struct BootLoader { dfu: Partition, } -impl BootLoader { +impl BootLoader { + /// Create a new instance of a bootloader with the given partitions. + /// + /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. + /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { - assert_eq!(active.len() % PAGE_SIZE, 0); - assert_eq!(dfu.len() % PAGE_SIZE, 0); - // DFU partition must have an extra page - assert!(dfu.len() - active.len() >= PAGE_SIZE); Self { active, dfu, state } } + /// Return the boot address for the active partition. pub fn boot_address(&self) -> usize { self.active.from } @@ -194,44 +214,43 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot(&mut self, p: &mut P) -> Result - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - [(); <

::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, - { + pub fn prepare_boot( + &mut self, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result { // Ensure we have enough progress pages to store copy progress - assert!( - self.active.len() / PAGE_SIZE - <= (self.state.len() - <

::STATE as FlashConfig>::FLASH::WRITE_SIZE) - / <

::STATE as FlashConfig>::FLASH::WRITE_SIZE - ); + assert_eq!(self.active.len() % page.len(), 0); + assert_eq!(self.dfu.len() % page.len(), 0); + assert!(self.dfu.len() - self.active.len() >= page.len()); + assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE); + assert_eq!(magic.len(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p.state())?; + let state = self.read_state(p, magic)?; match state { State::Swap => { // // Check if we already swapped. If we're in the swap state, this means we should revert // since the app has failed to mark boot as successful // - if !self.is_swapped(p.state())? { + if !self.is_swapped(p, magic, page)? { trace!("Swapping"); - self.swap(p)?; + self.swap(p, magic, page)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p)?; + self.revert(p, magic, page)?; // Overwrite magic and reset progress - let fstate = p.state().flash(); - let aligned = Aligned( - [!P::STATE::ERASE_VALUE; <

::STATE as FlashConfig>::FLASH::WRITE_SIZE], - ); - fstate.write(self.state.from as u32, &aligned.0)?; + let fstate = p.state(); + magic.fill(!P::STATE::ERASE_VALUE); + fstate.write(self.state.from as u32, magic)?; fstate.erase(self.state.from as u32, self.state.to as u32)?; - let aligned = - Aligned([BOOT_MAGIC; <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]); - fstate.write(self.state.from as u32, &aligned.0)?; + + magic.fill(BOOT_MAGIC); + fstate.write(self.state.from as u32, magic)?; } } _ => {} @@ -239,166 +258,152 @@ impl BootLoader { Ok(state) } - fn is_swapped(&mut self, p: &mut P) -> Result - where - [(); P::FLASH::WRITE_SIZE]:, - { - let page_count = self.active.len() / P::FLASH::ERASE_SIZE; - let progress = self.current_progress(p)?; + fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + let progress = self.current_progress(p, magic)?; Ok(progress >= page_count * 2) } - fn current_progress(&mut self, p: &mut P) -> Result - where - [(); P::FLASH::WRITE_SIZE]:, - { - let write_size = P::FLASH::WRITE_SIZE; + fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { + let write_size = aligned.len(); let max_index = ((self.state.len() - write_size) / write_size) - 1; - let flash = p.flash(); - let mut aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]); + aligned.fill(!P::STATE::ERASE_VALUE); + + let flash = config.state(); for i in 0..max_index { - flash.read((self.state.from + write_size + i * write_size) as u32, &mut aligned.0)?; - if aligned.0 == [P::ERASE_VALUE; P::FLASH::WRITE_SIZE] { + flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; + + if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { return Ok(i); } } Ok(max_index) } - fn update_progress(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> - where - [(); P::FLASH::WRITE_SIZE]:, - { - let flash = p.flash(); - let write_size = P::FLASH::WRITE_SIZE; + fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { + let flash = p.state(); + let write_size = magic.len(); let w = self.state.from + write_size + idx * write_size; - let aligned = Aligned([!P::ERASE_VALUE; P::FLASH::WRITE_SIZE]); - flash.write(w as u32, &aligned.0)?; + + let aligned = magic; + aligned.fill(!P::STATE::ERASE_VALUE); + flash.write(w as u32, aligned)?; Ok(()) } - fn active_addr(&self, n: usize) -> usize { - self.active.from + n * PAGE_SIZE + fn active_addr(&self, n: usize, page_size: usize) -> usize { + self.active.from + n * page_size } - fn dfu_addr(&self, n: usize) -> usize { - self.dfu.from + n * PAGE_SIZE + fn dfu_addr(&self, n: usize, page_size: usize) -> usize { + self.dfu.from + n * page_size } - fn copy_page_once_to_active( + fn copy_page_once_to_active( &mut self, idx: usize, from_page: usize, to_page: usize, p: &mut P, - ) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; - if self.current_progress(p.state())? <= idx { + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { let mut offset = from_page; for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { - p.dfu().flash().read(offset as u32, chunk)?; + p.dfu().read(offset as u32, chunk)?; offset += chunk.len(); } - p.active().flash().erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?; + p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; let mut offset = to_page; for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { - p.active().flash().write(offset as u32, &chunk)?; + p.active().write(offset as u32, chunk)?; offset += chunk.len(); } - self.update_progress(idx, p.state())?; + self.update_progress(idx, p, magic)?; } Ok(()) } - fn copy_page_once_to_dfu( + fn copy_page_once_to_dfu( &mut self, idx: usize, from_page: usize, to_page: usize, p: &mut P, - ) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let mut buf: [u8; PAGE_SIZE] = [0; PAGE_SIZE]; - if self.current_progress(p.state())? <= idx { + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { let mut offset = from_page; for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - p.active().flash().read(offset as u32, chunk)?; + p.active().read(offset as u32, chunk)?; offset += chunk.len(); } - p.dfu().flash().erase(to_page as u32, (to_page + PAGE_SIZE) as u32)?; + p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; let mut offset = to_page; for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { - p.dfu().flash().write(offset as u32, chunk)?; + p.dfu().write(offset as u32, chunk)?; offset += chunk.len(); } - self.update_progress(idx, p.state())?; + self.update_progress(idx, p, magic)?; } Ok(()) } - fn swap(&mut self, p: &mut P) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let page_count = self.active.len() / PAGE_SIZE; + fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; trace!("Page count: {}", page_count); - for page in 0..page_count { - trace!("COPY PAGE {}", page); + for page_num in 0..page_count { + trace!("COPY PAGE {}", page_num); // Copy active page to the 'next' DFU page. - let active_page = self.active_addr(page_count - 1 - page); - let dfu_page = self.dfu_addr(page_count - page); + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - page_num, page_size); //trace!("Copy active {} to dfu {}", active_page, dfu_page); - self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?; + self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; // Copy DFU page to the active page - let active_page = self.active_addr(page_count - 1 - page); - let dfu_page = self.dfu_addr(page_count - 1 - page); + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); //trace!("Copy dfy {} to active {}", dfu_page, active_page); - self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; + self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; } Ok(()) } - fn revert(&mut self, p: &mut P) -> Result<(), BootError> - where - [(); <

::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - { - let page_count = self.active.len() / PAGE_SIZE; - for page in 0..page_count { + fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + for page_num in 0..page_count { // Copy the bad active page to the DFU page - let active_page = self.active_addr(page); - let dfu_page = self.dfu_addr(page); - self.copy_page_once_to_dfu(page_count * 2 + page * 2, active_page, dfu_page, p)?; + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num, page_size); + self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; // Copy the DFU page back to the active page - let active_page = self.active_addr(page); - let dfu_page = self.dfu_addr(page + 1); - self.copy_page_once_to_active(page_count * 2 + page * 2 + 1, dfu_page, active_page, p)?; + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num + 1, page_size); + self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; } Ok(()) } - fn read_state(&mut self, p: &mut P) -> Result - where - [(); P::FLASH::WRITE_SIZE]:, - { - let mut magic: [u8; P::FLASH::WRITE_SIZE] = [0; P::FLASH::WRITE_SIZE]; - let flash = p.flash(); - flash.read(self.state.from as u32, &mut magic)?; + fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { + let flash = config.state(); + flash.read(self.state.from as u32, magic)?; - if magic == [SWAP_MAGIC; P::FLASH::WRITE_SIZE] { + if !magic.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) } else { Ok(State::Boot) @@ -406,108 +411,149 @@ impl BootLoader { } } -/// Convenience provider that uses a single flash for everything -pub struct SingleFlashProvider<'a, F, const ERASE_VALUE: u8 = 0xFF> +/// Convenience provider that uses a single flash for all partitions. +pub struct SingleFlashConfig<'a, F> where - F: NorFlash + ReadNorFlash, + F: Flash, { - config: SingleFlashConfig<'a, F, ERASE_VALUE>, + flash: &'a mut F, } -impl<'a, F, const ERASE_VALUE: u8> SingleFlashProvider<'a, F, ERASE_VALUE> +impl<'a, F> SingleFlashConfig<'a, F> where - F: NorFlash + ReadNorFlash, + F: Flash, { + /// Create a provider for a single flash. pub fn new(flash: &'a mut F) -> Self { - Self { - config: SingleFlashConfig { flash }, - } + Self { flash } } } -pub struct SingleFlashConfig<'a, F, const ERASE_VALUE: u8 = 0xFF> +impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> +where + F: Flash, +{ + type STATE = F; + type ACTIVE = F; + type DFU = F; + + fn active(&mut self) -> &mut Self::STATE { + self.flash + } + fn dfu(&mut self) -> &mut Self::ACTIVE { + self.flash + } + fn state(&mut self) -> &mut Self::DFU { + self.flash + } +} + +/// A flash wrapper implementing the Flash and embedded_storage traits. +pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> where F: NorFlash + ReadNorFlash, { flash: &'a mut F, } -impl<'a, F> FlashProvider for SingleFlashProvider<'a, F> +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> where F: NorFlash + ReadNorFlash, { - type STATE = SingleFlashConfig<'a, F>; - type ACTIVE = SingleFlashConfig<'a, F>; - type DFU = SingleFlashConfig<'a, F>; - - fn active(&mut self) -> &mut Self::STATE { - &mut self.config - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - &mut self.config - } - fn state(&mut self) -> &mut Self::DFU { - &mut self.config + /// Create a new instance of a bootable flash + pub fn new(flash: &'a mut F) -> Self { + Self { flash } } } -impl<'a, F, const ERASE_VALUE: u8> FlashConfig for SingleFlashConfig<'a, F, ERASE_VALUE> +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> where F: NorFlash + ReadNorFlash, { - const BLOCK_SIZE: usize = F::ERASE_SIZE; + const BLOCK_SIZE: usize = BLOCK_SIZE; const ERASE_VALUE: u8 = ERASE_VALUE; - type FLASH = F; - fn flash(&mut self) -> &mut F { - self.flash +} + +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +where + F: ReadNorFlash + NorFlash, +{ + type Error = F::Error; +} + +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +where + F: ReadNorFlash + NorFlash, +{ + const WRITE_SIZE: usize = F::WRITE_SIZE; + const ERASE_SIZE: usize = F::ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + F::erase(self.flash, from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + F::write(self.flash, offset, bytes) } } -/// Convenience provider that uses a single flash for everything -pub struct MultiFlashProvider<'a, ACTIVE, STATE, DFU> +impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> where - ACTIVE: NorFlash + ReadNorFlash, - STATE: NorFlash + ReadNorFlash, - DFU: NorFlash + ReadNorFlash, + F: ReadNorFlash + NorFlash, { - active: SingleFlashConfig<'a, ACTIVE>, - state: SingleFlashConfig<'a, STATE>, - dfu: SingleFlashConfig<'a, DFU>, + const READ_SIZE: usize = F::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + F::read(self.flash, offset, bytes) + } + + fn capacity(&self) -> usize { + F::capacity(self.flash) + } } -impl<'a, ACTIVE, STATE, DFU> MultiFlashProvider<'a, ACTIVE, STATE, DFU> +/// Convenience flash provider that uses separate flash instances for each partition. +pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: NorFlash + ReadNorFlash, - STATE: NorFlash + ReadNorFlash, - DFU: NorFlash + ReadNorFlash, + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, { + active: &'a mut ACTIVE, + state: &'a mut STATE, + dfu: &'a mut DFU, +} + +impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + /// Create a new flash provider with separate configuration for all three partitions. pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { - Self { - active: SingleFlashConfig { flash: active }, - state: SingleFlashConfig { flash: state }, - dfu: SingleFlashConfig { flash: dfu }, - } + Self { active, state, dfu } } } -impl<'a, ACTIVE, STATE, DFU> FlashProvider for MultiFlashProvider<'a, ACTIVE, STATE, DFU> +impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: NorFlash + ReadNorFlash, - STATE: NorFlash + ReadNorFlash, - DFU: NorFlash + ReadNorFlash, + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, { - type STATE = SingleFlashConfig<'a, STATE>; - type ACTIVE = SingleFlashConfig<'a, ACTIVE>; - type DFU = SingleFlashConfig<'a, DFU>; + type STATE = STATE; + type ACTIVE = ACTIVE; + type DFU = DFU; fn active(&mut self) -> &mut Self::ACTIVE { - &mut self.active + self.active } fn dfu(&mut self) -> &mut Self::DFU { - &mut self.dfu + self.dfu } fn state(&mut self) -> &mut Self::STATE { - &mut self.state + self.state } } @@ -518,10 +564,6 @@ pub struct FirmwareUpdater { dfu: Partition, } -// NOTE: Aligned to the largest write size supported by flash -#[repr(align(32))] -pub struct Aligned([u8; N]); - impl Default for FirmwareUpdater { fn default() -> Self { extern "C" { @@ -551,6 +593,7 @@ impl Default for FirmwareUpdater { } impl FirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. pub const fn new(dfu: Partition, state: Partition) -> Self { Self { dfu, state } } @@ -560,23 +603,24 @@ impl FirmwareUpdater { self.dfu.len() } - /// Instruct bootloader that DFU should commence at next boot. - /// Must be provided with an aligned buffer to use for reading and writing magic; - pub async fn update(&mut self, flash: &mut F) -> Result<(), F::Error> - where - [(); F::WRITE_SIZE]:, - { - let mut aligned = Aligned([0; { F::WRITE_SIZE }]); - self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_updated(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC, flash).await } - /// Mark firmware boot successfully - pub async fn mark_booted(&mut self, flash: &mut F) -> Result<(), F::Error> - where - [(); F::WRITE_SIZE]:, - { - let mut aligned = Aligned([0; { F::WRITE_SIZE }]); - self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_booted(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC, flash).await } async fn set_magic( @@ -587,7 +631,7 @@ impl FirmwareUpdater { ) -> Result<(), F::Error> { flash.read(self.state.from as u32, aligned).await?; - if aligned.iter().find(|&&b| b != magic).is_some() { + if aligned.iter().any(|&b| b != magic) { aligned.fill(0); flash.write(self.state.from as u32, aligned).await?; @@ -599,7 +643,13 @@ impl FirmwareUpdater { Ok(()) } - // Write to a region of the DFU page + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. pub async fn write_firmware( &mut self, offset: usize, @@ -668,7 +718,7 @@ mod tests { #[test] fn test_bad_magic() { let mut flash = MemFlash([0xff; 131072]); - let mut flash = SingleFlashProvider::new(&mut flash); + let mut flash = SingleFlashConfig::new(&mut flash); let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); @@ -687,11 +737,16 @@ mod tests { let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); - let mut flash = SingleFlashProvider::new(&mut flash); + let mut flash = SingleFlashConfig::new(&mut flash); - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); + let mut magic = [0; 4]; + let mut page = [0; 4096]; + assert_eq!( + State::Boot, + bootloader.prepare_boot(&mut flash, &mut magic, &mut page).unwrap() + ); } #[test] @@ -703,24 +758,27 @@ mod tests { let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let mut aligned = [0; 4]; for i in ACTIVE.from..ACTIVE.to { flash.0[i] = original[i - ACTIVE.from]; } - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut updater = FirmwareUpdater::new(DFU, STATE); let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap(); offset += chunk.len(); } - block_on(updater.update(&mut flash)).unwrap(); + block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); + let mut magic = [0; 4]; + let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) .unwrap() ); @@ -737,7 +795,7 @@ mod tests { assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) .unwrap() ); @@ -751,11 +809,11 @@ mod tests { } // Mark as booted - block_on(updater.mark_booted(&mut flash)).unwrap(); + block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); assert_eq!( State::Boot, bootloader - .prepare_boot(&mut SingleFlashProvider::new(&mut flash)) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) .unwrap() ); } @@ -769,6 +827,7 @@ mod tests { let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); + let mut aligned = [0; 4]; let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; @@ -781,16 +840,23 @@ mod tests { let mut offset = 0; for chunk in update.chunks(2048) { - block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); offset += chunk.len(); } - block_on(updater.update(&mut state)).unwrap(); + block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); + + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + let mut magic = [0; 4]; + let mut page = [0; 4096]; - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); assert_eq!( State::Swap, bootloader - .prepare_boot(&mut MultiFlashProvider::new(&mut active, &mut state, &mut dfu,)) + .prepare_boot( + &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), + &mut magic, + &mut page + ) .unwrap() ); @@ -810,6 +876,7 @@ mod tests { const ACTIVE: Partition = Partition::new(4096, 16384); const DFU: Partition = Partition::new(0, 16384); + let mut aligned = [0; 4]; let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); @@ -825,16 +892,22 @@ mod tests { let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, &chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); offset += chunk.len(); } - block_on(updater.update(&mut state)).unwrap(); + block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); - let mut bootloader: BootLoader<4096> = BootLoader::new(ACTIVE, DFU, STATE); + let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + let mut magic = [0; 4]; + let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot(&mut MultiFlashProvider::new(&mut active, &mut state, &mut dfu,)) + .prepare_boot( + &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), + &mut magic, + &mut page + ) .unwrap() ); @@ -899,6 +972,13 @@ mod tests { } } + impl super::Flash + for MemFlash + { + const BLOCK_SIZE: usize = ERASE_SIZE; + const ERASE_VALUE: u8 = 0xFF; + } + impl AsyncReadNorFlash for MemFlash { diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 2d6b837cb..0c14781a2 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,19 +1,21 @@ #![no_std] #![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] - +#![warn(missing_docs)] +#![doc = include_str!("../../README.md")] mod fmt; -pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider}; +pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +/// A bootloader for nRF devices. pub struct BootLoader { - boot: embassy_boot::BootLoader, + boot: embassy_boot::BootLoader, + magic: AlignedBuffer<4>, + page: AlignedBuffer, } impl BootLoader { @@ -58,21 +60,25 @@ impl BootLoader { pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { Self { boot: embassy_boot::BootLoader::new(active, dfu, state), + magic: AlignedBuffer([0; 4]), + page: AlignedBuffer([0; PAGE_SIZE]), } } - /// Boots the application without softdevice mechanisms - pub fn prepare(&mut self, flash: &mut F) -> usize - where - [(); <::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - [(); <::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, - { - match self.boot.prepare_boot(flash) { + /// 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, &mut self.magic.0, &mut self.page.0) { Ok(_) => self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } } + /// Boots the application without softdevice mechanisms. + /// + /// # Safety + /// + /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] pub unsafe fn load(&mut self, start: usize) -> ! { let mut p = cortex_m::Peripherals::steal(); @@ -81,6 +87,11 @@ impl BootLoader { cortex_m::asm::bootload(start as *const u32) } + /// Boots the application assuming softdevice is present. + /// + /// # Safety + /// + /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(feature = "softdevice")] pub unsafe fn load(&mut self, _app: usize) -> ! { use nrf_softdevice_mbr as mbr; diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 5a4f2d058..39f080517 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,19 +1,20 @@ #![no_std] #![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] - +#![warn(missing_docs)] +#![doc = include_str!("../../README.md")] mod fmt; -pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State}; -use embedded_storage::nor_flash::NorFlash; +pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; -pub struct BootLoader { - boot: embassy_boot::BootLoader, +/// A bootloader for STM32 devices. +pub struct BootLoader { + boot: embassy_boot::BootLoader, + magic: AlignedBuffer, + page: AlignedBuffer, } -impl BootLoader { +impl BootLoader { /// Create a new bootloader instance using parameters from linker script pub fn default() -> Self { extern "C" { @@ -55,21 +56,25 @@ impl BootLoader { 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; PAGE_SIZE]), } } - /// Boots the application - pub fn prepare(&mut self, flash: &mut F) -> usize - where - [(); <::STATE as FlashConfig>::FLASH::WRITE_SIZE]:, - [(); <::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:, - { - match self.boot.prepare_boot(flash) { + /// 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_stm32::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)] diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index bd8fa3246..133a3e678 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -36,7 +36,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); offset += chunk.len(); } - updater.update(&mut nvmc).await.unwrap(); + let mut magic = [0; 4]; + updater.mark_updated(&mut nvmc, &mut magic).await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 11eecc5e2..fdbd5ab99 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index a3b66e7c9..c08880fb3 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5669527fe..7a76a8db5 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -4,7 +4,7 @@ name = "embassy-boot-stm32h7-examples" version = "0.1.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +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", "tick-32768hz"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32h7/flash-boot.sh b/examples/boot/application/stm32h7/flash-boot.sh index debdb17a7..a3003681a 100755 --- a/examples/boot/application/stm32h7/flash-boot.sh +++ b/examples/boot/application/stm32h7/flash-boot.sh @@ -1,8 +1,9 @@ #!/bin/bash +probe-rs-cli erase --chip STM32H743ZITx mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x cp memory-bl.x ../../bootloader/stm32/memory.x -cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32f767zi --chip STM32F767ZITx --target thumbv7em-none-eabihf +cargo flash --manifest-path ../../bootloader/stm32/Cargo.toml --release --features embassy-stm32/stm32h743zi --chip STM32H743ZITx --target thumbv7em-none-eabihf rm ../../bootloader/stm32/memory.x mv ../../bootloader/stm32/memory-old.x ../../bootloader/stm32/memory.x diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 0ecf60348..f5a8fdb61 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -29,13 +29,17 @@ async fn main(_spawner: Spawner) { let mut updater = FirmwareUpdater::default(); button.wait_for_rising_edge().await; let mut offset = 0; - let mut buf: [u8; 128 * 1024] = [0; 128 * 1024]; + let mut buf = AlignedBuffer([0; 128 * 1024]); for chunk in APP_B.chunks(128 * 1024) { - buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + buf.as_mut()[..chunk.len()].copy_from_slice(chunk); + updater + .write_firmware(offset, buf.as_ref(), &mut flash, 2048) + .await + .unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index f4f1d7119..f0b0b80e3 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_time::{Duration, Timer}; use panic_reset as _; @@ -38,7 +38,8 @@ async fn main(_spawner: Spawner) { offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index f4f1d7119..f0b0b80e3 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_time::{Duration, Timer}; use panic_reset as _; @@ -38,7 +38,8 @@ async fn main(_spawner: Spawner) { offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 178b2e04a..5119bad2e 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -35,7 +35,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index c71a42654..faa650778 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -4,11 +4,11 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::FirmwareUpdater; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use panic_reset as _; @@ -37,7 +37,8 @@ async fn main(_spawner: Spawner) { updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); offset += chunk.len(); } - updater.update(&mut flash).await.unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); //defmt::info!("Marked as updated"); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index bc7e0755f..9031997c2 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -20,10 +20,8 @@ fn main() -> ! { */ let mut bl = BootLoader::default(); - let start = bl.prepare(&mut SingleFlashProvider::new(&mut WatchdogFlash::start( - Nvmc::new(p.NVMC), - p.WDT, - 5, + let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( + &mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), ))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 45c511ced..bb5d3e531 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, ERASE_SIZE}; +use embassy_stm32::flash::{Flash, ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; #[entry] fn main() -> ! { @@ -19,9 +19,11 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); + let mut bl: BootLoader = BootLoader::default(); let mut flash = Flash::unlock(p.FLASH); - let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash)); + let start = bl.prepare(&mut SingleFlashConfig::new( + &mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash), + )); core::mem::drop(flash); unsafe { bl.load(start) } } From 3aa0c13ba5cbef09b825619f22221d028532732d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 2 Sep 2022 08:42:42 +0200 Subject: [PATCH 0099/1575] Fix a few clippy warnings --- embassy-boot/boot/src/lib.rs | 44 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index e8ebe628d..4a2b112a9 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -28,6 +28,7 @@ impl Partition { } /// Return the length of the partition + #[allow(clippy::len_without_is_empty)] pub const fn len(&self) -> usize { self.to - self.from } @@ -229,31 +230,28 @@ impl BootLoader { // Copy contents from partition N to active let state = self.read_state(p, magic)?; - match state { - State::Swap => { - // - // Check if we already swapped. If we're in the swap state, this means we should revert - // since the app has failed to mark boot as successful - // - if !self.is_swapped(p, magic, page)? { - trace!("Swapping"); - self.swap(p, magic, page)?; - trace!("Swapping done"); - } else { - trace!("Reverting"); - self.revert(p, magic, page)?; + if state == State::Swap { + // + // Check if we already swapped. If we're in the swap state, this means we should revert + // since the app has failed to mark boot as successful + // + if !self.is_swapped(p, magic, page)? { + trace!("Swapping"); + self.swap(p, magic, page)?; + trace!("Swapping done"); + } else { + trace!("Reverting"); + self.revert(p, magic, page)?; - // Overwrite magic and reset progress - let fstate = p.state(); - magic.fill(!P::STATE::ERASE_VALUE); - fstate.write(self.state.from as u32, magic)?; - fstate.erase(self.state.from as u32, self.state.to as u32)?; + // Overwrite magic and reset progress + let fstate = p.state(); + magic.fill(!P::STATE::ERASE_VALUE); + fstate.write(self.state.from as u32, magic)?; + fstate.erase(self.state.from as u32, self.state.to as u32)?; - magic.fill(BOOT_MAGIC); - fstate.write(self.state.from as u32, magic)?; - } + magic.fill(BOOT_MAGIC); + fstate.write(self.state.from as u32, magic)?; } - _ => {} } Ok(state) } @@ -1005,7 +1003,7 @@ mod tests { const ERASE_SIZE: usize = ERASE_SIZE; type EraseFuture<'a> = impl Future> + 'a; - fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { + fn erase(&mut self, from: u32, to: u32) -> Self::EraseFuture<'_> { async move { let from = from as usize; let to = to as usize; From d6e8a11ea70140619d494a622eea166fdc2bdf72 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 3 Sep 2022 14:50:37 +0200 Subject: [PATCH 0100/1575] Update documentation link This commit updates the documentation link which currently results in a 404 Not Found. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dbbb5f51..9f08bf676 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries. -## Documentation - API reference - Website - Chat +## Documentation - API reference - Website - Chat ## Rust + async ❤️ embedded The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system. From 506e5a44930aca6ae0b23c59bfb9bde02aa4e485 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 3 Sep 2022 15:09:36 +0200 Subject: [PATCH 0101/1575] Use embassy_executor::main in runtime.adoc This commit replaces embassy::main with embassy_executor::main in the runtime documentation page. Refs: https://embassy.dev/dev/runtime.html --- docs/modules/ROOT/pages/runtime.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/runtime.adoc b/docs/modules/ROOT/pages/runtime.adoc index a7d6a8d0c..5096f5a43 100644 --- a/docs/modules/ROOT/pages/runtime.adoc +++ b/docs/modules/ROOT/pages/runtime.adoc @@ -20,7 +20,7 @@ IMPORTANT: The executor relies on tasks not blocking indefinitely, as this preve image::embassy_executor.png[Executor model] -If you use the `#[embassy::main]` macro in your application, it creates the `Executor` for you and spawns the main entry point as the first task. You can also create the Executor manually, and you can in fact create multiple Executors. +If you use the `#[embassy_executor::main]` macro in your application, it creates the `Executor` for you and spawns the main entry point as the first task. You can also create the Executor manually, and you can in fact create multiple Executors. == Interrupts From 6cdff72d6d2becb3f8a4659571fd5e6a339cbefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 3 Sep 2022 20:36:18 +0200 Subject: [PATCH 0102/1575] run `cargo fmt` --- embassy-lora/src/stm32wl/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 9307cdebb..4d11244b6 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -7,8 +7,8 @@ use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; use embassy_stm32::subghz::{ CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, - LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, - StandbyClk, Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, + LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, + Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; @@ -158,8 +158,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio.set_buffer_base_address(0, 0)?; // NOTE: Upper layer handles timeout by cancelling the future - self.radio - .set_rx(Timeout::DISABLED)?; + self.radio.set_rx(Timeout::DISABLED)?; trace!("RX started"); From f66f20b1ce07e222d7419f856510b91d105cea3e Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 4 Sep 2022 10:00:02 -0700 Subject: [PATCH 0103/1575] usbd-hid: 0.5.2 -> 0.6.0 --- embassy-usb-hid/Cargo.toml | 2 +- examples/nrf/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-usb-hid/Cargo.toml b/embassy-usb-hid/Cargo.toml index 730351485..2f7733dc6 100644 --- a/embassy-usb-hid/Cargo.toml +++ b/embassy-usb-hid/Cargo.toml @@ -19,6 +19,6 @@ embassy-usb = { version = "0.1.0", path = "../embassy-usb" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -usbd-hid = { version = "0.5.2", optional = true } +usbd-hid = { version = "0.6.0", optional = true } ssmarshal = { version = "1.0", default-features = false, optional = true } futures-util = { version = "0.3.21", default-features = false } diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index b0190acc8..dbc659cda 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -30,5 +30,5 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" -usbd-hid = "0.5.2" +usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index ea3d5ab88..c4ff8f3b5 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -15,7 +15,7 @@ embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", fea embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -usbd-hid = "0.5.2" +usbd-hid = "0.6.0" defmt = "0.3" defmt-rtt = "0.3" From 95f3484b87d7ae829e14492a43d49a5a0a58f1b8 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 6 Sep 2022 11:38:33 +0000 Subject: [PATCH 0104/1575] Implement minimal tx flow control The credit update code uses constants from https://github.com/Infineon/wifi-host-driver/blob/master/WiFi_Host_Driver/src/whd_sdpcm.c#L307-L317 --- src/lib.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 34170a266..f818caf6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -525,6 +525,8 @@ pub struct Runner<'a, PWR, SPI> { ioctl_id: u16, sdpcm_seq: u8, backplane_window: u32, + + tx_seq_max: u8, } pub async fn new<'a, PWR, SPI>( @@ -546,6 +548,8 @@ where ioctl_id: 0, sdpcm_seq: 0, backplane_window: 0xAAAA_AAAA, + + tx_seq_max: 1, }; runner.init(firmware).await; @@ -670,13 +674,17 @@ where loop { // Send stuff // TODO flow control - if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { - self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; - self.state.ioctl_state.set(IoctlState::Sent { buf }); - } + if self.sdpcm_seq == self.tx_seq_max || self.tx_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 != 0 { + warn!("TX stalled"); + } else { + if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { + self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; + self.state.ioctl_state.set(IoctlState::Sent { buf }); + } - if let Ok(p) = self.state.tx_channel.try_recv() { - self.send_packet(&p).await; + if let Ok(p) = self.state.tx_channel.try_recv() { + self.send_packet(&p).await; + } } // Receive stuff @@ -788,6 +796,8 @@ where return; } + self.update_credit(&sdpcm_header); + let channel = sdpcm_header.channel_and_flags & 0x0f; let payload = &packet[sdpcm_header.header_length as _..]; @@ -894,6 +904,16 @@ where } } + fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { + if sdpcm_header.channel_and_flags & 0xf < 3 { + let mut tx_seq_max = sdpcm_header.bus_data_credit; + if tx_seq_max - self.sdpcm_seq > 0x40 { + tx_seq_max = self.sdpcm_seq + 2; + } + self.tx_seq_max = tx_seq_max; + } + } + async fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) { let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); From 5c4d6232ae5822d70cba4dbd60cfe348a7f0d687 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 6 Sep 2022 20:50:27 +0000 Subject: [PATCH 0105/1575] Fixes after review - rename tx_seq_max to sdpcm_seq_max - make sure we have credit for each packet we send --- src/lib.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f818caf6b..5e79e6e40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -526,7 +526,7 @@ pub struct Runner<'a, PWR, SPI> { sdpcm_seq: u8, backplane_window: u32, - tx_seq_max: u8, + sdpcm_seq_max: u8, } pub async fn new<'a, PWR, SPI>( @@ -549,7 +549,7 @@ where sdpcm_seq: 0, backplane_window: 0xAAAA_AAAA, - tx_seq_max: 1, + sdpcm_seq_max: 1, }; runner.init(firmware).await; @@ -673,17 +673,20 @@ where let mut buf = [0; 512]; loop { // Send stuff - // TODO flow control - if self.sdpcm_seq == self.tx_seq_max || self.tx_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 != 0 { + // TODO flow control not yet complete + if !self.has_credit() { warn!("TX stalled"); } else { if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; self.state.ioctl_state.set(IoctlState::Sent { buf }); } - - if let Ok(p) = self.state.tx_channel.try_recv() { - self.send_packet(&p).await; + if !self.has_credit() { + warn!("TX stalled"); + } else { + if let Ok(p) = self.state.tx_channel.try_recv() { + self.send_packet(&p).await; + } } } @@ -906,14 +909,18 @@ where fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { if sdpcm_header.channel_and_flags & 0xf < 3 { - let mut tx_seq_max = sdpcm_header.bus_data_credit; - if tx_seq_max - self.sdpcm_seq > 0x40 { - tx_seq_max = self.sdpcm_seq + 2; + let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; + if sdpcm_seq_max - self.sdpcm_seq > 0x40 { + sdpcm_seq_max = self.sdpcm_seq + 2; } - self.tx_seq_max = tx_seq_max; + self.sdpcm_seq_max = sdpcm_seq_max; } } + fn has_credit(&mut self) -> bool { + self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 + } + async fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) { let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); From ea0738c4851cbeb87de0d40ce1e8246368db4c6b Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 6 Sep 2022 21:06:47 +0000 Subject: [PATCH 0106/1575] Add gpio_set Example: Blink LED ``` loop { info!("on"); control.gpio_set(0, true).await; Timer::after(Duration::from_millis(200)).await; info!("off"); control.gpio_set(0, false).await; Timer::after(Duration::from_millis(200)).await; } ``` --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5e79e6e40..21b8b2d80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -397,6 +397,12 @@ impl<'a> Control<'a> { info!("JOINED"); } + pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { + assert!(gpio_n < 3); + self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) + .await + } + async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { let mut buf = [0; 8]; buf[0..4].copy_from_slice(&val1.to_le_bytes()); From 7004b095c346e1a18d30f0f96227cdfeced05e6c Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 2 Sep 2022 15:27:15 +0200 Subject: [PATCH 0107/1575] Add critical-section/std to std feature This commit suggests adding critical-section/std to the std feature as without this a link time error is generated. --- embassy-executor/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 409a341f0..fa3d0b2b6 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -22,7 +22,7 @@ flavors = [ [features] default = [] -std = ["embassy-macros/std"] +std = ["embassy-macros/std", "critical-section/std"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"] # Enable nightly-only features From 34ed3441ce4924be4b637fb86ee4aa003dcc0e8d Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Thu, 8 Sep 2022 17:01:45 +0200 Subject: [PATCH 0108/1575] Fix typo in peripheral.rs --- embassy-hal-common/src/peripheral.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-common/src/peripheral.rs index 038cebb5e..f507468f8 100644 --- a/embassy-hal-common/src/peripheral.rs +++ b/embassy-hal-common/src/peripheral.rs @@ -6,7 +6,7 @@ use core::ops::{Deref, DerefMut}; /// This is functionally the same as a `&'a mut T`. The reason for having a /// dedicated struct is memory efficiency: /// -/// Peripheral singletons are typically either zero-sized (for concrete peripehrals +/// Peripheral singletons are typically either zero-sized (for concrete peripherals /// like `PA9` or `Spi4`) or very small (for example `AnyPin` which is 1 byte). /// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized. /// PeripheralRef stores a copy of `T` instead, so it's the same size. From 9611e7c9f2609271af2ead7e91e6ee918d3dadd3 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:05:12 +0200 Subject: [PATCH 0109/1575] Add BufferedUart implementation, and feature-guard time-driver initialization, to free up TIMER peripheral if not used with embassy executor --- embassy-rp/Cargo.toml | 45 ++-- embassy-rp/src/lib.rs | 2 + embassy-rp/src/uart/buffered.rs | 286 ++++++++++++++++++++++++ embassy-rp/src/{uart.rs => uart/mod.rs} | 84 ++++++- 4 files changed, 401 insertions(+), 16 deletions(-) create mode 100644 embassy-rp/src/uart/buffered.rs rename embassy-rp/src/{uart.rs => uart/mod.rs} (87%) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..92780ee39 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -7,9 +7,7 @@ edition = "2021" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] -flavors = [ - { name = "rp2040", target = "thumbv6m-none-eabi" }, -] +flavors = [{ name = "rp2040", target = "thumbv6m-none-eabi" }] [features] defmt = ["dep:defmt", "embassy-usb?/defmt"] @@ -20,8 +18,16 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] +time-driver = [] + # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] +nightly = [ + "embassy-executor/nightly", + "embedded-hal-1", + "embedded-hal-async", + "embassy-embedded-hal/nightly", + "dep:embassy-usb", +] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -30,11 +36,15 @@ unstable-traits = ["embedded-hal-1"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } -embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } -embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ + "tick-hz-1_000_000", +] } +embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = [ + "prio-bits-2", +] } +embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-usb = { version = "0.1.0", path = "../embassy-usb", optional = true } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -43,11 +53,18 @@ cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.1" -futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", +] } +embedded-io = { version = "0.3.0", features = ["async"], optional = true } -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } +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.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ + "unproven", +] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true } +embedded-hal-async = { version = "0.1.0-alpha.1", optional = true } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..8dcefece2 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; +#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -108,6 +109,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); + #[cfg(feature = "time-driver")] timer::init(); dma::init(); } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs new file mode 100644 index 000000000..c31af8018 --- /dev/null +++ b/embassy-rp/src/uart/buffered.rs @@ -0,0 +1,286 @@ +use core::future::Future; +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 futures::future::poll_fn; + +use super::*; + +pub struct State<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> State<'d, T> { + pub fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct StateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + rx_waker: WakerRegistration, + rx: RingBuffer<'d>, + + tx_waker: WakerRegistration, + tx: RingBuffer<'d>, +} + +unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} + +pub struct BufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, StateInner<'d, T>>, +} + +impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} + +impl<'d, T: Instance> BufferedUart<'d, T> { + pub fn new( + state: &'d mut State<'d, T>, + _uart: Uart<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + ) -> BufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + phantom: PhantomData, + tx: RingBuffer::new(tx_buffer), + tx_waker: WakerRegistration::new(), + + rx: RingBuffer::new(rx_buffer), + rx_waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> StateInner<'d, T> +where + Self: 'd, +{ + fn on_rx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rxic(true); + w.set_rtic(true); + }); + + if ris.rxris() { + if ris.peris() { + warn!("Parity error"); + } + if ris.feris() { + warn!("Framing error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + let buf = self.rx.push_buf(); + if !buf.is_empty() { + buf[0] = r.uartdr().read().data(); + self.rx.push(1); + } else { + warn!("RX buffer full, discard received byte"); + } + + if self.rx.is_full() { + self.rx_waker.wake(); + } + } + + if ris.rtris() { + self.rx_waker.wake(); + }; + } + } + + fn on_tx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rtic(true); + }); + + if ris.txris() { + let buf = self.tx.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.tx.pop(1); + self.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); + } + } + } + } +} + +impl<'d, T: Instance> PeripheralState for StateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.on_rx(); + self.on_tx(); + } +} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + 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); + + // We have data ready in buffer? Return it. + let buf = state.rx.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)); + } + + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + 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| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.tx.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart/mod.rs similarity index 87% rename from embassy-rp/src/uart.rs rename to embassy-rp/src/uart/mod.rs index 987b716b4..3b71d87be 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart/mod.rs @@ -475,6 +475,76 @@ mod eh1 { impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> { + fn read(&mut self) -> nb::Result { + let r = T::regs(); + unsafe { + let dr = r.uartdr().read(); + + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else if dr.fe() { + Ok(dr.data()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + } #[cfg(all( @@ -532,6 +602,12 @@ mod eha { } } +#[cfg(feature = "nightly")] +mod buffered; +#[cfg(feature = "nightly")] +pub use buffered::*; + + mod sealed { use super::*; @@ -541,6 +617,8 @@ mod sealed { const TX_DREQ: u8; const RX_DREQ: u8; + type Interrupt: crate::interrupt::Interrupt; + fn regs() -> pac::uart::Uart; } pub trait TxPin {} @@ -571,6 +649,8 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { pac::$inst @@ -580,8 +660,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0, 20, 21); -impl_instance!(UART1, UART1, 22, 23); +impl_instance!(UART0, UART0_IRQ, 20, 21); +impl_instance!(UART1, UART1_IRQ, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} From 31d85da78a06cdee6ec037d73b12537d3c906725 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:36:27 +0200 Subject: [PATCH 0110/1575] Add bufferedUart, including a split version for only Rx or Tx --- embassy-rp/src/uart/buffered.rs | 379 ++++++++++++++++++++++++++------ embassy-rp/src/uart/mod.rs | 2 +- 2 files changed, 315 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index c31af8018..3eb96e3d5 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -9,31 +9,70 @@ use futures::future::poll_fn; use super::*; -pub struct State<'d, T: Instance>(StateStorage>); +pub struct State<'d, T: Instance>(StateStorage>); impl<'d, T: Instance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } -struct StateInner<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - - rx_waker: WakerRegistration, - rx: RingBuffer<'d>, - - tx_waker: WakerRegistration, - tx: RingBuffer<'d>, +pub struct RxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> RxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } } -unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} +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, StateInner<'d, T>>, + inner: PeripheralMutex<'d, FullStateInner<'d, T>>, +} + +pub struct RxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, RxStateInner<'d, T>>, +} + +pub struct TxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, TxStateInner<'d, T>>, } impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for RxBufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for TxBufferedUart<'d, T> {} impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( @@ -55,66 +94,158 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { - phantom: PhantomData, - tx: RingBuffer::new(tx_buffer), - tx_waker: WakerRegistration::new(), - - rx: RingBuffer::new(rx_buffer), - rx_waker: WakerRegistration::new(), + 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), + }, }), } } } -impl<'d, T: Instance> StateInner<'d, T> +impl<'d, T: Instance> RxBufferedUart<'d, T> { + pub fn new( + state: &'d mut RxState<'d, T>, + _uart: UartRx<'d, T, M>, + irq: impl Peripheral

+ 'd, + rx_buffer: &'d mut [u8], + ) -> RxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(rx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> TxBufferedUart<'d, T> { + pub fn new( + state: &'d mut TxState<'d, T>, + _uart: UartTx<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + ) -> TxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(tx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T> where Self: 'd, { - fn on_rx(&mut self) { + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.rx.on_interrupt(); + self.tx.on_interrupt(); + } +} + +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(); + let ris = r.uartmis().read(); // Clear interrupt flags - r.uarticr().write(|w| { + r.uarticr().modify(|w| { w.set_rxic(true); w.set_rtic(true); }); - if ris.rxris() { - if ris.peris() { + if ris.rxmis() { + if ris.pemis() { warn!("Parity error"); + r.uarticr().modify(|w| { + w.set_peic(true); + }); } - if ris.feris() { + if ris.femis() { warn!("Framing error"); + r.uarticr().modify(|w| { + w.set_feic(true); + }); } - if ris.beris() { + if ris.bemis() { warn!("Break error"); + r.uarticr().modify(|w| { + w.set_beic(true); + }); } - if ris.oeris() { + if ris.oemis() { warn!("Overrun error"); + r.uarticr().modify(|w| { + w.set_oeic(true); + }); } - let buf = self.rx.push_buf(); + let buf = self.buf.push_buf(); if !buf.is_empty() { buf[0] = r.uartdr().read().data(); - self.rx.push(1); + self.buf.push(1); } else { warn!("RX buffer full, discard received byte"); } - if self.rx.is_full() { - self.rx_waker.wake(); + if self.buf.is_full() { + self.waker.wake(); } } - if ris.rtris() { - self.rx_waker.wake(); + if ris.rtmis() { + self.waker.wake(); }; } } +} - fn on_tx(&mut self) { +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 ris = r.uartris().read(); @@ -124,14 +255,14 @@ where }); if ris.txris() { - let buf = self.tx.pop_buf(); + 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.tx.pop(1); - self.tx_waker.wake(); + self.buf.pop(1); + self.waker.wake(); } else { // Disable interrupt until we have something to transmit again r.uartimsc().modify(|w| { @@ -143,17 +274,6 @@ where } } -impl<'d, T: Instance> PeripheralState for StateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.on_rx(); - self.on_tx(); - } -} - impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other @@ -164,8 +284,16 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } +impl<'d, T: Instance> embedded_io::Io for RxBufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance> embedded_io::Io for TxBufferedUart<'d, T> { + type Error = Error; +} + impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> where Self: 'a; @@ -176,20 +304,58 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); + let data = state.rx.buf.pop_buf(); if !data.is_empty() { let len = data.len().min(buf.len()); buf[..len].copy_from_slice(&data[..len]); - if state.rx.is_full() { + if state.rx.buf.is_full() { do_pend = true; } - state.rx.pop(len); + state.rx.buf.pop(len); return Poll::Ready(Ok(len)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.buf.is_full() { + do_pend = true; + } + state.buf.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.waker.register(cx.waker()); Poll::Pending }); @@ -213,7 +379,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); + let buf = state.rx.buf.pop_buf(); if !buf.is_empty() { let buf: &[u8] = buf; // Safety: buffer lives as long as uart @@ -221,7 +387,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> return Poll::Ready(Ok(buf)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); Poll::>::Pending }) }) @@ -229,8 +395,45 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> fn consume(&mut self, amt: usize) { let signal = self.inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); + let full = state.rx.buf.is_full(); + state.rx.buf.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + 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); + + // We have data ready in buffer? Return it. + let buf = state.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)); + } + + state.waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.buf.is_full(); + state.buf.pop(amt); full }); if signal { @@ -247,16 +450,16 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); + let empty = state.tx.buf.is_empty(); + let tx_buf = state.tx.buf.push_buf(); if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); + state.tx.waker.register(cx.waker()); return (Poll::Pending, empty); } let n = core::cmp::min(tx_buf.len(), buf.len()); tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); + state.tx.buf.push(n); (Poll::Ready(Ok(n)), empty) }); @@ -274,8 +477,54 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { poll_fn(move |cx| { self.inner.with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); + if !state.tx.buf.is_empty() { + state.tx.waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + 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| { + let empty = state.buf.is_empty(); + let tx_buf = state.buf.push_buf(); + if tx_buf.is_empty() { + state.waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.buf.is_empty() { + state.waker.register(cx.waker()); return Poll::Pending; } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 3b71d87be..67e24b605 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -343,7 +343,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_stp2(config.stop_bits == StopBits::STOP2); w.set_pen(pen); w.set_eps(eps); - w.set_fen(true); + w.set_fen(false); }); r.uartcr().write(|w| { From b2d0f8d5903f868277732b4b12365945783d1720 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:49:47 +0200 Subject: [PATCH 0111/1575] Formatting --- embassy-rp/src/uart/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 67e24b605..76ecdf7ac 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -544,7 +544,6 @@ mod eh1 { self.blocking_flush().map_err(nb::Error::Other) } } - } #[cfg(all( @@ -607,7 +606,6 @@ mod buffered; #[cfg(feature = "nightly")] pub use buffered::*; - mod sealed { use super::*; @@ -649,7 +647,7 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { From fe5229670f40757f63e56c68388be241b5470bf6 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 9 Sep 2022 11:57:02 +0200 Subject: [PATCH 0112/1575] Add constants for ioctl commands This commit adds contants for the IOCTL commands that are currently used in cyw43::Control. --- src/lib.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 21b8b2d80..3f801fe9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,6 +126,12 @@ const IRQ_F1_INTR: u16 = 0x2000; const IRQ_F2_INTR: u16 = 0x4000; const IRQ_F3_INTR: u16 = 0x8000; +const IOCTL_CMD_UP: u32 = 2; +const IOCTL_CMD_SET_SSID: u32 = 26; +const IOCTL_CMD_SET_VAR: u32 = 263; +const IOCTL_CMD_GET_VAR: u32 = 262; +const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; + #[derive(Clone, Copy, PartialEq, Eq)] enum Core { WLAN = 0, @@ -263,7 +269,8 @@ impl<'a> Control<'a> { buf[0..8].copy_from_slice(b"clmload\x00"); buf[8..20].copy_from_slice(&header.to_bytes()); buf[20..][..chunk.len()].copy_from_slice(&chunk); - self.ioctl(2, 263, 0, &mut buf[..8 + 12 + chunk.len()]).await; + self.ioctl(2, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) + .await; } // check clmload ok @@ -323,7 +330,7 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; // set wifi up - self.ioctl(2, 2, 0, &mut []).await; + self.ioctl(2, IOCTL_CMD_UP, 0, &mut []).await; Timer::after(Duration::from_millis(100)).await; @@ -360,7 +367,7 @@ impl<'a> Control<'a> { ssid: [0; 32], }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.ioctl(2, 26, 0, &mut i.to_bytes()).await; // set_ssid + self.ioctl(2, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()).await; // set_ssid info!("JOINED"); } @@ -381,7 +388,7 @@ impl<'a> Control<'a> { passphrase: [0; 64], }; pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); - self.ioctl(2, 268, 0, &mut pfi.to_bytes()).await; // WLC_SET_WSEC_PMK + self.ioctl(2, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()).await; // WLC_SET_WSEC_PMK self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) @@ -430,7 +437,7 @@ impl<'a> Control<'a> { buf[name.len() + 1..][..val.len()].copy_from_slice(val); let total_len = name.len() + 1 + val.len(); - self.ioctl(2, 263, 0, &mut buf[..total_len]).await; + self.ioctl(2, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]).await; } // TODO this is not really working, it always returns all zeros. @@ -442,7 +449,7 @@ impl<'a> Control<'a> { buf[name.len()] = 0; let total_len = max(name.len() + 1, res.len()); - let res_len = self.ioctl(0, 262, 0, &mut buf[..total_len]).await; + let res_len = self.ioctl(0, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]).await; let out_len = min(res.len(), res_len); res[..out_len].copy_from_slice(&buf[..out_len]); From f0b7f43c4104ea15c860d557c4a507681cba0d0d Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 9 Sep 2022 14:15:19 +0200 Subject: [PATCH 0113/1575] Use wrapping_sub in update_credit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit uses wrapping_sub for subtraction in update_credit. The motivation for this is that currently the rpi-pico-w example panics (at least for me) with the following error: 3.825277 INFO init done └─ cyw43::{impl#4}::init::{async_fn#0} @ /embassy/cyw43/src/fmt.rs:138 3.825486 INFO Downloading CLM... └─ cyw43::{impl#2}::init::{async_fn#0} @ /embassy/cyw43/src/fmt.rs:138 3.841328 WARN TX stalled └─ cyw43::{impl#4}::run::{async_fn#0} @ /embassy/cyw43/src/fmt.rs:151 3.845549 ERROR panicked at 'attempt to subtract with overflow', /embassy/cyw43/src/lib.rs:919:16 └─ panic_probe::print_defmt::print @ .cargo/registry/src/github.com-1ecc6299db9ec823/panic-probe-0.3.0/src/lib.rs:91 ──────────────────────────────────────────────────────────────────────────────── stack backtrace: 0: HardFaultTrampoline 1: lib::inline::__udf at ./asm/inline.rs:181:5 2: __udf at ./asm/lib.rs:51:17 3: cortex_m::asm::udf at .cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.7.6/src/asm.rs:43:5 4: rust_begin_unwind at .cargo/registry/src/github.com-1ecc6299db9ec823/panic-probe-0.3.0/src/lib.rs:72:9 5: core::panicking::panic_fmt at rustc/1c7b36d4db582cb47513a6c7176baaec1c3346ab/library/core/src/panicking.rs:142:14 6: core::panicking::panic at /rustc/1c7b36d4db582cb47513a6c7176baaec1c3346ab/library/core/src/panicking.rs:48:5 7: cyw43::Runner::update_credit at /embassy/cyw43/src/lib.rs:919:16 8: cyw43::Runner::rx at /embassy/cyw43/src/lib.rs:808:9 9: cyw43::Runner::run::{{closure}} at /embassy/cyw43/src/lib.rs:727:21 10: as core::future::future::Future>::poll at /rustc/1c7b36d4db582cb47513a6c7176baaec1c3346ab/library/core/src/future/mod.rs:91:19 11: cyw43_example_rpi_pico_w::__wifi_task_task::{{closure}} at src/main.rs:32:17 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 21b8b2d80..d9a21f4bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -916,7 +916,7 @@ where fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { if sdpcm_header.channel_and_flags & 0xf < 3 { let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; - if sdpcm_seq_max - self.sdpcm_seq > 0x40 { + if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { sdpcm_seq_max = self.sdpcm_seq + 2; } self.sdpcm_seq_max = sdpcm_seq_max; From be20512f17210ae179078c4bb082211d00d828da Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Mon, 12 Sep 2022 11:44:21 +0200 Subject: [PATCH 0114/1575] Add contants and update comment about ALP This commit add two constants and updates the comment about ALP. It was not easy to find the definition of ALP but after searching I found what I believe is the correct definition in section 3.3 "Clocks" in the referenced document below. Active Low Power (ALP): Supplied by an internal or external oscillator. This clock is requested by cores when accessing backplane registers in other cores or when performing minor computations. When an external crystal is used to provide reference clock, ALP clock frequency is determined by the frequency of the external oscillator. A 37.4 MHz reference clock is recommended. Refs: https://www.infineon.com/dgdl/Infineon-AN214828_Power_Consumption_Measurements-ApplicationNotes-v03_00-EN.pdf?fileId=8ac78c8c7cdc391c017d0d2803a4630d --- src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e145b821b..8e43f51f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,6 +92,9 @@ const BACKPLANE_WINDOW_SIZE: usize = 0x8000; const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; +// Active Low Power (ALP) clock constants +const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08; +const BACKPLANE_ALP_AVAIL: u8 = 0x40; // Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect (AI) // constants @@ -603,10 +606,11 @@ where // seems to break backplane??? eat the 4-byte delay instead, that's what the vendor drivers do... //self.write32(FUNC_BUS, REG_BUS_RESP_DELAY, 0).await; - // Init ALP (no idea what that stands for) clock - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x08).await; + // Init ALP (Active Low Power) clock + self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) + .await; info!("waiting for clock..."); - while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x40 == 0 {} + while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} info!("clock ok"); let chip_id = self.bp_read16(0x1800_0000).await; From ea5f2c71e063aff9fdc0bde04656f36a3883178d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Sep 2022 12:05:58 +0200 Subject: [PATCH 0115/1575] sync/signal: wake old waker on overflow instead of panicking. This makes behavior consistent with `WakerRegistration`. It allows canceling `wait` in one task and then calling `wait` in another. If two tasks are `wait`ing concurrently the signal will be received by only one of them, randomly. --- embassy-sync/src/signal.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index f6ebeb9b9..34201d03a 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -79,7 +79,11 @@ impl Signal { Poll::Pending } State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, - State::Waiting(_) => panic!("waker overflow"), + State::Waiting(w) => { + let w = mem::replace(w, cx.waker().clone()); + w.wake(); + Poll::Pending + } State::Signaled(_) => match mem::replace(state, State::None) { State::Signaled(res) => Poll::Ready(res), _ => unreachable!(), From 96214f9db658be6d84082c8ddac21dcf4b09c3ff Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Thu, 15 Sep 2022 09:56:12 +0200 Subject: [PATCH 0116/1575] Add constants for channel types --- src/lib.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e145b821b..def738b8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,6 +132,10 @@ const IOCTL_CMD_SET_VAR: u32 = 263; const IOCTL_CMD_GET_VAR: u32 = 262; const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; +const CHANNEL_TYPE_CONTROL: u8 = 0; +const CHANNEL_TYPE_EVENT: u8 = 1; +const CHANNEL_TYPE_DATA: u8 = 2; + #[derive(Clone, Copy, PartialEq, Eq)] enum Core { WLAN = 0, @@ -755,7 +759,7 @@ where len: total_len as u16, // TODO does this len need to be rounded up to u32? len_inv: !total_len as u16, sequence: seq, - channel_and_flags: 2, // data channel + channel_and_flags: CHANNEL_TYPE_DATA, next_length: 0, header_length: SdpcmHeader::SIZE as _, wireless_flow_control: 0, @@ -819,7 +823,7 @@ where let payload = &packet[sdpcm_header.header_length as _..]; match channel { - 0 => { + CHANNEL_TYPE_CONTROL => { if payload.len() < CdcHeader::SIZE { warn!("payload too short, len={}", payload.len()); return; @@ -840,7 +844,7 @@ where } } } - 1 => { + CHANNEL_TYPE_EVENT => { let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); trace!(" {:?}", bcd_header); @@ -897,7 +901,7 @@ where evt_data ); } - 2 => { + CHANNEL_TYPE_DATA => { let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); trace!(" {:?}", bcd_header); @@ -948,7 +952,7 @@ where len: total_len as u16, // TODO does this len need to be rounded up to u32? len_inv: !total_len as u16, sequence: sdpcm_seq, - channel_and_flags: 0, // control channel + channel_and_flags: CHANNEL_TYPE_CONTROL, next_length: 0, header_length: SdpcmHeader::SIZE as _, wireless_flow_control: 0, From c4d5c047d7311e506ee1c0c9322cbf4e72dd6bcc Mon Sep 17 00:00:00 2001 From: Vincent Stakenburg Date: Thu, 15 Sep 2022 12:34:17 +0200 Subject: [PATCH 0117/1575] make `State::new()` const, consistent with others --- embassy-stm32/src/eth/v1/mod.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 1ab0438ad..38629a932 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -29,7 +29,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index d67c3c5e4..a81ee1183 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -19,7 +19,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index a7fa43894..5f6dabb3b 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -11,7 +11,7 @@ use super::*; pub struct State<'d, T: BasicInstance>(StateStorage>); impl<'d, T: BasicInstance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } From 70a3b85acc3b87abab5a66b1a02da033789b5e1a Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:32:43 +0200 Subject: [PATCH 0118/1575] Add .into_inner() and .get_mut() to Mutex --- embassy-sync/src/mutex.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 75a6e8dd3..a792cf070 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -111,6 +111,20 @@ where Ok(MutexGuard { mutex: self }) } + + /// Consumes this mutex, returning the underlying data. + pub fn into_inner(self) -> T + where T: Sized { + self.inner.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the Mutex mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() + } } /// Async mutex guard. From 79654510b71290632ee659dd2ae1851f33f48374 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:44:33 +0200 Subject: [PATCH 0119/1575] Make rustfmt happy --- embassy-sync/src/mutex.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index a792cf070..92101c6b5 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -114,7 +114,9 @@ where /// Consumes this mutex, returning the underlying data. pub fn into_inner(self) -> T - where T: Sized { + where + T: Sized, + { self.inner.into_inner() } From f7267d493fe9ed63b02f82267d29a10a4b1cf515 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:45:03 +0200 Subject: [PATCH 0120/1575] Feature-gate time-driver in embassy-rp --- ci.sh | 2 +- embassy-rp/Cargo.toml | 2 ++ embassy-rp/src/lib.rs | 2 ++ examples/rp/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index fa24d5268..ae1b44281 100755 --- a/ci.sh +++ b/ci.sh @@ -120,7 +120,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/time-driver --out-dir out/tests/rpi-pico \ $BUILD_EXTRA diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..1e26f7dd2 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -20,6 +20,8 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] +time-driver = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..8dcefece2 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; +#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -108,6 +109,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); + #[cfg(feature = "time-driver")] timer::init(); dma::init(); } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 18a92b094..17393322c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" 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"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } From feead3ae89d57e9f0ff7d7a264136d3e89aaebcf Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 06:45:27 +0200 Subject: [PATCH 0121/1575] Implement RealTimeClock for embassy-rp --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/lib.rs | 3 + embassy-rp/src/rtc/datetime_chrono.rs | 62 ++++++++ embassy-rp/src/rtc/datetime_no_deps.rs | 127 +++++++++++++++++ embassy-rp/src/rtc/filter.rs | 100 +++++++++++++ embassy-rp/src/rtc/mod.rs | 188 +++++++++++++++++++++++++ 7 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/rtc/datetime_chrono.rs create mode 100644 embassy-rp/src/rtc/datetime_no_deps.rs create mode 100644 embassy-rp/src/rtc/filter.rs create mode 100644 embassy-rp/src/rtc/mod.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..bc6f44662 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -44,6 +44,7 @@ cortex-m-rt = ">=0.6.15,<0.8" 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 } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 3ad1e5d82..1c446f389 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -122,7 +122,7 @@ pub(crate) fn clk_peri_freq() -> u32 { 125_000_000 } -pub(crate) fn _clk_rtc_freq() -> u32 { +pub(crate) fn clk_rtc_freq() -> u32 { 46875 } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..730354557 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rtc; pub mod spi; pub mod timer; pub mod uart; @@ -84,6 +85,8 @@ embassy_hal_common::peripherals! { DMA_CH11, USB, + + RTC, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/rtc/datetime_chrono.rs b/embassy-rp/src/rtc/datetime_chrono.rs new file mode 100644 index 000000000..b3c78dd47 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_chrono.rs @@ -0,0 +1,62 @@ +use chrono::{Datelike, Timelike}; + +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Alias for [`chrono::NaiveDateTime`] +pub type DateTime = chrono::NaiveDateTime; +/// Alias for [`chrono::Weekday`] +pub type DayOfWeek = chrono::Weekday; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] has an invalid year. The year must be between 0 and 4095. + InvalidYear, + /// The [DateTime] contains an invalid date. + InvalidDate, + /// The [DateTime] contains an invalid time. + InvalidTime, +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw.num_days_from_sunday() as u8 +} + +pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year() < 0 || dt.year() > 4095 { + // rp2040 can't hold these years + Err(Error::InvalidYear) + } else { + // The rest of the chrono date is assumed to be valid + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year() as u16); + w.set_month(dt.month() as u8); + w.set_day(dt.day() as u8); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.weekday().num_days_from_sunday() as u8); + w.set_hour(dt.hour() as u8); + w.set_min(dt.minute() as u8); + w.set_sec(dt.second() as u8); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year() as i32; + let month = rtc_1.month() as u32; + let day = rtc_1.day() as u32; + + let hour = rtc_0.hour() as u32; + let minute = rtc_0.min() as u32; + let second = rtc_0.sec() as u32; + + let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?; + let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?; + Ok(DateTime::new(date, time)) +} diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs new file mode 100644 index 000000000..92770e984 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_no_deps.rs @@ -0,0 +1,127 @@ +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. + InvalidYear, + /// The [DateTime] contains an invalid month value. Must be between `1..=12`. + InvalidMonth, + /// The [DateTime] contains an invalid day value. Must be between `1..=31`. + InvalidDay, + /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday. + InvalidDayOfWeek( + /// The value of the DayOfWeek that was given. + u8, + ), + /// The [DateTime] contains an invalid hour value. Must be between `0..=23`. + InvalidHour, + /// The [DateTime] contains an invalid minute value. Must be between `0..=59`. + InvalidMinute, + /// The [DateTime] contains an invalid second value. Must be between `0..=59`. + InvalidSecond, +} + +/// Structure containing date and time information +pub struct DateTime { + /// 0..4095 + pub year: u16, + /// 1..12, 1 is January + pub month: u8, + /// 1..28,29,30,31 depending on month + pub day: u8, + /// + pub day_of_week: DayOfWeek, + /// 0..23 + pub hour: u8, + /// 0..59 + pub minute: u8, + /// 0..59 + pub second: u8, +} + +/// A day of the week +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[allow(missing_docs)] +pub enum DayOfWeek { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, +} + +fn day_of_week_from_u8(v: u8) -> Result { + Ok(match v { + 0 => DayOfWeek::Sunday, + 1 => DayOfWeek::Monday, + 2 => DayOfWeek::Tuesday, + 3 => DayOfWeek::Wednesday, + 4 => DayOfWeek::Thursday, + 5 => DayOfWeek::Friday, + 6 => DayOfWeek::Saturday, + x => return Err(Error::InvalidDayOfWeek(x)), + }) +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw as u8 +} + +pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year > 4095 { + Err(Error::InvalidYear) + } else if dt.month < 1 || dt.month > 12 { + Err(Error::InvalidMonth) + } else if dt.day < 1 || dt.day > 31 { + Err(Error::InvalidDay) + } else if dt.hour > 23 { + Err(Error::InvalidHour) + } else if dt.minute > 59 { + Err(Error::InvalidMinute) + } else if dt.second > 59 { + Err(Error::InvalidSecond) + } else { + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year); + w.set_month(dt.month); + w.set_day(dt.day); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.day_of_week as u8); + w.set_hour(dt.hour); + w.set_min(dt.minute); + w.set_sec(dt.second); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year(); + let month = rtc_1.month(); + let day = rtc_1.day(); + + let day_of_week = rtc_0.dotw(); + let hour = rtc_0.hour(); + let minute = rtc_0.min(); + let second = rtc_0.sec(); + + let day_of_week = day_of_week_from_u8(day_of_week)?; + Ok(DateTime { + year, + month, + day, + day_of_week, + hour, + minute, + second, + }) +} diff --git a/embassy-rp/src/rtc/filter.rs b/embassy-rp/src/rtc/filter.rs new file mode 100644 index 000000000..d4a3bab2f --- /dev/null +++ b/embassy-rp/src/rtc/filter.rs @@ -0,0 +1,100 @@ +use super::DayOfWeek; +use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1}; + +/// A filter used for [`RealTimeClock::schedule_alarm`]. +/// +/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm +#[derive(Default)] +pub struct DateTimeFilter { + /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. + pub year: Option, + /// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value. + pub month: Option, + /// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value. + pub day: Option, + /// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value. + pub day_of_week: Option, + /// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value. + pub hour: Option, + /// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value. + pub minute: Option, + /// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value. + pub second: Option, +} + +impl DateTimeFilter { + /// Set a filter on the given year + pub fn year(mut self, year: u16) -> Self { + self.year = Some(year); + self + } + /// Set a filter on the given month + pub fn month(mut self, month: u8) -> Self { + self.month = Some(month); + self + } + /// Set a filter on the given day + pub fn day(mut self, day: u8) -> Self { + self.day = Some(day); + self + } + /// Set a filter on the given day of the week + pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self { + self.day_of_week = Some(day_of_week); + self + } + /// Set a filter on the given hour + pub fn hour(mut self, hour: u8) -> Self { + self.hour = Some(hour); + self + } + /// Set a filter on the given minute + pub fn minute(mut self, minute: u8) -> Self { + self.minute = Some(minute); + self + } + /// Set a filter on the given second + pub fn second(mut self, second: u8) -> Self { + self.second = Some(second); + self + } +} + +// register helper functions +impl DateTimeFilter { + pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) { + if let Some(year) = self.year { + w.set_year_ena(true); + + w.set_year(year); + } + if let Some(month) = self.month { + w.set_month_ena(true); + w.set_month(month); + } + if let Some(day) = self.day { + w.set_day_ena(true); + w.set_day(day); + } + } + pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) { + if let Some(day_of_week) = self.day_of_week { + w.set_dotw_ena(true); + let bits = super::datetime::day_of_week_to_u8(day_of_week); + + w.set_dotw(bits); + } + if let Some(hour) = self.hour { + w.set_hour_ena(true); + w.set_hour(hour); + } + if let Some(minute) = self.minute { + w.set_min_ena(true); + w.set_min(minute); + } + if let Some(second) = self.second { + w.set_sec_ena(true); + w.set_sec(second); + } + } +} diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs new file mode 100644 index 000000000..7f3bbbe73 --- /dev/null +++ b/embassy-rp/src/rtc/mod.rs @@ -0,0 +1,188 @@ +mod filter; + +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; + +pub use self::filter::DateTimeFilter; + +#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")] +#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")] +mod datetime; + +pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; +use crate::clocks::clk_rtc_freq; + +/// A reference to the real time clock of the system +pub struct RealTimeClock<'d, T: Instance> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> RealTimeClock<'d, T> { + /// Create a new instance of the real time clock, with the given date as an initial value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn new(inner: impl Peripheral

+ 'd, initial_date: DateTime) -> Result { + into_ref!(inner); + + // Set the RTC divider + unsafe { + inner + .regs() + .clkdiv_m1() + .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1)) + }; + + let mut result = Self { inner }; + result.set_leap_year_check(true); // should be on by default, make sure this is the case. + result.set_datetime(initial_date)?; + Ok(result) + } + + /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. + /// + /// Leap year checking is enabled by default. + pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) { + unsafe { + self.inner + .regs() + .ctrl() + .modify(|w| w.set_force_notleapyear(!leap_year_check_enabled)) + }; + } + + /// Checks to see if this RealTimeClock is running + pub fn is_running(&self) -> bool { + unsafe { self.inner.regs().ctrl().read().rtc_active() } + } + + /// Set the datetime to a new value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> { + self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; + + // disable RTC while we configure it + unsafe { + self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); + while self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + + self.inner.regs().setup_0().write(|w| { + self::datetime::write_setup_0(&t, w); + }); + self.inner.regs().setup_1().write(|w| { + self::datetime::write_setup_1(&t, w); + }); + + // Load the new datetime and re-enable RTC + self.inner.regs().ctrl().write(|w| w.set_load(true)); + self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); + while !self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + } + Ok(()) + } + + /// Return the current datetime. + /// + /// # Errors + /// + /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. + pub fn now(&self) -> Result { + if !self.is_running() { + return Err(RtcError::NotRunning); + } + + let rtc_0 = unsafe { self.inner.regs().rtc_0().read() }; + let rtc_1 = unsafe { self.inner.regs().rtc_1().read() }; + + self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime) + } + + /// Disable the alarm that was scheduled with [`schedule_alarm`]. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn disable_alarm(&mut self) { + unsafe { + self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false)); + + while self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Schedule an alarm. The `filter` determines at which point in time this alarm is set. + /// + /// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call: + /// ```no_run + /// # #[cfg(feature = "chrono")] + /// # fn main() { } + /// # #[cfg(not(feature = "chrono"))] + /// # fn main() { + /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; + /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; + /// let now = real_time_clock.now().unwrap(); + /// real_time_clock.schedule_alarm( + /// DateTimeFilter::default() + /// .minute(if now.minute == 59 { 0 } else { now.minute + 1 }) + /// ); + /// # } + /// ``` + pub fn schedule_alarm(&mut self, filter: DateTimeFilter) { + self.disable_alarm(); + + unsafe { + self.inner.regs().irq_setup_0().write(|w| { + filter.write_setup_0(w); + }); + self.inner.regs().irq_setup_1().write(|w| { + filter.write_setup_1(w); + }); + + // Set the enable bit and check if it is set + self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); + while !self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered, + /// or the next [`schedule_alarm`] will never fire. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn clear_interrupt(&mut self) { + self.disable_alarm(); + } +} + +/// Errors that can occur on methods on [RtcClock] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RtcError { + /// An invalid DateTime was given or stored on the hardware. + InvalidDateTime(DateTimeError), + + /// The RTC clock is not running + NotRunning, +} + +mod sealed { + pub trait Instance { + fn regs(&self) -> crate::pac::rtc::Rtc; + } +} + +pub trait Instance: sealed::Instance {} + +impl sealed::Instance for crate::peripherals::RTC { + fn regs(&self) -> crate::pac::rtc::Rtc { + crate::pac::RTC + } +} +impl Instance for crate::peripherals::RTC {} From feb840c503a7110db5d12c0f49eea10ac92aa1c1 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 12:40:39 +0200 Subject: [PATCH 0122/1575] First iteration attempt on implementing generic flash mutation access for RP2040 --- embassy-rp/Cargo.toml | 2 + embassy-rp/src/flash.rs | 100 ++++++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 1 + 3 files changed, 103 insertions(+) create mode 100644 embassy-rp/src/flash.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..51cdda7b1 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -44,6 +44,8 @@ cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +rp2040-flash = { version = "0.1" } +embedded-storage = { version = "0.3" } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs new file mode 100644 index 000000000..3b6405090 --- /dev/null +++ b/embassy-rp/src/flash.rs @@ -0,0 +1,100 @@ +use embedded_storage::nor_flash::{ + ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, +}; + +/// Error type for NVMC operations. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Opration using a location not in flash. + OutOfBounds, + /// Unaligned operation or using unaligned buffers. + Unaligned, +} + +impl NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match self { + Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, + Self::Unaligned => NorFlashErrorKind::NotAligned, + } + } +} + +pub struct Flash; + +impl ErrorType for Flash { + type Error = Error; +} + +impl MultiwriteNorFlash for Flash {} + +impl ReadNorFlash for Flash { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + + let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl NorFlash for Flash { + const WRITE_SIZE: usize = 4; + + const ERASE_SIZE: usize = 4096; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if to < from || to as usize > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let len = to - from; + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_erase(from, len, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { + return Err(Error::Unaligned); + } + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_program(offset, bytes, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..d86162fb2 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -14,6 +14,7 @@ pub mod uart; pub mod usb; mod clocks; +pub mod flash; mod reset; // Reexports From c495c765df42ca273da55b320c869b0aaabc6ef8 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:28:35 +0200 Subject: [PATCH 0123/1575] Enable embedded-io on nightly --- embassy-rp/Cargo.toml | 44 ++++++++++++++----------------------------- embassy-rp/src/lib.rs | 2 -- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 92780ee39..3debca710 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -7,7 +7,9 @@ edition = "2021" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] -flavors = [{ name = "rp2040", target = "thumbv6m-none-eabi" }] +flavors = [ + { name = "rp2040", target = "thumbv6m-none-eabi" }, +] [features] defmt = ["dep:defmt", "embassy-usb?/defmt"] @@ -18,16 +20,8 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] -time-driver = [] - # Enable nightly-only features -nightly = [ - "embassy-executor/nightly", - "embedded-hal-1", - "embedded-hal-async", - "embassy-embedded-hal/nightly", - "dep:embassy-usb", -] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -36,15 +30,11 @@ unstable-traits = ["embedded-hal-1"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } -embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ - "tick-hz-1_000_000", -] } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = [ - "prio-bits-2", -] } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } -embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} +embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -53,18 +43,12 @@ cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.1" -futures = { version = "0.3.17", default-features = false, features = [ - "async-await", -] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.3.0", features = ["async"], optional = true } -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev = "017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = [ - "rt", -] } +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.8", optional = true } -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true } +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} +embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 8dcefece2..aebbbf567 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,7 +8,6 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; -#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -109,7 +108,6 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); - #[cfg(feature = "time-driver")] timer::init(); dma::init(); } From 1c657d2d5532979f326d89a0d40e8aa4bc96d0c2 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 16:45:59 +0200 Subject: [PATCH 0124/1575] Add time-driver feature to docs --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 1e26f7dd2..7315d673c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] flavors = [ { name = "rp2040", target = "thumbv6m-none-eabi" }, ] From 520860622b5d42471c58a041ae37337cb92e3fc9 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 17 Sep 2022 09:06:23 +0200 Subject: [PATCH 0125/1575] Make self parameter to has_credit non-mutable --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4baaaa51c..d09be5062 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -938,7 +938,7 @@ where } } - fn has_credit(&mut self) -> bool { + fn has_credit(&self) -> bool { self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 } From ab1a6889a62e86a80af6fc572ffa992cfb9ef960 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:02:05 -0700 Subject: [PATCH 0126/1575] rp: fix async SPI read and write --- embassy-rp/src/dma.rs | 38 ++++++++++++++++++++++++++++ embassy-rp/src/spi.rs | 59 ++++++++++++++++++++++++++++++------------- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index acf338225..b256cc2f0 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,6 +56,25 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } +pub unsafe fn read_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: *const W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let mut dummy: u32 = 0; + copy_inner( + ch, + from as *const u32, + &mut dummy as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], @@ -75,6 +94,25 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } +pub unsafe fn write_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + to: *mut W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let dummy: u32 = 0; + copy_inner( + ch, + &dummy as *const u32, + to as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn copy<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: &[W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 74f0b04de..e7cd99929 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,53 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let ch = self.tx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); reg.set_txdmae(true); - }); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read_repeated( + rx_ch, + self.inner.regs().dr().ptr() as *const u8, + buffer.len(), + T::RX_DREQ, + ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let ch = self.rx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { reg.set_rxdmae(true); - }); + reg.set_txdmae(true); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } @@ -364,20 +387,20 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); assert_eq!(from_len, to_len); + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + }) + }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_txdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_rxdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) From 295cc997ae8b1468617dd9f430be4e502901f4f2 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:23:17 -0700 Subject: [PATCH 0127/1575] rp: let SPI RX overflow during async write --- embassy-rp/src/dma.rs | 19 ------------------- embassy-rp/src/spi.rs | 35 +++++++++++++++++------------------ 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index b256cc2f0..7ad1a6bfe 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,25 +56,6 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } -pub unsafe fn read_repeated<'a, C: Channel, W: Word>( - ch: impl Peripheral

+ 'a, - from: *const W, - len: usize, - dreq: u8, -) -> Transfer<'a, C> { - let mut dummy: u32 = 0; - copy_inner( - ch, - from as *const u32, - &mut dummy as *mut u32, - len, - W::size(), - false, - false, - dreq, - ) -} - pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index e7cd99929..3cf823573 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,29 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - unsafe { - self.inner.regs().dmacr().write(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - }) - }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_txdmae(true); + }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - let rx_ch = self.rx_dma.as_mut().unwrap(); - let rx_transfer = unsafe { - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read_repeated( - rx_ch, - self.inner.regs().dr().ptr() as *const u8, - buffer.len(), - T::RX_DREQ, - ) - }; - join(tx_transfer, rx_transfer).await; + tx_transfer.await; + + let p = self.inner.regs(); + unsafe { + while p.sr().read().bsy() {} + + // clear RX FIFO contents to prevent stale reads + while p.sr().read().rne() { + let _: u16 = p.dr().read().data(); + } + // clear RX overrun interrupt + p.icr().write(|w| w.set_roric(true)); + } + Ok(()) } From 0c6933fefb749aa8857ade8cf3d52cb38a398d55 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 14:54:24 -0700 Subject: [PATCH 0128/1575] rp: remove extraneous newlines in logs --- embassy-rp/src/gpio.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a0328302a..9b9a08110 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -159,7 +159,7 @@ unsafe fn IO_IRQ_BANK0() { w.set_edge_low(pin_group, false); } InterruptTrigger::LevelHigh => { - debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin); + debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); w.set_level_high(pin_group, false); } InterruptTrigger::LevelLow => { @@ -198,7 +198,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { - debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin()); + debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); } InterruptTrigger::LevelLow => { @@ -245,45 +245,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // the pin and if it has been disabled that means it was done by the // interrupt service routine, so we then know that the event/trigger // happened and Poll::Ready will be returned. - debug!("{:?} for pin {}\n", self.level, self.pin.pin()); + debug!("{:?} for pin {}", self.level, self.pin.pin()); match self.level { InterruptTrigger::AnyEdge => { if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelHigh => { if !inte.level_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelLow => { if !inte.level_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeHigh => { if !inte.edge_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeLow => { if !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } } - debug!("InputFuture::poll return Poll::Pending\n"); + debug!("InputFuture::poll return Poll::Pending"); Poll::Pending } } From d0fe654c82b548d65f49213ad50fc2edc5b3d71e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 09:42:40 +0200 Subject: [PATCH 0129/1575] Remove BootFlash borrow Compiler will infer a different lifetime for BootFlash than for the borrowed flash, which makes it require more type annotations than if it was just owning the type. Since it doesn't really matter if it owns or borrows in practical use, change it to own so that it simplifies usage. --- embassy-boot/boot/src/lib.rs | 24 +++++++++++----------- examples/boot/bootloader/nrf/src/main.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 7 +++---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4a2b112a9..015dd58db 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -447,24 +447,24 @@ where } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> +pub struct BootFlash where F: NorFlash + ReadNorFlash, { - flash: &'a mut F, + flash: F, } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl BootFlash where F: NorFlash + ReadNorFlash, { /// Create a new instance of a bootable flash - pub fn new(flash: &'a mut F) -> Self { + pub fn new(flash: F) -> Self { Self { flash } } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl Flash for BootFlash where F: NorFlash + ReadNorFlash, { @@ -472,14 +472,14 @@ where const ERASE_VALUE: u8 = ERASE_VALUE; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ErrorType for BootFlash where F: ReadNorFlash + NorFlash, { type Error = F::Error; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl NorFlash for BootFlash where F: ReadNorFlash + NorFlash, { @@ -487,26 +487,26 @@ where const ERASE_SIZE: usize = F::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(self.flash, from, to) + F::erase(&mut self.flash, from, to) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(self.flash, offset, bytes) + F::write(&mut self.flash, offset, bytes) } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ReadNorFlash for BootFlash where F: ReadNorFlash + NorFlash, { const READ_SIZE: usize = F::READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(self.flash, offset, bytes) + F::read(&mut self.flash, offset, bytes) } fn capacity(&self) -> usize { - F::capacity(self.flash) + F::capacity(&self.flash) } } diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 9031997c2..8266206b3 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -21,7 +21,7 @@ fn main() -> ! { let mut bl = BootLoader::default(); let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - &mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), + WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), ))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index bb5d3e531..294464d1c 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,10 +20,9 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let mut flash = Flash::unlock(p.FLASH); - let start = bl.prepare(&mut SingleFlashConfig::new( - &mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash), - )); + let flash = Flash::unlock(p.FLASH); + let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } } From b418c0e4d620db0332d02c16fbbd455e7b8805a9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 14:03:04 +0200 Subject: [PATCH 0130/1575] Take into account size of revert index Fixes a bug in the partition assertions that ensures that the state page(s) have enough space for 2x active partition range. Add unit test to verify that panic is observed. --- embassy-boot/boot/src/lib.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 015dd58db..3d359533e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -222,10 +222,7 @@ impl BootLoader { page: &mut [u8], ) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(self.active.len() % page.len(), 0); - assert_eq!(self.dfu.len() % page.len(), 0); - assert!(self.dfu.len() - self.active.len() >= page.len()); - assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE); + assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); assert_eq!(magic.len(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active @@ -409,6 +406,13 @@ impl BootLoader { } } +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { + assert_eq!(active.len() % page_size, 0); + assert_eq!(dfu.len() % page_size, 0); + assert!(dfu.len() - active.len() >= page_size); + assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); +} + /// Convenience provider that uses a single flash for all partitions. pub struct SingleFlashConfig<'a, F> where @@ -919,6 +923,15 @@ mod tests { } } + #[test] + #[should_panic] + fn test_range_asserts() { + const ACTIVE: Partition = Partition::new(4096, 4194304); + const DFU: Partition = Partition::new(4194304, 2 * 4194304); + const STATE: Partition = Partition::new(0, 4096); + assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + } + struct MemFlash([u8; SIZE]); impl NorFlash From 1d3e41f970c1b04fc98533a0a6d09cb5be85ff09 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 21 Sep 2022 06:00:35 +0200 Subject: [PATCH 0131/1575] Remove code-duplication in async bufferedUart implementations --- embassy-rp/src/uart/buffered.rs | 215 +++++++++++++------------------- 1 file changed, 89 insertions(+), 126 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 3eb96e3d5..6d395b6f4 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,5 +1,5 @@ use core::future::Future; -use core::task::Poll; +use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; @@ -87,9 +87,9 @@ impl<'d, T: Instance> BufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); + w.set_txim(true); }); } @@ -122,7 +122,6 @@ impl<'d, T: Instance> RxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); }); @@ -151,9 +150,7 @@ impl<'d, T: Instance> TxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? - w.set_rxim(true); - w.set_rtim(true); + w.set_txim(true); }); } @@ -179,6 +176,51 @@ where } } +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) + } + + 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, @@ -240,6 +282,35 @@ where } } +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, @@ -299,26 +370,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.buf.is_full() { - do_pend = true; - } - state.rx.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx.waker.register(cx.waker()); - Poll::Pending + state.rx.read(buf, cx.waker()) }); if do_pend { @@ -337,26 +391,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.buf.is_full() { - do_pend = true; - } - state.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.waker.register(cx.waker()); - Poll::Pending + state.read(buf, cx.waker()) }); if do_pend { @@ -377,28 +414,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.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)); - } - - state.rx.waker.register(cx.waker()); - Poll::>::Pending + state.rx.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.rx.buf.is_full(); - state.rx.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.rx.consume(amt)); if signal { self.inner.pend(); } @@ -414,28 +436,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.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)); - } - - state.waker.register(cx.waker()); - Poll::>::Pending + state.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.buf.is_full(); - state.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.consume(amt)); if signal { self.inner.pend(); } @@ -449,20 +456,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.buf.is_empty(); - let tx_buf = state.tx.buf.push_buf(); - if tx_buf.is_empty() { - state.tx.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -475,16 +469,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.tx.buf.is_empty() { - state.tx.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))) } } @@ -495,20 +480,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.buf.is_empty(); - let tx_buf = state.buf.push_buf(); - if tx_buf.is_empty() { - state.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -521,15 +493,6 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.buf.is_empty() { - state.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))) } } From 3d708a459c9c5f18ddd5c63a06f272371b6225c8 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 10:47:49 +0200 Subject: [PATCH 0132/1575] Implement proper `Drop` for `BufferedUarte` --- embassy-nrf/src/buffered_uarte.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c3cba2470..84ef86c96 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -27,7 +27,7 @@ use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; -use crate::gpio::Pin as GpioPin; +use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::InterruptExt; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; @@ -427,23 +427,26 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { + debug!("oh no, dropping uarte"); let r = U::regs(); // TODO this probably deadlocks. do like Uarte instead. + r.inten.reset(); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); - self.timer.stop(); - if let RxState::Receiving = self.rx_state { - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - } - if let TxState::Transmitting(_) = self.tx_state { - r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - } - if let RxState::Receiving = self.rx_state { - low_power_wait_until(|| r.events_endrx.read().bits() == 1); - } - if let TxState::Transmitting(_) = self.tx_state { - low_power_wait_until(|| r.events_endtx.read().bits() == 1); - } + r.events_txstopped.reset(); + r.tasks_stoptx.write(|w| w.tasks_stoptx().set_bit()); + while !r.events_txstopped.read().events_txstopped().bit_is_set() {} + + while !r.events_rxto.read().events_rxto().bit_is_set() {} + + r.enable.write(|w| w.enable().disabled()); + + gpio::deconfigure_pin(r.psel.rxd.read().bits()); + gpio::deconfigure_pin(r.psel.txd.read().bits()); + gpio::deconfigure_pin(r.psel.rts.read().bits()); + gpio::deconfigure_pin(r.psel.cts.read().bits()); } } From 0f55f5a73d356dc991dbc3c4bc102e7d652c5fc5 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 11:06:06 +0200 Subject: [PATCH 0133/1575] Remove left-in comments and logs --- embassy-nrf/src/buffered_uarte.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 84ef86c96..3ee3e9477 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -427,10 +427,8 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { - debug!("oh no, dropping uarte"); let r = U::regs(); - // TODO this probably deadlocks. do like Uarte instead. r.inten.reset(); r.events_rxto.reset(); r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); From 15b4f9db9000cfb402357c5ac2c84876a3ad27c3 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 11:19:47 +0200 Subject: [PATCH 0134/1575] Remove unused function --- embassy-nrf/src/buffered_uarte.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 3ee3e9477..e212e9897 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -550,13 +550,3 @@ impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, trace!("irq: end"); } } - -/// Low power blocking wait loop using WFE/SEV. -fn low_power_wait_until(mut condition: impl FnMut() -> bool) { - while !condition() { - // WFE might "eat" an event that would have otherwise woken the executor. - cortex_m::asm::wfe(); - } - // Retrigger an event to be transparent to the executor. - cortex_m::asm::sev(); -} From 5f7e0eb2aea6f7f6e23d9a5b7400b29377e57d8e Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 14:06:56 +0200 Subject: [PATCH 0135/1575] Fix builds on other nrf pacs --- embassy-nrf/src/buffered_uarte.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index e212e9897..eb0b1b0cd 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -431,13 +431,13 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { r.inten.reset(); r.events_rxto.reset(); - r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); r.events_txstopped.reset(); - r.tasks_stoptx.write(|w| w.tasks_stoptx().set_bit()); - while !r.events_txstopped.read().events_txstopped().bit_is_set() {} + r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + while r.events_txstopped.read().bits() == 0 {} - while !r.events_rxto.read().events_rxto().bit_is_set() {} + while r.events_rxto.read().bits() == 0 {} r.enable.write(|w| w.enable().disabled()); From 5914d80968a6aca99f0018148e4b4ed7c4e06bf0 Mon Sep 17 00:00:00 2001 From: Andrew Ealovega Date: Wed, 21 Sep 2022 22:29:57 -0400 Subject: [PATCH 0136/1575] Add non blocking Bxcan constructor. Signed-off-by: Andrew Ealovega --- embassy-stm32/src/can/bxcan.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index c0bd44e0f..bd92b35a0 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -12,6 +12,7 @@ pub struct Can<'d, T: Instance> { } impl<'d, T: Instance> Can<'d, T> { + /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. pub fn new( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -31,6 +32,28 @@ impl<'d, T: Instance> Can<'d, T> { can: bxcan::Can::builder(BxcanInstance(peri)).enable(), } } + + /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. + /// You must call [Can::enable_non_blocking] to use the peripheral. + pub fn new_disabled( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, rx, tx); + + unsafe { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } + + T::enable(); + T::reset(); + + Self { + can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(), + } + } } impl<'d, T: Instance> Drop for Can<'d, T> { From 483edf694b399779636b6bfd8cf243eb430d1323 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sun, 11 Sep 2022 09:34:39 +0200 Subject: [PATCH 0137/1575] Introduce IoctlType enum for IOCTL types This commit introduces an enum to represent the IOCTL command types available, the direction of the data transfer (Get and Set). --- src/lib.rs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d09be5062..a6b26188d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,6 +139,12 @@ const CHANNEL_TYPE_CONTROL: u8 = 0; const CHANNEL_TYPE_EVENT: u8 = 1; const CHANNEL_TYPE_DATA: u8 = 2; +#[derive(Clone, Copy)] +pub enum IoctlType { + Get = 0, + Set = 2, +} + #[derive(Clone, Copy, PartialEq, Eq)] enum Core { WLAN = 0, @@ -212,7 +218,7 @@ enum IoctlState { Idle, Pending { - kind: u32, + kind: IoctlType, cmd: u32, iface: u32, buf: *mut [u8], @@ -276,7 +282,7 @@ impl<'a> Control<'a> { buf[0..8].copy_from_slice(b"clmload\x00"); buf[8..20].copy_from_slice(&header.to_bytes()); buf[20..][..chunk.len()].copy_from_slice(&chunk); - self.ioctl(2, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) .await; } @@ -337,7 +343,7 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; // set wifi up - self.ioctl(2, IOCTL_CMD_UP, 0, &mut []).await; + self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; Timer::after(Duration::from_millis(100)).await; @@ -374,7 +380,8 @@ impl<'a> Control<'a> { ssid: [0; 32], }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.ioctl(2, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()).await; // set_ssid + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) + .await; // set_ssid info!("JOINED"); } @@ -395,7 +402,8 @@ impl<'a> Control<'a> { passphrase: [0; 64], }; pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); - self.ioctl(2, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()).await; // WLC_SET_WSEC_PMK + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) + .await; // WLC_SET_WSEC_PMK self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) @@ -406,7 +414,7 @@ impl<'a> Control<'a> { ssid: [0; 32], }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.ioctl(2, 26, 0, &mut i.to_bytes()).await; // set_ssid + self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; // set_ssid info!("JOINED"); } @@ -444,7 +452,8 @@ impl<'a> Control<'a> { buf[name.len() + 1..][..val.len()].copy_from_slice(val); let total_len = name.len() + 1 + val.len(); - self.ioctl(2, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]).await; + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) + .await; } // TODO this is not really working, it always returns all zeros. @@ -456,7 +465,9 @@ impl<'a> Control<'a> { buf[name.len()] = 0; let total_len = max(name.len() + 1, res.len()); - let res_len = self.ioctl(0, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]).await; + let res_len = self + .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) + .await; let out_len = min(res.len(), res_len); res[..out_len].copy_from_slice(&buf[..out_len]); @@ -465,10 +476,10 @@ impl<'a> Control<'a> { async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { let mut buf = val.to_le_bytes(); - self.ioctl(2, cmd, 0, &mut buf).await; + self.ioctl(IoctlType::Set, cmd, 0, &mut buf).await; } - async fn ioctl(&mut self, kind: u32, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { + async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { // TODO cancel ioctl on future drop. while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) { @@ -942,7 +953,7 @@ where self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 } - async fn send_ioctl(&mut self, kind: u32, cmd: u32, iface: u32, data: &[u8]) { + async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); From 897b72c872183221e088611aa6f30989800afd2b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:28:56 +0200 Subject: [PATCH 0138/1575] Update Rust nightly. Removes feature(generic_associated_types) --- embassy-boot/boot/src/lib.rs | 1 - embassy-boot/nrf/src/lib.rs | 1 - embassy-boot/stm32/src/lib.rs | 1 - embassy-embedded-hal/src/lib.rs | 2 +- embassy-lora/src/lib.rs | 1 - embassy-net/src/lib.rs | 2 +- embassy-nrf/src/lib.rs | 2 +- embassy-rp/src/lib.rs | 2 +- embassy-stm32/src/lib.rs | 2 +- embassy-sync/src/lib.rs | 2 +- embassy-time/src/lib.rs | 2 +- embassy-usb-hid/src/lib.rs | 1 - embassy-usb-serial/src/lib.rs | 1 - embassy-usb/src/lib.rs | 1 - examples/boot/application/nrf/src/bin/a.rs | 1 - examples/boot/application/nrf/src/bin/b.rs | 1 - examples/nrf/src/bin/usb_ethernet.rs | 1 - examples/nrf/src/bin/usb_hid_keyboard.rs | 1 - examples/nrf/src/bin/usb_hid_mouse.rs | 1 - examples/nrf/src/bin/usb_serial.rs | 1 - examples/nrf/src/bin/usb_serial_multitask.rs | 1 - examples/rp/src/bin/usb_ethernet.rs | 1 - examples/rp/src/bin/usb_serial.rs | 1 - examples/stm32l0/src/bin/lorawan.rs | 1 - examples/stm32l5/src/bin/usb_ethernet.rs | 1 - examples/stm32l5/src/bin/usb_hid_mouse.rs | 1 - examples/stm32wl/src/bin/lorawan.rs | 1 - examples/stm32wl/src/bin/subghz.rs | 1 - rust-toolchain.toml | 2 +- 29 files changed, 8 insertions(+), 29 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 3d359533e..96878ace9 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,5 +1,4 @@ #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] #![no_std] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 0c14781a2..385e089fe 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 39f080517..edba39cca 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 0c6f2786a..a12a3a3a0 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![warn(missing_docs)] //! Utilities to use `embedded-hal` traits with Embassy. diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 2483dcb2e..90ba0d1d4 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device //! crate's async LoRaWAN MAC implementation. diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 8eebc798e..4d30550d3 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index f3b3ca0ca..d7bd21702 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -43,7 +43,7 @@ //! mutable slices always reside in RAM. #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #[cfg(not(any( feature = "nrf51", diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index c3976d261..9ce09064a 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 30ff02d56..0392e8086 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 25150e8aa..80bb907a3 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 5b2620986..4edc883fe 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] #![warn(missing_docs)] diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs index 5fee60bbc..8b181aec8 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb-hid/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] //! Implements HID functionality for a usb-device device. diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index f3de2ec1b..27b536a6b 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index b165804ef..ca7dde627 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 133a3e678..7a404a914 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_boot_nrf::FirmwareUpdater; diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 5394bf0c7..1373f277d 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 352660b59..33ca380ff 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 7fdb0b685..4761fcf66 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 7cd2ece17..f1b57a17f 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index a68edb329..f9526cbc4 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index d62d7e520..c646c0bbd 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 2df7f62f6..166ffe175 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 74be1f598..00cf3e93f 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::{info, panic}; diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 00ff67f3f..27d7c29c2 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -3,7 +3,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 2c8706e41..c96a83ead 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 7d763e7fd..0aca6f1cd 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 9143e64da..7f34dd306 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index 8f674d796..3c60a8de4 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f5e342edc..1ec19e58b 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-08-16" +channel = "nightly-2022-09-22" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", From a0487380da42a71ab7532e2bc1befd1039c18a78 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:42:49 +0200 Subject: [PATCH 0139/1575] Replace futures::future::poll_fn -> core::future::poll_fn. --- embassy-executor/src/spawner.rs | 3 +-- embassy-lora/src/stm32wl/mod.rs | 3 +-- embassy-net/src/stack.rs | 3 +-- embassy-net/src/tcp.rs | 6 +++--- embassy-net/src/udp.rs | 2 +- embassy-nrf/src/buffered_uarte.rs | 3 +-- embassy-nrf/src/gpiote.rs | 3 +-- embassy-nrf/src/qdec.rs | 2 +- embassy-nrf/src/qspi.rs | 2 +- embassy-nrf/src/rng.rs | 2 +- embassy-nrf/src/saadc.rs | 2 +- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/temp.rs | 2 +- embassy-nrf/src/timer.rs | 2 +- embassy-nrf/src/twim.rs | 3 +-- embassy-nrf/src/uarte.rs | 2 +- embassy-nrf/src/usb.rs | 3 +-- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/usb.rs | 3 +-- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 3 +-- embassy-stm32/src/usb/usb.rs | 3 +-- embassy-sync/src/mutex.rs | 3 +-- embassy-sync/src/signal.rs | 4 ++-- examples/nrf-rtos-trace/src/bin/rtos_trace.rs | 3 ++- examples/nrf/src/bin/executor_fairness_test.rs | 3 ++- 29 files changed, 34 insertions(+), 43 deletions(-) diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 25a0d7dbb..400d973ff 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,10 +1,9 @@ +use core::future::poll_fn; use core::marker::PhantomData; use core::mem; use core::ptr::NonNull; use core::task::Poll; -use futures_util::future::poll_fn; - use super::raw; /// Token to spawn a newly-created task in an executor. diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 4d11244b6..e28fa2c1a 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,5 +1,5 @@ //! A radio driver integration for the radio found on STM32WL family devices. -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::Poll; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -11,7 +11,6 @@ use embassy_stm32::subghz::{ Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 8d2dd4bca..3a7610758 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -1,10 +1,9 @@ use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; -use futures::future::poll_fn; use futures::pin_mut; use heapless::Vec; #[cfg(feature = "dhcpv4")] diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0fa873602..f8fff3e2d 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; use smoltcp::time::Duration; @@ -103,7 +103,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)), tcp::State::Listen => unreachable!(), @@ -128,7 +128,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => { s.register_send_waker(cx.waker()); diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 78b09a492..f2e33493c 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c3cba2470..fec875cb8 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::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -23,7 +23,6 @@ use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorag use embassy_hal_common::ring_buffer::RingBuffer; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index d99f592b0..b418be9d5 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,10 +1,9 @@ use core::convert::Infallible; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 762e09715..253c85c32 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,10 +1,10 @@ //! Quadrature decoder interface +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index c97cb1656..ea0a17031 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -1,11 +1,11 @@ #![macro_use] +use core::future::poll_fn; use core::ptr; use core::task::Poll; use embassy_hal_common::drop::DropBomb; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 42da51d0f..e0caeaaee 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,3 +1,4 @@ +use core::future::poll_fn; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; @@ -5,7 +6,6 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::interrupt::InterruptExt; use crate::peripherals::RNG; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 9bc89eb38..d1c82423e 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; // We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 2955182e4..51cd73a47 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,12 +1,12 @@ #![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 futures::future::poll_fn; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index d520fd686..7a7f61b51 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -1,12 +1,12 @@ //! Temperature sensor interface. +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; -use futures::future::poll_fn; use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3de5a8962..bc8710640 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::interrupt::{Interrupt, InterruptExt}; use crate::ppi::{Event, Task}; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 3d4af753a..7c6ca1d30 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -6,7 +6,7 @@ //! //! - nRF52832: Section 33 //! - nRF52840: Section 6.31 -use core::future::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -16,7 +16,6 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use futures::future::poll_fn; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index c250e24ca..5f9c4f17d 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -13,12 +13,12 @@ //! memory may be used given that buffers are passed in directly to its read and write //! methods. +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 688326e9c..0685d419c 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; @@ -11,8 +12,6 @@ use embassy_sync::waitqueue::AtomicWaker; pub use embassy_usb; use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 7ad1a6bfe..410c48666 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,3 +1,4 @@ +use core::future::Future; use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; @@ -5,7 +6,6 @@ use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::Future; use pac::dma::vals::DataSize; use crate::pac::dma::vals; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 82eafdefd..a7ec5fb79 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,3 +1,4 @@ +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::slice; use core::sync::atomic::Ordering; @@ -8,8 +9,6 @@ use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{pac, peripherals, Peripheral, RegExt}; diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index fb9dc9d08..ff9157d51 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -1,8 +1,8 @@ +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::AFType; use crate::gpio::Speed; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index db4924461..b7c89931c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,4 +1,5 @@ use core::cmp; +use core::future::poll_fn; use core::task::Poll; use atomic_polyfill::{AtomicUsize, Ordering}; @@ -6,7 +7,6 @@ use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::dma::NoDma; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 520f2ab9a..10fc4a75e 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -1,10 +1,10 @@ #![macro_use] +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use rand_core::{CryptoRng, RngCore}; use crate::{pac, peripherals, Peripheral}; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 67758c492..a8bc6385f 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1,12 +1,12 @@ #![macro_use] use core::default::Default; +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; use crate::dma::NoDma; diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 5f6dabb3b..46c49a997 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; 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 futures::future::poll_fn; use super::*; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index db965824a..e5ee1181c 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; @@ -10,8 +11,6 @@ use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 92101c6b5..fcf056d36 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -2,11 +2,10 @@ //! //! This module provides a mutex that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; +use core::future::poll_fn; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use futures_util::future::poll_fn; - use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex as BlockingMutex; use crate::waitqueue::WakerRegistration; diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 34201d03a..b4d99513a 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,6 +1,6 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::mem; use core::task::{Context, Poll, Waker}; @@ -94,7 +94,7 @@ impl Signal { /// Future that completes when this Signal has been signaled. pub fn wait(&self) -> impl Future + '_ { - futures_util::future::poll_fn(move |cx| self.poll_wait(cx)) + poll_fn(move |cx| self.poll_wait(cx)) } /// non-blocking method to check whether this signal has been signaled. diff --git a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs index 7d1ad87c8..cf8b2f808 100644 --- a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs +++ b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use embassy_executor::Spawner; @@ -46,7 +47,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf/src/bin/executor_fairness_test.rs index 9ae030d07..2a28f2763 100644 --- a/examples/nrf/src/bin/executor_fairness_test.rs +++ b/examples/nrf/src/bin/executor_fairness_test.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use defmt::{info, unwrap}; @@ -26,7 +27,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) From 10d1ad2343825388277dc54db649c9349a0b6de8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:48:35 +0200 Subject: [PATCH 0140/1575] Replace futures::future::join -> embassy_futures::join::join. --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/spi.rs | 2 +- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/dcmi.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- examples/nrf/src/bin/usb_hid_mouse.rs | 2 +- examples/nrf/src/bin/usb_serial.rs | 2 +- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/Cargo.toml | 1 + examples/stm32f1/src/bin/usb_serial.rs | 2 +- examples/stm32f3/Cargo.toml | 1 + examples/stm32f3/src/bin/usb_serial.rs | 2 +- examples/stm32l5/Cargo.toml | 1 + examples/stm32l5/src/bin/usb_hid_mouse.rs | 2 +- examples/stm32l5/src/bin/usb_serial.rs | 2 +- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/gpio_async.rs | 2 +- 19 files changed, 20 insertions(+), 13 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 63997c99f..885a4746d 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -33,6 +33,7 @@ unstable-traits = ["embedded-hal-1"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 3cf823573..03293e064 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -1,9 +1,9 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; -use futures::future::join; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin as _; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b4c19f32e..484496f24 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -34,6 +34,7 @@ flavors = [ embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index ff9157d51..20e1a4070 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -429,7 +429,7 @@ where } }); - let (_, result) = futures::future::join(dma_read, result).await; + let (_, result) = embassy_futures::join::join(dma_read, result).await; unsafe { Self::toggle(false) }; @@ -537,7 +537,7 @@ where unsafe { Self::toggle(true) }; - let (_, result) = futures::future::join(dma_result, result).await; + let (_, result) = embassy_futures::join::join(dma_result, result).await; unsafe { Self::toggle(false) }; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 02e6020b0..556d12305 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -3,9 +3,9 @@ use core::ptr; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; 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 futures::future::join; use self::sealed::WordSize; use crate::dma::{slice_ptr_parts, NoDma, Transfer}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 4761fcf66..70318b78f 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -7,6 +7,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; @@ -15,7 +16,6 @@ use embassy_sync::signal::Signal; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index f1b57a17f..65fbda1cf 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -6,13 +6,13 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index f9526cbc4..a740b4e0a 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -6,12 +6,12 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 17393322c..24c3cdd67 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,6 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 00cf3e93f..bf92a1636 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -4,12 +4,12 @@ use defmt::{info, panic}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_rp::interrupt; use embassy_rp::usb::{Driver, Instance}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 33ac63db1..895e043dd 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -10,6 +10,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a9c46068f..a14e728ba 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 208f39080..27f5c260a 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -11,6 +11,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index d3702fc35..b9fd20e2b 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index c451bd221..05945f6bf 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -15,6 +15,7 @@ embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", fea embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", 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" } usbd-hid = "0.6.0" defmt = "0.3" diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 0aca6f1cd..fa92ceae3 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; @@ -11,7 +12,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index b576a7353..7484dc832 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -4,13 +4,13 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 4d6877ccd..11ecb9169 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,6 +8,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 1eeaac1f6..f20b8fcbd 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -4,9 +4,9 @@ use defmt::{assert, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_time::{Duration, Instant, Timer}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 2e7916c5feed13893921936ce26dab44c8a617a9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 23 Sep 2022 06:38:47 +0200 Subject: [PATCH 0141/1575] Add RP2040 ROM functions and intrinsics aliases --- embassy-rp/Cargo.toml | 4 + embassy-rp/src/intrinsics.rs | 273 +++++++++++++ embassy-rp/src/lib.rs | 3 + embassy-rp/src/rom_data.rs | 730 +++++++++++++++++++++++++++++++++++ 4 files changed, 1010 insertions(+) create mode 100644 embassy-rp/src/intrinsics.rs create mode 100644 embassy-rp/src/rom_data.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 885a4746d..df0af8dfb 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -22,6 +22,10 @@ unstable-pac = [] time-driver = [] +rom-func-cache = [] +disable-intrinsics = [] +rom-v2-intrinsics = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs new file mode 100644 index 000000000..9e6624cf0 --- /dev/null +++ b/embassy-rp/src/intrinsics.rs @@ -0,0 +1,273 @@ +#![macro_use] + +/// Generate a series of aliases for an intrinsic function. +macro_rules! intrinsics_aliases { + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; + + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; +} + +/// The macro used to define overridden intrinsics. +/// +/// This is heavily inspired by the macro used by compiler-builtins. The idea +/// is to abstract anything special that needs to be done to override an +/// intrinsic function. Intrinsic generation is disabled for non-ARM targets +/// so things like CI and docs generation do not have problems. Additionally +/// they can be disabled with the crate feature `disable-intrinsics` for +/// testing or comparing performance. +/// +/// Like the compiler-builtins macro, it accepts a series of functions that +/// looks like normal Rust code: +/// +/// intrinsics! { +/// extern "C" fn foo(a: i32) -> u32 { +/// // ... +/// } +/// +/// #[nonstandard_attribute] +/// extern "C" fn bar(a: i32) -> u32 { +/// // ... +/// } +/// } +/// +/// Each function can also be decorated with nonstandard attributes to control +/// additional behaviour: +/// +/// * `slower_than_default` - indicates that the override is slower than the +/// default implementation. Currently this just disables the override +/// entirely. +/// * `bootrom_v2` - indicates that the override is only available +/// on a V2 bootrom or higher. Only enabled when the feature +/// `rom-v2-intrinsics` is set. +/// * `alias` - accepts a list of names to alias the intrinsic to. +/// * `aeabi` - accepts a list of ARM EABI names to alias to. +/// +macro_rules! intrinsics { + () => {}; + + ( + #[slower_than_default] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + #[bootrom_v2] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(feature = "rom-v2-intrinsics"))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(feature = "rom-v2-intrinsics")] + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[aeabi = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ce09064a..9ac98d226 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -4,9 +4,12 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod intrinsics; + pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rom_data; pub mod rtc; pub mod spi; #[cfg(feature = "time-driver")] diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs new file mode 100644 index 000000000..93a3632a5 --- /dev/null +++ b/embassy-rp/src/rom_data.rs @@ -0,0 +1,730 @@ +//! Functions and data from the RPI Bootrom. +//! +//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: +//! +//! > The Bootrom contains a number of public functions that provide useful +//! > RP2040 functionality that might be needed in the absence of any other code +//! > on the device, as well as highly optimized versions of certain key +//! > functionality that would otherwise have to take up space in most user +//! > binaries. + +/// A bootrom function table code. +pub type RomFnTableCode = [u8; 2]; + +/// This function searches for (table) +type RomTableLookupFn = unsafe extern "C" fn(*const u16, u32) -> T; + +/// The following addresses are described at `2.8.2. Bootrom Contents` +/// Pointer to the lookup table function supplied by the rom. +const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _; + +/// Pointer to helper functions lookup table. +const FUNC_TABLE: *const u16 = 0x0000_0014 as _; + +/// Pointer to the public data lookup table. +const DATA_TABLE: *const u16 = 0x0000_0016 as _; + +/// Address of the version number of the ROM. +const VERSION_NUMBER: *const u8 = 0x0000_0013 as _; + +/// Retrive rom content from a table using a code. +fn rom_table_lookup(table: *const u16, tag: RomFnTableCode) -> T { + unsafe { + let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR); + let rom_table_lookup: RomTableLookupFn = core::mem::transmute(rom_table_lookup_ptr); + rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32) + } +} + +/// To save space, the ROM likes to store memory pointers (which are 32-bit on +/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the +/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped +/// to address `0x0000_0000` and so 16-bits are always sufficient. +/// +/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer. +unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 { + let ptr: u16 = *rom_address; + ptr as *const u32 +} + +macro_rules! declare_rom_function { + ( + $(#[$outer:meta])* + fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; + + ( + $(#[$outer:meta])* + unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; +} + +macro_rules! rom_functions { + () => {}; + + ( + $(#[$outer:meta])* + $c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; + + ( + $(#[$outer:meta])* + $c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; +} + +rom_functions! { + /// Return a count of the number of 1 bits in value. + b"P3" fn popcount32(value: u32) -> u32; + + /// Return the bits of value in the reverse order. + b"R3" fn reverse32(value: u32) -> u32; + + /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32. + b"L3" fn clz32(value: u32) -> u32; + + /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32. + b"T3" fn ctz32(value: u32) -> u32; + + /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode: + /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED + /// for the USB Mass Storage Device: + /// * 0 No pins are used as per cold boot. + /// * Otherwise a single bit set indicating which GPIO pin should be set to output and + /// raised whenever there is mass storage activity from the host. + /// * disable_interface_mask may be used to control the exposed USB interfaces: + /// * 0 To enable both interfaces (as per cold boot). + /// * 1 To disable the USB Mass Storage Interface. + /// * 2 to Disable the USB PICOBOOT Interface. + b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> (); + + /// Sets n bytes start at ptr to the value c and returns ptr + b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8; + + /// Sets n bytes start at ptr to the value c and returns ptr. + /// + /// Note this is a slightly more efficient variant of _memset that may only + /// be used if ptr is word aligned. + // Note the datasheet does not match the actual ROM for the code here, see + // https://github.com/raspberrypi/pico-feedback/issues/217 + b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + /// + /// Note this is a slightly more efficient variant of _memcpy that may only be + /// used if dest and src are word aligned. + b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8; + + /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads. + b"IF" unsafe fn connect_internal_flash() -> (); + + /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence. + /// + /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be + /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This + /// function configures the SSI with a fixed SCK clock divisor of /6. + b"EX" unsafe fn flash_exit_xip() -> (); + + /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a + /// block erase command e.g. D8h block erase, and the size of the block erased by this + /// command — this function will use the larger block erase where possible, for much higher + /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of + /// 4096 bytes. + b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> (); + + /// Program data to a range of flash addresses starting at `addr` (and + /// offset from the start of flash) and `count` bytes in size. The value + /// `addr` must be aligned to a 256-byte boundary, and `count` must be a + /// multiple of 256. + b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> (); + + /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can + /// drive the flashchip select as normal. + b"FC" unsafe fn flash_flush_cache() -> (); + + /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits, + /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported. + /// The debugger calls this function after performing a flash erase/programming operation, so + /// that the freshly-programmed code and data is visible to the debug host, without having to + /// know exactly what kind of flash device is connected. + b"CX" unsafe fn flash_enter_cmd_xip() -> (); + + /// This is the method that is entered by core 1 on reset to wait to be launched by core 0. + /// There are few cases where you should call this method (resetting core 1 is much better). + /// This method does not return and should only ever be called on core 1. + b"WV" unsafe fn wait_for_vector() -> !; +} + +// Various C intrinsics in the ROM +intrinsics! { + #[alias = __popcountdi2] + extern "C" fn __popcountsi2(x: u32) -> u32 { + popcount32(x) + } + + #[alias = __clzdi2] + extern "C" fn __clzsi2(x: u32) -> u32 { + clz32(x) + } + + #[alias = __ctzdi2] + extern "C" fn __ctzsi2(x: u32) -> u32 { + ctz32(x) + } + + // __rbit is only unofficial, but it show up in the ARM documentation, + // so may as well hook it up. + #[alias = __rbitl] + extern "C" fn __rbit(x: u32) -> u32 { + reverse32(x) + } + + unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset(dest, c as u8, n as u32); + } + + #[alias = __aeabi_memset8] + unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset4(dest as *mut u32, c as u8, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () { + memset(dest, 0, n as u32); + } + + #[alias = __aeabi_memclr8] + unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () { + memset4(dest as *mut u32, 0, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy(dest, src, n as u32); + } + + #[alias = __aeabi_memcpy8] + unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy44(dest as *mut u32, src as *const u32, n as u32); + } +} + +unsafe fn convert_str(s: *const u8) -> &'static str { + let mut end = s; + while *end != 0 { + end = end.add(1); + } + let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize); + core::str::from_utf8_unchecked(s) +} + +/// The version number of the rom. +pub fn rom_version_number() -> u8 { + unsafe { *VERSION_NUMBER } +} + +/// The Raspberry Pi Trading Ltd copyright string. +pub fn copyright_string() -> &'static str { + let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR"); + unsafe { convert_str(s) } +} + +/// The 8 most significant hex digits of the Bootrom git revision. +pub fn git_revision() -> u32 { + let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR"); + unsafe { *s } +} + +/// The start address of the floating point library code and data. +/// +/// This and fplib_end along with the individual function pointers in +/// soft_float_table can be used to copy the floating point implementation into +/// RAM if desired. +pub fn fplib_start() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FS") +} + +/// See Table 180 in the RP2040 datasheet for the contents of this table. +pub fn soft_float_table() -> *const usize { + rom_table_lookup(DATA_TABLE, *b"SF") +} + +/// The end address of the floating point library code and data. +pub fn fplib_end() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FE") +} + +/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. +pub fn soft_double_table() -> *const usize { + if rom_version_number() < 2 { + panic!( + "Double precision operations require V2 bootrom (found: V{})", + rom_version_number() + ); + } + rom_table_lookup(DATA_TABLE, *b"SD") +} + +/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms) +pub mod float_funcs { + + macro_rules! make_functions { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_functions! { + /// Calculates `a + b` + 0x00 fadd(a: f32, b: f32) -> f32; + /// Calculates `a - b` + 0x04 fsub(a: f32, b: f32) -> f32; + /// Calculates `a * b` + 0x08 fmul(a: f32, b: f32) -> f32; + /// Calculates `a / b` + 0x0c fdiv(a: f32, b: f32) -> f32; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 fsqrt(v: f32) -> f32; + /// Converts an f32 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c float_to_int(v: f32) -> i32; + /// Converts an f32 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 float_to_fix(v: f32, n: i32) -> i32; + /// Converts an f32 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 float_to_uint(v: f32) -> u32; + /// Converts an f32 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 float_to_ufix(v: f32, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// f32 value, rounding to even on tie + 0x2c int_to_float(v: i32) -> f32; + /// Converts a signed fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_float(v: i32, n: i32) -> f32; + /// Converts an unsigned integer to the nearest + /// f32 value, rounding to even on tie + 0x34 uint_to_float(v: u32) -> f32; + /// Converts an unsigned fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x38 ufix_to_float(v: u32, n: i32) -> f32; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c fcos(angle: f32) -> f32; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 fsin(angle: f32) -> f32; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 ftan(angle: f32) -> f32; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c fexp(v: f32) -> f32; + /// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity + 0x50 fln(v: f32) -> f32; + } + + macro_rules! make_functions_v2 { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + if $crate::rom_data::rom_version_number() < 2 { + panic!( + "Floating point function requires V2 bootrom (found: V{})", + $crate::rom_data::rom_version_number() + ); + } + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + // These are only on BootROM v2 or higher + make_functions_v2! { + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 fcmp(a: f32, b: f32) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 fatan2(y: f32, x: f32) -> f32; + /// Converts a signed 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x5c int64_to_float(v: i64) -> f32; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_float(v: i64, n: i32) -> f32; + /// Converts an unsigned 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x64 uint64_to_float(v: u64) -> f32; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest f32 value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_float(v: u64, n: i32) -> f32; + /// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c float_to_int64(v: f32) -> i64; + /// Converts an f32 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 float_to_fix64(v: f32, n: i32) -> f32; + /// Converts an f32 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 float_to_uint64(v: f32) -> u64; + /// Converts an f32 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 float_to_ufix64(v: f32, n: i32) -> u64; + /// Converts an f32 to an f64. + 0x7c float_to_double(v: f32) -> f64; + } +} + +/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms) +pub mod double_funcs { + + macro_rules! make_double_funcs { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_double_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_double_funcs! { + /// Calculates `a + b` + 0x00 dadd(a: f64, b: f64) -> f64; + /// Calculates `a - b` + 0x04 dsub(a: f64, b: f64) -> f64; + /// Calculates `a * b` + 0x08 dmul(a: f64, b: f64) -> f64; + /// Calculates `a / b` + 0x0c ddiv(a: f64, b: f64) -> f64; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 dsqrt(v: f64) -> f64; + /// Converts an f64 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c double_to_int(v: f64) -> i32; + /// Converts an f64 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 double_to_fix(v: f64, n: i32) -> i32; + /// Converts an f64 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 double_to_uint(v: f64) -> u32; + /// Converts an f64 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 double_to_ufix(v: f64, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// double value, rounding to even on tie + 0x2c int_to_double(v: i32) -> f64; + /// Converts a signed fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_double(v: i32, n: i32) -> f64; + /// Converts an unsigned integer to the nearest + /// double value, rounding to even on tie + 0x34 uint_to_double(v: u32) -> f64; + /// Converts an unsigned fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so f = + /// nearest(v/(2^n)) + 0x38 ufix_to_double(v: u32, n: i32) -> f64; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c dcos(angle: f64) -> f64; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 dsin(angle: f64) -> f64; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 dtan(angle: f64) -> f64; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c dexp(v: f64) -> f64; + /// Calculates the natural logarithm of v. If v <= 0 return -Infinity + 0x50 dln(v: f64) -> f64; + + // These are only on BootROM v2 or higher + + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 dcmp(a: f64, b: f64) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 datan2(y: f64, x: f64) -> f64; + /// Converts a signed 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x5c int64_to_double(v: i64) -> f64; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_doubl(v: i64, n: i32) -> f64; + /// Converts an unsigned 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x64 uint64_to_double(v: u64) -> f64; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest double value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_double(v: u64, n: i32) -> f64; + /// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c double_to_int64(v: f64) -> i64; + /// Converts an f64 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 double_to_fix64(v: f64, n: i32) -> i64; + /// Converts an f64 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 double_to_uint64(v: f64) -> u64; + /// Converts an f64 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 double_to_ufix64(v: f64, n: i32) -> u64; + /// Converts an f64 to an f32 + 0x7c double_to_float(v: f64) -> f32; + } +} From 7bb9620b1ae2a6290b98769835e339a4157d2d47 Mon Sep 17 00:00:00 2001 From: Vincent Stakenburg Date: Thu, 15 Sep 2022 12:34:17 +0200 Subject: [PATCH 0142/1575] make `State::new()` const, consistent with others --- embassy-stm32/src/eth/v1/mod.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 1ab0438ad..38629a932 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -29,7 +29,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index d67c3c5e4..a81ee1183 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -19,7 +19,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index a7fa43894..5f6dabb3b 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -11,7 +11,7 @@ use super::*; pub struct State<'d, T: BasicInstance>(StateStorage>); impl<'d, T: BasicInstance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } From e5af4c4bcef8a826d14f741fd72ab99ad930a626 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:32:43 +0200 Subject: [PATCH 0143/1575] Add .into_inner() and .get_mut() to Mutex --- embassy-sync/src/mutex.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 75a6e8dd3..a792cf070 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -111,6 +111,20 @@ where Ok(MutexGuard { mutex: self }) } + + /// Consumes this mutex, returning the underlying data. + pub fn into_inner(self) -> T + where T: Sized { + self.inner.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the Mutex mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() + } } /// Async mutex guard. From 3f672c8a934602d444061ab0880ed415777d21f6 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:44:33 +0200 Subject: [PATCH 0144/1575] Make rustfmt happy --- embassy-sync/src/mutex.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index a792cf070..92101c6b5 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -114,7 +114,9 @@ where /// Consumes this mutex, returning the underlying data. pub fn into_inner(self) -> T - where T: Sized { + where + T: Sized, + { self.inner.into_inner() } From f46b83874693382b891f0449c412b4f8591b81d8 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:45:03 +0200 Subject: [PATCH 0145/1575] Feature-gate time-driver in embassy-rp --- ci.sh | 2 +- embassy-rp/Cargo.toml | 2 ++ embassy-rp/src/lib.rs | 2 ++ examples/rp/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index fa24d5268..ae1b44281 100755 --- a/ci.sh +++ b/ci.sh @@ -120,7 +120,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/time-driver --out-dir out/tests/rpi-pico \ $BUILD_EXTRA diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f6525..1e26f7dd2 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -20,6 +20,8 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] +time-driver = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf567..8dcefece2 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; +#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -108,6 +109,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); + #[cfg(feature = "time-driver")] timer::init(); dma::init(); } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 18a92b094..17393322c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" 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"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } From 5d1576ea736e91949134b56c786bcc42822cc17e Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 16:45:59 +0200 Subject: [PATCH 0146/1575] Add time-driver feature to docs --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 1e26f7dd2..7315d673c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] flavors = [ { name = "rp2040", target = "thumbv6m-none-eabi" }, ] From 81298394b5f831197bd758d2e6bd665b7257bb73 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 14:54:24 -0700 Subject: [PATCH 0147/1575] rp: remove extraneous newlines in logs --- embassy-rp/src/gpio.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a0328302a..9b9a08110 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -159,7 +159,7 @@ unsafe fn IO_IRQ_BANK0() { w.set_edge_low(pin_group, false); } InterruptTrigger::LevelHigh => { - debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin); + debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); w.set_level_high(pin_group, false); } InterruptTrigger::LevelLow => { @@ -198,7 +198,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { - debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin()); + debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); } InterruptTrigger::LevelLow => { @@ -245,45 +245,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // the pin and if it has been disabled that means it was done by the // interrupt service routine, so we then know that the event/trigger // happened and Poll::Ready will be returned. - debug!("{:?} for pin {}\n", self.level, self.pin.pin()); + debug!("{:?} for pin {}", self.level, self.pin.pin()); match self.level { InterruptTrigger::AnyEdge => { if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelHigh => { if !inte.level_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelLow => { if !inte.level_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeHigh => { if !inte.edge_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeLow => { if !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } } - debug!("InputFuture::poll return Poll::Pending\n"); + debug!("InputFuture::poll return Poll::Pending"); Poll::Pending } } From c14527486d7b3d4b2c66004c922c40f7819e0411 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:02:05 -0700 Subject: [PATCH 0148/1575] rp: fix async SPI read and write --- embassy-rp/src/dma.rs | 38 ++++++++++++++++++++++++++++ embassy-rp/src/spi.rs | 59 ++++++++++++++++++++++++++++++------------- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index acf338225..b256cc2f0 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,6 +56,25 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } +pub unsafe fn read_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: *const W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let mut dummy: u32 = 0; + copy_inner( + ch, + from as *const u32, + &mut dummy as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], @@ -75,6 +94,25 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } +pub unsafe fn write_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + to: *mut W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let dummy: u32 = 0; + copy_inner( + ch, + &dummy as *const u32, + to as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn copy<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: &[W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 74f0b04de..e7cd99929 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,53 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let ch = self.tx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); reg.set_txdmae(true); - }); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read_repeated( + rx_ch, + self.inner.regs().dr().ptr() as *const u8, + buffer.len(), + T::RX_DREQ, + ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let ch = self.rx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { reg.set_rxdmae(true); - }); + reg.set_txdmae(true); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } @@ -364,20 +387,20 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); assert_eq!(from_len, to_len); + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + }) + }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_txdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_rxdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) From 4322293f634b922c1d12dd544fcdc04e56df1292 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:23:17 -0700 Subject: [PATCH 0149/1575] rp: let SPI RX overflow during async write --- embassy-rp/src/dma.rs | 19 ------------------- embassy-rp/src/spi.rs | 35 +++++++++++++++++------------------ 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index b256cc2f0..7ad1a6bfe 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,25 +56,6 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } -pub unsafe fn read_repeated<'a, C: Channel, W: Word>( - ch: impl Peripheral

+ 'a, - from: *const W, - len: usize, - dreq: u8, -) -> Transfer<'a, C> { - let mut dummy: u32 = 0; - copy_inner( - ch, - from as *const u32, - &mut dummy as *mut u32, - len, - W::size(), - false, - false, - dreq, - ) -} - pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index e7cd99929..3cf823573 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,29 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - unsafe { - self.inner.regs().dmacr().write(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - }) - }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_txdmae(true); + }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - let rx_ch = self.rx_dma.as_mut().unwrap(); - let rx_transfer = unsafe { - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read_repeated( - rx_ch, - self.inner.regs().dr().ptr() as *const u8, - buffer.len(), - T::RX_DREQ, - ) - }; - join(tx_transfer, rx_transfer).await; + tx_transfer.await; + + let p = self.inner.regs(); + unsafe { + while p.sr().read().bsy() {} + + // clear RX FIFO contents to prevent stale reads + while p.sr().read().rne() { + let _: u16 = p.dr().read().data(); + } + // clear RX overrun interrupt + p.icr().write(|w| w.set_roric(true)); + } + Ok(()) } From 54ba4725408bca51c5997d16b58cf334b519ac24 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 09:42:40 +0200 Subject: [PATCH 0150/1575] Remove BootFlash borrow Compiler will infer a different lifetime for BootFlash than for the borrowed flash, which makes it require more type annotations than if it was just owning the type. Since it doesn't really matter if it owns or borrows in practical use, change it to own so that it simplifies usage. --- embassy-boot/boot/src/lib.rs | 24 +++++++++++----------- examples/boot/bootloader/nrf/src/main.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 7 +++---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4a2b112a9..015dd58db 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -447,24 +447,24 @@ where } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> +pub struct BootFlash where F: NorFlash + ReadNorFlash, { - flash: &'a mut F, + flash: F, } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl BootFlash where F: NorFlash + ReadNorFlash, { /// Create a new instance of a bootable flash - pub fn new(flash: &'a mut F) -> Self { + pub fn new(flash: F) -> Self { Self { flash } } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl Flash for BootFlash where F: NorFlash + ReadNorFlash, { @@ -472,14 +472,14 @@ where const ERASE_VALUE: u8 = ERASE_VALUE; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ErrorType for BootFlash where F: ReadNorFlash + NorFlash, { type Error = F::Error; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl NorFlash for BootFlash where F: ReadNorFlash + NorFlash, { @@ -487,26 +487,26 @@ where const ERASE_SIZE: usize = F::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(self.flash, from, to) + F::erase(&mut self.flash, from, to) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(self.flash, offset, bytes) + F::write(&mut self.flash, offset, bytes) } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ReadNorFlash for BootFlash where F: ReadNorFlash + NorFlash, { const READ_SIZE: usize = F::READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(self.flash, offset, bytes) + F::read(&mut self.flash, offset, bytes) } fn capacity(&self) -> usize { - F::capacity(self.flash) + F::capacity(&self.flash) } } diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 9031997c2..8266206b3 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -21,7 +21,7 @@ fn main() -> ! { let mut bl = BootLoader::default(); let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - &mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), + WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), ))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index bb5d3e531..294464d1c 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,10 +20,9 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let mut flash = Flash::unlock(p.FLASH); - let start = bl.prepare(&mut SingleFlashConfig::new( - &mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash), - )); + let flash = Flash::unlock(p.FLASH); + let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } } From 334dfcdb65f2ec9c5686923cb1f854492fae02f0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 14:03:04 +0200 Subject: [PATCH 0151/1575] Take into account size of revert index Fixes a bug in the partition assertions that ensures that the state page(s) have enough space for 2x active partition range. Add unit test to verify that panic is observed. --- embassy-boot/boot/src/lib.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 015dd58db..3d359533e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -222,10 +222,7 @@ impl BootLoader { page: &mut [u8], ) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(self.active.len() % page.len(), 0); - assert_eq!(self.dfu.len() % page.len(), 0); - assert!(self.dfu.len() - self.active.len() >= page.len()); - assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE); + assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); assert_eq!(magic.len(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active @@ -409,6 +406,13 @@ impl BootLoader { } } +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { + assert_eq!(active.len() % page_size, 0); + assert_eq!(dfu.len() % page_size, 0); + assert!(dfu.len() - active.len() >= page_size); + assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); +} + /// Convenience provider that uses a single flash for all partitions. pub struct SingleFlashConfig<'a, F> where @@ -919,6 +923,15 @@ mod tests { } } + #[test] + #[should_panic] + fn test_range_asserts() { + const ACTIVE: Partition = Partition::new(4096, 4194304); + const DFU: Partition = Partition::new(4194304, 2 * 4194304); + const STATE: Partition = Partition::new(0, 4096); + assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + } + struct MemFlash([u8; SIZE]); impl NorFlash From 0db1332da82afa2ac7a58c15403ad6c5d0f367f4 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 06:45:27 +0200 Subject: [PATCH 0152/1575] Implement RealTimeClock for embassy-rp --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/lib.rs | 3 + embassy-rp/src/rtc/datetime_chrono.rs | 62 ++++++++ embassy-rp/src/rtc/datetime_no_deps.rs | 127 +++++++++++++++++ embassy-rp/src/rtc/filter.rs | 100 +++++++++++++ embassy-rp/src/rtc/mod.rs | 188 +++++++++++++++++++++++++ 7 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/rtc/datetime_chrono.rs create mode 100644 embassy-rp/src/rtc/datetime_no_deps.rs create mode 100644 embassy-rp/src/rtc/filter.rs create mode 100644 embassy-rp/src/rtc/mod.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 7315d673c..63997c99f 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -46,6 +46,7 @@ cortex-m-rt = ">=0.6.15,<0.8" 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 } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 3ad1e5d82..1c446f389 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -122,7 +122,7 @@ pub(crate) fn clk_peri_freq() -> u32 { 125_000_000 } -pub(crate) fn _clk_rtc_freq() -> u32 { +pub(crate) fn clk_rtc_freq() -> u32 { 46875 } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 8dcefece2..c3976d261 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rtc; pub mod spi; #[cfg(feature = "time-driver")] pub mod timer; @@ -85,6 +86,8 @@ embassy_hal_common::peripherals! { DMA_CH11, USB, + + RTC, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/rtc/datetime_chrono.rs b/embassy-rp/src/rtc/datetime_chrono.rs new file mode 100644 index 000000000..b3c78dd47 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_chrono.rs @@ -0,0 +1,62 @@ +use chrono::{Datelike, Timelike}; + +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Alias for [`chrono::NaiveDateTime`] +pub type DateTime = chrono::NaiveDateTime; +/// Alias for [`chrono::Weekday`] +pub type DayOfWeek = chrono::Weekday; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] has an invalid year. The year must be between 0 and 4095. + InvalidYear, + /// The [DateTime] contains an invalid date. + InvalidDate, + /// The [DateTime] contains an invalid time. + InvalidTime, +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw.num_days_from_sunday() as u8 +} + +pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year() < 0 || dt.year() > 4095 { + // rp2040 can't hold these years + Err(Error::InvalidYear) + } else { + // The rest of the chrono date is assumed to be valid + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year() as u16); + w.set_month(dt.month() as u8); + w.set_day(dt.day() as u8); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.weekday().num_days_from_sunday() as u8); + w.set_hour(dt.hour() as u8); + w.set_min(dt.minute() as u8); + w.set_sec(dt.second() as u8); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year() as i32; + let month = rtc_1.month() as u32; + let day = rtc_1.day() as u32; + + let hour = rtc_0.hour() as u32; + let minute = rtc_0.min() as u32; + let second = rtc_0.sec() as u32; + + let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?; + let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?; + Ok(DateTime::new(date, time)) +} diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs new file mode 100644 index 000000000..92770e984 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_no_deps.rs @@ -0,0 +1,127 @@ +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. + InvalidYear, + /// The [DateTime] contains an invalid month value. Must be between `1..=12`. + InvalidMonth, + /// The [DateTime] contains an invalid day value. Must be between `1..=31`. + InvalidDay, + /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday. + InvalidDayOfWeek( + /// The value of the DayOfWeek that was given. + u8, + ), + /// The [DateTime] contains an invalid hour value. Must be between `0..=23`. + InvalidHour, + /// The [DateTime] contains an invalid minute value. Must be between `0..=59`. + InvalidMinute, + /// The [DateTime] contains an invalid second value. Must be between `0..=59`. + InvalidSecond, +} + +/// Structure containing date and time information +pub struct DateTime { + /// 0..4095 + pub year: u16, + /// 1..12, 1 is January + pub month: u8, + /// 1..28,29,30,31 depending on month + pub day: u8, + /// + pub day_of_week: DayOfWeek, + /// 0..23 + pub hour: u8, + /// 0..59 + pub minute: u8, + /// 0..59 + pub second: u8, +} + +/// A day of the week +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[allow(missing_docs)] +pub enum DayOfWeek { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, +} + +fn day_of_week_from_u8(v: u8) -> Result { + Ok(match v { + 0 => DayOfWeek::Sunday, + 1 => DayOfWeek::Monday, + 2 => DayOfWeek::Tuesday, + 3 => DayOfWeek::Wednesday, + 4 => DayOfWeek::Thursday, + 5 => DayOfWeek::Friday, + 6 => DayOfWeek::Saturday, + x => return Err(Error::InvalidDayOfWeek(x)), + }) +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw as u8 +} + +pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year > 4095 { + Err(Error::InvalidYear) + } else if dt.month < 1 || dt.month > 12 { + Err(Error::InvalidMonth) + } else if dt.day < 1 || dt.day > 31 { + Err(Error::InvalidDay) + } else if dt.hour > 23 { + Err(Error::InvalidHour) + } else if dt.minute > 59 { + Err(Error::InvalidMinute) + } else if dt.second > 59 { + Err(Error::InvalidSecond) + } else { + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year); + w.set_month(dt.month); + w.set_day(dt.day); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.day_of_week as u8); + w.set_hour(dt.hour); + w.set_min(dt.minute); + w.set_sec(dt.second); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year(); + let month = rtc_1.month(); + let day = rtc_1.day(); + + let day_of_week = rtc_0.dotw(); + let hour = rtc_0.hour(); + let minute = rtc_0.min(); + let second = rtc_0.sec(); + + let day_of_week = day_of_week_from_u8(day_of_week)?; + Ok(DateTime { + year, + month, + day, + day_of_week, + hour, + minute, + second, + }) +} diff --git a/embassy-rp/src/rtc/filter.rs b/embassy-rp/src/rtc/filter.rs new file mode 100644 index 000000000..d4a3bab2f --- /dev/null +++ b/embassy-rp/src/rtc/filter.rs @@ -0,0 +1,100 @@ +use super::DayOfWeek; +use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1}; + +/// A filter used for [`RealTimeClock::schedule_alarm`]. +/// +/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm +#[derive(Default)] +pub struct DateTimeFilter { + /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. + pub year: Option, + /// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value. + pub month: Option, + /// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value. + pub day: Option, + /// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value. + pub day_of_week: Option, + /// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value. + pub hour: Option, + /// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value. + pub minute: Option, + /// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value. + pub second: Option, +} + +impl DateTimeFilter { + /// Set a filter on the given year + pub fn year(mut self, year: u16) -> Self { + self.year = Some(year); + self + } + /// Set a filter on the given month + pub fn month(mut self, month: u8) -> Self { + self.month = Some(month); + self + } + /// Set a filter on the given day + pub fn day(mut self, day: u8) -> Self { + self.day = Some(day); + self + } + /// Set a filter on the given day of the week + pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self { + self.day_of_week = Some(day_of_week); + self + } + /// Set a filter on the given hour + pub fn hour(mut self, hour: u8) -> Self { + self.hour = Some(hour); + self + } + /// Set a filter on the given minute + pub fn minute(mut self, minute: u8) -> Self { + self.minute = Some(minute); + self + } + /// Set a filter on the given second + pub fn second(mut self, second: u8) -> Self { + self.second = Some(second); + self + } +} + +// register helper functions +impl DateTimeFilter { + pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) { + if let Some(year) = self.year { + w.set_year_ena(true); + + w.set_year(year); + } + if let Some(month) = self.month { + w.set_month_ena(true); + w.set_month(month); + } + if let Some(day) = self.day { + w.set_day_ena(true); + w.set_day(day); + } + } + pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) { + if let Some(day_of_week) = self.day_of_week { + w.set_dotw_ena(true); + let bits = super::datetime::day_of_week_to_u8(day_of_week); + + w.set_dotw(bits); + } + if let Some(hour) = self.hour { + w.set_hour_ena(true); + w.set_hour(hour); + } + if let Some(minute) = self.minute { + w.set_min_ena(true); + w.set_min(minute); + } + if let Some(second) = self.second { + w.set_sec_ena(true); + w.set_sec(second); + } + } +} diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs new file mode 100644 index 000000000..7f3bbbe73 --- /dev/null +++ b/embassy-rp/src/rtc/mod.rs @@ -0,0 +1,188 @@ +mod filter; + +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; + +pub use self::filter::DateTimeFilter; + +#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")] +#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")] +mod datetime; + +pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; +use crate::clocks::clk_rtc_freq; + +/// A reference to the real time clock of the system +pub struct RealTimeClock<'d, T: Instance> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> RealTimeClock<'d, T> { + /// Create a new instance of the real time clock, with the given date as an initial value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn new(inner: impl Peripheral

+ 'd, initial_date: DateTime) -> Result { + into_ref!(inner); + + // Set the RTC divider + unsafe { + inner + .regs() + .clkdiv_m1() + .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1)) + }; + + let mut result = Self { inner }; + result.set_leap_year_check(true); // should be on by default, make sure this is the case. + result.set_datetime(initial_date)?; + Ok(result) + } + + /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. + /// + /// Leap year checking is enabled by default. + pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) { + unsafe { + self.inner + .regs() + .ctrl() + .modify(|w| w.set_force_notleapyear(!leap_year_check_enabled)) + }; + } + + /// Checks to see if this RealTimeClock is running + pub fn is_running(&self) -> bool { + unsafe { self.inner.regs().ctrl().read().rtc_active() } + } + + /// Set the datetime to a new value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> { + self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; + + // disable RTC while we configure it + unsafe { + self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); + while self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + + self.inner.regs().setup_0().write(|w| { + self::datetime::write_setup_0(&t, w); + }); + self.inner.regs().setup_1().write(|w| { + self::datetime::write_setup_1(&t, w); + }); + + // Load the new datetime and re-enable RTC + self.inner.regs().ctrl().write(|w| w.set_load(true)); + self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); + while !self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + } + Ok(()) + } + + /// Return the current datetime. + /// + /// # Errors + /// + /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. + pub fn now(&self) -> Result { + if !self.is_running() { + return Err(RtcError::NotRunning); + } + + let rtc_0 = unsafe { self.inner.regs().rtc_0().read() }; + let rtc_1 = unsafe { self.inner.regs().rtc_1().read() }; + + self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime) + } + + /// Disable the alarm that was scheduled with [`schedule_alarm`]. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn disable_alarm(&mut self) { + unsafe { + self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false)); + + while self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Schedule an alarm. The `filter` determines at which point in time this alarm is set. + /// + /// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call: + /// ```no_run + /// # #[cfg(feature = "chrono")] + /// # fn main() { } + /// # #[cfg(not(feature = "chrono"))] + /// # fn main() { + /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; + /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; + /// let now = real_time_clock.now().unwrap(); + /// real_time_clock.schedule_alarm( + /// DateTimeFilter::default() + /// .minute(if now.minute == 59 { 0 } else { now.minute + 1 }) + /// ); + /// # } + /// ``` + pub fn schedule_alarm(&mut self, filter: DateTimeFilter) { + self.disable_alarm(); + + unsafe { + self.inner.regs().irq_setup_0().write(|w| { + filter.write_setup_0(w); + }); + self.inner.regs().irq_setup_1().write(|w| { + filter.write_setup_1(w); + }); + + // Set the enable bit and check if it is set + self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); + while !self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered, + /// or the next [`schedule_alarm`] will never fire. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn clear_interrupt(&mut self) { + self.disable_alarm(); + } +} + +/// Errors that can occur on methods on [RtcClock] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RtcError { + /// An invalid DateTime was given or stored on the hardware. + InvalidDateTime(DateTimeError), + + /// The RTC clock is not running + NotRunning, +} + +mod sealed { + pub trait Instance { + fn regs(&self) -> crate::pac::rtc::Rtc; + } +} + +pub trait Instance: sealed::Instance {} + +impl sealed::Instance for crate::peripherals::RTC { + fn regs(&self) -> crate::pac::rtc::Rtc { + crate::pac::RTC + } +} +impl Instance for crate::peripherals::RTC {} From 7412a859fd7089b874b4c162b8e3cc822f09ab22 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:28:56 +0200 Subject: [PATCH 0153/1575] Update Rust nightly. Removes feature(generic_associated_types) --- embassy-boot/boot/src/lib.rs | 1 - embassy-boot/nrf/src/lib.rs | 1 - embassy-boot/stm32/src/lib.rs | 1 - embassy-embedded-hal/src/lib.rs | 2 +- embassy-lora/src/lib.rs | 1 - embassy-net/src/lib.rs | 2 +- embassy-nrf/src/lib.rs | 2 +- embassy-rp/src/lib.rs | 2 +- embassy-stm32/src/lib.rs | 2 +- embassy-sync/src/lib.rs | 2 +- embassy-time/src/lib.rs | 2 +- embassy-usb-hid/src/lib.rs | 1 - embassy-usb-serial/src/lib.rs | 1 - embassy-usb/src/lib.rs | 1 - examples/boot/application/nrf/src/bin/a.rs | 1 - examples/boot/application/nrf/src/bin/b.rs | 1 - examples/nrf/src/bin/usb_ethernet.rs | 1 - examples/nrf/src/bin/usb_hid_keyboard.rs | 1 - examples/nrf/src/bin/usb_hid_mouse.rs | 1 - examples/nrf/src/bin/usb_serial.rs | 1 - examples/nrf/src/bin/usb_serial_multitask.rs | 1 - examples/rp/src/bin/usb_ethernet.rs | 1 - examples/rp/src/bin/usb_serial.rs | 1 - examples/stm32l0/src/bin/lorawan.rs | 1 - examples/stm32l5/src/bin/usb_ethernet.rs | 1 - examples/stm32l5/src/bin/usb_hid_mouse.rs | 1 - examples/stm32wl/src/bin/lorawan.rs | 1 - examples/stm32wl/src/bin/subghz.rs | 1 - rust-toolchain.toml | 2 +- 29 files changed, 8 insertions(+), 29 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 3d359533e..96878ace9 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,5 +1,4 @@ #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] #![no_std] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 0c14781a2..385e089fe 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 39f080517..edba39cca 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 0c6f2786a..a12a3a3a0 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![warn(missing_docs)] //! Utilities to use `embedded-hal` traits with Embassy. diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 2483dcb2e..90ba0d1d4 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device //! crate's async LoRaWAN MAC implementation. diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 8eebc798e..4d30550d3 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index f3b3ca0ca..d7bd21702 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -43,7 +43,7 @@ //! mutable slices always reside in RAM. #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #[cfg(not(any( feature = "nrf51", diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index c3976d261..9ce09064a 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 30ff02d56..0392e8086 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 25150e8aa..80bb907a3 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 5b2620986..4edc883fe 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] #![warn(missing_docs)] diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs index 5fee60bbc..8b181aec8 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb-hid/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] //! Implements HID functionality for a usb-device device. diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index f3de2ec1b..27b536a6b 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index b165804ef..ca7dde627 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 133a3e678..7a404a914 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_boot_nrf::FirmwareUpdater; diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 5394bf0c7..1373f277d 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 352660b59..33ca380ff 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 7fdb0b685..4761fcf66 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 7cd2ece17..f1b57a17f 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index a68edb329..f9526cbc4 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index d62d7e520..c646c0bbd 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 2df7f62f6..166ffe175 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 74be1f598..00cf3e93f 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::{info, panic}; diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 00ff67f3f..27d7c29c2 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -3,7 +3,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 2c8706e41..c96a83ead 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 7d763e7fd..0aca6f1cd 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 9143e64da..7f34dd306 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index 8f674d796..3c60a8de4 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f5e342edc..1ec19e58b 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-08-16" +channel = "nightly-2022-09-22" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", From 2fed9f949a28f197e8dbc72fa923c71679dd19c2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:42:49 +0200 Subject: [PATCH 0154/1575] Replace futures::future::poll_fn -> core::future::poll_fn. --- embassy-executor/src/spawner.rs | 3 +-- embassy-lora/src/stm32wl/mod.rs | 3 +-- embassy-net/src/stack.rs | 3 +-- embassy-net/src/tcp.rs | 6 +++--- embassy-net/src/udp.rs | 2 +- embassy-nrf/src/buffered_uarte.rs | 3 +-- embassy-nrf/src/gpiote.rs | 3 +-- embassy-nrf/src/qdec.rs | 2 +- embassy-nrf/src/qspi.rs | 2 +- embassy-nrf/src/rng.rs | 2 +- embassy-nrf/src/saadc.rs | 2 +- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/temp.rs | 2 +- embassy-nrf/src/timer.rs | 2 +- embassy-nrf/src/twim.rs | 3 +-- embassy-nrf/src/uarte.rs | 2 +- embassy-nrf/src/usb.rs | 3 +-- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/usb.rs | 3 +-- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 3 +-- embassy-stm32/src/usb/usb.rs | 3 +-- embassy-sync/src/mutex.rs | 3 +-- embassy-sync/src/signal.rs | 4 ++-- examples/nrf-rtos-trace/src/bin/rtos_trace.rs | 3 ++- examples/nrf/src/bin/executor_fairness_test.rs | 3 ++- 29 files changed, 34 insertions(+), 43 deletions(-) diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 25a0d7dbb..400d973ff 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,10 +1,9 @@ +use core::future::poll_fn; use core::marker::PhantomData; use core::mem; use core::ptr::NonNull; use core::task::Poll; -use futures_util::future::poll_fn; - use super::raw; /// Token to spawn a newly-created task in an executor. diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 4d11244b6..e28fa2c1a 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,5 +1,5 @@ //! A radio driver integration for the radio found on STM32WL family devices. -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::Poll; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -11,7 +11,6 @@ use embassy_stm32::subghz::{ Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 8d2dd4bca..3a7610758 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -1,10 +1,9 @@ use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; -use futures::future::poll_fn; use futures::pin_mut; use heapless::Vec; #[cfg(feature = "dhcpv4")] diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0fa873602..f8fff3e2d 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; use smoltcp::time::Duration; @@ -103,7 +103,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)), tcp::State::Listen => unreachable!(), @@ -128,7 +128,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => { s.register_send_waker(cx.waker()); diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 78b09a492..f2e33493c 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c3cba2470..fec875cb8 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::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -23,7 +23,6 @@ use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorag use embassy_hal_common::ring_buffer::RingBuffer; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index d99f592b0..b418be9d5 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,10 +1,9 @@ use core::convert::Infallible; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 762e09715..253c85c32 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,10 +1,10 @@ //! Quadrature decoder interface +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index c97cb1656..ea0a17031 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -1,11 +1,11 @@ #![macro_use] +use core::future::poll_fn; use core::ptr; use core::task::Poll; use embassy_hal_common::drop::DropBomb; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 42da51d0f..e0caeaaee 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,3 +1,4 @@ +use core::future::poll_fn; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; @@ -5,7 +6,6 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::interrupt::InterruptExt; use crate::peripherals::RNG; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 9bc89eb38..d1c82423e 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; // We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 2955182e4..51cd73a47 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,12 +1,12 @@ #![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 futures::future::poll_fn; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index d520fd686..7a7f61b51 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -1,12 +1,12 @@ //! Temperature sensor interface. +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; -use futures::future::poll_fn; use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3de5a8962..bc8710640 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::interrupt::{Interrupt, InterruptExt}; use crate::ppi::{Event, Task}; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 3d4af753a..7c6ca1d30 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -6,7 +6,7 @@ //! //! - nRF52832: Section 33 //! - nRF52840: Section 6.31 -use core::future::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -16,7 +16,6 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use futures::future::poll_fn; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index c250e24ca..5f9c4f17d 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -13,12 +13,12 @@ //! memory may be used given that buffers are passed in directly to its read and write //! methods. +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 688326e9c..0685d419c 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; @@ -11,8 +12,6 @@ use embassy_sync::waitqueue::AtomicWaker; pub use embassy_usb; use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 7ad1a6bfe..410c48666 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,3 +1,4 @@ +use core::future::Future; use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; @@ -5,7 +6,6 @@ use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::Future; use pac::dma::vals::DataSize; use crate::pac::dma::vals; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 82eafdefd..a7ec5fb79 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,3 +1,4 @@ +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::slice; use core::sync::atomic::Ordering; @@ -8,8 +9,6 @@ use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{pac, peripherals, Peripheral, RegExt}; diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index fb9dc9d08..ff9157d51 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -1,8 +1,8 @@ +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::AFType; use crate::gpio::Speed; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index db4924461..b7c89931c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,4 +1,5 @@ use core::cmp; +use core::future::poll_fn; use core::task::Poll; use atomic_polyfill::{AtomicUsize, Ordering}; @@ -6,7 +7,6 @@ use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::dma::NoDma; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 520f2ab9a..10fc4a75e 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -1,10 +1,10 @@ #![macro_use] +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use rand_core::{CryptoRng, RngCore}; use crate::{pac, peripherals, Peripheral}; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 67758c492..a8bc6385f 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1,12 +1,12 @@ #![macro_use] use core::default::Default; +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; use crate::dma::NoDma; diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 5f6dabb3b..46c49a997 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; 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 futures::future::poll_fn; use super::*; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index db965824a..e5ee1181c 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; @@ -10,8 +11,6 @@ use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 92101c6b5..fcf056d36 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -2,11 +2,10 @@ //! //! This module provides a mutex that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; +use core::future::poll_fn; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use futures_util::future::poll_fn; - use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex as BlockingMutex; use crate::waitqueue::WakerRegistration; diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 34201d03a..b4d99513a 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,6 +1,6 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::mem; use core::task::{Context, Poll, Waker}; @@ -94,7 +94,7 @@ impl Signal { /// Future that completes when this Signal has been signaled. pub fn wait(&self) -> impl Future + '_ { - futures_util::future::poll_fn(move |cx| self.poll_wait(cx)) + poll_fn(move |cx| self.poll_wait(cx)) } /// non-blocking method to check whether this signal has been signaled. diff --git a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs index 7d1ad87c8..cf8b2f808 100644 --- a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs +++ b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use embassy_executor::Spawner; @@ -46,7 +47,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf/src/bin/executor_fairness_test.rs index 9ae030d07..2a28f2763 100644 --- a/examples/nrf/src/bin/executor_fairness_test.rs +++ b/examples/nrf/src/bin/executor_fairness_test.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use defmt::{info, unwrap}; @@ -26,7 +27,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) From 4f33cc5d1a0c5eb4ce9044f477f879ac0445ec36 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:48:35 +0200 Subject: [PATCH 0155/1575] Replace futures::future::join -> embassy_futures::join::join. --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/spi.rs | 2 +- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/dcmi.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- examples/nrf/src/bin/usb_hid_mouse.rs | 2 +- examples/nrf/src/bin/usb_serial.rs | 2 +- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/Cargo.toml | 1 + examples/stm32f1/src/bin/usb_serial.rs | 2 +- examples/stm32f3/Cargo.toml | 1 + examples/stm32f3/src/bin/usb_serial.rs | 2 +- examples/stm32l5/Cargo.toml | 1 + examples/stm32l5/src/bin/usb_hid_mouse.rs | 2 +- examples/stm32l5/src/bin/usb_serial.rs | 2 +- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/gpio_async.rs | 2 +- 19 files changed, 20 insertions(+), 13 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 63997c99f..885a4746d 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -33,6 +33,7 @@ unstable-traits = ["embedded-hal-1"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 3cf823573..03293e064 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -1,9 +1,9 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; -use futures::future::join; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin as _; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b4c19f32e..484496f24 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -34,6 +34,7 @@ flavors = [ embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index ff9157d51..20e1a4070 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -429,7 +429,7 @@ where } }); - let (_, result) = futures::future::join(dma_read, result).await; + let (_, result) = embassy_futures::join::join(dma_read, result).await; unsafe { Self::toggle(false) }; @@ -537,7 +537,7 @@ where unsafe { Self::toggle(true) }; - let (_, result) = futures::future::join(dma_result, result).await; + let (_, result) = embassy_futures::join::join(dma_result, result).await; unsafe { Self::toggle(false) }; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 02e6020b0..556d12305 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -3,9 +3,9 @@ use core::ptr; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; 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 futures::future::join; use self::sealed::WordSize; use crate::dma::{slice_ptr_parts, NoDma, Transfer}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 4761fcf66..70318b78f 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -7,6 +7,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; @@ -15,7 +16,6 @@ use embassy_sync::signal::Signal; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index f1b57a17f..65fbda1cf 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -6,13 +6,13 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index f9526cbc4..a740b4e0a 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -6,12 +6,12 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 17393322c..24c3cdd67 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,6 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 00cf3e93f..bf92a1636 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -4,12 +4,12 @@ use defmt::{info, panic}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_rp::interrupt; use embassy_rp::usb::{Driver, Instance}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 33ac63db1..895e043dd 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -10,6 +10,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a9c46068f..a14e728ba 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 208f39080..27f5c260a 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -11,6 +11,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index d3702fc35..b9fd20e2b 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index c451bd221..05945f6bf 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -15,6 +15,7 @@ embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", fea embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", 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" } usbd-hid = "0.6.0" defmt = "0.3" diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 0aca6f1cd..fa92ceae3 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; @@ -11,7 +12,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index b576a7353..7484dc832 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -4,13 +4,13 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 4d6877ccd..11ecb9169 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,6 +8,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 1eeaac1f6..f20b8fcbd 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -4,9 +4,9 @@ use defmt::{assert, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_time::{Duration, Instant, Timer}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 816778e3fadf31772413ac7bd78a3eeaead66cad Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 23 Sep 2022 06:38:47 +0200 Subject: [PATCH 0156/1575] Add RP2040 ROM functions and intrinsics aliases --- embassy-rp/Cargo.toml | 4 + embassy-rp/src/intrinsics.rs | 273 +++++++++++++ embassy-rp/src/lib.rs | 3 + embassy-rp/src/rom_data.rs | 730 +++++++++++++++++++++++++++++++++++ 4 files changed, 1010 insertions(+) create mode 100644 embassy-rp/src/intrinsics.rs create mode 100644 embassy-rp/src/rom_data.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 885a4746d..df0af8dfb 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -22,6 +22,10 @@ unstable-pac = [] time-driver = [] +rom-func-cache = [] +disable-intrinsics = [] +rom-v2-intrinsics = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs new file mode 100644 index 000000000..9e6624cf0 --- /dev/null +++ b/embassy-rp/src/intrinsics.rs @@ -0,0 +1,273 @@ +#![macro_use] + +/// Generate a series of aliases for an intrinsic function. +macro_rules! intrinsics_aliases { + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; + + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; +} + +/// The macro used to define overridden intrinsics. +/// +/// This is heavily inspired by the macro used by compiler-builtins. The idea +/// is to abstract anything special that needs to be done to override an +/// intrinsic function. Intrinsic generation is disabled for non-ARM targets +/// so things like CI and docs generation do not have problems. Additionally +/// they can be disabled with the crate feature `disable-intrinsics` for +/// testing or comparing performance. +/// +/// Like the compiler-builtins macro, it accepts a series of functions that +/// looks like normal Rust code: +/// +/// intrinsics! { +/// extern "C" fn foo(a: i32) -> u32 { +/// // ... +/// } +/// +/// #[nonstandard_attribute] +/// extern "C" fn bar(a: i32) -> u32 { +/// // ... +/// } +/// } +/// +/// Each function can also be decorated with nonstandard attributes to control +/// additional behaviour: +/// +/// * `slower_than_default` - indicates that the override is slower than the +/// default implementation. Currently this just disables the override +/// entirely. +/// * `bootrom_v2` - indicates that the override is only available +/// on a V2 bootrom or higher. Only enabled when the feature +/// `rom-v2-intrinsics` is set. +/// * `alias` - accepts a list of names to alias the intrinsic to. +/// * `aeabi` - accepts a list of ARM EABI names to alias to. +/// +macro_rules! intrinsics { + () => {}; + + ( + #[slower_than_default] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + #[bootrom_v2] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(feature = "rom-v2-intrinsics"))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(feature = "rom-v2-intrinsics")] + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[aeabi = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ce09064a..9ac98d226 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -4,9 +4,12 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod intrinsics; + pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rom_data; pub mod rtc; pub mod spi; #[cfg(feature = "time-driver")] diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs new file mode 100644 index 000000000..93a3632a5 --- /dev/null +++ b/embassy-rp/src/rom_data.rs @@ -0,0 +1,730 @@ +//! Functions and data from the RPI Bootrom. +//! +//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: +//! +//! > The Bootrom contains a number of public functions that provide useful +//! > RP2040 functionality that might be needed in the absence of any other code +//! > on the device, as well as highly optimized versions of certain key +//! > functionality that would otherwise have to take up space in most user +//! > binaries. + +/// A bootrom function table code. +pub type RomFnTableCode = [u8; 2]; + +/// This function searches for (table) +type RomTableLookupFn = unsafe extern "C" fn(*const u16, u32) -> T; + +/// The following addresses are described at `2.8.2. Bootrom Contents` +/// Pointer to the lookup table function supplied by the rom. +const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _; + +/// Pointer to helper functions lookup table. +const FUNC_TABLE: *const u16 = 0x0000_0014 as _; + +/// Pointer to the public data lookup table. +const DATA_TABLE: *const u16 = 0x0000_0016 as _; + +/// Address of the version number of the ROM. +const VERSION_NUMBER: *const u8 = 0x0000_0013 as _; + +/// Retrive rom content from a table using a code. +fn rom_table_lookup(table: *const u16, tag: RomFnTableCode) -> T { + unsafe { + let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR); + let rom_table_lookup: RomTableLookupFn = core::mem::transmute(rom_table_lookup_ptr); + rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32) + } +} + +/// To save space, the ROM likes to store memory pointers (which are 32-bit on +/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the +/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped +/// to address `0x0000_0000` and so 16-bits are always sufficient. +/// +/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer. +unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 { + let ptr: u16 = *rom_address; + ptr as *const u32 +} + +macro_rules! declare_rom_function { + ( + $(#[$outer:meta])* + fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; + + ( + $(#[$outer:meta])* + unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; +} + +macro_rules! rom_functions { + () => {}; + + ( + $(#[$outer:meta])* + $c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; + + ( + $(#[$outer:meta])* + $c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; +} + +rom_functions! { + /// Return a count of the number of 1 bits in value. + b"P3" fn popcount32(value: u32) -> u32; + + /// Return the bits of value in the reverse order. + b"R3" fn reverse32(value: u32) -> u32; + + /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32. + b"L3" fn clz32(value: u32) -> u32; + + /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32. + b"T3" fn ctz32(value: u32) -> u32; + + /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode: + /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED + /// for the USB Mass Storage Device: + /// * 0 No pins are used as per cold boot. + /// * Otherwise a single bit set indicating which GPIO pin should be set to output and + /// raised whenever there is mass storage activity from the host. + /// * disable_interface_mask may be used to control the exposed USB interfaces: + /// * 0 To enable both interfaces (as per cold boot). + /// * 1 To disable the USB Mass Storage Interface. + /// * 2 to Disable the USB PICOBOOT Interface. + b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> (); + + /// Sets n bytes start at ptr to the value c and returns ptr + b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8; + + /// Sets n bytes start at ptr to the value c and returns ptr. + /// + /// Note this is a slightly more efficient variant of _memset that may only + /// be used if ptr is word aligned. + // Note the datasheet does not match the actual ROM for the code here, see + // https://github.com/raspberrypi/pico-feedback/issues/217 + b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + /// + /// Note this is a slightly more efficient variant of _memcpy that may only be + /// used if dest and src are word aligned. + b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8; + + /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads. + b"IF" unsafe fn connect_internal_flash() -> (); + + /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence. + /// + /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be + /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This + /// function configures the SSI with a fixed SCK clock divisor of /6. + b"EX" unsafe fn flash_exit_xip() -> (); + + /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a + /// block erase command e.g. D8h block erase, and the size of the block erased by this + /// command — this function will use the larger block erase where possible, for much higher + /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of + /// 4096 bytes. + b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> (); + + /// Program data to a range of flash addresses starting at `addr` (and + /// offset from the start of flash) and `count` bytes in size. The value + /// `addr` must be aligned to a 256-byte boundary, and `count` must be a + /// multiple of 256. + b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> (); + + /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can + /// drive the flashchip select as normal. + b"FC" unsafe fn flash_flush_cache() -> (); + + /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits, + /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported. + /// The debugger calls this function after performing a flash erase/programming operation, so + /// that the freshly-programmed code and data is visible to the debug host, without having to + /// know exactly what kind of flash device is connected. + b"CX" unsafe fn flash_enter_cmd_xip() -> (); + + /// This is the method that is entered by core 1 on reset to wait to be launched by core 0. + /// There are few cases where you should call this method (resetting core 1 is much better). + /// This method does not return and should only ever be called on core 1. + b"WV" unsafe fn wait_for_vector() -> !; +} + +// Various C intrinsics in the ROM +intrinsics! { + #[alias = __popcountdi2] + extern "C" fn __popcountsi2(x: u32) -> u32 { + popcount32(x) + } + + #[alias = __clzdi2] + extern "C" fn __clzsi2(x: u32) -> u32 { + clz32(x) + } + + #[alias = __ctzdi2] + extern "C" fn __ctzsi2(x: u32) -> u32 { + ctz32(x) + } + + // __rbit is only unofficial, but it show up in the ARM documentation, + // so may as well hook it up. + #[alias = __rbitl] + extern "C" fn __rbit(x: u32) -> u32 { + reverse32(x) + } + + unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset(dest, c as u8, n as u32); + } + + #[alias = __aeabi_memset8] + unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset4(dest as *mut u32, c as u8, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () { + memset(dest, 0, n as u32); + } + + #[alias = __aeabi_memclr8] + unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () { + memset4(dest as *mut u32, 0, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy(dest, src, n as u32); + } + + #[alias = __aeabi_memcpy8] + unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy44(dest as *mut u32, src as *const u32, n as u32); + } +} + +unsafe fn convert_str(s: *const u8) -> &'static str { + let mut end = s; + while *end != 0 { + end = end.add(1); + } + let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize); + core::str::from_utf8_unchecked(s) +} + +/// The version number of the rom. +pub fn rom_version_number() -> u8 { + unsafe { *VERSION_NUMBER } +} + +/// The Raspberry Pi Trading Ltd copyright string. +pub fn copyright_string() -> &'static str { + let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR"); + unsafe { convert_str(s) } +} + +/// The 8 most significant hex digits of the Bootrom git revision. +pub fn git_revision() -> u32 { + let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR"); + unsafe { *s } +} + +/// The start address of the floating point library code and data. +/// +/// This and fplib_end along with the individual function pointers in +/// soft_float_table can be used to copy the floating point implementation into +/// RAM if desired. +pub fn fplib_start() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FS") +} + +/// See Table 180 in the RP2040 datasheet for the contents of this table. +pub fn soft_float_table() -> *const usize { + rom_table_lookup(DATA_TABLE, *b"SF") +} + +/// The end address of the floating point library code and data. +pub fn fplib_end() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FE") +} + +/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. +pub fn soft_double_table() -> *const usize { + if rom_version_number() < 2 { + panic!( + "Double precision operations require V2 bootrom (found: V{})", + rom_version_number() + ); + } + rom_table_lookup(DATA_TABLE, *b"SD") +} + +/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms) +pub mod float_funcs { + + macro_rules! make_functions { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_functions! { + /// Calculates `a + b` + 0x00 fadd(a: f32, b: f32) -> f32; + /// Calculates `a - b` + 0x04 fsub(a: f32, b: f32) -> f32; + /// Calculates `a * b` + 0x08 fmul(a: f32, b: f32) -> f32; + /// Calculates `a / b` + 0x0c fdiv(a: f32, b: f32) -> f32; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 fsqrt(v: f32) -> f32; + /// Converts an f32 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c float_to_int(v: f32) -> i32; + /// Converts an f32 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 float_to_fix(v: f32, n: i32) -> i32; + /// Converts an f32 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 float_to_uint(v: f32) -> u32; + /// Converts an f32 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 float_to_ufix(v: f32, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// f32 value, rounding to even on tie + 0x2c int_to_float(v: i32) -> f32; + /// Converts a signed fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_float(v: i32, n: i32) -> f32; + /// Converts an unsigned integer to the nearest + /// f32 value, rounding to even on tie + 0x34 uint_to_float(v: u32) -> f32; + /// Converts an unsigned fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x38 ufix_to_float(v: u32, n: i32) -> f32; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c fcos(angle: f32) -> f32; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 fsin(angle: f32) -> f32; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 ftan(angle: f32) -> f32; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c fexp(v: f32) -> f32; + /// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity + 0x50 fln(v: f32) -> f32; + } + + macro_rules! make_functions_v2 { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + if $crate::rom_data::rom_version_number() < 2 { + panic!( + "Floating point function requires V2 bootrom (found: V{})", + $crate::rom_data::rom_version_number() + ); + } + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + // These are only on BootROM v2 or higher + make_functions_v2! { + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 fcmp(a: f32, b: f32) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 fatan2(y: f32, x: f32) -> f32; + /// Converts a signed 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x5c int64_to_float(v: i64) -> f32; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_float(v: i64, n: i32) -> f32; + /// Converts an unsigned 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x64 uint64_to_float(v: u64) -> f32; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest f32 value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_float(v: u64, n: i32) -> f32; + /// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c float_to_int64(v: f32) -> i64; + /// Converts an f32 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 float_to_fix64(v: f32, n: i32) -> f32; + /// Converts an f32 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 float_to_uint64(v: f32) -> u64; + /// Converts an f32 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 float_to_ufix64(v: f32, n: i32) -> u64; + /// Converts an f32 to an f64. + 0x7c float_to_double(v: f32) -> f64; + } +} + +/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms) +pub mod double_funcs { + + macro_rules! make_double_funcs { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_double_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_double_funcs! { + /// Calculates `a + b` + 0x00 dadd(a: f64, b: f64) -> f64; + /// Calculates `a - b` + 0x04 dsub(a: f64, b: f64) -> f64; + /// Calculates `a * b` + 0x08 dmul(a: f64, b: f64) -> f64; + /// Calculates `a / b` + 0x0c ddiv(a: f64, b: f64) -> f64; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 dsqrt(v: f64) -> f64; + /// Converts an f64 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c double_to_int(v: f64) -> i32; + /// Converts an f64 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 double_to_fix(v: f64, n: i32) -> i32; + /// Converts an f64 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 double_to_uint(v: f64) -> u32; + /// Converts an f64 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 double_to_ufix(v: f64, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// double value, rounding to even on tie + 0x2c int_to_double(v: i32) -> f64; + /// Converts a signed fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_double(v: i32, n: i32) -> f64; + /// Converts an unsigned integer to the nearest + /// double value, rounding to even on tie + 0x34 uint_to_double(v: u32) -> f64; + /// Converts an unsigned fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so f = + /// nearest(v/(2^n)) + 0x38 ufix_to_double(v: u32, n: i32) -> f64; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c dcos(angle: f64) -> f64; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 dsin(angle: f64) -> f64; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 dtan(angle: f64) -> f64; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c dexp(v: f64) -> f64; + /// Calculates the natural logarithm of v. If v <= 0 return -Infinity + 0x50 dln(v: f64) -> f64; + + // These are only on BootROM v2 or higher + + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 dcmp(a: f64, b: f64) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 datan2(y: f64, x: f64) -> f64; + /// Converts a signed 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x5c int64_to_double(v: i64) -> f64; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_doubl(v: i64, n: i32) -> f64; + /// Converts an unsigned 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x64 uint64_to_double(v: u64) -> f64; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest double value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_double(v: u64, n: i32) -> f64; + /// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c double_to_int64(v: f64) -> i64; + /// Converts an f64 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 double_to_fix64(v: f64, n: i32) -> i64; + /// Converts an f64 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 double_to_uint64(v: f64) -> u64; + /// Converts an f64 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 double_to_ufix64(v: f64, n: i32) -> u64; + /// Converts an f64 to an f32 + 0x7c double_to_float(v: f64) -> f32; + } +} From 9d674f0212bf2a51fccd192632a16f104ba9629c Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 12:40:39 +0200 Subject: [PATCH 0157/1575] First iteration attempt on implementing generic flash mutation access for RP2040 --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/flash.rs | 100 ++++++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 1 + 3 files changed, 102 insertions(+) create mode 100644 embassy-rp/src/flash.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index df0af8dfb..ce78acd3c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -52,6 +52,7 @@ 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-storage = { version = "0.3" } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs new file mode 100644 index 000000000..3b6405090 --- /dev/null +++ b/embassy-rp/src/flash.rs @@ -0,0 +1,100 @@ +use embedded_storage::nor_flash::{ + ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, +}; + +/// Error type for NVMC operations. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Opration using a location not in flash. + OutOfBounds, + /// Unaligned operation or using unaligned buffers. + Unaligned, +} + +impl NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match self { + Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, + Self::Unaligned => NorFlashErrorKind::NotAligned, + } + } +} + +pub struct Flash; + +impl ErrorType for Flash { + type Error = Error; +} + +impl MultiwriteNorFlash for Flash {} + +impl ReadNorFlash for Flash { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + + let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl NorFlash for Flash { + const WRITE_SIZE: usize = 4; + + const ERASE_SIZE: usize = 4096; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if to < from || to as usize > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let len = to - from; + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_erase(from, len, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if offset as usize + bytes.len() > FLASH_SIZE { + return Err(Error::OutOfBounds); + } + if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { + return Err(Error::Unaligned); + } + + // Make sure to uphold the contract point with rp2040-flash. + // - interrupts must be disabled + // - DMA must not access flash memory + // FIXME: Pause all DMA channels for the duration of the flash_write? + + critical_section::with(|_| { + unsafe { rp2040_flash::flash::flash_range_program(offset, bytes, true) }; + }); + + // Re-enable DMA channels + + Ok(()) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ac98d226..ce013b09c 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -19,6 +19,7 @@ pub mod uart; pub mod usb; mod clocks; +pub mod flash; mod reset; // Reexports From 18dc0dea636daa6e48aa9208b5a74cdfedc8b513 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 23 Sep 2022 08:12:32 +0200 Subject: [PATCH 0158/1575] Drop rp2040-flash as dependency, as they pull in rp2040-hal for rom-data functions, which are now part of this HAL as well --- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/flash.rs | 335 +++++++++++++++++++++++++++++++++++----- 2 files changed, 295 insertions(+), 42 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 410c48666..fd281fd5d 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -191,7 +191,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { } } -const CHANNEL_COUNT: usize = 12; +pub(crate) const CHANNEL_COUNT: usize = 12; const NEW_AW: AtomicWaker = AtomicWaker::new(); static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 3b6405090..0fc19f5a2 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,7 +1,18 @@ use embedded_storage::nor_flash::{ - ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, + check_erase, check_read, check_write, ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +pub const FLASH_BASE: usize = 0x10000000; + +// **NOTE**: +// +// These limitations are currently enforced because of using the +// RP2040 boot-rom flash functions, that are optimized for flash compatibility +// rather than performance. +pub const WRITE_SIZE: usize = 256; +pub const READ_SIZE: usize = 1; +pub const ERASE_SIZE: usize = 4096; + /// Error type for NVMC operations. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -10,6 +21,17 @@ pub enum Error { OutOfBounds, /// Unaligned operation or using unaligned buffers. Unaligned, + Other, +} + +impl From for Error { + fn from(e: NorFlashErrorKind) -> Self { + match e { + NorFlashErrorKind::NotAligned => Self::Unaligned, + NorFlashErrorKind::OutOfBounds => Self::OutOfBounds, + _ => Self::Other, + } + } } impl NorFlashError for Error { @@ -17,27 +39,57 @@ impl NorFlashError for Error { match self { Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, Self::Unaligned => NorFlashErrorKind::NotAligned, + Self::Other => NorFlashErrorKind::Other, } } } pub struct Flash; +impl Flash { + /// Make sure to uphold the contract points with rp2040-flash. + /// - interrupts must be disabled + /// - DMA must not access flash memory + unsafe fn in_ram(&mut self, operation: impl FnOnce()) { + let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; + + // TODO: Make sure CORE1 is paused during the entire duration of the RAM function + + critical_section::with(|_| { + // Pause all DMA channels for the duration of the ram operation + for (number, status) in dma_status.iter_mut().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + *status = ch.ctrl_trig().read().en(); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(false)); + } + } + + // Run our flash operation in RAM + operation(); + + // Re-enable previously enabled DMA channels + for (number, status) in dma_status.iter().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(true)); + } + } + }); + } +} impl ErrorType for Flash { type Error = Error; } -impl MultiwriteNorFlash for Flash {} - impl ReadNorFlash for Flash { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - if offset as usize >= FLASH_SIZE || offset as usize + bytes.len() > FLASH_SIZE { - return Err(Error::OutOfBounds); - } + 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()) }; - let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } @@ -48,53 +100,254 @@ impl ReadNorFlash for Flash { } impl NorFlash for Flash { - const WRITE_SIZE: usize = 4; + const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = 4096; + const ERASE_SIZE: usize = ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - if to < from || to as usize > FLASH_SIZE { - return Err(Error::OutOfBounds); - } - if from as usize % Self::ERASE_SIZE != 0 || to as usize % Self::ERASE_SIZE != 0 { - return Err(Error::Unaligned); - } + check_erase(self, from, to)?; let len = to - from; - // Make sure to uphold the contract point with rp2040-flash. - // - interrupts must be disabled - // - DMA must not access flash memory - // FIXME: Pause all DMA channels for the duration of the flash_write? - - critical_section::with(|_| { - unsafe { rp2040_flash::flash::flash_range_erase(from, len, true) }; - }); - - // Re-enable DMA channels + unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) }; Ok(()) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - if offset as usize + bytes.len() > FLASH_SIZE { - return Err(Error::OutOfBounds); - } - if offset as usize % 4 != 0 || bytes.len() as usize % 4 != 0 { - return Err(Error::Unaligned); - } + check_write(self, offset, bytes.len())?; - // Make sure to uphold the contract point with rp2040-flash. - // - interrupts must be disabled - // - DMA must not access flash memory - // FIXME: Pause all DMA channels for the duration of the flash_write? + trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), offset); - critical_section::with(|_| { - unsafe { rp2040_flash::flash::flash_range_program(offset, bytes, true) }; - }); - - // Re-enable DMA channels + unsafe { self.in_ram(|| ram_helpers::flash_range_program(offset, bytes, true)) }; Ok(()) } } + +mod ram_helpers { + use core::marker::PhantomData; + + use crate::rom_data; + + #[repr(C)] + struct FlashFunctionPointers<'a> { + connect_internal_flash: unsafe extern "C" fn() -> (), + flash_exit_xip: unsafe extern "C" fn() -> (), + flash_range_erase: Option ()>, + flash_range_program: Option ()>, + flash_flush_cache: unsafe extern "C" fn() -> (), + flash_enter_cmd_xip: unsafe extern "C" fn() -> (), + phantom: PhantomData<&'a ()>, + } + + #[allow(unused)] + fn flash_function_pointers(erase: bool, write: bool) -> FlashFunctionPointers<'static> { + FlashFunctionPointers { + connect_internal_flash: rom_data::connect_internal_flash::ptr(), + flash_exit_xip: rom_data::flash_exit_xip::ptr(), + flash_range_erase: if erase { + Some(rom_data::flash_range_erase::ptr()) + } else { + None + }, + flash_range_program: if write { + Some(rom_data::flash_range_program::ptr()) + } else { + None + }, + flash_flush_cache: rom_data::flash_flush_cache::ptr(), + flash_enter_cmd_xip: rom_data::flash_enter_cmd_xip::ptr(), + phantom: PhantomData, + } + } + + #[allow(unused)] + /// # Safety + /// + /// `boot2` must contain a valid 2nd stage boot loader which can be called to re-initialize XIP mode + unsafe fn flash_function_pointers_with_boot2(erase: bool, write: bool, boot2: &[u32; 64]) -> FlashFunctionPointers { + let boot2_fn_ptr = (boot2 as *const u32 as *const u8).offset(1); + let boot2_fn: unsafe extern "C" fn() -> () = core::mem::transmute(boot2_fn_ptr); + FlashFunctionPointers { + connect_internal_flash: rom_data::connect_internal_flash::ptr(), + flash_exit_xip: rom_data::flash_exit_xip::ptr(), + flash_range_erase: if erase { + Some(rom_data::flash_range_erase::ptr()) + } else { + None + }, + flash_range_program: if write { + Some(rom_data::flash_range_program::ptr()) + } else { + None + }, + flash_flush_cache: rom_data::flash_flush_cache::ptr(), + flash_enter_cmd_xip: boot2_fn, + phantom: PhantomData, + } + } + + /// Erase a flash range starting at `addr` with length `len`. + /// + /// `addr` and `len` must be multiples of 4096 + /// + /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// is used to re-initialize the XIP engine after flashing. + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// `addr` and `len` parameters must be valid and are not checked. + pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(true, false, &boot2) + } else { + flash_function_pointers(true, false) + }; + write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers); + } + + /// Erase and rewrite a flash range starting at `addr` with data `data`. + /// + /// `addr` and `data.len()` must be multiples of 4096 + /// + /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// is used to re-initialize the XIP engine after flashing. + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// `addr` and `len` parameters must be valid and are not checked. + pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(true, true, &boot2) + } else { + flash_function_pointers(true, true) + }; + write_flash_inner( + addr, + data.len() as u32, + Some(data), + &ptrs as *const FlashFunctionPointers, + ); + } + + /// Write a flash range starting at `addr` with data `data`. + /// + /// `addr` and `data.len()` must be multiples of 256 + /// + /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// is used to re-initialize the XIP engine after flashing. + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// `addr` and `len` parameters must be valid and are not checked. + pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(false, true, &boot2) + } else { + flash_function_pointers(false, true) + }; + write_flash_inner( + addr, + data.len() as u32, + Some(data), + &ptrs as *const FlashFunctionPointers, + ); + } + + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// Length of data must be a multiple of 4096 + /// addr must be aligned to 4096 + #[inline(never)] + #[link_section = ".data.ram_func"] + unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { + /* + Should be equivalent to: + rom_data::connect_internal_flash(); + rom_data::flash_exit_xip(); + rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected + rom_data::flash_range_program(addr, data as *const _, len); // if selected + rom_data::flash_flush_cache(); + rom_data::flash_enter_cmd_xip(); + */ + core::arch::asm!( + "mov r8, r0", + "mov r9, r2", + "mov r10, r1", + "ldr r4, [{ptrs}, #0]", + "blx r4", // connect_internal_flash() + + "ldr r4, [{ptrs}, #4]", + "blx r4", // flash_exit_xip() + + "mov r0, r8", // r0 = addr + "mov r1, r10", // r1 = len + "movs r2, #1", + "lsls r2, r2, #31", // r2 = 1 << 31 + "movs r3, #0", // r3 = 0 + "ldr r4, [{ptrs}, #8]", + "cmp r4, #0", + "beq 1f", + "blx r4", // flash_range_erase(addr, len, 1 << 31, 0) + "1:", + + "mov r0, r8", // r0 = addr + "mov r1, r9", // r0 = data + "mov r2, r10", // r2 = len + "ldr r4, [{ptrs}, #12]", + "cmp r4, #0", + "beq 1f", + "blx r4", // flash_range_program(addr, data, len); + "1:", + + "ldr r4, [{ptrs}, #16]", + "blx r4", // flash_flush_cache(); + + "ldr r4, [{ptrs}, #20]", + "blx r4", // flash_enter_cmd_xip(); + ptrs = in(reg) ptrs, + // Registers r8-r15 are not allocated automatically, + // so assign them manually. We need to use them as + // otherwise there are not enough registers available. + in("r0") addr, + in("r2") data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()), + in("r1") len, + out("r3") _, + out("r4") _, + lateout("r8") _, + lateout("r9") _, + lateout("r10") _, + clobber_abi("C"), + ); + } +} From 8f21a5b11698e0c84e61e22c9c505d59d5e50f67 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 23 Sep 2022 08:27:18 +0200 Subject: [PATCH 0159/1575] Add comment about bus:txglom iovar This commit adds a comment to the setting of the iovar `bus:txglom`. The motivation for this is that I had not heard of 'glom/glomming' before and having a comment might help others that are not familar with the term. --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a6b26188d..7a09a539b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -291,6 +291,9 @@ impl<'a> Control<'a> { info!("Configuring misc stuff..."); + // Disable tx gloming which transfers multiple packets in one request. + // 'glom' is short for "conglomerate" which means "gather together into + // a compact mass". self.set_iovar_u32("bus:txglom", 0).await; self.set_iovar_u32("apsta", 1).await; From 3ba0b3ef3b62ed05afed5ddeec8afaedc87e190e Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 23 Sep 2022 09:04:59 +0200 Subject: [PATCH 0160/1575] Comment out extra Timer:after calls This commit comments out two Timer::after calls which look like they go together with previous instructions, but those instructions are currently commented out, so it looks like these calls are not currently needed. --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a6b26188d..013b23431 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -315,14 +315,14 @@ impl<'a> Control<'a> { self.set_iovar_u32("bus:txglom", 0).await; Timer::after(Duration::from_millis(100)).await; //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? - Timer::after(Duration::from_millis(100)).await; + //Timer::after(Duration::from_millis(100)).await; self.set_iovar_u32("ampdu_ba_wsize", 8).await; Timer::after(Duration::from_millis(100)).await; self.set_iovar_u32("ampdu_mpdu", 4).await; Timer::after(Duration::from_millis(100)).await; //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes - Timer::after(Duration::from_millis(100)).await; + //Timer::after(Duration::from_millis(100)).await; // evts let mut evts = EventMask { From 28bf4b7b6da0e0f6ce0878580363f135c7e7a879 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 23 Sep 2022 09:35:54 +0200 Subject: [PATCH 0161/1575] Add const for IOCTL ANTDIV --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a6b26188d..f543f75e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,6 +131,7 @@ const IRQ_F3_INTR: u16 = 0x8000; const IOCTL_CMD_UP: u32 = 2; const IOCTL_CMD_SET_SSID: u32 = 26; +const IOCTL_CMD_ANTDIV: u32 = 64; const IOCTL_CMD_SET_VAR: u32 = 263; const IOCTL_CMD_GET_VAR: u32 = 262; const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; @@ -310,7 +311,8 @@ impl<'a> Control<'a> { // set country takes some time, next ioctls fail if we don't wait. Timer::after(Duration::from_millis(100)).await; - self.ioctl_set_u32(64, 0, 0).await; // WLC_SET_ANTDIV + // Set antenna to chip antenna + self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; self.set_iovar_u32("bus:txglom", 0).await; Timer::after(Duration::from_millis(100)).await; From 281cbcb1e8f2df2af6584c8dcef2d45b1ef73f4b Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 23 Sep 2022 09:39:29 +0200 Subject: [PATCH 0162/1575] Update ioctl_set_u32 to pass through iface param This commit updates ioctl_set_u32 to pass through the `iface` parameter to self.iotcl. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a6b26188d..80e076629 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -476,7 +476,7 @@ impl<'a> Control<'a> { async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { let mut buf = val.to_le_bytes(); - self.ioctl(IoctlType::Set, cmd, 0, &mut buf).await; + self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; } async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { From b4f2c2a05ebe736de1faaf5541f1913ac3c4eff6 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Fri, 23 Sep 2022 12:34:02 +0200 Subject: [PATCH 0163/1575] Re-add timer.stop() --- embassy-nrf/src/buffered_uarte.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index eb0b1b0cd..47f32fac8 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -429,14 +429,15 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { let r = U::regs(); + self.timer.stop(); + r.inten.reset(); r.events_rxto.reset(); r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - r.events_txstopped.reset(); r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - while r.events_txstopped.read().bits() == 0 {} + while r.events_txstopped.read().bits() == 0 {} while r.events_rxto.read().bits() == 0 {} r.enable.write(|w| w.enable().disabled()); From 9aaefa6e7163c80b26546a8ce58740b437a5a03e Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 23 Sep 2022 15:01:09 +0200 Subject: [PATCH 0164/1575] Add constants for cmd_word arguments This commit adds constants intended to be used with the `cmd_word` function. The motivation for this to (hopefully) improve the readability of the code. --- src/lib.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a6b26188d..c72f29ed5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,13 @@ fn swap16(x: u32) -> u32 { x.rotate_left(16) } +// CYW_SPID command structure constants. +const WRITE: bool = true; +const READ: bool = false; +const INC_ADDR: bool = true; +#[allow(unused)] +const FIXED_ADDR: bool = false; + fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) } @@ -734,7 +741,7 @@ where if status & STATUS_F2_PKT_AVAILABLE != 0 { let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - let cmd = cmd_word(false, true, FUNC_WLAN, 0, len); + let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len); self.spi .transaction(|bus| { @@ -799,7 +806,7 @@ where trace!(" {:02x}", &buf8[..total_len.min(48)]); - let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); + let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _); self.spi .transaction(|bus| { let bus = unsafe { &mut *bus }; @@ -993,7 +1000,7 @@ where trace!(" {:02x}", &buf8[..total_len.min(48)]); - let cmd = cmd_word(true, true, FUNC_WLAN, 0, total_len as _); + let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _); self.spi .transaction(|bus| { @@ -1081,7 +1088,7 @@ where self.backplane_set_window(addr).await; - let cmd = cmd_word(false, true, FUNC_BACKPLANE, window_offs, len as u32); + let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); self.spi .transaction(|bus| { @@ -1126,7 +1133,7 @@ where self.backplane_set_window(addr).await; - let cmd = cmd_word(true, true, FUNC_BACKPLANE, window_offs, len as u32); + let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); self.spi .transaction(|bus| { @@ -1245,7 +1252,7 @@ where } async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { - let cmd = cmd_word(false, true, func, addr, len); + let cmd = cmd_word(READ, INC_ADDR, func, addr, len); let mut buf = [0; 1]; self.spi @@ -1268,7 +1275,7 @@ where } async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { - let cmd = cmd_word(true, true, func, addr, len); + let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); self.spi .transaction(|bus| { @@ -1283,7 +1290,7 @@ where } async fn read32_swapped(&mut self, addr: u32) -> u32 { - let cmd = cmd_word(false, true, FUNC_BUS, addr, 4); + let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4); let mut buf = [0; 1]; self.spi @@ -1302,7 +1309,7 @@ where } async fn write32_swapped(&mut self, addr: u32, val: u32) { - let cmd = cmd_word(true, true, FUNC_BUS, addr, 4); + let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); self.spi .transaction(|bus| { From a45fb2d7183738bdcc020a9b1916fdfab469ec0c Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 24 Sep 2022 09:42:06 -0700 Subject: [PATCH 0165/1575] usb: fix compile errors with the log feature --- embassy-usb-serial/src/lib.rs | 6 +++--- embassy-usb/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index 27b536a6b..15c2bb0a7 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -268,7 +268,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { } /// Number of stop bits for LineCoding -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum StopBits { /// 1 stop bit @@ -292,7 +292,7 @@ impl From for StopBits { } /// Parity for LineCoding -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParityType { None = 0, @@ -316,7 +316,7 @@ impl From for ParityType { /// /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can /// be ignored if you don't plan to interface with a physical UART. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct LineCoding { stop_bits: StopBits, diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index ca7dde627..6f58c953c 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -247,7 +247,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { async fn handle_control(&mut self, req: [u8; 8]) { let req = Request::parse(&req); - trace!("control request: {:02x}", req); + trace!("control request: {:?}", req); match req.direction { UsbDirection::In => self.handle_control_in(req).await, From ca92302d038e3a8f1446085040228bac7a1d00e6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Thu, 22 Sep 2022 09:05:40 +0300 Subject: [PATCH 0166/1575] Parameterize Signal with RawMutex --- embassy-sync/src/signal.rs | 69 ++++++++++++++---------- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index b4d99513a..7c38637c3 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,9 +1,12 @@ //! A synchronization primitive for passing the latest value to a task. -use core::cell::UnsafeCell; +use core::cell::Cell; use core::future::{poll_fn, Future}; use core::mem; use core::task::{Context, Poll, Waker}; +use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use crate::blocking_mutex::Mutex; + /// Single-slot signaling primitive. /// /// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except @@ -28,8 +31,11 @@ use core::task::{Context, Poll, Waker}; /// /// static SOME_SIGNAL: Signal = Signal::new(); /// ``` -pub struct Signal { - state: UnsafeCell>, +pub struct Signal +where + R: RawMutex, +{ + state: Mutex>>, } enum State { @@ -38,24 +44,27 @@ enum State { Signaled(T), } -unsafe impl Send for Signal {} -unsafe impl Sync for Signal {} - -impl Signal { +impl Signal +where + R: RawMutex, +{ /// Create a new `Signal`. pub const fn new() -> Self { Self { - state: UnsafeCell::new(State::None), + state: Mutex::new(Cell::new(State::None)), } } } -impl Signal { +impl Signal +where + R: RawMutex, +{ /// Mark this Signal as signaled. pub fn signal(&self, val: T) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) { + self.state.lock(|cell| { + let state = cell.replace(State::Signaled(val)); + if let State::Waiting(waker) = state { waker.wake(); } }) @@ -63,31 +72,27 @@ impl Signal { /// Remove the queued value in this `Signal`, if any. pub fn reset(&self) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - *state = State::None - }) + self.state.lock(|cell| cell.set(State::None)); } - /// Manually poll the Signal future. - pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); + fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { + self.state.lock(|cell| { + let state = cell.replace(State::None); match state { State::None => { - *state = State::Waiting(cx.waker().clone()); + cell.set(State::Waiting(cx.waker().clone())); + Poll::Pending + } + State::Waiting(w) if w.will_wake(cx.waker()) => { + cell.set(State::Waiting(w)); Poll::Pending } - State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, State::Waiting(w) => { - let w = mem::replace(w, cx.waker().clone()); + cell.set(State::Waiting(cx.waker().clone())); w.wake(); Poll::Pending } - State::Signaled(_) => match mem::replace(state, State::None) { - State::Signaled(res) => Poll::Ready(res), - _ => unreachable!(), - }, + State::Signaled(res) => Poll::Ready(res), } }) } @@ -99,6 +104,14 @@ impl Signal { /// non-blocking method to check whether this signal has been signaled. pub fn signaled(&self) -> bool { - critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_))) + self.state.lock(|cell| { + let state = cell.replace(State::None); + + let res = matches!(state, State::Signaled(_)); + + cell.set(state); + + res + }) } } diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 70318b78f..980bc1038 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) { // Build the builder. let mut usb = builder.build(); - let remote_wakeup = Signal::new(); + let remote_wakeup: Signal<_> = Signal::new(); // Run the USB device. let usb_fut = async { From 85366661489c09fa8dec1375e9b9beee522e5e9f Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Sep 2022 12:08:46 +0300 Subject: [PATCH 0167/1575] Remove default, reorder generic params --- embassy-sync/src/signal.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 7c38637c3..8cb832a2b 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,7 +1,6 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::Cell; use core::future::{poll_fn, Future}; -use core::mem; use core::task::{Context, Poll, Waker}; use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; From c5ce02b30e488aade19f9f859425aa127d085b92 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Sep 2022 12:08:46 +0300 Subject: [PATCH 0168/1575] Remove default, reorder generic params --- embassy-sync/src/signal.rs | 19 ++++++++++--------- examples/nrf/src/bin/usb_hid_keyboard.rs | 3 ++- examples/stm32h7/src/bin/signal.rs | 3 ++- examples/stm32wl/src/bin/subghz.rs | 3 ++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 8cb832a2b..c3c10a8af 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -3,7 +3,7 @@ use core::cell::Cell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll, Waker}; -use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; /// Single-slot signaling primitive. @@ -22,19 +22,20 @@ use crate::blocking_mutex::Mutex; /// /// ``` /// use embassy_sync::signal::Signal; +/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; /// /// enum SomeCommand { /// On, /// Off, /// } /// -/// static SOME_SIGNAL: Signal = Signal::new(); +/// static SOME_SIGNAL: Signal = Signal::new(); /// ``` -pub struct Signal +pub struct Signal where - R: RawMutex, + M: RawMutex, { - state: Mutex>>, + state: Mutex>>, } enum State { @@ -43,9 +44,9 @@ enum State { Signaled(T), } -impl Signal +impl Signal where - R: RawMutex, + M: RawMutex, { /// Create a new `Signal`. pub const fn new() -> Self { @@ -55,9 +56,9 @@ where } } -impl Signal +impl Signal where - R: RawMutex, + M: RawMutex, { /// Mark this Signal as signaled. pub fn signal(&self, val: T) { diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 980bc1038..4eb7d37c9 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -12,6 +12,7 @@ use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; @@ -77,7 +78,7 @@ async fn main(_spawner: Spawner) { // Build the builder. let mut usb = builder.build(); - let remote_wakeup: Signal<_> = Signal::new(); + let remote_wakeup: Signal = Signal::new(); // Run the USB device. let usb_fut = async { diff --git a/examples/stm32h7/src/bin/signal.rs b/examples/stm32h7/src/bin/signal.rs index cc3e4e3ca..6d7c168d5 100644 --- a/examples/stm32h7/src/bin/signal.rs +++ b/examples/stm32h7/src/bin/signal.rs @@ -4,11 +4,12 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; -static SIGNAL: Signal = Signal::new(); +static SIGNAL: Signal = Signal::new(); #[embassy_executor::task] async fn my_sending_task() { diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index 3c60a8de4..32c8b5515 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -12,6 +12,7 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::interrupt; use embassy_stm32::interrupt::{Interrupt, InterruptExt}; use embassy_stm32::subghz::*; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use {defmt_rtt as _, panic_probe as _}; @@ -64,7 +65,7 @@ async fn main(_spawner: Spawner) { let button = Input::new(p.PA0, Pull::Up); let mut pin = ExtiInput::new(button, p.EXTI0); - static IRQ_SIGNAL: Signal<()> = Signal::new(); + static IRQ_SIGNAL: Signal = Signal::new(); let radio_irq = interrupt::take!(SUBGHZ_RADIO); radio_irq.set_handler(|_| { IRQ_SIGNAL.signal(()); From b743d9f48ce7635b720119336c1a711269f304ed Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 05:32:45 +0200 Subject: [PATCH 0169/1575] Add HIL test for bufferedUart --- embassy-rp/src/uart/buffered.rs | 3 +-- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/uart_buffered.rs | 37 +++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/rp/src/bin/uart_buffered.rs diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6d395b6f4..9c4fbfeb2 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Poll, Waker}; 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 futures::future::poll_fn; use super::*; diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 4d6877ccd..70fd95557 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -19,6 +19,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } 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.0", features = ["async"] } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs new file mode 100644 index 000000000..a0a3df8da --- /dev/null +++ b/tests/rp/src/bin/uart_buffered.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::uart::{BufferedUart, Config, State, Uart}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + 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; 32]; + let rx_buf = &mut [0u8; 32]; + let mut state = State::new(); + let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); + + let data = [0xC0, 0xDE]; + uart.write(&data).await.unwrap(); + + let mut buf = [0; 2]; + uart.read(&mut buf).await.unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 7f16b1cd23f53a429bf074e76254bcf592c0b9cf Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 06:01:18 +0200 Subject: [PATCH 0170/1575] Add blocking API to FirmwareUpdater, and allow for a split prepare/write api --- embassy-boot/boot/src/lib.rs | 186 +++++++++++++++++++++++++++++++++-- 1 file changed, 179 insertions(+), 7 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 96878ace9..1c4d2d47f 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -660,12 +660,6 @@ impl FirmwareUpdater { ) -> Result<(), F::Error> { assert!(data.len() >= F::ERASE_SIZE); - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.dfu.from + offset, - data.len() - ); - flash .erase( (self.dfu.from + offset) as u32, @@ -679,7 +673,141 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - let mut write_offset = self.dfu.from + offset; + FirmwareWriter(self) + .write_firmware(offset, data, flash, block_size) + .await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub fn prepare_update(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self)) + } + + // + // Blocking API + // + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_updated_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, SWAP_MAGIC, flash) + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, BOOT_MAGIC, flash) + } + + fn set_magic_blocking( + &mut self, + aligned: &mut [u8], + magic: u8, + flash: &mut F, + ) -> Result<(), F::Error> { + flash.read(self.state.from as u32, aligned)?; + + if aligned.iter().any(|&b| b != magic) { + aligned.fill(0); + + flash.write(self.state.from as u32, aligned)?; + flash.erase(self.state.from as u32, self.state.to as u32)?; + + aligned.fill(magic); + flash.write(self.state.from as u32, aligned)?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + assert!(data.len() >= F::ERASE_SIZE); + + flash.erase( + (self.dfu.from + offset) as u32, + (self.dfu.from + offset + data.len()) as u32, + )?; + + trace!( + "Erased from {} to {}", + self.dfu.from + offset, + self.dfu.from + offset + data.len() + ); + + FirmwareWriter(self).write_firmware_blocking(offset, data, flash, block_size)?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware_blocking` allows for an optimized + /// API in exchange for added complexity. + pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self)) + } +} + +/// FirmwareWriter allows writing blocks to an already erased flash. +pub struct FirmwareWriter<'a>(&'a mut FirmwareUpdater); + +impl<'a> FirmwareWriter<'a> { + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.dfu.from + offset, + data.len() + ); + + let mut write_offset = self.0.dfu.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk).await?; @@ -702,6 +830,50 @@ impl FirmwareUpdater { Ok(()) } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.dfu.from + offset, + data.len() + ); + + let mut write_offset = self.0.dfu.from + offset; + for chunk in data.chunks(block_size) { + trace!("Wrote chunk at {}: {:?}", write_offset, chunk); + flash.write(write_offset as u32, chunk)?; + write_offset += chunk.len(); + } + /* + trace!("Wrote data, reading back for verification"); + + let mut buf: [u8; 4096] = [0; 4096]; + let mut data_offset = 0; + let mut read_offset = self.dfu.from + offset; + for chunk in buf.chunks_mut(block_size) { + flash.read(read_offset as u32, chunk).await?; + trace!("Read chunk at {}: {:?}", read_offset, chunk); + assert_eq!(&data[data_offset..data_offset + block_size], chunk); + read_offset += chunk.len(); + data_offset += chunk.len(); + } + */ + + Ok(()) + } } #[cfg(test)] From b2a327a85884f822d011964bcd44b463b301467f Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 06:53:40 +0200 Subject: [PATCH 0171/1575] Add get_state helpers to allow self-testing before calling mark_booted --- embassy-boot/boot/src/lib.rs | 56 +++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 1c4d2d47f..6f22d08ea 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -604,6 +604,21 @@ impl FirmwareUpdater { self.dfu.len() } + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + flash.read(self.state.from as u32, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -673,8 +688,8 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - FirmwareWriter(self) - .write_firmware(offset, data, flash, block_size) + FirmwareWriter(self.dfu) + .write_block(offset, data, flash, block_size) .await?; Ok(()) @@ -690,13 +705,28 @@ impl FirmwareUpdater { trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - Ok(FirmwareWriter(self)) + Ok(FirmwareWriter(self.dfu)) } // // Blocking API // + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + flash.read(self.state.from as u32, aligned)?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -764,7 +794,7 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - FirmwareWriter(self).write_firmware_blocking(offset, data, flash, block_size)?; + FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; Ok(()) } @@ -779,14 +809,14 @@ impl FirmwareUpdater { trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - Ok(FirmwareWriter(self)) + Ok(FirmwareWriter(self.dfu)) } } /// FirmwareWriter allows writing blocks to an already erased flash. -pub struct FirmwareWriter<'a>(&'a mut FirmwareUpdater); +pub struct FirmwareWriter(Partition); -impl<'a> FirmwareWriter<'a> { +impl FirmwareWriter { /// Write data to a flash page. /// /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. @@ -794,7 +824,7 @@ impl<'a> FirmwareWriter<'a> { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( + pub async fn write_block( &mut self, offset: usize, data: &[u8], @@ -803,11 +833,11 @@ impl<'a> FirmwareWriter<'a> { ) -> Result<(), F::Error> { trace!( "Writing firmware at offset 0x{:x} len {}", - self.0.dfu.from + offset, + self.0.from + offset, data.len() ); - let mut write_offset = self.0.dfu.from + offset; + let mut write_offset = self.0.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk).await?; @@ -838,7 +868,7 @@ impl<'a> FirmwareWriter<'a> { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( + pub fn write_block_blocking( &mut self, offset: usize, data: &[u8], @@ -847,11 +877,11 @@ impl<'a> FirmwareWriter<'a> { ) -> Result<(), F::Error> { trace!( "Writing firmware at offset 0x{:x} len {}", - self.0.dfu.from + offset, + self.0.from + offset, data.len() ); - let mut write_offset = self.0.dfu.from + offset; + let mut write_offset = self.0.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk)?; From 6fa74b0c022c41c9ac6dd0b937ef402846cbdfae Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 10:36:21 +0200 Subject: [PATCH 0172/1575] make prepare_update async --- embassy-boot/boot/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 6f22d08ea..8286601ec 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -700,8 +700,8 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub fn prepare_update(&mut self, flash: &mut F) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + pub async fn prepare_update(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); From fa7781c48d9e6687914e674aba63291225ee8d17 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 11:57:26 +0200 Subject: [PATCH 0173/1575] Add credits to rp-hal --- embassy-rp/src/intrinsics.rs | 3 +++ embassy-rp/src/rom_data.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 9e6624cf0..ac1f54800 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -1,5 +1,8 @@ #![macro_use] +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs + /// Generate a series of aliases for an intrinsic function. macro_rules! intrinsics_aliases { ( diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 93a3632a5..757a27114 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -8,6 +8,9 @@ //! > functionality that would otherwise have to take up space in most user //! > binaries. +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs + /// A bootrom function table code. pub type RomFnTableCode = [u8; 2]; From 7f7c14b7bce5b84eb27c8122535a96a6f0e5dd77 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 12:29:27 +0200 Subject: [PATCH 0174/1575] usb: split driver trait to separate crate. --- embassy-nrf/src/usb.rs | 17 +-- embassy-rp/src/usb.rs | 27 ++--- embassy-stm32/src/usb/usb.rs | 35 +++--- embassy-usb-driver/Cargo.toml | 16 +++ .../src/lib.rs | 107 +++++++++++++++++- embassy-usb/Cargo.toml | 4 + embassy-usb/src/builder.rs | 11 +- embassy-usb/src/control.rs | 5 +- embassy-usb/src/descriptor.rs | 7 +- embassy-usb/src/descriptor_reader.rs | 2 +- embassy-usb/src/lib.rs | 18 +-- embassy-usb/src/types.rs | 105 ----------------- 12 files changed, 189 insertions(+), 165 deletions(-) create mode 100644 embassy-usb-driver/Cargo.toml rename embassy-usb/src/driver.rs => embassy-usb-driver/src/lib.rs (72%) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 0685d419c..20510eb49 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -10,8 +10,9 @@ use cortex_m::peripheral::NVIC; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; pub use embassy_usb; -use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; @@ -243,7 +244,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> interval: u8, ) -> Result { let index = self.alloc_in.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); + let ep_addr = EndpointAddress::from_parts(index, Direction::In); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, ep_type, @@ -259,7 +260,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> interval: u8, ) -> Result { let index = self.alloc_out.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); + let ep_addr = EndpointAddress::from_parts(index, Direction::Out); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, ep_type, @@ -428,8 +429,8 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { let regs = T::regs(); let i = ep_addr.index(); match ep_addr.direction() { - UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(), - UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(), + Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(), + Direction::In => regs.halted.epin[i].read().getstatus().is_halted(), } } @@ -442,7 +443,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { let mut was_enabled = false; regs.epinen.modify(|r, w| { let mut bits = r.bits(); @@ -466,7 +467,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { In::waker(i).wake(); } - UsbDirection::Out => { + Direction::Out => { regs.epouten.modify(|r, w| { let mut bits = r.bits(); if enabled { diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index a7ec5fb79..ce473b21d 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -7,8 +7,9 @@ use core::task::Poll; use atomic_polyfill::compiler_fence; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; -use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{pac, peripherals, Peripheral, RegExt}; @@ -204,8 +205,8 @@ impl<'d, T: Instance> Driver<'d, T> { ); let alloc = match D::dir() { - UsbDirection::Out => &mut self.ep_out, - UsbDirection::In => &mut self.ep_in, + Direction::Out => &mut self.ep_out, + Direction::In => &mut self.ep_in, }; let index = alloc.iter_mut().enumerate().find(|(i, ep)| { @@ -254,7 +255,7 @@ impl<'d, T: Instance> Driver<'d, T> { }; match D::dir() { - UsbDirection::Out => unsafe { + Direction::Out => unsafe { T::dpram().ep_out_control(index - 1).write(|w| { w.set_enable(false); w.set_buffer_address(addr); @@ -262,7 +263,7 @@ impl<'d, T: Instance> Driver<'d, T> { w.set_endpoint_type(ep_type_reg); }) }, - UsbDirection::In => unsafe { + Direction::In => unsafe { T::dpram().ep_in_control(index - 1).write(|w| { w.set_enable(false); w.set_buffer_address(addr); @@ -429,14 +430,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let n = ep_addr.index(); match ep_addr.direction() { - UsbDirection::In => unsafe { + Direction::In => unsafe { T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| { w.set_pid(0, true); // first packet is DATA0, but PID is flipped before }); EP_IN_WAKERS[n].wake(); }, - UsbDirection::Out => unsafe { + Direction::Out => unsafe { T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| { @@ -474,14 +475,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } trait Dir { - fn dir() -> UsbDirection; + fn dir() -> Direction; fn waker(i: usize) -> &'static AtomicWaker; } pub enum In {} impl Dir for In { - fn dir() -> UsbDirection { - UsbDirection::In + fn dir() -> Direction { + Direction::In } #[inline] @@ -492,8 +493,8 @@ impl Dir for In { pub enum Out {} impl Dir for Out { - fn dir() -> UsbDirection { - UsbDirection::Out + fn dir() -> Direction { + Direction::Out } #[inline] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index e5ee1181c..39809a3e1 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -9,8 +9,9 @@ use atomic_polyfill::{AtomicBool, AtomicU8}; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; -use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; @@ -279,8 +280,8 @@ impl<'d, T: Instance> Driver<'d, T> { } let used = ep.used_out || ep.used_in; let used_dir = match D::dir() { - UsbDirection::Out => ep.used_out, - UsbDirection::In => ep.used_in, + Direction::Out => ep.used_out, + Direction::In => ep.used_in, }; !used || (ep.ep_type == ep_type && !used_dir) }); @@ -293,7 +294,7 @@ impl<'d, T: Instance> Driver<'d, T> { ep.ep_type = ep_type; let buf = match D::dir() { - UsbDirection::Out => { + Direction::Out => { assert!(!ep.used_out); ep.used_out = true; @@ -312,7 +313,7 @@ impl<'d, T: Instance> Driver<'d, T> { _phantom: PhantomData, } } - UsbDirection::In => { + Direction::In => { assert!(!ep.used_in); ep.used_in = true; @@ -504,7 +505,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { // This can race, so do a retry loop. let reg = T::regs().epr(ep_addr.index() as _); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { loop { let r = unsafe { reg.read() }; match r.stat_tx() { @@ -523,7 +524,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } EP_IN_WAKERS[ep_addr.index()].wake(); } - UsbDirection::Out => { + Direction::Out => { loop { let r = unsafe { reg.read() }; match r.stat_rx() { @@ -549,8 +550,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let regs = T::regs(); let epr = unsafe { regs.epr(ep_addr.index() as _).read() }; match ep_addr.direction() { - UsbDirection::In => epr.stat_tx() == Stat::STALL, - UsbDirection::Out => epr.stat_rx() == Stat::STALL, + Direction::In => epr.stat_tx() == Stat::STALL, + Direction::Out => epr.stat_rx() == Stat::STALL, } } @@ -560,7 +561,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let reg = T::regs().epr(ep_addr.index() as _); trace!("EPR before: {:04x}", unsafe { reg.read() }.0); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { loop { let want_stat = match enabled { false => Stat::DISABLED, @@ -576,7 +577,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } EP_IN_WAKERS[ep_addr.index()].wake(); } - UsbDirection::Out => { + Direction::Out => { loop { let want_stat = match enabled { false => Stat::DISABLED, @@ -616,14 +617,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } trait Dir { - fn dir() -> UsbDirection; + fn dir() -> Direction; fn waker(i: usize) -> &'static AtomicWaker; } pub enum In {} impl Dir for In { - fn dir() -> UsbDirection { - UsbDirection::In + fn dir() -> Direction { + Direction::In } #[inline] @@ -634,8 +635,8 @@ impl Dir for In { pub enum Out {} impl Dir for Out { - fn dir() -> UsbDirection { - UsbDirection::Out + fn dir() -> Direction { + Direction::Out } #[inline] diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml new file mode 100644 index 000000000..b525df337 --- /dev/null +++ b/embassy-usb-driver/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "embassy-usb-driver" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-driver-v$VERSION/embassy-usb/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-driver/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } \ No newline at end of file diff --git a/embassy-usb/src/driver.rs b/embassy-usb-driver/src/lib.rs similarity index 72% rename from embassy-usb/src/driver.rs rename to embassy-usb-driver/src/lib.rs index 7888f1639..051190a48 100644 --- a/embassy-usb/src/driver.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,111 @@ +#![no_std] + use core::future::Future; -use super::types::*; +/// 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 +/// for consistency. +/// +/// The values of the enum also match the direction bit used in endpoint addresses and control +/// request types. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Direction { + /// Host to device (OUT) + Out = 0x00, + /// Device to host (IN) + In = 0x80, +} + +impl From for Direction { + fn from(value: u8) -> Self { + unsafe { core::mem::transmute(value & 0x80) } + } +} + +/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the +/// transfer bmAttributes transfer type bits. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum EndpointType { + /// Control endpoint. Used for device management. Only the host can initiate requests. Usually + /// used only endpoint 0. + Control = 0b00, + /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. + Isochronous = 0b01, + /// Bulk endpoint. Used for large amounts of best-effort reliable data. + Bulk = 0b10, + /// Interrupt endpoint. Used for small amounts of time-critical reliable data. + Interrupt = 0b11, +} + +/// Type-safe endpoint address. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EndpointAddress(u8); + +impl From for EndpointAddress { + #[inline] + fn from(addr: u8) -> EndpointAddress { + EndpointAddress(addr) + } +} + +impl From for u8 { + #[inline] + fn from(addr: EndpointAddress) -> u8 { + addr.0 + } +} + +impl EndpointAddress { + const INBITS: u8 = Direction::In as u8; + + /// Constructs a new EndpointAddress with the given index and direction. + #[inline] + pub fn from_parts(index: usize, dir: Direction) -> Self { + EndpointAddress(index as u8 | dir as u8) + } + + /// Gets the direction part of the address. + #[inline] + pub fn direction(&self) -> Direction { + if (self.0 & Self::INBITS) != 0 { + Direction::In + } else { + Direction::Out + } + } + + /// Returns true if the direction is IN, otherwise false. + #[inline] + pub fn is_in(&self) -> bool { + (self.0 & Self::INBITS) != 0 + } + + /// Returns true if the direction is OUT, otherwise false. + #[inline] + pub fn is_out(&self) -> bool { + (self.0 & Self::INBITS) == 0 + } + + /// Gets the index part of the endpoint address. + #[inline] + pub fn index(&self) -> usize { + (self.0 & !Self::INBITS) as usize + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EndpointInfo { + pub addr: EndpointAddress, + pub ep_type: EndpointType, + pub max_packet_size: u16, + pub interval: u8, +} /// Driver for a specific USB peripheral. Implement this to add support for a new hardware /// platform. diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 8cad4d314..660ecc8cc 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -9,8 +9,12 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/s features = ["defmt"] target = "thumbv7em-none-eabi" +[features] +defmt = ["dep:defmt", "embassy-usb-driver/defmt"] + [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 6be88bc76..87a8333bb 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -1,11 +1,10 @@ use heapless::Vec; -use super::control::ControlHandler; -use super::descriptor::{BosWriter, DescriptorWriter}; -use super::driver::{Driver, Endpoint}; -use super::types::*; -use super::{DeviceStateHandler, UsbDevice, MAX_INTERFACE_COUNT}; -use crate::{Interface, STRING_INDEX_CUSTOM_START}; +use crate::control::ControlHandler; +use crate::descriptor::{BosWriter, DescriptorWriter}; +use crate::driver::{Driver, Endpoint, EndpointType}; +use crate::types::*; +use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 3e5749a01..9e0dee888 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -1,7 +1,8 @@ //! USB control data types. use core::mem; -use super::types::*; +use crate::driver::Direction; +use crate::types::StringIndex; /// Control request type. #[repr(u8)] @@ -42,7 +43,7 @@ pub enum Recipient { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Request { /// Direction of the request. - pub direction: UsbDirection, + pub direction: Direction, /// Type of the request. pub request_type: RequestType, /// Recipient of the request. diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index b94a4b161..497f03196 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -1,6 +1,7 @@ -use super::builder::Config; -use super::types::*; -use super::CONFIGURATION_VALUE; +use crate::builder::Config; +use crate::driver::EndpointInfo; +use crate::types::*; +use crate::CONFIGURATION_VALUE; /// Standard descriptor types #[allow(missing_docs)] diff --git a/embassy-usb/src/descriptor_reader.rs b/embassy-usb/src/descriptor_reader.rs index 0a12b566c..d64bcb73b 100644 --- a/embassy-usb/src/descriptor_reader.rs +++ b/embassy-usb/src/descriptor_reader.rs @@ -1,5 +1,5 @@ use crate::descriptor::descriptor_type; -use crate::types::EndpointAddress; +use crate::driver::EndpointAddress; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 6f58c953c..e1a99cfae 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -4,23 +4,23 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +pub use embassy_usb_driver as driver; + mod builder; pub mod control; pub mod descriptor; mod descriptor_reader; -pub mod driver; pub mod types; use embassy_futures::select::{select, Either}; use heapless::Vec; -pub use self::builder::{Builder, Config}; -use self::control::*; -use self::descriptor::*; -use self::driver::{Bus, Driver, Event}; -use self::types::*; +pub use crate::builder::{Builder, Config}; +use crate::control::*; +use crate::descriptor::*; use crate::descriptor_reader::foreach_endpoint; -use crate::driver::ControlPipe; +use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event}; +use crate::types::*; /// The global state of the USB device. /// @@ -250,8 +250,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { trace!("control request: {:?}", req); match req.direction { - UsbDirection::In => self.handle_control_in(req).await, - UsbDirection::Out => self.handle_control_out(req).await, + Direction::In => self.handle_control_in(req).await, + Direction::Out => self.handle_control_out(req).await, } if self.inner.set_address_pending { diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index b8717ffa9..aeab063d1 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -1,108 +1,3 @@ -/// 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 -/// for consistency. -/// -/// The values of the enum also match the direction bit used in endpoint addresses and control -/// request types. -#[repr(u8)] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum UsbDirection { - /// Host to device (OUT) - Out = 0x00, - /// Device to host (IN) - In = 0x80, -} - -impl From for UsbDirection { - fn from(value: u8) -> Self { - unsafe { core::mem::transmute(value & 0x80) } - } -} - -/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the -/// transfer bmAttributes transfer type bits. -#[repr(u8)] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum EndpointType { - /// Control endpoint. Used for device management. Only the host can initiate requests. Usually - /// used only endpoint 0. - Control = 0b00, - /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. - Isochronous = 0b01, - /// Bulk endpoint. Used for large amounts of best-effort reliable data. - Bulk = 0b10, - /// Interrupt endpoint. Used for small amounts of time-critical reliable data. - Interrupt = 0b11, -} - -/// Type-safe endpoint address. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EndpointAddress(u8); - -impl From for EndpointAddress { - #[inline] - fn from(addr: u8) -> EndpointAddress { - EndpointAddress(addr) - } -} - -impl From for u8 { - #[inline] - fn from(addr: EndpointAddress) -> u8 { - addr.0 - } -} - -impl EndpointAddress { - const INBITS: u8 = UsbDirection::In as u8; - - /// Constructs a new EndpointAddress with the given index and direction. - #[inline] - pub fn from_parts(index: usize, dir: UsbDirection) -> Self { - EndpointAddress(index as u8 | dir as u8) - } - - /// Gets the direction part of the address. - #[inline] - pub fn direction(&self) -> UsbDirection { - if (self.0 & Self::INBITS) != 0 { - UsbDirection::In - } else { - UsbDirection::Out - } - } - - /// Returns true if the direction is IN, otherwise false. - #[inline] - pub fn is_in(&self) -> bool { - (self.0 & Self::INBITS) != 0 - } - - /// Returns true if the direction is OUT, otherwise false. - #[inline] - pub fn is_out(&self) -> bool { - (self.0 & Self::INBITS) == 0 - } - - /// Gets the index part of the endpoint address. - #[inline] - pub fn index(&self) -> usize { - (self.0 & !Self::INBITS) as usize - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EndpointInfo { - pub addr: EndpointAddress, - pub ep_type: EndpointType, - pub max_packet_size: u16, - pub interval: u8, -} - /// A handle for a USB interface that contains its number. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From f4f58249722bc656a13865e06535d208440c3e4a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 12:35:33 +0200 Subject: [PATCH 0175/1575] usb: do not allow converting Directon to/from u8 --- embassy-usb-driver/src/lib.rs | 11 ++--------- embassy-usb/src/control.rs | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 051190a48..fc29786fc 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -8,20 +8,13 @@ use core::future::Future; /// /// The values of the enum also match the direction bit used in endpoint addresses and control /// request types. -#[repr(u8)] #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Direction { /// Host to device (OUT) - Out = 0x00, + Out, /// Device to host (IN) - In = 0x80, -} - -impl From for Direction { - fn from(value: u8) -> Self { - unsafe { core::mem::transmute(value & 0x80) } - } + In, } /// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 9e0dee888..d6d0c6565 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -106,7 +106,7 @@ impl Request { let recipient = rt & 0b11111; Request { - direction: rt.into(), + direction: if rt & 0x80 == 0 { Direction::Out } else { Direction::In }, request_type: unsafe { mem::transmute((rt >> 5) & 0b11) }, recipient: if recipient <= 3 { unsafe { mem::transmute(recipient) } From f27a47a37b59bf3b9079f4d4d5f43caf7b7872f8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 13:00:21 +0200 Subject: [PATCH 0176/1575] usb: move classes into the `embassy-usb` crate. --- embassy-usb-hid/Cargo.toml | 24 -- embassy-usb-hid/src/fmt.rs | 225 ------------------ embassy-usb-ncm/Cargo.toml | 17 -- embassy-usb-ncm/src/fmt.rs | 225 ------------------ embassy-usb-serial/Cargo.toml | 17 -- embassy-usb-serial/src/fmt.rs | 225 ------------------ embassy-usb/Cargo.toml | 9 +- .../src/class/cdc_acm.rs | 15 +- .../src/class/cdc_ncm.rs | 13 +- .../lib.rs => embassy-usb/src/class/hid.rs | 23 +- embassy-usb/src/class/mod.rs | 3 + embassy-usb/src/lib.rs | 1 + examples/nrf/Cargo.toml | 5 +- examples/nrf/src/bin/usb_ethernet.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 4 +- examples/nrf/src/bin/usb_hid_mouse.rs | 4 +- examples/nrf/src/bin/usb_serial.rs | 2 +- examples/nrf/src/bin/usb_serial_multitask.rs | 2 +- examples/rp/Cargo.toml | 2 - examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/Cargo.toml | 1 - examples/stm32f1/src/bin/usb_serial.rs | 2 +- examples/stm32f3/Cargo.toml | 2 - examples/stm32f3/src/bin/usb_serial.rs | 2 +- examples/stm32l5/Cargo.toml | 3 - examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 4 +- examples/stm32l5/src/bin/usb_serial.rs | 2 +- 29 files changed, 45 insertions(+), 795 deletions(-) delete mode 100644 embassy-usb-hid/Cargo.toml delete mode 100644 embassy-usb-hid/src/fmt.rs delete mode 100644 embassy-usb-ncm/Cargo.toml delete mode 100644 embassy-usb-ncm/src/fmt.rs delete mode 100644 embassy-usb-serial/Cargo.toml delete mode 100644 embassy-usb-serial/src/fmt.rs rename embassy-usb-serial/src/lib.rs => embassy-usb/src/class/cdc_acm.rs (96%) rename embassy-usb-ncm/src/lib.rs => embassy-usb/src/class/cdc_ncm.rs (97%) rename embassy-usb-hid/src/lib.rs => embassy-usb/src/class/hid.rs (96%) create mode 100644 embassy-usb/src/class/mod.rs diff --git a/embassy-usb-hid/Cargo.toml b/embassy-usb-hid/Cargo.toml deleted file mode 100644 index 2f7733dc6..000000000 --- a/embassy-usb-hid/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "embassy-usb-hid" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-hid-v$VERSION/embassy-usb-hid/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-hid/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[features] -default = ["usbd-hid"] -usbd-hid = ["dep:usbd-hid", "ssmarshal"] - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } -usbd-hid = { version = "0.6.0", optional = true } -ssmarshal = { version = "1.0", default-features = false, optional = true } -futures-util = { version = "0.3.21", default-features = false } diff --git a/embassy-usb-hid/src/fmt.rs b/embassy-usb-hid/src/fmt.rs deleted file mode 100644 index 066970813..000000000 --- a/embassy-usb-hid/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![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-usb-ncm/Cargo.toml b/embassy-usb-ncm/Cargo.toml deleted file mode 100644 index 15d3db96f..000000000 --- a/embassy-usb-ncm/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "embassy-usb-ncm" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-ncm-v$VERSION/embassy-usb-ncm/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-ncm/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } diff --git a/embassy-usb-ncm/src/fmt.rs b/embassy-usb-ncm/src/fmt.rs deleted file mode 100644 index 066970813..000000000 --- a/embassy-usb-ncm/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![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-usb-serial/Cargo.toml b/embassy-usb-serial/Cargo.toml deleted file mode 100644 index 9788588e9..000000000 --- a/embassy-usb-serial/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "embassy-usb-serial" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-serial-v$VERSION/embassy-usb-serial/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-serial/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } diff --git a/embassy-usb-serial/src/fmt.rs b/embassy-usb-serial/src/fmt.rs deleted file mode 100644 index 066970813..000000000 --- a/embassy-usb-serial/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![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-usb/Cargo.toml b/embassy-usb/Cargo.toml index 660ecc8cc..aad54dbaf 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -11,11 +11,18 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] +usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] +default = ["usbd-hid"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -heapless = "0.7.10" \ No newline at end of file +heapless = "0.7.10" + +# for HID +usbd-hid = { version = "0.6.0", optional = true } +ssmarshal = { version = "1.0", default-features = false, optional = true } diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb/src/class/cdc_acm.rs similarity index 96% rename from embassy-usb-serial/src/lib.rs rename to embassy-usb/src/class/cdc_acm.rs index 15c2bb0a7..09bb1cc8d 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -1,18 +1,13 @@ -#![no_std] -#![feature(type_alias_impl_trait)] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::cell::Cell; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; use embassy_sync::blocking_mutex::CriticalSectionMutex; -use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::types::*; -use embassy_usb::Builder; + +use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::types::*; +use crate::Builder; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/embassy-usb-ncm/src/lib.rs b/embassy-usb/src/class/cdc_ncm.rs similarity index 97% rename from embassy-usb-ncm/src/lib.rs rename to embassy-usb/src/class/cdc_ncm.rs index e796af28f..a39b87e9b 100644 --- a/embassy-usb-ncm/src/lib.rs +++ b/embassy-usb/src/class/cdc_ncm.rs @@ -1,15 +1,10 @@ -#![no_std] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; -use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::types::*; -use embassy_usb::Builder; +use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::types::*; +use crate::Builder; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb/src/class/hid.rs similarity index 96% rename from embassy-usb-hid/src/lib.rs rename to embassy-usb/src/class/hid.rs index 8b181aec8..4d1fa995f 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb/src/class/hid.rs @@ -1,23 +1,16 @@ -#![no_std] -#![feature(type_alias_impl_trait)] - -//! Implements HID functionality for a usb-device device. - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::mem::MaybeUninit; use core::ops::Range; use core::sync::atomic::{AtomicUsize, Ordering}; -use embassy_usb::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::Builder; #[cfg(feature = "usbd-hid")] use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; +use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::Builder; + const USB_CLASS_HID: u8 = 0x03; const USB_SUBCLASS_NONE: u8 = 0x00; const USB_PROTOCOL_NONE: u8 = 0x00; @@ -204,9 +197,9 @@ pub enum ReadError { Sync(Range), } -impl From for ReadError { - fn from(val: embassy_usb::driver::EndpointError) -> Self { - use embassy_usb::driver::EndpointError::*; +impl From for ReadError { + fn from(val: EndpointError) -> Self { + use EndpointError::*; match val { BufferOverflow => ReadError::BufferOverflow, Disabled => ReadError::Disabled, @@ -437,7 +430,7 @@ impl<'d> ControlHandler for Control<'d> { } } - fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse { + fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { trace!("HID control_out {:?} {=[u8]:x}", req, data); if let RequestType::Class = req.request_type { match req.request { diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs new file mode 100644 index 000000000..af27577a6 --- /dev/null +++ b/embassy-usb/src/class/mod.rs @@ -0,0 +1,3 @@ +pub mod cdc_acm; +pub mod cdc_ncm; +pub mod hid; diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index e1a99cfae..661b84119 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; pub use embassy_usb_driver as driver; mod builder; +pub mod class; pub mod control; pub mod descriptor; mod descriptor_reader; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index dbc659cda..a5d340c69 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -15,9 +15,6 @@ 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 } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true } embedded-io = "0.3.0" defmt = "0.3" diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 33ca380ff..de93a2b45 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -15,8 +15,8 @@ use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 4eb7d37c9..76e198719 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -14,9 +14,9 @@ use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; -use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -67,7 +67,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: KeyboardReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 65fbda1cf..4916a38d4 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -10,9 +10,9 @@ use embassy_futures::join::join; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; -use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -59,7 +59,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: MouseReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index a740b4e0a..7c9c4184b 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -9,9 +9,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index c646c0bbd..93efc2fe6 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -8,9 +8,9 @@ use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_serial::{CdcAcmClass, State}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 24c3cdd67..3c8f923e7 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,9 +10,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 166ffe175..1057fe7fd 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -13,8 +13,8 @@ use embassy_rp::usb::Driver; use embassy_rp::{interrupt, peripherals}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index bf92a1636..b7d6493b4 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -7,9 +7,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::interrupt; use embassy_rp::usb::{Driver, Instance}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 895e043dd..e6553789a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -9,7 +9,6 @@ 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", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a14e728ba..ad92cdeb2 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -10,9 +10,9 @@ use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 27f5c260a..f5b0b880c 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -9,8 +9,6 @@ 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", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index b9fd20e2b..f6d27c860 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -10,9 +10,9 @@ use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 05945f6bf..9ebab6476 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,9 +11,6 @@ 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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", 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" } usbd-hid = "0.6.0" diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index c96a83ead..4f36d3f5a 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -15,8 +15,8 @@ use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::StaticCell; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index fa92ceae3..d38ed7496 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -9,9 +9,9 @@ use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; -use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: MouseReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 7484dc832..7562a4e96 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -8,9 +8,9 @@ use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 9962db4ecf227792d777ff0bc91d9e4d50d24f85 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Fri, 23 Sep 2022 13:29:33 +0200 Subject: [PATCH 0177/1575] Suppress compiler warnings This commit adds the allow(unused) attribute to functions and constants that are not currently used. There is one warning remaining but https://github.com/embassy-rs/cyw43/pull/23 attempts to address that one. The constants have been moved into a module to allow the attribute to be applied to the module as a whole. The motivation for this is that it will hopefully make it easier to spot new warnings that might be introduced by new, or updated code. --- src/lib.rs | 198 ++++++++++++++++++++++++++----------------------- src/structs.rs | 3 + 2 files changed, 107 insertions(+), 94 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c7e0285f9..b8ce2e2a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,13 +32,6 @@ fn swap16(x: u32) -> u32 { x.rotate_left(16) } -// CYW_SPID command structure constants. -const WRITE: bool = true; -const READ: bool = false; -const INC_ADDR: bool = true; -#[allow(unused)] -const FIXED_ADDR: bool = false; - fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) } @@ -48,104 +41,114 @@ fn slice8_mut(x: &mut [u32]) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } } -const FUNC_BUS: u32 = 0; -const FUNC_BACKPLANE: u32 = 1; -const FUNC_WLAN: u32 = 2; -const FUNC_BT: u32 = 3; +mod constants { + #![allow(unused)] + pub(crate) const FUNC_BUS: u32 = 0; + pub(crate) const FUNC_BACKPLANE: u32 = 1; + pub(crate) const FUNC_WLAN: u32 = 2; + pub(crate) const FUNC_BT: u32 = 3; -const REG_BUS_CTRL: u32 = 0x0; -const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status -const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask -const REG_BUS_STATUS: u32 = 0x8; -const REG_BUS_TEST_RO: u32 = 0x14; -const REG_BUS_TEST_RW: u32 = 0x18; -const REG_BUS_RESP_DELAY: u32 = 0x1c; -const WORD_LENGTH_32: u32 = 0x1; -const HIGH_SPEED: u32 = 0x10; + pub(crate) const REG_BUS_CTRL: u32 = 0x0; + pub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status + pub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask + pub(crate) const REG_BUS_STATUS: u32 = 0x8; + pub(crate) const REG_BUS_TEST_RO: u32 = 0x14; + pub(crate) const REG_BUS_TEST_RW: u32 = 0x18; + pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c; + pub(crate) const WORD_LENGTH_32: u32 = 0x1; + pub(crate) const HIGH_SPEED: u32 = 0x10; -// SPI_STATUS_REGISTER bits -const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; -const STATUS_UNDERFLOW: u32 = 0x00000002; -const STATUS_OVERFLOW: u32 = 0x00000004; -const STATUS_F2_INTR: u32 = 0x00000008; -const STATUS_F3_INTR: u32 = 0x00000010; -const STATUS_F2_RX_READY: u32 = 0x00000020; -const STATUS_F3_RX_READY: u32 = 0x00000040; -const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080; -const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100; -const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00; -const STATUS_F2_PKT_LEN_SHIFT: u32 = 9; -const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000; -const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000; -const STATUS_F3_PKT_LEN_SHIFT: u32 = 21; + // SPI_STATUS_REGISTER bits + pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; + pub(crate) const STATUS_UNDERFLOW: u32 = 0x00000002; + pub(crate) const STATUS_OVERFLOW: u32 = 0x00000004; + pub(crate) const STATUS_F2_INTR: u32 = 0x00000008; + pub(crate) const STATUS_F3_INTR: u32 = 0x00000010; + pub(crate) const STATUS_F2_RX_READY: u32 = 0x00000020; + pub(crate) const STATUS_F3_RX_READY: u32 = 0x00000040; + pub(crate) const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080; + pub(crate) const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100; + pub(crate) const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00; + pub(crate) const STATUS_F2_PKT_LEN_SHIFT: u32 = 9; + pub(crate) const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000; + pub(crate) const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000; + pub(crate) const STATUS_F3_PKT_LEN_SHIFT: u32 = 21; -const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005; -const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006; -const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007; -const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008; -const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009; -const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A; -const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B; -const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C; -const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D; -const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E; -const REG_BACKPLANE_PULL_UP: u32 = 0x1000F; -const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B; -const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C; -const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E; -const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F; + pub(crate) const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005; + pub(crate) const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006; + pub(crate) const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007; + pub(crate) const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008; + pub(crate) const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009; + pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A; + pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B; + pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C; + pub(crate) const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D; + pub(crate) const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E; + pub(crate) const REG_BACKPLANE_PULL_UP: u32 = 0x1000F; + pub(crate) const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B; + pub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C; + pub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E; + pub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F; -const BACKPLANE_WINDOW_SIZE: usize = 0x8000; -const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; -const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; -const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; -// Active Low Power (ALP) clock constants -const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08; -const BACKPLANE_ALP_AVAIL: u8 = 0x40; + pub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000; + pub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; + pub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; + pub(crate) const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; + // Active Low Power (ALP) clock constants + pub(crate) const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08; + pub(crate) const BACKPLANE_ALP_AVAIL: u8 = 0x40; -// Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect (AI) -// constants -const AI_IOCTRL_OFFSET: u32 = 0x408; -const AI_IOCTRL_BIT_FGC: u8 = 0x0002; -const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; -const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020; + // Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect + // (AI) pub (crate) constants + pub(crate) const AI_IOCTRL_OFFSET: u32 = 0x408; + pub(crate) const AI_IOCTRL_BIT_FGC: u8 = 0x0002; + pub(crate) const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; + pub(crate) const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020; -const AI_RESETCTRL_OFFSET: u32 = 0x800; -const AI_RESETCTRL_BIT_RESET: u8 = 1; + pub(crate) const AI_RESETCTRL_OFFSET: u32 = 0x800; + pub(crate) const AI_RESETCTRL_BIT_RESET: u8 = 1; -const AI_RESETSTATUS_OFFSET: u32 = 0x804; + pub(crate) const AI_RESETSTATUS_OFFSET: u32 = 0x804; -const TEST_PATTERN: u32 = 0x12345678; -const FEEDBEAD: u32 = 0xFEEDBEAD; + pub(crate) const TEST_PATTERN: u32 = 0x12345678; + pub(crate) const FEEDBEAD: u32 = 0xFEEDBEAD; -// SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits -const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1" -const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002; -const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004; -const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1 -const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1 -const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020; -const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040; -const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests -const IRQ_MISC_INTR0: u16 = 0x0100; -const IRQ_MISC_INTR1: u16 = 0x0200; -const IRQ_MISC_INTR2: u16 = 0x0400; -const IRQ_MISC_INTR3: u16 = 0x0800; -const IRQ_MISC_INTR4: u16 = 0x1000; -const IRQ_F1_INTR: u16 = 0x2000; -const IRQ_F2_INTR: u16 = 0x4000; -const IRQ_F3_INTR: u16 = 0x8000; + // SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits + pub(crate) const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1" + pub(crate) const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002; + pub(crate) const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004; + pub(crate) const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1 + pub(crate) const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1 + pub(crate) const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020; + pub(crate) const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040; + pub(crate) const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests + pub(crate) const IRQ_MISC_INTR0: u16 = 0x0100; + pub(crate) const IRQ_MISC_INTR1: u16 = 0x0200; + pub(crate) const IRQ_MISC_INTR2: u16 = 0x0400; + pub(crate) const IRQ_MISC_INTR3: u16 = 0x0800; + pub(crate) const IRQ_MISC_INTR4: u16 = 0x1000; + pub(crate) const IRQ_F1_INTR: u16 = 0x2000; + pub(crate) const IRQ_F2_INTR: u16 = 0x4000; + pub(crate) const IRQ_F3_INTR: u16 = 0x8000; -const IOCTL_CMD_UP: u32 = 2; -const IOCTL_CMD_SET_SSID: u32 = 26; -const IOCTL_CMD_ANTDIV: u32 = 64; -const IOCTL_CMD_SET_VAR: u32 = 263; -const IOCTL_CMD_GET_VAR: u32 = 262; -const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; + pub(crate) const IOCTL_CMD_UP: u32 = 2; + pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; + pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64; + pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; + pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262; + pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; -const CHANNEL_TYPE_CONTROL: u8 = 0; -const CHANNEL_TYPE_EVENT: u8 = 1; -const CHANNEL_TYPE_DATA: u8 = 2; + pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0; + pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1; + pub(crate) const CHANNEL_TYPE_DATA: u8 = 2; + + // CYW_SPID command structure constants. + pub(crate) const WRITE: bool = true; + pub(crate) const READ: bool = false; + pub(crate) const INC_ADDR: bool = true; + pub(crate) const FIXED_ADDR: bool = false; +} +use crate::constants::*; #[derive(Clone, Copy)] pub enum IoctlType { @@ -153,6 +156,7 @@ pub enum IoctlType { Set = 2, } +#[allow(unused)] #[derive(Clone, Copy, PartialEq, Eq)] enum Core { WLAN = 0, @@ -170,6 +174,7 @@ impl Core { } } +#[allow(unused)] struct Chip { arm_core_base_address: u32, socsram_base_address: u32, @@ -1077,6 +1082,7 @@ where true } + #[allow(unused)] async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u32]) { // It seems the HW force-aligns the addr // to 2 if data.len() >= 2 @@ -1170,10 +1176,12 @@ where self.backplane_readn(addr, 2).await as u16 } + #[allow(unused)] async fn bp_write16(&mut self, addr: u32, val: u16) { self.backplane_writen(addr, val as u32, 2).await } + #[allow(unused)] async fn bp_read32(&mut self, addr: u32) -> u32 { self.backplane_readn(addr, 4).await } @@ -1244,6 +1252,7 @@ where self.readn(func, addr, 2).await as u16 } + #[allow(unused)] async fn write16(&mut self, func: u32, addr: u32, val: u16) { self.writen(func, addr, val as u32, 2).await } @@ -1252,6 +1261,7 @@ where self.readn(func, addr, 4).await } + #[allow(unused)] async fn write32(&mut self, func: u32, addr: u32, val: u32) { self.writen(func, addr, val, 4).await } diff --git a/src/structs.rs b/src/structs.rs index 355470971..ed5fc18df 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -5,10 +5,12 @@ macro_rules! impl_bytes { impl $t { pub const SIZE: usize = core::mem::size_of::(); + #[allow(unused)] pub fn to_bytes(&self) -> [u8; Self::SIZE] { unsafe { core::mem::transmute(*self) } } + #[allow(unused)] pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { unsafe { core::mem::transmute(*bytes) } } @@ -167,6 +169,7 @@ pub struct DownloadHeader { } impl_bytes!(DownloadHeader); +#[allow(unused)] pub const DOWNLOAD_FLAG_NO_CRC: u16 = 0x0001; pub const DOWNLOAD_FLAG_BEGIN: u16 = 0x0002; pub const DOWNLOAD_FLAG_END: u16 = 0x0004; From 3b04ef265c0f47b160ca8e89ef0b8fefc4b7e6ec Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Mon, 26 Sep 2022 14:53:37 +0200 Subject: [PATCH 0178/1575] Add constants for BDC_VERSION This commit adds two constants intended to be used with the bdc_header.flags field. I believe these are the correct values after looking at following lines in `whd_cdc_bdc.c`: https://github.com/Infineon/wifi-host-driver/blob/40a7ec2273a950fbf89353d3eac98c5c1c2fd8cd/WiFi_Host_Driver/src/whd_cdc_bdc.c#L34-L35 https://github.com/Infineon/wifi-host-driver/blob/40a7ec2273a950fbf89353d3eac98c5c1c2fd8cd/WiFi_Host_Driver/src/whd_cdc_bdc.c#L447 --- src/lib.rs | 2 +- src/structs.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b8ce2e2a2..d446313c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -800,7 +800,7 @@ where }; let bcd_header = BcdHeader { - flags: 0x20, + flags: BDC_VERSION << BDC_VERSION_SHIFT, priority: 0, flags2: 0, data_offset: 0, diff --git a/src/structs.rs b/src/structs.rs index ed5fc18df..6d4525a46 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -53,6 +53,9 @@ pub struct CdcHeader { } impl_bytes!(CdcHeader); +pub const BDC_VERSION: u8 = 2; +pub const BDC_VERSION_SHIFT: u8 = 4; + #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] From daf2744716db2ddb2aa97d85c2a91475920b2530 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Mon, 26 Sep 2022 15:28:09 +0200 Subject: [PATCH 0179/1575] Rework STM32 BufferedUart internals so we can split into Rx and Tx like embassy-nrf --- embassy-stm32/src/usart/buffered.rs | 260 +++++++++++++++++++--------- 1 file changed, 175 insertions(+), 85 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 46c49a997..2a711bc06 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,3 +1,4 @@ +use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::Poll; @@ -29,7 +30,15 @@ unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {} unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {} pub struct BufferedUart<'d, T: BasicInstance> { - inner: PeripheralMutex<'d, StateInner<'d, T>>, + inner: RefCell>>, +} + +pub struct BufferedUartTx<'u, 'd, T: BasicInstance> { + inner: &'u BufferedUart<'d, T>, +} + +pub struct BufferedUartRx<'u, 'd, T: BasicInstance> { + inner: &'u BufferedUart<'d, T>, } impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} @@ -53,14 +62,124 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { } Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { phantom: PhantomData, tx: RingBuffer::new(tx_buffer), tx_waker: WakerRegistration::new(), rx: RingBuffer::new(rx_buffer), rx_waker: WakerRegistration::new(), - }), + })), + } + } + + pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) { + (BufferedUartRx { inner: self }, BufferedUartTx { inner: self }) + } + + async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { + poll_fn(move |cx| { + let mut do_pend = false; + let mut inner = self.inner.borrow_mut(); + let res = inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + inner.pend(); + } + + res + }) + .await + } + + async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { + poll_fn(move |cx| { + let mut inner = self.inner.borrow_mut(); + let (poll, empty) = inner.with(|state| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + inner.pend(); + } + poll + }) + .await + } + + async fn inner_flush<'a>(&'a self) -> Result<(), Error> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { + if !state.tx.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + .await + } + + async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { + poll_fn(move |cx| { + self.inner.borrow_mut().with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.rx.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)); + } + + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + .await + } + + fn inner_consume(&self, amt: usize) { + let mut inner = self.inner.borrow_mut(); + let signal = inner.with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + inner.pend(); } } } @@ -155,41 +274,31 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } +impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> { + type Error = Error; +} + +impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { + type Error = Error; +} + impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { type ReadFuture<'a> = impl Future> where Self: 'a; fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); + self.inner_read(buf) + } +} - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); +impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; - if state.rx.is_full() { - do_pend = true; - } - state.rx.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx_waker.register(cx.waker()); - Poll::Pending - }); - - if do_pend { - self.inner.pend(); - } - - res - }) + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.inner.inner_read(buf) } } @@ -199,34 +308,25 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> Self: 'a; fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.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)); - } - - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) - }) + self.inner_fill_buf() } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - self.inner.pend(); - } + self.inner_consume(amt) + } +} + +impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + self.inner.inner_fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.inner.inner_consume(amt) } } @@ -236,26 +336,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { 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| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - (Poll::Ready(Ok(n)), empty) - }); - if empty { - self.inner.pend(); - } - poll - }) + self.inner_write(buf) } type FlushFuture<'a> = impl Future> @@ -263,15 +344,24 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + self.inner_flush() + } +} + +impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + self.inner.inner_write(buf) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + self.inner.inner_flush() } } From 1e95c4fcfff3edf87a6c4cdacb228cb08e6d4e50 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 19:52:55 +0200 Subject: [PATCH 0180/1575] rp: Disable intrinsics by default. --- ci.sh | 1 + embassy-rp/Cargo.toml | 2 +- embassy-rp/src/intrinsics.rs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ci.sh b/ci.sh index ae1b44281..69440ec36 100755 --- a/ci.sh +++ b/ci.sh @@ -58,6 +58,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ + --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \ diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index df0af8dfb..c43fd7e72 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -23,7 +23,7 @@ unstable-pac = [] time-driver = [] rom-func-cache = [] -disable-intrinsics = [] +intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index ac1f54800..3e75fb7fc 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -17,7 +17,7 @@ macro_rules! intrinsics_aliases { $alias:ident $($rest:ident)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { extern $abi fn $alias( $($argname: $ty),* ) -> $ret { $name($($argname),*) @@ -35,7 +35,7 @@ macro_rules! intrinsics_aliases { $alias:ident $($rest:ident)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { $name($($argname),*) @@ -55,7 +55,7 @@ macro_rules! intrinsics_aliases { /// is to abstract anything special that needs to be done to override an /// intrinsic function. Intrinsic generation is disabled for non-ARM targets /// so things like CI and docs generation do not have problems. Additionally -/// they can be disabled with the crate feature `disable-intrinsics` for +/// they can be disabled by disabling the crate feature `intrinsics` for /// testing or comparing performance. /// /// Like the compiler-builtins macro, it accepts a series of functions that @@ -214,13 +214,13 @@ macro_rules! intrinsics { $($rest:tt)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] $(#[$($attr)*])* extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { #[no_mangle] $(#[$($attr)*])* @@ -231,7 +231,7 @@ macro_rules! intrinsics { // Not exported, but defined so the actual implementation is // considered used - #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[cfg(not(all(target_arch = "arm", feature = "intrinsics")))] #[allow(dead_code)] fn $name( $($argname: $ty),* ) -> $ret { $($body)* @@ -248,13 +248,13 @@ macro_rules! intrinsics { $($rest:tt)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] $(#[$($attr)*])* unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { #[no_mangle] $(#[$($attr)*])* @@ -265,7 +265,7 @@ macro_rules! intrinsics { // Not exported, but defined so the actual implementation is // considered used - #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[cfg(not(all(target_arch = "arm", feature = "intrinsics")))] #[allow(dead_code)] unsafe fn $name( $($argname: $ty),* ) -> $ret { $($body)* From 75e93cc142c1c017e94dd28f6f04d3a4cb1ac67b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 20:33:20 +0200 Subject: [PATCH 0181/1575] rp: enable time-driver in Cargo.toml instead of ci.sh --- ci.sh | 2 +- tests/rp/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index 69440ec36..cd1c0786c 100755 --- a/ci.sh +++ b/ci.sh @@ -121,7 +121,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/time-driver --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ $BUILD_EXTRA diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 11ecb9169..7e2717ddf 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" 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"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" From ee76831f93e792757bf43136be712c343c4d5336 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:05:12 +0200 Subject: [PATCH 0182/1575] Add BufferedUart implementation, and feature-guard time-driver initialization, to free up TIMER peripheral if not used with embassy executor --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/uart/buffered.rs | 286 ++++++++++++++++++++++++ embassy-rp/src/{uart.rs => uart/mod.rs} | 84 ++++++- 3 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 embassy-rp/src/uart/buffered.rs rename embassy-rp/src/{uart.rs => uart/mod.rs} (87%) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index c43fd7e72..211b6a401 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -52,6 +52,7 @@ 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.0", features = ["async"], optional = true } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs new file mode 100644 index 000000000..c31af8018 --- /dev/null +++ b/embassy-rp/src/uart/buffered.rs @@ -0,0 +1,286 @@ +use core::future::Future; +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 futures::future::poll_fn; + +use super::*; + +pub struct State<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> State<'d, T> { + pub fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct StateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + rx_waker: WakerRegistration, + rx: RingBuffer<'d>, + + tx_waker: WakerRegistration, + tx: RingBuffer<'d>, +} + +unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} + +pub struct BufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, StateInner<'d, T>>, +} + +impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} + +impl<'d, T: Instance> BufferedUart<'d, T> { + pub fn new( + state: &'d mut State<'d, T>, + _uart: Uart<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + ) -> BufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + phantom: PhantomData, + tx: RingBuffer::new(tx_buffer), + tx_waker: WakerRegistration::new(), + + rx: RingBuffer::new(rx_buffer), + rx_waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> StateInner<'d, T> +where + Self: 'd, +{ + fn on_rx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rxic(true); + w.set_rtic(true); + }); + + if ris.rxris() { + if ris.peris() { + warn!("Parity error"); + } + if ris.feris() { + warn!("Framing error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + let buf = self.rx.push_buf(); + if !buf.is_empty() { + buf[0] = r.uartdr().read().data(); + self.rx.push(1); + } else { + warn!("RX buffer full, discard received byte"); + } + + if self.rx.is_full() { + self.rx_waker.wake(); + } + } + + if ris.rtris() { + self.rx_waker.wake(); + }; + } + } + + fn on_tx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rtic(true); + }); + + if ris.txris() { + let buf = self.tx.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.tx.pop(1); + self.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); + } + } + } + } +} + +impl<'d, T: Instance> PeripheralState for StateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.on_rx(); + self.on_tx(); + } +} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + 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); + + // We have data ready in buffer? Return it. + let buf = state.rx.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)); + } + + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + 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| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.tx.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart/mod.rs similarity index 87% rename from embassy-rp/src/uart.rs rename to embassy-rp/src/uart/mod.rs index 987b716b4..3b71d87be 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart/mod.rs @@ -475,6 +475,76 @@ mod eh1 { impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> { + fn read(&mut self) -> nb::Result { + let r = T::regs(); + unsafe { + let dr = r.uartdr().read(); + + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else if dr.fe() { + Ok(dr.data()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + } #[cfg(all( @@ -532,6 +602,12 @@ mod eha { } } +#[cfg(feature = "nightly")] +mod buffered; +#[cfg(feature = "nightly")] +pub use buffered::*; + + mod sealed { use super::*; @@ -541,6 +617,8 @@ mod sealed { const TX_DREQ: u8; const RX_DREQ: u8; + type Interrupt: crate::interrupt::Interrupt; + fn regs() -> pac::uart::Uart; } pub trait TxPin {} @@ -571,6 +649,8 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { pac::$inst @@ -580,8 +660,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0, 20, 21); -impl_instance!(UART1, UART1, 22, 23); +impl_instance!(UART0, UART0_IRQ, 20, 21); +impl_instance!(UART1, UART1_IRQ, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} From f2239d34cc26bb147d136d312b8b6af1020d4e0f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:36:27 +0200 Subject: [PATCH 0183/1575] Add bufferedUart, including a split version for only Rx or Tx --- embassy-rp/src/uart/buffered.rs | 379 ++++++++++++++++++++++++++------ embassy-rp/src/uart/mod.rs | 2 +- 2 files changed, 315 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index c31af8018..3eb96e3d5 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -9,31 +9,70 @@ use futures::future::poll_fn; use super::*; -pub struct State<'d, T: Instance>(StateStorage>); +pub struct State<'d, T: Instance>(StateStorage>); impl<'d, T: Instance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } -struct StateInner<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - - rx_waker: WakerRegistration, - rx: RingBuffer<'d>, - - tx_waker: WakerRegistration, - tx: RingBuffer<'d>, +pub struct RxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> RxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } } -unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} +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, StateInner<'d, T>>, + inner: PeripheralMutex<'d, FullStateInner<'d, T>>, +} + +pub struct RxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, RxStateInner<'d, T>>, +} + +pub struct TxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, TxStateInner<'d, T>>, } impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for RxBufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for TxBufferedUart<'d, T> {} impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( @@ -55,66 +94,158 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { - phantom: PhantomData, - tx: RingBuffer::new(tx_buffer), - tx_waker: WakerRegistration::new(), - - rx: RingBuffer::new(rx_buffer), - rx_waker: WakerRegistration::new(), + 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), + }, }), } } } -impl<'d, T: Instance> StateInner<'d, T> +impl<'d, T: Instance> RxBufferedUart<'d, T> { + pub fn new( + state: &'d mut RxState<'d, T>, + _uart: UartRx<'d, T, M>, + irq: impl Peripheral

+ 'd, + rx_buffer: &'d mut [u8], + ) -> RxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(rx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> TxBufferedUart<'d, T> { + pub fn new( + state: &'d mut TxState<'d, T>, + _uart: UartTx<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + ) -> TxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(tx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T> where Self: 'd, { - fn on_rx(&mut self) { + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.rx.on_interrupt(); + self.tx.on_interrupt(); + } +} + +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(); + let ris = r.uartmis().read(); // Clear interrupt flags - r.uarticr().write(|w| { + r.uarticr().modify(|w| { w.set_rxic(true); w.set_rtic(true); }); - if ris.rxris() { - if ris.peris() { + if ris.rxmis() { + if ris.pemis() { warn!("Parity error"); + r.uarticr().modify(|w| { + w.set_peic(true); + }); } - if ris.feris() { + if ris.femis() { warn!("Framing error"); + r.uarticr().modify(|w| { + w.set_feic(true); + }); } - if ris.beris() { + if ris.bemis() { warn!("Break error"); + r.uarticr().modify(|w| { + w.set_beic(true); + }); } - if ris.oeris() { + if ris.oemis() { warn!("Overrun error"); + r.uarticr().modify(|w| { + w.set_oeic(true); + }); } - let buf = self.rx.push_buf(); + let buf = self.buf.push_buf(); if !buf.is_empty() { buf[0] = r.uartdr().read().data(); - self.rx.push(1); + self.buf.push(1); } else { warn!("RX buffer full, discard received byte"); } - if self.rx.is_full() { - self.rx_waker.wake(); + if self.buf.is_full() { + self.waker.wake(); } } - if ris.rtris() { - self.rx_waker.wake(); + if ris.rtmis() { + self.waker.wake(); }; } } +} - fn on_tx(&mut self) { +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 ris = r.uartris().read(); @@ -124,14 +255,14 @@ where }); if ris.txris() { - let buf = self.tx.pop_buf(); + 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.tx.pop(1); - self.tx_waker.wake(); + self.buf.pop(1); + self.waker.wake(); } else { // Disable interrupt until we have something to transmit again r.uartimsc().modify(|w| { @@ -143,17 +274,6 @@ where } } -impl<'d, T: Instance> PeripheralState for StateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.on_rx(); - self.on_tx(); - } -} - impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other @@ -164,8 +284,16 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } +impl<'d, T: Instance> embedded_io::Io for RxBufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance> embedded_io::Io for TxBufferedUart<'d, T> { + type Error = Error; +} + impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> where Self: 'a; @@ -176,20 +304,58 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); + let data = state.rx.buf.pop_buf(); if !data.is_empty() { let len = data.len().min(buf.len()); buf[..len].copy_from_slice(&data[..len]); - if state.rx.is_full() { + if state.rx.buf.is_full() { do_pend = true; } - state.rx.pop(len); + state.rx.buf.pop(len); return Poll::Ready(Ok(len)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.buf.is_full() { + do_pend = true; + } + state.buf.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.waker.register(cx.waker()); Poll::Pending }); @@ -213,7 +379,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); + let buf = state.rx.buf.pop_buf(); if !buf.is_empty() { let buf: &[u8] = buf; // Safety: buffer lives as long as uart @@ -221,7 +387,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> return Poll::Ready(Ok(buf)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); Poll::>::Pending }) }) @@ -229,8 +395,45 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> fn consume(&mut self, amt: usize) { let signal = self.inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); + let full = state.rx.buf.is_full(); + state.rx.buf.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + 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); + + // We have data ready in buffer? Return it. + let buf = state.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)); + } + + state.waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.buf.is_full(); + state.buf.pop(amt); full }); if signal { @@ -247,16 +450,16 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); + let empty = state.tx.buf.is_empty(); + let tx_buf = state.tx.buf.push_buf(); if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); + state.tx.waker.register(cx.waker()); return (Poll::Pending, empty); } let n = core::cmp::min(tx_buf.len(), buf.len()); tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); + state.tx.buf.push(n); (Poll::Ready(Ok(n)), empty) }); @@ -274,8 +477,54 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { poll_fn(move |cx| { self.inner.with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); + if !state.tx.buf.is_empty() { + state.tx.waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + 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| { + let empty = state.buf.is_empty(); + let tx_buf = state.buf.push_buf(); + if tx_buf.is_empty() { + state.waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.buf.is_empty() { + state.waker.register(cx.waker()); return Poll::Pending; } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 3b71d87be..67e24b605 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -343,7 +343,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_stp2(config.stop_bits == StopBits::STOP2); w.set_pen(pen); w.set_eps(eps); - w.set_fen(true); + w.set_fen(false); }); r.uartcr().write(|w| { From d6af0f62860f79f790d6d1e042f36c8623328ab1 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:49:47 +0200 Subject: [PATCH 0184/1575] Formatting --- embassy-rp/src/uart/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 67e24b605..76ecdf7ac 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -544,7 +544,6 @@ mod eh1 { self.blocking_flush().map_err(nb::Error::Other) } } - } #[cfg(all( @@ -607,7 +606,6 @@ mod buffered; #[cfg(feature = "nightly")] pub use buffered::*; - mod sealed { use super::*; @@ -649,7 +647,7 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { From 1db9e464ff13a05d1267bb33fde490bcce35af5a Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:28:35 +0200 Subject: [PATCH 0185/1575] Enable embedded-io on nightly --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 211b6a401..d0cf8025c 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -27,7 +27,7 @@ intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. From b3dfd06dd6da3369813cf469a7fcd87c22047e87 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 21 Sep 2022 06:00:35 +0200 Subject: [PATCH 0186/1575] Remove code-duplication in async bufferedUart implementations --- embassy-rp/src/uart/buffered.rs | 215 +++++++++++++------------------- 1 file changed, 89 insertions(+), 126 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 3eb96e3d5..6d395b6f4 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,5 +1,5 @@ use core::future::Future; -use core::task::Poll; +use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; @@ -87,9 +87,9 @@ impl<'d, T: Instance> BufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); + w.set_txim(true); }); } @@ -122,7 +122,6 @@ impl<'d, T: Instance> RxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); }); @@ -151,9 +150,7 @@ impl<'d, T: Instance> TxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? - w.set_rxim(true); - w.set_rtim(true); + w.set_txim(true); }); } @@ -179,6 +176,51 @@ where } } +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) + } + + 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, @@ -240,6 +282,35 @@ where } } +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, @@ -299,26 +370,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.buf.is_full() { - do_pend = true; - } - state.rx.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx.waker.register(cx.waker()); - Poll::Pending + state.rx.read(buf, cx.waker()) }); if do_pend { @@ -337,26 +391,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.buf.is_full() { - do_pend = true; - } - state.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.waker.register(cx.waker()); - Poll::Pending + state.read(buf, cx.waker()) }); if do_pend { @@ -377,28 +414,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.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)); - } - - state.rx.waker.register(cx.waker()); - Poll::>::Pending + state.rx.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.rx.buf.is_full(); - state.rx.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.rx.consume(amt)); if signal { self.inner.pend(); } @@ -414,28 +436,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.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)); - } - - state.waker.register(cx.waker()); - Poll::>::Pending + state.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.buf.is_full(); - state.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.consume(amt)); if signal { self.inner.pend(); } @@ -449,20 +456,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.buf.is_empty(); - let tx_buf = state.tx.buf.push_buf(); - if tx_buf.is_empty() { - state.tx.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -475,16 +469,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.tx.buf.is_empty() { - state.tx.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))) } } @@ -495,20 +480,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.buf.is_empty(); - let tx_buf = state.buf.push_buf(); - if tx_buf.is_empty() { - state.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -521,15 +493,6 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.buf.is_empty() { - state.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))) } } From f76444bdc43f0a000b878dc52cb46ac06661a8b3 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 05:32:45 +0200 Subject: [PATCH 0187/1575] Add HIL test for bufferedUart --- embassy-rp/src/uart/buffered.rs | 3 +-- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/uart_buffered.rs | 37 +++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/rp/src/bin/uart_buffered.rs diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6d395b6f4..9c4fbfeb2 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Poll, Waker}; 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 futures::future::poll_fn; use super::*; diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 7e2717ddf..503373759 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -20,6 +20,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } 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.0", features = ["async"] } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs new file mode 100644 index 000000000..a0a3df8da --- /dev/null +++ b/tests/rp/src/bin/uart_buffered.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::uart::{BufferedUart, Config, State, Uart}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + 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; 32]; + let rx_buf = &mut [0u8; 32]; + let mut state = State::new(); + let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); + + let data = [0xC0, 0xDE]; + uart.write(&data).await.unwrap(); + + let mut buf = [0; 2]; + uart.read(&mut buf).await.unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From c863acd24f6430950a7fdb5c527b33b42c305fec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 20:36:06 +0200 Subject: [PATCH 0188/1575] rp: set correct teleprobe target for rpi-pico tests. --- tests/rp/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index 0330025e4..9611db3a0 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -3,7 +3,7 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" +#runner = "teleprobe client run --target rpi-pico --elf" runner = "teleprobe local run --chip RP2040 --elf" rustflags = [ From 5bf6564e95be4fa4240a08a4f524e9cf4bfee2bb Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 26 Sep 2022 20:33:24 +0200 Subject: [PATCH 0189/1575] Use firmware writer in stm32{f7, h7} example app The new FirmwareWriter is useful in particular for these architectures due to the large erase sector size. --- examples/boot/application/stm32f7/src/bin/a.rs | 17 +++++++++-------- examples/boot/application/stm32h7/src/bin/a.rs | 17 ++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index c08880fb3..77b897b0f 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -5,7 +5,6 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; -use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -17,8 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let mut flash = Flash::unlock(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -27,16 +25,19 @@ async fn main(_spawner: Spawner) { led.set_high(); let mut updater = FirmwareUpdater::default(); + let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; - let mut buf: [u8; 256 * 1024] = [0; 256 * 1024]; - for chunk in APP_B.chunks(256 * 1024) { - buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + let mut buf = AlignedBuffer([0; 4096]); + for chunk in APP_B.chunks(4096) { + buf.as_mut()[..chunk.len()].copy_from_slice(chunk); + writer + .write_block_blocking(offset, buf.as_ref(), &mut flash, chunk.len()) + .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index f5a8fdb61..0fe598a5d 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -5,7 +5,6 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; -use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -17,8 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let mut flash = Flash::unlock(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -27,19 +25,20 @@ async fn main(_spawner: Spawner) { led.set_high(); let mut updater = FirmwareUpdater::default(); + + let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; - let mut buf = AlignedBuffer([0; 128 * 1024]); - for chunk in APP_B.chunks(128 * 1024) { + let mut buf = AlignedBuffer([0; 4096]); + for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); - updater - .write_firmware(offset, buf.as_ref(), &mut flash, 2048) - .await + writer + .write_block_blocking(offset, buf.as_ref(), &mut flash, 4096) .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } From 65907204d64f104bfa5bf1ea9d1da8cb9cc09d19 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 05:51:31 +0200 Subject: [PATCH 0190/1575] Rename from {Rx,Tx}BufferedUart to BufferedUart{Rx,Tx} to be compliant with stm32 and nrf implementations --- embassy-rp/src/uart/buffered.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9c4fbfeb2..81ac61ee4 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -61,17 +61,17 @@ pub struct BufferedUart<'d, T: Instance> { inner: PeripheralMutex<'d, FullStateInner<'d, T>>, } -pub struct RxBufferedUart<'d, T: Instance> { +pub struct BufferedUartRx<'d, T: Instance> { inner: PeripheralMutex<'d, RxStateInner<'d, T>>, } -pub struct TxBufferedUart<'d, T: Instance> { +pub struct BufferedUartTx<'d, T: Instance> { inner: PeripheralMutex<'d, TxStateInner<'d, T>>, } impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} -impl<'d, T: Instance> Unpin for RxBufferedUart<'d, T> {} -impl<'d, T: Instance> Unpin for TxBufferedUart<'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( @@ -109,13 +109,13 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } } -impl<'d, T: Instance> RxBufferedUart<'d, T> { +impl<'d, T: Instance> BufferedUartRx<'d, T> { pub fn new( state: &'d mut RxState<'d, T>, _uart: UartRx<'d, T, M>, irq: impl Peripheral

+ 'd, rx_buffer: &'d mut [u8], - ) -> RxBufferedUart<'d, T> { + ) -> BufferedUartRx<'d, T> { into_ref!(irq); let r = T::regs(); @@ -137,13 +137,13 @@ impl<'d, T: Instance> RxBufferedUart<'d, T> { } } -impl<'d, T: Instance> TxBufferedUart<'d, T> { +impl<'d, T: Instance> BufferedUartTx<'d, T> { pub fn new( state: &'d mut TxState<'d, T>, _uart: UartTx<'d, T, M>, irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], - ) -> TxBufferedUart<'d, T> { + ) -> BufferedUartTx<'d, T> { into_ref!(irq); let r = T::regs(); @@ -354,11 +354,11 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for RxBufferedUart<'d, T> { +impl<'d, T: Instance> embedded_io::Io for BufferedUartRx<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for TxBufferedUart<'d, T> { +impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { type Error = Error; } @@ -383,7 +383,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { type ReadFuture<'a> = impl Future> where Self: 'a; @@ -426,7 +426,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { type FillBufFuture<'a> = impl Future> where Self: 'a; @@ -472,7 +472,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { type WriteFuture<'a> = impl Future> where Self: 'a; From 93354b812c89c1b0d56f93181eae8484c625fe89 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 05:51:38 +0200 Subject: [PATCH 0191/1575] Extend buffered-uart test to transmit 32 bytes --- tests/rp/src/bin/uart_buffered.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index a0a3df8da..4313ee3dd 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -25,10 +25,16 @@ async fn main(_spawner: Spawner) { let mut state = State::new(); let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); - let data = [0xC0, 0xDE]; + // 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, + ]; uart.write(&data).await.unwrap(); - let mut buf = [0; 2]; + let mut buf = [0; 32]; uart.read(&mut buf).await.unwrap(); assert_eq!(buf, data); From e129a97d48a00d7923886ab3faa82357b2369f13 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 07:45:10 +0200 Subject: [PATCH 0192/1575] Fix bufferedUart read and write tests --- embassy-rp/src/uart/buffered.rs | 88 ++++++++++++++----------------- embassy-rp/src/uart/mod.rs | 7 ++- tests/rp/src/bin/uart_buffered.rs | 9 ++-- 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 81ac61ee4..87e16f0eb 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -228,39 +228,39 @@ where fn on_interrupt(&mut self) { let r = T::regs(); unsafe { - let ris = r.uartmis().read(); + let ris = r.uartris().read(); // Clear interrupt flags r.uarticr().modify(|w| { w.set_rxic(true); w.set_rtic(true); }); - if ris.rxmis() { - if ris.pemis() { - warn!("Parity error"); - r.uarticr().modify(|w| { - w.set_peic(true); - }); - } - if ris.femis() { - warn!("Framing error"); - r.uarticr().modify(|w| { - w.set_feic(true); - }); - } - if ris.bemis() { - warn!("Break error"); - r.uarticr().modify(|w| { - w.set_beic(true); - }); - } - if ris.oemis() { - warn!("Overrun error"); - r.uarticr().modify(|w| { - w.set_oeic(true); - }); - } + 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(); @@ -274,7 +274,7 @@ where } } - if ris.rtmis() { + if ris.rtris() { self.waker.wake(); }; } @@ -318,27 +318,19 @@ where fn on_interrupt(&mut self) { let r = T::regs(); unsafe { - let ris = r.uartris().read(); - // Clear interrupt flags - r.uarticr().write(|w| { - w.set_rtic(true); - }); - - if ris.txris() { - 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(); - } else { - // Disable interrupt until we have something to transmit again - r.uartimsc().modify(|w| { - w.set_txim(false); - }); - } + 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(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); } } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 76ecdf7ac..d9285ee51 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -343,7 +343,12 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_stp2(config.stop_bits == StopBits::STOP2); w.set_pen(pen); w.set_eps(eps); - w.set_fen(false); + w.set_fen(true); + }); + + r.uartifls().write(|w| { + w.set_rxiflsel(0b000); + w.set_txiflsel(0b000); }); r.uartcr().write(|w| { diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 4313ee3dd..9cc20bb98 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -20,8 +20,8 @@ async fn main(_spawner: Spawner) { let uart = Uart::new_blocking(uart, tx, rx, config); let irq = interrupt::take!(UART0_IRQ); - let tx_buf = &mut [0u8; 32]; - let rx_buf = &mut [0u8; 32]; + 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); @@ -32,10 +32,11 @@ async fn main(_spawner: Spawner) { 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, ]; - uart.write(&data).await.unwrap(); + uart.write_all(&data).await.unwrap(); + info!("Done writing"); let mut buf = [0; 32]; - uart.read(&mut buf).await.unwrap(); + uart.read_exact(&mut buf).await.unwrap(); assert_eq!(buf, data); info!("Test OK"); From 17d8d11f734fb0d48d5f19fd17f91c9f9fa62e57 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Sep 2022 13:09:56 +0200 Subject: [PATCH 0193/1575] usb: make HALs depend only on embassy-usb-driver. --- embassy-nrf/Cargo.toml | 6 +++--- embassy-nrf/src/usb.rs | 6 ++---- embassy-rp/Cargo.toml | 6 +++--- embassy-rp/src/usb.rs | 5 +++-- embassy-stm32/Cargo.toml | 6 +++--- embassy-stm32/src/usb/usb.rs | 5 +++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 6fbbc8d9b..d80281fa3 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -18,10 +18,10 @@ flavors = [ time = ["dep:embassy-time"] -defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-usb", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] +nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. @@ -70,7 +70,7 @@ embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true } +embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 20510eb49..00da5c9dd 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -9,10 +9,8 @@ use core::task::Poll; use cortex_m::peripheral::NVIC; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -pub use embassy_usb; -use embassy_usb::driver::{ - self, Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, -}; +use embassy_usb_driver as driver; +use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index d0cf8025c..337a84f4a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -12,7 +12,7 @@ flavors = [ ] [features] -defmt = ["dep:defmt", "embassy-usb?/defmt"] +defmt = ["dep:defmt", "embassy-usb-driver?/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. @@ -27,7 +27,7 @@ intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb", "dep:embedded-io"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -41,7 +41,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index ce473b21d..0a904aab3 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -7,8 +7,9 @@ use core::task::Poll; use atomic_polyfill::compiler_fence; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; -use embassy_usb::driver::{ - self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +use embassy_usb_driver as driver; +use embassy_usb_driver::{ + Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 484496f24..a4a232f51 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -39,7 +39,7 @@ embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } -embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } +embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} @@ -73,7 +73,7 @@ quote = "1.0.15" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} [features] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] sdmmc-rs = ["embedded-sdmmc"] net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] @@ -92,7 +92,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 39809a3e1..2654f156a 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -9,8 +9,9 @@ use atomic_polyfill::{AtomicBool, AtomicU8}; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; -use embassy_usb::driver::{ - self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +use embassy_usb_driver as driver; +use embassy_usb_driver::{ + Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; From 820e6462b6a48a6a59f082bccd5d7a3035937b2f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 11:51:42 +0200 Subject: [PATCH 0194/1575] Add preliminary I2C implementation for RP2040 --- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/i2c.rs | 221 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 4 + embassy-rp/src/uart/mod.rs | 5 + 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/i2c.rs diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1c446f389..875c129c0 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -114,7 +114,7 @@ pub unsafe fn init() { reset::unreset_wait(peris); } -pub(crate) fn _clk_sys_freq() -> u32 { +pub(crate) fn clk_sys_freq() -> u32 { 125_000_000 } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs new file mode 100644 index 000000000..4a27ee8df --- /dev/null +++ b/embassy-rp/src/i2c.rs @@ -0,0 +1,221 @@ +use core::marker::PhantomData; + +use embassy_hal_common::into_ref; +use pac::i2c; + +use crate::{pac, peripherals, Peripheral}; + +/// I2C error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// I2C abort with error + Abort(u32), + /// User passed in a read buffer that was 0 length + InvalidReadBufferLength, + /// User passed in a write buffer that was 0 length + InvalidWriteBufferLength, + /// Target i2c address is out of range + AddressOutOfRange(u16), + /// Target i2c address is reserved + AddressReserved(u16), +} + +#[non_exhaustive] +#[derive(Copy, Clone)] +pub struct Config { + pub frequency: u32, + pub sda_pullup: bool, + pub scl_pullup: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + frequency: 100_000, + sda_pullup: false, + scl_pullup: false, + } + } +} + +pub struct I2c<'d, T: Instance, M: Mode> { + phantom: PhantomData<(&'d mut T, M)>, +} + +impl<'d, T: Instance> I2c<'d, T, Master> { + pub fn new_master( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(_peri, scl, sda); + + assert!(config.frequency <= 1_000_000); + assert!(config.frequency > 0); + + let p = T::regs(); + + unsafe { + p.ic_enable().write(|w| w.set_enable(false)); + + // select controller mode & speed + p.ic_con().write(|w| { + // Always use "fast" mode (<= 400 kHz, works fine for standard mode too) + w.set_speed(i2c::vals::Speed::FAST); + w.set_master_mode(true); + w.set_ic_slave_disable(true); + w.set_ic_restart_en(true); + w.set_tx_empty_ctrl(true); + }); + + // Clear FIFO threshold + p.ic_tx_tl().write(|w| w.set_tx_tl(0)); + p.ic_rx_tl().write(|w| w.set_rx_tl(0)); + + // Configure SCL & SDA pins + scl.io().ctrl().write(|w| w.set_funcsel(3)); + sda.io().ctrl().write(|w| w.set_funcsel(3)); + + scl.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_pue(config.scl_pullup); + }); + sda.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_pue(config.sda_pullup); + }); + + // Configure baudrate + + // There are some subtleties to I2C timing which we are completely ignoring here + // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 + let clk_base = crate::clocks::clk_sys_freq(); + + let period = (clk_base + config.frequency / 2) / config.frequency; + let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low + let hcnt = period - lcnt; // and 2/5 (40%) of the period high + + // Check for out-of-range divisors: + assert!(hcnt <= 0xffff); + assert!(lcnt <= 0xffff); + assert!(hcnt >= 8); + assert!(lcnt >= 8); + + // Per I2C-bus specification a device in standard or fast mode must + // internally provide a hold time of at least 300ns for the SDA signal to + // bridge the undefined region of the falling edge of SCL. A smaller hold + // time of 120ns is used for fast mode plus. + let sda_tx_hold_count = if config.frequency < 1_000_000 { + // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns) + // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. + // Add 1 to avoid division truncation. + ((clk_base * 3) / 10_000_000) + 1 + } else { + // fast mode plus requires a clk_base > 32MHz + assert!(clk_base >= 32_000_000); + + // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns) + // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. + // Add 1 to avoid division truncation. + ((clk_base * 3) / 25_000_000) + 1 + }; + assert!(sda_tx_hold_count <= lcnt - 2); + + p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); + p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); + p.ic_fs_spklen() + .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); + p.ic_sda_hold() + .write(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); + + // Enable I2C block + p.ic_enable().write(|w| w.set_enable(true)); + } + + Self { phantom: PhantomData } + } +} + +mod sealed { + pub trait Instance {} + pub trait Mode {} + + pub trait SdaPin {} + pub trait SclPin {} +} + +pub trait Mode: sealed::Mode {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Master; +pub struct Slave; + +impl_mode!(Master); +impl_mode!(Slave); + +pub trait Instance: sealed::Instance { + fn regs() -> pac::i2c::I2c; +} + +macro_rules! impl_instance { + ($type:ident, $irq:ident) => { + impl sealed::Instance for peripherals::$type {} + impl Instance for peripherals::$type { + fn regs() -> pac::i2c::I2c { + pac::$type + } + } + }; +} + +impl_instance!(I2C0, I2c0); +impl_instance!(I2C1, I2c1); + +pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} +pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} + +macro_rules! impl_pin { + ($pin:ident, $instance:ident, $function:ident) => { + impl sealed::$function for peripherals::$pin {} + impl $function for peripherals::$pin {} + }; +} + +impl_pin!(PIN_0, I2C0, SdaPin); +impl_pin!(PIN_1, I2C0, SclPin); +impl_pin!(PIN_2, I2C1, SdaPin); +impl_pin!(PIN_3, I2C1, SclPin); +impl_pin!(PIN_4, I2C0, SdaPin); +impl_pin!(PIN_5, I2C0, SclPin); +impl_pin!(PIN_6, I2C1, SdaPin); +impl_pin!(PIN_7, I2C1, SclPin); +impl_pin!(PIN_8, I2C0, SdaPin); +impl_pin!(PIN_9, I2C0, SclPin); +impl_pin!(PIN_10, I2C1, SdaPin); +impl_pin!(PIN_11, I2C1, SclPin); +impl_pin!(PIN_12, I2C0, SdaPin); +impl_pin!(PIN_13, I2C0, SclPin); +impl_pin!(PIN_14, I2C1, SdaPin); +impl_pin!(PIN_15, I2C1, SclPin); +impl_pin!(PIN_16, I2C0, SdaPin); +impl_pin!(PIN_17, I2C0, SclPin); +impl_pin!(PIN_18, I2C1, SdaPin); +impl_pin!(PIN_19, I2C1, SclPin); +impl_pin!(PIN_20, I2C0, SdaPin); +impl_pin!(PIN_21, I2C0, SclPin); +impl_pin!(PIN_22, I2C1, SdaPin); +impl_pin!(PIN_23, I2C1, SclPin); +impl_pin!(PIN_24, I2C0, SdaPin); +impl_pin!(PIN_25, I2C0, SclPin); +impl_pin!(PIN_26, I2C1, SdaPin); +impl_pin!(PIN_27, I2C1, SclPin); +impl_pin!(PIN_28, I2C0, SdaPin); +impl_pin!(PIN_29, I2C0, SclPin); diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ac98d226..e784399d4 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ mod intrinsics; pub mod dma; pub mod gpio; +pub mod i2c; pub mod interrupt; pub mod rom_data; pub mod rtc; @@ -75,6 +76,9 @@ embassy_hal_common::peripherals! { SPI0, SPI1, + I2C0, + I2C1, + DMA_CH0, DMA_CH1, DMA_CH2, diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index d9285ee51..567c79db3 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -428,9 +428,11 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } + fn bflush(&mut self) -> Result<(), Self::Error> { self.blocking_flush() } @@ -438,6 +440,7 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { type Error = Error; + fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } @@ -445,9 +448,11 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { type Error = Error; + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } + fn bflush(&mut self) -> Result<(), Self::Error> { self.blocking_flush() } From bcd3ab4ba1541a098a74b5abffdb30a293c97f64 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 19 Aug 2022 14:15:43 +0200 Subject: [PATCH 0195/1575] Add blocking read & write for I2C --- embassy-rp/src/i2c.rs | 306 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 286 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 4a27ee8df..f7e6a6f6b 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -39,12 +39,15 @@ impl Default for Config { } } +const TX_FIFO_SIZE: u8 = 16; +const RX_FIFO_SIZE: u8 = 16; + pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> I2c<'d, T, Master> { - pub fn new_master( +impl<'d, T: Instance> I2c<'d, T, Blocking> { + pub fn new_blocking( _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, @@ -60,9 +63,10 @@ impl<'d, T: Instance> I2c<'d, T, Master> { unsafe { p.ic_enable().write(|w| w.set_enable(false)); - // select controller mode & speed + // Select controller mode & speed p.ic_con().write(|w| { - // Always use "fast" mode (<= 400 kHz, works fine for standard mode too) + // Always use "fast" mode (<= 400 kHz, works fine for standard + // mode too) w.set_speed(i2c::vals::Speed::FAST); w.set_master_mode(true); w.set_ic_slave_disable(true); @@ -70,7 +74,8 @@ impl<'d, T: Instance> I2c<'d, T, Master> { w.set_tx_empty_ctrl(true); }); - // Clear FIFO threshold + // Set FIFO watermarks to 1 to make things simpler. This is encoded + // by a register value of 0. p.ic_tx_tl().write(|w| w.set_tx_tl(0)); p.ic_rx_tl().write(|w| w.set_rx_tl(0)); @@ -89,8 +94,9 @@ impl<'d, T: Instance> I2c<'d, T, Master> { // Configure baudrate - // There are some subtleties to I2C timing which we are completely ignoring here - // See: https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 + // There are some subtleties to I2C timing which we are completely + // ignoring here See: + // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 let clk_base = crate::clocks::clk_sys_freq(); let period = (clk_base + config.frequency / 2) / config.frequency; @@ -104,21 +110,21 @@ impl<'d, T: Instance> I2c<'d, T, Master> { assert!(lcnt >= 8); // Per I2C-bus specification a device in standard or fast mode must - // internally provide a hold time of at least 300ns for the SDA signal to - // bridge the undefined region of the falling edge of SCL. A smaller hold - // time of 120ns is used for fast mode plus. + // internally provide a hold time of at least 300ns for the SDA + // signal to bridge the undefined region of the falling edge of SCL. + // A smaller hold time of 120ns is used for fast mode plus. let sda_tx_hold_count = if config.frequency < 1_000_000 { - // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / 1e9ns) - // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. - // Add 1 to avoid division truncation. + // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / + // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. ((clk_base * 3) / 10_000_000) + 1 } else { // fast mode plus requires a clk_base > 32MHz assert!(clk_base >= 32_000_000); - // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / 1e9ns) - // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. - // Add 1 to avoid division truncation. + // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / + // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. ((clk_base * 3) / 25_000_000) + 1 }; assert!(sda_tx_hold_count <= lcnt - 2); @@ -138,6 +144,266 @@ impl<'d, T: Instance> I2c<'d, T, Master> { } } +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + /// Number of bytes currently in the RX FIFO + #[inline] + pub fn rx_fifo_used(&self) -> u8 { + unsafe { T::regs().ic_rxflr().read().rxflr() } + } + + /// Remaining capacity in the RX FIFO + #[inline] + pub fn rx_fifo_free(&self) -> u8 { + RX_FIFO_SIZE - self.rx_fifo_used() + } + + /// RX FIFO is empty + #[inline] + pub fn rx_fifo_empty(&self) -> bool { + self.rx_fifo_used() == 0 + } + + /// Number of bytes currently in the TX FIFO + #[inline] + pub fn tx_fifo_used(&self) -> u8 { + unsafe { T::regs().ic_txflr().read().txflr() } + } + + /// Remaining capacity in the TX FIFO + #[inline] + pub fn tx_fifo_free(&self) -> u8 { + TX_FIFO_SIZE - self.tx_fifo_used() + } + + /// TX FIFO is at capacity + #[inline] + pub fn tx_fifo_full(&self) -> bool { + self.tx_fifo_free() == 0 + } + + fn setup(addr: u16) -> Result<(), Error> { + if addr >= 0x80 { + return Err(Error::AddressOutOfRange(addr)); + } + + if i2c_reserved_addr(addr) { + return Err(Error::AddressReserved(addr)); + } + + let p = T::regs(); + unsafe { + p.ic_enable().write(|w| w.set_enable(false)); + p.ic_tar().write(|w| w.set_ic_tar(addr)); + p.ic_enable().write(|w| w.set_enable(true)); + } + Ok(()) + } + + fn read_and_clear_abort_reason(&mut self) -> Option { + let p = T::regs(); + unsafe { + let abort_reason = p.ic_tx_abrt_source().read().0; + if abort_reason != 0 { + // Note clearing the abort flag also clears the reason, and this + // instance of flag is clear-on-read! Note also the + // IC_CLR_TX_ABRT register always reads as 0. + p.ic_clr_tx_abrt().read(); + Some(abort_reason) + } else { + None + } + } + } + + fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = buffer.len() - 1; + for (i, byte) in buffer.iter_mut().enumerate() { + let first = i == 0; + let last = i == lastindex; + + // NOTE(unsafe) We have &mut self + unsafe { + // wait until there is space in the FIFO to write the next byte + while self.tx_fifo_full() {} + + p.ic_data_cmd().write(|w| { + if restart && first { + w.set_restart(true); + } else { + w.set_restart(false); + } + + if send_stop && last { + w.set_stop(true); + } else { + w.set_stop(false); + } + + w.cmd() + }); + + while p.ic_rxflr().read().rxflr() == 0 { + if let Some(abort_reason) = self.read_and_clear_abort_reason() { + return Err(Error::Abort(abort_reason)); + } + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + if bytes.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in bytes.iter().enumerate() { + let last = i == bytes.len() - 1; + + // NOTE(unsafe) We have &mut self + unsafe { + p.ic_data_cmd().write(|w| { + if send_stop && last { + w.set_stop(true); + } else { + w.set_stop(false); + } + w.set_dat(*byte); + }); + + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} + + let abort_reason = self.read_and_clear_abort_reason(); + + if abort_reason.is_some() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occured. + + while !p.ic_raw_intr_stat().read().stop_det() {} + + p.ic_clr_stop_det().read().clr_stop_det(); + } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + if let Some(abort_reason) = abort_reason { + return Err(Error::Abort(abort_reason)); + } + } + } + Ok(()) + } + + // ========================= Blocking public API + // ========================= + + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.read_blocking_internal(buffer, false, true) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, true) + } + + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, false)?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } +} + +// impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // +// Async public API // ========================= + +// pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), +// Error> { if bytes.is_empty() { self.write_blocking_internal(address, +// bytes, true) } else { self.write_dma_internal(address, bytes, +// true, true).await } } + +// pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> +// Result<(), Error> { if bytes.is_empty() { return +// Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); + +// let mut first = true; let mut current = iter.next(); while let +// Some(c) = current { let next = iter.next(); let is_last = +// next.is_none(); + +// self.write_dma_internal(address, c, first, is_last).await?; +// first = false; +// current = next; +// } Ok(()) +// } + +// pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> +// Result<(), Error> { if buffer.is_empty() { +// self.read_blocking_internal(address, buffer, false) } else { +// self.read_dma_internal(address, buffer, false).await } } + +// pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: +// &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { +// self.write_blocking_internal(address, bytes, false)?; } else { +// self.write_dma_internal(address, bytes, true, true).await?; } + +// if buffer.is_empty() { self.read_blocking_internal(address, buffer, +// true)?; } else { self.read_dma_internal(address, buffer, +// true).await?; } + +// Ok(()) +// } +// } + +mod eh02 { + use super::*; + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { + type Error = Error; + + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, bytes) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { + type Error = Error; + + fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, bytes, buffer) + } + } +} + +fn i2c_reserved_addr(addr: u16) -> bool { + (addr & 0x78) == 0 || (addr & 0x78) == 0x78 +} + mod sealed { pub trait Instance {} pub trait Mode {} @@ -155,11 +421,11 @@ macro_rules! impl_mode { }; } -pub struct Master; -pub struct Slave; +pub struct Blocking; +pub struct Async; -impl_mode!(Master); -impl_mode!(Slave); +impl_mode!(Blocking); +impl_mode!(Async); pub trait Instance: sealed::Instance { fn regs() -> pac::i2c::I2c; From 603513e76e0cf727808033540598c6c7dd597133 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:01:33 +0200 Subject: [PATCH 0196/1575] Fix blocking I2C --- embassy-rp/src/i2c.rs | 196 +++++++++++++++++++++++++++--------------- 1 file changed, 127 insertions(+), 69 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f7e6a6f6b..b368c49cf 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -25,22 +25,17 @@ pub enum Error { #[derive(Copy, Clone)] pub struct Config { pub frequency: u32, - pub sda_pullup: bool, - pub scl_pullup: bool, } impl Default for Config { fn default() -> Self { Self { frequency: 100_000, - sda_pullup: false, - scl_pullup: false, } } } -const TX_FIFO_SIZE: u8 = 16; -const RX_FIFO_SIZE: u8 = 16; +const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, @@ -64,7 +59,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_enable().write(|w| w.set_enable(false)); // Select controller mode & speed - p.ic_con().write(|w| { + p.ic_con().modify(|w| { // Always use "fast" mode (<= 400 kHz, works fine for standard // mode too) w.set_speed(i2c::vals::Speed::FAST); @@ -85,11 +80,17 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { scl.pad_ctrl().write(|w| { w.set_schmitt(true); - w.set_pue(config.scl_pullup); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); }); sda.pad_ctrl().write(|w| { w.set_schmitt(true); - w.set_pue(config.sda_pullup); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); }); // Configure baudrate @@ -97,7 +98,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // There are some subtleties to I2C timing which we are completely // ignoring here See: // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 - let clk_base = crate::clocks::clk_sys_freq(); + let clk_base = crate::clocks::clk_peri_freq(); let period = (clk_base + config.frequency / 2) / config.frequency; let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low @@ -134,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_fs_spklen() .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); p.ic_sda_hold() - .write(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); + .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); // Enable I2C block p.ic_enable().write(|w| w.set_enable(true)); @@ -145,42 +146,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { - /// Number of bytes currently in the RX FIFO - #[inline] - pub fn rx_fifo_used(&self) -> u8 { - unsafe { T::regs().ic_rxflr().read().rxflr() } - } - - /// Remaining capacity in the RX FIFO - #[inline] - pub fn rx_fifo_free(&self) -> u8 { - RX_FIFO_SIZE - self.rx_fifo_used() - } - - /// RX FIFO is empty - #[inline] - pub fn rx_fifo_empty(&self) -> bool { - self.rx_fifo_used() == 0 - } - - /// Number of bytes currently in the TX FIFO - #[inline] - pub fn tx_fifo_used(&self) -> u8 { - unsafe { T::regs().ic_txflr().read().txflr() } - } - - /// Remaining capacity in the TX FIFO - #[inline] - pub fn tx_fifo_free(&self) -> u8 { - TX_FIFO_SIZE - self.tx_fifo_used() - } - - /// TX FIFO is at capacity - #[inline] - pub fn tx_fifo_full(&self) -> bool { - self.tx_fifo_free() == 0 - } - fn setup(addr: u16) -> Result<(), Error> { if addr >= 0x80 { return Err(Error::AddressOutOfRange(addr)); @@ -229,22 +194,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // NOTE(unsafe) We have &mut self unsafe { // wait until there is space in the FIFO to write the next byte - while self.tx_fifo_full() {} + while p.ic_txflr().read().txflr() == FIFO_SIZE {} p.ic_data_cmd().write(|w| { - if restart && first { - w.set_restart(true); - } else { - w.set_restart(false); - } + w.set_restart(restart && first); + w.set_stop(send_stop && last); - if send_stop && last { - w.set_stop(true); - } else { - w.set_stop(false); - } - - w.cmd() + w.set_cmd(true); }); while p.ic_rxflr().read().rxflr() == 0 { @@ -273,11 +229,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // NOTE(unsafe) We have &mut self unsafe { p.ic_data_cmd().write(|w| { - if send_stop && last { - w.set_stop(true); - } else { - w.set_stop(false); - } + w.set_stop(send_stop && last); w.set_dat(*byte); }); @@ -310,12 +262,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - // ========================= Blocking public API + // ========================= + // Blocking public API // ========================= pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.read_blocking_internal(buffer, false, true) + self.read_blocking_internal(buffer, true, true) // Automatic Stop } @@ -400,6 +353,107 @@ mod eh02 { } } +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl embedded_hal_1::i2c::Error for Error { + fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { + match *self { + _ => embedded_hal_1::i2c::ErrorKind::Bus, + // Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, + // Self::Nack => { + // embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) + // } + // Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, + // Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, + // Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, + // Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { + type Error = Error; + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> { + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + + fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, buffer) + } + + fn write_iter(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let mut peekable = bytes.into_iter().peekable(); + Self::setup(address.into())?; + + while let Some(tx) = peekable.next() { + self.write_blocking_internal(&[tx], peekable.peek().is_none())?; + } + Ok(()) + } + + fn write_iter_read(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let peekable = bytes.into_iter().peekable(); + Self::setup(address.into())?; + + for tx in peekable { + self.write_blocking_internal(&[tx], false)? + } + self.read_blocking_internal(buffer, true, true) + } + + fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, wr_buffer, rd_buffer) + } + + fn transaction<'a>( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + ) -> Result<(), Self::Error> { + Self::setup(address.into())?; + for i in 0..operations.len() { + let last = i == operations.len() - 1; + match &mut operations[i] { + embedded_hal_1::i2c::blocking::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } + embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + + fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>, + { + Self::setup(address.into())?; + let mut peekable = operations.into_iter().peekable(); + while let Some(operation) = peekable.next() { + let last = peekable.peek().is_none(); + match operation { + embedded_hal_1::i2c::blocking::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } + embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + } +} + fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 } @@ -428,6 +482,8 @@ impl_mode!(Blocking); impl_mode!(Async); pub trait Instance: sealed::Instance { + type Interrupt; + fn regs() -> pac::i2c::I2c; } @@ -435,6 +491,8 @@ macro_rules! impl_instance { ($type:ident, $irq:ident) => { impl sealed::Instance for peripherals::$type {} impl Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + fn regs() -> pac::i2c::I2c { pac::$type } @@ -442,8 +500,8 @@ macro_rules! impl_instance { }; } -impl_instance!(I2C0, I2c0); -impl_instance!(I2C1, I2c1); +impl_instance!(I2C0, I2C0_IRQ); +impl_instance!(I2C1, I2C1_IRQ); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From be68d8ebb773fcf6d0cb94f3bc580d6661f6779b Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 14:24:49 +0200 Subject: [PATCH 0197/1575] Add further i2c error types --- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/i2c.rs | 69 +++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 875c129c0..1c446f389 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -114,7 +114,7 @@ pub unsafe fn init() { reset::unreset_wait(peris); } -pub(crate) fn clk_sys_freq() -> u32 { +pub(crate) fn _clk_sys_freq() -> u32 { 125_000_000 } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index b368c49cf..20616cd62 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -5,12 +5,25 @@ use pac::i2c; use crate::{pac, peripherals, Peripheral}; +/// I2C error abort reason +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AbortReason { + /// A bus operation was not acknowledged, e.g. due to the addressed device + /// not being available on the bus or the device not being ready to process + /// requests at the moment + NoAcknowledge, + /// The arbitration was lost, e.g. electrical problems with the clock signal + ArbitrationLoss, + Other(u32), +} + /// I2C error #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// I2C abort with error - Abort(u32), + Abort(AbortReason), /// User passed in a read buffer that was 0 length InvalidReadBufferLength, /// User passed in a write buffer that was 0 length @@ -29,9 +42,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Self { - frequency: 100_000, - } + Self { frequency: 100_000 } } } @@ -164,18 +175,30 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } - fn read_and_clear_abort_reason(&mut self) -> Option { + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); unsafe { - let abort_reason = p.ic_tx_abrt_source().read().0; - if abort_reason != 0 { + let abort_reason = p.ic_tx_abrt_source().read(); + if abort_reason.0 != 0 { // Note clearing the abort flag also clears the reason, and this // instance of flag is clear-on-read! Note also the // IC_CLR_TX_ABRT register always reads as 0. p.ic_clr_tx_abrt().read(); - Some(abort_reason) + + let reason = if abort_reason.abrt_7b_addr_noack() + | abort_reason.abrt_10addr1_noack() + | abort_reason.abrt_10addr2_noack() + { + AbortReason::NoAcknowledge + } else if abort_reason.arb_lost() { + AbortReason::ArbitrationLoss + } else { + AbortReason::Other(abort_reason.0) + }; + + Err(Error::Abort(reason)) } else { - None + Ok(()) } } } @@ -204,9 +227,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { }); while p.ic_rxflr().read().rxflr() == 0 { - if let Some(abort_reason) = self.read_and_clear_abort_reason() { - return Err(Error::Abort(abort_reason)); - } + self.read_and_clear_abort_reason()?; } *byte = p.ic_data_cmd().read().dat(); @@ -241,7 +262,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let abort_reason = self.read_and_clear_abort_reason(); - if abort_reason.is_some() || (send_stop && last) { + if abort_reason.is_err() || (send_stop && last) { // If the transaction was aborted or if it completed // successfully wait until the STOP condition has occured. @@ -254,9 +275,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { // condition. Note also the hardware clears RX FIFO as well as // TX on abort, ecause we set hwparam // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - if let Some(abort_reason) = abort_reason { - return Err(Error::Abort(abort_reason)); - } + abort_reason?; } } Ok(()) @@ -360,15 +379,15 @@ mod eh1 { impl embedded_hal_1::i2c::Error for Error { fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { match *self { - _ => embedded_hal_1::i2c::ErrorKind::Bus, - // Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, - // Self::Nack => { - // embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) - // } - // Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, - // Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, - // Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, - // Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, + Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, + Self::Abort(AbortReason::NoAcknowledge) => { + embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) + } + Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other, + Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other, + Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other, + Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other, + Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other, } } } From 53c34ccc399c7bec98880657e52842477af09f49 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 29 Aug 2022 13:31:17 +0200 Subject: [PATCH 0198/1575] Add async API for I2C --- embassy-rp/src/i2c.rs | 281 +++++++++++++++++++++++++++++++++--------- 1 file changed, 225 insertions(+), 56 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 20616cd62..19cdef133 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,8 +1,13 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use atomic_polyfill::Ordering; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; +use crate::dma::{AnyChannel, Channel}; +use crate::gpio::sealed::Pin; +use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; /// I2C error abort reason @@ -49,9 +54,165 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { + tx_dma: Option>, + rx_dma: Option>, + dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new( + _peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda, irq, tx_dma, rx_dma); + + // Enable interrupts + unsafe { + T::regs().ic_intr_mask().modify(|w| { + w.set_m_rx_done(true); + }); + } + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self::new_inner( + _peri, + scl.map_into(), + sda.map_into(), + Some(tx_dma.map_into()), + Some(rx_dma.map_into()), + config, + ) + } + + unsafe fn on_interrupt(_: *mut ()) { + let status = T::regs().ic_intr_stat().read(); + + // FIXME: + if status.tcr() || status.tc() { + let state = T::state(); + state.chunks_transferred.fetch_add(1, Ordering::Relaxed); + state.waker.wake(); + } + // The flag can only be cleared by writting to nbytes, we won't do that here, so disable + // the interrupt + // critical_section::with(|_| { + // regs.cr1().modify(|w| w.set_tcie(false)); + // }); + } + + async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + let len = bytes.len(); + for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() { + let first = idx == 0; + let last = idx * self.dma_buf.len() + chunk.len() == len; + + for (i, byte) in chunk.iter().enumerate() { + let mut b = i2c::regs::IcDataCmd::default(); + b.set_dat(*byte); + b.set_stop(send_stop && last); + + self.dma_buf[i] = b.0 as u16; + } + + // Note(safety): Unwrap should be safe, as this can only be called + // when `Mode == Async`, where we have dma channels. + let ch = self.tx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().ic_dma_cr().modify(|w| { + w.set_tdmae(true); + }); + + crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ) + }; + + transfer.await; + } + + Ok(()) + } + + async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let len = buffer.len(); + self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; + + if len > 2 { + // Note(safety): Unwrap should be safe, as this can only be called + // when `Mode == Async`, where we have dma channels. + let ch = self.rx_dma.as_mut().unwrap(); + let transfer = unsafe { + T::regs().ic_data_cmd().modify(|w| { + w.set_cmd(true); + }); + + T::regs().ic_dma_cr().modify(|reg| { + reg.set_rdmae(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read( + ch, + T::regs().ic_data_cmd().ptr() as *const _, + &mut buffer[1..len - 1], + T::RX_DREQ, + ) + }; + transfer.await; + } + + if len > 2 { + self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; + } + + Ok(()) + } + + // ========================= + // Async public API + // ========================= + + pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if bytes.is_empty() { + self.write_blocking_internal(bytes, true) + } else { + self.write_internal(bytes, true).await + } + } + + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if buffer.is_empty() { + self.read_blocking_internal(buffer, true, true) + } else { + self.read_internal(buffer).await + } + } + + pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + if bytes.is_empty() { + self.write_blocking_internal(bytes, false)?; + } else { + self.write_internal(bytes, false).await?; + } + + if buffer.is_empty() { + self.read_blocking_internal(buffer, true, true) + } else { + self.read_internal(buffer).await + } + } +} + impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( _peri: impl Peripheral

+ 'd, @@ -59,7 +220,21 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { sda: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(_peri, scl, sda); + into_ref!(scl, sda); + Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) + } +} + +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + scl: PeripheralRef<'d, AnyPin>, + sda: PeripheralRef<'d, AnyPin>, + tx_dma: Option>, + rx_dma: Option>, + config: Config, + ) -> Self { + into_ref!(_peri); assert!(config.frequency <= 1_000_000); assert!(config.frequency > 0); @@ -152,11 +327,14 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { p.ic_enable().write(|w| w.set_enable(true)); } - Self { phantom: PhantomData } + Self { + tx_dma, + rx_dma, + dma_buf: [0; 256], + phantom: PhantomData, + } } -} -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { fn setup(addr: u16) -> Result<(), Error> { if addr >= 0x80 { return Err(Error::AddressOutOfRange(addr)); @@ -304,46 +482,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } -// impl<'d, T: Instance> I2c<'d, T, Async> { // ========================= // -// Async public API // ========================= - -// pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), -// Error> { if bytes.is_empty() { self.write_blocking_internal(address, -// bytes, true) } else { self.write_dma_internal(address, bytes, -// true, true).await } } - -// pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> -// Result<(), Error> { if bytes.is_empty() { return -// Err(Error::ZeroLengthTransfer); } let mut iter = bytes.iter(); - -// let mut first = true; let mut current = iter.next(); while let -// Some(c) = current { let next = iter.next(); let is_last = -// next.is_none(); - -// self.write_dma_internal(address, c, first, is_last).await?; -// first = false; -// current = next; -// } Ok(()) -// } - -// pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> -// Result<(), Error> { if buffer.is_empty() { -// self.read_blocking_internal(address, buffer, false) } else { -// self.read_dma_internal(address, buffer, false).await } } - -// pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: -// &mut [u8]) -> Result<(), Error> { if bytes.is_empty() { -// self.write_blocking_internal(address, bytes, false)?; } else { -// self.write_dma_internal(address, bytes, true, true).await?; } - -// if buffer.is_empty() { self.read_blocking_internal(address, buffer, -// true)?; } else { self.read_dma_internal(address, buffer, -// true).await?; } - -// Ok(()) -// } -// } - mod eh02 { use super::*; @@ -478,7 +616,34 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - pub trait Instance {} + use atomic_polyfill::AtomicUsize; + use embassy_cortex_m::interrupt::Interrupt; + use embassy_sync::waitqueue::AtomicWaker; + + pub(crate) struct State { + pub(crate) waker: AtomicWaker, + pub(crate) chunks_transferred: AtomicUsize, + } + + impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + chunks_transferred: AtomicUsize::new(0), + } + } + } + + pub trait Instance { + const TX_DREQ: u8; + const RX_DREQ: u8; + + type Interrupt: Interrupt; + + fn regs() -> crate::pac::i2c::I2c; + fn state() -> &'static State; + } + pub trait Mode {} pub trait SdaPin {} @@ -500,27 +665,31 @@ pub struct Async; impl_mode!(Blocking); impl_mode!(Async); -pub trait Instance: sealed::Instance { - type Interrupt; - - fn regs() -> pac::i2c::I2c; -} +pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident) => { - impl sealed::Instance for peripherals::$type {} - impl Instance for peripherals::$type { + ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + impl sealed::Instance for peripherals::$type { + const TX_DREQ: u8 = $tx_dreq; + const RX_DREQ: u8 = $rx_dreq; + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::i2c::I2c { pac::$type } + + fn state() -> &'static sealed::State { + static STATE: sealed::State = sealed::State::new(); + &STATE + } } + impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, I2C0_IRQ); -impl_instance!(I2C1, I2C1_IRQ); +impl_instance!(I2C0, I2C0_IRQ, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From b0d91e9f310f86b4eb9d75c92471831f1656ed1b Mon Sep 17 00:00:00 2001 From: Mathias Koch Date: Thu, 15 Sep 2022 05:38:55 +0200 Subject: [PATCH 0199/1575] Apply suggestions from code review Co-authored-by: Jacob Gonzalez --- embassy-rp/src/i2c.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 19cdef133..12fae3b7b 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -144,7 +144,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let len = buffer.len(); self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; - if len > 2 { + if len >= 2 { // Note(safety): Unwrap should be safe, as this can only be called // when `Mode == Async`, where we have dma channels. let ch = self.rx_dma.as_mut().unwrap(); @@ -168,7 +168,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { transfer.await; } - if len > 2 { + if len >= 2 { self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; } @@ -202,7 +202,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { if bytes.is_empty() { self.write_blocking_internal(bytes, false)?; } else { - self.write_internal(bytes, false).await?; + self.write_internal(bytes, true).await?; } if buffer.is_empty() { From 44c46e3c93ae0718110a8805cab4c6c8a9b5df55 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 07:55:28 +0200 Subject: [PATCH 0200/1575] Move async i2c implementation to new PR, to merge working blocking implementation faster --- embassy-rp/src/i2c.rs | 179 +----------------------------------------- 1 file changed, 1 insertion(+), 178 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 12fae3b7b..d1ec77d33 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,11 +1,9 @@ use core::marker::PhantomData; -use atomic_polyfill::Ordering; -use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; -use crate::dma::{AnyChannel, Channel}; +use crate::dma::AnyChannel; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; @@ -60,159 +58,6 @@ pub struct I2c<'d, T: Instance, M: Mode> { phantom: PhantomData<(&'d mut T, M)>, } -impl<'d, T: Instance> I2c<'d, T, Async> { - pub fn new( - _peri: impl Peripheral

+ 'd, - scl: impl Peripheral

> + 'd, - sda: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(scl, sda, irq, tx_dma, rx_dma); - - // Enable interrupts - unsafe { - T::regs().ic_intr_mask().modify(|w| { - w.set_m_rx_done(true); - }); - } - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner( - _peri, - scl.map_into(), - sda.map_into(), - Some(tx_dma.map_into()), - Some(rx_dma.map_into()), - config, - ) - } - - unsafe fn on_interrupt(_: *mut ()) { - let status = T::regs().ic_intr_stat().read(); - - // FIXME: - if status.tcr() || status.tc() { - let state = T::state(); - state.chunks_transferred.fetch_add(1, Ordering::Relaxed); - state.waker.wake(); - } - // The flag can only be cleared by writting to nbytes, we won't do that here, so disable - // the interrupt - // critical_section::with(|_| { - // regs.cr1().modify(|w| w.set_tcie(false)); - // }); - } - - async fn write_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - let len = bytes.len(); - for (idx, chunk) in bytes.chunks(self.dma_buf.len()).enumerate() { - let first = idx == 0; - let last = idx * self.dma_buf.len() + chunk.len() == len; - - for (i, byte) in chunk.iter().enumerate() { - let mut b = i2c::regs::IcDataCmd::default(); - b.set_dat(*byte); - b.set_stop(send_stop && last); - - self.dma_buf[i] = b.0 as u16; - } - - // Note(safety): Unwrap should be safe, as this can only be called - // when `Mode == Async`, where we have dma channels. - let ch = self.tx_dma.as_mut().unwrap(); - let transfer = unsafe { - T::regs().ic_dma_cr().modify(|w| { - w.set_tdmae(true); - }); - - crate::dma::write(ch, &self.dma_buf, T::regs().ic_data_cmd().ptr() as *mut _, T::TX_DREQ) - }; - - transfer.await; - } - - Ok(()) - } - - async fn read_internal(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let len = buffer.len(); - self.read_blocking_internal(&mut buffer[..1], true, len == 1)?; - - if len >= 2 { - // Note(safety): Unwrap should be safe, as this can only be called - // when `Mode == Async`, where we have dma channels. - let ch = self.rx_dma.as_mut().unwrap(); - let transfer = unsafe { - T::regs().ic_data_cmd().modify(|w| { - w.set_cmd(true); - }); - - T::regs().ic_dma_cr().modify(|reg| { - reg.set_rdmae(true); - }); - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read( - ch, - T::regs().ic_data_cmd().ptr() as *const _, - &mut buffer[1..len - 1], - T::RX_DREQ, - ) - }; - transfer.await; - } - - if len >= 2 { - self.read_blocking_internal(&mut buffer[len - 1..], false, true)?; - } - - Ok(()) - } - - // ========================= - // Async public API - // ========================= - - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if bytes.is_empty() { - self.write_blocking_internal(bytes, true) - } else { - self.write_internal(bytes, true).await - } - } - - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if buffer.is_empty() { - self.read_blocking_internal(buffer, true, true) - } else { - self.read_internal(buffer).await - } - } - - pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - if bytes.is_empty() { - self.write_blocking_internal(bytes, false)?; - } else { - self.write_internal(bytes, true).await?; - } - - if buffer.is_empty() { - self.read_blocking_internal(buffer, true, true) - } else { - self.read_internal(buffer).await - } - } -} - impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( _peri: impl Peripheral

+ 'd, @@ -616,23 +461,7 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - use atomic_polyfill::AtomicUsize; use embassy_cortex_m::interrupt::Interrupt; - use embassy_sync::waitqueue::AtomicWaker; - - pub(crate) struct State { - pub(crate) waker: AtomicWaker, - pub(crate) chunks_transferred: AtomicUsize, - } - - impl State { - pub(crate) const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - chunks_transferred: AtomicUsize::new(0), - } - } - } pub trait Instance { const TX_DREQ: u8; @@ -641,7 +470,6 @@ mod sealed { type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; - fn state() -> &'static State; } pub trait Mode {} @@ -678,11 +506,6 @@ macro_rules! impl_instance { fn regs() -> pac::i2c::I2c { pac::$type } - - fn state() -> &'static sealed::State { - static STATE: sealed::State = sealed::State::new(); - &STATE - } } impl Instance for peripherals::$type {} }; From bf1da0497ce1a591865f2bd8d0a29489079e710b Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 27 Sep 2022 08:03:49 +0200 Subject: [PATCH 0201/1575] Allow unused fields temporarily in i2c --- embassy-rp/src/i2c.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d1ec77d33..9596d661d 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -52,9 +52,9 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { - tx_dma: Option>, - rx_dma: Option>, - dma_buf: [u16; 256], + _tx_dma: Option>, + _rx_dma: Option>, + _dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } @@ -75,8 +75,8 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, sda: PeripheralRef<'d, AnyPin>, - tx_dma: Option>, - rx_dma: Option>, + _tx_dma: Option>, + _rx_dma: Option>, config: Config, ) -> Self { into_ref!(_peri); @@ -173,9 +173,9 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } Self { - tx_dma, - rx_dma, - dma_buf: [0; 256], + _tx_dma, + _rx_dma, + _dma_buf: [0; 256], phantom: PhantomData, } } From a89a0c2f122759a7e743e75152f7af7f67b4387b Mon Sep 17 00:00:00 2001 From: ceekdee Date: Tue, 27 Sep 2022 21:55:41 -0500 Subject: [PATCH 0202/1575] Initial add for sx126x --- README.md | 2 +- embassy-lora/Cargo.toml | 2 + embassy-lora/src/lib.rs | 2 + embassy-lora/src/sx126x/mod.rs | 171 ++++ .../src/sx126x/sx126x_lora/board_specific.rs | 251 ++++++ embassy-lora/src/sx126x/sx126x_lora/mod.rs | 735 ++++++++++++++++++ .../src/sx126x/sx126x_lora/mod_params.rs | 469 +++++++++++ .../src/sx126x/sx126x_lora/subroutine.rs | 688 ++++++++++++++++ examples/nrf/Cargo.toml | 7 +- examples/nrf/src/bin/lora_p2p_report.rs | 84 ++ examples/nrf/src/bin/lora_p2p_sense.rs | 173 +++++ 11 files changed, 2582 insertions(+), 2 deletions(-) create mode 100644 embassy-lora/src/sx126x/mod.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/board_specific.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod_params.rs create mode 100644 embassy-lora/src/sx126x/sx126x_lora/subroutine.rs create mode 100644 examples/nrf/src/bin/lora_p2p_report.rs create mode 100644 examples/nrf/src/bin/lora_p2p_sense.rs diff --git a/README.md b/README.md index 9f08bf676..eaa91012c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The embassy-net network stac The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. - **LoRa** - -embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers. +embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers. - **USB** - embassy-usb implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 1757efa8b..0c2f983f6 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -8,6 +8,7 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ + { name = "rak4631", target = "thumbv7em-none-eabihf", features = ["rak4631"] }, { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, ] @@ -15,6 +16,7 @@ flavors = [ [lib] [features] +rak4631 = [] sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 90ba0d1d4..342f66b2a 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -7,6 +7,8 @@ pub(crate) mod fmt; #[cfg(feature = "stm32wl")] pub mod stm32wl; +#[cfg(feature = "rak4631")] +pub mod sx126x; #[cfg(feature = "sx127x")] pub mod sx127x; diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs new file mode 100644 index 000000000..d67c7b109 --- /dev/null +++ b/embassy-lora/src/sx126x/mod.rs @@ -0,0 +1,171 @@ +use core::future::Future; + +use defmt::Format; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::*; +use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig}; +use lorawan_device::async_device::Timings; + +mod sx126x_lora; +use sx126x_lora::LoRa; + +use self::sx126x_lora::mod_params::RadioError; + +/// Semtech Sx126x LoRa peripheral +pub struct Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + pub lora: LoRa, +} + +impl Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + pub async fn new( + spi: SPI, + cs: CS, + reset: RESET, + antenna_rx: ANTRX, + antenna_tx: ANTTX, + dio1: WAIT, + busy: WAIT, + enable_public_network: bool, + ) -> Result> { + let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy); + lora.init().await?; + lora.set_lora_modem(enable_public_network).await?; + Ok(Self { lora }) + } +} + +impl Timings for Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + fn get_rx_window_offset_ms(&self) -> i32 { + -500 + } + fn get_rx_window_duration_ms(&self) -> u32 { + 800 + } +} + +impl PhyRxTx for Sx126xRadio +where + SPI: SpiBus + 'static, + CS: OutputPin + 'static, + RESET: OutputPin + 'static, + ANTRX: OutputPin + 'static, + ANTTX: OutputPin + 'static, + WAIT: Wait + 'static, + BUS: Error + Format + 'static, +{ + type PhyError = RadioError; + + type TxFuture<'m> = impl Future> + 'm + where + SPI: 'm, + CS: 'm, + RESET: 'm, + ANTRX: 'm, + ANTTX: 'm, + WAIT: 'm, + BUS: 'm; + + fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> { + trace!("TX START"); + async move { + self.lora + .set_tx_config( + config.pw, + config.rf.spreading_factor.into(), + config.rf.bandwidth.into(), + config.rf.coding_rate.into(), + 4, + false, + true, + false, + 0, + false, + ) + .await?; + self.lora.set_max_payload_length(buffer.len() as u8).await?; + self.lora.set_channel(config.rf.frequency).await?; + self.lora.send(buffer, 0xffffff).await?; + self.lora.process_irq(None, None, None).await?; + trace!("TX DONE"); + return Ok(0); + } + } + + type RxFuture<'m> = impl Future> + 'm + where + SPI: 'm, + CS: 'm, + RESET: 'm, + ANTRX: 'm, + ANTTX: 'm, + WAIT: 'm, + BUS: 'm; + + fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> { + trace!("RX START"); + async move { + self.lora + .set_rx_config( + config.spreading_factor.into(), + config.bandwidth.into(), + config.coding_rate.into(), + 4, + 4, + false, + 0u8, + true, + false, + 0, + false, + true, + ) + .await?; + self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; + self.lora.set_channel(config.frequency).await?; + self.lora.rx(90 * 1000).await?; + let mut received_len = 0u8; + self.lora + .process_irq(Some(receiving_buffer), Some(&mut received_len), None) + .await?; + trace!("RX DONE"); + + let packet_status = self.lora.get_latest_packet_status(); + let mut rssi = 0i16; + let mut snr = 0i8; + if packet_status.is_some() { + rssi = packet_status.unwrap().rssi as i16; + snr = packet_status.unwrap().snr; + } + + Ok((received_len as usize, RxQuality::new(rssi, snr))) + } + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs new file mode 100644 index 000000000..5b4891c04 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -0,0 +1,251 @@ +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +use super::mod_params::RadioError::*; +use super::mod_params::*; +use super::LoRa; + +// Defines the time required for the TCXO to wakeup [ms]. +const BRD_TCXO_WAKEUP_TIME: u32 = 10; + +// Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. +// The base implementation supports the RAK4631 board. + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. + pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Initialize the TCXO power pin + pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError> { + let timeout = self.brd_get_board_tcxo_wakeup_time() << 6; + self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout) + .await?; + Ok(()) + } + + // Initialize RF switch control pins + pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError> { + self.sub_set_dio2_as_rf_switch_ctrl(true).await?; + Ok(()) + } + + // Initialize the radio debug pins + pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Hardware reset of the radio + pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError> { + Timer::after(Duration::from_millis(10)).await; + self.reset.set_low().map_err(|_| Reset)?; + Timer::after(Duration::from_millis(20)).await; + self.reset.set_high().map_err(|_| Reset)?; + Timer::after(Duration::from_millis(10)).await; + Ok(()) + } + + // Wait while the busy pin is high + pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError> { + self.busy.wait_for_low().await.map_err(|_| Busy)?; + Ok(()) + } + + // Wake up the radio + pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError> { + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?; + self.spi.write(&[0x00]).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + self.brd_set_operating_mode(RadioMode::StandbyRC); + Ok(()) + } + + // Send a command that writes data to the radio + pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[op_code.value()]).await.map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + if op_code != OpCode::SetSleep { + self.brd_wait_on_busy().await?; + } + Ok(()) + } + + // Send a command that reads data from the radio, filling the provided buffer and returning a status + pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result> { + let mut status = [0u8]; + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[op_code.value()]).await.map_err(SPI)?; + self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + + Ok(status[0]) + } + + // Write one or more bytes of data to the radio memory + pub(super) async fn brd_write_registers( + &mut self, + start_register: Register, + buffer: &[u8], + ) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?; + self.spi + .write(&[ + ((start_register.addr() & 0xFF00) >> 8) as u8, + (start_register.addr() & 0x00FF) as u8, + ]) + .await + .map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Read one or more bytes of data from the radio memory + pub(super) async fn brd_read_registers( + &mut self, + start_register: Register, + buffer: &mut [u8], + ) -> Result<(), RadioError> { + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?; + self.spi + .write(&[ + ((start_register.addr() & 0xFF00) >> 8) as u8, + (start_register.addr() & 0x00FF) as u8, + 0x00u8, + ]) + .await + .map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Write data to the buffer holding the payload in the radio + pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError> { + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?; + self.spi.write(&[offset]).await.map_err(SPI)?; + self.spi.write(buffer).await.map_err(SPI)?; + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Read data from the buffer holding the payload in the radio + pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError> { + let mut input = [0u8]; + + self.sub_check_device_ready().await?; + + self.cs.set_low().map_err(|_| CS)?; + self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?; + self.spi.write(&[offset]).await.map_err(SPI)?; + self.spi.write(&[0x00]).await.map_err(SPI)?; + for i in 0..buffer.len() { + self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; + buffer[i] = input[0]; + } + self.cs.set_high().map_err(|_| CS)?; + + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Set the radio output power + pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError> { + self.sub_set_tx_params(power, RampTime::Ramp40Us).await?; + Ok(()) + } + + // Get the radio type + pub(super) fn brd_get_radio_type(&mut self) -> RadioType { + #[cfg(feature = "rak4631")] + RadioType::SX1262 + } + + // Initialize the RF switch I/O pins interface + pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // De-initialize the RF switch I/O pins interface for MCU low power modes + pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Check if the given RF frequency is supported by the hardware + pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { + #[cfg(feature = "rak4631")] + Ok(true) // all frequencies currently supported for the SX1262 within a rak4631 + } + + // Get the duration required for the TCXO to wakeup [ms]. + pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 { + BRD_TCXO_WAKEUP_TIME + } + + /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process + pub(super) async fn brd_get_dio1_pin_state( + &mut self, + ) -> Result> { + Ok(0) + } + */ + + // Get the current radio operatiing mode + pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode { + self.operating_mode + } + + // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts. + pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) { + self.operating_mode = mode; + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs new file mode 100644 index 000000000..02f5f83a2 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -0,0 +1,735 @@ +#![allow(dead_code)] + +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +mod board_specific; +pub mod mod_params; +mod subroutine; + +use mod_params::RadioError::*; +use mod_params::*; + +// Syncwords for public and private networks +const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444; +const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424; + +// Maximum number of registers that can be added to the retention list +const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4; + +// Possible LoRa bandwidths +const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz]; + +// Radio complete wakeup time with margin for temperature compensation [ms] +const RADIO_WAKEUP_TIME: u32 = 3; + +/// Provides high-level access to Semtech SX126x-based boards +pub struct LoRa { + spi: SPI, + cs: CS, + reset: RESET, + antenna_rx: ANTRX, + antenna_tx: ANTTX, + dio1: WAIT, + busy: WAIT, + operating_mode: RadioMode, + rx_continuous: bool, + max_payload_length: u8, + modulation_params: Option, + packet_type: PacketType, + packet_params: Option, + packet_status: Option, + image_calibrated: bool, + frequency_error: u32, +} + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () + pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self { + Self { + spi, + cs, + reset, + antenna_rx, + antenna_tx, + dio1, + busy, + operating_mode: RadioMode::Sleep, + rx_continuous: false, + max_payload_length: 0xFFu8, + modulation_params: None, + packet_type: PacketType::LoRa, + packet_params: None, + packet_status: None, + image_calibrated: false, + frequency_error: 0u32, // where is volatile FrequencyError modified ??? + } + } + + /// Initialize the radio + pub async fn init(&mut self) -> Result<(), RadioError> { + self.sub_init().await?; + self.sub_set_standby(StandbyMode::RC).await?; + self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?; + self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?; + self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?; + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + self.add_register_to_retention_list(Register::RxGain.addr()).await?; + self.add_register_to_retention_list(Register::TxModulation.addr()) + .await?; + Ok(()) + } + + /// Return current radio state + pub fn get_status(&mut self) -> RadioState { + match self.brd_get_operating_mode() { + RadioMode::Transmit => RadioState::TxRunning, + RadioMode::Receive => RadioState::RxRunning, + RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting, + _ => RadioState::Idle, + } + } + + /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired) + pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError> { + self.sub_set_packet_type(PacketType::LoRa).await?; + if enable_public_network { + self.brd_write_registers( + Register::LoRaSyncword, + &[ + ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8, + (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8, + ], + ) + .await?; + } else { + self.brd_write_registers( + Register::LoRaSyncword, + &[ + ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8, + (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8, + ], + ) + .await?; + } + + Ok(()) + } + + /// Sets the channel frequency + pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError> { + self.sub_set_rf_frequency(frequency).await?; + Ok(()) + } + + /* Checks if the channel is free for the given time. This is currently not implemented until a substitute + for switching to the FSK modem is found. + + pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool; + */ + + /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand. + /// After calling this function either set_rx_config() or set_tx_config() must be called. + pub async fn get_random_value(&mut self) -> Result> { + self.sub_set_dio_irq_params( + IrqMask::None.value(), + IrqMask::None.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + let result = self.sub_get_random().await?; + Ok(result) + } + + /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand. + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// symb_timeout RxSingle timeout value in symbols + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// payload_len payload length when fixed length is used + /// crc_on [0: OFF, 1: ON] + /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] + /// hop_period number of symbols between each hop + /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] + /// rx_continuous reception mode [false: single mode, true: continuous mode] + pub async fn set_rx_config( + &mut self, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + symb_timeout: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + _freq_hop_on: bool, + _hop_period: u8, + iq_inverted: bool, + rx_continuous: bool, + ) -> Result<(), RadioError> { + let mut symb_timeout_final = symb_timeout; + + self.rx_continuous = rx_continuous; + if self.rx_continuous { + symb_timeout_final = 0; + } + if fixed_len { + self.max_payload_length = payload_len; + } else { + self.max_payload_length = 0xFFu8; + } + + self.sub_set_stop_rx_timer_on_preamble_detect(false).await?; + + let mut low_data_rate_optimize = 0x00u8; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = 0x01u8; + } + + let modulation_params = ModulationParams { + spreading_factor: spreading_factor, + bandwidth: bandwidth, + coding_rate: coding_rate, + low_data_rate_optimize: low_data_rate_optimize, + }; + + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let packet_params = PacketParams { + preamble_length: preamble_length_final, + implicit_header: fixed_len, + payload_length: self.max_payload_length, + crc_on: crc_on, + iq_inverted: iq_inverted, + }; + + self.modulation_params = Some(modulation_params); + self.packet_params = Some(packet_params); + + self.standby().await?; + self.sub_set_modulation_params().await?; + self.sub_set_packet_params().await?; + self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?; + + // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) + let mut iq_polarity = [0x00u8]; + self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?; + if iq_inverted { + self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))]) + .await?; + } else { + self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)]) + .await?; + } + Ok(()) + } + + /// Set the transmission parameters for the LoRa modem (only). + /// power output power [dBm] + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// crc_on [0: OFF, 1: ON] + /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] + /// hop_period number of symbols between each hop + /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] + pub async fn set_tx_config( + &mut self, + power: i8, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + crc_on: bool, + _freq_hop_on: bool, + _hop_period: u8, + iq_inverted: bool, + ) -> Result<(), RadioError> { + let mut low_data_rate_optimize = 0x00u8; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = 0x01u8; + } + + let modulation_params = ModulationParams { + spreading_factor: spreading_factor, + bandwidth: bandwidth, + coding_rate: coding_rate, + low_data_rate_optimize: low_data_rate_optimize, + }; + + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let packet_params = PacketParams { + preamble_length: preamble_length_final, + implicit_header: fixed_len, + payload_length: self.max_payload_length, + crc_on: crc_on, + iq_inverted: iq_inverted, + }; + + self.modulation_params = Some(modulation_params); + self.packet_params = Some(packet_params); + + self.standby().await?; + self.sub_set_modulation_params().await?; + self.sub_set_packet_params().await?; + + // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) + + let mut tx_modulation = [0x00u8]; + self.brd_read_registers(Register::TxModulation, &mut tx_modulation) + .await?; + if bandwidth == Bandwidth::_500KHz { + self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))]) + .await?; + } else { + self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)]) + .await?; + } + + self.brd_set_rf_tx_power(power).await?; + Ok(()) + } + + /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported] + pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result> { + Ok(self.brd_check_rf_frequency(frequency).await?) + } + + /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called) + /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] + /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] + /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + /// preamble_length length in symbols (the hardware adds 4 more symbols) + /// fixed_len fixed length packets [0: variable, 1: fixed] + /// payload_len sets payload length when fixed length is used + /// crc_on [0: OFF, 1: ON] + pub fn get_time_on_air( + &mut self, + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + ) -> Result> { + let numerator = 1000 + * Self::get_lora_time_on_air_numerator( + spreading_factor, + bandwidth, + coding_rate, + preamble_length, + fixed_len, + payload_len, + crc_on, + ); + let denominator = bandwidth.value_in_hz(); + if denominator == 0 { + Err(RadioError::InvalidBandwidth) + } else { + Ok((numerator + denominator - 1) / denominator) + } + } + + /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms] + pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError> { + if self.packet_params.is_some() { + self.sub_set_dio_irq_params( + IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), + IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + let mut packet_params = self.packet_params.as_mut().unwrap(); + packet_params.payload_length = buffer.len() as u8; + self.sub_set_packet_params().await?; + self.sub_send_payload(buffer, timeout).await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + /// Set the radio in sleep mode + pub async fn sleep(&mut self) -> Result<(), RadioError> { + self.sub_set_sleep(SleepParams { + wakeup_rtc: false, + reset: false, + warm_start: true, + }) + .await?; + Timer::after(Duration::from_millis(2)).await; + Ok(()) + } + + /// Set the radio in standby mode + pub async fn standby(&mut self) -> Result<(), RadioError> { + self.sub_set_standby(StandbyMode::RC).await?; + Ok(()) + } + + /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)] + pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + if self.rx_continuous { + self.sub_set_rx(0xFFFFFF).await?; + } else { + self.sub_set_rx(timeout << 6).await?; + } + + Ok(()) + } + + /// Start a Channel Activity Detection + pub async fn start_cad(&mut self) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), + IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + self.sub_set_cad().await?; + Ok(()) + } + + /// Sets the radio in continuous wave transmission mode + /// frequency channel RF frequency + /// power output power [dBm] + /// timeout transmission mode timeout [s] + pub async fn set_tx_continuous_wave( + &mut self, + frequency: u32, + power: i8, + _timeout: u16, + ) -> Result<(), RadioError> { + self.sub_set_rf_frequency(frequency).await?; + self.brd_set_rf_tx_power(power).await?; + self.sub_set_tx_continuous_wave().await?; + + Ok(()) + } + + /// Read the current RSSI value for the LoRa modem (only) [dBm] + pub async fn get_rssi(&mut self) -> Result> { + let value = self.sub_get_rssi_inst().await?; + Ok(value as i16) + } + + /// Write one or more radio registers with a buffer of a given size, starting at the first register address + pub async fn write_registers_from_buffer( + &mut self, + start_register: Register, + buffer: &[u8], + ) -> Result<(), RadioError> { + self.brd_write_registers(start_register, buffer).await?; + Ok(()) + } + + /// Read one or more radio registers into a buffer of a given size, starting at the first register address + pub async fn read_registers_into_buffer( + &mut self, + start_register: Register, + buffer: &mut [u8], + ) -> Result<(), RadioError> { + self.brd_read_registers(start_register, buffer).await?; + Ok(()) + } + + /// Set the maximum payload length (in bytes) for a LoRa modem (only). + pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError> { + if self.packet_params.is_some() { + let packet_params = self.packet_params.as_mut().unwrap(); + self.max_payload_length = max; + packet_params.payload_length = max; + self.sub_set_packet_params().await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + /// Get the time required for the board plus radio to get out of sleep [ms] + pub fn get_wakeup_time(&mut self) -> u32 { + self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME + } + + /// Process the radio irq + pub async fn process_irq( + &mut self, + receiving_buffer: Option<&mut [u8]>, + received_len: Option<&mut u8>, + cad_activity_detected: Option<&mut bool>, + ) -> Result<(), RadioError> { + loop { + info!("process_irq loop entered"); // debug ??? + + let de = self.sub_get_device_errors().await?; + info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", + de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); + let st = self.sub_get_status().await?; + info!( + "radio status: cmd_status: {:x}, chip_mode: {:x}", + st.cmd_status, st.chip_mode + ); + + self.dio1.wait_for_high().await.map_err(|_| DIO1)?; + let operating_mode = self.brd_get_operating_mode(); + let irq_flags = self.sub_get_irq_status().await?; + self.sub_clear_irq_status(irq_flags).await?; + info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ??? + + // check for errors and unexpected interrupt masks (based on operation mode) + if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } + return Err(RadioError::HeaderError); + } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() { + if operating_mode == RadioMode::Receive { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } + return Err(RadioError::CRCErrorOnReceive); + } else { + return Err(RadioError::CRCErrorUnexpected); + } + } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() { + if operating_mode == RadioMode::Transmit { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Err(RadioError::TransmitTimeout); + } else if operating_mode == RadioMode::Receive { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Err(RadioError::ReceiveTimeout); + } else { + return Err(RadioError::TimeoutUnexpected); + } + } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value()) + && (operating_mode != RadioMode::Transmit) + { + return Err(RadioError::TransmitDoneUnexpected); + } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value()) + && (operating_mode != RadioMode::Receive) + { + return Err(RadioError::ReceiveDoneUnexpected); + } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value()) + || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value())) + && (operating_mode != RadioMode::ChannelActivityDetection) + { + return Err(RadioError::CADUnexpected); + } + + // debug ??? + if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { + info!("HeaderValid"); + } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { + info!("PreambleDetected"); + } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { + info!("SyncwordValid"); + } + + // handle completions + if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() { + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Ok(()); + } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() { + if !self.rx_continuous { + self.brd_set_operating_mode(RadioMode::StandbyRC); + + // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3) + self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?; + let mut evt_clr = [0x00u8]; + self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?; + evt_clr[0] |= 1 << 1; + self.brd_write_registers(Register::EvtClr, &evt_clr).await?; + } + + if receiving_buffer.is_some() && received_len.is_some() { + *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?; + } + self.packet_status = self.sub_get_packet_status().await?.into(); + return Ok(()); + } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() { + if cad_activity_detected.is_some() { + *(cad_activity_detected.unwrap()) = + (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value(); + } + self.brd_set_operating_mode(RadioMode::StandbyRC); + return Ok(()); + } + + // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid + // are in that category), loop to wait again + } + } + + // SX126x-specific functions + + /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms] + pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { + self.sub_set_dio_irq_params( + IrqMask::All.value(), + IrqMask::All.value(), + IrqMask::None.value(), + IrqMask::None.value(), + ) + .await?; + + if self.rx_continuous { + self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous + } else { + self.sub_set_rx_boosted(timeout << 6).await?; + } + + Ok(()) + } + + /// Set the Rx duty cycle management parameters (SX126x radios only) + /// rx_time structure describing reception timeout value + /// sleep_time structure describing sleep timeout value + pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { + self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?; + Ok(()) + } + + pub fn get_latest_packet_status(&mut self) -> Option { + self.packet_status + } + + // Utilities + + async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError> { + let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize]; + + // Read the address and registers already added to the list + self.brd_read_registers(Register::RetentionList, &mut buffer).await?; + + let number_of_registers = buffer[0]; + for i in 0..number_of_registers { + if register_address + == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16) + { + return Ok(()); // register already in list + } + } + + if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION { + buffer[0] += 1; // increment number of registers + + buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8; + buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8; + self.brd_write_registers(Register::RetentionList, &buffer).await?; + + Ok(()) + } else { + Err(RadioError::RetentionListExceeded) + } + } + + fn get_lora_time_on_air_numerator( + spreading_factor: SpreadingFactor, + bandwidth: Bandwidth, + coding_rate: CodingRate, + preamble_length: u16, + fixed_len: bool, + payload_len: u8, + crc_on: bool, + ) -> u32 { + let cell_denominator; + let cr_denominator = (coding_rate.value() as i32) + 4; + + // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 + let mut preamble_length_final = preamble_length; + if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) + && (preamble_length < 12) + { + preamble_length_final = 12; + } + + let mut low_data_rate_optimize = false; + if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) + && (bandwidth == Bandwidth::_125KHz)) + || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) + { + low_data_rate_optimize = true; + } + + let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 }) + - (4 * spreading_factor.value() as i32) + + (if fixed_len { 0 } else { 20 }); + + if spreading_factor.value() <= 6 { + cell_denominator = 4 * (spreading_factor.value() as i32); + } else { + cell_numerator += 8; + if low_data_rate_optimize { + cell_denominator = 4 * ((spreading_factor.value() as i32) - 2); + } else { + cell_denominator = 4 * (spreading_factor.value() as i32); + } + } + + if cell_numerator < 0 { + cell_numerator = 0; + } + + let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator) + + (preamble_length_final as i32) + + 12; + + if spreading_factor.value() <= 6 { + intermediate = intermediate + 2; + } + + (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32 + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs new file mode 100644 index 000000000..e270b2a09 --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs @@ -0,0 +1,469 @@ +use core::fmt::Debug; + +use lorawan_device::async_device::radio as device; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RadioError { + SPI(BUS), + CS, + Reset, + AntRx, + AntTx, + Busy, + DIO1, + PayloadSizeMismatch(usize, usize), + RetentionListExceeded, + InvalidBandwidth, + ModulationParamsMissing, + PacketParamsMissing, + HeaderError, + CRCErrorUnexpected, + CRCErrorOnReceive, + TransmitTimeout, + ReceiveTimeout, + TimeoutUnexpected, + TransmitDoneUnexpected, + ReceiveDoneUnexpected, + CADUnexpected, +} + +pub struct RadioSystemError { + pub rc_64khz_calibration: bool, + pub rc_13mhz_calibration: bool, + pub pll_calibration: bool, + pub adc_calibration: bool, + pub image_calibration: bool, + pub xosc_start: bool, + pub pll_lock: bool, + pub pa_ramp: bool, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum PacketType { + GFSK = 0x00, + LoRa = 0x01, + None = 0x0F, +} + +impl PacketType { + pub const fn value(self) -> u8 { + self as u8 + } + pub fn to_enum(value: u8) -> Self { + if value == 0x00 { + PacketType::GFSK + } else if value == 0x01 { + PacketType::LoRa + } else { + PacketType::None + } + } +} + +#[derive(Clone, Copy)] +pub struct PacketStatus { + pub rssi: i8, + pub snr: i8, + pub signal_rssi: i8, + pub freq_error: u32, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum RadioType { + SX1261, + SX1262, +} + +#[derive(Clone, Copy, PartialEq)] +pub enum RadioMode { + Sleep = 0x00, // sleep mode + StandbyRC = 0x01, // standby mode with RC oscillator + StandbyXOSC = 0x02, // standby mode with XOSC oscillator + FrequencySynthesis = 0x03, // frequency synthesis mode + Transmit = 0x04, // transmit mode + Receive = 0x05, // receive mode + ReceiveDutyCycle = 0x06, // receive duty cycle mode + ChannelActivityDetection = 0x07, // channel activity detection mode +} + +impl RadioMode { + /// Returns the value of the mode. + pub const fn value(self) -> u8 { + self as u8 + } + pub fn to_enum(value: u8) -> Self { + if value == 0x00 { + RadioMode::Sleep + } else if value == 0x01 { + RadioMode::StandbyRC + } else if value == 0x02 { + RadioMode::StandbyXOSC + } else if value == 0x03 { + RadioMode::FrequencySynthesis + } else if value == 0x04 { + RadioMode::Transmit + } else if value == 0x05 { + RadioMode::Receive + } else if value == 0x06 { + RadioMode::ReceiveDutyCycle + } else if value == 0x07 { + RadioMode::ChannelActivityDetection + } else { + RadioMode::Sleep + } + } +} + +pub enum RadioState { + Idle = 0x00, + RxRunning = 0x01, + TxRunning = 0x02, + ChannelActivityDetecting = 0x03, +} + +impl RadioState { + /// Returns the value of the state. + pub fn value(self) -> u8 { + self as u8 + } +} + +pub struct RadioStatus { + pub cmd_status: u8, + pub chip_mode: u8, +} + +impl RadioStatus { + pub fn value(self) -> u8 { + (self.chip_mode << 4) | (self.cmd_status << 1) + } +} + +#[derive(Clone, Copy)] +pub enum IrqMask { + None = 0x0000, + TxDone = 0x0001, + RxDone = 0x0002, + PreambleDetected = 0x0004, + SyncwordValid = 0x0008, + HeaderValid = 0x0010, + HeaderError = 0x0020, + CRCError = 0x0040, + CADDone = 0x0080, + CADActivityDetected = 0x0100, + RxTxTimeout = 0x0200, + All = 0xFFFF, +} + +impl IrqMask { + pub fn value(self) -> u16 { + self as u16 + } +} + +#[derive(Clone, Copy)] +pub enum Register { + PacketParams = 0x0704, // packet configuration + PayloadLength = 0x0702, // payload size + SynchTimeout = 0x0706, // recalculated number of symbols + Syncword = 0x06C0, // Syncword values + LoRaSyncword = 0x0740, // LoRa Syncword value + GeneratedRandomNumber = 0x0819, //32-bit generated random number + AnaLNA = 0x08E2, // disable the LNA + AnaMixer = 0x08E5, // disable the mixer + RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted) + XTATrim = 0x0911, // device internal trimming capacitor + OCP = 0x08E7, // over current protection max value + RetentionList = 0x029F, // retention list + IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) + TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) + TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) + RTCCtrl = 0x0902, // RTC control + EvtClr = 0x0944, // event clear +} + +impl Register { + pub fn addr(self) -> u16 { + self as u16 + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum OpCode { + GetStatus = 0xC0, + WriteRegister = 0x0D, + ReadRegister = 0x1D, + WriteBuffer = 0x0E, + ReadBuffer = 0x1E, + SetSleep = 0x84, + SetStandby = 0x80, + SetFS = 0xC1, + SetTx = 0x83, + SetRx = 0x82, + SetRxDutyCycle = 0x94, + SetCAD = 0xC5, + SetTxContinuousWave = 0xD1, + SetTxContinuousPremable = 0xD2, + SetPacketType = 0x8A, + GetPacketType = 0x11, + SetRFFrequency = 0x86, + SetTxParams = 0x8E, + SetPAConfig = 0x95, + SetCADParams = 0x88, + SetBufferBaseAddress = 0x8F, + SetModulationParams = 0x8B, + SetPacketParams = 0x8C, + GetRxBufferStatus = 0x13, + GetPacketStatus = 0x14, + GetRSSIInst = 0x15, + GetStats = 0x10, + ResetStats = 0x00, + CfgDIOIrq = 0x08, + GetIrqStatus = 0x12, + ClrIrqStatus = 0x02, + Calibrate = 0x89, + CalibrateImage = 0x98, + SetRegulatorMode = 0x96, + GetErrors = 0x17, + ClrErrors = 0x07, + SetTCXOMode = 0x97, + SetTxFallbackMode = 0x93, + SetRFSwitchMode = 0x9D, + SetStopRxTimerOnPreamble = 0x9F, + SetLoRaSymbTimeout = 0xA0, +} + +impl OpCode { + pub fn value(self) -> u8 { + self as u8 + } +} + +pub struct SleepParams { + pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC + pub reset: bool, + pub warm_start: bool, +} + +impl SleepParams { + pub fn value(self) -> u8 { + ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8) + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum StandbyMode { + RC = 0x00, + XOSC = 0x01, +} + +impl StandbyMode { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum RegulatorMode { + UseLDO = 0x00, + UseDCDC = 0x01, +} + +impl RegulatorMode { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub struct CalibrationParams { + pub rc64k_enable: bool, // calibrate RC64K clock + pub rc13m_enable: bool, // calibrate RC13M clock + pub pll_enable: bool, // calibrate PLL + pub adc_pulse_enable: bool, // calibrate ADC Pulse + pub adc_bulkn_enable: bool, // calibrate ADC bulkN + pub adc_bulkp_enable: bool, // calibrate ADC bulkP + pub img_enable: bool, +} + +impl CalibrationParams { + pub fn value(self) -> u8 { + ((self.img_enable as u8) << 6) + | ((self.adc_bulkp_enable as u8) << 5) + | ((self.adc_bulkn_enable as u8) << 4) + | ((self.adc_pulse_enable as u8) << 3) + | ((self.pll_enable as u8) << 2) + | ((self.rc13m_enable as u8) << 1) + | ((self.rc64k_enable as u8) << 0) + } +} + +#[derive(Clone, Copy)] +pub enum TcxoCtrlVoltage { + Ctrl1V6 = 0x00, + Ctrl1V7 = 0x01, + Ctrl1V8 = 0x02, + Ctrl2V2 = 0x03, + Ctrl2V4 = 0x04, + Ctrl2V7 = 0x05, + Ctrl3V0 = 0x06, + Ctrl3V3 = 0x07, +} + +impl TcxoCtrlVoltage { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum RampTime { + Ramp10Us = 0x00, + Ramp20Us = 0x01, + Ramp40Us = 0x02, + Ramp80Us = 0x03, + Ramp200Us = 0x04, + Ramp800Us = 0x05, + Ramp1700Us = 0x06, + Ramp3400Us = 0x07, +} + +impl RampTime { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum SpreadingFactor { + _5 = 0x05, + _6 = 0x06, + _7 = 0x07, + _8 = 0x08, + _9 = 0x09, + _10 = 0x0A, + _11 = 0x0B, + _12 = 0x0C, +} + +impl SpreadingFactor { + pub fn value(self) -> u8 { + self as u8 + } +} + +impl From for SpreadingFactor { + fn from(sf: device::SpreadingFactor) -> Self { + match sf { + device::SpreadingFactor::_7 => SpreadingFactor::_7, + device::SpreadingFactor::_8 => SpreadingFactor::_8, + device::SpreadingFactor::_9 => SpreadingFactor::_9, + device::SpreadingFactor::_10 => SpreadingFactor::_10, + device::SpreadingFactor::_11 => SpreadingFactor::_11, + device::SpreadingFactor::_12 => SpreadingFactor::_12, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum Bandwidth { + _500KHz = 0x06, + _250KHz = 0x05, + _125KHz = 0x04, +} + +impl Bandwidth { + pub fn value(self) -> u8 { + self as u8 + } + + pub fn value_in_hz(self) -> u32 { + match self { + Bandwidth::_125KHz => 125000u32, + Bandwidth::_250KHz => 250000u32, + Bandwidth::_500KHz => 500000u32, + } + } +} + +impl From for Bandwidth { + fn from(bw: device::Bandwidth) -> Self { + match bw { + device::Bandwidth::_500KHz => Bandwidth::_500KHz, + device::Bandwidth::_250KHz => Bandwidth::_250KHz, + device::Bandwidth::_125KHz => Bandwidth::_125KHz, + } + } +} + +#[derive(Clone, Copy)] +pub enum CodingRate { + _4_5 = 0x01, + _4_6 = 0x02, + _4_7 = 0x03, + _4_8 = 0x04, +} + +impl CodingRate { + pub fn value(self) -> u8 { + self as u8 + } +} + +impl From for CodingRate { + fn from(cr: device::CodingRate) -> Self { + match cr { + device::CodingRate::_4_5 => CodingRate::_4_5, + device::CodingRate::_4_6 => CodingRate::_4_6, + device::CodingRate::_4_7 => CodingRate::_4_7, + device::CodingRate::_4_8 => CodingRate::_4_8, + } + } +} + +#[derive(Clone, Copy)] +pub struct ModulationParams { + pub spreading_factor: SpreadingFactor, + pub bandwidth: Bandwidth, + pub coding_rate: CodingRate, + pub low_data_rate_optimize: u8, +} + +#[derive(Clone, Copy)] +pub struct PacketParams { + pub preamble_length: u16, // number of LoRa symbols in the preamble + pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length) + pub payload_length: u8, + pub crc_on: bool, + pub iq_inverted: bool, +} + +#[derive(Clone, Copy)] +pub enum CADSymbols { + _1 = 0x00, + _2 = 0x01, + _4 = 0x02, + _8 = 0x03, + _16 = 0x04, +} + +impl CADSymbols { + pub fn value(self) -> u8 { + self as u8 + } +} + +#[derive(Clone, Copy)] +pub enum CADExitMode { + CADOnly = 0x00, + CADRx = 0x01, + CADLBT = 0x10, +} + +impl CADExitMode { + pub fn value(self) -> u8 { + self as u8 + } +} diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs new file mode 100644 index 000000000..02a0a72ec --- /dev/null +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -0,0 +1,688 @@ +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiBus; + +use super::mod_params::*; +use super::LoRa; + +// Internal frequency of the radio +const SX126X_XTAL_FREQ: u32 = 32000000; + +// Scaling factor used to perform fixed-point operations +const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14; + +// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT +const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT); + +// Maximum value for parameter symbNum +const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; + +// Provides board-specific functionality for Semtech SX126x-based boards + +impl LoRa +where + SPI: SpiBus, + CS: OutputPin, + RESET: OutputPin, + ANTRX: OutputPin, + ANTTX: OutputPin, + WAIT: Wait, +{ + // Initialize the radio driver + pub(super) async fn sub_init(&mut self) -> Result<(), RadioError> { + self.brd_reset().await?; + self.brd_wakeup().await?; + self.sub_set_standby(StandbyMode::RC).await?; + self.brd_io_tcxo_init().await?; + self.brd_io_rf_switch_init().await?; + self.image_calibrated = false; + Ok(()) + } + + // Wakeup the radio if it is in Sleep mode and check that Busy is low + pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError> { + let operating_mode = self.brd_get_operating_mode(); + if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { + self.brd_wakeup().await?; + self.brd_ant_sw_on().await?; + } + self.brd_wait_on_busy().await?; + Ok(()) + } + + // Save the payload to be sent in the radio buffer + pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError> { + self.brd_write_buffer(0x00, payload).await?; + Ok(()) + } + + // Read the payload received. + pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result> { + let (size, offset) = self.sub_get_rx_buffer_status().await?; + if (size as usize) > buffer.len() { + Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len())) + } else { + self.brd_read_buffer(offset, buffer).await?; + Ok(size) + } + } + + // Send a payload + pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError> { + self.sub_set_payload(payload).await?; + self.sub_set_tx(timeout).await?; + Ok(()) + } + + // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command. + // + // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of + // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts + // generated during this process will not cause undesired side-effects in the software. + // + // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing. + pub(super) async fn sub_get_random(&mut self) -> Result> { + let mut reg_ana_lna_buffer_original = [0x00u8]; + let mut reg_ana_mixer_buffer_original = [0x00u8]; + let mut reg_ana_lna_buffer = [0x00u8]; + let mut reg_ana_mixer_buffer = [0x00u8]; + let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8]; + + self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original) + .await?; + reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0)); + self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?; + + self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original) + .await?; + reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7)); + self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer) + .await?; + + // Set radio in continuous reception + self.sub_set_rx(0xFFFFFFu32).await?; + + self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer) + .await?; + + self.sub_set_standby(StandbyMode::RC).await?; + + self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original) + .await?; + self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original) + .await?; + + Ok(Self::convert_u8_buffer_to_u32(&number_buffer)) + } + + // Set the radio in sleep mode + pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { + self.brd_ant_sw_off().await?; + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_low(); + + if !sleep_config.warm_start { + self.image_calibrated = false; + } + + self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()]) + .await?; + self.brd_set_operating_mode(RadioMode::Sleep); + Ok(()) + } + + // Set the radio in configuration mode + pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?; + if mode == StandbyMode::RC { + self.brd_set_operating_mode(RadioMode::StandbyRC); + } else { + self.brd_set_operating_mode(RadioMode::StandbyXOSC); + } + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_low(); + Ok(()) + } + + // Set the radio in FS mode + pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError> { + // antenna settings ??? + self.brd_write_command(OpCode::SetFS, &[]).await?; + self.brd_set_operating_mode(RadioMode::FrequencySynthesis); + Ok(()) + } + + // Set the radio in transmission mode with timeout specified + pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_set_operating_mode(RadioMode::Transmit); + self.brd_write_command(OpCode::SetTx, &buffer).await?; + Ok(()) + } + + // Set the radio in reception mode with timeout specified + pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_set_operating_mode(RadioMode::Receive); + self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; + self.brd_write_command(OpCode::SetRx, &buffer).await?; + Ok(()) + } + + // Set the radio in reception mode with Boosted LNA gain and timeout specified + pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { + let buffer = [ + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_set_operating_mode(RadioMode::Receive); + // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity + self.brd_write_registers(Register::RxGain, &[0x96u8]).await?; + self.brd_write_command(OpCode::SetRx, &buffer).await?; + Ok(()) + } + + // Set the Rx duty cycle management parameters + pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { + let buffer = [ + ((rx_time >> 16) & 0xFF) as u8, + ((rx_time >> 8) & 0xFF) as u8, + (rx_time & 0xFF) as u8, + ((sleep_time >> 16) & 0xFF) as u8, + ((sleep_time >> 8) & 0xFF) as u8, + (sleep_time & 0xFF) as u8, + ]; + + // antenna settings ??? + + self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?; + self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle); + Ok(()) + } + + // Set the radio in CAD mode + pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_high(); + let _fix_tx = self.antenna_tx.set_low(); + + self.brd_write_command(OpCode::SetCAD, &[]).await?; + self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); + Ok(()) + } + + // Set the radio in continuous wave transmission mode + pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; + self.brd_set_operating_mode(RadioMode::Transmit); + Ok(()) + } + + // Set the radio in continuous preamble transmission mode + pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { + let _fix_rx = self.antenna_rx.set_low(); + let _fix_tx = self.antenna_tx.set_high(); + + self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; + self.brd_set_operating_mode(RadioMode::Transmit); + Ok(()) + } + + // Decide which interrupt will stop the internal radio rx timer. + // false timer stop after header/syncword detection + // true timer stop after preamble detection + pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect( + &mut self, + enable: bool, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8]) + .await?; + Ok(()) + } + + // Set the number of symbols the radio will wait to validate a reception + pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError> { + let mut exp = 0u8; + let mut reg; + let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1; + while mant > 31 { + mant = (mant + 3) >> 2; + exp += 1; + } + reg = mant << ((2 * exp) + 1); + + self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?; + + if symb_num != 0 { + reg = exp + (mant << 3); + self.brd_write_registers(Register::SynchTimeout, &[reg]).await?; + } + + Ok(()) + } + + // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled + pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()]) + .await?; + Ok(()) + } + + // Calibrate the given radio block + pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError> { + self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()]) + .await?; + Ok(()) + } + + // Calibrate the image rejection based on the given frequency + pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError> { + let mut cal_freq = [0x00u8, 0x00u8]; + + if freq > 900000000 { + cal_freq[0] = 0xE1; + cal_freq[1] = 0xE9; + } else if freq > 850000000 { + cal_freq[0] = 0xD7; + cal_freq[1] = 0xDB; + } else if freq > 770000000 { + cal_freq[0] = 0xC1; + cal_freq[1] = 0xC5; + } else if freq > 460000000 { + cal_freq[0] = 0x75; + cal_freq[1] = 0x81; + } else if freq > 425000000 { + cal_freq[0] = 0x6B; + cal_freq[1] = 0x6F; + } + self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?; + Ok(()) + } + + // Activate the extention of the timeout when a long preamble is used + pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError> { + Ok(()) // no operation currently + } + + // Set the transmission parameters + // hp_max 0 for sx1261, 7 for sx1262 + // device_sel 1 for sx1261, 0 for sx1262 + // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT + pub(super) async fn sub_set_pa_config( + &mut self, + pa_duty_cycle: u8, + hp_max: u8, + device_sel: u8, + pa_lut: u8, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut]) + .await?; + Ok(()) + } + + // Define into which mode the chip goes after a TX / RX done + pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode]) + .await?; + Ok(()) + } + + // Set the IRQ mask and DIO masks + pub(super) async fn sub_set_dio_irq_params( + &mut self, + irq_mask: u16, + dio1_mask: u16, + dio2_mask: u16, + dio3_mask: u16, + ) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 8]; + + buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8; + buffer[1] = (irq_mask & 0x00FF) as u8; + buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8; + buffer[3] = (dio1_mask & 0x00FF) as u8; + buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8; + buffer[5] = (dio2_mask & 0x00FF) as u8; + buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8; + buffer[7] = (dio3_mask & 0x00FF) as u8; + self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?; + Ok(()) + } + + // Return the current IRQ status + pub(super) async fn sub_get_irq_status(&mut self) -> Result> { + let mut irq_status = [0x00u8, 0x00u8]; + self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?; + Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16)) + } + + // Indicate if DIO2 is used to control an RF Switch + pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?; + Ok(()) + } + + // Indicate if the radio main clock is supplied from a TCXO + // tcxo_voltage voltage used to control the TCXO on/off from DIO3 + // timeout duration given to the TCXO to go to 32MHz + pub(super) async fn sub_set_dio3_as_tcxo_ctrl( + &mut self, + tcxo_voltage: TcxoCtrlVoltage, + timeout: u32, + ) -> Result<(), RadioError> { + let buffer = [ + tcxo_voltage.value() & 0x07, + Self::timeout_1(timeout), + Self::timeout_2(timeout), + Self::timeout_3(timeout), + ]; + self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?; + + Ok(()) + } + + // Set the RF frequency (Hz) + pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 4]; + + if !self.image_calibrated { + self.sub_calibrate_image(frequency).await?; + self.image_calibrated = true; + } + + let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency); + + buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8; + buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8; + buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8; + buffer[3] = (freq_in_pll_steps & 0xFF) as u8; + self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?; + Ok(()) + } + + // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters. + pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError> { + self.packet_type = packet_type; + self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()]) + .await?; + Ok(()) + } + + // Get the current radio protocol (LoRa or GFSK) + pub(super) fn sub_get_packet_type(&mut self) -> PacketType { + self.packet_type + } + + // Set the transmission parameters + // power RF output power [-18..13] dBm + // ramp_time transmission ramp up time + pub(super) async fn sub_set_tx_params( + &mut self, + mut power: i8, + ramp_time: RampTime, + ) -> Result<(), RadioError> { + if self.brd_get_radio_type() == RadioType::SX1261 { + if power == 15 { + self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?; + } else { + self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?; + } + + if power >= 14 { + power = 14; + } else if power < -17 { + power = -17; + } + } else { + // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) + let mut tx_clamp_cfg = [0x00u8]; + self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?; + tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1); + self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?; + + self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?; + + if power > 22 { + power = 22; + } else if power < -9 { + power = -9; + } + } + + // power conversion of negative number from i8 to u8 ??? + self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()]) + .await?; + Ok(()) + } + + // Set the modulation parameters + pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError> { + if self.modulation_params.is_some() { + let mut buffer = [0x00u8; 4]; + + // Since this driver only supports LoRa, ensure the packet type is set accordingly + self.sub_set_packet_type(PacketType::LoRa).await?; + + let modulation_params = self.modulation_params.unwrap(); + buffer[0] = modulation_params.spreading_factor.value(); + buffer[1] = modulation_params.bandwidth.value(); + buffer[2] = modulation_params.coding_rate.value(); + buffer[3] = modulation_params.low_data_rate_optimize; + + self.brd_write_command(OpCode::SetModulationParams, &buffer).await?; + Ok(()) + } else { + Err(RadioError::ModulationParamsMissing) + } + } + + // Set the packet parameters + pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError> { + if self.packet_params.is_some() { + let mut buffer = [0x00u8; 6]; + + // Since this driver only supports LoRa, ensure the packet type is set accordingly + self.sub_set_packet_type(PacketType::LoRa).await?; + + let packet_params = self.packet_params.unwrap(); + buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8; + buffer[1] = (packet_params.preamble_length & 0xFF) as u8; + buffer[2] = packet_params.implicit_header as u8; + buffer[3] = packet_params.payload_length; + buffer[4] = packet_params.crc_on as u8; + buffer[5] = packet_params.iq_inverted as u8; + + self.brd_write_command(OpCode::SetPacketParams, &buffer).await?; + Ok(()) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + // Set the channel activity detection (CAD) parameters + // symbols number of symbols to use for CAD operations + // det_peak limit for detection of SNR peak used in the CAD + // det_min minimum symbol recognition for CAD + // exit_mode operation to be done at the end of CAD action + // timeout timeout value to abort the CAD activity + + pub(super) async fn sub_set_cad_params( + &mut self, + symbols: CADSymbols, + det_peak: u8, + det_min: u8, + exit_mode: CADExitMode, + timeout: u32, + ) -> Result<(), RadioError> { + let mut buffer = [0x00u8; 7]; + + buffer[0] = symbols.value(); + buffer[1] = det_peak; + buffer[2] = det_min; + buffer[3] = exit_mode.value(); + buffer[4] = Self::timeout_1(timeout); + buffer[5] = Self::timeout_2(timeout); + buffer[6] = Self::timeout_3(timeout); + + self.brd_write_command(OpCode::SetCADParams, &buffer).await?; + self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); + Ok(()) + } + + // Set the data buffer base address for transmission and reception + pub(super) async fn sub_set_buffer_base_address( + &mut self, + tx_base_address: u8, + rx_base_address: u8, + ) -> Result<(), RadioError> { + self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address]) + .await?; + Ok(()) + } + + // Get the current radio status + pub(super) async fn sub_get_status(&mut self) -> Result> { + let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?; + Ok(RadioStatus { + cmd_status: (status & (0x07 << 1)) >> 1, + chip_mode: (status & (0x07 << 4)) >> 4, + }) + } + + // Get the instantaneous RSSI value for the last packet received + pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { + let mut buffer = [0x00u8]; + self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; + let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ??? + Ok(rssi) + } + + // Get the last received packet buffer status + pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError> { + if self.packet_params.is_some() { + let mut status = [0x00u8; 2]; + let mut payload_length_buffer = [0x00u8]; + + self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?; + if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header { + self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer) + .await?; + } else { + payload_length_buffer[0] = status[0]; + } + + let payload_length = payload_length_buffer[0]; + let offset = status[1]; + + Ok((payload_length, offset)) + } else { + Err(RadioError::PacketParamsMissing) + } + } + + // Get the last received packet payload status + pub(super) async fn sub_get_packet_status(&mut self) -> Result> { + let mut status = [0x00u8; 3]; + self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; + + // check this ??? + let rssi = (-(status[0] as i8)) >> 1; + let snr = ((status[1] as i8) + 2) >> 2; + let signal_rssi = (-(status[2] as i8)) >> 1; + let freq_error = self.frequency_error; + + Ok(PacketStatus { + rssi, + snr, + signal_rssi, + freq_error, + }) + } + + // Get the possible system errors + pub(super) async fn sub_get_device_errors(&mut self) -> Result> { + let mut errors = [0x00u8; 2]; + self.brd_read_command(OpCode::GetErrors, &mut errors).await?; + + Ok(RadioSystemError { + rc_64khz_calibration: (errors[1] & (1 << 0)) != 0, + rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0, + pll_calibration: (errors[1] & (1 << 2)) != 0, + adc_calibration: (errors[1] & (1 << 3)) != 0, + image_calibration: (errors[1] & (1 << 4)) != 0, + xosc_start: (errors[1] & (1 << 5)) != 0, + pll_lock: (errors[1] & (1 << 6)) != 0, + pa_ramp: (errors[0] & (1 << 0)) != 0, + }) + } + + // Clear all the errors in the device + pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError> { + self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?; + Ok(()) + } + + // Clear the IRQs + pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError> { + let mut buffer = [0x00u8, 0x00u8]; + buffer[0] = ((irq >> 8) & 0xFF) as u8; + buffer[1] = (irq & 0xFF) as u8; + self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?; + Ok(()) + } + + // Utility functions + + fn timeout_1(timeout: u32) -> u8 { + ((timeout >> 16) & 0xFF) as u8 + } + fn timeout_2(timeout: u32) -> u8 { + ((timeout >> 8) & 0xFF) as u8 + } + fn timeout_3(timeout: u32) -> u8 { + (timeout & 0xFF) as u8 + } + + // check this ??? + fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 { + let b0 = buffer[0] as u32; + let b1 = buffer[1] as u32; + let b2 = buffer[2] as u32; + let b3 = buffer[3] as u32; + (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 + } + + fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 { + // Get integer and fractional parts of the frequency computed with a PLL step scaled value + let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED; + let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED); + + (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT) + + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED) + } +} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a5d340c69..54df16ba0 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,7 +5,8 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", + "embassy-lora", "lorawan-device", "lorawan"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -16,6 +17,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm 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.0" +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["rak4631", "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" diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs new file mode 100644 index 000000000..46cb848b1 --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -0,0 +1,84 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +#![no_std] +#![no_main] +#![macro_use] +#![allow(dead_code)] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::sx126x::*; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::{interrupt, spim}; +use embassy_time::{Duration, Timer}; +use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M1; // M16 ??? + + let mut radio = { + let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + + match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { + Ok(r) => r, + Err(err) => { + info!("Sx126xRadio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + let rf_config = RfConfig { + frequency: 903900000, // channel in Hz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }; + + let mut buffer = [00u8; 100]; + + // P2P receive + match radio.rx(rf_config, &mut buffer).await { + Ok((buffer_len, rx_quality)) => info!( + "RX received = {:?} with length = {} rssi = {} snr = {}", + &buffer[0..buffer_len], + buffer_len, + rx_quality.rssi(), + rx_quality.snr() + ), + Err(err) => info!("RX error = {}", err), + } + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); +} diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs new file mode 100644 index 000000000..57aaea665 --- /dev/null +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -0,0 +1,173 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] +#![feature(alloc_error_handler)] +#![allow(incomplete_features)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::sx126x::*; +use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::temp::Temp; +use embassy_nrf::{interrupt, spim}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pubsub::{PubSubChannel, Publisher}; +use embassy_time::{Duration, Timer}; +use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; +use {defmt_rtt as _, panic_probe as _, panic_probe as _}; + +// Sensor packet constants +const TEMPERATURE_UID: u8 = 0x01; +const MOTION_UID: u8 = 0x02; + +// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) +static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); + +#[derive(Clone, defmt::Format)] +enum Message { + Temperature(i32), + MotionDetected, +} + +#[embassy_executor::task] +async fn temperature_task( + mut temperature: Temp<'static>, + publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, +) { + Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds + + let mut temperature_reporting_threshhold = 10; + + loop { + let value = temperature.read().await; + let mut temperature_val = value.to_num::(); + + info!("Temperature: {}", temperature_val); // debug ??? + + // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report + + if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 { + temperature_val += 1; + } + + if temperature_reporting_threshhold > temperature_val + && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5) + { + temperature_reporting_threshhold = temperature_val; + publisher.publish(Message::Temperature(temperature_val)).await; + } else if temperature_val <= 3 { + publisher.publish(Message::Temperature(temperature_val)).await; + } + + Timer::after(Duration::from_secs(20 * 60)).await; + } +} + +#[embassy_executor::task] +async fn motion_detection_task( + mut pir_pin: Input<'static, AnyPin>, + publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, +) { + Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds + + loop { + // wait for motion detection + pir_pin.wait_for_low().await; + publisher.publish(Message::MotionDetected).await; + + // wait a minute before setting up for more motion detection + Timer::after(Duration::from_secs(60)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + // set up to funnel temperature and motion detection events to the Lora Tx task + let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber()); + let temperature_publisher = unwrap!(MESSAGE_BUS.publisher()); + let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); + + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M1; // M16 ??? + + let mut radio = { + let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); + let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + + match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { + Ok(r) => r, + Err(err) => { + info!("Sx126xRadio error = {}", err); + return; + } + } + }; + + // set up for the temperature task + let temperature_irq = interrupt::take!(TEMP); + let temperature = Temp::new(p.TEMP, temperature_irq); + + // set the motion detection pin + let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up); + + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher))); + unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher))); + + loop { + let message = lora_tx_subscriber.next_message_pure().await; + + let tx_config = TxConfig { + // 11 byte maximum payload for Bandwidth 125 and SF 10 + pw: 20, // up to 20 // 5 ??? + rf: RfConfig { + frequency: 903900000, // channel in Hz, not MHz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }, + }; + + let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8]; + match message { + Message::Temperature(temperature) => buffer[1] = temperature as u8, + Message::MotionDetected => buffer[3] = 0x01u8, + }; + + // crypto for text ??? + match radio.tx(tx_config, &buffer).await { + Ok(ret_val) => info!("TX ret_val = {}", ret_val), + Err(err) => info!("TX error = {}", err), + } + + match radio.lora.sleep().await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } +} From 823bd714fb6da94cf3b31c2066c398207228b4c6 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 28 Sep 2022 05:19:43 +0200 Subject: [PATCH 0203/1575] Add E-H1 uart blocking & nb implementation --- embassy-stm32/src/usart/mod.rs | 73 ++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6c2668748..5ee099348 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -359,6 +359,79 @@ mod eh1 { impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { type Error = Error; } + + + impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::nb::Read for UartRx<'d, T, RxDma> { + fn read(&mut self) -> nb::Result { + let r = T::regs(); + unsafe { + let sr = sr(r).read(); + if sr.pe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Parity)) + } else if sr.fe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Framing)) + } else if sr.ne() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Noise)) + } else if sr.ore() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Overrun)) + } else if sr.rxne() { + Ok(rdr(r).read_volatile()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, TxDma> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::nb::Write for UartTx<'d, T, TxDma> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Read for Uart<'d, T, TxDma, RxDma> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::blocking::Write for Uart<'d, T, TxDma, RxDma> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Write for Uart<'d, T, TxDma, RxDma> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } } #[cfg(all( From d7f7614b225da41477972b5fd287d10e6069846c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 28 Sep 2022 11:32:11 +0200 Subject: [PATCH 0204/1575] Remove subghz static lifetime requirement --- embassy-lora/src/stm32wl/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index e28fa2c1a..8d5d19531 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -234,15 +234,15 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf Ok(()) } -impl PhyRxTx for SubGhzRadio<'static, RS> { +impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm where RS: 'm; + type TxFuture<'m> = impl Future> + 'm where Self: 'm; fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { async move { self.do_tx(config, buf).await } } - type RxFuture<'m> = impl Future> + 'm where RS: 'm; + type RxFuture<'m> = impl Future> + 'm where Self: 'm; fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { async move { self.do_rx(config, buf).await } } From 526e90d3f3b986fedc55ec421178b7b2ec954abf Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 28 Sep 2022 14:27:34 -0500 Subject: [PATCH 0205/1575] Update some outstanding questions --- embassy-lora/src/sx126x/sx126x_lora/mod.rs | 18 +++++++++--------- examples/nrf/src/bin/lora_p2p_sense.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs index 02f5f83a2..53fbde749 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -511,22 +511,23 @@ where cad_activity_detected: Option<&mut bool>, ) -> Result<(), RadioError> { loop { - info!("process_irq loop entered"); // debug ??? + trace!("process_irq loop entered"); let de = self.sub_get_device_errors().await?; - info!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", + trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); let st = self.sub_get_status().await?; - info!( + trace!( "radio status: cmd_status: {:x}, chip_mode: {:x}", - st.cmd_status, st.chip_mode + st.cmd_status, + st.chip_mode ); self.dio1.wait_for_high().await.map_err(|_| DIO1)?; let operating_mode = self.brd_get_operating_mode(); let irq_flags = self.sub_get_irq_status().await?; self.sub_clear_irq_status(irq_flags).await?; - info!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // debug ??? + trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); // check for errors and unexpected interrupt masks (based on operation mode) if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { @@ -568,13 +569,12 @@ where return Err(RadioError::CADUnexpected); } - // debug ??? if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { - info!("HeaderValid"); + trace!("HeaderValid"); } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { - info!("PreambleDetected"); + trace!("PreambleDetected"); } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { - info!("SyncwordValid"); + trace!("SyncwordValid"); } // handle completions diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 57aaea665..3c6bb8767 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -44,7 +44,7 @@ async fn temperature_task( let value = temperature.read().await; let mut temperature_val = value.to_num::(); - info!("Temperature: {}", temperature_val); // debug ??? + info!("Temperature: {}", temperature_val); // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report From a83560c6b166e3f354471a2e1cf1c52a6d2be59b Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 29 Sep 2022 07:49:32 +0200 Subject: [PATCH 0206/1575] Implement RTC peripheral for all stm32 families with rtc --- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/rtc/datetime_chrono.rs | 85 ++++++++ embassy-stm32/src/rtc/datetime_no_deps.rs | 146 +++++++++++++ embassy-stm32/src/rtc/mod.rs | 238 ++++++++++++++++++++++ embassy-stm32/src/rtc/v2/mod.rs | 171 ++++++++++++++++ embassy-stm32/src/rtc/v2/v2f0.rs | 41 ++++ embassy-stm32/src/rtc/v2/v2f2.rs | 31 +++ embassy-stm32/src/rtc/v2/v2f3.rs | 31 +++ embassy-stm32/src/rtc/v2/v2f4.rs | 31 +++ embassy-stm32/src/rtc/v2/v2f7.rs | 41 ++++ embassy-stm32/src/rtc/v2/v2h7.rs | 33 +++ embassy-stm32/src/rtc/v2/v2l0.rs | 40 ++++ embassy-stm32/src/rtc/v2/v2l1.rs | 40 ++++ embassy-stm32/src/rtc/v2/v2l4.rs | 41 ++++ embassy-stm32/src/rtc/v2/v2wb.rs | 39 ++++ embassy-stm32/src/rtc/v3.rs | 212 +++++++++++++++++++ 17 files changed, 1223 insertions(+) create mode 100644 embassy-stm32/src/rtc/datetime_chrono.rs create mode 100644 embassy-stm32/src/rtc/datetime_no_deps.rs create mode 100644 embassy-stm32/src/rtc/mod.rs create mode 100644 embassy-stm32/src/rtc/v2/mod.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f0.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f2.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f3.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f4.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f7.rs create mode 100644 embassy-stm32/src/rtc/v2/v2h7.rs create mode 100644 embassy-stm32/src/rtc/v2/v2l0.rs create mode 100644 embassy-stm32/src/rtc/v2/v2l1.rs create mode 100644 embassy-stm32/src/rtc/v2/v2l4.rs create mode 100644 embassy-stm32/src/rtc/v2/v2wb.rs create mode 100644 embassy-stm32/src/rtc/v3.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a4a232f51..a3ce9d996 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -66,6 +66,7 @@ stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.3.0", features = ["async"], optional = true } +chrono = { version = "^0.4", default-features = false, optional = true} [build-dependencies] proc-macro2 = "1.0.36" diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0392e8086..906980ac4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -46,6 +46,8 @@ pub mod flash; pub mod pwm; #[cfg(rng)] pub mod rng; +#[cfg(all(rtc, not(rtc_v1)))] +pub mod rtc; #[cfg(sdmmc)] pub mod sdmmc; #[cfg(spi)] diff --git a/embassy-stm32/src/rtc/datetime_chrono.rs b/embassy-stm32/src/rtc/datetime_chrono.rs new file mode 100644 index 000000000..b46316cc9 --- /dev/null +++ b/embassy-stm32/src/rtc/datetime_chrono.rs @@ -0,0 +1,85 @@ +use chrono::{Datelike, Timelike}; + +use super::byte_to_bcd2; +use crate::pac::rtc::Rtc; + +/// Alias for [`chrono::NaiveDateTime`] +pub type DateTime = chrono::NaiveDateTime; +/// Alias for [`chrono::Weekday`] +pub type DayOfWeek = chrono::Weekday; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] has an invalid year. The year must be between 0 and 4095. + InvalidYear, + /// The [DateTime] contains an invalid date. + InvalidDate, + /// The [DateTime] contains an invalid time. + InvalidTime, +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw.num_days_from_monday() as u8 +} + +pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year() < 0 || dt.year() > 4095 { + // rp2040 can't hold these years + Err(Error::InvalidYear) + } else { + // The rest of the chrono date is assumed to be valid + Ok(()) + } +} + +pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) { + let (ht, hu) = byte_to_bcd2(t.hour() as u8); + let (mnt, mnu) = byte_to_bcd2(t.minute() as u8); + let (st, su) = byte_to_bcd2(t.second() as u8); + + let (dt, du) = byte_to_bcd2(t.day() as u8); + let (mt, mu) = byte_to_bcd2(t.month() as u8); + let yr = t.year() as u16; + let yr_offset = (yr - 1970_u16) as u8; + let (yt, yu) = byte_to_bcd2(yr_offset); + + unsafe { + rtc.tr().write(|w| { + w.set_ht(ht); + w.set_hu(hu); + w.set_mnt(mnt); + w.set_mnu(mnu); + w.set_st(st); + w.set_su(su); + w.set_pm(stm32_metapac::rtc::vals::Ampm::AM); + }); + + rtc.dr().write(|w| { + w.set_dt(dt); + w.set_du(du); + w.set_mt(mt > 0); + w.set_mu(mu); + w.set_yt(yt); + w.set_yu(yu); + w.set_wdu(day_of_week_to_u8(t.weekday())); + }); + } +} + +pub(super) fn datetime( + year: u16, + month: u8, + day: u8, + _day_of_week: u8, + hour: u8, + minute: u8, + second: u8, +) -> Result { + let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into()) + .ok_or(Error::InvalidDate)?; + let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?; + Ok(DateTime::new(date, time)) +} diff --git a/embassy-stm32/src/rtc/datetime_no_deps.rs b/embassy-stm32/src/rtc/datetime_no_deps.rs new file mode 100644 index 000000000..173f38377 --- /dev/null +++ b/embassy-stm32/src/rtc/datetime_no_deps.rs @@ -0,0 +1,146 @@ +use super::byte_to_bcd2; +use crate::pac::rtc::Rtc; + +/// Errors regarding the [`DateTime`] struct. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. + InvalidYear, + /// The [DateTime] contains an invalid month value. Must be between `1..=12`. + InvalidMonth, + /// The [DateTime] contains an invalid day value. Must be between `1..=31`. + InvalidDay, + /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday. + InvalidDayOfWeek( + /// The value of the DayOfWeek that was given. + u8, + ), + /// The [DateTime] contains an invalid hour value. Must be between `0..=23`. + InvalidHour, + /// The [DateTime] contains an invalid minute value. Must be between `0..=59`. + InvalidMinute, + /// The [DateTime] contains an invalid second value. Must be between `0..=59`. + InvalidSecond, +} + +/// Structure containing date and time information +pub struct DateTime { + /// 0..4095 + pub year: u16, + /// 1..12, 1 is January + pub month: u8, + /// 1..28,29,30,31 depending on month + pub day: u8, + /// + pub day_of_week: DayOfWeek, + /// 0..23 + pub hour: u8, + /// 0..59 + pub minute: u8, + /// 0..59 + pub second: u8, +} + +/// A day of the week +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[allow(missing_docs)] +pub enum DayOfWeek { + Monday = 0, + Tuesday = 1, + Wednesday = 2, + Thursday = 3, + Friday = 4, + Saturday = 5, + Sunday = 6, +} + +fn day_of_week_from_u8(v: u8) -> Result { + Ok(match v { + 0 => DayOfWeek::Monday, + 1 => DayOfWeek::Tuesday, + 2 => DayOfWeek::Wednesday, + 3 => DayOfWeek::Thursday, + 4 => DayOfWeek::Friday, + 5 => DayOfWeek::Saturday, + 6 => DayOfWeek::Sunday, + x => return Err(Error::InvalidDayOfWeek(x)), + }) +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw as u8 +} + +pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year > 4095 { + Err(Error::InvalidYear) + } else if dt.month < 1 || dt.month > 12 { + Err(Error::InvalidMonth) + } else if dt.day < 1 || dt.day > 31 { + Err(Error::InvalidDay) + } else if dt.hour > 23 { + Err(Error::InvalidHour) + } else if dt.minute > 59 { + Err(Error::InvalidMinute) + } else if dt.second > 59 { + Err(Error::InvalidSecond) + } else { + Ok(()) + } +} + +pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) { + let (ht, hu) = byte_to_bcd2(t.hour as u8); + let (mnt, mnu) = byte_to_bcd2(t.minute as u8); + let (st, su) = byte_to_bcd2(t.second as u8); + + let (dt, du) = byte_to_bcd2(t.day as u8); + let (mt, mu) = byte_to_bcd2(t.month as u8); + let yr = t.year as u16; + let yr_offset = (yr - 1970_u16) as u8; + let (yt, yu) = byte_to_bcd2(yr_offset); + + unsafe { + rtc.tr().write(|w| { + w.set_ht(ht); + w.set_hu(hu); + w.set_mnt(mnt); + w.set_mnu(mnu); + w.set_st(st); + w.set_su(su); + w.set_pm(stm32_metapac::rtc::vals::Ampm::AM); + }); + + rtc.dr().write(|w| { + w.set_dt(dt); + w.set_du(du); + w.set_mt(mt > 0); + w.set_mu(mu); + w.set_yt(yt); + w.set_yu(yu); + w.set_wdu(day_of_week_to_u8(t.day_of_week)); + }); + } +} + +pub(super) fn datetime( + year: u16, + month: u8, + day: u8, + day_of_week: u8, + hour: u8, + minute: u8, + second: u8, +) -> Result { + let day_of_week = day_of_week_from_u8(day_of_week)?; + Ok(DateTime { + year, + month, + day, + day_of_week, + hour, + minute, + second, + }) +} diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs new file mode 100644 index 000000000..ee3349b27 --- /dev/null +++ b/embassy-stm32/src/rtc/mod.rs @@ -0,0 +1,238 @@ +//! RTC peripheral abstraction +use core::marker::PhantomData; + +#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")] +#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")] +mod datetime; + +pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; + +/// refer to AN4759 to compare features of RTC2 and RTC3 +#[cfg_attr(any(rtc_v1), path = "v1.rs")] +#[cfg_attr( + any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ), + path = "v2/mod.rs" +)] +#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] +mod versions; +use embassy_hal_common::Peripheral; +pub use versions::*; + +/// Errors that can occur on methods on [RtcClock] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RtcError { + /// An invalid DateTime was given or stored on the hardware. + InvalidDateTime(DateTimeError), + + /// The RTC clock is not running + NotRunning, +} + +/// RTC Abstraction +pub struct Rtc<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + rtc_config: RtcConfig, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum RtcClockSource { + /// 00: No clock + NoClock = 0b00, + /// 01: LSE oscillator clock used as RTC clock + LSE = 0b01, + /// 10: LSI oscillator clock used as RTC clock + LSI = 0b10, + /// 11: HSE oscillator clock divided by 32 used as RTC clock + HSE = 0b11, +} + +#[derive(Copy, Clone, PartialEq)] +pub struct RtcConfig { + /// RTC clock source + clock_config: RtcClockSource, + /// Asynchronous prescaler factor + /// This is the asynchronous division factor: + /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) + /// ck_apre drives the subsecond register + async_prescaler: u8, + /// Synchronous prescaler factor + /// This is the synchronous division factor: + /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1) + /// ck_spre must be 1Hz + sync_prescaler: u16, +} + +impl Default for RtcConfig { + /// LSI with prescalers assuming 32.768 kHz. + /// Raw sub-seconds in 1/256. + fn default() -> Self { + RtcConfig { + clock_config: RtcClockSource::LSI, + async_prescaler: 127, + sync_prescaler: 255, + } + } +} + +impl RtcConfig { + /// Sets the clock source of RTC config + pub fn clock_config(mut self, cfg: RtcClockSource) -> Self { + self.clock_config = cfg; + self + } + + /// Set the asynchronous prescaler of RTC config + pub fn async_prescaler(mut self, prescaler: u8) -> Self { + self.async_prescaler = prescaler; + self + } + + /// Set the synchronous prescaler of RTC config + pub fn sync_prescaler(mut self, prescaler: u16) -> Self { + self.sync_prescaler = prescaler; + self + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum RtcCalibrationCyclePeriod { + /// 8-second calibration period + Seconds8, + /// 16-second calibration period + Seconds16, + /// 32-second calibration period + Seconds32, +} + +impl Default for RtcCalibrationCyclePeriod { + fn default() -> Self { + RtcCalibrationCyclePeriod::Seconds32 + } +} + +impl<'d, T: Instance> Rtc<'d, T> { + pub fn new(_rtc: impl Peripheral

+ 'd, rtc_config: RtcConfig) -> Self { + unsafe { enable_peripheral_clk() }; + + let mut rtc_struct = Self { + phantom: PhantomData, + rtc_config, + }; + + rtc_struct.apply_config(rtc_config); + + rtc_struct + } + + /// Set the datetime to a new value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> { + self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; + self.write(true, |rtc| self::datetime::write_date_time(rtc, t)); + + Ok(()) + } + + /// Return the current datetime. + /// + /// # Errors + /// + /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. + pub fn now(&self) -> Result { + let r = T::regs(); + unsafe { + let tr = r.tr().read(); + let second = bcd2_to_byte((tr.st(), tr.su())); + let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); + let hour = bcd2_to_byte((tr.ht(), tr.hu())); + // Reading either RTC_SSR or RTC_TR locks the values in the higher-order + // calendar shadow registers until RTC_DR is read. + let dr = r.dr().read(); + + let weekday = dr.wdu(); + let day = bcd2_to_byte((dr.dt(), dr.du())); + let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); + let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; + + self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) + } + } + + /// Check if daylight savings time is active. + pub fn get_daylight_savings(&self) -> bool { + let cr = unsafe { T::regs().cr().read() }; + cr.bkp() + } + + /// Enable/disable daylight savings time. + pub fn set_daylight_savings(&mut self, daylight_savings: bool) { + self.write(true, |rtc| { + unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) }; + }) + } + + pub fn get_config(&self) -> RtcConfig { + self.rtc_config + } + + pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT; + + /// Read content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + pub fn read_backup_register(&self, register: usize) -> Option { + read_backup_register(&T::regs(), register) + } + + /// Set content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + pub fn write_backup_register(&self, register: usize, value: u32) { + write_backup_register(&T::regs(), register, value) + } +} + +pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) { + let mut bcd_high: u8 = 0; + let mut value = byte; + + while value >= 10 { + bcd_high += 1; + value -= 10; + } + + (bcd_high, ((bcd_high << 4) | value) as u8) +} + +pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 { + let value = bcd.1 | bcd.0 << 4; + + let tmp = ((value & 0xF0) >> 0x4) * 10; + + tmp + (value & 0x0F) +} + +pub(crate) mod sealed { + pub trait Instance { + fn regs() -> crate::pac::rtc::Rtc; + } +} + +pub trait Instance: sealed::Instance + 'static {} + +impl sealed::Instance for crate::peripherals::RTC { + fn regs() -> crate::pac::rtc::Rtc { + crate::pac::RTC + } +} + +impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/rtc/v2/mod.rs b/embassy-stm32/src/rtc/v2/mod.rs new file mode 100644 index 000000000..296adae89 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/mod.rs @@ -0,0 +1,171 @@ +use stm32_metapac::rtc::vals::{Init, Osel, Pol}; + +use super::{Instance, RtcConfig}; +use crate::pac::rtc::Rtc; + +#[cfg_attr(rtc_v2f0, path = "v2f0.rs")] +#[cfg_attr(rtc_v2f2, path = "v2f2.rs")] +#[cfg_attr(rtc_v2f3, path = "v2f3.rs")] +#[cfg_attr(rtc_v2f4, path = "v2f4.rs")] +#[cfg_attr(rtc_v2f7, path = "v2f7.rs")] +#[cfg_attr(rtc_v2h7, path = "v2h7.rs")] +#[cfg_attr(rtc_v2l0, path = "v2l0.rs")] +#[cfg_attr(rtc_v2l1, path = "v2l1.rs")] +#[cfg_attr(rtc_v2l4, path = "v2l4.rs")] +#[cfg_attr(rtc_v2wb, path = "v2wb.rs")] +mod family; + +pub use family::*; + +impl<'d, T: Instance> super::Rtc<'d, T> { + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { + // Unlock the backup domain + unsafe { + unlock_backup_domain(rtc_config.clock_config as u8); + } + + self.write(true, |rtc| unsafe { + rtc.cr().modify(|w| { + #[cfg(rtc_v2f2)] + w.set_fmt(false); + #[cfg(not(rtc_v2f2))] + w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); + w.set_osel(Osel::DISABLED); + w.set_pol(Pol::HIGH); + }); + + rtc.prer().modify(|w| { + w.set_prediv_s(rtc_config.sync_prescaler); + w.set_prediv_a(rtc_config.async_prescaler); + }); + }); + + self.rtc_config = rtc_config; + } + + /// Calibrate the clock drift. + /// + /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range. + /// + /// ### Note + /// + /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` + /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). + #[cfg(not(rtc_v2f2))] + pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) { + const RTC_CALR_MIN_PPM: f32 = -487.1; + const RTC_CALR_MAX_PPM: f32 = 488.5; + const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537; + + if clock_drift < RTC_CALR_MIN_PPM { + clock_drift = RTC_CALR_MIN_PPM; + } else if clock_drift > RTC_CALR_MAX_PPM { + clock_drift = RTC_CALR_MAX_PPM; + } + + clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM; + + self.write(false, |rtc| { + unsafe { + rtc.calr().write(|w| { + match period { + super::RtcCalibrationCyclePeriod::Seconds8 => { + w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND); + } + super::RtcCalibrationCyclePeriod::Seconds16 => { + w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND); + } + super::RtcCalibrationCyclePeriod::Seconds32 => { + // Set neither `calw8` nor `calw16` to use 32 seconds + } + } + + // Extra pulses during calibration cycle period: CALP * 512 - CALM + // + // CALP sets whether pulses are added or omitted. + // + // CALM contains how many pulses (out of 512) are masked in a + // given calibration cycle period. + if clock_drift > 0.0 { + // Maximum (about 512.2) rounds to 512. + clock_drift += 0.5; + + // When the offset is positive (0 to 512), the opposite of + // the offset (512 - offset) is masked, i.e. for the + // maximum offset (512), 0 pulses are masked. + w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ); + w.set_calm(512 - clock_drift as u16); + } else { + // Minimum (about -510.7) rounds to -511. + clock_drift -= 0.5; + + // When the offset is negative or zero (-511 to 0), + // the absolute offset is masked, i.e. for the minimum + // offset (-511), 511 pulses are masked. + w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE); + w.set_calm((clock_drift * -1.0) as u16); + } + }); + } + }) + } + + pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + where + F: FnOnce(&crate::pac::rtc::Rtc) -> R, + { + let r = T::regs(); + // Disable write protection. + // This is safe, as we're only writin the correct and expected values. + unsafe { + r.wpr().write(|w| w.set_key(0xca)); + r.wpr().write(|w| w.set_key(0x53)); + + // true if initf bit indicates RTC peripheral is in init mode + if init_mode && !r.isr().read().initf() { + // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode + r.isr().modify(|w| w.set_init(Init::INITMODE)); + // wait till init state entered + // ~2 RTCCLK cycles + while !r.isr().read().initf() {} + } + } + + let result = f(&r); + + unsafe { + if init_mode { + r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode + } + + // Re-enable write protection. + // This is safe, as the field accepts the full range of 8-bit values. + r.wpr().write(|w| w.set_key(0xff)); + } + result + } +} + +/// Read content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option { + if register < BACKUP_REGISTER_COUNT { + Some(unsafe { rtc.bkpr(register).read().bkp() }) + } else { + None + } +} + +/// Set content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { + if register < BACKUP_REGISTER_COUNT { + unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) } + } +} diff --git a/embassy-stm32/src/rtc/v2/v2f0.rs b/embassy-stm32/src/rtc/v2/v2f0.rs new file mode 100644 index 000000000..d6871d91e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f0.rs @@ -0,0 +1,41 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2f2.rs b/embassy-stm32/src/rtc/v2/v2f2.rs new file mode 100644 index 000000000..e041f3f4e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f2.rs @@ -0,0 +1,31 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2f3.rs b/embassy-stm32/src/rtc/v2/v2f3.rs new file mode 100644 index 000000000..e041f3f4e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f3.rs @@ -0,0 +1,31 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2f4.rs b/embassy-stm32/src/rtc/v2/v2f4.rs new file mode 100644 index 000000000..4dd21cae4 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f4.rs @@ -0,0 +1,31 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2f7.rs b/embassy-stm32/src/rtc/v2/v2f7.rs new file mode 100644 index 000000000..d6871d91e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f7.rs @@ -0,0 +1,41 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2h7.rs b/embassy-stm32/src/rtc/v2/v2h7.rs new file mode 100644 index 000000000..f3b180683 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2h7.rs @@ -0,0 +1,33 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs new file mode 100644 index 000000000..8d8005887 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2l0.rs @@ -0,0 +1,40 @@ +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { + // FIXME: + // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + // while !crate::pac::PWR.cr1().read().dbp() {} + + // let reg = crate::pac::RCC.bdcr().read(); + // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + // if !reg.rtcen() || reg.rtcsel().0 != clock_config { + // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + // crate::pac::RCC.bdcr().modify(|w| { + // // Reset + // w.set_bdrst(false); + + // // Select RTC source + // w.set_rtcsel(Rtcsel(clock_config)); + // w.set_rtcen(true); + + // // Restore bcdr + // w.set_lscosel(reg.lscosel()); + // w.set_lscoen(reg.lscoen()); + + // w.set_lseon(reg.lseon()); + // w.set_lsedrv(reg.lsedrv()); + // w.set_lsebyp(reg.lsebyp()); + // }); + // } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // // enable peripheral clock for communication + // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // // read to allow the pwr clock to enable + // crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs new file mode 100644 index 000000000..8d8005887 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2l1.rs @@ -0,0 +1,40 @@ +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { + // FIXME: + // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + // while !crate::pac::PWR.cr1().read().dbp() {} + + // let reg = crate::pac::RCC.bdcr().read(); + // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + // if !reg.rtcen() || reg.rtcsel().0 != clock_config { + // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + // crate::pac::RCC.bdcr().modify(|w| { + // // Reset + // w.set_bdrst(false); + + // // Select RTC source + // w.set_rtcsel(Rtcsel(clock_config)); + // w.set_rtcen(true); + + // // Restore bcdr + // w.set_lscosel(reg.lscosel()); + // w.set_lscoen(reg.lscoen()); + + // w.set_lseon(reg.lseon()); + // w.set_lsedrv(reg.lsedrv()); + // w.set_lsebyp(reg.lsebyp()); + // }); + // } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // // enable peripheral clock for communication + // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // // read to allow the pwr clock to enable + // crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2l4.rs b/embassy-stm32/src/rtc/v2/v2l4.rs new file mode 100644 index 000000000..d6871d91e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2l4.rs @@ -0,0 +1,41 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2wb.rs b/embassy-stm32/src/rtc/v2/v2wb.rs new file mode 100644 index 000000000..98761fa60 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2wb.rs @@ -0,0 +1,39 @@ +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel() != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(clock_config); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs new file mode 100644 index 000000000..7255e97eb --- /dev/null +++ b/embassy-stm32/src/rtc/v3.rs @@ -0,0 +1,212 @@ +use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; + +use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig}; +use crate::pac::rtc::Rtc; + +impl<'d, T: Instance> super::Rtc<'d, T> { + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { + // Unlock the backup domain + unsafe { + #[cfg(feature = "stm32g0c1ve")] + { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + } + + #[cfg(not(feature = "stm32g0c1ve"))] + { + crate::pac::PWR + .cr1() + .modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED)); + while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {} + } + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + let config_rtcsel = rtc_config.clock_config as u8; + #[cfg(rtc_v3)] + #[cfg(not(any(feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve")))] + let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); + #[cfg(feature = "stm32g0c1ve")] + let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel); + + if !reg.rtcen() || reg.rtcsel() != config_rtcsel { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(config_rtcsel); + + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } + } + + self.write(true, |rtc| { + unsafe { + rtc.cr().modify(|w| { + w.set_fmt(Fmt::TWENTYFOURHOUR); + w.set_osel(Osel::DISABLED); + w.set_pol(Pol::HIGH); + }); + + rtc.prer().modify(|w| { + w.set_prediv_s(rtc_config.sync_prescaler); + w.set_prediv_a(rtc_config.async_prescaler); + }); + + // TODO: configuration for output pins + rtc.cr().modify(|w| { + w.set_out2en(false); + w.set_tampalrm_type(TampalrmType::PUSHPULL); + w.set_tampalrm_pu(TampalrmPu::NOPULLUP); + }); + } + }); + + self.rtc_config = rtc_config; + } + + const RTC_CALR_MIN_PPM: f32 = -487.1; + const RTC_CALR_MAX_PPM: f32 = 488.5; + const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537; + + /// Calibrate the clock drift. + /// + /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range. + /// + /// ### Note + /// + /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` + /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). + pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) { + if clock_drift < Self::RTC_CALR_MIN_PPM { + clock_drift = Self::RTC_CALR_MIN_PPM; + } else if clock_drift > Self::RTC_CALR_MAX_PPM { + clock_drift = Self::RTC_CALR_MAX_PPM; + } + + clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM; + + self.write(false, |rtc| { + unsafe { + rtc.calr().write(|w| { + match period { + RtcCalibrationCyclePeriod::Seconds8 => { + w.set_calw8(Calw8::EIGHTSECONDS); + } + RtcCalibrationCyclePeriod::Seconds16 => { + w.set_calw16(Calw16::SIXTEENSECONDS); + } + RtcCalibrationCyclePeriod::Seconds32 => { + // Set neither `calw8` nor `calw16` to use 32 seconds + } + } + + // Extra pulses during calibration cycle period: CALP * 512 - CALM + // + // CALP sets whether pulses are added or omitted. + // + // CALM contains how many pulses (out of 512) are masked in a + // given calibration cycle period. + if clock_drift > 0.0 { + // Maximum (about 512.2) rounds to 512. + clock_drift += 0.5; + + // When the offset is positive (0 to 512), the opposite of + // the offset (512 - offset) is masked, i.e. for the + // maximum offset (512), 0 pulses are masked. + w.set_calp(Calp::INCREASEFREQ); + w.set_calm(512 - clock_drift as u16); + } else { + // Minimum (about -510.7) rounds to -511. + clock_drift -= 0.5; + + // When the offset is negative or zero (-511 to 0), + // the absolute offset is masked, i.e. for the minimum + // offset (-511), 511 pulses are masked. + w.set_calp(Calp::NOCHANGE); + w.set_calm((clock_drift * -1.0) as u16); + } + }); + } + }) + } + + pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + where + F: FnOnce(&crate::pac::rtc::Rtc) -> R, + { + let r = T::regs(); + // Disable write protection. + // This is safe, as we're only writin the correct and expected values. + unsafe { + r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); + r.wpr().write(|w| w.set_key(Key::DEACTIVATE2)); + + if init_mode && !r.icsr().read().initf() { + r.icsr().modify(|w| w.set_init(Init::INITMODE)); + // wait till init state entered + // ~2 RTCCLK cycles + while !r.icsr().read().initf() {} + } + } + + let result = f(&r); + + unsafe { + if init_mode { + r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode + } + + // Re-enable write protection. + // This is safe, as the field accepts the full range of 8-bit values. + r.wpr().write(|w| w.set_key(Key::ACTIVATE)); + } + result + } +} + +pub(super) unsafe fn enable_peripheral_clk() { + // Nothing to do +} + +pub const BACKUP_REGISTER_COUNT: usize = 32; + +/// Read content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { + if register < BACKUP_REGISTER_COUNT { + //Some(rtc.bkpr()[register].read().bits()) + None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC + } else { + None + } +} + +/// Set content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) { + if register < BACKUP_REGISTER_COUNT { + // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC + //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) } + } +} From dc90006982899a5b1be43123905dd65f7b161789 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 29 Sep 2022 07:58:11 +0200 Subject: [PATCH 0207/1575] Remove code duplication on nb_read --- embassy-stm32/src/usart/mod.rs | 77 ++++++++++++++-------------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 5ee099348..4bf157292 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -160,6 +160,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { Ok(()) } + pub fn nb_read(&mut self) -> Result> { + let r = T::regs(); + unsafe { + let sr = sr(r).read(); + if sr.pe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Parity)) + } else if sr.fe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Framing)) + } else if sr.ne() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Noise)) + } else if sr.ore() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Overrun)) + } else if sr.rxne() { + Ok(rdr(r).read_volatile()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { unsafe { let r = T::regs(); @@ -263,6 +287,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.rx.read(buffer).await } + pub fn nb_read(&mut self) -> Result> { + self.rx.nb_read() + } + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.blocking_read(buffer) } @@ -281,27 +309,7 @@ mod eh02 { impl<'d, T: BasicInstance, RxDma> embedded_hal_02::serial::Read for UartRx<'d, T, RxDma> { type Error = Error; fn read(&mut self) -> Result> { - let r = T::regs(); - unsafe { - let sr = sr(r).read(); - if sr.pe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Parity)) - } else if sr.fe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Framing)) - } else if sr.ne() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Noise)) - } else if sr.ore() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Overrun)) - } else if sr.rxne() { - Ok(rdr(r).read_volatile()) - } else { - Err(nb::Error::WouldBlock) - } - } + self.nb_read() } } @@ -318,7 +326,7 @@ mod eh02 { impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::serial::Read for Uart<'d, T, TxDma, RxDma> { type Error = Error; fn read(&mut self) -> Result> { - embedded_hal_02::serial::Read::read(&mut self.rx) + self.nb_read() } } @@ -360,30 +368,9 @@ mod eh1 { type Error = Error; } - impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::nb::Read for UartRx<'d, T, RxDma> { fn read(&mut self) -> nb::Result { - let r = T::regs(); - unsafe { - let sr = sr(r).read(); - if sr.pe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Parity)) - } else if sr.fe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Framing)) - } else if sr.ne() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Noise)) - } else if sr.ore() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Overrun)) - } else if sr.rxne() { - Ok(rdr(r).read_volatile()) - } else { - Err(nb::Error::WouldBlock) - } - } + self.nb_read() } } @@ -409,7 +396,7 @@ mod eh1 { impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Read for Uart<'d, T, TxDma, RxDma> { fn read(&mut self) -> Result> { - embedded_hal_02::serial::Read::read(&mut self.rx) + self.nb_read() } } From f9c62d4f1da1c4a38219056be3f658c34d0031af Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 29 Sep 2022 09:09:35 +0200 Subject: [PATCH 0208/1575] Add flowcontrol to UART --- embassy-stm32/src/usart/mod.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6c2668748..22de6d180 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -214,7 +214,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); r.cr2().write(|_w| {}); - r.cr3().write(|_w| {}); r.brr().write_value(regs::Brr(div)); r.cr1().write(|w| { w.set_ue(true); @@ -241,6 +240,29 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } + pub fn new_with_rtscts( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cts, rts); + + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); + } + Self::new(_inner, rx, tx, tx_dma, rx_dma, config) + } + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> where TxDma: crate::usart::TxDma, From 72c2e985bb481fbc2e138a8e98b9dbb27878f370 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 29 Sep 2022 11:02:43 +0200 Subject: [PATCH 0209/1575] Update embedded-hal versions and explicitly pin --- embassy-embedded-hal/Cargo.toml | 4 ++-- .../src/shared_bus/asynch/spi.rs | 6 ++--- .../src/shared_bus/blocking/i2c.rs | 3 +-- .../src/shared_bus/blocking/spi.rs | 8 +++---- embassy-lora/Cargo.toml | 4 ++-- embassy-nrf/Cargo.toml | 4 ++-- embassy-nrf/src/gpio.rs | 12 +++++----- embassy-nrf/src/gpiote.rs | 2 +- embassy-nrf/src/spim.rs | 8 +++---- embassy-nrf/src/twim.rs | 6 ++--- embassy-nrf/src/uarte.rs | 4 ++-- embassy-rp/Cargo.toml | 7 +++--- embassy-rp/src/gpio.rs | 22 +++++++++---------- embassy-rp/src/i2c.rs | 18 ++++++--------- embassy-rp/src/spi.rs | 8 +++---- embassy-rp/src/uart/mod.rs | 12 +++++----- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/exti.rs | 2 +- embassy-stm32/src/gpio.rs | 3 +-- embassy-stm32/src/i2c/v1.rs | 6 ++--- embassy-stm32/src/i2c/v2.rs | 6 ++--- embassy-stm32/src/spi/mod.rs | 8 +++---- embassy-time/Cargo.toml | 6 ++--- embassy-time/src/delay.rs | 2 +- examples/rp/src/bin/spi_display.rs | 12 +++++----- examples/stm32h7/Cargo.toml | 4 ++-- examples/stm32l4/Cargo.toml | 4 ++-- tests/rp/Cargo.toml | 4 ++-- tests/stm32/Cargo.toml | 4 ++-- 29 files changed, 94 insertions(+), 99 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 462680720..fe8fac7c8 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -18,8 +18,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] [dependencies] 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.8" } -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2", 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/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index c95b59ef0..a3814d6d0 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -29,7 +29,7 @@ use core::future::Future; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; -use embedded_hal_1::digital::blocking::OutputPin; +use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi; @@ -57,7 +57,7 @@ where type Error = SpiDeviceError; } -impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +unsafe impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex + 'static, BUS: spi::SpiBusFlush + 'static, @@ -122,7 +122,7 @@ where type Error = SpiDeviceError; } -impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +unsafe impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex + 'static, BUS: spi::SpiBusFlush + SetConfig + 'static, diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index a611e2d27..892000b26 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -20,8 +20,7 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; -use embedded_hal_1::i2c::blocking::{I2c, Operation}; -use embedded_hal_1::i2c::ErrorType; +use embedded_hal_1::i2c::{ErrorType, I2c, Operation}; use crate::shared_bus::I2cDeviceError; use crate::SetConfig; diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 23845d887..4a08dc36e 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -22,9 +22,9 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; -use embedded_hal_1::digital::blocking::OutputPin; +use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi; -use embedded_hal_1::spi::blocking::SpiBusFlush; +use embedded_hal_1::spi::SpiBusFlush; use crate::shared_bus::SpiDeviceError; use crate::SetConfig; @@ -50,7 +50,7 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::blocking::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, BUS: SpiBusFlush, @@ -146,7 +146,7 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::blocking::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, BUS: SpiBusFlush + SetConfig, diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 1757efa8b..dcb0d8245 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -28,8 +28,8 @@ 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" } 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.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } 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-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index d80281fa3..58b820242 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -73,8 +73,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} embedded-io = { version = "0.3.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 924629908..bb64e41e9 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -574,7 +574,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -588,7 +588,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -598,7 +598,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -615,7 +615,7 @@ mod eh1 { /// Implement [`InputPin`] for [`Flex`]; /// /// If the pin is not in input mode the result is unspecified. - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -625,7 +625,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -635,7 +635,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index b418be9d5..25ad90496 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -457,7 +457,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for InputChannel<'d, C, T> { + impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> { fn is_high(&self) -> Result { Ok(self.pin.is_high()) } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 51cd73a47..d821d2353 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -446,25 +446,25 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead for Spim<'d, T> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite for Spim<'d, T> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 7c6ca1d30..8d6171fac 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -793,7 +793,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for Twim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -823,14 +823,14 @@ mod eh1 { fn transaction<'a>( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { todo!(); } fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { todo!(); } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 5f9c4f17d..d99599112 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1040,7 +1040,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for Uarte<'d, T> { + impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -1054,7 +1054,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for UarteTx<'d, T> { + impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 337a84f4a..3aca5dbb4 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -31,7 +31,7 @@ nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", " # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1"] +unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } @@ -58,5 +58,6 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90 #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.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 9b9a08110..a28bae96b 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -867,7 +867,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -881,7 +881,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -891,7 +891,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -901,7 +901,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Output<'d, T> { fn toggle(&mut self) -> Result<(), Self::Error> { Ok(self.toggle()) } @@ -911,7 +911,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -921,7 +921,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -931,7 +931,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for OutputOpenDrain<'d, T> { fn toggle(&mut self) -> Result<(), Self::Error> { Ok(self.toggle()) } @@ -941,7 +941,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } @@ -951,7 +951,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> { fn set_high(&mut self) -> Result<(), Self::Error> { Ok(self.set_high()) } @@ -961,7 +961,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> { fn is_set_high(&self) -> Result { Ok(self.is_set_high()) } @@ -971,7 +971,7 @@ mod eh1 { } } - impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> { + impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Flex<'d, T> { fn toggle(&mut self) -> Result<(), Self::Error> { Ok(self.toggle()) } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 9596d661d..52f910cef 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -379,7 +379,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -421,16 +421,14 @@ mod eh1 { fn transaction<'a>( &mut self, address: u8, - operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { Self::setup(address.into())?; for i in 0..operations.len() { let last = i == operations.len() - 1; match &mut operations[i] { - embedded_hal_1::i2c::blocking::Operation::Read(buf) => { - self.read_blocking_internal(buf, false, last)? - } - embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, + embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, } } Ok(()) @@ -438,17 +436,15 @@ mod eh1 { fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { Self::setup(address.into())?; let mut peekable = operations.into_iter().peekable(); while let Some(operation) = peekable.next() { let last = peekable.peek().is_none(); match operation { - embedded_hal_1::i2c::blocking::Operation::Read(buf) => { - self.read_blocking_internal(buf, false, last)? - } - embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, + embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, } } Ok(()) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 03293e064..754e2dd30 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -523,25 +523,25 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, M> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, M> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 567c79db3..56c25e189 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -486,7 +486,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> { fn read(&mut self) -> nb::Result { let r = T::regs(); unsafe { @@ -509,7 +509,7 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -519,7 +519,7 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -529,13 +529,13 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> { fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -545,7 +545,7 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a4a232f51..9566dbcaf 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -42,8 +42,8 @@ embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 07c96ead0..dca991859 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -155,7 +155,7 @@ mod eh1 { type Error = Infallible; } - impl<'d, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for ExtiInput<'d, T> { + impl<'d, T: GpioPin> embedded_hal_1::digital::InputPin for ExtiInput<'d, T> { fn is_high(&self) -> Result { Ok(self.is_high()) } diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index d794e3989..5e3346754 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -848,8 +848,7 @@ mod eh02 { #[cfg(feature = "unstable-traits")] mod eh1 { - use embedded_hal_1::digital::blocking::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; - use embedded_hal_1::digital::ErrorType; + use embedded_hal_1::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; use super::*; diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 9dc75789a..f39a37df6 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -334,7 +334,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -364,14 +364,14 @@ mod eh1 { fn transaction<'a>( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { todo!(); } fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { todo!(); } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index b7c89931c..89b52da98 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -883,7 +883,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, NoDma, NoDma> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } @@ -913,14 +913,14 @@ mod eh1 { fn transaction<'a>( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], ) -> Result<(), Self::Error> { todo!(); } fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> where - O: IntoIterator>, + O: IntoIterator>, { todo!(); } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 556d12305..396427782 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -843,25 +843,25 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, NoDma, NoDma> { fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.blocking_read(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, NoDma, NoDma> { fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T, NoDma, NoDma> { + impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBus for Spi<'d, T, NoDma, NoDma> { fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 6d71e100f..c3b361b8a 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -105,8 +105,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} -embedded-hal-async = { version = "0.1.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-macros = { version = "0.1.0", path = "../embassy-macros"} @@ -117,4 +117,4 @@ cfg-if = "1.0.0" # WASM dependencies wasm-bindgen = { version = "0.2.81", optional = true } js-sys = { version = "0.3", optional = true } -wasm-timer = { version = "0.2.5", optional = true } \ No newline at end of file +wasm-timer = { version = "0.2.5", optional = true } diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs index 0a7982963..ff6b6869a 100644 --- a/embassy-time/src/delay.rs +++ b/embassy-time/src/delay.rs @@ -18,7 +18,7 @@ pub struct Delay; mod eh1 { use super::*; - impl embedded_hal_1::delay::blocking::DelayUs for Delay { + impl embedded_hal_1::delay::DelayUs for Delay { type Error = core::convert::Infallible; fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 23cd4355e..778cad3fa 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -108,9 +108,9 @@ mod shared_spi { use core::cell::RefCell; use core::fmt::Debug; - use embedded_hal_1::digital::blocking::OutputPin; + use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi; - use embedded_hal_1::spi::blocking::SpiDevice; + use embedded_hal_1::spi::SpiDevice; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum SpiDeviceWithCsError { @@ -153,7 +153,7 @@ mod shared_spi { impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS> where - BUS: spi::blocking::SpiBusFlush, + BUS: spi::SpiBusFlush, CS: OutputPin, { type Bus = BUS; @@ -182,7 +182,7 @@ mod shared_spi { /// Driver for the XPT2046 resistive touchscreen sensor mod touch { - use embedded_hal_1::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; + use embedded_hal_1::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; struct Calibration { x1: i32, @@ -246,8 +246,8 @@ mod touch { mod my_display_interface { use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; - use embedded_hal_1::digital::blocking::OutputPin; - use embedded_hal_1::spi::blocking::{SpiBusWrite, SpiDevice}; + use embedded_hal_1::digital::OutputPin; + use embedded_hal_1::spi::{SpiBusWrite, SpiDevice}; /// SPI display interface. /// diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index fc60d7a88..e725e03cb 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -17,8 +17,8 @@ defmt-rtt = "0.3" 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.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } embedded-nal-async = "0.2.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 35a9c20f0..2e2d07dc3 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -18,8 +18,8 @@ defmt-rtt = "0.3" 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.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } 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/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 503373759..2745aef06 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -16,8 +16,8 @@ defmt-rtt = "0.3.0" 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.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } 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.0", features = ["async"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index d9cd3f120..daae3d464 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -24,8 +24,8 @@ defmt-rtt = "0.3.0" 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.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } +embedded-hal-async = { version = "=0.1.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] From f4ebc36b638a081b4a8b68ae72c4cca5199c4c4c Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 29 Sep 2022 14:24:42 +0200 Subject: [PATCH 0210/1575] Futures in pub & sub are now awaited instead of returned for better user compiler diagnostics. Added functions for reading how many messages are available --- embassy-sync/src/pubsub/mod.rs | 73 +++++++++++++++++++++++++++ embassy-sync/src/pubsub/publisher.rs | 13 ++++- embassy-sync/src/pubsub/subscriber.rs | 11 ++-- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 62a9e4763..335d7e33e 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -192,6 +192,10 @@ impl u64 { + self.inner.lock(|s| s.borrow().next_message_id - next_message_id) + } + fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> { self.inner.lock(|s| { let mut s = s.borrow_mut(); @@ -217,6 +221,13 @@ impl usize { + self.inner.lock(|s| { + let s = s.borrow(); + s.queue.capacity() - s.queue.len() + }) + } + fn unregister_subscriber(&self, subscriber_next_message_id: u64) { self.inner.lock(|s| { let mut s = s.borrow_mut(); @@ -388,6 +399,10 @@ pub trait PubSubBehavior { /// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers. fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll>; + /// Get the amount of messages that are between the given the next_message_id and the most recent message. + /// This is not necessarily the amount of messages a subscriber can still received as it may have lagged. + fn available(&self, next_message_id: u64) -> u64; + /// Try to publish a message to the queue. /// /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. @@ -396,6 +411,9 @@ pub trait PubSubBehavior { /// Publish a message immediately fn publish_immediate(&self, message: T); + /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + fn space(&self) -> usize; + /// Let the channel know that a subscriber has dropped fn unregister_subscriber(&self, subscriber_next_message_id: u64); @@ -539,4 +557,59 @@ mod tests { drop(sub0); } + + #[futures_test::test] + async fn correct_available() { + let channel = PubSubChannel::::new(); + + let mut sub0 = channel.subscriber().unwrap(); + let mut sub1 = channel.subscriber().unwrap(); + let pub0 = channel.publisher().unwrap(); + + assert_eq!(sub0.available(), 0); + assert_eq!(sub1.available(), 0); + + pub0.publish(42).await; + + assert_eq!(sub0.available(), 1); + assert_eq!(sub1.available(), 1); + + sub1.next_message().await; + + assert_eq!(sub1.available(), 0); + + pub0.publish(42).await; + + assert_eq!(sub0.available(), 2); + assert_eq!(sub1.available(), 1); + } + + #[futures_test::test] + async fn correct_space() { + let channel = PubSubChannel::::new(); + + let mut sub0 = channel.subscriber().unwrap(); + let mut sub1 = channel.subscriber().unwrap(); + let pub0 = channel.publisher().unwrap(); + + assert_eq!(pub0.space(), 4); + + pub0.publish(42).await; + + assert_eq!(pub0.space(), 3); + + pub0.publish(42).await; + + assert_eq!(pub0.space(), 2); + + sub0.next_message().await; + sub0.next_message().await; + + assert_eq!(pub0.space(), 2); + + sub1.next_message().await; + assert_eq!(pub0.space(), 3); + sub1.next_message().await; + assert_eq!(pub0.space(), 4); + } } diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index 705797f60..484f1dbfd 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -31,17 +31,26 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { } /// Publish a message. But if the message queue is full, wait for all subscribers to have read the last message - pub fn publish<'s>(&'s self, message: T) -> PublisherWaitFuture<'s, 'a, PSB, T> { + pub async fn publish<'s>(&'s self, message: T) { PublisherWaitFuture { message: Some(message), publisher: self, } + .await } /// Publish a message if there is space in the message queue pub fn try_publish(&self, message: T) -> Result<(), T> { self.channel.publish_with_context(message, None) } + + /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + /// + /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something. + /// So checking doesn't give any guarantees.* + pub fn space(&self) -> usize { + self.channel.space() + } } impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> { @@ -158,7 +167,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the publisher wait action -pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { /// The message we need to publish message: Option, publisher: &'s Pub<'a, PSB, T>, diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs index b9a2cbe18..8a8e9144b 100644 --- a/embassy-sync/src/pubsub/subscriber.rs +++ b/embassy-sync/src/pubsub/subscriber.rs @@ -28,8 +28,8 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { } /// Wait for a published message - pub fn next_message<'s>(&'s mut self) -> SubscriberWaitFuture<'s, 'a, PSB, T> { - SubscriberWaitFuture { subscriber: self } + pub async fn next_message(&mut self) -> WaitResult { + SubscriberWaitFuture { subscriber: self }.await } /// Wait for a published message (ignoring lag results) @@ -64,6 +64,11 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { } } } + + /// The amount of messages this subscriber hasn't received yet + pub fn available(&self) -> u64 { + self.channel.available(self.next_message_id) + } } impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> { @@ -135,7 +140,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the subscriber wait action -pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { subscriber: &'s mut Sub<'a, PSB, T>, } From 874384826d4a6f9c9a9c8d3abf41f99a662f58fb Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 29 Sep 2022 15:15:10 +0200 Subject: [PATCH 0211/1575] Went back to named futures but now with must_use --- embassy-sync/src/pubsub/mod.rs | 2 +- embassy-sync/src/pubsub/publisher.rs | 6 +++--- embassy-sync/src/pubsub/subscriber.rs | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 335d7e33e..faaf99dc6 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -562,7 +562,7 @@ mod tests { async fn correct_available() { let channel = PubSubChannel::::new(); - let mut sub0 = channel.subscriber().unwrap(); + let sub0 = channel.subscriber().unwrap(); let mut sub1 = channel.subscriber().unwrap(); let pub0 = channel.publisher().unwrap(); diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index 484f1dbfd..faa67d947 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -31,12 +31,11 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { } /// Publish a message. But if the message queue is full, wait for all subscribers to have read the last message - pub async fn publish<'s>(&'s self, message: T) { + pub fn publish<'s>(&'s self, message: T) -> PublisherWaitFuture<'s, 'a, PSB, T> { PublisherWaitFuture { message: Some(message), publisher: self, } - .await } /// Publish a message if there is space in the message queue @@ -167,7 +166,8 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the publisher wait action -struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { /// The message we need to publish message: Option, publisher: &'s Pub<'a, PSB, T>, diff --git a/embassy-sync/src/pubsub/subscriber.rs b/embassy-sync/src/pubsub/subscriber.rs index 8a8e9144b..f420a75f0 100644 --- a/embassy-sync/src/pubsub/subscriber.rs +++ b/embassy-sync/src/pubsub/subscriber.rs @@ -28,8 +28,8 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Sub<'a, PSB, T> { } /// Wait for a published message - pub async fn next_message(&mut self) -> WaitResult { - SubscriberWaitFuture { subscriber: self }.await + pub fn next_message<'s>(&'s mut self) -> SubscriberWaitFuture<'s, 'a, PSB, T> { + SubscriberWaitFuture { subscriber: self } } /// Wait for a published message (ignoring lag results) @@ -140,7 +140,8 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } /// Future for the subscriber wait action -struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { subscriber: &'s mut Sub<'a, PSB, T>, } From dab17627093faa709f309c81f067ed2b578f2a8e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Sep 2022 15:52:23 +0200 Subject: [PATCH 0212/1575] usb: remove all "Direction as u8" casts. --- embassy-usb-driver/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index fc29786fc..931e9c318 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -54,12 +54,16 @@ impl From for u8 { } impl EndpointAddress { - const INBITS: u8 = Direction::In as u8; + const INBITS: u8 = 0x80; /// Constructs a new EndpointAddress with the given index and direction. #[inline] pub fn from_parts(index: usize, dir: Direction) -> Self { - EndpointAddress(index as u8 | dir as u8) + let dir_u8 = match dir { + Direction::Out => 0x00, + Direction::In => Self::INBITS, + }; + EndpointAddress(index as u8 | dir_u8) } /// Gets the direction part of the address. From a7fdeac560b5e277afa80cd60f788a48df6069c9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 30 Sep 2022 06:00:46 +0200 Subject: [PATCH 0213/1575] Remove flash lock/unlock public API from stm32 flash, and perform the unlocking and locking automatically on erase and write operations --- embassy-stm32/src/flash/mod.rs | 27 +++++++++---------- .../boot/application/stm32f3/src/bin/a.rs | 2 +- .../boot/application/stm32f7/src/bin/a.rs | 2 +- .../boot/application/stm32h7/src/bin/a.rs | 2 +- .../boot/application/stm32l0/src/bin/a.rs | 2 +- .../boot/application/stm32l1/src/bin/a.rs | 2 +- .../boot/application/stm32l4/src/bin/a.rs | 2 +- .../boot/application/stm32wl/src/bin/a.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 2 +- examples/stm32f3/src/bin/flash.rs | 2 +- examples/stm32f4/src/bin/flash.rs | 2 +- examples/stm32f7/src/bin/flash.rs | 2 +- examples/stm32h7/src/bin/flash.rs | 2 +- examples/stm32l0/src/bin/flash.rs | 2 +- examples/stm32l1/src/bin/flash.rs | 2 +- examples/stm32wl/src/bin/flash.rs | 2 +- 16 files changed, 28 insertions(+), 29 deletions(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 5258c9b04..988cf9fae 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -23,17 +23,6 @@ impl<'d> Flash<'d> { Self { _inner: p } } - pub fn unlock(p: impl Peripheral

+ 'd) -> Self { - let flash = Self::new(p); - - unsafe { family::unlock() }; - flash - } - - pub fn lock(&mut self) { - unsafe { family::lock() }; - } - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { let offset = FLASH_BASE as u32 + offset; if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END { @@ -57,7 +46,12 @@ impl<'d> Flash<'d> { self.clear_all_err(); - unsafe { family::blocking_write(offset, buf) } + unsafe { + family::unlock(); + let res = family::blocking_write(offset, buf); + family::lock(); + res + } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { @@ -72,7 +66,12 @@ impl<'d> Flash<'d> { self.clear_all_err(); - unsafe { family::blocking_erase(from, to) } + unsafe { + family::unlock(); + let res = family::blocking_erase(from, to); + family::lock(); + res + } } fn clear_all_err(&mut self) { @@ -82,7 +81,7 @@ impl<'d> Flash<'d> { impl Drop for Flash<'_> { fn drop(&mut self) { - self.lock(); + unsafe { family::lock() }; } } diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index fdbd5ab99..d92d59b29 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 77b897b0f..79ab80e09 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::unlock(p.FLASH); + let mut flash = Flash::new(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 0fe598a5d..8b452be34 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::unlock(p.FLASH); + let mut flash = Flash::new(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index f0b0b80e3..59ca34386 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index f0b0b80e3..59ca34386 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 5119bad2e..6cddc6cc8 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index faa650778..1ff47eddd 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PA0, Pull::Up); diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 294464d1c..4b17cd799 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,7 +20,7 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let flash = Flash::unlock(p.FLASH); + let flash = Flash::new(p.FLASH); let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index 2cf24dbd3..baa7484d0 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 393d61e86..7ea068a42 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -13,7 +13,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); // Sector 5 test_flash(&mut f, 128 * 1024, 128 * 1024); diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index c10781d0c..4a7bca1fa 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -19,7 +19,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index 6682c64d5..ee86bdbf6 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -19,7 +19,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 867cb4d3e..ffe4fb10b 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index a76b9879f..476ed51a4 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index eb7489760..2a8880624 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::unlock(p.FLASH); + let mut f = Flash::new(p.FLASH); info!("Reading..."); let mut buf = [0u8; 8]; From a283c47557ee6a0c0e54bcb7f27b6c85813ae0e3 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 30 Sep 2022 05:35:46 +0200 Subject: [PATCH 0214/1575] Implement embedded-hal-nb for uart --- embassy-stm32/Cargo.toml | 3 ++- embassy-stm32/src/usart/mod.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9566dbcaf..fbe37fe3d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,6 +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.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } @@ -102,7 +103,7 @@ unstable-pac = [] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1"] +unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # BEGIN GENERATED FEATURES # Generated by stm32-gen-features. DO NOT EDIT. diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 4bf157292..a152a0c15 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -368,13 +368,13 @@ mod eh1 { type Error = Error; } - impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::nb::Read for UartRx<'d, T, RxDma> { + impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::Read for UartRx<'d, T, RxDma> { fn read(&mut self) -> nb::Result { self.nb_read() } } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, TxDma> { + impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -384,7 +384,7 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::nb::Write for UartTx<'d, T, TxDma> { + impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -394,13 +394,13 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Read for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Read for Uart<'d, T, TxDma, RxDma> { fn read(&mut self) -> Result> { self.nb_read() } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::blocking::Write for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) } @@ -410,7 +410,7 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::nb::Write for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } From 9f77dbf5ae442c1cac0c652b4ef25bf1c82ed9d4 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 29 Sep 2022 02:01:58 -0700 Subject: [PATCH 0215/1575] rp i2c: blocking example i2c example talking to mcp23017 i2c gpio expander. --- examples/rp/src/bin/i2c.rs | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 examples/rp/src/bin/i2c.rs diff --git a/examples/rp/src/bin/i2c.rs b/examples/rp/src/bin/i2c.rs new file mode 100644 index 000000000..a5991d0da --- /dev/null +++ b/examples/rp/src/bin/i2c.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::i2c::{self, Config}; +use embassy_time::{Duration, Timer}; +use embedded_hal_1::i2c::blocking::I2c; +use {defmt_rtt as _, panic_probe as _}; + +#[allow(dead_code)] +mod mcp23017 { + pub const ADDR: u8 = 0x20; // default addr + + pub const IODIRA: u8 = 0x00; + pub const IPOLA: u8 = 0x02; + pub const GPINTENA: u8 = 0x04; + pub const DEFVALA: u8 = 0x06; + pub const INTCONA: u8 = 0x08; + pub const IOCONA: u8 = 0x0A; + pub const GPPUA: u8 = 0x0C; + pub const INTFA: u8 = 0x0E; + pub const INTCAPA: u8 = 0x10; + pub const GPIOA: u8 = 0x12; + pub const OLATA: u8 = 0x14; + pub const IODIRB: u8 = 0x01; + pub const IPOLB: u8 = 0x03; + pub const GPINTENB: u8 = 0x05; + pub const DEFVALB: u8 = 0x07; + pub const INTCONB: u8 = 0x09; + pub const IOCONB: u8 = 0x0B; + pub const GPPUB: u8 = 0x0D; + pub const INTFB: u8 = 0x0F; + pub const INTCAPB: u8 = 0x11; + pub const GPIOB: u8 = 0x13; + pub const OLATB: u8 = 0x15; +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let sda = p.PIN_14; + let scl = p.PIN_15; + + info!("set up i2c "); + let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default()); + + use mcp23017::*; + + info!("init mcp23017 config for IxpandO"); + // init - a outputs, b inputs + i2c.write(ADDR, &[IODIRA, 0x00]).unwrap(); + i2c.write(ADDR, &[IODIRB, 0xff]).unwrap(); + i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups + + let mut val = 0xaa; + loop { + let mut portb = [0]; + + i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap(); + i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap(); + + info!("portb = {:02x}", portb[0]); + val = !val; + + Timer::after(Duration::from_secs(1)).await; + } +} From d5abd32da21998178216693a4d549a0f2683a4bb Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 1 Oct 2022 01:09:40 -0700 Subject: [PATCH 0216/1575] rename to i2c_blocking --- examples/rp/src/bin/{i2c.rs => i2c_blocking.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/rp/src/bin/{i2c.rs => i2c_blocking.rs} (100%) diff --git a/examples/rp/src/bin/i2c.rs b/examples/rp/src/bin/i2c_blocking.rs similarity index 100% rename from examples/rp/src/bin/i2c.rs rename to examples/rp/src/bin/i2c_blocking.rs From c96581879cf054d9e4fec9272e5cecb165844cbb Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 1 Oct 2022 01:33:36 -0700 Subject: [PATCH 0217/1575] update embedded-hal api Also pin to alpha.9 since its a breaking change --- examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/i2c_blocking.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 3c8f923e7..1bb6432e6 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -26,7 +26,7 @@ st7789 = "0.6.1" 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.8" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } embedded-hal-async = { version = "0.1.0-alpha.1" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } static_cell = "1.0.0" diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs index a5991d0da..7623e33c8 100644 --- a/examples/rp/src/bin/i2c_blocking.rs +++ b/examples/rp/src/bin/i2c_blocking.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::i2c::{self, Config}; use embassy_time::{Duration, Timer}; -use embedded_hal_1::i2c::blocking::I2c; +use embedded_hal_1::i2c::I2c; use {defmt_rtt as _, panic_probe as _}; #[allow(dead_code)] From 90d392205fe7c07cbc59b09679cde6cfc1e244b6 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 10:00:30 -0700 Subject: [PATCH 0218/1575] embassy-rp: inline I2c::regs It just returns a literal constant, so there's no reason not to always inline it. --- embassy-rp/src/i2c.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 52f910cef..ab56c9359 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -499,6 +499,7 @@ macro_rules! impl_instance { type Interrupt = crate::interrupt::$irq; + #[inline] fn regs() -> pac::i2c::I2c { pac::$type } From 8d38eacae426ef1b1e1f7255eed50a9588793fb4 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 27 Sep 2022 23:24:22 -0700 Subject: [PATCH 0219/1575] rp i2c: remove vestiges of DMA --- embassy-rp/src/i2c.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index ab56c9359..a6f278827 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; use pac::i2c; -use crate::dma::AnyChannel; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; @@ -52,9 +51,6 @@ impl Default for Config { const FIFO_SIZE: u8 = 16; pub struct I2c<'d, T: Instance, M: Mode> { - _tx_dma: Option>, - _rx_dma: Option>, - _dma_buf: [u16; 256], phantom: PhantomData<(&'d mut T, M)>, } @@ -66,7 +62,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(scl, sda); - Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config) + Self::new_inner(_peri, scl.map_into(), sda.map_into(), config) } } @@ -75,8 +71,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, sda: PeripheralRef<'d, AnyPin>, - _tx_dma: Option>, - _rx_dma: Option>, config: Config, ) -> Self { into_ref!(_peri); @@ -173,9 +167,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } Self { - _tx_dma, - _rx_dma, - _dma_buf: [0; 256], phantom: PhantomData, } } From 72b645b0c96c7b8312d0a64f851e807faacd78af Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Tue, 27 Sep 2022 23:44:14 -0700 Subject: [PATCH 0220/1575] rp i2c: make blocking only for Mode=Blocking --- embassy-rp/src/i2c.rs | 212 +++++++++++++++++++++--------------------- 1 file changed, 107 insertions(+), 105 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index a6f278827..c609b02ea 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -56,14 +56,115 @@ pub struct I2c<'d, T: Instance, M: Mode> { impl<'d, T: Instance> I2c<'d, T, Blocking> { pub fn new_blocking( - _peri: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(scl, sda); - Self::new_inner(_peri, scl.map_into(), sda.map_into(), config) + Self::new_inner(peri, scl.map_into(), sda.map_into(), config) } + + fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = buffer.len() - 1; + for (i, byte) in buffer.iter_mut().enumerate() { + let first = i == 0; + let last = i == lastindex; + + // NOTE(unsafe) We have &mut self + unsafe { + // wait until there is space in the FIFO to write the next byte + while p.ic_txflr().read().txflr() == FIFO_SIZE {} + + p.ic_data_cmd().write(|w| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); + + w.set_cmd(true); + }); + + while p.ic_rxflr().read().rxflr() == 0 { + self.read_and_clear_abort_reason()?; + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + if bytes.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in bytes.iter().enumerate() { + let last = i == bytes.len() - 1; + + // NOTE(unsafe) We have &mut self + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(send_stop && last); + w.set_dat(*byte); + }); + + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} + + let abort_reason = self.read_and_clear_abort_reason(); + + if abort_reason.is_err() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occured. + + while !p.ic_raw_intr_stat().read().stop_det() {} + + p.ic_clr_stop_det().read().clr_stop_det(); + } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + abort_reason?; + } + } + Ok(()) + } + + // ========================= + // Blocking public API + // ========================= + + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, true) + } + + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, false)?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } +} } impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { @@ -217,111 +318,12 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } - fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { - if buffer.is_empty() { - return Err(Error::InvalidReadBufferLength); - } - - let p = T::regs(); - let lastindex = buffer.len() - 1; - for (i, byte) in buffer.iter_mut().enumerate() { - let first = i == 0; - let last = i == lastindex; - - // NOTE(unsafe) We have &mut self - unsafe { - // wait until there is space in the FIFO to write the next byte - while p.ic_txflr().read().txflr() == FIFO_SIZE {} - - p.ic_data_cmd().write(|w| { - w.set_restart(restart && first); - w.set_stop(send_stop && last); - - w.set_cmd(true); - }); - - while p.ic_rxflr().read().rxflr() == 0 { - self.read_and_clear_abort_reason()?; - } - - *byte = p.ic_data_cmd().read().dat(); - } - } - - Ok(()) - } - - fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - if bytes.is_empty() { - return Err(Error::InvalidWriteBufferLength); - } - - let p = T::regs(); - - for (i, byte) in bytes.iter().enumerate() { - let last = i == bytes.len() - 1; - - // NOTE(unsafe) We have &mut self - unsafe { - p.ic_data_cmd().write(|w| { - w.set_stop(send_stop && last); - w.set_dat(*byte); - }); - - // Wait until the transmission of the address/data from the - // internal shift register has completed. For this to function - // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The - // TX_EMPTY_CTRL flag was set in i2c_init. - while !p.ic_raw_intr_stat().read().tx_empty() {} - - let abort_reason = self.read_and_clear_abort_reason(); - - if abort_reason.is_err() || (send_stop && last) { - // If the transaction was aborted or if it completed - // successfully wait until the STOP condition has occured. - - while !p.ic_raw_intr_stat().read().stop_det() {} - - p.ic_clr_stop_det().read().clr_stop_det(); - } - - // Note the hardware issues a STOP automatically on an abort - // condition. Note also the hardware clears RX FIFO as well as - // TX on abort, ecause we set hwparam - // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - abort_reason?; - } - } - Ok(()) - } - - // ========================= - // Blocking public API - // ========================= - - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } - - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, true) - } - - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, false)?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } } mod eh02 { use super::*; - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, Blocking> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -329,7 +331,7 @@ mod eh02 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, Blocking> { type Error = Error; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -337,7 +339,7 @@ mod eh02 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, Blocking> { type Error = Error; fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -370,7 +372,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, Blocking> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From 5e2c52ee5b6fcc5b50589fd2590657e3f1083bff Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 01:20:04 -0700 Subject: [PATCH 0221/1575] embassy-rp: async i2c implementation This is an interrupt-driven async i2c master implementation. It makes as best use of the RP2040's i2c block's fifos as possible to minimize interrupts. It implements embedded_hal_async::i2c for easy interop. WIP async impl --- embassy-rp/src/i2c.rs | 376 +++++++++++++++++++++++++++++++++++++++-- examples/rp/Cargo.toml | 3 + 2 files changed, 369 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index c609b02ea..6fc64d849 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -1,6 +1,10 @@ +use core::future; use core::marker::PhantomData; +use core::task::Poll; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; use crate::gpio::sealed::Pin; @@ -79,7 +83,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // NOTE(unsafe) We have &mut self unsafe { // wait until there is space in the FIFO to write the next byte - while p.ic_txflr().read().txflr() == FIFO_SIZE {} + while Self::tx_fifo_full() {} p.ic_data_cmd().write(|w| { w.set_restart(restart && first); @@ -88,7 +92,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { w.set_cmd(true); }); - while p.ic_rxflr().read().rxflr() == 0 { + while Self::rx_fifo_len() == 0 { self.read_and_clear_abort_reason()?; } @@ -165,9 +169,250 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { // Automatic Stop } } + +static I2C_WAKER: AtomicWaker = AtomicWaker::new(); + +impl<'d, T: Instance> I2c<'d, T, Async> { + pub fn new_async( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(scl, sda, irq); + + let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config); + + irq.set_handler(Self::on_interrupt); + unsafe { + let i2c = T::regs(); + + // mask everything initially + i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); + } + irq.unpend(); + debug_assert!(!irq.is_pending()); + irq.enable(); + + i2c + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + async fn wait_on(&mut self, mut f: F, mut g: G) -> U + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + future::poll_fn(|cx| { + let r = f(self); + + if r.is_pending() { + I2C_WAKER.register(cx.waker()); + g(self); + } + r + }) + .await + } + + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt(_: *mut ()) { + let i2c = T::regs(); + i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); + + I2C_WAKER.wake(); + } + + async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + + let mut remaining = buffer.len(); + let mut remaining_queue = buffer.len(); + + let mut abort_reason = None; + + while remaining > 0 { + // Waggle SCK - basically the same as write + let tx_fifo_space = Self::tx_fifo_capacity(); + let mut batch = 0; + + debug_assert!(remaining_queue > 0); + + for _ in 0..remaining_queue.min(tx_fifo_space as usize) { + remaining_queue -= 1; + let last = remaining_queue == 0; + batch += 1; + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_restart(restart && remaining_queue == buffer.len() - 1); + w.set_stop(last && send_stop); + w.set_cmd(true); + }); + } + } + + // We've either run out of txfifo or just plain finished setting up + // the clocks for the message - either way we need to wait for rx + // data. + + debug_assert!(batch > 0); + let res = self + .wait_on( + |me| { + let rxfifo = Self::rx_fifo_len(); + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + Poll::Ready(Err(abort_reason)) + } else if rxfifo >= batch { + Poll::Ready(Ok(rxfifo)) + } else { + Poll::Pending + } + }, + |_me| unsafe { + // Set the read threshold to the number of bytes we're + // expecting so we don't get spurious interrupts. + p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1)); + + p.ic_intr_mask().modify(|w| { + w.set_m_rx_full(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + + match res { + Err(reason) => { + abort_reason = Some(reason); + // XXX keep going anyway? + break; + } + Ok(rxfifo) => { + // Fetch things from rx fifo. We're assuming we're the only + // rxfifo reader, so nothing else can take things from it. + let rxbytes = (rxfifo as usize).min(remaining); + let received = buffer.len() - remaining; + for b in &mut buffer[received..received + rxbytes] { + *b = unsafe { p.ic_data_cmd().read().dat() }; + } + remaining -= rxbytes; + } + }; + } + + // wait for stop condition to be emitted. + self.wait_on( + |_me| unsafe { + if !p.ic_raw_intr_stat().read().stop_det() && send_stop { + Poll::Pending + } else { + Poll::Ready(()) + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + unsafe { p.ic_clr_stop_det().read() }; + + if let Some(abort_reason) = abort_reason { + return Err(abort_reason); + } + Ok(()) + } + + async fn write_async_internal( + &mut self, + bytes: impl IntoIterator, + send_stop: bool, + ) -> Result<(), Error> { + let p = T::regs(); + + let mut bytes = bytes.into_iter().peekable(); + + 'xmit: loop { + let tx_fifo_space = Self::tx_fifo_capacity(); + + for _ in 0..tx_fifo_space { + if let Some(byte) = bytes.next() { + let last = bytes.peek().is_none(); + + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(last && send_stop); + w.set_cmd(false); + w.set_dat(byte); + }); + } + } else { + break 'xmit; + } + } + + self.wait_on( + |me| { + if let Err(abort_reason) = me.read_and_clear_abort_reason() { + Poll::Ready(Err(abort_reason)) + } else if !Self::tx_fifo_full() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await?; + } + + // wait for fifo to drain + self.wait_on( + |_me| unsafe { + if p.ic_raw_intr_stat().read().tx_empty() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + + Ok(()) + } + + pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(addr)?; + self.read_async_internal(buffer, false, true).await + } + + pub async fn write_async(&mut self, addr: u16, buffer: &[u8]) -> Result<(), Error> { + Self::setup(addr)?; + self.write_async_internal(buffer.iter().copied(), true).await + } } -impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { +impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { fn new_inner( _peri: impl Peripheral

+ 'd, scl: PeripheralRef<'d, AnyPin>, @@ -182,6 +427,10 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { let p = T::regs(); unsafe { + let reset = T::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); + p.ic_enable().write(|w| w.set_enable(false)); // Select controller mode & speed @@ -267,9 +516,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { p.ic_enable().write(|w| w.set_enable(true)); } - Self { - phantom: PhantomData, - } + Self { phantom: PhantomData } } fn setup(addr: u16) -> Result<(), Error> { @@ -290,6 +537,23 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { Ok(()) } + #[inline] + fn tx_fifo_full() -> bool { + Self::tx_fifo_capacity() == 0 + } + + #[inline] + fn tx_fifo_capacity() -> u8 { + let p = T::regs(); + unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() } + } + + #[inline] + fn rx_fifo_len() -> u8 { + let p = T::regs(); + unsafe { p.ic_rxflr().read().rxflr() } + } + fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); unsafe { @@ -317,7 +581,6 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { } } } - } mod eh02 { @@ -444,6 +707,91 @@ 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; + + use super::*; + + impl<'d, A, T> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> + where + 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> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.read_async_internal(buffer, false, true).await + } + } + + fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> { + let addr: u16 = address.into(); + + async move { + Self::setup(addr)?; + self.write_async_internal(write.iter().copied(), true).await + } + } + + fn write_read<'a>( + &'a mut self, + address: A, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a> { + 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 + } + } + + fn transaction<'a, 'b>( + &'a mut self, + address: A, + operations: &'a mut [Operation<'b>], + ) -> Self::TransactionFuture<'a, 'b> { + let addr: u16 = address.into(); + + async move { + let mut iterator = operations.iter_mut(); + + 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?; + } + } + } + Ok(()) + } + } + } +} fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 @@ -459,6 +807,7 @@ mod sealed { type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; + fn reset() -> crate::pac::resets::regs::Peripherals; } pub trait Mode {} @@ -485,7 +834,7 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; @@ -496,13 +845,20 @@ macro_rules! impl_instance { fn regs() -> pac::i2c::I2c { pac::$type } + + #[inline] + fn reset() -> pac::resets::regs::Peripherals { + let mut ret = pac::resets::regs::Peripherals::default(); + ret.$reset(true); + ret + } } impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, I2C0_IRQ, 32, 33); -impl_instance!(I2C1, I2C1_IRQ, 34, 35); +impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 3c8f923e7..e689df6bd 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -30,3 +30,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } static_cell = "1.0.0" + +[profile.release] +debug = true From 1ee4bb22deb19e93a7c68e04875889e3e4e31c29 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Sep 2022 09:35:19 -0700 Subject: [PATCH 0222/1575] embassy-rp i2c: async (non-blocking) example Simple example exercising an mcp23017 GPIO expander, configured on RP2040 GPIOs 14+15 (i2c1) with 8 inputs and 8 outputs. Input bit 0 controls whether to display a mcp23017 register dump. --- examples/rp/src/bin/i2c_async.rs | 102 +++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 examples/rp/src/bin/i2c_async.rs diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs new file mode 100644 index 000000000..d1a2e3cd7 --- /dev/null +++ b/examples/rp/src/bin/i2c_async.rs @@ -0,0 +1,102 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::i2c::{self, Config}; +use embassy_rp::interrupt; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::i2c::I2c; +use {defmt_rtt as _, panic_probe as _}; + +#[allow(dead_code)] +mod mcp23017 { + pub const ADDR: u8 = 0x20; // default addr + + macro_rules! mcpregs { + ($($name:ident : $val:expr),* $(,)?) => { + $( + pub const $name: u8 = $val; + )* + + pub fn regname(reg: u8) -> &'static str { + match reg { + $( + $val => stringify!($name), + )* + _ => panic!("bad reg"), + } + } + } + } + + // These are correct for IOCON.BANK=0 + mcpregs! { + IODIRA: 0x00, + IPOLA: 0x02, + GPINTENA: 0x04, + DEFVALA: 0x06, + INTCONA: 0x08, + IOCONA: 0x0A, + GPPUA: 0x0C, + INTFA: 0x0E, + INTCAPA: 0x10, + GPIOA: 0x12, + OLATA: 0x14, + IODIRB: 0x01, + IPOLB: 0x03, + GPINTENB: 0x05, + DEFVALB: 0x07, + INTCONB: 0x09, + IOCONB: 0x0B, + GPPUB: 0x0D, + INTFB: 0x0F, + INTCAPB: 0x11, + GPIOB: 0x13, + OLATB: 0x15, + } +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let sda = p.PIN_14; + let scl = p.PIN_15; + let irq = interrupt::take!(I2C1_IRQ); + + info!("set up i2c "); + let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, Config::default()); + + use mcp23017::*; + + info!("init mcp23017 config for IxpandO"); + // init - a outputs, b inputs + i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap(); + i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap(); + i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups + + let mut val = 1; + loop { + let mut portb = [0]; + + i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap(); + info!("portb = {:02x}", portb[0]); + i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap(); + val = val.rotate_left(1); + + // get a register dump + info!("getting register dump"); + let mut regs = [0; 22]; + i2c.write_read(ADDR, &[0], &mut regs).await.unwrap(); + // always get the regdump but only display it if portb'0 is set + if portb[0] & 1 != 0 { + for (idx, reg) in regs.into_iter().enumerate() { + info!("{} => {:02x}", regname(idx as u8), reg); + } + } + + Timer::after(Duration::from_millis(100)).await; + } +} From 09afece93d0dccb750a0dbc9c63282d3dca55e48 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sat, 1 Oct 2022 19:28:27 -0700 Subject: [PATCH 0223/1575] make I2c::write_async take an iterator There's no other iterator async API right now. --- embassy-rp/src/i2c.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 6fc64d849..f62cf0b86 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -406,9 +406,9 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.read_async_internal(buffer, false, true).await } - pub async fn write_async(&mut self, addr: u16, buffer: &[u8]) -> Result<(), Error> { + pub async fn write_async(&mut self, addr: u16, bytes : impl IntoIterator) -> Result<(), Error> { Self::setup(addr)?; - self.write_async_internal(buffer.iter().copied(), true).await + self.write_async_internal(bytes, true).await } } From c385bbf07dfcadb832d02e91385bcecc45c0ef58 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 2 Oct 2022 21:28:34 +0200 Subject: [PATCH 0224/1575] Update embassy, embedded-hal. --- Cargo.toml | 4 ++-- examples/rpi-pico-w/Cargo.toml | 20 ++++++++++---------- examples/rpi-pico-w/src/main.rs | 10 +++++----- rust-toolchain.toml | 2 +- src/lib.rs | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb6aa0b29..30c0da07b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,6 @@ cortex-m = "0.7.3" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } +embedded-hal-async = { version = "0.1.0-alpha.2" } num_enum = { version = "0.5.7", default-features = false } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 53e72498b..e82d12eb9 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" cyw43 = { path = "../../", features = ["defmt"]} embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } -embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } +embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits", "nightly"] } atomic-polyfill = "0.1.5" static_cell = "1.0" @@ -21,19 +21,19 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]} cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } -embedded-hal-async = { version = "0.1.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } +embedded-hal-async = { version = "0.1.0-alpha.2" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "cb9f0ef5b800ce4a22cde1805e0eb88425f1e07b" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 986474ce3..0915ef6be 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -1,6 +1,6 @@ #![no_std] #![no_main] -#![feature(generic_associated_types, type_alias_impl_trait)] +#![feature(type_alias_impl_trait)] use core::convert::Infallible; use core::future::Future; @@ -44,15 +44,15 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Include the WiFi firmware and Country Locale Matrix (CLM) blobs. - let fw = include_bytes!("../../../firmware/43439A0.bin"); - let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); + //let fw = include_bytes!("../../../firmware/43439A0.bin"); + //let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 - //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; - //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0fa7cf7bf..a35a11b82 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-07-13" +channel = "nightly-2022-09-22" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", diff --git a/src/lib.rs b/src/lib.rs index d446313c0..2a3d4dee3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ use embassy_net::{PacketBoxExt, PacketBuf}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_time::{block_for, Duration, Timer}; -use embedded_hal_1::digital::blocking::OutputPin; +use embedded_hal_1::digital::OutputPin; use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; use self::structs::*; From 0d84533bcb25a0d6cbe6aee5418e1ebf2cbbc26a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Sep 2022 22:01:24 +0200 Subject: [PATCH 0225/1575] Use async spi transaction helper macro. --- src/lib.rs | 176 +++++++++++++++++++++-------------------------------- 1 file changed, 70 insertions(+), 106 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2a3d4dee3..ba8acd347 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; -use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; +use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice}; use self::structs::*; use crate::events::Event; @@ -753,17 +753,13 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len); - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[cmd]).await?; - bus.read(&mut buf[..(len as usize + 3) / 4]).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + bus.read(&mut buf[..(len as usize + 3) / 4]).await?; + Ok(()) + }) + .await + .unwrap(); trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); @@ -817,17 +813,13 @@ where trace!(" {:02x}", &buf8[..total_len.min(48)]); let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _); - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[cmd]).await?; - bus.write(&buf[..(total_len / 4)]).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + bus.write(&buf[..(total_len / 4)]).await?; + Ok(()) + }) + .await + .unwrap(); } fn rx(&mut self, packet: &[u8]) { @@ -1012,17 +1004,13 @@ where let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _); - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[cmd]).await?; - bus.write(&buf[..total_len / 4]).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + bus.write(&buf[..total_len / 4]).await?; + Ok(()) + }) + .await + .unwrap(); } async fn core_disable(&mut self, core: Core) { @@ -1101,23 +1089,19 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[cmd]).await?; + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; - // 4-byte response delay. - let mut junk = [0; 1]; - bus.read(&mut junk).await?; + // 4-byte response delay. + let mut junk = [0; 1]; + bus.read(&mut junk).await?; - // Read data - bus.read(&mut data[..len / 4]).await?; - Ok(()) - } - }) - .await - .unwrap(); + // Read data + bus.read(&mut data[..len / 4]).await?; + Ok(()) + }) + .await + .unwrap(); // Advance ptr. addr += len as u32; @@ -1146,17 +1130,13 @@ where let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[cmd]).await?; - bus.write(&buf[..(len + 3) / 4]).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + bus.write(&buf[..(len + 3) / 4]).await?; + Ok(()) + }) + .await + .unwrap(); // Advance ptr. addr += len as u32; @@ -1270,21 +1250,17 @@ where let cmd = cmd_word(READ, INC_ADDR, func, addr, len); let mut buf = [0; 1]; - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[cmd]).await?; - if func == FUNC_BACKPLANE { - // 4-byte response delay. - bus.read(&mut buf).await?; - } - bus.read(&mut buf).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + if func == FUNC_BACKPLANE { + // 4-byte response delay. + bus.read(&mut buf).await?; + } + bus.read(&mut buf).await?; + Ok(()) + }) + .await + .unwrap(); buf[0] } @@ -1292,33 +1268,25 @@ where async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[cmd, val]).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd, val]).await?; + Ok(()) + }) + .await + .unwrap(); } async fn read32_swapped(&mut self, addr: u32) -> u32 { let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4); let mut buf = [0; 1]; - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[swap16(cmd)]).await?; - bus.read(&mut buf).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[swap16(cmd)]).await?; + bus.read(&mut buf).await?; + Ok(()) + }) + .await + .unwrap(); swap16(buf[0]) } @@ -1326,16 +1294,12 @@ where async fn write32_swapped(&mut self, addr: u32, val: u32) { let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); - self.spi - .transaction(|bus| { - let bus = unsafe { &mut *bus }; - async { - bus.write(&[swap16(cmd), swap16(val)]).await?; - Ok(()) - } - }) - .await - .unwrap(); + transaction!(&mut self.spi, |bus| async { + bus.write(&[swap16(cmd), swap16(val)]).await?; + Ok(()) + }) + .await + .unwrap(); } } From 753781a2639c3505ab046cb48acb6473b84b214b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 2 Oct 2022 22:24:59 +0200 Subject: [PATCH 0226/1575] Build docs in CI --- .github/workflows/doc.yml | 85 +++++++++++++++++++++++++++++++++ .github/workflows/rust.yml | 2 +- embassy-embedded-hal/Cargo.toml | 2 +- embassy-executor/Cargo.toml | 2 +- embassy-usb/Cargo.toml | 2 +- 5 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/doc.yml diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 000000000..349ca4040 --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,85 @@ +name: Docs + +on: + push: + branches: [master] + +env: + BUILDER_THREADS: '2' + +jobs: + doc: + runs-on: ubuntu-latest + + # Since stm32 crates take SO LONG to build, we split them + # into a separate job. This way it doesn't slow down updating + # the rest. + strategy: + matrix: + crates: + - stm32 + - rest + + # This will ensure at most one doc build job is running at a time + # (for stm32 and non-stm32 independently). + # If another job is already running, the new job will wait. + # If another job is already waiting, it'll be canceled. + # This means some commits will be skipped, but that's fine because + # we only care that the latest gets built. + concurrency: doc-${{ matrix.crates }} + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Install Rust targets + run: | + rustup target add x86_64-unknown-linux-gnu + rustup target add wasm32-unknown-unknown + rustup target add thumbv6m-none-eabi + rustup target add thumbv7m-none-eabi + rustup target add thumbv7em-none-eabi + rustup target add thumbv7em-none-eabihf + rustup target add thumbv8m.base-none-eabi + rustup target add thumbv8m.main-none-eabi + rustup target add thumbv8m.main-none-eabihf + + - name: Install docserver + run: | + wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder" + chmod +x /usr/local/bin/builder + + - name: build-stm32 + if: ${{ matrix.crates=='stm32' }} + run: | + mkdir crates + builder ./embassy-stm32 crates/embassy-stm32/git.zup + builder ./stm32-metapac crates/stm32-metapac/git.zup + + - name: build-rest + if: ${{ matrix.crates=='rest' }} + run: | + mkdir crates + builder ./embassy-boot/boot crates/embassy-boot/git.zup + builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup + builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup + builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup + builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup + builder ./embassy-executor crates/embassy-executor/git.zup + builder ./embassy-futures crates/embassy-futures/git.zup + builder ./embassy-lora crates/embassy-lora/git.zup + builder ./embassy-net crates/embassy-net/git.zup + builder ./embassy-nrf crates/embassy-nrf/git.zup + builder ./embassy-rp crates/embassy-rp/git.zup + builder ./embassy-sync crates/embassy-sync/git.zup + builder ./embassy-usb crates/embassy-usb/git.zup + builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup + + - name: upload + run: | + mkdir -p ~/.kube + echo "${{secrets.KUBECONFIG}}" > ~/.kube/config + POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) + kubectl cp crates $POD:/data + + \ No newline at end of file diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d2e8e316b..b93c8783d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,7 +11,7 @@ env: jobs: all: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: [build-nightly, build-stable, test] steps: - name: Done diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index fe8fac7c8..845f742e4 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/" features = ["nightly", "std"] -target = "thumbv7em-none-eabi" +target = "x86_64-unknown-linux-gnu" [features] std = [] diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index fa3d0b2b6..3b1c4ab46 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["nightly", "defmt", "unstable-traits"] +features = ["nightly", "defmt"] flavors = [ { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index aad54dbaf..1f705e9ff 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/src/" -features = ["defmt"] +features = ["defmt", "usbd-hid"] target = "thumbv7em-none-eabi" [features] From e8bb8faa23c1e8b78285646ca7e711bafe990e20 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 2 Oct 2022 15:08:58 -0700 Subject: [PATCH 0227/1575] rp i2c: allow blocking ops on async contexts --- embassy-rp/src/i2c.rs | 210 +++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f62cf0b86..e01692dc1 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -68,106 +68,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { into_ref!(scl, sda); Self::new_inner(peri, scl.map_into(), sda.map_into(), config) } - - fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { - if buffer.is_empty() { - return Err(Error::InvalidReadBufferLength); - } - - let p = T::regs(); - let lastindex = buffer.len() - 1; - for (i, byte) in buffer.iter_mut().enumerate() { - let first = i == 0; - let last = i == lastindex; - - // NOTE(unsafe) We have &mut self - unsafe { - // wait until there is space in the FIFO to write the next byte - while Self::tx_fifo_full() {} - - p.ic_data_cmd().write(|w| { - w.set_restart(restart && first); - w.set_stop(send_stop && last); - - w.set_cmd(true); - }); - - while Self::rx_fifo_len() == 0 { - self.read_and_clear_abort_reason()?; - } - - *byte = p.ic_data_cmd().read().dat(); - } - } - - Ok(()) - } - - fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - if bytes.is_empty() { - return Err(Error::InvalidWriteBufferLength); - } - - let p = T::regs(); - - for (i, byte) in bytes.iter().enumerate() { - let last = i == bytes.len() - 1; - - // NOTE(unsafe) We have &mut self - unsafe { - p.ic_data_cmd().write(|w| { - w.set_stop(send_stop && last); - w.set_dat(*byte); - }); - - // Wait until the transmission of the address/data from the - // internal shift register has completed. For this to function - // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The - // TX_EMPTY_CTRL flag was set in i2c_init. - while !p.ic_raw_intr_stat().read().tx_empty() {} - - let abort_reason = self.read_and_clear_abort_reason(); - - if abort_reason.is_err() || (send_stop && last) { - // If the transaction was aborted or if it completed - // successfully wait until the STOP condition has occured. - - while !p.ic_raw_intr_stat().read().stop_det() {} - - p.ic_clr_stop_det().read().clr_stop_det(); - } - - // Note the hardware issues a STOP automatically on an abort - // condition. Note also the hardware clears RX FIFO as well as - // TX on abort, ecause we set hwparam - // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - abort_reason?; - } - } - Ok(()) - } - - // ========================= - // Blocking public API - // ========================= - - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } - - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, true) - } - - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - Self::setup(address.into())?; - self.write_blocking_internal(bytes, false)?; - self.read_blocking_internal(buffer, true, true) - // Automatic Stop - } } static I2C_WAKER: AtomicWaker = AtomicWaker::new(); @@ -406,7 +306,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { self.read_async_internal(buffer, false, true).await } - pub async fn write_async(&mut self, addr: u16, bytes : impl IntoIterator) -> Result<(), Error> { + pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator) -> Result<(), Error> { Self::setup(addr)?; self.write_async_internal(bytes, true).await } @@ -581,12 +481,112 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { } } } + + fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if buffer.is_empty() { + return Err(Error::InvalidReadBufferLength); + } + + let p = T::regs(); + let lastindex = buffer.len() - 1; + for (i, byte) in buffer.iter_mut().enumerate() { + let first = i == 0; + let last = i == lastindex; + + // NOTE(unsafe) We have &mut self + unsafe { + // wait until there is space in the FIFO to write the next byte + while Self::tx_fifo_full() {} + + p.ic_data_cmd().write(|w| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); + + w.set_cmd(true); + }); + + while Self::rx_fifo_len() == 0 { + self.read_and_clear_abort_reason()?; + } + + *byte = p.ic_data_cmd().read().dat(); + } + } + + Ok(()) + } + + fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + if bytes.is_empty() { + return Err(Error::InvalidWriteBufferLength); + } + + let p = T::regs(); + + for (i, byte) in bytes.iter().enumerate() { + let last = i == bytes.len() - 1; + + // NOTE(unsafe) We have &mut self + unsafe { + p.ic_data_cmd().write(|w| { + w.set_stop(send_stop && last); + w.set_dat(*byte); + }); + + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} + + let abort_reason = self.read_and_clear_abort_reason(); + + if abort_reason.is_err() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occured. + + while !p.ic_raw_intr_stat().read().stop_det() {} + + p.ic_clr_stop_det().read().clr_stop_det(); + } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + abort_reason?; + } + } + Ok(()) + } + + // ========================= + // Blocking public API + // ========================= + + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } + + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, true) + } + + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + Self::setup(address.into())?; + self.write_blocking_internal(bytes, false)?; + self.read_blocking_internal(buffer, true, true) + // Automatic Stop + } } mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -594,7 +594,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { type Error = Error; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -602,7 +602,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { type Error = Error; fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -635,7 +635,7 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, Blocking> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From f075e624440af121da7a27a145e2acee0730c542 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Oct 2022 01:59:44 +0200 Subject: [PATCH 0228/1575] Use 1 thread in ci doc building. --- .github/workflows/doc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 349ca4040..49e1cf71c 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -5,7 +5,7 @@ on: branches: [master] env: - BUILDER_THREADS: '2' + BUILDER_THREADS: '1' jobs: doc: From cae84991790976387aa4d4b7afae90094a876b25 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 3 Oct 2022 01:00:03 -0700 Subject: [PATCH 0229/1575] rp i2c: clean up tx_abrt handling Make sure we always wait for the stop bit if there's a reason to - either because we sent one, or because there was a hardware tx abort. --- embassy-rp/src/i2c.rs | 136 ++++++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index e01692dc1..f004c522c 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -135,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let mut remaining = buffer.len(); let mut remaining_queue = buffer.len(); - let mut abort_reason = None; + let mut abort_reason = Ok(()); while remaining > 0 { // Waggle SCK - basically the same as write @@ -190,8 +190,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { match res { Err(reason) => { - abort_reason = Some(reason); - // XXX keep going anyway? + abort_reason = Err(reason); break; } Ok(rxfifo) => { @@ -207,29 +206,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }; } - // wait for stop condition to be emitted. - self.wait_on( - |_me| unsafe { - if !p.ic_raw_intr_stat().read().stop_det() && send_stop { - Poll::Pending - } else { - Poll::Ready(()) - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_stop_det(true); - w.set_m_tx_abrt(true); - }); - }, - ) - .await; - unsafe { p.ic_clr_stop_det().read() }; - - if let Some(abort_reason) = abort_reason { - return Err(abort_reason); - } - Ok(()) + self.wait_stop_det(abort_reason, send_stop).await } async fn write_async_internal( @@ -241,7 +218,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let mut bytes = bytes.into_iter().peekable(); - 'xmit: loop { + let res = 'xmit: loop { let tx_fifo_space = Self::tx_fifo_capacity(); for _ in 0..tx_fifo_space { @@ -256,49 +233,76 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }); } } else { - break 'xmit; + break 'xmit Ok(()); } } - self.wait_on( - |me| { - if let Err(abort_reason) = me.read_and_clear_abort_reason() { - Poll::Ready(Err(abort_reason)) - } else if !Self::tx_fifo_full() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }) - }, - ) - .await?; + let res = self + .wait_on( + |me| { + if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() { + Poll::Ready(abort_reason) + } else if !Self::tx_fifo_full() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_tx_empty(true); + w.set_m_tx_abrt(true); + }) + }, + ) + .await; + if res.is_err() { + break res; + } + }; + + self.wait_stop_det(res, send_stop).await + } + + /// Helper to wait for a stop bit, for both tx and rx. If we had an abort, + /// then we'll get a hardware-generated stop, otherwise wait for a stop if + /// we're expecting it. + /// + /// Also handles an abort which arises while processing the tx fifo. + async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> { + if had_abort.is_err() || do_stop { + let p = T::regs(); + + let had_abort2 = self + .wait_on( + |me| unsafe { + // We could see an abort while processing fifo backlog, + // so handle it here. + let abort = me.read_and_clear_abort_reason(); + if had_abort.is_ok() && abort.is_err() { + Poll::Ready(abort) + } else if p.ic_raw_intr_stat().read().stop_det() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + }, + |_me| unsafe { + p.ic_intr_mask().modify(|w| { + w.set_m_stop_det(true); + w.set_m_tx_abrt(true); + }); + }, + ) + .await; + unsafe { + p.ic_clr_stop_det().read(); + } + + had_abort.and(had_abort2) + } else { + had_abort } - - // wait for fifo to drain - self.wait_on( - |_me| unsafe { - if p.ic_raw_intr_stat().read().tx_empty() { - Poll::Ready(()) - } else { - Poll::Pending - } - }, - |_me| unsafe { - p.ic_intr_mask().modify(|w| { - w.set_m_tx_empty(true); - w.set_m_tx_abrt(true); - }); - }, - ) - .await; - - Ok(()) } pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> { From 4fd831e4a88fdba14751c8a71ae45fc0eac92c66 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 3 Oct 2022 18:50:03 -0700 Subject: [PATCH 0230/1575] rp async i2c: raise the tx_empty threshold Assert "tx_empty" interrupt a little early so there's time to wake up and start refilling the fifo before it drains. This avoids stalling the i2c bus if the tx fifo completely drains. --- embassy-rp/src/i2c.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index f004c522c..68cfc653e 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -243,12 +243,18 @@ impl<'d, T: Instance> I2c<'d, T, Async> { if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() { Poll::Ready(abort_reason) } else if !Self::tx_fifo_full() { + // resume if there's any space free in the tx fifo Poll::Ready(Ok(())) } else { Poll::Pending } }, |_me| unsafe { + // Set tx "free" threshold a little high so that we get + // woken before the fifo completely drains to minimize + // transfer stalls. + p.ic_tx_tl().write(|w| w.set_tx_tl(1)); + p.ic_intr_mask().modify(|w| { w.set_m_tx_empty(true); w.set_m_tx_abrt(true); From 59765590e0339e8f4294719b6e99a6ab110266d8 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 4 Oct 2022 16:38:11 +0200 Subject: [PATCH 0231/1575] Add required info to embassy-sync package Updates the README.md based on embassy-futures structure. --- embassy-sync/Cargo.toml | 10 ++++++++++ embassy-sync/README.md | 24 ++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 14ab1d003..584d5ba9f 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -2,6 +2,16 @@ name = "embassy-sync" version = "0.1.0" edition = "2021" +description = "no-std, no-alloc synchronization primitives with async support" +repository = "https://github.com/embassy-rs/embassy" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", + "concurrency", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-sync-v$VERSION/embassy-sync/src/" diff --git a/embassy-sync/README.md b/embassy-sync/README.md index 106295c0d..cc65cf6ef 100644 --- a/embassy-sync/README.md +++ b/embassy-sync/README.md @@ -1,12 +1,32 @@ # embassy-sync -Synchronization primitives and data structures with an async API: +An [Embassy](https://embassy.dev) project. + +Synchronization primitives and data structures with async support: - [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. - [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers. - [`Signal`](signal::Signal) - Signalling latest value to a single consumer. -- [`Mutex`](mutex::Mutex) - A Mutex for synchronizing state between asynchronous tasks. +- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks. - [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits. - [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. - [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API. - [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s. + +## Interoperability + +Futures from this crate can run on any executor. + +## Minimum supported Rust version (MSRV) + +Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. + +## 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. From 530182d6683531f7c259448e6c54c866f35837c7 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 5 Oct 2022 15:15:03 +0200 Subject: [PATCH 0232/1575] Forgot to add space function to immediate publisher --- embassy-sync/src/pubsub/publisher.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index faa67d947..e1edc9eb9 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -123,6 +123,14 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> { pub fn try_publish(&self, message: T) -> Result<(), T> { self.channel.publish_with_context(message, None) } + + /// The amount of messages that can still be published without having to wait or without having to lag the subscribers + /// + /// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something. + /// So checking doesn't give any guarantees.* + pub fn space(&self) -> usize { + self.channel.space() + } } /// An immediate publisher that holds a dynamic reference to the channel From d49d1b6b1cf6de9577816397db3c41f6e93aa4e6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Oct 2022 17:08:02 +0200 Subject: [PATCH 0233/1575] ci/doc: build embassy-time too. --- .github/workflows/doc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 49e1cf71c..eb460e738 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -72,6 +72,7 @@ jobs: builder ./embassy-nrf crates/embassy-nrf/git.zup builder ./embassy-rp crates/embassy-rp/git.zup builder ./embassy-sync crates/embassy-sync/git.zup + builder ./embassy-time crates/embassy-time/git.zup builder ./embassy-usb crates/embassy-usb/git.zup builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup From 9dca368c3dd1a8f00295b21c87d4fbb94afb60a5 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 7 Oct 2022 13:29:56 +0300 Subject: [PATCH 0234/1575] Use RccPeripheral for adc_v2 --- embassy-stm32/src/adc/mod.rs | 4 ++-- embassy-stm32/src/adc/v2.rs | 26 +++++++++----------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 8da13073e..fba016a77 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -30,9 +30,9 @@ pub(crate) mod sealed { } } -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_v2)))] pub trait Instance: sealed::Instance + 'static {} -#[cfg(adc_f1)] +#[cfg(any(adc_f1, adc_v2))] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} #[cfg(all(not(adc_f1), not(adc_v1)))] pub trait Common: sealed::Common + 'static {} diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 25b7ba967..70e3b73b3 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -12,21 +12,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -#[cfg(not(any(rcc_f4, rcc_f7)))] -fn enable() { - todo!() -} - -#[cfg(any(rcc_f4, rcc_f7))] -fn enable() { - critical_section::with(|_| unsafe { - // TODO do not enable all adc clocks if not needed - crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true)); - crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true)); - crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true)); - }); -} - pub enum Resolution { TwelveBit, TenBit, @@ -164,9 +149,10 @@ where { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(_peri); - enable(); + T::enable(); + T::reset(); - let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) }; + let presc = unsafe { Prescaler::from_pclk2(T::frequency()) }; unsafe { T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); } @@ -288,3 +274,9 @@ where } } } + +impl<'d, T: Instance> Drop for Adc<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} From 6718ca3a9412c9fbd5f1161b1123664024ffbe37 Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 7 Oct 2022 12:41:56 +0200 Subject: [PATCH 0235/1575] all Cargo.toml: Add license to all crate Cargo.toml files Closes: https://github.com/embassy-rs/embassy/issues/1002 --- docs/modules/ROOT/examples/basic/Cargo.toml | 1 + .../ROOT/examples/layer-by-layer/blinky-async/Cargo.toml | 1 + .../modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml | 1 + .../modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml | 1 + .../modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml | 1 + embassy-boot/boot/Cargo.toml | 1 + embassy-boot/nrf/Cargo.toml | 1 + embassy-boot/stm32/Cargo.toml | 1 + embassy-cortex-m/Cargo.toml | 1 + embassy-embedded-hal/Cargo.toml | 1 + embassy-executor/Cargo.toml | 1 + embassy-hal-common/Cargo.toml | 1 + embassy-lora/Cargo.toml | 1 + embassy-macros/Cargo.toml | 1 + embassy-net/Cargo.toml | 1 + embassy-nrf/Cargo.toml | 1 + embassy-rp/Cargo.toml | 1 + embassy-stm32/Cargo.toml | 1 + embassy-time/Cargo.toml | 1 + embassy-usb-driver/Cargo.toml | 3 ++- embassy-usb/Cargo.toml | 1 + examples/boot/application/nrf/Cargo.toml | 1 + examples/boot/application/stm32f3/Cargo.toml | 1 + examples/boot/application/stm32f7/Cargo.toml | 1 + examples/boot/application/stm32h7/Cargo.toml | 1 + examples/boot/application/stm32l0/Cargo.toml | 1 + examples/boot/application/stm32l1/Cargo.toml | 1 + examples/boot/application/stm32l4/Cargo.toml | 1 + examples/boot/application/stm32wl/Cargo.toml | 1 + examples/boot/bootloader/nrf/Cargo.toml | 1 + examples/boot/bootloader/stm32/Cargo.toml | 1 + examples/nrf-rtos-trace/Cargo.toml | 1 + examples/nrf/Cargo.toml | 1 + examples/rp/Cargo.toml | 1 + examples/std/Cargo.toml | 1 + examples/stm32f0/Cargo.toml | 1 + examples/stm32f1/Cargo.toml | 1 + examples/stm32f2/Cargo.toml | 1 + examples/stm32f3/Cargo.toml | 1 + examples/stm32f4/Cargo.toml | 1 + examples/stm32f7/Cargo.toml | 1 + examples/stm32g0/Cargo.toml | 1 + examples/stm32g4/Cargo.toml | 1 + examples/stm32h7/Cargo.toml | 1 + examples/stm32l0/Cargo.toml | 1 + examples/stm32l1/Cargo.toml | 1 + examples/stm32l4/Cargo.toml | 1 + examples/stm32l5/Cargo.toml | 1 + examples/stm32u5/Cargo.toml | 1 + examples/stm32wb/Cargo.toml | 1 + examples/stm32wl/Cargo.toml | 1 + examples/wasm/Cargo.toml | 1 + stm32-gen-features/Cargo.toml | 1 + stm32-metapac-gen/Cargo.toml | 1 + tests/rp/Cargo.toml | 1 + tests/stm32/Cargo.toml | 1 + xtask/Cargo.toml | 1 + 57 files changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index ae124a871..c13f546e2 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -3,6 +3,7 @@ authors = ["Dario Nieuwenhuis "] edition = "2018" name = "embassy-basic-example" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index e2933076f..c9a963d4d 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-async" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml index dbd3aba8b..f86361dd5 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-hal" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml index 0dd326015..9733658b6 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-irq" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml index e7f4f5d1f..a077f1828 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml @@ -2,6 +2,7 @@ name = "blinky-pac" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index a42f88688..54c67a375 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-boot" version = "0.1.0" description = "Bootloader using Embassy" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/" diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index 234393e7c..c6af70144 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-boot-nrf" version = "0.1.0" description = "Bootloader lib for nRF chips" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/" diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index ad4657e0d..9d12c6cfd 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-boot-stm32" version = "0.1.0" description = "Bootloader lib for STM32 chips" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/" diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml index 7efced669..5c5718d50 100644 --- a/embassy-cortex-m/Cargo.toml +++ b/embassy-cortex-m/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-cortex-m" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/" diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 845f742e4..d0be6d195 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-embedded-hal" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 3b1c4ab46..d0f51646d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-executor" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-common/Cargo.toml index 58f0af6ab..e8617c02f 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-common/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-hal-common" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [features] diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index dcb0d8245..0e7a982a1 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-lora" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/" diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 03fa79dd8..91d5ec8a3 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-macros" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] syn = { version = "1.0.76", features = ["full", "extra-traits"] } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index d5b13204e..967ef26a7 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-net" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 58b820242..5459bc90c 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-nrf" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/" diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 3aca5dbb4..c56858415 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-rp" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fbe37fe3d..2610e5687 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-stm32" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c3b361b8a..c51a71d01 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-time" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index b525df337..d22bf7d72 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-usb-driver" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -13,4 +14,4 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } \ No newline at end of file +log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1f705e9ff..b59ba8a22 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-usb" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/" diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index b9ff92578..a5d82b601 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-nrf-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index ce1e6fe4a..3a1843562 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32f3-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 2fc7ae834..8d9c4490e 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32f7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index fd809714a..b4314aa72 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32h7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 470eca52a..a17d336a6 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32l0-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 2b4b29357..683f2c860 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32l1-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 40bddd194..b879c0d76 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32l4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 5b4a61e8f..e3bc0e49c 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-boot-stm32wl-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index aa2a13ecb..b417a40d1 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "nrf-bootloader-example" version = "0.1.0" description = "Bootloader for nRF chips" +license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 491777103..4ddd1c99c 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "stm32-bootloader-example" version = "0.1.0" description = "Example bootloader for STM32 chips" +license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 87c9f33f5..d8c24dfad 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-nrf-rtos-trace-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] default = ["log", "nightly"] diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a5d340c69..9ebd04845 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-nrf-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] default = ["nightly"] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 1bb6432e6..a5af8b2f0 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-rp-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index dbfd9d625..b9bd1e718 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-std-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index c82b79c86..a56c546ee 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-stm32f0-examples" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index e6553789a..6be131f30 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f1-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 60cd54bd9..f6adda2a3 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f2-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index f5b0b880c..27188dd19 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f3-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index ea5c47a43..6d4f09fba 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 6c2f846fe..dad92c0fc 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32f7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 6baf17f36..f5673718d 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32g0-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index d8c05a979..ecda28805 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32g4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index e725e03cb..1a05b9ecb 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32h7-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7e61f0c19..7e1120f48 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l0-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] default = ["nightly"] diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index a943c73d2..9460febf5 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l1-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 2e2d07dc3..657605ebe 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l4-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 9ebab6476..63eac3ed2 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32l5-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 164940586..3d704011b 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32u5-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 923833e46..5b96fa191 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32wb-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 94e0fb830..c827d2b71 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32wl-examples" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index ea61fb921..e0e799a34 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-wasm-example" version = "0.1.0" +license = "MIT OR Apache-2.0" [lib] crate-type = ["cdylib"] diff --git a/stm32-gen-features/Cargo.toml b/stm32-gen-features/Cargo.toml index f92d127ea..f4aa08ebe 100644 --- a/stm32-gen-features/Cargo.toml +++ b/stm32-gen-features/Cargo.toml @@ -2,6 +2,7 @@ name = "gen_features" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml index 0ec2075f3..3c1dab57a 100644 --- a/stm32-metapac-gen/Cargo.toml +++ b/stm32-metapac-gen/Cargo.toml @@ -2,6 +2,7 @@ name = "stm32-metapac-gen" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" [dependencies] diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 2745aef06..d6770d6e9 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-rp-tests" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index daae3d464..bebbf557e 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "embassy-stm32-tests" version = "0.1.0" +license = "MIT OR Apache-2.0" [features] stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index d9d6c9b26..696cfdafe 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -2,6 +2,7 @@ edition = "2021" name = "xtask" version = "0.1.0" +license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.43" From df7174ecb03f466d4b97f2ce1a11e687317bd93a Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 7 Oct 2022 14:31:55 +0300 Subject: [PATCH 0236/1575] Fix internal channel reading on adc_v2 --- embassy-stm32/src/adc/mod.rs | 5 ++ embassy-stm32/src/adc/v2.rs | 149 ++++++++++++++++++++++---------- embassy-stm32/src/adc/v4.rs | 8 -- examples/stm32f4/src/bin/adc.rs | 25 +++++- 4 files changed, 130 insertions(+), 57 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index fba016a77..0eb4eba73 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -28,6 +28,10 @@ pub(crate) mod sealed { pub trait AdcPin { fn channel(&self) -> u8; } + + pub trait InternalChannel { + fn channel(&self) -> u8; + } } #[cfg(not(any(adc_f1, adc_v2)))] @@ -37,6 +41,7 @@ pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} #[cfg(all(not(adc_f1), not(adc_v1)))] pub trait Common: sealed::Common + 'static {} pub trait AdcPin: sealed::AdcPin {} +pub trait InternalChannel: sealed::InternalChannel {} #[cfg(not(stm32h7))] foreach_peripheral!( diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 70e3b73b3..4fe4ad1f0 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -3,7 +3,9 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; +use super::InternalChannel; use crate::adc::{AdcPin, Instance}; +use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -12,6 +14,9 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; +/// ADC turn-on time +pub const ADC_POWERUP_TIME_US: u32 = 3; + pub enum Resolution { TwelveBit, TenBit, @@ -46,24 +51,53 @@ impl Resolution { } pub struct VrefInt; -impl AdcPin for VrefInt {} -impl super::sealed::AdcPin for VrefInt { +impl InternalChannel for VrefInt {} +impl super::sealed::InternalChannel for VrefInt { fn channel(&self) -> u8 { 17 } } +impl VrefInt { + /// Time needed for internal voltage reference to stabilize + pub fn start_time_us() -> u32 { + 10 + } +} + pub struct Temperature; -impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { - 16 + cfg_if::cfg_if! { + if #[cfg(any(stm32f40, stm32f41))] { + 16 + } else { + 18 + } + } + } +} + +impl Temperature { + /// Converts temperature sensor reading in millivolts to degrees celcius + pub fn to_celcius(sample_mv: u16) -> f32 { + // From 6.3.22 Temperature sensor characteristics + const V25: i32 = 760; // mV + const AVG_SLOPE: f32 = 2.5; // mV/C + + (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0 + } + + /// Time needed for temperature sensor readings to stabilize + pub fn start_time_us() -> u32 { + 10 } } pub struct Vbat; -impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } @@ -152,19 +186,16 @@ where T::enable(); T::reset(); - let presc = unsafe { Prescaler::from_pclk2(T::frequency()) }; + let presc = Prescaler::from_pclk2(T::frequency()); unsafe { T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); - } - unsafe { - // disable before config is set T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); + reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); }); } - delay.delay_us(20); // TODO? + delay.delay_us(ADC_POWERUP_TIME_US); Self { sample_time: Default::default(), @@ -194,6 +225,45 @@ where ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 } + /// Enables internal voltage reference and returns [VrefInt], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vrefint(&self) -> VrefInt { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); + } + + VrefInt {} + } + + /// Enables internal temperature sensor and returns [Temperature], which can be used in + /// [Adc::read_internal()] to perform conversion. + /// + /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, + /// temperature sensor will return vbat value. + pub fn enable_temperature(&self) -> Temperature { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); + } + + Temperature {} + } + + /// Enables vbat input and returns [Vbat], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vbat(&self) -> Vbat { + unsafe { + T::common_regs().ccr().modify(|reg| { + reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED); + }); + } + + Vbat {} + } + /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { @@ -224,44 +294,31 @@ where P: crate::gpio::sealed::Pin, { unsafe { - // dissable ADC - T::regs().cr2().modify(|reg| { - reg.set_swstart(false); - }); - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); - }); - pin.set_as_analog(); - // Configure ADC - T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); - - // Select channel - T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); - - // Configure channel - Self::set_channel_sample_time(pin.channel(), self.sample_time); - - // enable adc - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); - }); - - let val = self.convert(); - - // dissable ADC - T::regs().cr2().modify(|reg| { - reg.set_swstart(false); - }); - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::DISABLED); - }); - - val + self.read_channel(pin.channel()) } } + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + unsafe { self.read_channel(channel.channel()) } + } + + unsafe fn read_channel(&mut self, channel: u8) -> u16 { + // Configure ADC + T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); + + // Select channel + T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); + + // Configure channel + Self::set_channel_sample_time(channel, self.sample_time); + + let val = self.convert(); + + val + } + unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { if ch <= 9 { T::regs() diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index d356d7b66..7e6a219e1 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -50,14 +50,6 @@ impl Resolution { } } -pub trait InternalChannel: sealed::InternalChannel {} - -mod sealed { - pub trait InternalChannel { - fn channel(&self) -> u8; - } -} - // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 871185074..6f80c1ef1 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -2,9 +2,10 @@ #![no_main] #![feature(type_alias_impl_trait)] +use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::Adc; +use embassy_stm32::adc::{Adc, SampleTime, Temperature, VrefInt}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -13,12 +14,30 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut delay = Delay; + let mut adc = Adc::new(p.ADC1, &mut delay); let mut pin = p.PC1; + let mut vrefint = adc.enable_vrefint(); + let mut temp = adc.enable_temperature(); + + // Startup delay can be combined to the maximum of either + delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); + loop { + // Read pin let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); + + // Read internal temperature + let v = adc.read_internal(&mut temp); + let celcius = Temperature::to_celcius(adc.to_millivolts(v)); + info!("Internal temp: {} ({} C)", v, celcius); + + // Read internal voltage reference + let v = adc.read_internal(&mut vrefint); + info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); + Timer::after(Duration::from_millis(100)).await; } } From 322cfafed353ddfbe62121238ef97c56dd7a6eed Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 7 Oct 2022 14:53:03 +0300 Subject: [PATCH 0237/1575] Fix adc_v4 compilation --- embassy-stm32/src/adc/v4.rs | 8 ++++---- examples/stm32f4/src/bin/adc.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 7e6a219e1..eda2b2a72 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance}; +use super::{AdcPin, Instance, InternalChannel}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -53,7 +53,7 @@ impl Resolution { // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} -impl sealed::InternalChannel for VrefInt { +impl super::sealed::InternalChannel for VrefInt { fn channel(&self) -> u8 { 19 } @@ -61,7 +61,7 @@ impl sealed::InternalChannel for VrefInt { pub struct Temperature; impl InternalChannel for Temperature {} -impl sealed::InternalChannel for Temperature { +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 18 } @@ -69,7 +69,7 @@ impl sealed::InternalChannel for Temperature { pub struct Vbat; impl InternalChannel for Vbat {} -impl sealed::InternalChannel for Vbat { +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { // TODO this should be 14 for H7a/b/35 17 diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 6f80c1ef1..1d030f7dc 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -5,7 +5,7 @@ use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime, Temperature, VrefInt}; +use embassy_stm32::adc::{Adc, Temperature, VrefInt}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; From aa8ba2115c4606b53dedce1af2da5de2fd59f563 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 8 Oct 2022 11:35:11 +0800 Subject: [PATCH 0238/1575] Expose Pin::pin() and Pin::bank() as public --- embassy-rp/src/gpio.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a28bae96b..f79f592b4 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -599,12 +599,12 @@ pub(crate) mod sealed { fn pin_bank(&self) -> u8; #[inline] - fn pin(&self) -> u8 { + fn _pin(&self) -> u8 { self.pin_bank() & 0x1f } #[inline] - fn bank(&self) -> Bank { + fn _bank(&self) -> Bank { if self.pin_bank() & 0x20 == 0 { Bank::Bank0 } else { @@ -613,35 +613,35 @@ pub(crate) mod sealed { } fn io(&self) -> pac::io::Gpio { - let block = match self.bank() { + let block = match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, Bank::Qspi => crate::pac::IO_QSPI, }; - block.gpio(self.pin() as _) + block.gpio(self._pin() as _) } fn pad_ctrl(&self) -> Reg { - let block = match self.bank() { + let block = match self._bank() { Bank::Bank0 => crate::pac::PADS_BANK0, Bank::Qspi => crate::pac::PADS_QSPI, }; - block.gpio(self.pin() as _) + block.gpio(self._pin() as _) } fn sio_out(&self) -> pac::sio::Gpio { - SIO.gpio_out(self.bank() as _) + SIO.gpio_out(self._bank() as _) } fn sio_oe(&self) -> pac::sio::Gpio { - SIO.gpio_oe(self.bank() as _) + SIO.gpio_oe(self._bank() as _) } fn sio_in(&self) -> Reg { - SIO.gpio_in(self.bank() as _) + SIO.gpio_in(self._bank() as _) } fn int_proc(&self) -> pac::io::Int { - let io_block = match self.bank() { + let io_block = match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, Bank::Qspi => crate::pac::IO_QSPI, }; @@ -658,6 +658,18 @@ pub trait Pin: Peripheral

+ Into + sealed::Pin + Sized + 'stat pin_bank: self.pin_bank(), } } + + /// Returns the pin number within a bank + #[inline] + fn pin(&self) -> u8 { + self._pin() + } + + /// Returns the bank of this pin + #[inline] + fn bank(&self) -> Bank { + self._bank() + } } pub struct AnyPin { From f554962f54b9f9e8f85a0dd099635619bd1acd1d Mon Sep 17 00:00:00 2001 From: ceekdee Date: Sat, 8 Oct 2022 14:32:22 -0500 Subject: [PATCH 0239/1575] Improve generics and consolidate antenna handling --- embassy-lora/src/sx126x/mod.rs | 48 ++++++------------- .../src/sx126x/sx126x_lora/board_specific.rs | 39 ++++++++++----- embassy-lora/src/sx126x/sx126x_lora/mod.rs | 19 ++++---- .../src/sx126x/sx126x_lora/subroutine.rs | 34 ++++--------- examples/nrf/src/bin/lora_p2p_report.rs | 8 ++-- examples/nrf/src/bin/lora_p2p_sense.rs | 8 ++-- 6 files changed, 69 insertions(+), 87 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index d67c7b109..ed8cb4059 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -13,35 +13,29 @@ use sx126x_lora::LoRa; use self::sx126x_lora::mod_params::RadioError; /// Semtech Sx126x LoRa peripheral -pub struct Sx126xRadio +pub struct Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { - pub lora: LoRa, + pub lora: LoRa, } -impl Sx126xRadio +impl Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { pub async fn new( spi: SPI, - cs: CS, - reset: RESET, - antenna_rx: ANTRX, - antenna_tx: ANTTX, + cs: CTRL, + reset: CTRL, + antenna_rx: CTRL, + antenna_tx: CTRL, dio1: WAIT, busy: WAIT, enable_public_network: bool, @@ -53,13 +47,10 @@ where } } -impl Timings for Sx126xRadio +impl Timings for Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { @@ -71,13 +62,10 @@ where } } -impl PhyRxTx for Sx126xRadio +impl PhyRxTx for Sx126xRadio where SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - ANTRX: OutputPin + 'static, - ANTTX: OutputPin + 'static, + CTRL: OutputPin + 'static, WAIT: Wait + 'static, BUS: Error + Format + 'static, { @@ -86,10 +74,7 @@ where type TxFuture<'m> = impl Future> + 'm where SPI: 'm, - CS: 'm, - RESET: 'm, - ANTRX: 'm, - ANTTX: 'm, + CTRL: 'm, WAIT: 'm, BUS: 'm; @@ -122,10 +107,7 @@ where type RxFuture<'m> = impl Future> + 'm where SPI: 'm, - CS: 'm, - RESET: 'm, - ANTRX: 'm, - ANTTX: 'm, + CTRL: 'm, WAIT: 'm, BUS: 'm; diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs index 5b4891c04..1fb085887 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -13,13 +13,10 @@ const BRD_TCXO_WAKEUP_TIME: u32 = 10; // Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. // The base implementation supports the RAK4631 board. -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. @@ -210,14 +207,34 @@ where RadioType::SX1262 } - // Initialize the RF switch I/O pins interface - pub(super) async fn brd_ant_sw_on(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently + // Quiesce the antenna(s). + pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_low().map_err(|_| AntRx)?; + } + Ok(()) } - // De-initialize the RF switch I/O pins interface for MCU low power modes - pub(super) async fn brd_ant_sw_off(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently + // Prepare the antenna(s) for a receive operation + pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_high().map_err(|_| AntRx)?; + } + Ok(()) + } + + // Prepare the antenna(s) for a send operation + pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { + #[cfg(feature = "rak4631")] + { + self.antenna_rx.set_low().map_err(|_| AntRx)?; + self.antenna_tx.set_high().map_err(|_| AntTx)?; + } + Ok(()) } // Check if the given RF frequency is supported by the hardware diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs index 53fbde749..280f26d51 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/mod.rs @@ -26,12 +26,12 @@ const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, const RADIO_WAKEUP_TIME: u32 = 3; /// Provides high-level access to Semtech SX126x-based boards -pub struct LoRa { +pub struct LoRa { spi: SPI, - cs: CS, - reset: RESET, - antenna_rx: ANTRX, - antenna_tx: ANTTX, + cs: CTRL, + reset: CTRL, + antenna_rx: CTRL, + antenna_tx: CTRL, dio1: WAIT, busy: WAIT, operating_mode: RadioMode, @@ -45,17 +45,14 @@ pub struct LoRa { frequency_error: u32, } -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () - pub fn new(spi: SPI, cs: CS, reset: RESET, antenna_rx: ANTRX, antenna_tx: ANTTX, dio1: WAIT, busy: WAIT) -> Self { + pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self { Self { spi, cs, diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs index 02a0a72ec..283e60993 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -19,13 +19,10 @@ const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; // Provides board-specific functionality for Semtech SX126x-based boards -impl LoRa +impl LoRa where SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, - ANTRX: OutputPin, - ANTTX: OutputPin, + CTRL: OutputPin, WAIT: Wait, { // Initialize the radio driver @@ -44,7 +41,6 @@ where let operating_mode = self.brd_get_operating_mode(); if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { self.brd_wakeup().await?; - self.brd_ant_sw_on().await?; } self.brd_wait_on_busy().await?; Ok(()) @@ -117,10 +113,7 @@ where // Set the radio in sleep mode pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { - self.brd_ant_sw_off().await?; - - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_sleep()?; if !sleep_config.warm_start { self.image_calibrated = false; @@ -141,8 +134,7 @@ where self.brd_set_operating_mode(RadioMode::StandbyXOSC); } - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_sleep()?; Ok(()) } @@ -162,8 +154,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_set_operating_mode(RadioMode::Transmit); self.brd_write_command(OpCode::SetTx, &buffer).await?; @@ -178,8 +169,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_set_operating_mode(RadioMode::Receive); self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; @@ -195,8 +185,7 @@ where Self::timeout_3(timeout), ]; - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_set_operating_mode(RadioMode::Receive); // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity @@ -225,8 +214,7 @@ where // Set the radio in CAD mode pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_high(); - let _fix_tx = self.antenna_tx.set_low(); + self.brd_ant_set_rx()?; self.brd_write_command(OpCode::SetCAD, &[]).await?; self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); @@ -235,8 +223,7 @@ where // Set the radio in continuous wave transmission mode pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; self.brd_set_operating_mode(RadioMode::Transmit); @@ -245,8 +232,7 @@ where // Set the radio in continuous preamble transmission mode pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { - let _fix_rx = self.antenna_rx.set_low(); - let _fix_tx = self.antenna_tx.set_high(); + self.brd_ant_set_tx()?; self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; self.brd_set_operating_mode(RadioMode::Transmit); diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs index 46cb848b1..4ba3d30ce 100644 --- a/examples/nrf/src/bin/lora_p2p_report.rs +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -24,12 +24,12 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); - let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { Ok(r) => r, diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 3c6bb8767..405a8403f 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -97,12 +97,12 @@ async fn main(spawner: Spawner) { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); - let cs = Output::new(p.P1_10, Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06, Level::High, OutputDrive::Standard); + let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07, Level::Low, OutputDrive::Standard); + let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { Ok(r) => r, From e1faf8860776f6ad2bac2f3b06e7160fe00da7df Mon Sep 17 00:00:00 2001 From: huntc Date: Sun, 9 Oct 2022 13:07:25 +1100 Subject: [PATCH 0240/1575] Removes some of the code duplication for UarteWithIdle This commit removes some of the code duplication for UarteWithIdle at the expense of requiring a split. As the example illustrates though, this expense seems worth the benefit in terms of maintenance, and the avoidance of copying over methods. My main motivation for this commit was actually due to the `event_endtx` method not having been copied across. --- embassy-nrf/src/uarte.rs | 468 +++++++++++------------------- examples/nrf/src/bin/uart_idle.rs | 7 +- 2 files changed, 170 insertions(+), 305 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index d99599112..636d6c7a3 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> { (self.tx, self.rx) } + /// Split the Uarte into a transmitter and receiver that will + /// return on idle, which is determined as the time it takes + /// for two bytes to be received. + pub fn split_with_idle( + self, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) { + let mut timer = Timer::new(timer); + + into_ref!(ppi_ch1, ppi_ch2); + + let r = T::regs(); + + // BAUDRATE register values are `baudrate * 2^32 / 16000000` + // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values + // + // We want to stop RX if line is idle for 2 bytes worth of time + // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) + // This gives us the amount of 16M ticks for 20 bits. + let baudrate = r.baudrate.read().baudrate().variant().unwrap(); + let timeout = 0x8000_0000 / (baudrate as u32 / 40); + + timer.set_frequency(Frequency::F16MHz); + timer.cc(0).write(timeout); + timer.cc(0).short_compare_clear(); + timer.cc(0).short_compare_stop(); + + let mut ppi_ch1 = Ppi::new_one_to_two( + ppi_ch1.map_into(), + Event::from_reg(&r.events_rxdrdy), + timer.task_clear(), + timer.task_start(), + ); + ppi_ch1.enable(); + + let mut ppi_ch2 = Ppi::new_one_to_one( + ppi_ch2.map_into(), + timer.cc(0).event_compare(), + Task::from_reg(&r.tasks_stoprx), + ); + ppi_ch2.enable(); + + ( + self.tx, + UarteRxWithIdle { + rx: self.rx, + timer, + ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + }, + ) + } + /// Return the endtx event for use with PPI pub fn event_endtx(&self) -> Event { let r = T::regs(); @@ -597,6 +652,117 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { } } +pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { + rx: UarteRx<'d, T>, + timer: Timer<'d, U>, + ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, +} + +impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.ppi_ch1.disable(); + self.rx.read(buffer).await + } + + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.ppi_ch1.disable(); + self.rx.blocking_read(buffer) + } + + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let ptr = buffer.as_ptr(); + let len = buffer.len(); + + let r = T::regs(); + let s = T::state(); + + self.ppi_ch1.enable(); + + let drop = OnDrop::new(|| { + self.timer.stop(); + + r.intenclr.write(|w| w.endrx().clear()); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + }); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenset.write(|w| w.endrx().set()); + + compiler_fence(Ordering::SeqCst); + + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + poll_fn(|cx| { + s.endrx_waker.register(cx.waker()); + if r.events_endrx.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + let n = r.rxd.amount.read().amount().bits() as usize; + + self.timer.stop(); + r.events_rxstarted.reset(); + + drop.defuse(); + + Ok(n) + } + + pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let ptr = buffer.as_ptr(); + let len = buffer.len(); + + let r = T::regs(); + + self.ppi_ch1.enable(); + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + r.events_endrx.reset(); + r.intenclr.write(|w| w.endrx().clear()); + + compiler_fence(Ordering::SeqCst); + + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + while r.events_endrx.read().bits() == 0 {} + + compiler_fence(Ordering::SeqCst); + let n = r.rxd.amount.read().amount().bits() as usize; + + self.timer.stop(); + r.events_rxstarted.reset(); + + Ok(n) + } +} + #[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { // Do nothing @@ -665,270 +831,6 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) { } } -/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels, -/// allowing it to implement the ReadUntilIdle trait. -pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> { - tx: UarteTx<'d, U>, - rx: UarteRxWithIdle<'d, U, T>, -} - -impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> { - /// Create a new UARTE without hardware flow control - pub fn new( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(rxd, txd); - Self::new_inner( - uarte, - timer, - ppi_ch1, - ppi_ch2, - irq, - rxd.map_into(), - txd.map_into(), - None, - None, - config, - ) - } - - /// Create a new UARTE with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - cts: impl Peripheral

+ 'd, - rts: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(rxd, txd, cts, rts); - Self::new_inner( - uarte, - timer, - ppi_ch1, - ppi_ch2, - irq, - rxd.map_into(), - txd.map_into(), - Some(cts.map_into()), - Some(rts.map_into()), - config, - ) - } - - fn new_inner( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: PeripheralRef<'d, AnyPin>, - txd: PeripheralRef<'d, AnyPin>, - cts: Option>, - rts: Option>, - config: Config, - ) -> Self { - let baudrate = config.baudrate; - let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split(); - - let mut timer = Timer::new(timer); - - into_ref!(ppi_ch1, ppi_ch2); - - let r = U::regs(); - - // BAUDRATE register values are `baudrate * 2^32 / 16000000` - // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values - // - // We want to stop RX if line is idle for 2 bytes worth of time - // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) - // This gives us the amount of 16M ticks for 20 bits. - let timeout = 0x8000_0000 / (baudrate as u32 / 40); - - timer.set_frequency(Frequency::F16MHz); - timer.cc(0).write(timeout); - timer.cc(0).short_compare_clear(); - timer.cc(0).short_compare_stop(); - - let mut ppi_ch1 = Ppi::new_one_to_two( - ppi_ch1.map_into(), - Event::from_reg(&r.events_rxdrdy), - timer.task_clear(), - timer.task_start(), - ); - ppi_ch1.enable(); - - let mut ppi_ch2 = Ppi::new_one_to_one( - ppi_ch2.map_into(), - timer.cc(0).event_compare(), - Task::from_reg(&r.tasks_stoprx), - ); - ppi_ch2.enable(); - - Self { - tx, - rx: UarteRxWithIdle { - rx, - timer, - ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - }, - } - } - - /// Split the Uarte into a transmitter and receiver, which is - /// particuarly useful when having two tasks correlating to - /// transmitting and receiving. - pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) { - (self.tx, self.rx) - } - - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.read(buffer).await - } - - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.write(buffer).await - } - - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.rx.blocking_read(buffer) - } - - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.tx.blocking_write(buffer) - } - - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.rx.read_until_idle(buffer).await - } - - pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.rx.blocking_read_until_idle(buffer) - } -} - -pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> { - rx: UarteRx<'d, U>, - timer: Timer<'d, T>, - ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, -} - -impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> { - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.ppi_ch1.disable(); - self.rx.read(buffer).await - } - - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.ppi_ch1.disable(); - self.rx.blocking_read(buffer) - } - - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let ptr = buffer.as_ptr(); - let len = buffer.len(); - - let r = U::regs(); - let s = U::state(); - - self.ppi_ch1.enable(); - - let drop = OnDrop::new(|| { - self.timer.stop(); - - r.intenclr.write(|w| w.endrx().clear()); - r.events_rxto.reset(); - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - - while r.events_endrx.read().bits() == 0 {} - }); - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - - r.events_endrx.reset(); - r.intenset.write(|w| w.endrx().set()); - - compiler_fence(Ordering::SeqCst); - - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - poll_fn(|cx| { - s.endrx_waker.register(cx.waker()); - if r.events_endrx.read().bits() != 0 { - return Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; - - self.timer.stop(); - r.events_rxstarted.reset(); - - drop.defuse(); - - Ok(n) - } - - pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let ptr = buffer.as_ptr(); - let len = buffer.len(); - - let r = U::regs(); - - self.ppi_ch1.enable(); - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - - r.events_endrx.reset(); - r.intenclr.write(|w| w.endrx().clear()); - - compiler_fence(Ordering::SeqCst); - - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - while r.events_endrx.read().bits() == 0 {} - - compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; - - self.timer.stop(); - r.events_rxstarted.reset(); - - Ok(n) - } -} pub(crate) mod sealed { use core::sync::atomic::AtomicU8; @@ -1006,18 +908,6 @@ mod eh02 { Ok(()) } } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write for UarteWithIdle<'d, U, T> { - type Error = Error; - - fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn bflush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } } #[cfg(feature = "unstable-traits")] @@ -1067,10 +957,6 @@ mod eh1 { impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { type Error = Error; } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> { - type Error = Error; - } } #[cfg(all( @@ -1126,26 +1012,4 @@ 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<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } } diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf/src/bin/uart_idle.rs index 09ec624c0..6af4f7097 100644 --- a/examples/nrf/src/bin/uart_idle.rs +++ b/examples/nrf/src/bin/uart_idle.rs @@ -15,7 +15,8 @@ async fn main(_spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(UARTE0_UART0); - let mut uart = uarte::UarteWithIdle::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); info!("uarte initialized!"); @@ -23,12 +24,12 @@ async fn main(_spawner: Spawner) { let mut buf = [0; 8]; buf.copy_from_slice(b"Hello!\r\n"); - unwrap!(uart.write(&buf).await); + unwrap!(tx.write(&buf).await); info!("wrote hello in uart!"); loop { info!("reading..."); - let n = unwrap!(uart.read_until_idle(&mut buf).await); + let n = unwrap!(rx.read_until_idle(&mut buf).await); info!("got {} bytes", n); } } From 327d3cf0df7a1b116ea7ec44d36a569e6ba6ca16 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Mon, 10 Oct 2022 12:35:42 -0500 Subject: [PATCH 0241/1575] Change rak4631 feature to sx126x, removing use in board-specific processing; simplify the P2P examples; correct RSSI computation. --- embassy-lora/Cargo.toml | 4 +- embassy-lora/src/lib.rs | 2 +- .../src/sx126x/sx126x_lora/board_specific.rs | 28 ++----- .../src/sx126x/sx126x_lora/subroutine.rs | 6 +- examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/lora_p2p_report.rs | 62 +++++++------- examples/nrf/src/bin/lora_p2p_sense.rs | 84 ++++--------------- 7 files changed, 61 insertions(+), 127 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index bcb837d9d..ea2c3fe67 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -9,7 +9,7 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "rak4631", target = "thumbv7em-none-eabihf", features = ["rak4631"] }, + { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, ] @@ -17,7 +17,7 @@ flavors = [ [lib] [features] -rak4631 = [] +sx126x = [] sx127x = [] stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] time = [] diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 342f66b2a..3e4748430 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -7,7 +7,7 @@ pub(crate) mod fmt; #[cfg(feature = "stm32wl")] pub mod stm32wl; -#[cfg(feature = "rak4631")] +#[cfg(feature = "sx126x")] pub mod sx126x; #[cfg(feature = "sx127x")] pub mod sx127x; diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs index 1fb085887..a7b9e1486 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs @@ -10,8 +10,7 @@ use super::LoRa; // Defines the time required for the TCXO to wakeup [ms]. const BRD_TCXO_WAKEUP_TIME: u32 = 10; -// Provides board-specific functionality for Semtech SX126x-based boards. Use #[cfg(feature = "board_type")] to specify unique board functionality. -// The base implementation supports the RAK4631 board. +// Provides board-specific functionality for Semtech SX126x-based boards. impl LoRa where @@ -203,44 +202,33 @@ where // Get the radio type pub(super) fn brd_get_radio_type(&mut self) -> RadioType { - #[cfg(feature = "rak4631")] RadioType::SX1262 } // Quiesce the antenna(s). pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_low().map_err(|_| AntRx)?; - } + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_low().map_err(|_| AntRx)?; Ok(()) } // Prepare the antenna(s) for a receive operation pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_high().map_err(|_| AntRx)?; - } + self.antenna_tx.set_low().map_err(|_| AntTx)?; + self.antenna_rx.set_high().map_err(|_| AntRx)?; Ok(()) } // Prepare the antenna(s) for a send operation pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { - #[cfg(feature = "rak4631")] - { - self.antenna_rx.set_low().map_err(|_| AntRx)?; - self.antenna_tx.set_high().map_err(|_| AntTx)?; - } + self.antenna_rx.set_low().map_err(|_| AntRx)?; + self.antenna_tx.set_high().map_err(|_| AntTx)?; Ok(()) } // Check if the given RF frequency is supported by the hardware pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { - #[cfg(feature = "rak4631")] - Ok(true) // all frequencies currently supported for the SX1262 within a rak4631 + Ok(true) } // Get the duration required for the TCXO to wakeup [ms]. diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs index 283e60993..2e78b919b 100644 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs @@ -564,7 +564,7 @@ where pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { let mut buffer = [0x00u8]; self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; - let rssi: i8 = (-(buffer[0] as i8)) >> 1; // check this ??? + let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ??? Ok(rssi) } @@ -597,9 +597,9 @@ where self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; // check this ??? - let rssi = (-(status[0] as i8)) >> 1; + let rssi = ((-(status[0] as i32)) >> 1) as i8; let snr = ((status[1] as i8) + 2) >> 2; - let signal_rssi = (-(status[2] as i8)) >> 1; + let signal_rssi = ((-(status[2] as i32)) >> 1) as i8; let freq_error = self.frequency_error; Ok(PacketStatus { diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 91c418213..6949042e2 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm 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.0" -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["rak4631", "time", "defmt"], optional = true } +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 } diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf/src/bin/lora_p2p_report.rs index 4ba3d30ce..d512b83f6 100644 --- a/examples/nrf/src/bin/lora_p2p_report.rs +++ b/examples/nrf/src/bin/lora_p2p_report.rs @@ -1,4 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs. #![no_std] #![no_main] #![macro_use] @@ -18,7 +20,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M1; // M16 ??? + spi_config.frequency = spim::Frequency::M16; let mut radio = { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); @@ -47,38 +49,30 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_secs(5)).await; start_indicator.set_low(); - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), + loop { + let rf_config = RfConfig { + frequency: 903900000, // channel in Hz + bandwidth: Bandwidth::_250KHz, + spreading_factor: SpreadingFactor::_10, + coding_rate: CodingRate::_4_8, + }; + + let mut buffer = [00u8; 100]; + + // P2P receive + match radio.rx(rf_config, &mut buffer).await { + Ok((buffer_len, rx_quality)) => info!( + "RX received = {:?} with length = {} rssi = {} snr = {}", + &buffer[0..buffer_len], + buffer_len, + rx_quality.rssi(), + rx_quality.snr() + ), + Err(err) => info!("RX error = {}", err), + } + + debug_indicator.set_high(); + Timer::after(Duration::from_secs(2)).await; + debug_indicator.set_low(); } - - let rf_config = RfConfig { - frequency: 903900000, // channel in Hz - bandwidth: Bandwidth::_250KHz, - spreading_factor: SpreadingFactor::_10, - coding_rate: CodingRate::_4_8, - }; - - let mut buffer = [00u8; 100]; - - // P2P receive - match radio.rx(rf_config, &mut buffer).await { - Ok((buffer_len, rx_quality)) => info!( - "RX received = {:?} with length = {} rssi = {} snr = {}", - &buffer[0..buffer_len], - buffer_len, - rx_quality.rssi(), - rx_quality.snr() - ), - Err(err) => info!("RX error = {}", err), - } - - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), - } - - debug_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - debug_indicator.set_low(); } diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf/src/bin/lora_p2p_sense.rs index 405a8403f..b9768874b 100644 --- a/examples/nrf/src/bin/lora_p2p_sense.rs +++ b/examples/nrf/src/bin/lora_p2p_sense.rs @@ -1,4 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs. #![no_std] #![no_main] #![macro_use] @@ -9,8 +11,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; -use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::temp::Temp; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; use embassy_nrf::{interrupt, spim}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher}; @@ -18,10 +19,6 @@ use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; use {defmt_rtt as _, panic_probe as _, panic_probe as _}; -// Sensor packet constants -const TEMPERATURE_UID: u8 = 0x01; -const MOTION_UID: u8 = 0x02; - // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); @@ -32,53 +29,20 @@ enum Message { } #[embassy_executor::task] -async fn temperature_task( - mut temperature: Temp<'static>, - publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, -) { - Timer::after(Duration::from_secs(45)).await; // stabilize for 45 seconds - - let mut temperature_reporting_threshhold = 10; - +async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { + // Publish a fake temperature every 43 seconds, minimizing LORA traffic. loop { - let value = temperature.read().await; - let mut temperature_val = value.to_num::(); - - info!("Temperature: {}", temperature_val); - - // only report every 2 degree Celsius drops, from 9 through 5, but starting at 3 always report - - if temperature_val == 8 || temperature_val == 6 || temperature_val == 4 { - temperature_val += 1; - } - - if temperature_reporting_threshhold > temperature_val - && (temperature_val == 9 || temperature_val == 7 || temperature_val == 5) - { - temperature_reporting_threshhold = temperature_val; - publisher.publish(Message::Temperature(temperature_val)).await; - } else if temperature_val <= 3 { - publisher.publish(Message::Temperature(temperature_val)).await; - } - - Timer::after(Duration::from_secs(20 * 60)).await; + Timer::after(Duration::from_secs(43)).await; + publisher.publish(Message::Temperature(9)).await; } } #[embassy_executor::task] -async fn motion_detection_task( - mut pir_pin: Input<'static, AnyPin>, - publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>, -) { - Timer::after(Duration::from_secs(30)).await; // stabilize for 30 seconds - +async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { + // Publish a fake motion detection every 79 seconds, minimizing LORA traffic. loop { - // wait for motion detection - pir_pin.wait_for_low().await; + Timer::after(Duration::from_secs(79)).await; publisher.publish(Message::MotionDetected).await; - - // wait a minute before setting up for more motion detection - Timer::after(Duration::from_secs(60)).await; } } @@ -91,7 +55,7 @@ async fn main(spawner: Spawner) { let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M1; // M16 ??? + spi_config.frequency = spim::Frequency::M16; let mut radio = { let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); @@ -113,15 +77,7 @@ async fn main(spawner: Spawner) { } }; - // set up for the temperature task - let temperature_irq = interrupt::take!(TEMP); - let temperature = Temp::new(p.TEMP, temperature_irq); - - // set the motion detection pin - let pir_pin = Input::new(p.P0_10.degrade(), Pull::Up); - let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); - let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); start_indicator.set_high(); Timer::after(Duration::from_secs(5)).await; @@ -132,15 +88,15 @@ async fn main(spawner: Spawner) { Err(err) => info!("Sleep unsuccessful = {}", err), } - unwrap!(spawner.spawn(temperature_task(temperature, temperature_publisher))); - unwrap!(spawner.spawn(motion_detection_task(pir_pin, motion_detection_publisher))); + unwrap!(spawner.spawn(temperature_task(temperature_publisher))); + unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher))); loop { let message = lora_tx_subscriber.next_message_pure().await; let tx_config = TxConfig { // 11 byte maximum payload for Bandwidth 125 and SF 10 - pw: 20, // up to 20 // 5 ??? + pw: 10, // up to 20 rf: RfConfig { frequency: 903900000, // channel in Hz, not MHz bandwidth: Bandwidth::_250KHz, @@ -149,13 +105,13 @@ async fn main(spawner: Spawner) { }, }; - let mut buffer = [TEMPERATURE_UID, 0xffu8, MOTION_UID, 0x00u8]; + let mut buffer = [0x00u8]; match message { - Message::Temperature(temperature) => buffer[1] = temperature as u8, - Message::MotionDetected => buffer[3] = 0x01u8, + Message::Temperature(temperature) => buffer[0] = temperature as u8, + Message::MotionDetected => buffer[0] = 0x01u8, }; - // crypto for text ??? + // unencrypted match radio.tx(tx_config, &buffer).await { Ok(ret_val) => info!("TX ret_val = {}", ret_val), Err(err) => info!("TX error = {}", err), @@ -165,9 +121,5 @@ async fn main(spawner: Spawner) { Ok(()) => info!("Sleep successful"), Err(err) => info!("Sleep unsuccessful = {}", err), } - - debug_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - debug_indicator.set_low(); } } From 79cee7415110817b529a68848247a86708bcd6af Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 11 Oct 2022 09:19:47 +0200 Subject: [PATCH 0242/1575] Fix stm32wl55jc-cm4 RTC --- embassy-stm32/src/rtc/v3.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 7255e97eb..dfe27ed6e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -28,7 +28,12 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let config_rtcsel = rtc_config.clock_config as u8; #[cfg(rtc_v3)] - #[cfg(not(any(feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve")))] + #[cfg(not(any( + feature = "stm32wl54jc-cm0p", + feature = "stm32wle5ub", + feature = "stm32g0c1ve", + feature = "stm32wl55jc-cm4" + )))] let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); #[cfg(feature = "stm32g0c1ve")] let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel); From 4da6320e63ad1c93944774c15a9fcc4641fe32ef Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 11 Oct 2022 10:20:31 +0200 Subject: [PATCH 0243/1575] Add note on partition sizes to bootloader docs See #1007 --- docs/modules/ROOT/pages/bootloader.adoc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index ae92e9d5d..7dbfeb3eb 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc @@ -25,10 +25,19 @@ image::bootloader_flash.png[Bootloader flash layout] The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader instance or via linker scripts: -* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash. -* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. -* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. -* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped. +* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs. +* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application. +* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data: ++ +Partition Size~dfu~= Partition Size~active~+ Page Size~active~ ++ +All values are specified in bytes. + +* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by: ++ +Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) ++ +All values are specified in bytes. The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. From 9223b67306fcdf41b03d2a9e5d833e5b71b44954 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 11 Oct 2022 10:28:28 +0200 Subject: [PATCH 0244/1575] Fix RTC for v2l0 & v2l1 --- embassy-stm32/src/rtc/v2/v2l0.rs | 46 +++++++++++--------------------- embassy-stm32/src/rtc/v2/v2l1.rs | 44 ++++++++++-------------------- embassy-stm32/src/rtc/v3.rs | 3 ++- 3 files changed, 32 insertions(+), 61 deletions(-) diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs index 8d8005887..dbd3b0882 100644 --- a/embassy-stm32/src/rtc/v2/v2l0.rs +++ b/embassy-stm32/src/rtc/v2/v2l0.rs @@ -1,40 +1,26 @@ pub const BACKUP_REGISTER_COUNT: usize = 20; /// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { - // FIXME: - // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - // while !crate::pac::PWR.cr1().read().dbp() {} +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + // TODO: Missing from PAC? + // crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + // while !crate::pac::PWR.cr().read().dbp() {} - // let reg = crate::pac::RCC.bdcr().read(); - // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + let reg = crate::pac::RCC.csr().read(); - // if !reg.rtcen() || reg.rtcsel().0 != clock_config { - // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.csr().modify(|w| { + // Select RTC source + w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config)); + w.set_rtcen(true); - // crate::pac::RCC.bdcr().modify(|w| { - // // Reset - // w.set_bdrst(false); - - // // Select RTC source - // w.set_rtcsel(Rtcsel(clock_config)); - // w.set_rtcen(true); - - // // Restore bcdr - // w.set_lscosel(reg.lscosel()); - // w.set_lscoen(reg.lscoen()); - - // w.set_lseon(reg.lseon()); - // w.set_lsedrv(reg.lsedrv()); - // w.set_lsebyp(reg.lsebyp()); - // }); - // } + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } } pub(crate) unsafe fn enable_peripheral_clk() { - // // enable peripheral clock for communication - // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // // read to allow the pwr clock to enable - // crate::pac::PWR.cr1().read(); + // Nothing to do } diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs index 8d8005887..1ac78b31a 100644 --- a/embassy-stm32/src/rtc/v2/v2l1.rs +++ b/embassy-stm32/src/rtc/v2/v2l1.rs @@ -1,40 +1,24 @@ pub const BACKUP_REGISTER_COUNT: usize = 20; /// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { - // FIXME: - // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - // while !crate::pac::PWR.cr1().read().dbp() {} +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr().read().dbp() {} - // let reg = crate::pac::RCC.bdcr().read(); - // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + let reg = crate::pac::RCC.csr().read(); - // if !reg.rtcen() || reg.rtcsel().0 != clock_config { - // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.csr().modify(|w| { + // Select RTC source + w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config)); + w.set_rtcen(true); - // crate::pac::RCC.bdcr().modify(|w| { - // // Reset - // w.set_bdrst(false); - - // // Select RTC source - // w.set_rtcsel(Rtcsel(clock_config)); - // w.set_rtcen(true); - - // // Restore bcdr - // w.set_lscosel(reg.lscosel()); - // w.set_lscoen(reg.lscoen()); - - // w.set_lseon(reg.lseon()); - // w.set_lsedrv(reg.lsedrv()); - // w.set_lsebyp(reg.lsebyp()); - // }); - // } + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } } pub(crate) unsafe fn enable_peripheral_clk() { - // // enable peripheral clock for communication - // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // // read to allow the pwr clock to enable - // crate::pac::PWR.cr1().read(); + // Nothing to do } diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index dfe27ed6e..c9a794c3a 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -32,7 +32,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> { feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve", - feature = "stm32wl55jc-cm4" + feature = "stm32wl55jc-cm4", + feature = "stm32wl55uc-cm4" )))] let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); #[cfg(feature = "stm32g0c1ve")] From 86113e199f37fe0888979608a08bfdaf21bff19a Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 11 Oct 2022 10:35:43 +0200 Subject: [PATCH 0245/1575] Remove unused feature gate --- embassy-stm32/src/rtc/v3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index c9a794c3a..eba67c28f 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -27,7 +27,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); let config_rtcsel = rtc_config.clock_config as u8; - #[cfg(rtc_v3)] #[cfg(not(any( feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", From 5846b4ff7d6a3ab177abe1f1070b0b21d5a87f1d Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 12 Oct 2022 10:54:47 +0200 Subject: [PATCH 0246/1575] Correctly enable RTC_IRQ when scheduling an RTC alarm --- embassy-rp/src/rtc/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 7f3bbbe73..e4b6f0b1d 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -145,6 +145,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { filter.write_setup_1(w); }); + self.inner.regs().inte().modify(|w| w.set_rtc(true)); + // Set the enable bit and check if it is set self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); while !self.inner.regs().irq_setup_0().read().match_active() { From a4afab46403aacd7bb555cadaefad7f216fb9931 Mon Sep 17 00:00:00 2001 From: pbert Date: Sun, 10 Jul 2022 20:12:25 +0200 Subject: [PATCH 0247/1575] add support for pdm microphones in nrf driver --- embassy-nrf/src/chips/nrf52810.rs | 3 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/chips/nrf9160.rs | 3 + embassy-nrf/src/lib.rs | 8 + embassy-nrf/src/pdm.rs | 242 ++++++++++++++++++++++++++++++ examples/nrf/src/bin/pdm.rs | 33 ++++ 8 files changed, 298 insertions(+) create mode 100644 embassy-nrf/src/pdm.rs create mode 100644 examples/nrf/src/bin/pdm.rs diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index faa52d8fb..3e500098c 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index bbdf1cbe5..25c7c0d91 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 39a0f93f9..3b33907d2 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -158,6 +158,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index e3d8f34a1..ae59f8b25 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -161,6 +161,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index a4be8564e..f8ed11e03 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -260,6 +260,9 @@ embassy_hal_common::peripherals! { P0_29, P0_30, P0_31, + + // PDM + PDM, } impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d7bd21702..bc70fc2f6 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -76,6 +76,14 @@ pub mod gpio; pub mod gpiote; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; +#[cfg(any( + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf9160" +))] +pub mod pdm; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod pwm; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs new file mode 100644 index 000000000..b7c7022cf --- /dev/null +++ b/embassy-nrf/src/pdm.rs @@ -0,0 +1,242 @@ +//! PDM mirophone interface + +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use futures::future::poll_fn; + +use crate::chip::EASY_DMA_SIZE; +use crate::gpio::sealed::Pin; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::{self, InterruptExt}; +use crate::peripherals::PDM; +use crate::{pac, Peripheral}; + +/// PDM microphone interface +pub struct Pdm<'d> { + irq: PeripheralRef<'d, interrupt::PDM>, + phantom: PhantomData<&'d PDM>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + BufferTooLong, + BufferZeroLength, + NotRunning, +} + +static WAKER: AtomicWaker = AtomicWaker::new(); +static DUMMY_BUFFER: [i16; 1] = [0; 1]; + +impl<'d> Pdm<'d> { + /// Create PDM driver + pub fn new( + pdm: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + clk: impl Peripheral

+ 'd, + din: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(clk, din); + Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config) + } + + fn new_inner( + _pdm: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + clk: PeripheralRef<'d, AnyPin>, + din: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!(irq); + + let r = Self::regs(); + + // setup gpio pins + din.conf().write(|w| w.input().set_bit()); + r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); + clk.set_low(); + clk.conf().write(|w| w.dir().output()); + r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); + + // configure + // use default for + // - gain right + // - gain left + // - clk + // - ratio + r.mode.write(|w| { + w.edge().bit(config.edge == Edge::LeftRising); + w.operation().bit(config.operation_mode == OperationMode::Mono); + w + }); + r.gainl.write(|w| w.gainl().default_gain()); + r.gainr.write(|w| w.gainr().default_gain()); + + // IRQ + irq.disable(); + irq.set_handler(|_| { + let r = Self::regs(); + r.intenclr.write(|w| w.end().clear()); + WAKER.wake(); + }); + irq.enable(); + + r.enable.write(|w| w.enable().set_bit()); + + Self { + phantom: PhantomData, + irq, + } + } + + /// Start sampling microphon data into a dummy buffer + /// Usefull to start the microphon and keep it active between recording samples + pub async fn start(&mut self) { + let r = Self::regs(); + + // start dummy sampling because microphon needs some setup time + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + r.tasks_start.write(|w| w.tasks_start().set_bit()); + } + + /// Stop sampling microphon data inta a dummy buffer + pub async fn stop(&mut self) { + let r = Self::regs(); + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.events_started.reset(); + } + + pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let r = Self::regs(); + + if r.events_started.read().events_started().bit_is_clear() { + return Err(Error::NotRunning); + } + + let drop = OnDrop::new(move || { + r.intenclr.write(|w| w.end().clear()); + r.events_stopped.reset(); + + // reset to dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + while r.events_stopped.read().bits() == 0 {} + }); + + // setup user buffer + let ptr = buffer.as_ptr(); + let len = buffer.len(); + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); + + // wait till the current sample is finished and the user buffer sample is started + Self::wait_for_sample().await; + + // reset the buffer back to the dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + // wait till the user buffer is sampled + Self::wait_for_sample().await; + + drop.defuse(); + + Ok(()) + } + + async fn wait_for_sample() { + let r = Self::regs(); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + WAKER.register(cx.waker()); + if r.events_end.read().events_end().bit_is_set() { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + } + + fn regs() -> &'static pac::pdm::RegisterBlock { + unsafe { &*pac::PDM::ptr() } + } +} + +/// PDM microphone driver Config +pub struct Config { + /// Use stero or mono operation + pub operation_mode: OperationMode, + /// On which edge the left channel should be samples + pub edge: Edge, +} + +impl Default for Config { + fn default() -> Self { + Self { + operation_mode: OperationMode::Mono, + edge: Edge::LeftFalling, + } + } +} + +#[derive(PartialEq)] +pub enum OperationMode { + Mono, + Stereo, +} +#[derive(PartialEq)] +pub enum Edge { + LeftRising, + LeftFalling, +} + +impl<'d> Drop for Pdm<'d> { + fn drop(&mut self) { + let r = Self::regs(); + + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + + self.irq.disable(); + + r.enable.write(|w| w.enable().disabled()); + + r.psel.din.reset(); + r.psel.clk.reset(); + } +} diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs new file mode 100644 index 000000000..7388580fb --- /dev/null +++ b/examples/nrf/src/bin/pdm.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::pdm::{Config, Pdm}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let p = embassy_nrf::init(Default::default()); + let config = Config::default(); + let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); + + loop { + pdm.start().await; + + // wait some time till the microphon settled + Timer::after(Duration::from_millis(1000)).await; + + const SAMPLES: usize = 2048; + let mut buf = [0i16; SAMPLES]; + pdm.sample(&mut buf).await.unwrap(); + + info!("samples: {:?}", &buf); + + pdm.stop().await; + Timer::after(Duration::from_millis(100)).await; + } +} From e4c2b2aa9a61e5135d2e129705c9bdc3b4cb73ae Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Sun, 16 Oct 2022 18:00:23 -0700 Subject: [PATCH 0248/1575] rp i2c: have separate wakers for each i2c unit If they both share one waker, there's the possibility that some wakeups could get lost. --- embassy-rp/src/i2c.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 68cfc653e..b9e4382c7 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -70,7 +70,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } } -static I2C_WAKER: AtomicWaker = AtomicWaker::new(); +static I2C_WAKER: [AtomicWaker; 2] = [AtomicWaker::new(), AtomicWaker::new()]; impl<'d, T: Instance> I2c<'d, T, Async> { pub fn new_async( @@ -109,7 +109,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let r = f(self); if r.is_pending() { - I2C_WAKER.register(cx.waker()); + I2C_WAKER[T::IDX].register(cx.waker()); g(self); } r @@ -122,7 +122,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let i2c = T::regs(); i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - I2C_WAKER.wake(); + I2C_WAKER[T::IDX].wake(); } async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { @@ -813,6 +813,7 @@ mod sealed { pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; + const IDX: usize; type Interrupt: Interrupt; @@ -844,10 +845,11 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $idx:expr, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + const IDX: usize = $idx; type Interrupt = crate::interrupt::$irq; @@ -867,8 +869,8 @@ macro_rules! impl_instance { }; } -impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); -impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); +impl_instance!(I2C0, 0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, 1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From 02a3cdb507d535908bc0668a31526c05b5d01005 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Mon, 17 Oct 2022 21:50:40 -0700 Subject: [PATCH 0249/1575] Associate state with the instance rather than having a separate array --- embassy-rp/src/i2c.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index b9e4382c7..d6742f6a6 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -70,8 +70,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { } } -static I2C_WAKER: [AtomicWaker; 2] = [AtomicWaker::new(), AtomicWaker::new()]; - impl<'d, T: Instance> I2c<'d, T, Async> { pub fn new_async( peri: impl Peripheral

+ 'd, @@ -109,7 +107,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let r = f(self); if r.is_pending() { - I2C_WAKER[T::IDX].register(cx.waker()); + T::waker().register(cx.waker()); g(self); } r @@ -122,7 +120,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let i2c = T::regs(); i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - I2C_WAKER[T::IDX].wake(); + T::waker().wake(); } async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { @@ -809,16 +807,17 @@ fn i2c_reserved_addr(addr: u16) -> bool { mod sealed { use embassy_cortex_m::interrupt::Interrupt; + use embassy_sync::waitqueue::AtomicWaker; pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; - const IDX: usize; type Interrupt: Interrupt; fn regs() -> crate::pac::i2c::I2c; fn reset() -> crate::pac::resets::regs::Peripherals; + fn waker() -> &'static AtomicWaker; } pub trait Mode {} @@ -845,11 +844,10 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($type:ident, $idx:expr, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - const IDX: usize = $idx; type Interrupt = crate::interrupt::$irq; @@ -864,13 +862,20 @@ macro_rules! impl_instance { ret.$reset(true); ret } + + #[inline] + fn waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + + &WAKER + } } impl Instance for peripherals::$type {} }; } -impl_instance!(I2C0, 0, I2C0_IRQ, set_i2c0, 32, 33); -impl_instance!(I2C1, 1, I2C1_IRQ, set_i2c1, 34, 35); +impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); +impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} From 6c5d81ada52bfac1592c7be025038a3eea3acc42 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Tue, 18 Oct 2022 22:42:02 +0200 Subject: [PATCH 0250/1575] Add memory barriers to H7 flash driver to mitigate PGSERR errors The stm32h7xx-hal uses only the ordering barrier, while the CubeMX uses the DSB and ISB instructions, to be on the safe side, both are used here. --- embassy-stm32/src/flash/h7.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 7ce0ac776..3f2129de8 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -39,6 +39,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error w.set_psize(2); // 32 bits at once }); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + let ret = { let mut ret: Result<(), Error> = Ok(()); let mut offset = offset; @@ -64,6 +68,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error bank.cr().write(|w| w.set_pg(false)); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + ret } From 9d2641f2f5f96e85f3c900a69388071f1adddfdc Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Mon, 17 Oct 2022 15:50:46 +0200 Subject: [PATCH 0251/1575] Enable defmt in embassy-hal-common --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 2610e5687..9194ae788 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -75,7 +75,7 @@ quote = "1.0.15" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} [features] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] sdmmc-rs = ["embedded-sdmmc"] net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] From fa495b8e889b6fec2b44f5c59c69ea4fe798f4ec Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 19 Oct 2022 03:36:18 -0500 Subject: [PATCH 0252/1575] Add missing examples to rust-analyzer linked projects --- .vscode/settings.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5e9e51799..62e5a362f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,10 +20,13 @@ //"embassy-executor/Cargo.toml", //"embassy-sync/Cargo.toml", "examples/nrf/Cargo.toml", + // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", // "examples/stm32f1/Cargo.toml", + // "examples/stm32f2/Cargo.toml", + // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", @@ -32,8 +35,11 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", + // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", + // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", + // "examples/stm32wl/Cargo.toml", // "examples/stm32wl55/Cargo.toml", // "examples/wasm/Cargo.toml", ], From de103a5f4fba5e913988928e22301b4ee05e8515 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 20 Oct 2022 16:12:59 +0200 Subject: [PATCH 0253/1575] Add missing files and features for basic example --- docs/modules/ROOT/examples/basic/Cargo.toml | 5 ++- docs/modules/ROOT/examples/basic/build.rs | 35 +++++++++++++++++++++ docs/modules/ROOT/examples/basic/memory.x | 7 +++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 docs/modules/ROOT/examples/basic/build.rs create mode 100644 docs/modules/ROOT/examples/basic/memory.x diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index c13f546e2..d9f8a285a 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,14 +6,13 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] } +embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } defmt = "0.3" defmt-rtt = "0.3" -cortex-m = "0.7.3" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/docs/modules/ROOT/examples/basic/build.rs b/docs/modules/ROOT/examples/basic/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/docs/modules/ROOT/examples/basic/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/docs/modules/ROOT/examples/basic/memory.x b/docs/modules/ROOT/examples/basic/memory.x new file mode 100644 index 000000000..9b04edec0 --- /dev/null +++ b/docs/modules/ROOT/examples/basic/memory.x @@ -0,0 +1,7 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} From f45d34ce7c5663da9e3c96ab698e8a84dbafb4ac Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 20 Oct 2022 23:46:02 +0800 Subject: [PATCH 0254/1575] Fix mistaken EP_IN_WAKERS --- embassy-rp/src/usb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 0a904aab3..8d6aa33b0 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -522,7 +522,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { trace!("wait_enabled IN WAITING"); let index = self.info.addr.index(); poll_fn(|cx| { - EP_OUT_WAKERS[index].register(cx.waker()); + 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(()) From 866a42f3aeb196e2b0f854a52683ee07cffc4ccd Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 21 Oct 2022 22:02:13 +0800 Subject: [PATCH 0255/1575] rp usb: wait for accept() completion This ensures that the current response has finished being sent before the subsequent set_address() happens. Otherwise connecting a device is intermittent, can fail depending on timing. --- embassy-rp/src/usb.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 0a904aab3..7a83dcb4a 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -811,8 +811,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { async move { trace!("control: accept"); + let bufcontrol = T::dpram().ep_in_buffer_control(0); unsafe { - let bufcontrol = T::dpram().ep_in_buffer_control(0); bufcontrol.write(|w| { w.set_length(0, 0); w.set_pid(0, true); @@ -826,6 +826,18 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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; } } From c2404ee8ca6200d9037f096eee3f0eab98711778 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Tue, 6 Sep 2022 21:39:23 +0300 Subject: [PATCH 0256/1575] Initial generic timer queue impl --- embassy-time/Cargo.toml | 5 + embassy-time/src/lib.rs | 1 + embassy-time/src/queue.rs | 197 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 embassy-time/src/queue.rs diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c51a71d01..0e3391d1f 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,6 +26,9 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] +# TODO: Doc +generic-queue = [] + # Set the `embassy_time` tick rate. # # At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. @@ -111,9 +114,11 @@ embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-macros = { version = "0.1.0", path = "../embassy-macros"} +embassy-sync = { version = "0.1", path = "../embassy-sync" } atomic-polyfill = "1.0.1" critical-section = "1.1" cfg-if = "1.0.0" +heapless = "0.7" # WASM dependencies wasm-bindgen = { version = "0.2.81", optional = true } diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 4edc883fe..0457a6571 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -11,6 +11,7 @@ mod delay; pub mod driver; mod duration; mod instant; +pub mod queue; mod tick; mod timer; diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs new file mode 100644 index 000000000..7e84090b1 --- /dev/null +++ b/embassy-time/src/queue.rs @@ -0,0 +1,197 @@ +//! Generic timer queue implementation +use core::cell::RefCell; +use core::cmp::Ordering; +use core::task::Waker; + +use atomic_polyfill::{AtomicBool, Ordering as AtomicOrdering}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use embassy_sync::blocking_mutex::Mutex; +use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; + +use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; +use crate::Instant; + +#[derive(Debug)] +struct Timer { + at: Instant, + waker: Waker, +} + +impl PartialEq for Timer { + fn eq(&self, other: &Self) -> bool { + self.at == other.at + } +} + +impl Eq for Timer {} + +impl PartialOrd for Timer { + fn partial_cmp(&self, other: &Self) -> Option { + self.at.partial_cmp(&other.at) + } +} + +impl Ord for Timer { + fn cmp(&self, other: &Self) -> Ordering { + self.at.cmp(&other.at) + } +} + +struct InnerQueue { + queue: SortedLinkedList, + alarm_at: Instant, + alarm: Option, +} + +impl InnerQueue { + const fn new() -> Self { + Self { + queue: SortedLinkedList::new_u8(), + alarm_at: Instant::MAX, + alarm: None, + } + } + + fn schedule(&mut self, at: Instant, waker: &Waker) { + self.queue + .find_mut(|timer| timer.waker.will_wake(waker)) + .map(|mut timer| { + timer.waker = waker.clone(); + timer.at = at; + + timer.finish(); + }) + .unwrap_or_else(|| { + let mut timer = Timer { + waker: waker.clone(), + at, + }; + + loop { + match self.queue.push(timer) { + Ok(()) => break, + Err(e) => timer = e, + } + + self.queue.pop().unwrap().waker.wake(); + } + }); + + // Don't wait for the alarm callback to trigger and directly + // dispatch all timers that are already due + // + // Then update the alarm if necessary + self.dispatch(); + } + + fn dispatch(&mut self) { + let now = Instant::now(); + + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + self.update_alarm(); + } + + fn update_alarm(&mut self) { + if let Some(timer) = self.queue.peek() { + let new_at = timer.at; + + if self.alarm_at != new_at { + self.alarm_at = new_at; + set_alarm(self.alarm.unwrap(), new_at.as_ticks()); + } + } else { + self.alarm_at = Instant::MAX; + } + } + + fn handle_alarm(&mut self) { + self.alarm_at = Instant::MAX; + + self.dispatch(); + } +} + +/// TODO: Doc +pub struct Queue { + initialized: AtomicBool, + inner: Mutex>>, +} + +impl Queue { + /// TODO: Doc + pub const fn new() -> Self { + Self { + initialized: AtomicBool::new(false), + inner: Mutex::new(RefCell::new(InnerQueue::::new())), + } + } + + /// TODO: Doc + pub unsafe fn initialize(&'static self) { + if self.initialized.load(AtomicOrdering::SeqCst) { + panic!("Queue already initialized"); + } + + let handle = allocate_alarm().unwrap(); + self.inner.lock(|inner| inner.borrow_mut().alarm = Some(handle)); + + set_alarm_callback(handle, Self::handle_alarm, self as *const _ as _); + + self.initialized.store(true, AtomicOrdering::SeqCst); + } + + /// TODO: Doc + pub fn schedule(&'static self, at: Instant, waker: &Waker) { + self.check_initialized(); + + self.inner.lock(|inner| inner.borrow_mut().schedule(at, waker)); + } + + fn check_initialized(&self) { + if !self.initialized.load(AtomicOrdering::SeqCst) { + panic!("Queue is not initialized"); + } + } + + fn handle_alarm(ctx: *mut ()) { + let this = unsafe { (ctx as *const Self).as_ref().unwrap() }; + + this.check_initialized(); + this.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + } +} + +/// TODO: Doc +pub unsafe fn initialize() { + extern "Rust" { + fn _embassy_time_generic_queue_initialize(); + } + + _embassy_time_generic_queue_initialize(); +} + +/// TODO: Doc +#[macro_export] +macro_rules! generic_queue { + (static $name:ident: $t: ty = $val:expr) => { + static $name: $t = $val; + + #[no_mangle] + fn _embassy_time_generic_queue_initialize() { + unsafe { + $crate::queue::Queue::initialize(&$name); + } + } + + #[no_mangle] + fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { + $crate::queue::Queue::schedule(&$name, at, waker); + } + }; +} + +#[cfg(feature = "generic-queue")] +generic_queue!(static QUEUE: Queue = Queue::new()); From ba6e452cc5d6c33029f34d7cfb5cd5ea846979bd Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Tue, 20 Sep 2022 20:23:56 +0300 Subject: [PATCH 0257/1575] Documentation and initial testing framework Add mock waker First simple test Tests & documentation --- embassy-time/Cargo.toml | 6 +- embassy-time/src/lib.rs | 2 +- embassy-time/src/queue.rs | 486 +++++++++++++++++++++++++++++++++++--- 3 files changed, 459 insertions(+), 35 deletions(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0e3391d1f..4fbf97f0d 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,7 +26,8 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] -# TODO: Doc +# Create a global queue that can be used with any executor +# To use this you must have a time driver provided. generic-queue = [] # Set the `embassy_time` tick rate. @@ -124,3 +125,6 @@ heapless = "0.7" wasm-bindgen = { version = "0.2.81", optional = true } js-sys = { version = "0.3", optional = true } wasm-timer = { version = "0.2.5", optional = true } + +[dev-dependencies] +serial_test = "0.9" diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 0457a6571..50f437baf 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] +#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] #![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs index 7e84090b1..56ad5af8d 100644 --- a/embassy-time/src/queue.rs +++ b/embassy-time/src/queue.rs @@ -1,9 +1,53 @@ //! Generic timer queue implementation -use core::cell::RefCell; +//! +//! This module provides a timer queue that works with any executor. +//! +//! In terms of performance, this queue will likely be less efficient in comparison to executor-native queues, +//! like the one provided with e.g. the `embassy-executor` crate. +//! +//! # Enabling the queue +//! - Enable the Cargo feature `generic-queue`. This will automatically instantiate the queue. +//! +//! # Initializing the queue +//! - Call ```unsafe { embassy_time::queue::initialize(); }``` early on in your program, before any of your futures that utilize `embassy-time` are polled. +//! +//! # Customizing the queue +//! - It is possible to customize two aspects of the queue: +//! - Queue size: +//! By default, the queue can hold up to 128 timer schedules and their corresponding wakers. While it will not crash if more timer schedules are added, +//! the performance will degrade, as one of the already added wakers will be awoken, thus making room for the new timer schedule and its waker. +//! - The mutex (i.e. the [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation) utilized by the queue: +//! By default, the utilized [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation is [`CriticalSectionRawMutex`](embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex) +//! which is provided by the `critical-section` crate. This should work just fine, except in a few niche cases like running on +//! top of an RTOS which provides a [`Driver`](crate::driver::Driver) implementation that will call-back directly from an ISR. As the +//! `critical-section` implementation for RTOS-es will likely provide an RTOS mutex which cannot be locked from an ISR, user needs to instead +//! configure the queue with a "disable-all-interrupts" style of mutex. +//! - To customize any of these queue aspects, don't enable the `generic-queue` Cargo feature and instead instantiate the queue with the [`generic_queue`](crate::generic_queue) +//! macro, as per the example below. +//! +//! +//! # Example +//! +//! ```ignore +//! use embassy_time::queue::Queue; +//! +//! // You only need to invoke this macro in case you need to customize the queue. +//! // +//! // Otherwise, just depend on the `embassy-time` crate with feature `generic-queue` enabled, +//! // and the queue instantiation will be done for you behind the scenes. +//! embassy_time::generic_queue!(static QUEUE: Queue<200, MyCustomRawMutex> = Queue::new()); +//! +//! fn main() { +//! unsafe { +//! embassy_time::queue::initialize(); +//! } +//! } +//! ``` +use core::cell::{Cell, RefCell}; use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicBool, Ordering as AtomicOrdering}; +use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; use embassy_sync::blocking_mutex::Mutex; use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; @@ -40,7 +84,6 @@ impl Ord for Timer { struct InnerQueue { queue: SortedLinkedList, alarm_at: Instant, - alarm: Option, } impl InnerQueue { @@ -48,11 +91,10 @@ impl InnerQueue { Self { queue: SortedLinkedList::new_u8(), alarm_at: Instant::MAX, - alarm: None, } } - fn schedule(&mut self, at: Instant, waker: &Waker) { + fn schedule(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { @@ -81,90 +123,128 @@ impl InnerQueue { // dispatch all timers that are already due // // Then update the alarm if necessary - self.dispatch(); + self.dispatch(alarm_schedule); } - fn dispatch(&mut self) { + fn dispatch(&mut self, alarm_schedule: &AtomicU64) { let now = Instant::now(); while self.queue.peek().filter(|timer| timer.at <= now).is_some() { self.queue.pop().unwrap().waker.wake(); } - self.update_alarm(); + self.update_alarm(alarm_schedule); } - fn update_alarm(&mut self) { + fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { if let Some(timer) = self.queue.peek() { let new_at = timer.at; if self.alarm_at != new_at { self.alarm_at = new_at; - set_alarm(self.alarm.unwrap(), new_at.as_ticks()); + alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); } } else { self.alarm_at = Instant::MAX; + alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); } } - fn handle_alarm(&mut self) { + fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { self.alarm_at = Instant::MAX; - self.dispatch(); + self.dispatch(alarm_schedule); } } -/// TODO: Doc +/// The generic queue implementation pub struct Queue { - initialized: AtomicBool, inner: Mutex>>, + alarm: Cell>, + alarm_schedule: AtomicU64, } impl Queue { - /// TODO: Doc + /// Create a Queue pub const fn new() -> Self { Self { - initialized: AtomicBool::new(false), inner: Mutex::new(RefCell::new(InnerQueue::::new())), + alarm: Cell::new(None), + alarm_schedule: AtomicU64::new(u64::MAX), } } - /// TODO: Doc + /// Initialize the queue + /// + /// This method is called from [`initialize`](crate::queue::initialize), so you are not expected to call it directly. + /// Call [`initialize`](crate::queue::initialize) instead. + /// + /// # Safety + /// It is UB call this function more than once, or to call it after any of your + /// futures that use `embassy-time` are polled already. pub unsafe fn initialize(&'static self) { - if self.initialized.load(AtomicOrdering::SeqCst) { - panic!("Queue already initialized"); + if self.alarm.get().is_some() { + panic!("Queue is already initialized"); } let handle = allocate_alarm().unwrap(); - self.inner.lock(|inner| inner.borrow_mut().alarm = Some(handle)); + self.alarm.set(Some(handle)); - set_alarm_callback(handle, Self::handle_alarm, self as *const _ as _); - - self.initialized.store(true, AtomicOrdering::SeqCst); + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); } - /// TODO: Doc + /// Schedule a new waker to be awoken at moment `at` + /// + /// This method is called internally by [`embassy-time`](crate), so you are not expected to call it directly. pub fn schedule(&'static self, at: Instant, waker: &Waker) { self.check_initialized(); - self.inner.lock(|inner| inner.borrow_mut().schedule(at, waker)); + self.inner + .lock(|inner| inner.borrow_mut().schedule(at, waker, &self.alarm_schedule)); + + self.update_alarm(); } fn check_initialized(&self) { - if !self.initialized.load(AtomicOrdering::SeqCst) { - panic!("Queue is not initialized"); + if self.alarm.get().is_none() { + panic!("Queue is not initialized yet"); } } - fn handle_alarm(ctx: *mut ()) { - let this = unsafe { (ctx as *const Self).as_ref().unwrap() }; + fn update_alarm(&self) { + // Need to set the alarm when we are *not* holding the mutex on the inner queue + // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately + // call us back if the timestamp is in the past. + let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - this.check_initialized(); - this.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + if alarm_at < u64::MAX { + set_alarm(self.alarm.get().unwrap(), alarm_at); + } + } + + fn handle_alarm(&self) { + self.check_initialized(); + self.inner + .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); + + self.update_alarm(); + } + + fn handle_alarm_callback(ctx: *mut ()) { + unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); } } -/// TODO: Doc +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} + +/// Initialize the queue +/// +/// Call this function early on in your program, before any of your futures that utilize `embassy-time` are polled. +/// +/// # Safety +/// It is UB call this function more than once, or to call it after any of your +/// futures that use `embassy-time` are polled already. pub unsafe fn initialize() { extern "Rust" { fn _embassy_time_generic_queue_initialize(); @@ -173,7 +253,12 @@ pub unsafe fn initialize() { _embassy_time_generic_queue_initialize(); } -/// TODO: Doc +/// Instantiates a global, generic (as in executor-agnostic) timer queue. +/// +/// Unless you plan to customize the queue (size or mutex), prefer +/// instantiating the queue via the `generic-queue` feature. +/// +/// See the module documentation for an example. #[macro_export] macro_rules! generic_queue { (static $name:ident: $t: ty = $val:expr) => { @@ -195,3 +280,338 @@ macro_rules! generic_queue { #[cfg(feature = "generic-queue")] generic_queue!(static QUEUE: Queue = Queue::new()); + +#[cfg(test)] +mod tests { + use core::cell::Cell; + use core::sync::atomic::Ordering; + use core::task::{RawWaker, RawWakerVTable, Waker}; + use std::rc::Rc; + use std::sync::Mutex; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use serial_test::serial; + + use super::InnerQueue; + use crate::driver::{AlarmHandle, Driver}; + use crate::Instant; + + struct InnerTestDriver { + now: u64, + alarm: u64, + callback: fn(*mut ()), + ctx: *mut (), + } + + impl InnerTestDriver { + const fn new() -> Self { + Self { + now: 0, + alarm: u64::MAX, + callback: Self::noop, + ctx: core::ptr::null_mut(), + } + } + + fn noop(_ctx: *mut ()) {} + } + + unsafe impl Send for InnerTestDriver {} + + struct TestDriver(Mutex); + + impl TestDriver { + const fn new() -> Self { + Self(Mutex::new(InnerTestDriver::new())) + } + + fn reset(&self) { + *self.0.lock().unwrap() = InnerTestDriver::new(); + } + + fn set_now(&self, now: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if inner.now < now { + inner.now = now; + + if inner.alarm <= now { + inner.alarm = u64::MAX; + + Some((inner.callback, inner.ctx)) + } else { + None + } + } else { + panic!("Going back in time?"); + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + impl Driver for TestDriver { + fn now(&self) -> u64 { + self.0.lock().unwrap().now + } + + unsafe fn allocate_alarm(&self) -> Option { + Some(AlarmHandle::new(0)) + } + + fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + let mut inner = self.0.lock().unwrap(); + + inner.callback = callback; + inner.ctx = ctx; + } + + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if timestamp <= inner.now { + Some((inner.callback, inner.ctx)) + } else { + inner.alarm = timestamp; + None + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + struct TestWaker { + pub awoken: Rc>, + pub waker: Waker, + } + + impl TestWaker { + fn new() -> Self { + let flag = Rc::new(Cell::new(false)); + + const VTABLE: RawWakerVTable = RawWakerVTable::new( + |data: *const ()| { + unsafe { + Rc::increment_strong_count(data as *const Cell); + } + + RawWaker::new(data as _, &VTABLE) + }, + |data: *const ()| unsafe { + let data = data as *const Cell; + data.as_ref().unwrap().set(true); + Rc::decrement_strong_count(data); + }, + |data: *const ()| unsafe { + (data as *const Cell).as_ref().unwrap().set(true); + }, + |data: *const ()| unsafe { + Rc::decrement_strong_count(data); + }, + ); + + let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); + + Self { + awoken: flag.clone(), + waker: unsafe { Waker::from_raw(raw) }, + } + } + } + + // TODO: This impl should be part of `embassy-sync`, hidden behind the "std" feature gate + pub struct StdRawMutex(std::sync::Mutex<()>); + + unsafe impl RawMutex for StdRawMutex { + const INIT: Self = StdRawMutex(std::sync::Mutex::new(())); + + fn lock(&self, f: impl FnOnce() -> R) -> R { + let _guard = self.0.lock().unwrap(); + + f() + } + } + + const QUEUE_MAX_LEN: usize = 8; + + crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); + crate::generic_queue!(static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new()); + + fn setup() { + DRIVER.reset(); + + QUEUE.alarm.set(None); + QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); + QUEUE.inner.lock(|inner| { + *inner.borrow_mut() = InnerQueue::new(); + }); + + unsafe { super::initialize() }; + } + + fn queue_len() -> usize { + QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) + } + + #[test] + #[serial] + #[should_panic(expected = "Queue is not initialized yet")] + fn test_not_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + } + + #[test] + #[serial] + fn test_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + assert!(QUEUE.alarm.get().is_none()); + + unsafe { QUEUE.initialize() }; + + assert!(QUEUE.alarm.get().is_some()); + } + + #[test] + #[serial] + #[should_panic(expected = "Queue is already initialized")] + fn test_already_initialized() { + static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); + + unsafe { QUEUE.initialize() }; + + assert!(QUEUE.alarm.get().is_some()); + + unsafe { QUEUE.initialize() }; + } + + #[test] + #[serial] + fn test_schedule() { + setup(); + + assert_eq!(queue_len(), 0); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + + assert!(!waker.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_schedule_same() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule(Instant::from_secs(100), &waker.waker); + + assert_eq!(queue_len(), 1); + + let waker2 = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(100), &waker2.waker); + + assert_eq!(queue_len(), 2); + } + + #[test] + #[serial] + fn test_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(100), &waker.waker); + + assert!(!waker.awoken.get()); + + DRIVER.set_now(Instant::from_secs(99).as_ticks()); + + assert!(!waker.awoken.get()); + + assert_eq!(queue_len(), 1); + + DRIVER.set_now(Instant::from_secs(100).as_ticks()); + + assert!(waker.awoken.get()); + + assert_eq!(queue_len(), 0); + } + + #[test] + #[serial] + fn test_immediate_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(100), &waker.waker); + + DRIVER.set_now(Instant::from_secs(50).as_ticks()); + + let waker2 = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(40), &waker2.waker); + + assert!(!waker.awoken.get()); + assert!(waker2.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_queue_overflow() { + setup(); + + for i in 1..QUEUE_MAX_LEN { + let waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(310), &waker.waker); + + assert_eq!(queue_len(), i); + assert!(!waker.awoken.get()); + } + + let first_waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(300), &first_waker.waker); + + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(!first_waker.awoken.get()); + + let second_waker = TestWaker::new(); + + QUEUE.schedule(Instant::from_secs(305), &second_waker.waker); + + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(first_waker.awoken.get()); + + QUEUE.schedule(Instant::from_secs(320), &TestWaker::new().waker); + assert_eq!(queue_len(), QUEUE_MAX_LEN); + assert!(second_waker.awoken.get()); + } +} From 53608a87ac4b6c8c60b5508551d12f5ba76ca2f6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 26 Sep 2022 13:46:15 +0300 Subject: [PATCH 0258/1575] Address feedback after code review --- embassy-time/Cargo.toml | 16 +- embassy-time/src/lib.rs | 2 + embassy-time/src/queue.rs | 613 ++---------------------------- embassy-time/src/queue_generic.rs | 474 +++++++++++++++++++++++ 4 files changed, 518 insertions(+), 587 deletions(-) create mode 100644 embassy-time/src/queue_generic.rs diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 4fbf97f0d..e1ad4b9dd 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -26,10 +26,22 @@ unstable-traits = ["embedded-hal-1"] # To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] -# Create a global queue that can be used with any executor +# Create a global, generic queue that can be used with any executor # To use this you must have a time driver provided. generic-queue = [] +# Set the number of timers for the generic queue. +# +# At most 1 `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. +# +# When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the +# end user to pick. +generic-queue-8 = ["generic-queue"] +generic-queue-16 = ["generic-queue"] +generic-queue-32 = ["generic-queue"] +generic-queue-64 = ["generic-queue"] +generic-queue-128 = ["generic-queue"] + # Set the `embassy_time` tick rate. # # At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. @@ -128,3 +140,5 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" +critical-section = { version = "1.1", features = ["std"] } + diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 50f437baf..586aa28de 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -19,6 +19,8 @@ mod timer; mod driver_std; #[cfg(feature = "wasm")] mod driver_wasm; +#[cfg(feature = "generic-queue")] +mod queue_generic; pub use delay::{block_for, Delay}; pub use duration::Duration; diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs index 56ad5af8d..c6f8b440a 100644 --- a/embassy-time/src/queue.rs +++ b/embassy-time/src/queue.rs @@ -1,617 +1,58 @@ -//! Generic timer queue implementation +//! Timer queue implementation //! -//! This module provides a timer queue that works with any executor. +//! This module defines the interface a timer queue needs to implement to power the `embassy_time` module. //! -//! In terms of performance, this queue will likely be less efficient in comparison to executor-native queues, -//! like the one provided with e.g. the `embassy-executor` crate. +//! # Implementing a timer queue //! -//! # Enabling the queue -//! - Enable the Cargo feature `generic-queue`. This will automatically instantiate the queue. +//! - Define a struct `MyTimerQueue` +//! - Implement [`TimerQueue`] for it +//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). //! -//! # Initializing the queue -//! - Call ```unsafe { embassy_time::queue::initialize(); }``` early on in your program, before any of your futures that utilize `embassy-time` are polled. +//! # Linkage details //! -//! # Customizing the queue -//! - It is possible to customize two aspects of the queue: -//! - Queue size: -//! By default, the queue can hold up to 128 timer schedules and their corresponding wakers. While it will not crash if more timer schedules are added, -//! the performance will degrade, as one of the already added wakers will be awoken, thus making room for the new timer schedule and its waker. -//! - The mutex (i.e. the [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation) utilized by the queue: -//! By default, the utilized [`RawMutex`](embassy_sync::blocking_mutex::raw::RawMutex) implementation is [`CriticalSectionRawMutex`](embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex) -//! which is provided by the `critical-section` crate. This should work just fine, except in a few niche cases like running on -//! top of an RTOS which provides a [`Driver`](crate::driver::Driver) implementation that will call-back directly from an ISR. As the -//! `critical-section` implementation for RTOS-es will likely provide an RTOS mutex which cannot be locked from an ISR, user needs to instead -//! configure the queue with a "disable-all-interrupts" style of mutex. -//! - To customize any of these queue aspects, don't enable the `generic-queue` Cargo feature and instead instantiate the queue with the [`generic_queue`](crate::generic_queue) -//! macro, as per the example below. +//! Check the documentation of the [`driver`](crate::driver) module for more information. //! +//! Similarly to driver, if there is none or multiple timer queues in the crate tree, linking will fail. //! //! # Example //! -//! ```ignore -//! use embassy_time::queue::Queue; +//! ``` +//! use core::task::Waker; //! -//! // You only need to invoke this macro in case you need to customize the queue. -//! // -//! // Otherwise, just depend on the `embassy-time` crate with feature `generic-queue` enabled, -//! // and the queue instantiation will be done for you behind the scenes. -//! embassy_time::generic_queue!(static QUEUE: Queue<200, MyCustomRawMutex> = Queue::new()); +//! use embassy_time::Instant; +//! use embassy_time::queue::{TimerQueue}; //! -//! fn main() { -//! unsafe { -//! embassy_time::queue::initialize(); +//! struct MyTimerQueue{}; // not public! +//! embassy_time::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); +//! +//! impl TimerQueue for MyTimerQueue { +//! fn schedule_wake(&'static self, at: Instant, waker: &Waker) { +//! todo!() //! } //! } //! ``` -use core::cell::{Cell, RefCell}; -use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; -use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; -use embassy_sync::blocking_mutex::Mutex; -use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; - -use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; use crate::Instant; -#[derive(Debug)] -struct Timer { - at: Instant, - waker: Waker, +/// Timer queue +pub trait TimerQueue { + /// Schedules a waker in the queue to be awoken at moment `at`. + /// If this moment is in the past, the waker might be awoken immediately. + fn schedule_wake(&'static self, at: Instant, waker: &Waker); } -impl PartialEq for Timer { - fn eq(&self, other: &Self) -> bool { - self.at == other.at - } -} - -impl Eq for Timer {} - -impl PartialOrd for Timer { - fn partial_cmp(&self, other: &Self) -> Option { - self.at.partial_cmp(&other.at) - } -} - -impl Ord for Timer { - fn cmp(&self, other: &Self) -> Ordering { - self.at.cmp(&other.at) - } -} - -struct InnerQueue { - queue: SortedLinkedList, - alarm_at: Instant, -} - -impl InnerQueue { - const fn new() -> Self { - Self { - queue: SortedLinkedList::new_u8(), - alarm_at: Instant::MAX, - } - } - - fn schedule(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { - self.queue - .find_mut(|timer| timer.waker.will_wake(waker)) - .map(|mut timer| { - timer.waker = waker.clone(); - timer.at = at; - - timer.finish(); - }) - .unwrap_or_else(|| { - let mut timer = Timer { - waker: waker.clone(), - at, - }; - - loop { - match self.queue.push(timer) { - Ok(()) => break, - Err(e) => timer = e, - } - - self.queue.pop().unwrap().waker.wake(); - } - }); - - // Don't wait for the alarm callback to trigger and directly - // dispatch all timers that are already due - // - // Then update the alarm if necessary - self.dispatch(alarm_schedule); - } - - fn dispatch(&mut self, alarm_schedule: &AtomicU64) { - let now = Instant::now(); - - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); - } - - self.update_alarm(alarm_schedule); - } - - fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { - if let Some(timer) = self.queue.peek() { - let new_at = timer.at; - - if self.alarm_at != new_at { - self.alarm_at = new_at; - alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); - } - } else { - self.alarm_at = Instant::MAX; - alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); - } - } - - fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { - self.alarm_at = Instant::MAX; - - self.dispatch(alarm_schedule); - } -} - -/// The generic queue implementation -pub struct Queue { - inner: Mutex>>, - alarm: Cell>, - alarm_schedule: AtomicU64, -} - -impl Queue { - /// Create a Queue - pub const fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(InnerQueue::::new())), - alarm: Cell::new(None), - alarm_schedule: AtomicU64::new(u64::MAX), - } - } - - /// Initialize the queue - /// - /// This method is called from [`initialize`](crate::queue::initialize), so you are not expected to call it directly. - /// Call [`initialize`](crate::queue::initialize) instead. - /// - /// # Safety - /// It is UB call this function more than once, or to call it after any of your - /// futures that use `embassy-time` are polled already. - pub unsafe fn initialize(&'static self) { - if self.alarm.get().is_some() { - panic!("Queue is already initialized"); - } - - let handle = allocate_alarm().unwrap(); - self.alarm.set(Some(handle)); - - set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); - } - - /// Schedule a new waker to be awoken at moment `at` - /// - /// This method is called internally by [`embassy-time`](crate), so you are not expected to call it directly. - pub fn schedule(&'static self, at: Instant, waker: &Waker) { - self.check_initialized(); - - self.inner - .lock(|inner| inner.borrow_mut().schedule(at, waker, &self.alarm_schedule)); - - self.update_alarm(); - } - - fn check_initialized(&self) { - if self.alarm.get().is_none() { - panic!("Queue is not initialized yet"); - } - } - - fn update_alarm(&self) { - // Need to set the alarm when we are *not* holding the mutex on the inner queue - // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately - // call us back if the timestamp is in the past. - let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - - if alarm_at < u64::MAX { - set_alarm(self.alarm.get().unwrap(), alarm_at); - } - } - - fn handle_alarm(&self) { - self.check_initialized(); - self.inner - .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); - - self.update_alarm(); - } - - fn handle_alarm_callback(ctx: *mut ()) { - unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); - } -} - -unsafe impl Send for Queue {} -unsafe impl Sync for Queue {} - -/// Initialize the queue -/// -/// Call this function early on in your program, before any of your futures that utilize `embassy-time` are polled. -/// -/// # Safety -/// It is UB call this function more than once, or to call it after any of your -/// futures that use `embassy-time` are polled already. -pub unsafe fn initialize() { - extern "Rust" { - fn _embassy_time_generic_queue_initialize(); - } - - _embassy_time_generic_queue_initialize(); -} - -/// Instantiates a global, generic (as in executor-agnostic) timer queue. -/// -/// Unless you plan to customize the queue (size or mutex), prefer -/// instantiating the queue via the `generic-queue` feature. +/// Set the TimerQueue implementation. /// /// See the module documentation for an example. #[macro_export] -macro_rules! generic_queue { +macro_rules! timer_queue_impl { (static $name:ident: $t: ty = $val:expr) => { static $name: $t = $val; - #[no_mangle] - fn _embassy_time_generic_queue_initialize() { - unsafe { - $crate::queue::Queue::initialize(&$name); - } - } - #[no_mangle] fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) { - $crate::queue::Queue::schedule(&$name, at, waker); + <$t as $crate::queue::TimerQueue>::schedule_wake(&$name, at, waker); } }; } - -#[cfg(feature = "generic-queue")] -generic_queue!(static QUEUE: Queue = Queue::new()); - -#[cfg(test)] -mod tests { - use core::cell::Cell; - use core::sync::atomic::Ordering; - use core::task::{RawWaker, RawWakerVTable, Waker}; - use std::rc::Rc; - use std::sync::Mutex; - - use embassy_sync::blocking_mutex::raw::RawMutex; - use serial_test::serial; - - use super::InnerQueue; - use crate::driver::{AlarmHandle, Driver}; - use crate::Instant; - - struct InnerTestDriver { - now: u64, - alarm: u64, - callback: fn(*mut ()), - ctx: *mut (), - } - - impl InnerTestDriver { - const fn new() -> Self { - Self { - now: 0, - alarm: u64::MAX, - callback: Self::noop, - ctx: core::ptr::null_mut(), - } - } - - fn noop(_ctx: *mut ()) {} - } - - unsafe impl Send for InnerTestDriver {} - - struct TestDriver(Mutex); - - impl TestDriver { - const fn new() -> Self { - Self(Mutex::new(InnerTestDriver::new())) - } - - fn reset(&self) { - *self.0.lock().unwrap() = InnerTestDriver::new(); - } - - fn set_now(&self, now: u64) { - let notify = { - let mut inner = self.0.lock().unwrap(); - - if inner.now < now { - inner.now = now; - - if inner.alarm <= now { - inner.alarm = u64::MAX; - - Some((inner.callback, inner.ctx)) - } else { - None - } - } else { - panic!("Going back in time?"); - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); - } - } - } - - impl Driver for TestDriver { - fn now(&self) -> u64 { - self.0.lock().unwrap().now - } - - unsafe fn allocate_alarm(&self) -> Option { - Some(AlarmHandle::new(0)) - } - - fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - let mut inner = self.0.lock().unwrap(); - - inner.callback = callback; - inner.ctx = ctx; - } - - fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { - let notify = { - let mut inner = self.0.lock().unwrap(); - - if timestamp <= inner.now { - Some((inner.callback, inner.ctx)) - } else { - inner.alarm = timestamp; - None - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); - } - } - } - - struct TestWaker { - pub awoken: Rc>, - pub waker: Waker, - } - - impl TestWaker { - fn new() -> Self { - let flag = Rc::new(Cell::new(false)); - - const VTABLE: RawWakerVTable = RawWakerVTable::new( - |data: *const ()| { - unsafe { - Rc::increment_strong_count(data as *const Cell); - } - - RawWaker::new(data as _, &VTABLE) - }, - |data: *const ()| unsafe { - let data = data as *const Cell; - data.as_ref().unwrap().set(true); - Rc::decrement_strong_count(data); - }, - |data: *const ()| unsafe { - (data as *const Cell).as_ref().unwrap().set(true); - }, - |data: *const ()| unsafe { - Rc::decrement_strong_count(data); - }, - ); - - let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); - - Self { - awoken: flag.clone(), - waker: unsafe { Waker::from_raw(raw) }, - } - } - } - - // TODO: This impl should be part of `embassy-sync`, hidden behind the "std" feature gate - pub struct StdRawMutex(std::sync::Mutex<()>); - - unsafe impl RawMutex for StdRawMutex { - const INIT: Self = StdRawMutex(std::sync::Mutex::new(())); - - fn lock(&self, f: impl FnOnce() -> R) -> R { - let _guard = self.0.lock().unwrap(); - - f() - } - } - - const QUEUE_MAX_LEN: usize = 8; - - crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); - crate::generic_queue!(static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new()); - - fn setup() { - DRIVER.reset(); - - QUEUE.alarm.set(None); - QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); - QUEUE.inner.lock(|inner| { - *inner.borrow_mut() = InnerQueue::new(); - }); - - unsafe { super::initialize() }; - } - - fn queue_len() -> usize { - QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) - } - - #[test] - #[serial] - #[should_panic(expected = "Queue is not initialized yet")] - fn test_not_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - } - - #[test] - #[serial] - fn test_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - assert!(QUEUE.alarm.get().is_none()); - - unsafe { QUEUE.initialize() }; - - assert!(QUEUE.alarm.get().is_some()); - } - - #[test] - #[serial] - #[should_panic(expected = "Queue is already initialized")] - fn test_already_initialized() { - static QUEUE: super::Queue<{ QUEUE_MAX_LEN }, StdRawMutex> = super::Queue::new(); - - unsafe { QUEUE.initialize() }; - - assert!(QUEUE.alarm.get().is_some()); - - unsafe { QUEUE.initialize() }; - } - - #[test] - #[serial] - fn test_schedule() { - setup(); - - assert_eq!(queue_len(), 0); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - - assert!(!waker.awoken.get()); - assert_eq!(queue_len(), 1); - } - - #[test] - #[serial] - fn test_schedule_same() { - setup(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule(Instant::from_secs(1), &waker.waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule(Instant::from_secs(100), &waker.waker); - - assert_eq!(queue_len(), 1); - - let waker2 = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(100), &waker2.waker); - - assert_eq!(queue_len(), 2); - } - - #[test] - #[serial] - fn test_trigger() { - setup(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(100), &waker.waker); - - assert!(!waker.awoken.get()); - - DRIVER.set_now(Instant::from_secs(99).as_ticks()); - - assert!(!waker.awoken.get()); - - assert_eq!(queue_len(), 1); - - DRIVER.set_now(Instant::from_secs(100).as_ticks()); - - assert!(waker.awoken.get()); - - assert_eq!(queue_len(), 0); - } - - #[test] - #[serial] - fn test_immediate_trigger() { - setup(); - - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(100), &waker.waker); - - DRIVER.set_now(Instant::from_secs(50).as_ticks()); - - let waker2 = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(40), &waker2.waker); - - assert!(!waker.awoken.get()); - assert!(waker2.awoken.get()); - assert_eq!(queue_len(), 1); - } - - #[test] - #[serial] - fn test_queue_overflow() { - setup(); - - for i in 1..QUEUE_MAX_LEN { - let waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(310), &waker.waker); - - assert_eq!(queue_len(), i); - assert!(!waker.awoken.get()); - } - - let first_waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(300), &first_waker.waker); - - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(!first_waker.awoken.get()); - - let second_waker = TestWaker::new(); - - QUEUE.schedule(Instant::from_secs(305), &second_waker.waker); - - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(first_waker.awoken.get()); - - QUEUE.schedule(Instant::from_secs(320), &TestWaker::new().waker); - assert_eq!(queue_len(), QUEUE_MAX_LEN); - assert!(second_waker.awoken.get()); - } -} diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs new file mode 100644 index 000000000..1c4e5398b --- /dev/null +++ b/embassy-time/src/queue_generic.rs @@ -0,0 +1,474 @@ +use core::cell::RefCell; +use core::cmp::Ordering; +use core::task::Waker; + +use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; + +use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; +use crate::queue::TimerQueue; +use crate::Instant; + +#[cfg(feature = "generic-queue-8")] +const QUEUE_SIZE: usize = 8; +#[cfg(feature = "generic-queue-16")] +const QUEUE_SIZE: usize = 16; +#[cfg(feature = "generic-queue-32")] +const QUEUE_SIZE: usize = 32; +#[cfg(feature = "generic-queue-64")] +const QUEUE_SIZE: usize = 32; +#[cfg(feature = "generic-queue-128")] +const QUEUE_SIZE: usize = 128; +#[cfg(not(any( + feature = "generic-queue-8", + feature = "generic-queue-16", + feature = "generic-queue-32", + feature = "generic-queue-64", + feature = "generic-queue-128" +)))] +const QUEUE_SIZE: usize = 64; + +#[derive(Debug)] +struct Timer { + at: Instant, + waker: Waker, +} + +impl PartialEq for Timer { + fn eq(&self, other: &Self) -> bool { + self.at == other.at + } +} + +impl Eq for Timer {} + +impl PartialOrd for Timer { + fn partial_cmp(&self, other: &Self) -> Option { + self.at.partial_cmp(&other.at) + } +} + +impl Ord for Timer { + fn cmp(&self, other: &Self) -> Ordering { + self.at.cmp(&other.at) + } +} + +struct InnerQueue { + queue: SortedLinkedList, + alarm: Option, + alarm_at: Instant, +} + +impl InnerQueue { + const fn new() -> Self { + Self { + queue: SortedLinkedList::new_u8(), + alarm: None, + alarm_at: Instant::MAX, + } + } + + fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { + self.queue + .find_mut(|timer| timer.waker.will_wake(waker)) + .map(|mut timer| { + timer.at = at; + timer.finish(); + }) + .unwrap_or_else(|| { + let mut timer = Timer { + waker: waker.clone(), + at, + }; + + loop { + match self.queue.push(timer) { + Ok(()) => break, + Err(e) => timer = e, + } + + self.queue.pop().unwrap().waker.wake(); + } + }); + + // Don't wait for the alarm callback to trigger and directly + // dispatch all timers that are already due + // + // Then update the alarm if necessary + self.dispatch(alarm_schedule); + } + + fn dispatch(&mut self, alarm_schedule: &AtomicU64) { + let now = Instant::now(); + + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + self.update_alarm(alarm_schedule); + } + + fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { + if let Some(timer) = self.queue.peek() { + let new_at = timer.at; + + if self.alarm_at != new_at { + self.alarm_at = new_at; + alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); + } + } else { + self.alarm_at = Instant::MAX; + alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); + } + } + + fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { + self.alarm_at = Instant::MAX; + + self.dispatch(alarm_schedule); + } +} + +struct Queue { + inner: Mutex>, + alarm_schedule: AtomicU64, +} + +impl Queue { + const fn new() -> Self { + Self { + inner: Mutex::new(RefCell::new(InnerQueue::new())), + alarm_schedule: AtomicU64::new(u64::MAX), + } + } + + fn schedule_wake(&'static self, at: Instant, waker: &Waker) { + self.inner.lock(|inner| { + let mut inner = inner.borrow_mut(); + + if inner.alarm.is_none() { + let handle = unsafe { allocate_alarm() }.unwrap(); + inner.alarm = Some(handle); + + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); + } + + inner.schedule_wake(at, waker, &self.alarm_schedule) + }); + + self.update_alarm(); + } + + fn update_alarm(&self) { + // Need to set the alarm when we are *not* holding the mutex on the inner queue + // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately + // call us back if the timestamp is in the past. + let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); + + if alarm_at < u64::MAX { + set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at); + } + } + + fn handle_alarm(&self) { + self.inner + .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); + + self.update_alarm(); + } + + fn handle_alarm_callback(ctx: *mut ()) { + unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); + } +} + +impl TimerQueue for Queue { + fn schedule_wake(&'static self, at: Instant, waker: &Waker) { + Queue::schedule_wake(self, at, waker); + } +} + +crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); + +#[cfg(test)] +mod tests { + use core::cell::Cell; + use core::sync::atomic::Ordering; + use core::task::{RawWaker, RawWakerVTable, Waker}; + use std::rc::Rc; + use std::sync::Mutex; + + use serial_test::serial; + + use super::InnerQueue; + use crate::driver::{AlarmHandle, Driver}; + use crate::queue_generic::QUEUE; + use crate::Instant; + + struct InnerTestDriver { + now: u64, + alarm: u64, + callback: fn(*mut ()), + ctx: *mut (), + } + + impl InnerTestDriver { + const fn new() -> Self { + Self { + now: 0, + alarm: u64::MAX, + callback: Self::noop, + ctx: core::ptr::null_mut(), + } + } + + fn noop(_ctx: *mut ()) {} + } + + unsafe impl Send for InnerTestDriver {} + + struct TestDriver(Mutex); + + impl TestDriver { + const fn new() -> Self { + Self(Mutex::new(InnerTestDriver::new())) + } + + fn reset(&self) { + *self.0.lock().unwrap() = InnerTestDriver::new(); + } + + fn set_now(&self, now: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if inner.now < now { + inner.now = now; + + if inner.alarm <= now { + inner.alarm = u64::MAX; + + Some((inner.callback, inner.ctx)) + } else { + None + } + } else { + panic!("Going back in time?"); + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + impl Driver for TestDriver { + fn now(&self) -> u64 { + self.0.lock().unwrap().now + } + + unsafe fn allocate_alarm(&self) -> Option { + Some(AlarmHandle::new(0)) + } + + fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + let mut inner = self.0.lock().unwrap(); + + inner.callback = callback; + inner.ctx = ctx; + } + + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { + let notify = { + let mut inner = self.0.lock().unwrap(); + + if timestamp <= inner.now { + Some((inner.callback, inner.ctx)) + } else { + inner.alarm = timestamp; + None + } + }; + + if let Some((callback, ctx)) = notify { + (callback)(ctx); + } + } + } + + struct TestWaker { + pub awoken: Rc>, + pub waker: Waker, + } + + impl TestWaker { + fn new() -> Self { + let flag = Rc::new(Cell::new(false)); + + const VTABLE: RawWakerVTable = RawWakerVTable::new( + |data: *const ()| { + unsafe { + Rc::increment_strong_count(data as *const Cell); + } + + RawWaker::new(data as _, &VTABLE) + }, + |data: *const ()| unsafe { + let data = data as *const Cell; + data.as_ref().unwrap().set(true); + Rc::decrement_strong_count(data); + }, + |data: *const ()| unsafe { + (data as *const Cell).as_ref().unwrap().set(true); + }, + |data: *const ()| unsafe { + Rc::decrement_strong_count(data); + }, + ); + + let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE); + + Self { + awoken: flag.clone(), + waker: unsafe { Waker::from_raw(raw) }, + } + } + } + + crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new()); + + fn setup() { + DRIVER.reset(); + + QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); + QUEUE.inner.lock(|inner| { + *inner.borrow_mut() = InnerQueue::new(); + }); + } + + fn queue_len() -> usize { + QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) + } + + #[test] + #[serial] + fn test_schedule() { + setup(); + + assert_eq!(queue_len(), 0); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert!(!waker.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_schedule_same() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker); + + assert_eq!(queue_len(), 1); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + assert_eq!(queue_len(), 1); + + let waker2 = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker2.waker); + + assert_eq!(queue_len(), 2); + } + + #[test] + #[serial] + fn test_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + assert!(!waker.awoken.get()); + + DRIVER.set_now(Instant::from_secs(99).as_ticks()); + + assert!(!waker.awoken.get()); + + assert_eq!(queue_len(), 1); + + DRIVER.set_now(Instant::from_secs(100).as_ticks()); + + assert!(waker.awoken.get()); + + assert_eq!(queue_len(), 0); + } + + #[test] + #[serial] + fn test_immediate_trigger() { + setup(); + + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); + + DRIVER.set_now(Instant::from_secs(50).as_ticks()); + + let waker2 = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(40), &waker2.waker); + + assert!(!waker.awoken.get()); + assert!(waker2.awoken.get()); + assert_eq!(queue_len(), 1); + } + + #[test] + #[serial] + fn test_queue_overflow() { + setup(); + + for i in 1..super::QUEUE_SIZE { + let waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(310), &waker.waker); + + assert_eq!(queue_len(), i); + assert!(!waker.awoken.get()); + } + + let first_waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(300), &first_waker.waker); + + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(!first_waker.awoken.get()); + + let second_waker = TestWaker::new(); + + QUEUE.schedule_wake(Instant::from_secs(305), &second_waker.waker); + + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(first_waker.awoken.get()); + + QUEUE.schedule_wake(Instant::from_secs(320), &TestWaker::new().waker); + assert_eq!(queue_len(), super::QUEUE_SIZE); + assert!(second_waker.awoken.get()); + } +} From 4d5550070fe5e80ff2296a71239c568c774b9ceb Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 09:17:43 +0300 Subject: [PATCH 0259/1575] Change time Driver contract to never fire the alarm synchronously --- embassy-executor/src/raw/mod.rs | 78 +++++++++++++++++-------------- embassy-nrf/src/time_driver.rs | 19 ++++---- embassy-rp/src/timer.rs | 17 +++---- embassy-stm32/src/time_driver.rs | 18 +++---- embassy-time/src/driver.rs | 13 +++--- embassy-time/src/driver_std.rs | 4 +- embassy-time/src/driver_wasm.rs | 4 +- embassy-time/src/queue_generic.rs | 77 +++++++++++------------------- 8 files changed, 113 insertions(+), 117 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e1258ebb5..5bcb1e6e7 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -354,46 +354,54 @@ impl Executor { /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { - #[cfg(feature = "integrated-timers")] - self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); + loop { + #[cfg(feature = "integrated-timers")] + self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); - self.run_queue.dequeue_all(|p| { - let task = p.as_ref(); + self.run_queue.dequeue_all(|p| { + let task = p.as_ref(); + + #[cfg(feature = "integrated-timers")] + task.expires_at.set(Instant::MAX); + + let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); + if state & STATE_SPAWNED == 0 { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + return; + } + + #[cfg(feature = "rtos-trace")] + trace::task_exec_begin(p.as_ptr() as u32); + + // Run the task + task.poll_fn.read()(p as _); + + #[cfg(feature = "rtos-trace")] + trace::task_exec_end(); + + // Enqueue or update into timer_queue + #[cfg(feature = "integrated-timers")] + self.timer_queue.update(p); + }); #[cfg(feature = "integrated-timers")] - task.expires_at.set(Instant::MAX); - - let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); - if state & STATE_SPAWNED == 0 { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - return; + { + // If this is already in the past, set_alarm might return false + // In that case do another poll loop iteration. + let next_expiration = self.timer_queue.next_expiration(); + if driver::set_alarm(self.alarm, next_expiration.as_ticks()) { + break; + } } - #[cfg(feature = "rtos-trace")] - trace::task_exec_begin(p.as_ptr() as u32); - - // Run the task - task.poll_fn.read()(p as _); - - #[cfg(feature = "rtos-trace")] - trace::task_exec_end(); - - // Enqueue or update into timer_queue - #[cfg(feature = "integrated-timers")] - self.timer_queue.update(p); - }); - - #[cfg(feature = "integrated-timers")] - { - // If this is already in the past, set_alarm will immediately trigger the alarm. - // This will cause `signal_fn` to be called, which will cause `poll()` to be called again, - // so we immediately do another poll loop iteration. - let next_expiration = self.timer_queue.next_expiration(); - driver::set_alarm(self.alarm, next_expiration.as_ticks()); + #[cfg(not(feature = "integrated-timers"))] + { + break; + } } #[cfg(feature = "rtos-trace")] diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index c32a44637..0d03ad529 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -243,20 +243,19 @@ impl Driver for RtcDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { + let t = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= t { + return false; + } + let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); - let t = self.now(); - - // If alarm timestamp has passed, trigger it instantly. - if timestamp <= t { - self.trigger_alarm(n, cs); - return; - } - let r = rtc(); // If it hasn't triggered yet, setup it in the compare channel. @@ -287,6 +286,8 @@ impl Driver for RtcDriver { // It will be setup later by `next_period`. r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); } + + true }) } } diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 5215c0c0f..8f280f550 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -68,9 +68,16 @@ impl Driver for TimerDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { let n = alarm.id() as usize; critical_section::with(|cs| { + let now = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= now { + return false; + } + let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(timestamp); @@ -80,13 +87,7 @@ impl Driver for TimerDriver { // it is checked if the alarm time has passed. unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; - let now = self.now(); - - // If alarm timestamp has passed, trigger it instantly. - // This disarms it. - if timestamp <= now { - self.trigger_alarm(n, cs); - } + true }) } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ed3225c51..e4c266e7f 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -292,21 +292,21 @@ impl Driver for RtcDriver { }) } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { + let t = self.now(); + + // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. + if timestamp <= t { + return false; + } + let r = T::regs_gp16(); let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); - let t = self.now(); - if timestamp <= t { - unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) }; - self.trigger_alarm(n, cs); - return; - } - let safe_timestamp = timestamp.max(t + 3); // Write the CCR value regardless of whether we're going to enable it now or not. @@ -317,6 +317,8 @@ impl Driver for RtcDriver { let diff = timestamp - t; // NOTE(unsafe) We're in a critical section unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) }; + + true }) } } diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs index 79ae14b91..5c2ad3b23 100644 --- a/embassy-time/src/driver.rs +++ b/embassy-time/src/driver.rs @@ -105,20 +105,21 @@ pub trait Driver: Send + Sync + 'static { /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm /// timestamp, the provided callback function will be called. /// - /// If `timestamp` is already in the past, the alarm callback must be immediately fired. - /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`. + /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`. + /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set, + /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously. /// /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. /// /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; } extern "Rust" { fn _embassy_time_now() -> u64; fn _embassy_time_allocate_alarm() -> Option; fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); + fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool; } /// See [`Driver::now`] @@ -139,7 +140,7 @@ pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ( } /// See [`Driver::set_alarm`] -pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) { +pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool { unsafe { _embassy_time_set_alarm(alarm, timestamp) } } @@ -167,7 +168,7 @@ macro_rules! time_driver_impl { } #[no_mangle] - fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) { + fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) -> bool { <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp) } }; diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 2ddb2e604..fc7fd1979 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -127,12 +127,14 @@ impl Driver for TimeDriver { alarm.ctx = ctx; } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.timestamp = timestamp; unsafe { self.signaler.as_ref() }.signal(); + + true } } diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index e4497e6a2..d7a6b0d8d 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -81,13 +81,15 @@ impl Driver for TimeDriver { } } - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.closure.replace(Closure::new(move || { callback(ctx); })); + + true } fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 1c4e5398b..83f734848 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -2,7 +2,6 @@ use core::cell::RefCell; use core::cmp::Ordering; use core::task::Waker; -use atomic_polyfill::{AtomicU64, Ordering as AtomicOrdering}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; @@ -71,7 +70,7 @@ impl InnerQueue { } } - fn schedule_wake(&mut self, at: Instant, waker: &Waker, alarm_schedule: &AtomicU64) { + fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { @@ -98,50 +97,54 @@ impl InnerQueue { // dispatch all timers that are already due // // Then update the alarm if necessary - self.dispatch(alarm_schedule); + self.dispatch(); } - fn dispatch(&mut self, alarm_schedule: &AtomicU64) { - let now = Instant::now(); + fn dispatch(&mut self) { + loop { + let now = Instant::now(); - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); + while self.queue.peek().filter(|timer| timer.at <= now).is_some() { + self.queue.pop().unwrap().waker.wake(); + } + + if self.update_alarm() { + break; + } } - - self.update_alarm(alarm_schedule); } - fn update_alarm(&mut self, alarm_schedule: &AtomicU64) { + fn update_alarm(&mut self) -> bool { if let Some(timer) = self.queue.peek() { let new_at = timer.at; if self.alarm_at != new_at { self.alarm_at = new_at; - alarm_schedule.store(new_at.as_ticks(), AtomicOrdering::SeqCst); + + return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks()); } } else { self.alarm_at = Instant::MAX; - alarm_schedule.store(Instant::MAX.as_ticks(), AtomicOrdering::SeqCst); } + + true } - fn handle_alarm(&mut self, alarm_schedule: &AtomicU64) { + fn handle_alarm(&mut self) { self.alarm_at = Instant::MAX; - self.dispatch(alarm_schedule); + self.dispatch(); } } struct Queue { inner: Mutex>, - alarm_schedule: AtomicU64, } impl Queue { const fn new() -> Self { Self { inner: Mutex::new(RefCell::new(InnerQueue::new())), - alarm_schedule: AtomicU64::new(u64::MAX), } } @@ -156,28 +159,12 @@ impl Queue { set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); } - inner.schedule_wake(at, waker, &self.alarm_schedule) + inner.schedule_wake(at, waker) }); - - self.update_alarm(); - } - - fn update_alarm(&self) { - // Need to set the alarm when we are *not* holding the mutex on the inner queue - // because mutexes are not re-entrant, which is a problem because `set_alarm` might immediately - // call us back if the timestamp is in the past. - let alarm_at = self.alarm_schedule.swap(u64::MAX, AtomicOrdering::SeqCst); - - if alarm_at < u64::MAX { - set_alarm(self.inner.lock(|inner| inner.borrow().alarm.unwrap()), alarm_at); - } } fn handle_alarm(&self) { - self.inner - .lock(|inner| inner.borrow_mut().handle_alarm(&self.alarm_schedule)); - - self.update_alarm(); + self.inner.lock(|inner| inner.borrow_mut().handle_alarm()); } fn handle_alarm_callback(ctx: *mut ()) { @@ -196,7 +183,6 @@ crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); #[cfg(test)] mod tests { use core::cell::Cell; - use core::sync::atomic::Ordering; use core::task::{RawWaker, RawWakerVTable, Waker}; use std::rc::Rc; use std::sync::Mutex; @@ -282,20 +268,14 @@ mod tests { inner.ctx = ctx; } - fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) { - let notify = { - let mut inner = self.0.lock().unwrap(); + fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool { + let mut inner = self.0.lock().unwrap(); - if timestamp <= inner.now { - Some((inner.callback, inner.ctx)) - } else { - inner.alarm = timestamp; - None - } - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); + if timestamp <= inner.now { + false + } else { + inner.alarm = timestamp; + true } } } @@ -344,7 +324,6 @@ mod tests { fn setup() { DRIVER.reset(); - QUEUE.alarm_schedule.store(u64::MAX, Ordering::SeqCst); QUEUE.inner.lock(|inner| { *inner.borrow_mut() = InnerQueue::new(); }); From f78c706b89feed71a7e0a3eaf332f55813698c7f Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 11:10:59 +0300 Subject: [PATCH 0260/1575] Address review feedback --- embassy-nrf/src/time_driver.rs | 18 +++++++++++------- embassy-rp/src/timer.rs | 20 ++++++++++++-------- embassy-stm32/src/time_driver.rs | 18 +++++++++++------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 0d03ad529..bc2c8a3c1 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -245,19 +245,23 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let t = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= t { - return false; - } - let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); let r = rtc(); + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + + alarm.timestamp.set(u64::MAX); + + return false; + } + // If it hasn't triggered yet, setup it in the compare channel. // Write the CC value regardless of whether we're going to enable it now or not. diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 8f280f550..80efd779f 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -71,13 +71,6 @@ impl Driver for TimerDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { let n = alarm.id() as usize; critical_section::with(|cs| { - let now = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= now { - return false; - } - let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(timestamp); @@ -87,7 +80,18 @@ impl Driver for TimerDriver { // it is checked if the alarm time has passed. unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; - true + let now = self.now(); + if timestamp <= now { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } + + alarm.timestamp.set(u64::MAX); + + false + } else { + true + } }) } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index e4c266e7f..ab55cc081 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -294,19 +294,23 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let t = self.now(); - - // If alarm timestamp has passed don't set the alarm and return `false` to indicate that. - if timestamp <= t { - return false; - } - let r = T::regs_gp16(); let n = alarm.id() as _; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) }; + + alarm.timestamp.set(u64::MAX); + + return false; + } + let safe_timestamp = timestamp.max(t + 3); // Write the CCR value regardless of whether we're going to enable it now or not. From 4ce4131f8bad84ab38f595ac02dc5292611aaac0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 11:30:04 +0300 Subject: [PATCH 0261/1575] Implement i2cv1 timeout --- embassy-stm32/src/i2c/mod.rs | 3 + embassy-stm32/src/i2c/timeout.rs | 132 +++++++++++++++++++++++++++++++ embassy-stm32/src/i2c/v1.rs | 101 +++++++++++++++++------ examples/stm32f4/src/bin/i2c.rs | 30 +++++++ 4 files changed, 243 insertions(+), 23 deletions(-) create mode 100644 embassy-stm32/src/i2c/timeout.rs create mode 100644 examples/stm32f4/src/bin/i2c.rs diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 9d314f411..f4f64992c 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,6 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; +mod timeout; +pub use timeout::*; + use crate::peripherals; #[derive(Debug)] diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs new file mode 100644 index 000000000..12319af8a --- /dev/null +++ b/embassy-stm32/src/i2c/timeout.rs @@ -0,0 +1,132 @@ +use embassy_time::{Duration, Instant}; + +use super::{Error, I2c, Instance}; + +pub struct TimeoutI2c<'d, T: Instance> { + i2c: &'d mut I2c<'d, T>, + timeout: Duration, +} + +fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { + let deadline = Instant::now() + timeout; + move || { + if Instant::now() > deadline { + Err(Error::Timeout) + } else { + Ok(()) + } + } +} + +impl<'d, T: Instance> TimeoutI2c<'d, T> { + pub fn new(i2c: &'d mut I2c<'d, T>, timeout: Duration) -> Self { + Self { i2c, timeout } + } + + pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) + } + + pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, buffer, self.timeout) + } + + pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) + } + + pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, bytes, self.timeout) + } + + pub fn blocking_write_read_timeout( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + timeout: Duration, + ) -> Result<(), Error> { + self.i2c + .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) + } + + pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> { + type Error = Error; + + fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, buffer) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T> { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, bytes) + } +} + +impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T> { + type Error = Error; + + fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(addr, bytes, buffer) + } +} + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T> { + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } + + fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, buffer) + } + + fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator, + { + todo!(); + } + + fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> + where + B: IntoIterator, + { + todo!(); + } + + fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, wr_buffer, rd_buffer) + } + + fn transaction<'a>( + &mut self, + _address: u8, + _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + ) -> Result<(), Self::Error> { + todo!(); + } + + fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>, + { + todo!(); + } + } +} diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index f39a37df6..92a898824 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -141,7 +141,12 @@ impl<'d, T: Instance> I2c<'d, T> { Ok(sr1) } - unsafe fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + unsafe fn write_bytes( + &mut self, + addr: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { // Send a START condition T::regs().cr1().modify(|reg| { @@ -149,7 +154,9 @@ impl<'d, T: Instance> I2c<'d, T> { }); // Wait until START condition was generated - while !self.check_and_clear_error_flags()?.start() {} + while !self.check_and_clear_error_flags()?.start() { + check_timeout()?; + } // Also wait until signalled we're master and everything is waiting for us while { @@ -157,7 +164,9 @@ impl<'d, T: Instance> I2c<'d, T> { let sr2 = T::regs().sr2().read(); !sr2.msl() && !sr2.busy() - } {} + } { + check_timeout()?; + } // Set up current address, we're trying to talk to T::regs().dr().write(|reg| reg.set_dr(addr << 1)); @@ -165,26 +174,30 @@ impl<'d, T: Instance> I2c<'d, T> { // Wait until address was sent // Wait for the address to be acknowledged // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. - while !self.check_and_clear_error_flags()?.addr() {} + while !self.check_and_clear_error_flags()?.addr() { + check_timeout()?; + } // Clear condition by reading SR2 let _ = T::regs().sr2().read(); // Send bytes for c in bytes { - self.send_byte(*c)?; + self.send_byte(*c, &check_timeout)?; } // Fallthrough is success Ok(()) } - unsafe fn send_byte(&self, byte: u8) -> Result<(), Error> { + unsafe fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { // Wait until we're ready for sending while { // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. !self.check_and_clear_error_flags()?.txe() - } {} + } { + check_timeout()?; + } // Push out a byte of data T::regs().dr().write(|reg| reg.set_dr(byte)); @@ -193,24 +206,33 @@ impl<'d, T: Instance> I2c<'d, T> { while { // Check for any potential error conditions. !self.check_and_clear_error_flags()?.btf() - } {} + } { + check_timeout()?; + } Ok(()) } - unsafe fn recv_byte(&self) -> Result { + unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result { while { // Check for any potential error conditions. self.check_and_clear_error_flags()?; !T::regs().sr1().read().rxne() - } {} + } { + check_timeout()?; + } let value = T::regs().dr().read().dr(); Ok(value) } - pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read_timeout( + &mut self, + addr: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { if let Some((last, buffer)) = buffer.split_last_mut() { // Send a START condition and set ACK bit unsafe { @@ -221,27 +243,33 @@ impl<'d, T: Instance> I2c<'d, T> { } // Wait until START condition was generated - while unsafe { !T::regs().sr1().read().start() } {} + while unsafe { !self.check_and_clear_error_flags()?.start() } { + check_timeout()?; + } // Also wait until signalled we're master and everything is waiting for us while { let sr2 = unsafe { T::regs().sr2().read() }; !sr2.msl() && !sr2.busy() - } {} + } { + check_timeout()?; + } // Set up current address, we're trying to talk to unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) } // Wait until address was sent // Wait for the address to be acknowledged - while unsafe { !self.check_and_clear_error_flags()?.addr() } {} + while unsafe { !self.check_and_clear_error_flags()?.addr() } { + check_timeout()?; + } // Clear condition by reading SR2 let _ = unsafe { T::regs().sr2().read() }; // Receive bytes into buffer for c in buffer { - *c = unsafe { self.recv_byte()? }; + *c = unsafe { self.recv_byte(&check_timeout)? }; } // Prepare to send NACK then STOP after next byte @@ -253,10 +281,12 @@ impl<'d, T: Instance> I2c<'d, T> { } // Receive last byte - *last = unsafe { self.recv_byte()? }; + *last = unsafe { self.recv_byte(&check_timeout)? }; // Wait for the STOP to be sent. - while unsafe { T::regs().cr1().read().stop() } {} + while unsafe { T::regs().cr1().read().stop() } { + check_timeout()?; + } // Fallthrough is success Ok(()) @@ -265,25 +295,50 @@ impl<'d, T: Instance> I2c<'d, T> { } } - pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, buffer, || Ok(())) + } + + pub fn blocking_write_timeout( + &mut self, + addr: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { unsafe { - self.write_bytes(addr, bytes)?; + self.write_bytes(addr, bytes, &check_timeout)?; // Send a STOP condition T::regs().cr1().modify(|reg| reg.set_stop(true)); // Wait for STOP condition to transmit. - while T::regs().cr1().read().stop() {} + while T::regs().cr1().read().stop() { + check_timeout()?; + } }; // Fallthrough is success Ok(()) } - pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - unsafe { self.write_bytes(addr, bytes)? }; - self.blocking_read(addr, buffer)?; + pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, bytes, || Ok(())) + } + + pub fn blocking_write_read_timeout( + &mut self, + addr: u8, + bytes: &[u8], + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { + unsafe { self.write_bytes(addr, bytes, &check_timeout)? }; + self.blocking_read_timeout(addr, buffer, &check_timeout)?; Ok(()) } + + pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(())) + } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs new file mode 100644 index 000000000..99e3cecfc --- /dev/null +++ b/examples/stm32f4/src/bin/i2c.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} From e3cf4255c6395ff5174432ab332189adf1ef431a Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 11:31:54 +0300 Subject: [PATCH 0262/1575] Help compiler with type inference --- embassy-stm32/src/time_driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index ab55cc081..8e84570a4 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -296,7 +296,7 @@ impl Driver for RtcDriver { critical_section::with(|cs| { let r = T::regs_gp16(); - let n = alarm.id() as _; + let n = alarm.id() as usize; let alarm = self.get_alarm(cs, alarm); alarm.timestamp.set(timestamp); From d99841fea90caccfd95c2dad8f233ab3198f7371 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 11:38:15 +0300 Subject: [PATCH 0263/1575] Implement time feature --- embassy-stm32/Cargo.toml | 5 ++++- embassy-stm32/src/i2c/mod.rs | 2 ++ embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/subghz/timeout.rs | 1 + embassy-stm32/src/subghz/tx_params.rs | 1 + 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9194ae788..c598604bd 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -82,9 +82,12 @@ memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] +# Enables additional driver features that depend on embassy-time +time = ["dep:embassy-time"] + # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. -_time-driver = ["dep:embassy-time"] +_time-driver = ["time"] time-driver-any = ["_time-driver"] time-driver-tim2 = ["_time-driver"] time-driver-tim3 = ["_time-driver"] diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f4f64992c..f898fcc8b 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,7 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; +#[cfg(feature = "time")] mod timeout; +#[cfg(feature = "time")] pub use timeout::*; use crate::peripherals; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0392e8086..bcf2feee8 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -52,7 +52,7 @@ pub mod sdmmc; pub mod spi; #[cfg(usart)] pub mod usart; -#[cfg(usb)] +#[cfg(all(usb, feature = "time"))] pub mod usb; #[cfg(any(otgfs, otghs))] pub mod usb_otg; diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs index 28b3b0c21..0ae49dd90 100644 --- a/embassy-stm32/src/subghz/timeout.rs +++ b/embassy-stm32/src/subghz/timeout.rs @@ -439,6 +439,7 @@ impl From for [u8; 3] { } } +#[cfg(feature = "time")] impl From for embassy_time::Duration { fn from(to: Timeout) -> Self { embassy_time::Duration::from_micros(to.as_micros().into()) diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs index cede6f2c1..03bdb1ea8 100644 --- a/embassy-stm32/src/subghz/tx_params.rs +++ b/embassy-stm32/src/subghz/tx_params.rs @@ -44,6 +44,7 @@ impl From for core::time::Duration { } } +#[cfg(feature = "time")] impl From for embassy_time::Duration { fn from(rt: RampTime) -> Self { match rt { From 545cc9326b47efc27549a60b3539e93ea0d04d70 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 23 Oct 2022 16:31:10 -0500 Subject: [PATCH 0264/1575] stm32/adc: Remove voltage and temperature conversions --- embassy-stm32/src/adc/f1.rs | 21 ------------------ embassy-stm32/src/adc/v2.rs | 23 ------------------- embassy-stm32/src/adc/v3.rs | 39 --------------------------------- embassy-stm32/src/adc/v4.rs | 14 ------------ examples/stm32f1/src/bin/adc.rs | 12 ++++++++-- examples/stm32f4/src/bin/adc.rs | 27 ++++++++++++++++++++--- examples/stm32f7/src/bin/adc.rs | 12 +++++++++- 7 files changed, 45 insertions(+), 103 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 50d4f9bf9..c5b317ce9 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -86,7 +86,6 @@ pub use sample_time::SampleTime; pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - calibrated_vdda: u32, phantom: PhantomData<&'d mut T>, } @@ -122,7 +121,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), - calibrated_vdda: VDDA_CALIB_MV, phantom: PhantomData, } } @@ -162,29 +160,10 @@ impl<'d, T: Instance> Adc<'d, T> { Temperature {} } - /// Calculates the system VDDA by sampling the internal VREF channel and comparing - /// to the expected value. If the chip's VDDA is not stable, run this before each ADC - /// conversion. - pub fn calibrate(&mut self, vref: &mut Vref) -> u32 { - let old_sample_time = self.sample_time; - self.sample_time = SampleTime::Cycles239_5; - - let vref_samp = self.read(vref); - self.sample_time = old_sample_time; - - self.calibrated_vdda = (ADC_MAX * VREF_INT) / u32::from(vref_samp); - self.calibrated_vdda - } - pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.calibrated_vdda) / ADC_MAX) as u16 - } - /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 4fe4ad1f0..53419c7f2 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -80,15 +80,6 @@ impl super::sealed::InternalChannel for Temperature { } impl Temperature { - /// Converts temperature sensor reading in millivolts to degrees celcius - pub fn to_celcius(sample_mv: u16) -> f32 { - // From 6.3.22 Temperature sensor characteristics - const V25: i32 = 760; // mV - const AVG_SLOPE: f32 = 2.5; // mV/C - - (sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0 - } - /// Time needed for temperature sensor readings to stabilize pub fn start_time_us() -> u32 { 10 @@ -172,7 +163,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -200,7 +190,6 @@ where Self { sample_time: Default::default(), resolution: Resolution::default(), - vref_mv: VREF_DEFAULT_MV, phantom: PhantomData, } } @@ -213,18 +202,6 @@ where self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /// Enables internal voltage reference and returns [VrefInt], which can be used in /// [Adc::read_internal()] to perform conversion. pub fn enable_vrefint(&self) -> VrefInt { diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 0f1090888..816feeac7 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -205,7 +205,6 @@ pub use sample_time::SampleTime; pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -244,7 +243,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), resolution: Resolution::default(), - vref_mv: VREF_DEFAULT_MV, phantom: PhantomData, } } @@ -285,31 +283,6 @@ impl<'d, T: Instance> Adc<'d, T> { Vbat {} } - /// Calculates the system VDDA by sampling the internal VREFINT channel and comparing - /// the result with the value stored at the factory. If the chip's VDDA is not stable, run - /// this before each ADC conversion. - #[cfg(not(stm32g0))] // TODO is this supposed to be public? - #[allow(unused)] // TODO is this supposed to be public? - fn calibrate(&mut self, vrefint: &mut VrefInt) { - #[cfg(stm32l5)] - let vrefint_cal: u32 = todo!(); - #[cfg(not(stm32l5))] - let vrefint_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() }; - let old_sample_time = self.sample_time; - - // "Table 24. Embedded internal voltage reference" states that the sample time needs to be - // at a minimum 4 us. With 640.5 ADC cycles we have a minimum of 8 us at 80 MHz, leaving - // some headroom. - self.sample_time = SampleTime::Cycles640_5; - - // This can't actually fail, it's just in a result to satisfy hal trait - let vrefint_samp = self.read(vrefint); - - self.sample_time = old_sample_time; - - self.vref_mv = (VREF_CALIB_MV * u32::from(vrefint_cal)) / u32::from(vrefint_samp); - } - pub fn set_sample_time(&mut self, sample_time: SampleTime) { self.sample_time = sample_time; } @@ -318,18 +291,6 @@ impl<'d, T: Instance> Adc<'d, T> { self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /* /// Convert a raw sample from the `Temperature` to deg C pub fn to_degrees_centigrade(sample: u16) -> f32 { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index eda2b2a72..2b8f10533 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -314,7 +314,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - vref_mv: u32, resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -352,7 +351,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { let mut s = Self { sample_time: Default::default(), - vref_mv: VREF_DEFAULT_MV, resolution: Resolution::default(), phantom: PhantomData, }; @@ -459,18 +457,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { self.resolution = resolution; } - /// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion. - /// - /// Use this if you have a known precise VREF (VDDA) pin reference voltage. - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - - /// Convert a measurement to millivolts - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 - } - /// Perform a single conversion. fn convert(&mut self) -> u16 { unsafe { diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 2d6b4a0e9..3521d06bd 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -17,10 +17,18 @@ async fn main(_spawner: Spawner) { let mut pin = p.PB1; let mut vref = adc.enable_vref(&mut Delay); - adc.calibrate(&mut vref); + let vref_sample = adc.read(&mut vref); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/CD00161566.pdf + // 5.3.4 Embedded reference voltage + const VREF_MV: u32 = 1200; + + (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + }; + loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 1d030f7dc..5e036bb44 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,19 +24,40 @@ async fn main(_spawner: Spawner) { // Startup delay can be combined to the maximum of either delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); + let vref_sample = adc.read_internal(&mut vrefint); + + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00071990.pdf + // 6.3.24 Reference voltage + const VREF_MILLIVOLTS: u32 = 1210; // mV + + (u32::from(sample) * VREF_MILLIVOLTS / u32::from(vref_sample)) as u16 + }; + + let convert_to_celcius = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00071990.pdf + // 6.3.22 Temperature sensor characteristics + const V25: i32 = 760; // mV + const AVG_SLOPE: f32 = 2.5; // mV/C + + let sample_mv = convert_to_millivolts(sample) as i32; + + (sample_mv - V25) as f32 / AVG_SLOPE + 25.0 + }; + loop { // Read pin let v = adc.read(&mut pin); - info!("PC1: {} ({} mV)", v, adc.to_millivolts(v)); + info!("PC1: {} ({} mV)", v, convert_to_millivolts(v)); // Read internal temperature let v = adc.read_internal(&mut temp); - let celcius = Temperature::to_celcius(adc.to_millivolts(v)); + let celcius = convert_to_celcius(v); info!("Internal temp: {} ({} C)", v, celcius); // Read internal voltage reference let v = adc.read_internal(&mut vrefint); - info!("VrefInt: {} ({} mV)", v, adc.to_millivolts(v)); + info!("VrefInt: {} ({} mV)", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index 80fad8c41..d932f8b31 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -16,9 +16,19 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PA3; + let mut vref = adc.enable_vrefint(); + let vref_sample = adc.read_internal(&mut vref); + let convert_to_millivolts = |sample| { + // From http://www.st.com/resource/en/datasheet/DM00273119.pdf + // 6.3.27 Reference voltage + const VREF_MV: u32 = 1210; + + (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + }; + loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } From 516f4ce94684d9b3f9310c9972f878dbf883600c Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 24 Oct 2022 12:15:53 +0300 Subject: [PATCH 0265/1575] Fix embassy-time wasm build and fix a bug in wasm time driver --- embassy-time/src/driver_wasm.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index d7a6b0d8d..63d049897 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -81,26 +81,32 @@ impl Driver for TimeDriver { } } - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) -> bool { + fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; alarm.closure.replace(Closure::new(move || { callback(ctx); })); - - true } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { + fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); let alarm = &mut alarms[alarm.id() as usize]; - let timeout = (timestamp - self.now()) as u32; if let Some(token) = alarm.token { clearTimeout(token); } - alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + + let now = self.now(); + if timestamp <= now { + false + } else { + let timeout = (timestamp - now) as u32; + alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + + true + } } } From 1bed02296cf15013e0149b36c3ddedb3278e9b88 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:33:17 +0300 Subject: [PATCH 0266/1575] i2cv2 timeouts --- embassy-stm32/src/i2c/v2.rs | 204 ++++++++++++++++++++++---------- examples/stm32h7/src/bin/i2c.rs | 41 +++++++ 2 files changed, 182 insertions(+), 63 deletions(-) create mode 100644 examples/stm32h7/src/bin/i2c.rs diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 89b52da98..40413b696 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -147,14 +147,23 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - unsafe fn master_read(address: u8, length: usize, stop: Stop, reload: bool, restart: bool) { + unsafe fn master_read( + address: u8, + length: usize, + stop: Stop, + reload: bool, + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256); if !restart { // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() {} + while T::regs().cr2().read().start() { + check_timeout()?; + } } // Set START and prepare to receive bytes into @@ -176,15 +185,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_autoend(stop.autoend()); w.set_reload(reload); }); + + Ok(()) } - unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) { + unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { assert!(length < 256); // Wait for any previous address sequence to end // automatically. This could be up to 50% of a bus // cycle (ie. up to 0.5/freq) - while T::regs().cr2().read().start() {} + while T::regs().cr2().read().start() { + check_timeout()?; + } let reload = if reload { i2c::vals::Reload::NOTCOMPLETED @@ -204,12 +217,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_autoend(stop.autoend()); w.set_reload(reload); }); + + Ok(()) } - unsafe fn master_continue(length: usize, reload: bool) { + unsafe fn master_continue(length: usize, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { assert!(length < 256 && length > 0); - while !T::regs().isr().read().tcr() {} + while !T::regs().isr().read().tcr() { + check_timeout()?; + } let reload = if reload { i2c::vals::Reload::NOTCOMPLETED @@ -221,6 +238,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_nbytes(length as u8); w.set_reload(reload); }); + + Ok(()) } fn flush_txdr(&self) { @@ -243,7 +262,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { //} } - fn wait_txe(&self) -> Result<(), Error> { + fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -261,10 +280,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn wait_rxne(&self) -> Result<(), Error> { + fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -282,10 +303,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn wait_tc(&self) -> Result<(), Error> { + fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { unsafe { let isr = T::regs().isr().read(); @@ -303,10 +326,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Nack); } } + + check_timeout()?; } } - fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> { + fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { let completed_chunks = buffer.len() / 255; let total_chunks = if completed_chunks * 255 == buffer.len() { completed_chunks @@ -322,20 +347,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Automatic, last_chunk_idx != 0, restart, - ); + &check_timeout + )?; } for (number, chunk) in buffer.chunks_mut(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx); + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } } for byte in chunk { // Wait until we have received something - self.wait_rxne()?; + self.wait_rxne(&check_timeout)?; unsafe { *byte = T::regs().rxdr().read().rxdata(); @@ -345,7 +371,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool) -> Result<(), Error> { + fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { let completed_chunks = bytes.len() / 255; let total_chunks = if completed_chunks * 255 == bytes.len() { completed_chunks @@ -359,14 +385,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ST SAD+W // NOTE(unsafe) We have &mut self unsafe { - Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0); + Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout)?; } for (number, chunk) in bytes.chunks(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx); + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } } @@ -374,7 +400,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe()?; + self.wait_txe(&check_timeout)?; unsafe { T::regs().txdr().write(|w| w.set_txdata(*byte)); @@ -382,7 +408,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc()?; + self.wait_tc(&check_timeout)?; if send_stop { self.master_stop(); @@ -396,6 +422,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { bytes: &[u8], first_slice: bool, last_slice: bool, + check_timeout: impl Fn() -> Result<(), Error> ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -447,11 +474,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { total_len.min(255), Stop::Software, (total_chunks != 1) || !last_slice, - ); + &check_timeout + )?; } } else { unsafe { - Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice); + Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?; T::regs().cr1().modify(|w| w.set_tcie(true)); } } @@ -461,32 +489,34 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); if chunks_transferred == total_chunks { - return Poll::Ready(()); + return Poll::Ready(Ok(())); } else if chunks_transferred != 0 { remaining_len = remaining_len.saturating_sub(255); let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice; // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers unsafe { - Self::master_continue(remaining_len.min(255), !last_piece); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)); + } T::regs().cr1().modify(|w| w.set_tcie(true)); } } Poll::Pending }) - .await; + .await?; dma_transfer.await; if last_slice { // This should be done already - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); } Ok(()) } - async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool) -> Result<(), Error> + async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { @@ -527,7 +557,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart); + Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart, &check_timeout)?; } poll_fn(|cx| { @@ -535,25 +565,27 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); if chunks_transferred == total_chunks { - return Poll::Ready(()); + return Poll::Ready(Ok(())); } else if chunks_transferred != 0 { remaining_len = remaining_len.saturating_sub(255); let last_piece = chunks_transferred + 1 == total_chunks; // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_continue(remaining_len.min(255), !last_piece); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)) + } T::regs().cr1().modify(|w| w.set_tcie(true)); } } Poll::Pending }) - .await; + .await?; dma_transfer.await; // This should be done already - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); Ok(()) } @@ -561,18 +593,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + pub async fn write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { if bytes.is_empty() { - self.write_internal(address, bytes, true) + self.write_internal(address, bytes, true, &check_timeout) } else { - self.write_dma_internal(address, bytes, true, true).await + self.write_dma_internal(address, bytes, true, true, &check_timeout).await } } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_timeout(address, bytes, || Ok(())).await + } + + pub async fn write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -587,63 +626,97 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - self.write_dma_internal(address, c, first, is_last).await?; + self.write_dma_internal(address, c, first, is_last, &check_timeout).await?; first = false; current = next; } Ok(()) } - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_vectored_timeout(address, bytes, || Ok(())).await + } + + pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { if buffer.is_empty() { - self.read_internal(address, buffer, false) + self.read_internal(address, buffer, false, &check_timeout) } else { - self.read_dma_internal(address, buffer, false).await + self.read_dma_internal(address, buffer, false, &check_timeout).await } } + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_timeout(address, buffer, || Ok(())).await + } + + pub async fn write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + if bytes.is_empty() { + self.write_internal(address, bytes, false, &check_timeout)?; + } else { + self.write_dma_internal(address, bytes, true, true, &check_timeout).await?; + } + + if buffer.is_empty() { + self.read_internal(address, buffer, true, &check_timeout)?; + } else { + self.read_dma_internal(address, buffer, true, &check_timeout).await?; + } + + Ok(()) + } + pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { - if bytes.is_empty() { - self.write_internal(address, bytes, false)?; - } else { - self.write_dma_internal(address, bytes, true, true).await?; - } - - if buffer.is_empty() { - self.read_internal(address, buffer, true)?; - } else { - self.read_dma_internal(address, buffer, true).await?; - } - - Ok(()) + self.write_read_timeout(address, bytes, buffer, || Ok(())).await } // ========================= // Blocking public API - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.read_internal(address, buffer, false) + pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + self.read_internal(address, buffer, false, &check_timeout) // Automatic Stop } + pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(address, buffer, || Ok(())) + } + + pub fn blocking_write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + self.write_internal(address, bytes, true, &check_timeout) + } + pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - self.write_internal(address, bytes, true) + self.blocking_write_timeout(address, bytes, || Ok(())) } - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.write_internal(address, bytes, false)?; - self.read_internal(address, buffer, true) + pub fn blocking_write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + self.write_internal(address, bytes, false, &check_timeout)?; + self.read_internal(address, buffer, true, &check_timeout) // Automatic Stop } - pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) + } + + pub fn blocking_write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { if bytes.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -657,7 +730,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), - ); + &check_timeout + )?; } for (idx, slice) in bytes.iter().enumerate() { @@ -673,7 +747,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if idx != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255)); + Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), &check_timeout)?; } } @@ -681,7 +755,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index)); + Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), &check_timeout)?; } } @@ -689,7 +763,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe()?; + self.wait_txe(&check_timeout)?; // Put byte on the wire //self.i2c.txdr.write(|w| w.txdata().bits(*byte)); @@ -700,11 +774,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc()?; + self.wait_tc(&check_timeout)?; self.master_stop(); Ok(()) } + + pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { + self.blocking_write_vectored_timeout(address, bytes, || Ok(())) + } } mod eh02 { diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs new file mode 100644 index 000000000..7a314b996 --- /dev/null +++ b/examples/stm32h7/src/bin/i2c.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + p.DMA1_CH4, + p.DMA1_CH5, + Hertz(100_000), + Default::default(), + ); + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} From 5f02bee388c1754a95e5cab95338aef9c5605c9a Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:34:55 +0300 Subject: [PATCH 0267/1575] Gate TimeoutI2c behind i2cv1 --- embassy-stm32/src/i2c/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f898fcc8b..1dffbd10a 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,9 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; -#[cfg(feature = "time")] +#[cfg(all(i2c_v1, feature = "time"))] mod timeout; -#[cfg(feature = "time")] +#[cfg(all(i2c_v1, feature = "time"))] pub use timeout::*; use crate::peripherals; From 9b209ffe1c9048f296213c7050919ccbcd7ded1b Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 12:39:47 +0300 Subject: [PATCH 0268/1575] Add docs --- embassy-stm32/src/i2c/timeout.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 12319af8a..c4c035b42 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -2,6 +2,7 @@ use embassy_time::{Duration, Instant}; use super::{Error, I2c, Instance}; +/// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. pub struct TimeoutI2c<'d, T: Instance> { i2c: &'d mut I2c<'d, T>, timeout: Duration, @@ -23,22 +24,27 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { Self { i2c, timeout } } + /// Blocking read with a custom timeout pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) } + /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`] pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { self.blocking_read_timeout(addr, buffer, self.timeout) } + /// Blocking write with a custom timeout pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) } + /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`] pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { self.blocking_write_timeout(addr, bytes, self.timeout) } + /// Blocking write-read with a custom timeout pub fn blocking_write_read_timeout( &mut self, addr: u8, @@ -50,6 +56,7 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) } + /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`] pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) } From ad0eb3f4bd2b124fcb7cda6d6bd88e2b12632ea7 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 24 Oct 2022 12:17:22 +0200 Subject: [PATCH 0269/1575] Implement flash padding to 256 under assumption that all QSPI NOR flashes are MultiwriteNorFlashes --- embassy-rp/src/flash.rs | 56 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 1ff2fd195..7a26085b6 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,5 +1,6 @@ use embedded_storage::nor_flash::{ - check_erase, check_read, check_write, ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, + check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, + ReadNorFlash, }; pub const FLASH_BASE: usize = 0x10000000; @@ -9,7 +10,8 @@ pub const FLASH_BASE: usize = 0x10000000; // These limitations are currently enforced because of using the // RP2040 boot-rom flash functions, that are optimized for flash compatibility // rather than performance. -pub const WRITE_SIZE: usize = 256; +pub const PAGE_SIZE: usize = 256; +pub const WRITE_SIZE: usize = 1; pub const READ_SIZE: usize = 1; pub const ERASE_SIZE: usize = 4096; @@ -100,6 +102,8 @@ impl ReadNorFlash for Flash { } } +impl MultiwriteNorFlash for Flash {} + impl NorFlash for Flash { const WRITE_SIZE: usize = WRITE_SIZE; @@ -120,12 +124,58 @@ impl NorFlash for Flash { trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), offset); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(offset, bytes, true)) }; + let end_offset = offset as usize + bytes.len(); + + let padded_offset = (offset as *const u8).align_offset(PAGE_SIZE); + let start_padding = core::cmp::min(padded_offset, bytes.len()); + + // Pad in the beginning + if start_padding > 0 { + let start = PAGE_SIZE - padded_offset; + let end = start + start_padding; + + let mut pad_buf = [0xFF_u8; PAGE_SIZE]; + pad_buf[start..end].copy_from_slice(&bytes[..start_padding]); + + let unaligned_offset = offset as usize - start; + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + } + + let remaining_len = bytes.len() - start_padding; + let end_padding = start_padding + PAGE_SIZE * (remaining_len / PAGE_SIZE); + + // Write aligned slice of length in multiples of 256 bytes + // If the remaining bytes to be written is more than a full page. + if remaining_len >= PAGE_SIZE { + let aligned_data = &bytes[start_padding..end_padding]; + + let aligned_offset = if start_padding > 0 { + offset as usize + padded_offset + } else { + offset as usize + }; + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + } + + // Pad in the end + let rem_offset = (end_offset as *const u8).align_offset(PAGE_SIZE); + let rem_padding = remaining_len % PAGE_SIZE; + if rem_padding > 0 { + let mut pad_buf = [0xFF_u8; PAGE_SIZE]; + pad_buf[..rem_padding].copy_from_slice(&bytes[end_padding..]); + + let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset); + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + } Ok(()) } } +#[allow(dead_code)] mod ram_helpers { use core::marker::PhantomData; From ca8afacfd07b24c27441f5a4ea1d66719fd50038 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:11:15 +0300 Subject: [PATCH 0270/1575] Implement TimeoutI2c for i2cv2 --- embassy-stm32/src/i2c/mod.rs | 4 ++-- embassy-stm32/src/i2c/timeout.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 1dffbd10a..f898fcc8b 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -7,9 +7,9 @@ use crate::interrupt::Interrupt; mod _version; pub use _version::*; -#[cfg(all(i2c_v1, feature = "time"))] +#[cfg(feature = "time")] mod timeout; -#[cfg(all(i2c_v1, feature = "time"))] +#[cfg(feature = "time")] pub use timeout::*; use crate::peripherals; diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index c4c035b42..fd9a753a5 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -3,8 +3,8 @@ use embassy_time::{Duration, Instant}; use super::{Error, I2c, Instance}; /// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. -pub struct TimeoutI2c<'d, T: Instance> { - i2c: &'d mut I2c<'d, T>, +pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { + i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, } @@ -19,8 +19,8 @@ fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { } } -impl<'d, T: Instance> TimeoutI2c<'d, T> { - pub fn new(i2c: &'d mut I2c<'d, T>, timeout: Duration) -> Self { +impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { + pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { Self { i2c, timeout } } @@ -62,7 +62,7 @@ impl<'d, T: Instance> TimeoutI2c<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -70,7 +70,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T> } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -78,7 +78,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -90,11 +90,11 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<' mod eh1 { use super::*; - impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T> { + impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T> { + impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, buffer) } From 6062978d58915e1d0c7db103365f0048f836babc Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:22:20 +0300 Subject: [PATCH 0271/1575] Remove weird async timeouts --- embassy-stm32/src/i2c/v2.rs | 73 +++++++++++-------------------------- 1 file changed, 22 insertions(+), 51 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 40413b696..876f6223f 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -593,25 +593,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - if bytes.is_empty() { - self.write_internal(address, bytes, true, &check_timeout) - } else { - self.write_dma_internal(address, bytes, true, true, &check_timeout).await - } - } - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { - self.write_timeout(address, bytes, || Ok(())).await + if bytes.is_empty() { + self.write_internal(address, bytes, true, || Ok(())) + } else { + self.write_dma_internal(address, bytes, true, true, || Ok(())).await + } } - pub async fn write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -626,56 +619,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - self.write_dma_internal(address, c, first, is_last, &check_timeout).await?; + self.write_dma_internal(address, c, first, is_last, || Ok(())).await?; first = false; current = next; } Ok(()) } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - self.write_vectored_timeout(address, bytes, || Ok(())).await - } - - pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - if buffer.is_empty() { - self.read_internal(address, buffer, false, &check_timeout) - } else { - self.read_dma_internal(address, buffer, false, &check_timeout).await - } - } - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { - self.read_timeout(address, buffer, || Ok(())).await - } - - pub async fn write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> - where - TXDMA: super::TxDma, - RXDMA: super::RxDma, - { - if bytes.is_empty() { - self.write_internal(address, bytes, false, &check_timeout)?; - } else { - self.write_dma_internal(address, bytes, true, true, &check_timeout).await?; - } - if buffer.is_empty() { - self.read_internal(address, buffer, true, &check_timeout)?; + self.read_internal(address, buffer, false, || Ok(())) } else { - self.read_dma_internal(address, buffer, true, &check_timeout).await?; + self.read_dma_internal(address, buffer, false, || Ok(())).await } - - Ok(()) } pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> @@ -683,7 +642,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { TXDMA: super::TxDma, RXDMA: super::RxDma, { - self.write_read_timeout(address, bytes, buffer, || Ok(())).await + if bytes.is_empty() { + self.write_internal(address, bytes, false, || Ok(()))?; + } else { + self.write_dma_internal(address, bytes, true, true, || Ok(())).await?; + } + + if buffer.is_empty() { + self.read_internal(address, buffer, true, || Ok(()))?; + } else { + self.read_dma_internal(address, buffer, true, || Ok(())).await?; + } + + Ok(()) } // ========================= From 33f75419e542ef52d7d6a1403c9e3dbfd1c39abe Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:34:10 +0300 Subject: [PATCH 0272/1575] Unify i2cv1 definition with i2cv2 --- embassy-stm32/src/i2c/v1.rs | 22 +++++++++++++++++----- examples/stm32f4/src/bin/i2c.rs | 14 +++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 92a898824..f140e2b0d 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,8 +1,9 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::into_ref; +use embassy_hal_common::{into_ref, PeripheralRef}; +use crate::dma::NoDma; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; @@ -34,19 +35,26 @@ impl State { } } -pub struct I2c<'d, T: Instance> { +pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { phantom: PhantomData<&'d mut T>, + #[allow(dead_code)] + tx_dma: PeripheralRef<'d, TXDMA>, + #[allow(dead_code)] + rx_dma: PeripheralRef<'d, RXDMA>, } -impl<'d, T: Instance> I2c<'d, T> { +impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn new( _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, + _irq: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, freq: Hertz, config: Config, ) -> Self { - into_ref!(scl, sda); + into_ref!(scl, sda, tx_dma, rx_dma); T::enable(); T::reset(); @@ -99,7 +107,11 @@ impl<'d, T: Instance> I2c<'d, T> { }); } - Self { phantom: PhantomData } + Self { + phantom: PhantomData, + tx_dma, + rx_dma, + } } unsafe fn check_and_clear_error_flags(&self) -> Result { diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 99e3cecfc..12965d2b8 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -4,7 +4,9 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +19,17 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + NoDma, + NoDma, + Hertz(100_000), + Default::default(), + ); let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); let mut data = [0u8; 1]; From ac61e0ee9fcec2792c73806afe4c998019b0db75 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:39:13 +0300 Subject: [PATCH 0273/1575] fmt --- embassy-stm32/src/i2c/v2.rs | 106 ++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 876f6223f..aa4e6bb08 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -189,7 +189,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + unsafe fn master_write( + address: u8, + length: usize, + stop: Stop, + reload: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256); // Wait for any previous address sequence to end @@ -221,7 +227,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_continue(length: usize, reload: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + unsafe fn master_continue( + length: usize, + reload: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { assert!(length < 256 && length > 0); while !T::regs().isr().read().tcr() { @@ -331,7 +341,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - fn read_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn read_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { let completed_chunks = buffer.len() / 255; let total_chunks = if completed_chunks * 255 == buffer.len() { completed_chunks @@ -347,7 +363,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Stop::Automatic, last_chunk_idx != 0, restart, - &check_timeout + &check_timeout, )?; } @@ -371,7 +387,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - fn write_internal(&mut self, address: u8, bytes: &[u8], send_stop: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn write_internal( + &mut self, + address: u8, + bytes: &[u8], + send_stop: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { let completed_chunks = bytes.len() / 255; let total_chunks = if completed_chunks * 255 == bytes.len() { completed_chunks @@ -385,7 +407,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ST SAD+W // NOTE(unsafe) We have &mut self unsafe { - Self::master_write(address, bytes.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout)?; + Self::master_write( + address, + bytes.len().min(255), + Stop::Software, + last_chunk_idx != 0, + &check_timeout, + )?; } for (number, chunk) in bytes.chunks(255).enumerate() { @@ -422,7 +450,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { bytes: &[u8], first_slice: bool, last_slice: bool, - check_timeout: impl Fn() -> Result<(), Error> + check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -474,7 +502,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { total_len.min(255), Stop::Software, (total_chunks != 1) || !last_slice, - &check_timeout + &check_timeout, )?; } } else { @@ -516,7 +544,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - async fn read_dma_internal(&mut self, address: u8, buffer: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> + async fn read_dma_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { @@ -557,7 +591,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { - Self::master_read(address, total_len.min(255), Stop::Software, total_chunks != 1, restart, &check_timeout)?; + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_chunks != 1, + restart, + &check_timeout, + )?; } poll_fn(|cx| { @@ -573,7 +614,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { - return Poll::Ready(Err(e)) + return Poll::Ready(Err(e)); } T::regs().cr1().modify(|w| w.set_tcie(true)); } @@ -660,7 +701,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Blocking public API - pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_read_timeout( + &mut self, + address: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { self.read_internal(address, buffer, false, &check_timeout) // Automatic Stop } @@ -669,7 +715,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.blocking_read_timeout(address, buffer, || Ok(())) } - pub fn blocking_write_timeout(&mut self, address: u8, bytes: &[u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_write_timeout( + &mut self, + address: u8, + bytes: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { self.write_internal(address, bytes, true, &check_timeout) } @@ -677,7 +728,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.blocking_write_timeout(address, bytes, || Ok(())) } - pub fn blocking_write_read_timeout(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_write_read_timeout( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { self.write_internal(address, bytes, false, &check_timeout)?; self.read_internal(address, buffer, true, &check_timeout) // Automatic Stop @@ -687,7 +744,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) } - pub fn blocking_write_vectored_timeout(&mut self, address: u8, bytes: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + pub fn blocking_write_vectored_timeout( + &mut self, + address: u8, + bytes: &[&[u8]], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> { if bytes.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -701,7 +763,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), - &check_timeout + &check_timeout, )?; } @@ -718,7 +780,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if idx != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), &check_timeout)?; + Self::master_continue( + slice_len.min(255), + (idx != last_slice_index) || (slice_len > 255), + &check_timeout, + )?; } } @@ -726,7 +792,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { - Self::master_continue(chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), &check_timeout)?; + Self::master_continue( + chunk.len(), + (number != last_chunk_idx) || (idx != last_slice_index), + &check_timeout, + )?; } } From 52c03cf0a4ae5a7a6374e6acac123670b83860fe Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Oct 2022 22:48:40 +0300 Subject: [PATCH 0274/1575] Add more docs --- embassy-stm32/src/i2c/timeout.rs | 3 +++ examples/stm32f4/src/bin/i2c.rs | 3 +++ examples/stm32h7/src/bin/i2c.rs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index fd9a753a5..4fca1ca2b 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -3,6 +3,9 @@ use embassy_time::{Duration, Instant}; use super::{Error, I2c, Instance}; /// An I2C wrapper, which provides `embassy-time` based timeouts for all `embedded-hal` trait methods. +/// +/// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state. +/// A regular [I2c] would freeze until condition is removed. pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 12965d2b8..6e51c211d 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -30,6 +30,9 @@ async fn main(_spawner: Spawner) -> ! { Hertz(100_000), Default::default(), ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); let mut data = [0u8; 1]; diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs index 7a314b996..d44319ae6 100644 --- a/examples/stm32h7/src/bin/i2c.rs +++ b/examples/stm32h7/src/bin/i2c.rs @@ -29,6 +29,9 @@ async fn main(_spawner: Spawner) -> ! { Hertz(100_000), Default::default(), ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); let mut data = [0u8; 1]; From 7a6732adcfd09d72f5335f85cbe4e263234849e7 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Mon, 24 Oct 2022 15:01:16 -0500 Subject: [PATCH 0275/1575] Improve examples --- examples/stm32f1/src/bin/adc.rs | 8 ++++---- examples/stm32f4/src/bin/adc.rs | 12 ++++++++---- examples/stm32f7/src/bin/adc.rs | 8 ++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs index 3521d06bd..ed59e2799 100644 --- a/examples/stm32f1/src/bin/adc.rs +++ b/examples/stm32f1/src/bin/adc.rs @@ -16,14 +16,14 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PB1; - let mut vref = adc.enable_vref(&mut Delay); - let vref_sample = adc.read(&mut vref); + let mut vrefint = adc.enable_vref(&mut Delay); + let vrefint_sample = adc.read(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/CD00161566.pdf // 5.3.4 Embedded reference voltage - const VREF_MV: u32 = 1200; + const VREFINT_MV: u32 = 1200; // mV - (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; loop { diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5e036bb44..1c9a0b35d 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -24,14 +24,14 @@ async fn main(_spawner: Spawner) { // Startup delay can be combined to the maximum of either delay.delay_us(Temperature::start_time_us().max(VrefInt::start_time_us())); - let vref_sample = adc.read_internal(&mut vrefint); + let vrefint_sample = adc.read_internal(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00071990.pdf // 6.3.24 Reference voltage - const VREF_MILLIVOLTS: u32 = 1210; // mV + const VREFINT_MV: u32 = 1210; // mV - (u32::from(sample) * VREF_MILLIVOLTS / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; let convert_to_celcius = |sample| { @@ -45,6 +45,10 @@ async fn main(_spawner: Spawner) { (sample_mv - V25) as f32 / AVG_SLOPE + 25.0 }; + info!("VrefInt: {}", vrefint_sample); + const MAX_ADC_SAMPLE: u16 = (1 << 12) - 1; + info!("VCCA: {} mV", convert_to_millivolts(MAX_ADC_SAMPLE)); + loop { // Read pin let v = adc.read(&mut pin); @@ -57,7 +61,7 @@ async fn main(_spawner: Spawner) { // Read internal voltage reference let v = adc.read_internal(&mut vrefint); - info!("VrefInt: {} ({} mV)", v, convert_to_millivolts(v)); + info!("VrefInt: {}", v); Timer::after(Duration::from_millis(100)).await; } diff --git a/examples/stm32f7/src/bin/adc.rs b/examples/stm32f7/src/bin/adc.rs index d932f8b31..70b3b2a75 100644 --- a/examples/stm32f7/src/bin/adc.rs +++ b/examples/stm32f7/src/bin/adc.rs @@ -16,14 +16,14 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC1, &mut Delay); let mut pin = p.PA3; - let mut vref = adc.enable_vrefint(); - let vref_sample = adc.read_internal(&mut vref); + let mut vrefint = adc.enable_vrefint(); + let vrefint_sample = adc.read_internal(&mut vrefint); let convert_to_millivolts = |sample| { // From http://www.st.com/resource/en/datasheet/DM00273119.pdf // 6.3.27 Reference voltage - const VREF_MV: u32 = 1210; + const VREFINT_MV: u32 = 1210; // mV - (u32::from(sample) * VREF_MV / u32::from(vref_sample)) as u16 + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; loop { From ea868920e6859aac924a6266968f790a76d6b160 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 26 Oct 2022 09:13:26 +0200 Subject: [PATCH 0276/1575] Update nrf pacs --- embassy-nrf/Cargo.toml | 20 ++++++++++---------- embassy-nrf/src/chips/nrf52805.rs | 4 ++-- embassy-nrf/src/chips/nrf52810.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52811.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52832.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52833.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf52840.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf5340_app.rs | 16 ++++++++-------- embassy-nrf/src/chips/nrf9160.rs | 16 ++++++++-------- 9 files changed, 68 insertions(+), 68 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 5459bc90c..dca5fd1bc 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -90,13 +90,13 @@ embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } cfg-if = "1.0.0" -nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52811-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52820-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52832-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52833-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf52840-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf5340-app-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf5340-net-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } -nrf9160-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } +nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52810-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52811-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52820-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52832-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52833-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52840-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf5340-app-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf5340-net-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf9160-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index d078fa0ad..dec31a84c 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -193,8 +193,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 3e500098c..e57a4a383 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -211,14 +211,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 25c7c0d91..918404cf1 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -212,14 +212,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 18b8eda67..81e66c193 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -225,14 +225,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 3b33907d2..92499e3c9 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -271,14 +271,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index ae59f8b25..4beadfba8 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -276,14 +276,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -impl_saadc_input!(P0_02, ANALOGINPUT0); -impl_saadc_input!(P0_03, ANALOGINPUT1); -impl_saadc_input!(P0_04, ANALOGINPUT2); -impl_saadc_input!(P0_05, ANALOGINPUT3); -impl_saadc_input!(P0_28, ANALOGINPUT4); -impl_saadc_input!(P0_29, ANALOGINPUT5); -impl_saadc_input!(P0_30, ANALOGINPUT6); -impl_saadc_input!(P0_31, ANALOGINPUT7); +impl_saadc_input!(P0_02, ANALOG_INPUT0); +impl_saadc_input!(P0_03, ANALOG_INPUT1); +impl_saadc_input!(P0_04, ANALOG_INPUT2); +impl_saadc_input!(P0_05, ANALOG_INPUT3); +impl_saadc_input!(P0_28, ANALOG_INPUT4); +impl_saadc_input!(P0_29, ANALOG_INPUT5); +impl_saadc_input!(P0_30, ANALOG_INPUT6); +impl_saadc_input!(P0_31, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index edf800ef3..7845d4a8e 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -458,14 +458,14 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -impl_saadc_input!(P0_13, ANALOGINPUT0); -impl_saadc_input!(P0_14, ANALOGINPUT1); -impl_saadc_input!(P0_15, ANALOGINPUT2); -impl_saadc_input!(P0_16, ANALOGINPUT3); -impl_saadc_input!(P0_17, ANALOGINPUT4); -impl_saadc_input!(P0_18, ANALOGINPUT5); -impl_saadc_input!(P0_19, ANALOGINPUT6); -impl_saadc_input!(P0_20, ANALOGINPUT7); +impl_saadc_input!(P0_13, ANALOG_INPUT0); +impl_saadc_input!(P0_14, ANALOG_INPUT1); +impl_saadc_input!(P0_15, ANALOG_INPUT2); +impl_saadc_input!(P0_16, ANALOG_INPUT3); +impl_saadc_input!(P0_17, ANALOG_INPUT4); +impl_saadc_input!(P0_18, ANALOG_INPUT5); +impl_saadc_input!(P0_19, ANALOG_INPUT6); +impl_saadc_input!(P0_20, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index f8ed11e03..b5a53ed80 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -339,14 +339,14 @@ impl_ppi_channel!(PPI_CH13, 13 => configurable); impl_ppi_channel!(PPI_CH14, 14 => configurable); impl_ppi_channel!(PPI_CH15, 15 => configurable); -impl_saadc_input!(P0_13, ANALOGINPUT0); -impl_saadc_input!(P0_14, ANALOGINPUT1); -impl_saadc_input!(P0_15, ANALOGINPUT2); -impl_saadc_input!(P0_16, ANALOGINPUT3); -impl_saadc_input!(P0_17, ANALOGINPUT4); -impl_saadc_input!(P0_18, ANALOGINPUT5); -impl_saadc_input!(P0_19, ANALOGINPUT6); -impl_saadc_input!(P0_20, ANALOGINPUT7); +impl_saadc_input!(P0_13, ANALOG_INPUT0); +impl_saadc_input!(P0_14, ANALOG_INPUT1); +impl_saadc_input!(P0_15, ANALOG_INPUT2); +impl_saadc_input!(P0_16, ANALOG_INPUT3); +impl_saadc_input!(P0_17, ANALOG_INPUT4); +impl_saadc_input!(P0_18, ANALOG_INPUT5); +impl_saadc_input!(P0_19, ANALOG_INPUT6); +impl_saadc_input!(P0_20, ANALOG_INPUT7); pub mod irqs { use embassy_cortex_m::interrupt::_export::declare; From 0c9ec8dc362efb3a569b4b85c6fcd97a7f9d7860 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Wed, 26 Oct 2022 10:39:29 +0200 Subject: [PATCH 0277/1575] Update usb --- embassy-nrf/src/usb.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 00da5c9dd..eb1472fa5 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -313,7 +313,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } }) .await; - regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear. + regs.eventcause.write(|w| w.ready().clear_bit_by_one()); errata::post_enable(); @@ -367,24 +367,24 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { let r = regs.eventcause.read(); if r.isooutcrc().bit() { - regs.eventcause.write(|w| w.isooutcrc().set_bit()); + regs.eventcause.write(|w| w.isooutcrc().detected()); trace!("USB event: isooutcrc"); } if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().set_bit()); + regs.eventcause.write(|w| w.usbwuallowed().allowed()); trace!("USB event: usbwuallowed"); } if r.suspend().bit() { - regs.eventcause.write(|w| w.suspend().set_bit()); + regs.eventcause.write(|w| w.suspend().detected()); regs.lowpower.write(|w| w.lowpower().low_power()); return Poll::Ready(Event::Suspend); } if r.resume().bit() { - regs.eventcause.write(|w| w.resume().set_bit()); + regs.eventcause.write(|w| w.resume().detected()); return Poll::Ready(Event::Resume); } if r.ready().bit() { - regs.eventcause.write(|w| w.ready().set_bit()); + regs.eventcause.write(|w| w.ready().ready()); trace!("USB event: ready"); } @@ -512,7 +512,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } else if r.resume().bit() { Poll::Ready(()) } else if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().set_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()); From 66611a80caea216241d58cc3b848fe55b8865b49 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Oct 2022 11:51:37 +0200 Subject: [PATCH 0278/1575] Introduce shared new_inner for uart instantiation --- embassy-stm32/src/usart/mod.rs | 63 +++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 074061218..3f5b99523 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -222,10 +222,48 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(_inner, rx, tx, tx_dma, rx_dma); + T::enable(); + T::reset(); + + Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + } + + pub fn new_with_rtscts( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cts, rts); T::enable(); T::reset(); + + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); + } + Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + } + + fn new_inner( + _inner: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(_inner, rx, tx, tx_dma, rx_dma); + let pclk_freq = T::frequency(); // TODO: better calculation, including error checking and OVER8 if possible. @@ -264,29 +302,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } - pub fn new_with_rtscts( - _inner: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - rts: impl Peripheral

> + 'd, - cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(cts, rts); - - unsafe { - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_rtse(true); - w.set_ctse(true); - }); - } - Self::new(_inner, rx, tx, tx_dma, rx_dma, config) - } - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> where TxDma: crate::usart::TxDma, From 80e58426fcf40b6cea28368e43a7289e827461cf Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Oct 2022 10:01:52 +0200 Subject: [PATCH 0279/1575] Add flash example & flash HIL test --- ci.sh | 2 +- embassy-rp/src/flash.rs | 39 ++++++++++++++--- embassy-rp/src/intrinsics.rs | 1 + embassy-rp/src/lib.rs | 2 + embassy-rp/src/rom_data.rs | 1 + examples/rp/Cargo.toml | 1 + examples/rp/src/bin/flash.rs | 84 ++++++++++++++++++++++++++++++++++++ tests/rp/Cargo.toml | 1 + tests/rp/src/bin/flash.rs | 48 +++++++++++++++++++++ 9 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 examples/rp/src/bin/flash.rs create mode 100644 tests/rp/src/bin/flash.rs diff --git a/ci.sh b/ci.sh index 69440ec36..cd1c0786c 100755 --- a/ci.sh +++ b/ci.sh @@ -121,7 +121,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ - --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/time-driver --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ $BUILD_EXTRA diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 7a26085b6..62f68bd3c 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,8 +1,13 @@ +use core::marker::PhantomData; + +use embassy_hal_common::Peripheral; use embedded_storage::nor_flash::{ check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +use crate::peripherals::FLASH; + pub const FLASH_BASE: usize = 0x10000000; // **NOTE**: @@ -46,9 +51,13 @@ impl NorFlashError for Error { } } -pub struct Flash; +pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>); + +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { + pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + Self(PhantomData) + } -impl Flash { /// Make sure to uphold the contract points with rp2040-flash. /// - interrupts must be disabled /// - DMA must not access flash memory @@ -81,11 +90,11 @@ impl Flash { } } -impl ErrorType for Flash { +impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { type Error = Error; } -impl ReadNorFlash for Flash { +impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -102,9 +111,9 @@ impl ReadNorFlash for Flash { } } -impl MultiwriteNorFlash for Flash {} +impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} -impl NorFlash for Flash { +impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; @@ -112,6 +121,12 @@ impl NorFlash for Flash { fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { check_erase(self, from, to)?; + trace!( + "Erasing from 0x{:x} to 0x{:x}", + FLASH_BASE as u32 + from, + FLASH_BASE as u32 + to + ); + let len = to - from; unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) }; @@ -122,7 +137,7 @@ impl NorFlash for Flash { fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { check_write(self, offset, bytes.len())?; - trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), offset); + trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); let end_offset = offset as usize + bytes.len(); @@ -351,6 +366,7 @@ mod ram_helpers { rom_data::flash_flush_cache(); rom_data::flash_enter_cmd_xip(); */ + #[cfg(target_arch = "arm")] core::arch::asm!( "mov r8, r0", "mov r9, r2", @@ -402,3 +418,12 @@ mod ram_helpers { ); } } + +mod sealed { + pub trait Instance {} +} + +pub trait Instance: sealed::Instance {} + +impl sealed::Instance for FLASH {} +impl Instance for FLASH {} diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 67e8202a4..3e75fb7fc 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -1,4 +1,5 @@ #![macro_use] + // Credit: taken from `rp-hal` (also licensed Apache+MIT) // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 445639618..f608f1768 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -96,6 +96,8 @@ embassy_hal_common::peripherals! { USB, RTC, + + FLASH, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 8e953dcf2..757a27114 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -7,6 +7,7 @@ //! > on the device, as well as highly optimized versions of certain key //! > functionality that would otherwise have to take up space in most user //! > binaries. + // Credit: taken from `rp-hal` (also licensed Apache+MIT) // https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 747dde515..38355bbf8 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -30,6 +30,7 @@ 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.1" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +embedded-storage = { version = "0.3" } static_cell = "1.0.0" [profile.release] diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs new file mode 100644 index 000000000..17549e4be --- /dev/null +++ b/examples/rp/src/bin/flash.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::peripherals::FLASH; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +const ADDR_OFFSET: u32 = 0x100000; +const FLASH_SIZE: usize = 2 * 1024 * 1024; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); + + erase_write_sector(&mut flash, 0x00); + + multiwrite_bytes(&mut flash, ERASE_SIZE as u32); + + loop {} +} + +fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { + info!(">>>> [multiwrite_bytes]"); + let mut read_buf = [0u8; ERASE_SIZE]; + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + + info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); + info!("Contents start with {=[u8]}", read_buf[0..4]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); + if read_buf.iter().any(|x| *x != 0xFF) { + defmt::panic!("unexpected"); + } + + defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); + defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); + defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); + defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + info!("Contents after write starts with {=[u8]}", read_buf[0..4]); + if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { + defmt::panic!("unexpected"); + } +} + +fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { + info!(">>>> [erase_write_sector]"); + let mut buf = [0u8; ERASE_SIZE]; + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + + info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); + info!("Contents start with {=[u8]}", buf[0..4]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + info!("Contents after erase starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xFF) { + defmt::panic!("unexpected"); + } + + for b in buf.iter_mut() { + *b = 0xDA; + } + + defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); + + defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + info!("Contents after write starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xDA) { + defmt::panic!("unexpected"); + } +} diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index d6770d6e9..48c88e85b 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -22,6 +22,7 @@ embedded-hal-async = { version = "=0.1.0-alpha.2" } 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.0", features = ["async"] } +embedded-storage = { version = "0.3" } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs new file mode 100644 index 000000000..4cbd78fe9 --- /dev/null +++ b/tests/rp/src/bin/flash.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use {defmt_rtt as _, panic_probe as _}; + +const ADDR_OFFSET: u32 = 0x4000; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); + + let mut buf = [0u8; ERASE_SIZE]; + defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + + info!("Addr of flash block is {:x}", ADDR_OFFSET + FLASH_BASE as u32); + info!("Contents start with {=[u8]}", buf[0..4]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + info!("Contents after erase starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xFF) { + defmt::panic!("unexpected"); + } + + for b in buf.iter_mut() { + *b = 0xDA; + } + + defmt::unwrap!(flash.write(ADDR_OFFSET, &mut buf)); + + defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + info!("Contents after write starts with {=[u8]}", buf[0..4]); + if buf.iter().any(|x| *x != 0xDA) { + defmt::panic!("unexpected"); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 1669e395654430dff6dffda0cef9522f9ccfd213 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Oct 2022 14:47:32 +0200 Subject: [PATCH 0280/1575] Buffer data to be written to flash in ram if it does not already reside in ram --- embassy-rp/src/flash.rs | 34 +++++++++++++++++++++++++++------- examples/rp/src/bin/flash.rs | 1 - 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 62f68bd3c..0f86684e2 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -55,6 +55,8 @@ pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + // FIXME: WHY is this needed?! + cortex_m::asm::delay(50_000); Self(PhantomData) } @@ -163,15 +165,24 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S // Write aligned slice of length in multiples of 256 bytes // If the remaining bytes to be written is more than a full page. if remaining_len >= PAGE_SIZE { - let aligned_data = &bytes[start_padding..end_padding]; - - let aligned_offset = if start_padding > 0 { + let mut aligned_offset = if start_padding > 0 { offset as usize + padded_offset } else { offset as usize }; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + if bytes.as_ptr() as usize >= 0x2000_0000 { + let aligned_data = &bytes[start_padding..end_padding]; + + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + } else { + for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) { + let mut ram_buf = [0xFF_u8; PAGE_SIZE]; + ram_buf.copy_from_slice(chunk); + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) } + aligned_offset += PAGE_SIZE; + } + } } // Pad in the end @@ -273,11 +284,14 @@ mod ram_helpers { pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); flash_function_pointers_with_boot2(true, false, &boot2) } else { flash_function_pointers(true, false) }; + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + write_flash_inner(addr, len, None, &ptrs as *const FlashFunctionPointers); } @@ -300,11 +314,14 @@ mod ram_helpers { pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); flash_function_pointers_with_boot2(true, true, &boot2) } else { flash_function_pointers(true, true) }; + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + write_flash_inner( addr, data.len() as u32, @@ -332,11 +349,14 @@ mod ram_helpers { pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); flash_function_pointers_with_boot2(false, true, &boot2) } else { flash_function_pointers(false, true) }; + + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + write_flash_inner( addr, data.len() as u32, diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 17549e4be..d4dfca759 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -18,7 +18,6 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); - erase_write_sector(&mut flash, 0x00); multiwrite_bytes(&mut flash, ERASE_SIZE as u32); From eeb072d9cbc457892c58670ca6fefacf8c80a32b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 16:47:29 +0200 Subject: [PATCH 0281/1575] Update Rust nightly. --- embassy-embedded-hal/Cargo.toml | 2 +- embassy-lora/Cargo.toml | 2 +- embassy-net/Cargo.toml | 2 +- embassy-net/src/tcp.rs | 18 +++++++++--------- embassy-nrf/Cargo.toml | 4 ++-- embassy-nrf/src/buffered_uarte.rs | 16 ++++++++-------- embassy-rp/Cargo.toml | 4 ++-- embassy-rp/src/uart/buffered.rs | 16 ++++++++-------- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/sdmmc/mod.rs | 12 ++++++------ embassy-stm32/src/usart/buffered.rs | 16 ++++++++-------- embassy-sync/Cargo.toml | 2 +- embassy-sync/src/pipe.rs | 18 +++++++++--------- embassy-time/Cargo.toml | 2 +- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 4 ++-- examples/std/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 4 ++-- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- rust-toolchain.toml | 2 +- tests/rp/Cargo.toml | 4 ++-- tests/stm32/Cargo.toml | 2 +- 26 files changed, 74 insertions(+), 74 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index d0be6d195..85ee856a6 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.2", optional = true } +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.3.0", optional = true } nb = "1.0.0" diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index ea2c3fe67..dc2004172 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.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } 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-net/Cargo.toml b/embassy-net/Cargo.toml index 967ef26a7..76217075a 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.0", optional = true } +embedded-io = { version = "0.3.1", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index f8fff3e2d..f3bd2361c 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -292,7 +292,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -302,7 +302,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -310,7 +310,7 @@ mod embedded_io_impls { self.io.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -324,7 +324,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Read for TcpReader<'d> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -338,7 +338,7 @@ mod embedded_io_impls { } impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -346,7 +346,7 @@ mod embedded_io_impls { self.io.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -445,7 +445,7 @@ 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> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -457,7 +457,7 @@ pub mod client { 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> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -465,7 +465,7 @@ pub mod client { self.socket.write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index dca5fd1bc..67b6bec40 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.2", optional = true} -embedded-io = { version = "0.3.0", features = ["async"], optional = true } +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} +embedded-io = { version = "0.3.1", 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 6e85a159f..9c8fe65f4 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -341,7 +341,7 @@ 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> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -351,7 +351,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -361,7 +361,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read f } impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -375,7 +375,7 @@ 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> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -389,7 +389,7 @@ 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> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -397,7 +397,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff self.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -407,7 +407,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff } impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -415,7 +415,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write self.inner.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index c56858415..d2196be76 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -53,12 +53,12 @@ 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.0", features = ["async"], optional = true } +embedded-io = { version = "0.3.1", features = ["async"], optional = true } 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.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 87e16f0eb..4f0a55532 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -355,7 +355,7 @@ 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> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -376,7 +376,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -397,7 +397,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -419,7 +419,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -441,7 +441,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -455,7 +455,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { }) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -465,7 +465,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -479,7 +479,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> }) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9194ae788..0b88e89c9 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.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", 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.0", features = ["async"], optional = true } +embedded-io = { version = "0.3.1", features = ["async"], optional = true } [build-dependencies] proc-macro2 = "1.0.36" diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a8bc6385f..c91f3c8bf 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1534,14 +1534,14 @@ mod sdmmc_rs { impl<'d, T: Instance, P: Pins> BlockDevice for Sdmmc<'d, T, P> { type Error = Error; - type ReadFuture<'a> + + type ReadFuture<'a> = impl Future> + 'a where - Self: 'a, - = impl Future> + 'a; - type WriteFuture<'a> + Self: 'a; + + type WriteFuture<'a> = impl Future> + 'a where - Self: 'a, - = impl Future> + 'a; + Self: 'a; fn read<'a>( &'a mut self, diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 2a711bc06..c30bbc20a 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -283,7 +283,7 @@ 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> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -293,7 +293,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -303,7 +303,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, } impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { - type FillBufFuture<'a> = impl Future> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -317,7 +317,7 @@ 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> + type FillBufFuture<'a> = impl Future> + 'a where Self: 'a; @@ -331,7 +331,7 @@ 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> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -339,7 +339,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { self.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -349,7 +349,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { } impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -357,7 +357,7 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, self.inner.inner_write(buf) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 584d5ba9f..cd6ff07db 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -32,7 +32,7 @@ atomic-polyfill = "1.0.1" critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" -embedded-io = "0.3.0" +embedded-io = "0.3.1" [dev-dependencies] futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 7d64b648e..cd577f34f 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -361,7 +361,7 @@ mod io_impls { } impl embedded_io::asynch::Read for Pipe { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -371,7 +371,7 @@ mod io_impls { } impl embedded_io::asynch::Write for Pipe { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -379,7 +379,7 @@ mod io_impls { Pipe::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -393,7 +393,7 @@ mod io_impls { } impl embedded_io::asynch::Read for &Pipe { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -403,7 +403,7 @@ mod io_impls { } impl embedded_io::asynch::Write for &Pipe { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -411,7 +411,7 @@ mod io_impls { Pipe::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; @@ -425,7 +425,7 @@ mod io_impls { } impl embedded_io::asynch::Read for Reader<'_, M, N> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -439,7 +439,7 @@ mod io_impls { } impl embedded_io::asynch::Write for Writer<'_, M, N> { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; @@ -447,7 +447,7 @@ mod io_impls { Writer::write(self, buf).map(Ok) } - type FlushFuture<'a> = impl Future> + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c51a71d01..0fd4645a6 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -107,7 +107,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.2", optional = true} +embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} futures-util = { version = "0.3.17", default-features = false } embassy-macros = { version = "0.1.0", path = "../embassy-macros"} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 6949042e2..c633f82f5 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -17,7 +17,7 @@ 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.0" +embedded-io = "0.3.1" 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 } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 747dde515..ec9896b77 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -28,8 +28,8 @@ 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.1" } -embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +embedded-hal-async = { version = "0.1.0-alpha.3" } +embedded-io = { version = "0.3.1", features = ["async", "defmt"] } static_cell = "1.0.0" [profile.release] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index b9bd1e718..790258382 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.0", features = ["async", "std", "futures"] } +embedded-io = { version = "0.3.1", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } async-io = "1.6.0" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 6d4f09fba..b05457eaa 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = "0.3" 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.0" +embedded-io = "0.3.1" 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 dad92c0fc..b14afd2fe 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -10,7 +10,7 @@ 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.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 1a05b9ecb..0dccff6e8 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,7 +10,7 @@ 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.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } defmt = "0.3" defmt-rtt = "0.3" @@ -19,7 +19,7 @@ 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.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } embedded-nal-async = "0.2.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7e1120f48..8b00773be 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -22,7 +22,7 @@ defmt = "0.3" defmt-rtt = "0.3" embedded-storage = "0.3.0" -embedded-io = "0.3.0" +embedded-io = "0.3.1" 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 657605ebe..83d456b26 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -20,7 +20,7 @@ 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.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } 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 63eac3ed2..848723f8b 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -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.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } static_cell = "1.0" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1ec19e58b..c05aac3fc 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-09-22" +channel = "nightly-2022-10-25" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index d6770d6e9..e1a6dce41 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -18,10 +18,10 @@ 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.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } 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.0", features = ["async"] } +embedded-io = { version = "0.3.1", features = ["async"] } [profile.dev] debug = 2 diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bebbf557e..602c1fb57 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -26,7 +26,7 @@ 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.2" } +embedded-hal-async = { version = "=0.1.0-alpha.3" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] From ac6995f9e656a724d92590e722ac0c25f417893b Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 26 Oct 2022 17:48:22 +0300 Subject: [PATCH 0282/1575] Fix a bug identified during code review --- embassy-time/src/queue_generic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 83f734848..6769d6a58 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use core::cmp::Ordering; +use core::cmp::{min, Ordering}; use core::task::Waker; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -74,7 +74,7 @@ impl InnerQueue { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { - timer.at = at; + timer.at = min(timer.at, at); timer.finish(); }) .unwrap_or_else(|| { From 560eecdb737642e6aba132408039195c7d6b6dd8 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 26 Oct 2022 18:05:27 +0300 Subject: [PATCH 0283/1575] Remove the _embassy_time_schedule_wake magic --- embassy-executor/src/raw/mod.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 5bcb1e6e7..181dabe8e 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -444,14 +444,21 @@ pub unsafe fn wake_task(task: NonNull) { } #[cfg(feature = "integrated-timers")] -#[no_mangle] -unsafe fn _embassy_time_schedule_wake(at: Instant, waker: &core::task::Waker) { - let task = waker::task_from_waker(waker); - let task = task.as_ref(); - let expires_at = task.expires_at.get(); - task.expires_at.set(expires_at.min(at)); +struct TimerQueue; + +#[cfg(feature = "integrated-timers")] +impl embassy_time::queue::TimerQueue for TimerQueue { + fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { + let task = waker::task_from_waker(waker); + let task = unsafe { task.as_ref() }; + let expires_at = task.expires_at.get(); + task.expires_at.set(expires_at.min(at)); + } } +#[cfg(feature = "integrated-timers")] +embassy_time::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); + #[cfg(feature = "rtos-trace")] impl rtos_trace::RtosTraceOSCallbacks for Executor { fn task_list() { From 9cac649fcf5a633a89aa1b6e550d641270d14956 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Wed, 26 Oct 2022 18:58:22 +0200 Subject: [PATCH 0284/1575] stm32: Add support for read_until_idle on UART --- embassy-stm32/src/usart/buffered.rs | 34 +- embassy-stm32/src/usart/mod.rs | 402 +++++++++++++++++++-- examples/stm32f3/src/bin/usart_dma.rs | 4 +- examples/stm32f4/src/bin/usart.rs | 4 +- examples/stm32f4/src/bin/usart_buffered.rs | 15 +- examples/stm32f4/src/bin/usart_dma.rs | 4 +- examples/stm32f7/src/bin/usart_dma.rs | 4 +- examples/stm32h7/src/bin/usart.rs | 4 +- examples/stm32h7/src/bin/usart_dma.rs | 4 +- examples/stm32h7/src/bin/usart_split.rs | 4 +- examples/stm32l0/src/bin/usart_dma.rs | 4 +- examples/stm32l0/src/bin/usart_irq.rs | 12 +- examples/stm32l4/src/bin/usart.rs | 4 +- examples/stm32l4/src/bin/usart_dma.rs | 4 +- tests/stm32/src/bin/usart.rs | 17 +- tests/stm32/src/bin/usart_dma.rs | 48 ++- 16 files changed, 500 insertions(+), 68 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index c30bbc20a..3be0677bd 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -46,16 +46,44 @@ impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( state: &'d mut State<'d, T>, - _uart: Uart<'d, T, NoDma, NoDma>, + _peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], + config: Config, ) -> BufferedUart<'d, T> { - into_ref!(irq); + into_ref!(_peri, rx, tx, irq); + + T::enable(); + T::reset(); let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + unsafe { - r.cr1().modify(|w| { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + r.cr2().write(|_w| {}); + r.cr3().write(|_w| {}); + r.cr1().write(|w| { + w.set_ue(true); + w.set_te(true); + w.set_re(true); + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); w.set_rxneie(true); w.set_idleie(true); }); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 3f5b99523..dee466b21 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1,7 +1,11 @@ #![macro_use] +use core::future::poll_fn; use core::marker::PhantomData; +use core::task::Poll; +use atomic_polyfill::{compiler_fence, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::NoDma; @@ -10,6 +14,7 @@ use crate::gpio::sealed::AFType; use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; #[cfg(not(any(lpuart_v1, lpuart_v2)))] use crate::pac::usart::{regs, vals, Usart as Regs}; +use crate::time::Hertz; use crate::{peripherals, Peripheral}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -44,6 +49,10 @@ pub struct Config { pub data_bits: DataBits, pub stop_bits: StopBits, pub parity: Parity, + /// if true, on read-like method, if there is a latent error pending, + /// read will abort, the error reported and cleared + /// if false, the error is ignored and cleared + pub detect_previous_overrun: bool, } impl Default for Config { @@ -53,6 +62,8 @@ impl Default for Config { data_bits: DataBits::DataBits8, stop_bits: StopBits::STOP1, parity: Parity::ParityNone, + // historical behavior + detect_previous_overrun: false, } } } @@ -70,10 +81,11 @@ pub enum Error { Overrun, /// Parity check error Parity, + /// Buffer too large for DMA + BufferTooLong, } pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { - phantom: PhantomData<&'d mut T>, tx: UartTx<'d, T, TxDma>, rx: UartRx<'d, T, RxDma>, } @@ -84,8 +96,9 @@ pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> { } pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { - phantom: PhantomData<&'d mut T>, + _peri: PeripheralRef<'d, T>, rx_dma: PeripheralRef<'d, RxDma>, + detect_previous_overrun: bool, } impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { @@ -135,10 +148,112 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { - fn new(rx_dma: PeripheralRef<'d, RxDma>) -> Self { + /// usefull if you only want Uart Rx. It saves 1 pin and consumes a little less power + pub fn new( + peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, irq, rx, rx_dma); + + T::enable(); + T::reset(); + + let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + + unsafe { + rx.set_as_af(rx.af_num(), AFType::Input); + + r.cr2().write(|_w| {}); + r.cr3().write(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + }); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable receiver + w.set_re(true); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); + } + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + // create state once! + let _s = T::state(); + Self { + _peri: peri, rx_dma, - phantom: PhantomData, + detect_previous_overrun: config.detect_previous_overrun, + } + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; + + let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); + + if has_errors { + // clear all interrupts and DMA Rx Request + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + compiler_fence(Ordering::SeqCst); + + s.rx_waker.wake(); + } else if cr1.idleie() && sr.idle() { + // IDLE detected: no more data will come + unsafe { + r.cr1().modify(|w| { + // disable idle line detection + w.set_idleie(false); + }); + + r.cr3().modify(|w| { + // disable DMA Rx Request + w.set_dmar(false); + }); + } + compiler_fence(Ordering::SeqCst); + + s.rx_waker.wake(); } } @@ -146,17 +261,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { where RxDma: crate::usart::RxDma, { - let ch = &mut self.rx_dma; - let request = ch.request(); - unsafe { - T::regs().cr3().modify(|reg| { - reg.set_dmar(true); - }); - } - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); - transfer.await; + self.inner_read(buffer, false).await?; + Ok(()) } @@ -211,13 +317,202 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } Ok(()) } + + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result + where + RxDma: crate::usart::RxDma, + { + self.inner_read(buffer, true).await + } + + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result + where + RxDma: crate::usart::RxDma, + { + if buffer.is_empty() { + return Ok(0); + } else if buffer.len() > 0xFFFF { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + + let buffer_len = buffer.len(); + + let ch = &mut self.rx_dma; + let request = ch.request(); + + // SAFETY: The only way we might have a problem is using split rx and tx + // here we only modify or read Rx related flags, interrupts and DMA channel + unsafe { + // Start USART DMA + // will not do anything yet because DMAR is not yet set + ch.start_read(request, rdr(r), buffer, Default::default()); + + // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer + if !self.detect_previous_overrun { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + } + + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // enable parity interrupt if not ParityNone + w.set_peie(w.pce()); + }); + + r.cr3().modify(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + // enable DMA Rx Request + w.set_dmar(true); + }); + + compiler_fence(Ordering::SeqCst); + + // In case of errors already pending when reception started, interrupts may have already been raised + // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts + // have been disabled in interrupt handler and DMA Rx Request has been disabled. + + let cr3 = r.cr3().read(); + + if !cr3.dmar() { + // something went wrong + // because the only way to get this flag cleared is to have an interrupt + + // abort DMA transfer + ch.request_stop(); + while ch.is_running() {} + + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + if sr.pe() { + return Err(Error::Parity); + } + if sr.fe() { + return Err(Error::Framing); + } + if sr.ne() { + return Err(Error::Noise); + } + if sr.ore() { + return Err(Error::Overrun); + } + + unreachable!(); + } + + // clear idle flag + if enable_idle_line_detection { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); + } + } + + compiler_fence(Ordering::SeqCst); + + let res = poll_fn(move |cx| { + let s = T::state(); + + ch.set_waker(cx.waker()); + s.rx_waker.register(cx.waker()); + + // SAFETY: read only and we only use Rx related flags + let sr = unsafe { sr(r).read() }; + + // SAFETY: only clears Rx related flags + unsafe { + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + } + + compiler_fence(Ordering::SeqCst); + + let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); + + if has_errors { + // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler + + // stop dma transfer + ch.request_stop(); + while ch.is_running() {} + + if sr.pe() { + return Poll::Ready(Err(Error::Parity)); + } + if sr.fe() { + return Poll::Ready(Err(Error::Framing)); + } + if sr.ne() { + return Poll::Ready(Err(Error::Noise)); + } + if sr.ore() { + return Poll::Ready(Err(Error::Overrun)); + } + } + + if sr.idle() { + // Idle line + + // stop dma transfer + ch.request_stop(); + while ch.is_running() {} + + let n = buffer_len - (ch.remaining_transfers() as usize); + + return Poll::Ready(Ok(n)); + } else if !ch.is_running() { + // DMA complete + return Poll::Ready(Ok(buffer_len)); + } + + Poll::Pending + }) + .await; + + // clear all interrupts and DMA Rx Request + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + res + } } impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { pub fn new( - _inner: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, @@ -225,13 +520,14 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable(); T::reset(); - Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } pub fn new_with_rtscts( - _inner: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, @@ -251,32 +547,29 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { w.set_ctse(true); }); } - Self::new_inner(_inner, rx, tx, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } fn new_inner( - _inner: impl Peripheral

+ 'd, + peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(_inner, rx, tx, tx_dma, rx_dma); - - let pclk_freq = T::frequency(); - - // TODO: better calculation, including error checking and OVER8 if possible. - let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * T::MULTIPLIER; + into_ref!(peri, rx, tx, irq, tx_dma, rx_dma); let r = T::regs(); + configure(r, &config, T::frequency(), T::MULTIPLIER); + unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); r.cr2().write(|_w| {}); - r.brr().write_value(regs::Brr(div)); r.cr1().write(|w| { w.set_ue(true); w.set_te(true); @@ -295,10 +588,20 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }); } + irq.set_handler(UartRx::::on_interrupt); + irq.unpend(); + irq.enable(); + + // create state once! + let _s = T::state(); + Self { tx: UartTx::new(tx_dma), - rx: UartRx::new(rx_dma), - phantom: PhantomData {}, + rx: UartRx { + _peri: peri, + rx_dma, + detect_previous_overrun: config.detect_previous_overrun, + }, } } @@ -332,6 +635,13 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.rx.blocking_read(buffer) } + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result + where + RxDma: crate::usart::RxDma, + { + self.rx.read_until_idle(buffer).await + } + /// Split the Uart into a transmitter and receiver, which is /// particuarly useful when having two tasks correlating to /// transmitting and receiving. @@ -340,6 +650,15 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } +fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32) { + // TODO: better calculation, including error checking and OVER8 if possible. + let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier; + + unsafe { + r.brr().write_value(regs::Brr(div)); + } +} + mod eh02 { use super::*; @@ -389,6 +708,7 @@ mod eh1 { Self::Noise => embedded_hal_1::serial::ErrorKind::Noise, Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, + Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, } } } @@ -573,13 +893,30 @@ unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { } pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + use super::*; + pub struct State { + pub rx_waker: AtomicWaker, + pub tx_waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + } + } + } + pub trait BasicInstance: crate::rcc::RccPeripheral { const MULTIPLIER: u32; type Interrupt: crate::interrupt::Interrupt; fn regs() -> Regs; + fn state() -> &'static State; } pub trait FullInstance: BasicInstance { @@ -587,7 +924,7 @@ pub(crate) mod sealed { } } -pub trait BasicInstance: sealed::BasicInstance {} +pub trait BasicInstance: Peripheral

+ sealed::BasicInstance + 'static + Send {} pub trait FullInstance: sealed::FullInstance {} @@ -609,6 +946,11 @@ macro_rules! impl_lpuart { fn regs() -> Regs { Regs(crate::pac::$inst.0) } + + fn state() -> &'static crate::usart::sealed::State { + static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); + &STATE + } } impl BasicInstance for peripherals::$inst {} diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 3bc5a287f..47121acf1 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, p.DMA1_CH4, NoDma, config); + let irq = interrupt::take!(USART1); + let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, irq, p.DMA1_CH4, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 90ad882b8..8f41bb6c4 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs @@ -5,6 +5,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); + let irq = interrupt::take!(USART3); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index 7bcecbd26..dd171fe13 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -4,9 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; +use embassy_stm32::usart::{BufferedUart, Config, State}; use embedded_io::asynch::BufRead; use {defmt_rtt as _, panic_probe as _}; @@ -16,13 +15,21 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let usart = Uart::new(p.USART3, p.PD9, p.PD8, NoDma, NoDma, config); let mut state = State::new(); let irq = interrupt::take!(USART3); let mut tx_buf = [0u8; 32]; let mut rx_buf = [0u8; 32]; - let mut buf_usart = BufferedUart::new(&mut state, usart, irq, &mut tx_buf, &mut rx_buf); + let mut buf_usart = BufferedUart::new( + &mut state, + p.USART3, + p.PD9, + p.PD8, + irq, + &mut tx_buf, + &mut rx_buf, + config, + ); loop { let buf = buf_usart.fill_buf().await.unwrap(); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index bb41b8b4f..78baeaa0d 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, p.DMA1_CH3, NoDma, config); + let irq = interrupt::take!(USART3); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index 07270479c..4827c52ae 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, p.DMA1_CH1, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, irq, p.DMA1_CH1, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index 87c2b1253..405f18ec7 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -6,6 +6,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +16,8 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, NoDma, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index 3adffcbeb..6e3491e55 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -8,6 +8,7 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use static_cell::StaticCell; @@ -18,7 +19,8 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, NoDma, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index df2b600f8..f97176ecb 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -5,6 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::peripherals::{DMA1_CH1, UART7}; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -31,7 +32,8 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, p.DMA1_CH0, p.DMA1_CH1, config); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, p.DMA1_CH1, config); unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); let (mut tx, rx) = usart.split(); diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index 66657d0f0..c307f857a 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs @@ -4,13 +4,15 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, p.DMA1_CH2, p.DMA1_CH3, Config::default()); + let irq = interrupt::take!(USART1); + let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH2, p.DMA1_CH3, Config::default()); usart.write(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 0e2237388..8e84cd092 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -4,9 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State, Uart}; +use embassy_stm32::usart::{BufferedUart, Config, State}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -21,15 +20,18 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 9600; - let usart = Uart::new(p.USART2, p.PA3, p.PA2, NoDma, NoDma, config); let mut state = State::new(); + let irq = interrupt::take!(USART2); let mut usart = unsafe { BufferedUart::new( &mut state, - usart, - interrupt::take!(USART2), + p.USART2, + p.PA3, + p.PA2, + irq, &mut TX_BUFFER, &mut RX_BUFFER, + config, ) }; diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 4a4b46c53..7d874d9d7 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +15,8 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, NoDma, NoDma, config); + let irq = interrupt::take!(UART4); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 728906897..452bede30 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -7,6 +7,7 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -17,7 +18,8 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, p.DMA1_CH3, NoDma, config); + let irq = interrupt::take!(UART4); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 7673bfe6d..af55867f2 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -7,6 +7,7 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use example_common::*; @@ -18,22 +19,22 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart) = (p.PA9, p.PA10, p.USART1); + let (tx, rx, usart, irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart) = (p.PC4, p.PC5, p.USART1); + let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart) = (p.PC4, p.PC5, p.USART1); + let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart) = (p.PG14, p.PG9, p.USART6); + let (tx, rx, usart, irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart) = (p.PA2, p.PA3, p.LPUART1); + let (tx, rx, usart, irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart) = (p.PB6, p.PB7, p.USART1); + let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart) = (p.PD8, p.PD9, p.USART3); + let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, NoDma, NoDma, config); + let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index e0389446f..d12605a9a 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -6,6 +6,7 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use example_common::*; @@ -17,22 +18,53 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, p.DMA1_CH4, p.DMA1_CH5); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA9, + p.PA10, + p.USART1, + interrupt::take!(USART1), + p.DMA1_CH4, + p.DMA1_CH5, + ); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PG14, p.PG9, p.USART6, p.DMA2_CH6, p.DMA2_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PG14, + p.PG9, + p.USART6, + interrupt::take!(USART6), + p.DMA2_CH6, + p.DMA2_CH1, + ); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PA2, p.PA3, p.LPUART1, p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA2, + p.PA3, + p.LPUART1, + interrupt::take!(LPUART1), + p.DMA1_CH1, + p.DMA1_CH2, + ); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH0, p.DMA1_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH0, p.DMA1_CH1); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, p.GPDMA1_CH0, p.GPDMA1_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PD8, + p.PD9, + p.USART3, + interrupt::take!(USART3), + p.GPDMA1_CH0, + p.GPDMA1_CH1, + ); let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, tx_dma, rx_dma, config); + let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. From 4976cbbe6040d5e147e7c42bd29b72d6223b05b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 20:02:58 +0200 Subject: [PATCH 0285/1575] time/generic-queue: ensure queue goes in .bss instead of .data --- embassy-time/src/queue_generic.rs | 38 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 6769d6a58..8a355b327 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -57,19 +57,11 @@ impl Ord for Timer { struct InnerQueue { queue: SortedLinkedList, - alarm: Option, + alarm: AlarmHandle, alarm_at: Instant, } impl InnerQueue { - const fn new() -> Self { - Self { - queue: SortedLinkedList::new_u8(), - alarm: None, - alarm_at: Instant::MAX, - } - } - fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue .find_mut(|timer| timer.waker.will_wake(waker)) @@ -121,7 +113,7 @@ impl InnerQueue { if self.alarm_at != new_at { self.alarm_at = new_at; - return set_alarm(self.alarm.unwrap(), self.alarm_at.as_ticks()); + return set_alarm(self.alarm, self.alarm_at.as_ticks()); } } else { self.alarm_at = Instant::MAX; @@ -138,13 +130,13 @@ impl InnerQueue { } struct Queue { - inner: Mutex>, + inner: Mutex>>, } impl Queue { const fn new() -> Self { Self { - inner: Mutex::new(RefCell::new(InnerQueue::new())), + inner: Mutex::new(RefCell::new(None)), } } @@ -152,19 +144,25 @@ impl Queue { self.inner.lock(|inner| { let mut inner = inner.borrow_mut(); - if inner.alarm.is_none() { - let handle = unsafe { allocate_alarm() }.unwrap(); - inner.alarm = Some(handle); + if inner.is_none() {} - set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); - } - - inner.schedule_wake(at, waker) + inner + .get_or_insert_with(|| { + let handle = unsafe { allocate_alarm() }.unwrap(); + set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); + InnerQueue { + queue: SortedLinkedList::new_u8(), + alarm: handle, + alarm_at: Instant::MAX, + } + }) + .schedule_wake(at, waker) }); } fn handle_alarm(&self) { - self.inner.lock(|inner| inner.borrow_mut().handle_alarm()); + self.inner + .lock(|inner| inner.borrow_mut().as_mut().unwrap().handle_alarm()); } fn handle_alarm_callback(ctx: *mut ()) { From f9da6271cea7035b2c9f27cfe479aa81889168d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 21:00:50 +0200 Subject: [PATCH 0286/1575] time/generic_queue: use Vec instead of SortedLinkedList --- embassy-time/src/queue_generic.rs | 46 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 8a355b327..20ae7e6cc 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -4,7 +4,7 @@ use core::task::Waker; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; -use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; +use heapless::Vec; use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; use crate::queue::TimerQueue; @@ -56,18 +56,17 @@ impl Ord for Timer { } struct InnerQueue { - queue: SortedLinkedList, + queue: Vec, alarm: AlarmHandle, - alarm_at: Instant, } impl InnerQueue { fn schedule_wake(&mut self, at: Instant, waker: &Waker) { self.queue - .find_mut(|timer| timer.waker.will_wake(waker)) + .iter_mut() + .find(|timer| timer.waker.will_wake(waker)) .map(|mut timer| { timer.at = min(timer.at, at); - timer.finish(); }) .unwrap_or_else(|| { let mut timer = Timer { @@ -96,35 +95,35 @@ impl InnerQueue { loop { let now = Instant::now(); - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); + let mut next_alarm = Instant::MAX; + + let mut i = 0; + while i < self.queue.len() { + let timer = &self.queue[i]; + if timer.at <= now { + let timer = self.queue.swap_remove(i); + timer.waker.wake(); + } else { + next_alarm = min(next_alarm, timer.at); + i += 1; + } } - if self.update_alarm() { + if self.update_alarm(next_alarm) { break; } } } - fn update_alarm(&mut self) -> bool { - if let Some(timer) = self.queue.peek() { - let new_at = timer.at; - - if self.alarm_at != new_at { - self.alarm_at = new_at; - - return set_alarm(self.alarm, self.alarm_at.as_ticks()); - } + fn update_alarm(&mut self, next_alarm: Instant) -> bool { + if next_alarm == Instant::MAX { + true } else { - self.alarm_at = Instant::MAX; + set_alarm(self.alarm, next_alarm.as_ticks()) } - - true } fn handle_alarm(&mut self) { - self.alarm_at = Instant::MAX; - self.dispatch(); } } @@ -151,9 +150,8 @@ impl Queue { let handle = unsafe { allocate_alarm() }.unwrap(); set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); InnerQueue { - queue: SortedLinkedList::new_u8(), + queue: Vec::new(), alarm: handle, - alarm_at: Instant::MAX, } }) .schedule_wake(at, waker) From d2246ae693d15187f3af9c263bc02809ba096b80 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 22:11:08 +0200 Subject: [PATCH 0287/1575] Release embassy-sync, embassy-time v0.1.0 --- embassy-sync/Cargo.toml | 3 +++ embassy-time/Cargo.toml | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index cd6ff07db..b7fe1643c 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -19,6 +19,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-sync/ features = ["nightly"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["nightly"] + [features] nightly = ["embedded-io/async"] std = [] diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 9ed4d57ea..e3d2cbcdd 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -11,6 +11,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/ features = ["nightly", "defmt", "unstable-traits", "std"] target = "x86_64-unknown-linux-gnu" +[package.metadata.docs.rs] +features = ["nightly", "defmt", "unstable-traits", "std"] + [features] std = ["tick-hz-1_000_000"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] @@ -126,7 +129,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optiona embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true} futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.1.0", path = "../embassy-macros"} embassy-sync = { version = "0.1", path = "../embassy-sync" } atomic-polyfill = "1.0.1" critical-section = "1.1" From 61560e740dea1b4c7ca036dafd66c834a1ff92e2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Oct 2022 22:18:10 +0200 Subject: [PATCH 0288/1575] time: add missing cargo manifest fields. --- embassy-time/Cargo.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index e3d2cbcdd..9487003cc 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -2,8 +2,16 @@ name = "embassy-time" version = "0.1.0" edition = "2021" +description = "Instant and Duration for embedded no-std systems, with async timer support" +repository = "https://github.com/embassy-rs/embassy" +readme = "README.md" license = "MIT OR Apache-2.0" - +categories = [ + "embedded", + "no-std", + "concurrency", + "asynchronous", +] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/" From a5b1d2237fc327f71a7fb09456ceb448a796efd7 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 00:31:46 -0500 Subject: [PATCH 0289/1575] Refactor: Factor out `SampleTime` --- embassy-stm32/src/adc/f1.rs | 57 +------------- embassy-stm32/src/adc/mod.rs | 5 ++ embassy-stm32/src/adc/sample_time.rs | 114 +++++++++++++++++++++++++++ embassy-stm32/src/adc/v2.rs | 38 +-------- embassy-stm32/src/adc/v3.rs | 112 +------------------------- embassy-stm32/src/adc/v4.rs | 53 +------------ 6 files changed, 123 insertions(+), 256 deletions(-) create mode 100644 embassy-stm32/src/adc/sample_time.rs diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index c5b317ce9..56d1a2f33 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance}; +use crate::adc::{AdcPin, Instance, SampleTime}; use crate::rcc::get_freqs; use crate::time::Hertz; use crate::Peripheral; @@ -29,61 +29,6 @@ impl super::sealed::AdcPin for Temperature { } } -mod sample_time { - /// ADC sample time - /// - /// The default setting is 1.5 ADC clock cycles. - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5 = 0b000, - - /// 7.5 ADC clock cycles - Cycles7_5 = 0b001, - - /// 13.5 ADC clock cycles - Cycles13_5 = 0b010, - - /// 28.5 ADC clock cycles - Cycles28_5 = 0b011, - - /// 41.5 ADC clock cycles - Cycles41_5 = 0b100, - - /// 55.5 ADC clock cycles - Cycles55_5 = 0b101, - - /// 71.5 ADC clock cycles - Cycles71_5 = 0b110, - - /// 239.5 ADC clock cycles - Cycles239_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { - match self { - SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5, - SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5, - SampleTime::Cycles13_5 => crate::pac::adc::vals::SampleTime::CYCLES13_5, - SampleTime::Cycles28_5 => crate::pac::adc::vals::SampleTime::CYCLES28_5, - SampleTime::Cycles41_5 => crate::pac::adc::vals::SampleTime::CYCLES41_5, - SampleTime::Cycles55_5 => crate::pac::adc::vals::SampleTime::CYCLES55_5, - SampleTime::Cycles71_5 => crate::pac::adc::vals::SampleTime::CYCLES71_5, - SampleTime::Cycles239_5 => crate::pac::adc::vals::SampleTime::CYCLES239_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles28_5 - } - } -} - -pub use sample_time::SampleTime; - pub struct Adc<'d, T: Instance> { sample_time: SampleTime, phantom: PhantomData<&'d mut T>, diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 0eb4eba73..6232678f0 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -8,8 +8,13 @@ #[cfg_attr(adc_v1, path = "v1.rs")] mod _version; +#[cfg(not(adc_v1))] +mod sample_time; + #[allow(unused)] pub use _version::*; +#[cfg(not(adc_v1))] +pub use sample_time::SampleTime; use crate::peripherals; diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs new file mode 100644 index 000000000..f0eb104f0 --- /dev/null +++ b/embassy-stm32/src/adc/sample_time.rs @@ -0,0 +1,114 @@ +macro_rules! impl_sample_time { + ($default_doc:expr, $default:ident, $pac:ty, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { + #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + $( + #[doc = concat!($doc, " ADC clock cycles.")] + $variant, + )* + } + + impl SampleTime { + pub(crate) fn sample_time(&self) -> $pac { + match self { + $(Self::$variant => <$pac>::$pac_variant),* + } + } + } + + impl Default for SampleTime { + fn default() -> Self { + Self::$default + } + } + }; +} + +// TODO: Fix default +#[cfg(adc_f1)] +impl_sample_time!( + "1.5", + Cycles28_5, + crate::pac::adc::vals::SampleTime, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("7.5", Cycles7_5, CYCLES7_5), + ("13.5", Cycles13_5, CYCLES13_5), + ("28.5", Cycles28_5, CYCLES28_5), + ("41.5", Cycles41_5, CYCLES41_5), + ("55.5", Cycles55_5, CYCLES55_5), + ("71.5", Cycles71_5, CYCLES71_5), + ("239.5", Cycles239_5, CYCLES239_5) + ) +); + +// TODO: Fix 85 vs 84 +#[cfg(adc_v2)] +impl_sample_time!( + "3", + Cycles3, + crate::pac::adc::vals::Smp, + ( + ("3", Cycles3, CYCLES3), + ("15", Cycles15, CYCLES15), + ("28", Cycles28, CYCLES28), + ("56", Cycles56, CYCLES56), + ("85", Cycles85, CYCLES84), + ("112", Cycles112, CYCLES112), + ("144", Cycles144, CYCLES144), + ("480", Cycles480, CYCLES480) + ) +); + +#[cfg(adc_v3)] +impl_sample_time!( + "2.5", + Cycles2_5, + crate::pac::adc::vals::SampleTime, + ( + ("2.5", Cycles2_5, CYCLES2_5), + ("6.5", Cycles6_5, CYCLES6_5), + ("12.5", Cycles12_5, CYCLES12_5), + ("24.5", Cycles24_5, CYCLES24_5), + ("47.5", Cycles47_5, CYCLES47_5), + ("92.5", Cycles92_5, CYCLES92_5), + ("247.5", Cycles247_5, CYCLES247_5), + ("640.5", Cycles640_5, CYCLES640_5) + ) +); + +#[cfg(adc_g0)] +impl_sample_time!( + "1.5", + Cycles1_5, + crate::pac::adc::vals::SampleTime, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("3.5", Cycles3_5, CYCLES3_5), + ("7.5", Cycles7_5, CYCLES7_5), + ("12.5", Cycles12_5, CYCLES12_5), + ("19.5", Cycles19_5, CYCLES19_5), + ("39.5", Cycles39_5, CYCLES39_5), + ("79.5", Cycles79_5, CYCLES79_5), + ("160.5", Cycles160_5, CYCLES160_5) + ) +); + +// TODO: Fix default doc +#[cfg(adc_v4)] +impl_sample_time!( + "2.5", + Cycles1_5, + crate::pac::adc::vals::Smp, + ( + ("1.5", Cycles1_5, CYCLES1_5), + ("2.5", Cycles2_5, CYCLES2_5), + ("8.5", Cycles8_5, CYCLES8_5), + ("16.5", Cycles16_5, CYCLES16_5), + ("32.5", Cycles32_5, CYCLES32_5), + ("64.5", Cycles64_5, CYCLES64_5), + ("387.5", Cycles387_5, CYCLES387_5), + ("810.5", Cycles810_5, CYCLES810_5) + ) +); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 53419c7f2..00d9b3077 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -4,7 +4,7 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; -use crate::adc::{AdcPin, Instance}; +use crate::adc::{AdcPin, Instance, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -94,42 +94,6 @@ impl super::sealed::InternalChannel for Vbat { } } -/// ADC sample time -/// -/// The default setting is 3 ADC clock cycles. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum SampleTime { - Cycles3 = 0b000, - Cycles15 = 0b001, - Cycles28 = 0b010, - Cycles56 = 0b011, - Cycles85 = 0b100, - Cycles112 = 0b101, - Cycles144 = 0b110, - Cycles480 = 0b111, -} - -impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { - match self { - SampleTime::Cycles3 => crate::pac::adc::vals::Smp::CYCLES3, - SampleTime::Cycles15 => crate::pac::adc::vals::Smp::CYCLES15, - SampleTime::Cycles28 => crate::pac::adc::vals::Smp::CYCLES28, - SampleTime::Cycles56 => crate::pac::adc::vals::Smp::CYCLES56, - SampleTime::Cycles85 => crate::pac::adc::vals::Smp::CYCLES84, - SampleTime::Cycles112 => crate::pac::adc::vals::Smp::CYCLES112, - SampleTime::Cycles144 => crate::pac::adc::vals::Smp::CYCLES144, - SampleTime::Cycles480 => crate::pac::adc::vals::Smp::CYCLES480, - } - } -} - -impl Default for SampleTime { - fn default() -> Self { - Self::Cycles3 - } -} - enum Prescaler { Div2, Div4, diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 816feeac7..b638e8ca5 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance}; +use crate::adc::{AdcPin, Instance, SampleTime}; use crate::Peripheral; /// Default VREF voltage used for sample conversion to millivolts. @@ -93,116 +93,6 @@ impl super::sealed::AdcPin for Vbat { } } -#[cfg(not(adc_g0))] -mod sample_time { - /// ADC sample time - /// - /// The default setting is 2.5 ADC clock cycles. - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 2.5 ADC clock cycles - Cycles2_5 = 0b000, - - /// 6.5 ADC clock cycles - Cycles6_5 = 0b001, - - /// 12.5 ADC clock cycles - Cycles12_5 = 0b010, - - /// 24.5 ADC clock cycles - Cycles24_5 = 0b011, - - /// 47.5 ADC clock cycles - Cycles47_5 = 0b100, - - /// 92.5 ADC clock cycles - Cycles92_5 = 0b101, - - /// 247.5 ADC clock cycles - Cycles247_5 = 0b110, - - /// 640.5 ADC clock cycles - Cycles640_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { - match self { - SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5, - SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5, - SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5, - SampleTime::Cycles24_5 => crate::pac::adc::vals::SampleTime::CYCLES24_5, - SampleTime::Cycles47_5 => crate::pac::adc::vals::SampleTime::CYCLES47_5, - SampleTime::Cycles92_5 => crate::pac::adc::vals::SampleTime::CYCLES92_5, - SampleTime::Cycles247_5 => crate::pac::adc::vals::SampleTime::CYCLES247_5, - SampleTime::Cycles640_5 => crate::pac::adc::vals::SampleTime::CYCLES640_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles2_5 - } - } -} - -#[cfg(adc_g0)] -mod sample_time { - /// ADC sample time - /// - /// The default setting is 1.5 ADC clock cycles. - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5 = 0b000, - - /// 3.5 ADC clock cycles - Cycles3_5 = 0b001, - - /// 7.5 ADC clock cycles - Cycles7_5 = 0b010, - - /// 12.5 ADC clock cycles - Cycles12_5 = 0b011, - - /// 19.5 ADC clock cycles - Cycles19_5 = 0b100, - - /// 39.5 ADC clock cycles - Cycles39_5 = 0b101, - - /// 79.5 ADC clock cycles - Cycles79_5 = 0b110, - - /// 160.5 ADC clock cycles - Cycles160_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { - match self { - SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5, - SampleTime::Cycles3_5 => crate::pac::adc::vals::SampleTime::CYCLES3_5, - SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5, - SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5, - SampleTime::Cycles19_5 => crate::pac::adc::vals::SampleTime::CYCLES19_5, - SampleTime::Cycles39_5 => crate::pac::adc::vals::SampleTime::CYCLES39_5, - SampleTime::Cycles79_5 => crate::pac::adc::vals::SampleTime::CYCLES79_5, - SampleTime::Cycles160_5 => crate::pac::adc::vals::SampleTime::CYCLES160_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles1_5 - } - } -} - -pub use sample_time::SampleTime; - pub struct Adc<'d, T: Instance> { sample_time: SampleTime, resolution: Resolution, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 2b8f10533..b9222947b 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance, InternalChannel}; +use super::{AdcPin, Instance, InternalChannel, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -193,57 +193,6 @@ foreach_peripheral!( }; ); -/// ADC sample time -/// -/// The default setting is 2.5 ADC clock cycles. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5, - - /// 2.5 ADC clock cycles - Cycles2_5, - - /// 8.5 ADC clock cycles - Cycles8_5, - - /// 16.5 ADC clock cycles - Cycles16_5, - - /// 32.5 ADC clock cycles - Cycles32_5, - - /// 64.5 ADC clock cycles - Cycles64_5, - - /// 387.5 ADC clock cycles - Cycles387_5, - - /// 810.5 ADC clock cycles - Cycles810_5, -} - -impl SampleTime { - pub(crate) fn sample_time(&self) -> pac::adc::vals::Smp { - match self { - SampleTime::Cycles1_5 => pac::adc::vals::Smp::CYCLES1_5, - SampleTime::Cycles2_5 => pac::adc::vals::Smp::CYCLES2_5, - SampleTime::Cycles8_5 => pac::adc::vals::Smp::CYCLES8_5, - SampleTime::Cycles16_5 => pac::adc::vals::Smp::CYCLES16_5, - SampleTime::Cycles32_5 => pac::adc::vals::Smp::CYCLES32_5, - SampleTime::Cycles64_5 => pac::adc::vals::Smp::CYCLES64_5, - SampleTime::Cycles387_5 => pac::adc::vals::Smp::CYCLES387_5, - SampleTime::Cycles810_5 => pac::adc::vals::Smp::CYCLES810_5, - } - } -} - -impl Default for SampleTime { - fn default() -> Self { - Self::Cycles1_5 - } -} - // NOTE (unused): The prescaler enum closely copies the hardware capabilities, // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. #[allow(unused)] From 51426747863f5d73f5d8c55d39add1b1aeb3356a Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 01:44:48 -0500 Subject: [PATCH 0290/1575] Fix pre-existing `SampleTime` typos --- embassy-stm32/src/adc/sample_time.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index f0eb104f0..1850d80fb 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -25,11 +25,10 @@ macro_rules! impl_sample_time { }; } -// TODO: Fix default #[cfg(adc_f1)] impl_sample_time!( "1.5", - Cycles28_5, + Cycles1_5, crate::pac::adc::vals::SampleTime, ( ("1.5", Cycles1_5, CYCLES1_5), @@ -43,7 +42,6 @@ impl_sample_time!( ) ); -// TODO: Fix 85 vs 84 #[cfg(adc_v2)] impl_sample_time!( "3", @@ -54,7 +52,7 @@ impl_sample_time!( ("15", Cycles15, CYCLES15), ("28", Cycles28, CYCLES28), ("56", Cycles56, CYCLES56), - ("85", Cycles85, CYCLES84), + ("84", Cycles84, CYCLES84), ("112", Cycles112, CYCLES112), ("144", Cycles144, CYCLES144), ("480", Cycles480, CYCLES480) @@ -95,10 +93,9 @@ impl_sample_time!( ) ); -// TODO: Fix default doc #[cfg(adc_v4)] impl_sample_time!( - "2.5", + "1.5", Cycles1_5, crate::pac::adc::vals::Smp, ( From 7b38b95e1036d2a62e1d0abfc41b064839497242 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 02:04:52 -0500 Subject: [PATCH 0291/1575] Refactor: Factor out `Resolution` --- embassy-stm32/src/adc/mod.rs | 5 +++ embassy-stm32/src/adc/resolution.rs | 59 +++++++++++++++++++++++++++++ embassy-stm32/src/adc/v2.rs | 35 +---------------- embassy-stm32/src/adc/v3.rs | 35 +---------------- embassy-stm32/src/adc/v4.rs | 38 +------------------ 5 files changed, 67 insertions(+), 105 deletions(-) create mode 100644 embassy-stm32/src/adc/resolution.rs diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 6232678f0..35d84f976 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -11,8 +11,13 @@ mod _version; #[cfg(not(adc_v1))] mod sample_time; +#[cfg(not(any(adc_f1, adc_v1)))] +mod resolution; + #[allow(unused)] pub use _version::*; +#[cfg(not(any(adc_f1, adc_v1)))] +pub use resolution::Resolution; #[cfg(not(adc_v1))] pub use sample_time::SampleTime; diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs new file mode 100644 index 000000000..354cabfb3 --- /dev/null +++ b/embassy-stm32/src/adc/resolution.rs @@ -0,0 +1,59 @@ +#[cfg(any(adc_v2, adc_v3, adc_g0))] +pub enum Resolution { + TwelveBit, + TenBit, + EightBit, + SixBit, +} + +#[cfg(adc_v4)] +pub enum Resolution { + SixteenBit, + FourteenBit, + TwelveBit, + TenBit, + EightBit, +} + +impl Default for Resolution { + fn default() -> Self { + #[cfg(any(adc_v2, adc_v3, adc_g0))] + { + Self::TwelveBit + } + #[cfg(adc_v4)] + { + Self::SixteenBit + } + } +} + +impl Resolution { + pub(super) fn res(&self) -> crate::pac::adc::vals::Res { + match self { + #[cfg(adc_v4)] + Resolution::SixteenBit => crate::pac::adc::vals::Res::SIXTEENBIT, + #[cfg(adc_v4)] + Resolution::FourteenBit => crate::pac::adc::vals::Res::FOURTEENBITV, + Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, + Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, + Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, + #[cfg(any(adc_v2, adc_v3, adc_g0))] + Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, + } + } + + pub fn to_max_count(&self) -> u32 { + match self { + #[cfg(adc_v4)] + Resolution::SixteenBit => (1 << 16) - 1, + #[cfg(adc_v4)] + Resolution::FourteenBit => (1 << 14) - 1, + Resolution::TwelveBit => (1 << 12) - 1, + Resolution::TenBit => (1 << 10) - 1, + Resolution::EightBit => (1 << 8) - 1, + #[cfg(any(adc_v2, adc_v3, adc_g0))] + Resolution::SixBit => (1 << 6) - 1, + } + } +} diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 00d9b3077..e2678fe4a 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -4,7 +4,7 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; -use crate::adc::{AdcPin, Instance, SampleTime}; +use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -17,39 +17,6 @@ pub const VREF_CALIB_MV: u32 = 3300; /// ADC turn-on time pub const ADC_POWERUP_TIME_US: u32 = 3; -pub enum Resolution { - TwelveBit, - TenBit, - EightBit, - SixBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::TwelveBit - } -} - -impl Resolution { - fn res(&self) -> crate::pac::adc::vals::Res { - match self { - Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, - Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, - Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - Resolution::SixBit => (1 << 6) - 1, - } - } -} - pub struct VrefInt; impl InternalChannel for VrefInt {} impl super::sealed::InternalChannel for VrefInt { diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index b638e8ca5..7bc130e55 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance, SampleTime}; +use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; use crate::Peripheral; /// Default VREF voltage used for sample conversion to millivolts. @@ -24,39 +24,6 @@ fn enable() { }); } -pub enum Resolution { - TwelveBit, - TenBit, - EightBit, - SixBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::TwelveBit - } -} - -impl Resolution { - fn res(&self) -> crate::pac::adc::vals::Res { - match self { - Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, - Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, - Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - Resolution::SixBit => (1 << 6) - 1, - } - } -} - pub struct VrefInt; impl AdcPin for VrefInt {} impl super::sealed::AdcPin for VrefInt { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b9222947b..9dc5318e2 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance, InternalChannel, SampleTime}; +use super::{AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -14,42 +14,6 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -pub enum Resolution { - SixteenBit, - FourteenBit, - TwelveBit, - TenBit, - EightBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::SixteenBit - } -} - -impl Resolution { - fn res(&self) -> pac::adc::vals::Res { - match self { - Resolution::SixteenBit => pac::adc::vals::Res::SIXTEENBIT, - Resolution::FourteenBit => pac::adc::vals::Res::FOURTEENBITV, - Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBITV, - Resolution::TenBit => pac::adc::vals::Res::TENBIT, - Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::SixteenBit => (1 << 16) - 1, - Resolution::FourteenBit => (1 << 14) - 1, - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - } - } -} - // NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs pub struct VrefInt; impl InternalChannel for VrefInt {} From 2cfe2439c980129b2dc8170f35944f3c0505c230 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 02:28:39 -0500 Subject: [PATCH 0292/1575] Refactor: Impl `From` for `SampleTime` and `Resolution` --- embassy-stm32/src/adc/f1.rs | 9 +++------ embassy-stm32/src/adc/mod.rs | 5 ++--- embassy-stm32/src/adc/resolution.rs | 10 +++++++--- embassy-stm32/src/adc/sample_time.rs | 8 ++++---- embassy-stm32/src/adc/v2.rs | 11 ++++------- embassy-stm32/src/adc/v3.rs | 15 ++++++--------- embassy-stm32/src/adc/v4.rs | 11 ++++------- 7 files changed, 30 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 56d1a2f33..8a3b4be56 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -146,14 +146,11 @@ impl<'d, T: Instance> Adc<'d, T> { } unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr2() - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr1() - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 35d84f976..328470ef6 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -8,11 +8,10 @@ #[cfg_attr(adc_v1, path = "v1.rs")] mod _version; -#[cfg(not(adc_v1))] -mod sample_time; - #[cfg(not(any(adc_f1, adc_v1)))] mod resolution; +#[cfg(not(adc_v1))] +mod sample_time; #[allow(unused)] pub use _version::*; diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 354cabfb3..62b52a46c 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,4 +1,5 @@ #[cfg(any(adc_v2, adc_v3, adc_g0))] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { TwelveBit, TenBit, @@ -7,6 +8,7 @@ pub enum Resolution { } #[cfg(adc_v4)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { SixteenBit, FourteenBit, @@ -28,9 +30,9 @@ impl Default for Resolution { } } -impl Resolution { - pub(super) fn res(&self) -> crate::pac::adc::vals::Res { - match self { +impl From for crate::pac::adc::vals::Res { + fn from(res: Resolution) -> crate::pac::adc::vals::Res { + match res { #[cfg(adc_v4)] Resolution::SixteenBit => crate::pac::adc::vals::Res::SIXTEENBIT, #[cfg(adc_v4)] @@ -42,7 +44,9 @@ impl Resolution { Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } +} +impl Resolution { pub fn to_max_count(&self) -> u32 { match self { #[cfg(adc_v4)] diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 1850d80fb..60ba80048 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -9,10 +9,10 @@ macro_rules! impl_sample_time { )* } - impl SampleTime { - pub(crate) fn sample_time(&self) -> $pac { - match self { - $(Self::$variant => <$pac>::$pac_variant),* + impl From for $pac { + fn from(sample_time: SampleTime) -> $pac { + match sample_time { + $(SampleTime::$variant => <$pac>::$pac_variant),* } } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e2678fe4a..30ac5d872 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -214,7 +214,7 @@ where unsafe fn read_channel(&mut self, channel: u8) -> u16 { // Configure ADC - T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cr1().modify(|reg| reg.set_res(self.resolution.into())); // Select channel T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); @@ -228,14 +228,11 @@ where } unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr2() - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr1() - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr1().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 7bc130e55..7962f2414 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -199,9 +199,9 @@ impl<'d, T: Instance> Adc<'d, T> { // Configure ADC #[cfg(not(stm32g0))] - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); #[cfg(stm32g0)] - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.into())); // Configure channel Self::set_channel_sample_time(pin.channel(), self.sample_time); @@ -231,19 +231,16 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(stm32g0)] unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { - T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.sample_time())); + T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } #[cfg(not(stm32g0))] unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr1() - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr2() - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 9dc5318e2..dc5f62d3c 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -409,7 +409,7 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { unsafe fn read_channel(&mut self, channel: u8) -> u16 { // Configure ADC - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); // Configure channel Self::set_channel_sample_time(channel, self.sample_time); @@ -427,14 +427,11 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { } unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); if ch <= 9 { - T::regs() - .smpr(0) - .modify(|reg| reg.set_smp(ch as _, sample_time.sample_time())); + T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); } else { - T::regs() - .smpr(1) - .modify(|reg| reg.set_smp((ch - 10) as _, sample_time.sample_time())); + T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } } From 88bbc238b7da95162e4959a62d15c79bfef05149 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 16:45:12 -0500 Subject: [PATCH 0293/1575] Set resolution directly --- embassy-stm32/src/adc/v2.rs | 7 +++---- embassy-stm32/src/adc/v3.rs | 15 ++++++--------- embassy-stm32/src/adc/v4.rs | 9 +++------ 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 30ac5d872..2cdaa00dc 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -94,7 +94,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -120,7 +119,6 @@ where Self { sample_time: Default::default(), - resolution: Resolution::default(), phantom: PhantomData, } } @@ -130,7 +128,9 @@ where } pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; + unsafe { + T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + } } /// Enables internal voltage reference and returns [VrefInt], which can be used in @@ -214,7 +214,6 @@ where unsafe fn read_channel(&mut self, channel: u8) -> u16 { // Configure ADC - T::regs().cr1().modify(|reg| reg.set_res(self.resolution.into())); // Select channel T::regs().sqr3().write(|reg| reg.set_sq(0, channel)); diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 7962f2414..c6898f54a 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -62,7 +62,6 @@ impl super::sealed::AdcPin for Vbat { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -99,7 +98,6 @@ impl<'d, T: Instance> Adc<'d, T> { Self { sample_time: Default::default(), - resolution: Resolution::default(), phantom: PhantomData, } } @@ -145,7 +143,12 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; + unsafe { + #[cfg(not(stm32g0))] + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + #[cfg(stm32g0)] + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } } /* @@ -197,12 +200,6 @@ impl<'d, T: Instance> Adc<'d, T> { // spin } - // Configure ADC - #[cfg(not(stm32g0))] - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); - #[cfg(stm32g0)] - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.into())); - // Configure channel Self::set_channel_sample_time(pin.channel(), self.sample_time); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index dc5f62d3c..750f3480f 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -227,7 +227,6 @@ impl Prescaler { pub struct Adc<'d, T: Instance> { sample_time: SampleTime, - resolution: Resolution, phantom: PhantomData<&'d mut T>, } @@ -264,7 +263,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { let mut s = Self { sample_time: Default::default(), - resolution: Resolution::default(), phantom: PhantomData, }; s.power_up(delay); @@ -367,7 +365,9 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; + unsafe { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } } /// Perform a single conversion. @@ -408,9 +408,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { } unsafe fn read_channel(&mut self, channel: u8) -> u16 { - // Configure ADC - T::regs().cfgr().modify(|reg| reg.set_res(self.resolution.into())); - // Configure channel Self::set_channel_sample_time(channel, self.sample_time); From 6bf24b4d1ab1df57e410de6d6c8f413a2c44d5af Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:21:38 -0500 Subject: [PATCH 0294/1575] Refactor: Remove unused `Common` trait --- embassy-stm32/src/adc/mod.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 328470ef6..08989649b 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -29,11 +29,6 @@ pub(crate) mod sealed { fn common_regs() -> &'static crate::pac::adccommon::AdcCommon; } - #[cfg(all(not(adc_f1), not(adc_v1)))] - pub trait Common { - fn regs() -> &'static crate::pac::adccommon::AdcCommon; - } - pub trait AdcPin { fn channel(&self) -> u8; } @@ -47,8 +42,7 @@ pub(crate) mod sealed { pub trait Instance: sealed::Instance + 'static {} #[cfg(any(adc_f1, adc_v2))] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} -#[cfg(all(not(adc_f1), not(adc_v1)))] -pub trait Common: sealed::Common + 'static {} + pub trait AdcPin: sealed::AdcPin {} pub trait InternalChannel: sealed::InternalChannel {} @@ -111,19 +105,6 @@ foreach_peripheral!( }; ); -#[cfg(all(not(adc_f1), not(adc_v1)))] -foreach_peripheral!( - (adccommon, $inst:ident) => { - impl sealed::Common for peripherals::$inst { - fn regs() -> &'static crate::pac::adccommon::AdcCommon { - &crate::pac::$inst - } - } - - impl crate::adc::Common for peripherals::$inst {} - }; -); - macro_rules! impl_adc_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::adc::AdcPin for crate::peripherals::$pin {} From f363f6ce92879a9cb21f058fa308b05256e7c482 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:33:32 -0500 Subject: [PATCH 0295/1575] Refactor: Don't return references to pointers --- embassy-stm32/src/adc/mod.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 08989649b..439fe0ba6 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -24,9 +24,9 @@ use crate::peripherals; pub(crate) mod sealed { pub trait Instance { - fn regs() -> &'static crate::pac::adc::Adc; + fn regs() -> crate::pac::adc::Adc; #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon; + fn common_regs() -> crate::pac::adccommon::AdcCommon; } pub trait AdcPin { @@ -50,14 +50,14 @@ pub trait InternalChannel: sealed::InternalChannel {} foreach_peripheral!( (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { - fn regs() -> &'static crate::pac::adc::Adc { - &crate::pac::$inst + fn regs() -> crate::pac::adc::Adc { + crate::pac::$inst } #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { - return &crate::pac::$common_inst + return crate::pac::$common_inst }; } } @@ -71,14 +71,14 @@ foreach_peripheral!( foreach_peripheral!( (adc, ADC3) => { impl crate::adc::sealed::Instance for peripherals::ADC3 { - fn regs() -> &'static crate::pac::adc::Adc { - &crate::pac::ADC3 + fn regs() -> crate::pac::adc::Adc { + crate::pac::ADC3 } #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, ADC3_COMMON) => { - return &crate::pac::ADC3_COMMON + return crate::pac::ADC3_COMMON }; } } @@ -88,14 +88,14 @@ foreach_peripheral!( }; (adc, $inst:ident) => { impl crate::adc::sealed::Instance for peripherals::$inst { - fn regs() -> &'static crate::pac::adc::Adc { - &crate::pac::$inst + fn regs() -> crate::pac::adc::Adc { + crate::pac::$inst } #[cfg(all(not(adc_f1), not(adc_v1)))] - fn common_regs() -> &'static crate::pac::adccommon::AdcCommon { + fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, ADC_COMMON) => { - return &crate::pac::ADC_COMMON + return crate::pac::ADC_COMMON }; } } From 9c30d565b970330dd0041e790516ab4517dd3b22 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:51:12 -0500 Subject: [PATCH 0296/1575] Refactor: Factor out `Adc` struct declaration --- embassy-stm32/src/adc/f1.rs | 7 +------ embassy-stm32/src/adc/mod.rs | 6 ++++++ embassy-stm32/src/adc/v2.rs | 7 +------ embassy-stm32/src/adc/v3.rs | 7 +------ embassy-stm32/src/adc/v4.rs | 7 +------ 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 8a3b4be56..4282a6553 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, SampleTime}; use crate::rcc::get_freqs; use crate::time::Hertz; use crate::Peripheral; @@ -29,11 +29,6 @@ impl super::sealed::AdcPin for Temperature { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(_peri); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 439fe0ba6..70c1025e5 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -22,6 +22,12 @@ pub use sample_time::SampleTime; use crate::peripherals; +#[cfg(not(adc_v1))] +pub struct Adc<'d, T: Instance> { + sample_time: SampleTime, + phantom: core::marker::PhantomData<&'d mut T>, +} + pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 2cdaa00dc..f1e65dc53 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -4,7 +4,7 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; -use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; use crate::Peripheral; @@ -92,11 +92,6 @@ impl Prescaler { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T> Adc<'d, T> where T: Instance, diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index c6898f54a..ee5f278b1 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; use crate::Peripheral; /// Default VREF voltage used for sample conversion to millivolts. @@ -60,11 +60,6 @@ impl super::sealed::AdcPin for Vbat { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(_peri); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 750f3480f..c83674e61 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use super::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::time::Hertz; use crate::{pac, Peripheral}; @@ -225,11 +225,6 @@ impl Prescaler { } } -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { embassy_hal_common::into_ref!(_peri); From 4f2dcca34b6e17b07df58ee4f26f852a992e7f8b Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 17:59:44 -0500 Subject: [PATCH 0297/1575] Refactor: Fix v4 `RccPeripheral` bounds --- embassy-stm32/src/adc/mod.rs | 4 ++-- embassy-stm32/src/adc/v4.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 70c1025e5..5d90b8b3d 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -44,9 +44,9 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v2)))] +#[cfg(not(any(adc_f1, adc_v2, adc_v4)))] pub trait Instance: sealed::Instance + 'static {} -#[cfg(any(adc_f1, adc_v2))] +#[cfg(any(adc_f1, adc_v2, adc_v4))] pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} pub trait AdcPin: sealed::AdcPin {} diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c83674e61..12f86885c 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -225,7 +225,7 @@ impl Prescaler { } } -impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> { +impl<'d, T: Instance> Adc<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { embassy_hal_common::into_ref!(_peri); T::enable(); From 08c8022583a12dcbea8905249f3c19630462092e Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 18:04:52 -0500 Subject: [PATCH 0298/1575] Refactor: Reorder `_version` cfgs --- embassy-stm32/src/adc/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 5d90b8b3d..6d8afea57 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,11 +1,10 @@ #![macro_use] -#[cfg_attr(adc_v4, path = "v4.rs")] -#[cfg_attr(adc_v3, path = "v3.rs")] -#[cfg_attr(adc_v2, path = "v2.rs")] -#[cfg_attr(adc_g0, path = "v3.rs")] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] +#[cfg_attr(adc_v2, path = "v2.rs")] +#[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] +#[cfg_attr(adc_v4, path = "v4.rs")] mod _version; #[cfg(not(any(adc_f1, adc_v1)))] From 171b764d8275b8da3af5650edfac0d5c4c3e3fe1 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 26 Oct 2022 18:36:04 -0500 Subject: [PATCH 0299/1575] Refactor: Use `PeripheralRef` --- embassy-stm32/src/adc/f1.rs | 8 +++----- embassy-stm32/src/adc/mod.rs | 7 ++++--- embassy-stm32/src/adc/v2.rs | 8 +++----- embassy-stm32/src/adc/v3.rs | 8 +++----- embassy-stm32/src/adc/v4.rs | 8 +++----- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 4282a6553..d30ec001d 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; @@ -30,8 +28,8 @@ impl super::sealed::AdcPin for Temperature { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); T::enable(); T::reset(); unsafe { @@ -60,8 +58,8 @@ impl<'d, T: Instance> Adc<'d, T> { delay.delay_us((1_000_000) / Self::freq().0 + 1); Self { + adc, sample_time: Default::default(), - phantom: PhantomData, } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 6d8afea57..ec49dace7 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -23,8 +23,9 @@ use crate::peripherals; #[cfg(not(adc_v1))] pub struct Adc<'d, T: Instance> { + #[allow(unused)] + adc: crate::PeripheralRef<'d, T>, sample_time: SampleTime, - phantom: core::marker::PhantomData<&'d mut T>, } pub(crate) mod sealed { @@ -44,9 +45,9 @@ pub(crate) mod sealed { } #[cfg(not(any(adc_f1, adc_v2, adc_v4)))] -pub trait Instance: sealed::Instance + 'static {} +pub trait Instance: sealed::Instance + crate::Peripheral

{} #[cfg(any(adc_f1, adc_v2, adc_v4))] -pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {} +pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} pub trait InternalChannel: sealed::InternalChannel {} diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index f1e65dc53..11a51f993 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; @@ -96,8 +94,8 @@ impl<'d, T> Adc<'d, T> where T: Instance, { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); T::enable(); T::reset(); @@ -113,8 +111,8 @@ where delay.delay_us(ADC_POWERUP_TIME_US); Self { + adc, sample_time: Default::default(), - phantom: PhantomData, } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index ee5f278b1..8f81cb7a3 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; @@ -61,8 +59,8 @@ impl super::sealed::AdcPin for Vbat { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); enable(); unsafe { T::regs().cr().modify(|reg| { @@ -92,8 +90,8 @@ impl<'d, T: Instance> Adc<'d, T> { delay.delay_us(1); Self { + adc, sample_time: Default::default(), - phantom: PhantomData, } } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 12f86885c..f5aa0e63d 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,5 +1,3 @@ -use core::marker::PhantomData; - use atomic_polyfill::{AtomicU8, Ordering}; use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; @@ -226,8 +224,8 @@ impl Prescaler { } impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - embassy_hal_common::into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + embassy_hal_common::into_ref!(adc); T::enable(); T::reset(); @@ -257,8 +255,8 @@ impl<'d, T: Instance> Adc<'d, T> { } let mut s = Self { + adc, sample_time: Default::default(), - phantom: PhantomData, }; s.power_up(delay); s.configure_differential_inputs(); From 3c6c382465131c6f76567f976198b77e327df4b2 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 27 Oct 2022 07:10:27 +0200 Subject: [PATCH 0300/1575] Remove random delay from example, and move flash functions to allow using without embedded-storage in scope --- embassy-rp/src/flash.rs | 120 +++++++++++++++++++---------------- examples/rp/src/bin/flash.rs | 8 ++- 2 files changed, 74 insertions(+), 54 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 0f86684e2..d09cc62fb 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -55,51 +55,10 @@ pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { pub fn new(_flash: impl Peripheral

+ 'd) -> Self { - // FIXME: WHY is this needed?! - cortex_m::asm::delay(50_000); Self(PhantomData) } - /// Make sure to uphold the contract points with rp2040-flash. - /// - interrupts must be disabled - /// - DMA must not access flash memory - unsafe fn in_ram(&mut self, operation: impl FnOnce()) { - let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; - - // TODO: Make sure CORE1 is paused during the entire duration of the RAM function - - critical_section::with(|_| { - // Pause all DMA channels for the duration of the ram operation - for (number, status) in dma_status.iter_mut().enumerate() { - let ch = crate::pac::DMA.ch(number as _); - *status = ch.ctrl_trig().read().en(); - if *status { - ch.ctrl_trig().modify(|w| w.set_en(false)); - } - } - - // Run our flash operation in RAM - operation(); - - // Re-enable previously enabled DMA channels - for (number, status) in dma_status.iter().enumerate() { - let ch = crate::pac::DMA.ch(number as _); - if *status { - ch.ctrl_trig().modify(|w| w.set_en(true)); - } - } - }); - } -} - -impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { - type Error = Error; -} - -impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { 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()) }; @@ -108,19 +67,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLA Ok(()) } - fn capacity(&self) -> usize { + pub fn capacity(&self) -> usize { FLASH_SIZE } -} -impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} - -impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { - const WRITE_SIZE: usize = WRITE_SIZE; - - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { check_erase(self, from, to)?; trace!( @@ -136,7 +87,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S Ok(()) } - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { check_write(self, offset, bytes.len())?; trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); @@ -199,6 +150,69 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S Ok(()) } + + /// Make sure to uphold the contract points with rp2040-flash. + /// - interrupts must be disabled + /// - DMA must not access flash memory + unsafe fn in_ram(&mut self, operation: impl FnOnce()) { + let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; + + // TODO: Make sure CORE1 is paused during the entire duration of the RAM function + + critical_section::with(|_| { + // Pause all DMA channels for the duration of the ram operation + for (number, status) in dma_status.iter_mut().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + *status = ch.ctrl_trig().read().en(); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(false)); + } + } + + // Run our flash operation in RAM + operation(); + + // Re-enable previously enabled DMA channels + for (number, status) in dma_status.iter().enumerate() { + let ch = crate::pac::DMA.ch(number as _); + if *status { + ch.ctrl_trig().modify(|w| w.set_en(true)); + } + } + }); + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { + type Error = Error; +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} + +impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) + } } #[allow(dead_code)] diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index d4dfca759..8d6b379f4 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; use embassy_rp::peripherals::FLASH; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; const ADDR_OFFSET: u32 = 0x100000; @@ -17,6 +17,12 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); + // add some delay to give an attached debug probe time to parse the + // defmt RTT header. Reading that header might touch flash memory, which + // interferes with flash write operations. + // https://github.com/knurling-rs/defmt/pull/683 + Timer::after(Duration::from_millis(10)).await; + let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); erase_write_sector(&mut flash, 0x00); From a7b90c7fb60e3814e929ee963fd328db2a138ca7 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 27 Oct 2022 11:36:46 +0200 Subject: [PATCH 0301/1575] Remove unused imports from test --- tests/rp/src/bin/flash.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 4cbd78fe9..51371963e 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; const ADDR_OFFSET: u32 = 0x4000; From bc21b6efafe607e6ed582b048baedb7803483ee7 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 27 Oct 2022 12:41:06 +0200 Subject: [PATCH 0302/1575] Add delay to flash test to allow time to parse RTT header --- tests/rp/src/bin/flash.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 51371963e..897e3804f 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -5,6 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; const ADDR_OFFSET: u32 = 0x4000; @@ -14,6 +15,12 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); + // add some delay to give an attached debug probe time to parse the + // defmt RTT header. Reading that header might touch flash memory, which + // interferes with flash write operations. + // https://github.com/knurling-rs/defmt/pull/683 + Timer::after(Duration::from_millis(10)).await; + let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); let mut buf = [0u8; ERASE_SIZE]; From 9423987ac52b0e4f7a176e70114016acd3752592 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Thu, 27 Oct 2022 10:52:21 +0200 Subject: [PATCH 0303/1575] embassy-stm32: Add hardware flow control constructor for UartRx and UartTx --- embassy-stm32/src/usart/mod.rs | 117 +++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dee466b21..dde0799bb 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -102,7 +102,80 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { } impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { - fn new(tx_dma: PeripheralRef<'d, TxDma>) -> Self { + /// usefull if you only want Uart Tx. It saves 1 pin and consumes a little less power + pub fn new( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + T::enable(); + T::reset(); + + Self::new_inner(peri, tx, tx_dma, config) + } + + pub fn new_with_cts( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(cts); + + T::enable(); + T::reset(); + + unsafe { + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_ctse(true); + }); + } + Self::new_inner(peri, tx, tx_dma, config) + } + + fn new_inner( + _peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(_peri, tx, tx_dma); + + let r = T::regs(); + + configure(r, &config, T::frequency(), T::MULTIPLIER); + + unsafe { + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + r.cr2().write(|_w| {}); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable transceiver + w.set_te(true); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); + } + + // create state once! + let _s = T::state(); + Self { tx_dma, phantom: PhantomData, @@ -156,11 +229,44 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(peri, irq, rx, rx_dma); + T::enable(); + T::reset(); + + Self::new_inner(peri, irq, rx, rx_dma, config) + } + + pub fn new_with_rts( + peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(rts); T::enable(); T::reset(); + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_rtse(true); + }); + } + + Self::new_inner(peri, irq, rx, rx_dma, config) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, irq, rx, rx_dma); + let r = T::regs(); configure(r, &config, T::frequency(), T::MULTIPLIER); @@ -169,7 +275,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx.set_as_af(rx.af_num(), AFType::Input); r.cr2().write(|_w| {}); - r.cr3().write(|w| { + r.cr3().modify(|w| { // enable Error Interrupt: (Frame error, Noise error, Overrun error) w.set_eie(true); }); @@ -596,7 +702,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let _s = T::state(); Self { - tx: UartTx::new(tx_dma), + tx: UartTx { + tx_dma, + phantom: PhantomData, + }, rx: UartRx { _peri: peri, rx_dma, From f053bf742c2e6e5e5e18e36bb773bc1e84010a2a Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Thu, 27 Oct 2022 11:03:37 +0200 Subject: [PATCH 0304/1575] embassy-stm32: Add support for hardware flow control for BufferedUart --- embassy-stm32/src/usart/buffered.rs | 49 ++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 3be0677bd..59c7a8cca 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -45,6 +45,51 @@ impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( + state: &'d mut State<'d, T>, + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> BufferedUart<'d, T> { + T::enable(); + T::reset(); + + Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + } + + pub fn new_with_rtscts( + state: &'d mut State<'d, T>, + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> BufferedUart<'d, T> { + into_ref!(cts, rts); + + T::enable(); + T::reset(); + + unsafe { + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); + } + + Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + } + + fn new_inner( state: &'d mut State<'d, T>, _peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -56,9 +101,6 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { ) -> BufferedUart<'d, T> { into_ref!(_peri, rx, tx, irq); - T::enable(); - T::reset(); - let r = T::regs(); configure(r, &config, T::frequency(), T::MULTIPLIER); @@ -68,7 +110,6 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); r.cr2().write(|_w| {}); - r.cr3().write(|_w| {}); r.cr1().write(|w| { w.set_ue(true); w.set_te(true); From 79b49c6fae7b4f7df2c03ae0be970d154f4faac8 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Fri, 28 Oct 2022 09:32:05 +0200 Subject: [PATCH 0305/1575] embassy-stm32: remove duplicated code for USART general configuration --- embassy-stm32/src/usart/buffered.rs | 23 ++---- embassy-stm32/src/usart/mod.rs | 104 +++++++++------------------- 2 files changed, 38 insertions(+), 89 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 59c7a8cca..0a6d6e149 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -103,28 +103,15 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } - r.cr2().write(|_w| {}); - r.cr1().write(|w| { - w.set_ue(true); - w.set_te(true); - w.set_re(true); - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); + configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + + unsafe { + r.cr1().modify(|w| { w.set_rxneie(true); w.set_idleie(true); }); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dde0799bb..716247d9e 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -146,33 +146,12 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - - r.cr2().write(|_w| {}); - r.cr1().write(|w| { - // enable uart - w.set_ue(true); - // enable transceiver - w.set_te(true); - // configure word size - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - // configure parity - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - }); } + configure(r, &config, T::frequency(), T::MULTIPLIER, false, true); + // create state once! let _s = T::state(); @@ -269,37 +248,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { rx.set_as_af(rx.af_num(), AFType::Input); - - r.cr2().write(|_w| {}); - r.cr3().modify(|w| { - // enable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(true); - }); - r.cr1().write(|w| { - // enable uart - w.set_ue(true); - // enable receiver - w.set_re(true); - // configure word size - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - // configure parity - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - }); } + configure(r, &config, T::frequency(), T::MULTIPLIER, true, false); + irq.set_handler(Self::on_interrupt); irq.unpend(); irq.enable(); @@ -669,31 +623,13 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let r = T::regs(); - configure(r, &config, T::frequency(), T::MULTIPLIER); - unsafe { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - - r.cr2().write(|_w| {}); - r.cr1().write(|w| { - w.set_ue(true); - w.set_te(true); - w.set_re(true); - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - }); } + configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + irq.set_handler(UartRx::::on_interrupt); irq.unpend(); irq.enable(); @@ -759,12 +695,38 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } -fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32) { +fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable_rx: bool, enable_tx: bool) { + if !enable_rx && !enable_tx { + panic!("USART: At least one of RX or TX should be enabled"); + } + // TODO: better calculation, including error checking and OVER8 if possible. let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier; unsafe { r.brr().write_value(regs::Brr(div)); + r.cr2().write(|_w| {}); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable transceiver + w.set_te(enable_tx); + // enable receiver + w.set_re(enable_rx); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, + }); + }); } } From 49e109130937bd0ab0742a1a4765a97d0dc8a00c Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Fri, 28 Oct 2022 10:49:59 +0200 Subject: [PATCH 0306/1575] embassy-stm32: Fix bug where USART idle flag could end a `read` prematuraly --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dee466b21..e059c91b2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -465,7 +465,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } - if sr.idle() { + if enable_idle_line_detection && sr.idle() { // Idle line // stop dma transfer From 0b2d6996e8f35496ee1242bf80a213eb36121a7a Mon Sep 17 00:00:00 2001 From: Daniel Bevenius Date: Sat, 29 Oct 2022 15:16:09 +0200 Subject: [PATCH 0307/1575] Fix ascii table in BootLoader doc comment Signed-off-by: Daniel Bevenius --- embassy-boot/boot/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 8286601ec..429323ec9 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -150,7 +150,7 @@ impl BootLoader { /// +-----------+------------+--------+--------+--------+--------+ /// | Active | 0 | 1 | 2 | 3 | - | /// | DFU | 0 | 3 | 2 | 1 | X | - /// +-----------+-------+--------+--------+--------+--------+ + /// +-----------+------------+--------+--------+--------+--------+ /// /// The algorithm starts by copying 'backwards', and after the first step, the layout is /// as follows: From 97d18c5ffb21cee4352c3ae799d762533c60e42b Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 1 Nov 2022 07:54:43 +0100 Subject: [PATCH 0308/1575] Move default initializer function to Default trait implementation --- embassy-boot/nrf/src/lib.rs | 6 ++- embassy-boot/stm32/src/lib.rs | 76 ++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 385e089fe..82475d1e2 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -17,9 +17,9 @@ pub struct BootLoader { page: AlignedBuffer, } -impl BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script - pub fn default() -> Self { + fn default() -> Self { extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; @@ -54,7 +54,9 @@ impl BootLoader { Self::new(active, dfu, state) } +} +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 { diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index edba39cca..d549eccc6 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -14,43 +14,6 @@ pub struct BootLoader { } impl BootLoader { - /// Create a new bootloader instance using parameters from linker script - pub 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) - } - /// 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 { @@ -85,3 +48,42 @@ impl BootLoader 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) + } +} From fc086fd4ba5679cae1bacb0556e503afd6e8894a Mon Sep 17 00:00:00 2001 From: miathedev Date: Tue, 1 Nov 2022 10:38:02 +0100 Subject: [PATCH 0309/1575] Add uart async example --- examples/stm32wl/src/bin/uart_async.rs | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/stm32wl/src/bin/uart_async.rs diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs new file mode 100644 index 000000000..f12fec4c8 --- /dev/null +++ b/examples/stm32wl/src/bin/uart_async.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::interrupt; +use embassy_stm32::usart::{Config, Uart}; +use {defmt_rtt as _, panic_probe as _}; + +/* +Pass Incoming data from LPUART1 to USART1 +Example is written for the LoRa-E5 mini v1.0, +but can be surely changed for your needs. +*/ +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; + let p = embassy_stm32::init(config); + + defmt::info!("Starting system"); + + let mut config1 = Config::default(); + config1.baudrate = 9600; + + let mut config2 = Config::default(); + config2.baudrate = 9600; + + //RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0 + let irq = interrupt::take!(USART1); + let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH3, p.DMA1_CH4, config1); + + //RX1/TX1 (LPUART) on LoRa-E5 mini v1.0 + let irq = interrupt::take!(LPUART1); + let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, irq, p.DMA1_CH5, p.DMA1_CH6, config2); + + unwrap!(usart1.write(b"Hello Embassy World!\r\n").await); + unwrap!(usart2.write(b"Hello Embassy World!\r\n").await); + + let mut buf = [0u8; 300]; + loop { + let result = usart2.read_until_idle(&mut buf).await; + match result { + Ok(size) => { + match usart1.write(&buf[0..size]).await { + Ok(()) => { + //Write suc. + } + Err(..) => { + //Wasnt able to write + } + } + } + Err(_err) => { + //Ignore eg. framing errors + } + } + } +} From ea4d08b6cf6f17b18b515326261f9b37e6f43856 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 1 Nov 2022 14:23:22 -0500 Subject: [PATCH 0310/1575] stm32-metapac-gen: Use `serde_json` to parse json files --- stm32-metapac-gen/Cargo.toml | 2 ++ stm32-metapac-gen/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml index 3c1dab57a..9598a5945 100644 --- a/stm32-metapac-gen/Cargo.toml +++ b/stm32-metapac-gen/Cargo.toml @@ -9,5 +9,7 @@ license = "MIT OR Apache-2.0" regex = "1.5.4" chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "28ffa8a19d84914089547f52900ffb5877a5dc23" } serde = { version = "1.0.130", features = [ "derive" ] } +serde_json = "1.0.87" serde_yaml = "0.8.21" proc-macro2 = "1.0.29" + diff --git a/stm32-metapac-gen/src/lib.rs b/stm32-metapac-gen/src/lib.rs index 9bd60cb79..64045986e 100644 --- a/stm32-metapac-gen/src/lib.rs +++ b/stm32-metapac-gen/src/lib.rs @@ -223,7 +223,7 @@ impl Gen { fn load_chip(&mut self, name: &str) -> Chip { let chip_path = self.opts.data_dir.join("chips").join(&format!("{}.json", name)); let chip = fs::read(chip_path).expect(&format!("Could not load chip {}", name)); - serde_yaml::from_slice(&chip).unwrap() + serde_json::from_slice(&chip).unwrap() } pub fn gen(&mut self) { From 1920e90dcdbebc1e2f86001f1491a9f28eb0f0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 00:15:43 +0100 Subject: [PATCH 0311/1575] embassy-nrf: Add SPIS module --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52820.rs | 3 + embassy-nrf/src/chips/nrf52832.rs | 4 + embassy-nrf/src/chips/nrf52833.rs | 4 + embassy-nrf/src/chips/nrf52840.rs | 4 + embassy-nrf/src/chips/nrf5340_app.rs | 5 + embassy-nrf/src/chips/nrf5340_net.rs | 1 + embassy-nrf/src/chips/nrf9160.rs | 5 + embassy-nrf/src/lib.rs | 1 + embassy-nrf/src/spis.rs | 516 +++++++++++++++++++++++++++ examples/nrf/src/bin/spis.rs | 25 ++ 13 files changed, 575 insertions(+) create mode 100644 embassy-nrf/src/spis.rs create mode 100644 examples/nrf/src/bin/spis.rs diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index dec31a84c..11a6840c8 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -131,6 +131,8 @@ 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_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index e57a4a383..3614cd229 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -137,6 +137,8 @@ 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_pwm!(PWM0, PWM0, PWM0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 918404cf1..dc4a8660e 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -138,6 +138,9 @@ 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_pwm!(PWM0, PWM0, PWM0); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index dba033b0f..7668920bd 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -136,6 +136,9 @@ 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); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 81e66c193..851643b55 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -146,6 +146,10 @@ 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); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 92499e3c9..5342ba8c2 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -174,6 +174,10 @@ 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); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4beadfba8..a330aef8b 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -177,6 +177,10 @@ 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); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 7845d4a8e..1c027ec02 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -361,6 +361,11 @@ 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); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index ae136e09d..3bcd44fcb 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -238,6 +238,7 @@ 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_timer!(TIMER0, TIMER0, TIMER0); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index b5a53ed80..0dfa112fe 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -275,6 +275,11 @@ 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); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f6..587e19be5 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -96,6 +96,7 @@ 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; diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs new file mode 100644 index 000000000..32c0b6f93 --- /dev/null +++ b/embassy-nrf/src/spis.rs @@ -0,0 +1,516 @@ +#![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, PselBits}; +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, + 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) + } + + pub fn new_rxonly( + 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) + } + + 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 { + into_ref!(cs, spis, irq); + + let r = T::regs(); + + // Configure pins + sck.conf().write(|w| w.input().connect().drive().h0h1()); + cs.conf().write(|w| w.input().connect().drive().h0h1()); + if let Some(mosi) = &mosi { + mosi.conf().write(|w| w.input().connect().drive().h0h1()); + } + if let Some(miso) = &miso { + miso.conf().write(|w| w.dir().output().drive().h0h1()); + } + + match config.mode.polarity { + Polarity::IdleHigh => { + sck.set_high(); + if let Some(mosi) = &mosi { + mosi.set_high(); + } + } + Polarity::IdleLow => { + sck.set_low(); + if let Some(mosi) = &mosi { + mosi.set_low(); + } + } + } + + if config.auto_acquire { + r.shorts.write(|w| w.end_acquire().bit(true)); + } + + // Select pins. + r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); + r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); + r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); + 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) }); + + // 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.end_waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_acquired.read().bits() != 0 { + s.acquire_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 and enable the end event + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + // 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> { + self.prepare(rx, tx)?; + let r = T::regs(); + + // 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(); + + if r.semstat.read().bits() != 1 { + // Reset and enable the acquire event + r.events_acquired.reset(); + r.intenset.write(|w| w.acquired().set()); + + // Requests acquiring the SPIS semaphore + r.tasks_acquire.write(|w| unsafe { w.bits(1) }); + + // Wait for 'acquire' event. + poll_fn(|cx| { + s.acquire_waker.register(cx.waker()); + if r.events_acquired.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + } + + self.prepare(rx, tx)?; + + // Wait for 'end' event. + poll_fn(|cx| { + s.end_waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { + 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 the buffer has been filled. + /// 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(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(&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) + } +} + +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 end_waker: AtomicWaker, + pub acquire_waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + end_waker: AtomicWaker::new(), + acquire_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) }); + + // Set auto acquire + let auto_acquire = config.auto_acquire; + r.shorts.write(|w| w.end_acquire().bit(auto_acquire)); + + } +} diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs new file mode 100644 index 000000000..181e08404 --- /dev/null +++ b/examples/nrf/src/bin/spis.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::spis::{self, Config}; +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::Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); + + loop { + let mut buf = [0_u8; 64]; + if let Ok(n) = spis.read(&mut buf).await { + info!("RX: {:?}", buf[..n]); + } + } +} From a3e8a6bc3a706bc59b9d017699eaab93c1ba60d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 00:19:52 +0100 Subject: [PATCH 0312/1575] rustfmt --- embassy-nrf/src/spis.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 32c0b6f93..ab7986b89 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -82,7 +82,15 @@ impl<'d, T: Instance> Spis<'d, T> { 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) + Self::new_inner( + spis, + irq, + cs.map_into(), + sck.map_into(), + None, + Some(mosi.map_into()), + config, + ) } pub fn new_rxonly( @@ -94,7 +102,15 @@ impl<'d, T: Instance> Spis<'d, T> { 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) + Self::new_inner( + spis, + irq, + cs.map_into(), + sck.map_into(), + Some(miso.map_into()), + None, + config, + ) } fn new_inner( @@ -278,7 +294,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Requests acquiring the SPIS semaphore r.tasks_acquire.write(|w| unsafe { w.bits(1) }); - + // Wait for 'acquire' event. poll_fn(|cx| { s.acquire_waker.register(cx.waker()); @@ -511,6 +527,5 @@ impl<'d, T: Instance> SetConfig for Spis<'d, T> { // Set auto acquire let auto_acquire = config.auto_acquire; r.shorts.write(|w| w.end_acquire().bit(auto_acquire)); - } } From 7da18e194a8a9fef207803b96b16e7b7bc787ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:12:25 +0100 Subject: [PATCH 0313/1575] Add status checks --- embassy-nrf/src/spis.rs | 69 ++++++++++++++++++++++-------------- examples/nrf/src/bin/spis.rs | 4 +-- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index ab7986b89..71106b7df 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -126,7 +126,7 @@ impl<'d, T: Instance> Spis<'d, T> { let r = T::regs(); - // Configure pins + // Configure pins. sck.conf().write(|w| w.input().connect().drive().h0h1()); cs.conf().write(|w| w.input().connect().drive().h0h1()); if let Some(mosi) = &mosi { @@ -151,10 +151,6 @@ impl<'d, T: Instance> Spis<'d, T> { } } - if config.auto_acquire { - r.shorts.write(|w| w.end_acquire().bit(true)); - } - // Select pins. r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); @@ -193,15 +189,20 @@ impl<'d, T: Instance> Spis<'d, T> { w }); - // Set over-read character + // Set over-read character. let orc = config.orc; r.orc.write(|w| unsafe { w.orc().bits(orc) }); - // Set default character + // Set default character. let def = config.def; r.def.write(|w| unsafe { w.def().bits(def) }); - // Disable all events interrupts + // 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); @@ -245,11 +246,11 @@ impl<'d, T: Instance> Spis<'d, T> { r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - // Reset and enable the end event + // Reset and enable the end event. r.events_end.reset(); r.intenset.write(|w| w.end().set()); - // Release the semaphore + // Release the semaphore. r.tasks_release.write(|w| unsafe { w.bits(1) }); Ok(()) @@ -287,12 +288,15 @@ impl<'d, T: Instance> Spis<'d, T> { let r = T::regs(); let s = T::state(); + // Clear status register. + r.status.write(|w| w.overflow().clear().overread().clear()); + if r.semstat.read().bits() != 1 { - // Reset and enable the acquire event + // Reset and enable the acquire event. r.events_acquired.reset(); r.intenset.write(|w| w.acquired().set()); - // Requests acquiring the SPIS semaphore + // Request acquiring the SPIS semaphore. r.tasks_acquire.write(|w| unsafe { w.bits(1) }); // Wait for 'acquire' event. @@ -341,81 +345,92 @@ impl<'d, T: Instance> Spis<'d, T> { } /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled. - /// Returns number of bytes read + /// 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)` + /// 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)` + /// 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(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 + /// 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 + /// 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 + /// Returns number of bytes written. pub fn blocking_write_from_ram(&mut self, data: &[u8]) -> Result { self.blocking_inner(&mut [], data).map(|n| n.1) } /// Reads data from the SPI bus without sending anything. - /// Returns number of bytes read + /// 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)` + /// 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)` + /// 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 + /// 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 + /// 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 + /// 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> { @@ -516,15 +531,15 @@ impl<'d, T: Instance> SetConfig for Spis<'d, T> { w }); - // Set over-read character + // Set over-read character. let orc = config.orc; r.orc.write(|w| unsafe { w.orc().bits(orc) }); - // Set default character + // Set default character. let def = config.def; r.def.write(|w| unsafe { w.def().bits(def) }); - // Set auto acquire + // 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/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs index 181e08404..0fce23d31 100644 --- a/examples/nrf/src/bin/spis.rs +++ b/examples/nrf/src/bin/spis.rs @@ -5,7 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_nrf::interrupt; -use embassy_nrf::spis::{self, Config}; +use embassy_nrf::spis::{Spis, Config}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { info!("Running!"); let irq = interrupt::take!(SPIM2_SPIS2_SPI2); - let mut spis = spis::Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); + 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 buf = [0_u8; 64]; From 207fa195512c9e6604a1dec80ec055b06b9b49dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:34:52 +0100 Subject: [PATCH 0314/1575] Acquire semaphore on blocking --- embassy-nrf/src/spis.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 71106b7df..22c13557a 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -257,9 +257,19 @@ impl<'d, T: Instance> Spis<'d, T> { } fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { - self.prepare(rx, tx)?; + 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 {} @@ -291,6 +301,7 @@ impl<'d, T: Instance> Spis<'d, T> { // 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(); @@ -299,10 +310,10 @@ impl<'d, T: Instance> Spis<'d, T> { // Request acquiring the SPIS semaphore. r.tasks_acquire.write(|w| unsafe { w.bits(1) }); - // Wait for 'acquire' event. + // Wait until CPU has acquired the semaphore. poll_fn(|cx| { s.acquire_waker.register(cx.waker()); - if r.events_acquired.read().bits() != 0 { + if r.semstat.read().bits() == 1 { return Poll::Ready(()); } Poll::Pending From aecfce1159480610586bc60cb90af712fae782c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:36:29 +0100 Subject: [PATCH 0315/1575] rustfmt --- embassy-nrf/src/spis.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 22c13557a..3f77c61d1 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -441,7 +441,6 @@ impl<'d, T: Instance> Spis<'d, T> { pub fn is_overflow(&mut self) -> bool { T::regs().status.read().overflow().is_present() } - } impl<'d, T: Instance> Drop for Spis<'d, T> { From af34fc4ccc110bf37165f2b9655585ba3a33889a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Sat, 5 Nov 2022 01:40:20 +0100 Subject: [PATCH 0316/1575] rustfmt --- examples/nrf/src/bin/spis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs index 0fce23d31..dade5fcbd 100644 --- a/examples/nrf/src/bin/spis.rs +++ b/examples/nrf/src/bin/spis.rs @@ -5,7 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_nrf::interrupt; -use embassy_nrf::spis::{Spis, Config}; +use embassy_nrf::spis::{Config, Spis}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 14a2d1524080593f7795fe14950a3f0ee6e2b409 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sat, 5 Nov 2022 22:55:04 +0800 Subject: [PATCH 0317/1575] Derive Default for WakerRegistration This simplifies creating arrays of WakerRegistrations --- embassy-sync/src/waitqueue/waker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/waitqueue/waker.rs b/embassy-sync/src/waitqueue/waker.rs index 64e300eb8..9ce94a089 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, } From 1365ce6ab88d4891ddad945fae9f71043f521fb6 Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Mon, 7 Nov 2022 17:46:32 +0100 Subject: [PATCH 0318/1575] embassy-stm32: Fix bug when Uart::read future is dropped and DMA request was not stopped fixes issue #1045 regression was introduced with PR #1031 --- embassy-stm32/src/usart/mod.rs | 164 ++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 64 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0a1ea94ed..aea054a4b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -6,6 +6,8 @@ use core::task::Poll; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::interrupt::InterruptExt; +use embassy_futures::select::{select, Either}; +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::NoDma; @@ -85,6 +87,13 @@ pub enum Error { BufferTooLong, } +enum ReadCompletionEvent { + // DMA Read transfer completed first + DmaCompleted, + // Idle line detected first + Idle, +} + pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { tx: UartTx<'d, T, TxDma>, rx: UartRx<'d, T, RxDma>, @@ -385,30 +394,50 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { self.inner_read(buffer, true).await } - async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result + async fn inner_read_run( + &mut self, + buffer: &mut [u8], + enable_idle_line_detection: bool, + ) -> Result where RxDma: crate::usart::RxDma, { - if buffer.is_empty() { - return Ok(0); - } else if buffer.len() > 0xFFFF { - return Err(Error::BufferTooLong); - } - let r = T::regs(); - let buffer_len = buffer.len(); + // make sure USART state is restored to neutral state when this future is dropped + let _drop = OnDrop::new(move || { + // defmt::trace!("Clear all USART interrupts and DMA Read Request"); + // clear all interrupts and DMA Rx Request + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + }); let ch = &mut self.rx_dma; let request = ch.request(); + // Start USART DMA + // will not do anything yet because DMAR is not yet set + // future which will complete when DMA Read request completes + let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); + // SAFETY: The only way we might have a problem is using split rx and tx // here we only modify or read Rx related flags, interrupts and DMA channel unsafe { - // Start USART DMA - // will not do anything yet because DMAR is not yet set - ch.start_read(request, rdr(r), buffer, Default::default()); - // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer if !self.detect_previous_overrun { let sr = sr(r).read(); @@ -443,9 +472,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // something went wrong // because the only way to get this flag cleared is to have an interrupt - // abort DMA transfer - ch.request_stop(); - while ch.is_running() {} + // DMA will be stopped when transfer is dropped let sr = sr(r).read(); // This read also clears the error and idle interrupt flags on v1. @@ -468,26 +495,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { unreachable!(); } - // clear idle flag - if enable_idle_line_detection { - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); + if !enable_idle_line_detection { + transfer.await; - // enable idle interrupt - r.cr1().modify(|w| { - w.set_idleie(true); - }); + return Ok(ReadCompletionEvent::DmaCompleted); } + + // clear idle flag + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); } compiler_fence(Ordering::SeqCst); - let res = poll_fn(move |cx| { + // future which completes when idle line is detected + let idle = poll_fn(move |cx| { let s = T::state(); - ch.set_waker(cx.waker()); s.rx_waker.register(cx.waker()); // SAFETY: read only and we only use Rx related flags @@ -507,10 +538,6 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { if has_errors { // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler - // stop dma transfer - ch.request_stop(); - while ch.is_running() {} - if sr.pe() { return Poll::Ready(Err(Error::Parity)); } @@ -525,45 +552,54 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } - if enable_idle_line_detection && sr.idle() { - // Idle line - - // stop dma transfer - ch.request_stop(); - while ch.is_running() {} - - let n = buffer_len - (ch.remaining_transfers() as usize); - - return Poll::Ready(Ok(n)); - } else if !ch.is_running() { - // DMA complete - return Poll::Ready(Ok(buffer_len)); + if sr.idle() { + // Idle line detected + return Poll::Ready(Ok(())); } Poll::Pending - }) - .await; + }); - // clear all interrupts and DMA Rx Request - // SAFETY: only clears Rx related flags - unsafe { - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // disable parity interrupt - w.set_peie(false); - // disable idle line interrupt - w.set_idleie(false); - }); - r.cr3().modify(|w| { - // disable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(false); - // disable DMA Rx Request - w.set_dmar(false); - }); + // wait for the first of DMA request or idle line detected to completes + // select consumes its arguments + // when transfer is dropped, it will stop the DMA request + match select(transfer, idle).await { + // DMA transfer completed first + Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), + + // Idle line detected first + Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle), + + // error occurred + Either::Second(Err(e)) => Err(e), + } + } + + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result + where + RxDma: crate::usart::RxDma, + { + if buffer.is_empty() { + return Ok(0); + } else if buffer.len() > 0xFFFF { + return Err(Error::BufferTooLong); } - res + let buffer_len = buffer.len(); + + // wait for DMA to complete or IDLE line detection if requested + let res = self.inner_read_run(buffer, enable_idle_line_detection).await; + + let ch = &mut self.rx_dma; + + match res { + Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), + Ok(ReadCompletionEvent::Idle) => { + let n = buffer_len - (ch.remaining_transfers() as usize); + Ok(n) + } + Err(e) => Err(e), + } } } From bd5ef80bec42c91cb1b4fd50ea4bbc12eac85283 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 7 Nov 2022 20:51:29 +0100 Subject: [PATCH 0319/1575] Ensure embassy-lora stm32wl supports log crate --- embassy-lora/src/stm32wl/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 8d5d19531..08bf32f99 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -70,7 +70,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form /// the upcoming RX window start. async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - trace!("TX request: {}", config); + trace!("TX request: {:?}", config); self.switch.set_tx(); self.radio @@ -130,7 +130,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { /// be able to hold a single LoRaWAN packet. async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { assert!(buf.len() >= 255); - trace!("RX request: {}", config); + trace!("RX request: {:?}", config); self.switch.set_rx(); self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; @@ -172,7 +172,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio.read_buffer(ptr, &mut buf[..len as usize])?; self.radio.set_standby(StandbyClk::Rc)?; - trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); + trace!("RX done: {:02x?}", &mut buf[..len as usize]); return Ok((len as usize, RxQuality::new(rssi, snr as i8))); } @@ -193,7 +193,7 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { .clear_irq_status(irq_status) .expect("error clearing irq status"); - trace!("SUGHZ IRQ 0b{=u16:b}, {:?}", irq_status, status); + trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status); if irq_status == 0 { Poll::Pending From 27771e60afa0fe71c5512cee241400502e121b91 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 7 Nov 2022 22:44:20 +0100 Subject: [PATCH 0320/1575] Bake the blob into the firmware by default. --- examples/rpi-pico-w/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 0915ef6be..dbc7761c8 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -44,15 +44,15 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Include the WiFi firmware and Country Locale Matrix (CLM) blobs. - //let fw = include_bytes!("../../../firmware/43439A0.bin"); - //let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); + let fw = include_bytes!("../../../firmware/43439A0.bin"); + let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 - let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; - let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); From 8a81114baf4ffe12ec54e80e342f098c596177d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 7 Nov 2022 22:51:58 +0100 Subject: [PATCH 0321/1575] Update Embassy, nightly, deps. --- Cargo.toml | 2 +- examples/rpi-pico-w/Cargo.toml | 14 +++++++------- examples/rpi-pico-w/src/main.rs | 6 +++--- rust-toolchain.toml | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 30c0da07b..8e1eddc19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,5 @@ cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.2" } +embedded-hal-async = { version = "0.1.0-alpha.3" } num_enum = { version = "0.5.7", default-features = false } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index e82d12eb9..7ba22a69e 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -22,18 +22,18 @@ cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.2" } +embedded-hal-async = { version = "0.1.0-alpha.3" } embedded-io = { version = "0.3.0", features = ["async", "defmt"] } heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "73208d524843ca451b4cbfdb06e35f1b85290f4c" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index dbc7761c8..705c7accb 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -13,7 +13,7 @@ use embassy_rp::gpio::{Flex, Level, Output}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite}; -use embedded_io::asynch::{Read, Write}; +use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -165,7 +165,7 @@ impl SpiBusFlush for MySpi { } impl SpiBusRead for MySpi { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; @@ -195,7 +195,7 @@ impl SpiBusRead for MySpi { } impl SpiBusWrite for MySpi { - type WriteFuture<'a> = impl Future> + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a35a11b82..3e219b0c2 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-09-22" +channel = "nightly-2022-10-25" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", From a3a58e8e4a1f27402c0800356ec5bb5bf39478ec Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 9 Nov 2022 10:04:37 +0100 Subject: [PATCH 0322/1575] Special handling for log and defmt --- embassy-lora/src/stm32wl/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 08bf32f99..6ed63bf7b 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -172,6 +172,10 @@ impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { self.radio.read_buffer(ptr, &mut buf[..len as usize])?; self.radio.set_standby(StandbyClk::Rc)?; + #[cfg(feature = "defmt")] + trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); + + #[cfg(feature = "log")] trace!("RX done: {:02x?}", &mut buf[..len as usize]); return Ok((len as usize, RxQuality::new(rssi, snr as i8))); } From cecd77938c694ff2bad2a259ff64f2f468dcb04a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:14:43 +0100 Subject: [PATCH 0323/1575] Draft: Initial support for I2S with a working example. Co-authored-by: @brainstorm --- embassy-nrf/Cargo.toml | 1 + embassy-nrf/src/chips/nrf52840.rs | 5 + embassy-nrf/src/i2s.rs | 403 ++++++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 2 + examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/i2s.rs | 48 ++++ 6 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 embassy-nrf/src/i2s.rs create mode 100644 examples/nrf/src/bin/i2s.rs diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 67b6bec40..aa1576fd4 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -48,6 +48,7 @@ nrf9160-s = ["_nrf9160"] nrf9160-ns = ["_nrf9160"] gpiote = [] +i2s = [] time-driver-rtc1 = ["_time-driver"] # Features starting with `_` are for internal use only. They're not intended diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4beadfba8..cf800c7b4 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")] @@ -285,6 +288,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; diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs new file mode 100644 index 000000000..0199ac615 --- /dev/null +++ b/embassy-nrf/src/i2s.rs @@ -0,0 +1,403 @@ +#![macro_use] + +//! I2S + +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; +use pac::i2s::config::mcken; + +use crate::{pac, Peripheral}; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; +use crate::gpio::sealed::Pin as _; + +// TODO: Define those in lib.rs somewhere else +// +// I2S EasyDMA MAXCNT bit length = 14 +const MAX_DMA_MAXCNT: u32 = 1 << 14; + +// Limits for Easy DMA - it can only read from data ram +pub const SRAM_LOWER: usize = 0x2000_0000; +pub const SRAM_UPPER: usize = 0x3000_0000; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + BufferTooLong, + BufferZeroLength, + DMABufferNotInDataMemory, + BufferMisaligned, + // TODO: add other error variants. +} + +#[derive(Clone)] +#[non_exhaustive] +pub struct Config { + pub ratio: Ratio, + pub sample_width: SampleWidth, + pub align: Align, + pub format: Format, + pub channels: Channels, +} + +impl Default for Config { + fn default() -> Self { + Self { + ratio: Ratio::_32x, + sample_width: SampleWidth::_16bit, + align: Align::Left, + format: Format::I2S, + channels: Channels::Stereo, + } + } +} + +/// MCK / LRCK ratio. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Ratio { + _32x, + _48x, + _64x, + _96x, + _128x, + _192x, + _256x, + _384x, + _512x, +} + +impl From for u8 { + fn from(variant: Ratio) -> Self { + variant as _ + } +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum SampleWidth { + _8bit, + _16bit, + _24bit, +} + +impl From for u8 { + fn from(variant: SampleWidth) -> Self { + variant as _ + } +} + +/// Alignment of sample within 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, + } + } +} + +/// Enable channels. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Channels { + Stereo, + Left, + Right, +} + +impl From for u8 { + fn from(variant: Channels) -> Self { + variant as _ + } +} + +/// I2S Mode +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Mode { + Controller, + Peripheral, +} + +// /// Master clock generator frequency. +// #[derive(Debug, Eq, PartialEq, Clone, Copy)] +// pub enum MckFreq { +// _32MDiv8 = 0x20000000, +// _32MDiv10 = 0x18000000, +// _32MDiv11 = 0x16000000, +// _32MDiv15 = 0x11000000, +// _32MDiv16 = 0x10000000, +// _32MDiv21 = 0x0C000000, +// _32MDiv23 = 0x0B000000, +// _32MDiv30 = 0x08800000, +// _32MDiv31 = 0x08400000, +// _32MDiv32 = 0x08000000, +// _32MDiv42 = 0x06000000, +// _32MDiv63 = 0x04100000, +// _32MDiv125 = 0x020C0000, +// } + + +/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. +/// +/// For more details about EasyDMA, consult the module documentation. +pub struct I2s<'d, T: Instance> { + output: I2sOutput<'d, T>, + input: I2sInput<'d, T>, +} + +/// Transmitter interface to the UARTE peripheral obtained +/// via [Uarte]::split. +pub struct I2sOutput<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +/// Receiver interface to the UARTE peripheral obtained +/// via [Uarte]::split. +pub struct I2sInput<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> I2s<'d, T> { + /// Create a new I2S + pub fn new( + i2s: impl Peripheral

+ 'd, + // irq: impl Peripheral

+ 'd, + mck: impl Peripheral

+ 'd, + sck: impl Peripheral

+ 'd, + lrck: impl Peripheral

+ 'd, + sdin: impl Peripheral

+ 'd, + sdout: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(mck, sck, lrck, sdin, sdout); + Self::new_inner( + i2s, + // irq, + mck.map_into(), sck.map_into(), lrck.map_into(), sdin.map_into(), sdout.map_into(), config) + } + + fn new_inner( + i2s: impl Peripheral

+ 'd, + // irq: impl Peripheral

+ 'd, + mck: PeripheralRef<'d, AnyPin>, + sck: PeripheralRef<'d, AnyPin>, + lrck: PeripheralRef<'d, AnyPin>, + sdin: PeripheralRef<'d, AnyPin>, + sdout: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!( + i2s, + // irq, + mck, sck, lrck, sdin, sdout); + + let r = T::regs(); + + // TODO get configuration rather than hardcoding ratio, swidth, align, format, channels + + r.config.mcken.write(|w| w.mcken().enabled()); + r.config.mckfreq.write(|w| w.mckfreq()._32mdiv16()); + r.config.ratio.write(|w| w.ratio()._192x()); + r.config.mode.write(|w| w.mode().master()); + r.config.swidth.write(|w| w.swidth()._16bit()); + r.config.align.write(|w| w.align().left()); + r.config.format.write(|w| w.format().i2s()); + r.config.channels.write(|w| w.channels().stereo()); + + r.psel.mck.write(|w| { + unsafe { w.bits(mck.psel_bits()) }; + w.connect().connected() + }); + + r.psel.sck.write(|w| { + unsafe { w.bits(sck.psel_bits()) }; + w.connect().connected() + }); + + r.psel.lrck.write(|w| { + unsafe { w.bits(lrck.psel_bits()) }; + w.connect().connected() + }); + + r.psel.sdin.write(|w| { + unsafe { w.bits(sdin.psel_bits()) }; + w.connect().connected() + }); + + r.psel.sdout.write(|w| { + unsafe { w.bits(sdout.psel_bits()) }; + w.connect().connected() + }); + + r.enable.write(|w| w.enable().enabled()); + + Self { + output: I2sOutput { + _p: unsafe { i2s.clone_unchecked() }, + }, + input: I2sInput { _p: i2s }, + } + } + + /// Enables the I2S module. + #[inline(always)] + pub fn enable(&self) -> &Self { + let r = T::regs(); + r.enable.write(|w| w.enable().enabled()); + self + } + + /// Disables the I2S module. + #[inline(always)] + pub fn disable(&self) -> &Self { + let r = T::regs(); + r.enable.write(|w| w.enable().disabled()); + self + } + + /// Starts I2S transfer. + #[inline(always)] + pub fn start(&self) -> &Self { + let r = T::regs(); + self.enable(); + r.tasks_start.write(|w| unsafe { w.bits(1) }); + self + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub fn stop(&self) -> &Self { + todo!() + } + + /// Enables/disables I2S transmission (TX). + #[inline(always)] + pub fn set_tx_enabled(&self, enabled: bool) -> &Self { + let r = T::regs(); + r.config.txen.write(|w| w.txen().bit(enabled)); + self + } + + /// Enables/disables I2S reception (RX). + #[inline(always)] + pub fn set_rx_enabled(&self, enabled: bool) -> &Self { + let r = T::regs(); + r.config.rxen.write(|w| w.rxen().bit(enabled)); + self + } + + /// Transmits the given `tx_buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + // TODO Define a better interface for the input buffer + #[allow(unused_mut)] + pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { + self.output.tx(ptr, len).await + } +} + +impl<'d, T: Instance> I2sOutput<'d, T> { + /// Transmits the given `tx_buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + // TODO Define a better interface for the input buffer + pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { + if ptr as u32 % 4 != 0 { + return Err(Error::BufferMisaligned); + } + let maxcnt = (len / (core::mem::size_of::() / core::mem::size_of::())) as u32; + if maxcnt > MAX_DMA_MAXCNT { + return Err(Error::BufferTooLong); + } + if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { + return Err(Error::DMABufferNotInDataMemory); + } + + let r = T::regs(); + let _s = T::state(); + + // TODO we can not progress until the last buffer written in TXD.PTR + // has started the transmission. + // We can use some sync primitive from `embassy-sync`. + + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + + Ok(()) + } +} + +pub(crate) mod sealed { + use core::sync::atomic::AtomicU8; + + use embassy_sync::waitqueue::AtomicWaker; + + use super::*; + + pub struct State { + pub input_waker: AtomicWaker, + pub output_waker: AtomicWaker, + pub buffers_refcount: AtomicU8, + } + impl State { + pub const fn new() -> Self { + Self { + input_waker: AtomicWaker::new(), + output_waker: AtomicWaker::new(), + buffers_refcount: AtomicU8::new(0), + } + } + } + + pub trait Instance { + fn regs() -> &'static 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 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 bc70fc2f6..ac797db9b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,6 +74,8 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; +// #[cfg(all(feature = "i2s", feature = "nrf52840"))] +pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; #[cfg(any( diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index c633f82f5..a79044e8e 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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"] } -embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "i2s", "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" diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs new file mode 100644 index 000000000..556f6b2e2 --- /dev/null +++ b/examples/nrf/src/bin/i2s.rs @@ -0,0 +1,48 @@ +// Example inspired by RTIC's I2S demo: https://github.com/nrf-rs/nrf-hal/blob/master/examples/i2s-controller-demo/src/main.rs + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{i2s}; +use {defmt_rtt as _, panic_probe as _}; + +#[repr(align(4))] +pub struct Aligned(T); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let config = i2s::Config::default(); + + let mut i2s = i2s::I2s::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + + let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); + let len = signal_buf.0.len() / 2; + for x in 0..len { + signal_buf.0[2 * x] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; + signal_buf.0[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; + } + + let ptr = &signal_buf.0 as *const i16 as *const u8; + let len = signal_buf.0.len() * core::mem::size_of::(); + + i2s.start(); + i2s.set_tx_enabled(true); + + loop { + i2s.tx(ptr, len).await; + } +} + +fn triangle_wave(x: i32, length: usize, amplitude: i32, phase: i32, periods: i32) -> i32 { + let length = length as i32; + amplitude + - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) + % (2 * amplitude) + - amplitude) + .abs() + - amplitude / 2 +} From 3760b60db382c8c4f8d7067a8d472affa6db928b Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Wed, 9 Nov 2022 21:58:56 +0100 Subject: [PATCH 0324/1575] Make bors grin ;) --- embassy-nrf/src/i2s.rs | 47 +++++++++++++++++++++---------------- examples/nrf/src/bin/i2s.rs | 17 +++++++------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 0199ac615..e0fe6a6eb 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -2,18 +2,19 @@ //! I2S -use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::Poll; +//use core::future::poll_fn; +//use core::sync::atomic::{compiler_fence, Ordering}; +//use core::task::Poll; -use embassy_hal_common::drop::OnDrop; +//use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use pac::i2s::config::mcken; -use crate::{pac, Peripheral}; -use crate::interrupt::{Interrupt, InterruptExt}; -use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::gpio::sealed::Pin as _; +//use crate::pac::i2s::config::mcken; + +//use crate::gpio::sealed::Pin as _; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::Interrupt; +use crate::Peripheral; // TODO: Define those in lib.rs somewhere else // @@ -161,13 +162,12 @@ pub enum Mode { // _32MDiv125 = 0x020C0000, // } - /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. pub struct I2s<'d, T: Instance> { output: I2sOutput<'d, T>, - input: I2sInput<'d, T>, + _input: I2sInput<'d, T>, } /// Transmitter interface to the UARTE peripheral obtained @@ -198,7 +198,13 @@ impl<'d, T: Instance> I2s<'d, T> { Self::new_inner( i2s, // irq, - mck.map_into(), sck.map_into(), lrck.map_into(), sdin.map_into(), sdout.map_into(), config) + mck.map_into(), + sck.map_into(), + lrck.map_into(), + sdin.map_into(), + sdout.map_into(), + config, + ) } fn new_inner( @@ -209,12 +215,12 @@ impl<'d, T: Instance> I2s<'d, T> { lrck: PeripheralRef<'d, AnyPin>, sdin: PeripheralRef<'d, AnyPin>, sdout: PeripheralRef<'d, AnyPin>, - config: Config, + _config: Config, ) -> Self { into_ref!( - i2s, - // irq, - mck, sck, lrck, sdin, sdout); + i2s, // irq, + mck, sck, lrck, sdin, sdout + ); let r = T::regs(); @@ -260,7 +266,7 @@ impl<'d, T: Instance> I2s<'d, T> { output: I2sOutput { _p: unsafe { i2s.clone_unchecked() }, }, - input: I2sInput { _p: i2s }, + _input: I2sInput { _p: i2s }, } } @@ -357,7 +363,7 @@ pub(crate) mod sealed { use embassy_sync::waitqueue::AtomicWaker; - use super::*; + //use super::*; pub struct State { pub input_waker: AtomicWaker, @@ -375,7 +381,7 @@ pub(crate) mod sealed { } pub trait Instance { - fn regs() -> &'static pac::i2s::RegisterBlock; + fn regs() -> &'static crate::pac::i2s::RegisterBlock; fn state() -> &'static State; } } @@ -384,6 +390,8 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { type Interrupt: Interrupt; } +// TODO: Unsure why this macro is flagged as unused by CI when in fact it's used elsewhere? +#[allow(unused_macros)] macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::i2s::sealed::Instance for peripherals::$type { @@ -400,4 +408,3 @@ macro_rules! impl_i2s { } }; } - diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 556f6b2e2..60cde3b65 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -4,9 +4,9 @@ #![no_main] #![feature(type_alias_impl_trait)] -use defmt::*; +//use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{i2s}; +use embassy_nrf::i2s; use {defmt_rtt as _, panic_probe as _}; #[repr(align(4))] @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { let config = i2s::Config::default(); let mut i2s = i2s::I2s::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); - + let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); let len = signal_buf.0.len() / 2; for x in 0..len { @@ -31,18 +31,19 @@ async fn main(_spawner: Spawner) { i2s.start(); i2s.set_tx_enabled(true); - + loop { - i2s.tx(ptr, len).await; + match i2s.tx(ptr, len).await { + Ok(_) => todo!(), + Err(_) => todo!(), + }; } } fn triangle_wave(x: i32, length: usize, amplitude: i32, phase: i32, periods: i32) -> i32 { let length = length as i32; amplitude - - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) - % (2 * amplitude) - - amplitude) + - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) % (2 * amplitude) - amplitude) .abs() - amplitude / 2 } From 356beabc3b11d78612c6958d1cfe542209e43558 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Wed, 9 Nov 2022 22:47:55 +0100 Subject: [PATCH 0325/1575] Apply config --- embassy-nrf/src/i2s.rs | 85 ++++++++++++++++++++++++++++--------- examples/nrf/src/bin/i2s.rs | 2 +- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index e0fe6a6eb..8752dfdef 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -9,11 +9,10 @@ //use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -//use crate::pac::i2s::config::mcken; - //use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; +use crate::pac::i2s::CONFIG; use crate::Peripheral; // TODO: Define those in lib.rs somewhere else @@ -40,7 +39,7 @@ pub enum Error { #[non_exhaustive] pub struct Config { pub ratio: Ratio, - pub sample_width: SampleWidth, + pub swidth: SampleWidth, pub align: Align, pub format: Format, pub channels: Channels, @@ -50,7 +49,7 @@ impl Default for Config { fn default() -> Self { Self { ratio: Ratio::_32x, - sample_width: SampleWidth::_16bit, + swidth: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, channels: Channels::Stereo, @@ -165,7 +164,7 @@ pub enum Mode { /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. -pub struct I2s<'d, T: Instance> { +pub struct I2S<'d, T: Instance> { output: I2sOutput<'d, T>, _input: I2sInput<'d, T>, } @@ -182,7 +181,7 @@ pub struct I2sInput<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> I2s<'d, T> { +impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S pub fn new( i2s: impl Peripheral

+ 'd, @@ -215,25 +214,13 @@ impl<'d, T: Instance> I2s<'d, T> { lrck: PeripheralRef<'d, AnyPin>, sdin: PeripheralRef<'d, AnyPin>, sdout: PeripheralRef<'d, AnyPin>, - _config: Config, + config: Config, ) -> Self { - into_ref!( - i2s, // irq, - mck, sck, lrck, sdin, sdout - ); + into_ref!(i2s, /* irq, */ mck, sck, lrck, sdin, sdout); let r = T::regs(); - // TODO get configuration rather than hardcoding ratio, swidth, align, format, channels - - r.config.mcken.write(|w| w.mcken().enabled()); - r.config.mckfreq.write(|w| w.mckfreq()._32mdiv16()); - r.config.ratio.write(|w| w.ratio()._192x()); - r.config.mode.write(|w| w.mode().master()); - r.config.swidth.write(|w| w.swidth()._16bit()); - r.config.align.write(|w| w.align().left()); - r.config.format.write(|w| w.format().i2s()); - r.config.channels.write(|w| w.channels().stereo()); + Self::apply_config(&r.config, &config); r.psel.mck.write(|w| { unsafe { w.bits(mck.psel_bits()) }; @@ -325,6 +312,62 @@ impl<'d, T: Instance> I2s<'d, T> { pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { self.output.tx(ptr, len).await } + + fn apply_config(c: &CONFIG, config: &Config) { + // TODO support slave too + c.mcken.write(|w| w.mcken().enabled()); + c.mckfreq.write(|w| w.mckfreq()._32mdiv16()); + c.mode.write(|w| w.mode().master()); + + c.ratio.write(|w| { + let ratio = w.ratio(); + match config.ratio { + Ratio::_32x => ratio._32x(), + Ratio::_48x => ratio._48x(), + Ratio::_64x => ratio._64x(), + Ratio::_96x => ratio._96x(), + Ratio::_128x => ratio._128x(), + Ratio::_192x => ratio._192x(), + Ratio::_256x => ratio._256x(), + Ratio::_384x => ratio._384x(), + Ratio::_512x => ratio._512x(), + } + }); + + c.swidth.write(|w| { + let swidth = w.swidth(); + match config.swidth { + SampleWidth::_8bit => swidth._8bit(), + SampleWidth::_16bit => swidth._16bit(), + SampleWidth::_24bit => swidth._24bit(), + } + }); + + c.align.write(|w| { + let align = w.align(); + match config.align { + Align::Left => align.left(), + Align::Right => align.right(), + } + }); + + c.format.write(|w| { + let format = w.format(); + match config.format { + Format::I2S => format.i2s(), + Format::Aligned => format.aligned(), + } + }); + + c.channels.write(|w| { + let channels = w.channels(); + match config.channels { + Channels::Stereo => channels.stereo(), + Channels::Left => channels.left(), + Channels::Right => channels.right(), + } + }); + } } impl<'d, T: Instance> I2sOutput<'d, T> { diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 60cde3b65..a395c7141 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let config = i2s::Config::default(); - let mut i2s = i2s::I2s::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + let mut i2s = i2s::I2S::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); let len = signal_buf.0.len() / 2; From 5a64bf651c66f2da16cd3ae20ed9ba2489f40d7a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 10 Nov 2022 00:10:42 +0100 Subject: [PATCH 0326/1575] Buffer trait. Simpler config. --- embassy-nrf/src/i2s.rs | 122 +++++++++++++++++++----------------- examples/nrf/src/bin/i2s.rs | 7 +-- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 8752dfdef..3f5491ee3 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -284,7 +284,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Stops the I2S transfer and waits until it has stopped. #[inline(always)] - pub fn stop(&self) -> &Self { + pub async fn stop(&self) -> &Self { todo!() } @@ -307,10 +307,12 @@ impl<'d, T: Instance> I2S<'d, T> { /// Transmits the given `tx_buffer`. /// Buffer address must be 4 byte aligned and located in RAM. /// Returns a value that represents the in-progress DMA transfer. - // TODO Define a better interface for the input buffer #[allow(unused_mut)] - pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { - self.output.tx(ptr, len).await + pub async fn tx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + self.output.tx(buffer).await } fn apply_config(c: &CONFIG, config: &Config) { @@ -319,54 +321,12 @@ impl<'d, T: Instance> I2S<'d, T> { c.mckfreq.write(|w| w.mckfreq()._32mdiv16()); c.mode.write(|w| w.mode().master()); - c.ratio.write(|w| { - let ratio = w.ratio(); - match config.ratio { - Ratio::_32x => ratio._32x(), - Ratio::_48x => ratio._48x(), - Ratio::_64x => ratio._64x(), - Ratio::_96x => ratio._96x(), - Ratio::_128x => ratio._128x(), - Ratio::_192x => ratio._192x(), - Ratio::_256x => ratio._256x(), - Ratio::_384x => ratio._384x(), - Ratio::_512x => ratio._512x(), - } - }); - - c.swidth.write(|w| { - let swidth = w.swidth(); - match config.swidth { - SampleWidth::_8bit => swidth._8bit(), - SampleWidth::_16bit => swidth._16bit(), - SampleWidth::_24bit => swidth._24bit(), - } - }); - - c.align.write(|w| { - let align = w.align(); - match config.align { - Align::Left => align.left(), - Align::Right => align.right(), - } - }); - - c.format.write(|w| { - let format = w.format(); - match config.format { - Format::I2S => format.i2s(), - Format::Aligned => format.aligned(), - } - }); - - c.channels.write(|w| { - let channels = w.channels(); - match config.channels { - Channels::Stereo => channels.stereo(), - Channels::Left => channels.left(), - Channels::Right => channels.right(), - } - }); + c.ratio.write(|w| unsafe { w.ratio().bits(config.ratio.into()) }); + c.swidth.write(|w| unsafe { w.swidth().bits(config.swidth.into()) }); + c.align.write(|w| w.align().bit(config.align.into())); + c.format.write(|w| w.format().bit(config.format.into())); + c.channels + .write(|w| unsafe { w.channels().bits(config.channels.into()) }); } } @@ -374,18 +334,23 @@ impl<'d, T: Instance> I2sOutput<'d, T> { /// Transmits the given `tx_buffer`. /// Buffer address must be 4 byte aligned and located in RAM. /// Returns a value that represents the in-progress DMA transfer. - // TODO Define a better interface for the input buffer - pub async fn tx(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { + pub async fn tx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let ptr = buffer.bytes_ptr(); + let len = buffer.bytes_len(); + if ptr as u32 % 4 != 0 { return Err(Error::BufferMisaligned); } - let maxcnt = (len / (core::mem::size_of::() / core::mem::size_of::())) as u32; - if maxcnt > MAX_DMA_MAXCNT { - return Err(Error::BufferTooLong); - } if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { return Err(Error::DMABufferNotInDataMemory); } + let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + if maxcnt > MAX_DMA_MAXCNT { + return Err(Error::BufferTooLong); + } let r = T::regs(); let _s = T::state(); @@ -401,6 +366,47 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } } +pub trait Buffer: Sized { + fn bytes_ptr(&self) -> *const u8; + fn bytes_len(&self) -> usize; +} + +impl Buffer for &[u8] { + #[inline] + fn bytes_ptr(&self) -> *const u8 { + self.as_ptr() + } + + #[inline] + fn bytes_len(&self) -> usize { + self.len() + } +} + +impl Buffer for &[i16] { + #[inline] + fn bytes_ptr(&self) -> *const u8 { + self.as_ptr() as *const u8 + } + + #[inline] + fn bytes_len(&self) -> usize { + self.len() * core::mem::size_of::() + } +} + +impl Buffer for &[i32] { + #[inline] + fn bytes_ptr(&self) -> *const u8 { + self.as_ptr() as *const u8 + } + + #[inline] + fn bytes_len(&self) -> usize { + self.len() * core::mem::size_of::() + } +} + pub(crate) mod sealed { use core::sync::atomic::AtomicU8; diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index a395c7141..e8ddb4a40 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -26,14 +26,11 @@ async fn main(_spawner: Spawner) { signal_buf.0[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; } - let ptr = &signal_buf.0 as *const i16 as *const u8; - let len = signal_buf.0.len() * core::mem::size_of::(); - - i2s.start(); i2s.set_tx_enabled(true); + i2s.start(); loop { - match i2s.tx(ptr, len).await { + match i2s.tx(signal_buf.0.as_slice()).await { Ok(_) => todo!(), Err(_) => todo!(), }; From f22f36f51ba4466dd15df78df0ad86ac96f9051c Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 10 Nov 2022 00:24:49 +0100 Subject: [PATCH 0327/1575] Add input rx --- embassy-nrf/src/i2s.rs | 59 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 3f5491ee3..fb6fa4bdf 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -166,7 +166,7 @@ pub enum Mode { /// For more details about EasyDMA, consult the module documentation. pub struct I2S<'d, T: Instance> { output: I2sOutput<'d, T>, - _input: I2sInput<'d, T>, + input: I2sInput<'d, T>, } /// Transmitter interface to the UARTE peripheral obtained @@ -253,7 +253,7 @@ impl<'d, T: Instance> I2S<'d, T> { output: I2sOutput { _p: unsafe { i2s.clone_unchecked() }, }, - _input: I2sInput { _p: i2s }, + input: I2sInput { _p: i2s }, } } @@ -284,7 +284,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Stops the I2S transfer and waits until it has stopped. #[inline(always)] - pub async fn stop(&self) -> &Self { + pub async fn stop(&self) { todo!() } @@ -304,10 +304,8 @@ impl<'d, T: Instance> I2S<'d, T> { self } - /// Transmits the given `tx_buffer`. + /// Transmits the given `buffer`. /// Buffer address must be 4 byte aligned and located in RAM. - /// Returns a value that represents the in-progress DMA transfer. - #[allow(unused_mut)] pub async fn tx(&mut self, buffer: B) -> Result<(), Error> where B: Buffer, @@ -315,6 +313,15 @@ impl<'d, T: Instance> I2S<'d, T> { self.output.tx(buffer).await } + /// Receives data into the given `buffer` until it's filled. + /// Buffer address must be 4 byte aligned and located in RAM. + pub async fn rx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + self.input.rx(buffer).await + } + fn apply_config(c: &CONFIG, config: &Config) { // TODO support slave too c.mcken.write(|w| w.mcken().enabled()); @@ -331,9 +338,9 @@ impl<'d, T: Instance> I2S<'d, T> { } impl<'d, T: Instance> I2sOutput<'d, T> { - /// Transmits the given `tx_buffer`. + /// Transmits the given `buffer`. /// Buffer address must be 4 byte aligned and located in RAM. - /// Returns a value that represents the in-progress DMA transfer. + #[allow(unused_mut)] pub async fn tx(&mut self, buffer: B) -> Result<(), Error> where B: Buffer, @@ -366,6 +373,42 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } } +impl<'d, T: Instance> I2sInput<'d, T> { + /// Receives into the given `buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + #[allow(unused_mut)] + pub async fn rx(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let ptr = buffer.bytes_ptr(); + let len = buffer.bytes_len(); + + if ptr as u32 % 4 != 0 { + return Err(Error::BufferMisaligned); + } + if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { + return Err(Error::DMABufferNotInDataMemory); + } + let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + if maxcnt > MAX_DMA_MAXCNT { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + let _s = T::state(); + + // TODO we can not progress until the last buffer written in RXD.PTR + // has started the transmission. + // We can use some sync primitive from `embassy-sync`. + + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + + Ok(()) + } +} + pub trait Buffer: Sized { fn bytes_ptr(&self) -> *const u8; fn bytes_len(&self) -> usize; From dbe97b409891ed865d07edc29cec4d8af13e55d9 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 10 Nov 2022 14:37:42 +0100 Subject: [PATCH 0328/1575] Adapted nvmc so it can be used for all nrf targets --- embassy-nrf/src/chips/nrf5340_app.rs | 5 ++ embassy-nrf/src/chips/nrf5340_net.rs | 5 ++ embassy-nrf/src/chips/nrf9160.rs | 5 ++ embassy-nrf/src/lib.rs | 1 - embassy-nrf/src/nvmc.rs | 70 ++++++++++++++++++++++++---- 5 files changed, 75 insertions(+), 11 deletions(-) diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 7845d4a8e..e20edcdf3 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -213,6 +213,8 @@ pub mod pac { pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; +pub const FLASH_SIZE: usize = 1024 * 1024; + embassy_hal_common::peripherals! { // USB USBD, @@ -224,6 +226,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // UARTE, TWI & SPI UARTETWISPI0, UARTETWISPI1, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index ae136e09d..8c292e52b 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -104,6 +104,8 @@ pub mod pac { pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; +pub const FLASH_SIZE: usize = 256 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -112,6 +114,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // UARTE, TWI & SPI UARTETWISPI0, UARTETWISPI1, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index b5a53ed80..5c00b65a2 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -164,6 +164,8 @@ pub mod pac { pub const EASY_DMA_SIZE: usize = (1 << 13) - 1; pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; +pub const FLASH_SIZE: usize = 1024 * 1024; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -172,6 +174,9 @@ embassy_hal_common::peripherals! { // WDT WDT, + // NVMC + NVMC, + // UARTE, TWI & SPI UARTETWISPI0, UARTETWISPI1, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f6..cac55071c 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,7 +74,6 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; #[cfg(any( feature = "nrf52810", diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 6f66f7a78..ba6a59129 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -10,8 +10,12 @@ use embedded_storage::nor_flash::{ use crate::peripherals::NVMC; use crate::{pac, Peripheral}; +#[cfg(not(feature = "_nrf5340-net"))] /// Erase size of NVMC flash in bytes. pub const PAGE_SIZE: usize = 4096; +#[cfg(feature = "_nrf5340-net")] +/// Erase size of NVMC flash in bytes. +pub const PAGE_SIZE: usize = 2048; /// Size of NVMC flash in bytes. pub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE; @@ -55,6 +59,56 @@ impl<'d> Nvmc<'d> { let p = Self::regs(); while p.ready.read().ready().is_busy() {} } + + #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] + fn wait_ready_write(&mut self) { + self.wait_ready(); + } + + #[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] + fn wait_ready_write(&mut self) { + let p = Self::regs(); + while p.readynext.read().readynext().is_busy() {} + } + + #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] + fn erase_page(&mut self, page: u32) { + Self::regs().erasepage().write(|w| unsafe { w.bits(page) }); + } + + #[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] + fn erase_page(&mut self, page: u32) { + #[cfg(not(feature = "_nrf5340-net"))] + const FLASH_START_ADDR: u32 = 0; + #[cfg(feature = "_nrf5340-net")] + const FLASH_START_ADDR: u32 = 0x100_0000; + + let first_page_word = (FLASH_START_ADDR + page * PAGE_SIZE as u32) as *mut u32; + unsafe { + first_page_word.write_volatile(0xFFFF_FFFF); + } + } + + fn enable_erase(&self) { + #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + Self::regs().config.write(|w| w.wen().een()); + #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + Self::regs().configns.write(|w| w.wen().een()); + } + + fn enable_read(&self) { + #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + Self::regs().config.write(|w| w.wen().ren()); + #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + Self::regs().configns.write(|w| w.wen().ren()); + } + + fn enable_write(&self) { + #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + Self::regs().config.write(|w| w.wen().wen()); + #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + Self::regs().configns.write(|w| w.wen().wen()); + } } impl<'d> MultiwriteNorFlash for Nvmc<'d> {} @@ -93,17 +147,15 @@ impl<'d> NorFlash for Nvmc<'d> { return Err(Error::Unaligned); } - let p = Self::regs(); - - p.config.write(|w| w.wen().een()); + self.enable_erase(); self.wait_ready(); for page in (from..to).step_by(PAGE_SIZE) { - p.erasepage().write(|w| unsafe { w.bits(page) }); + self.erase_page(page); self.wait_ready(); } - p.config.reset(); + self.enable_read(); self.wait_ready(); Ok(()) @@ -117,9 +169,7 @@ impl<'d> NorFlash for Nvmc<'d> { return Err(Error::Unaligned); } - let p = Self::regs(); - - p.config.write(|w| w.wen().wen()); + self.enable_write(); self.wait_ready(); unsafe { @@ -129,11 +179,11 @@ impl<'d> NorFlash for Nvmc<'d> { for i in 0..words { let w = ptr::read_unaligned(p_src.add(i)); ptr::write_volatile(p_dst.add(i), w); - self.wait_ready(); + self.wait_ready_write(); } } - p.config.reset(); + self.enable_read(); self.wait_ready(); Ok(()) From cbc97758e391d981d497bf37fe63b5a35913c115 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 9 Nov 2022 14:10:46 +0100 Subject: [PATCH 0329/1575] stm32: Fix watchdog division by zero for 256 prescaler, add watchdog example for H7 --- embassy-stm32/src/wdg/mod.rs | 8 ++++---- examples/stm32h7/src/bin/wdg.rs | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 examples/stm32h7/src/bin/wdg.rs diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 85176eefc..92b9a5ca8 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/examples/stm32h7/src/bin/wdg.rs b/examples/stm32h7/src/bin/wdg.rs new file mode 100644 index 000000000..2b0301aad --- /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() }; + } +} From 99682d313b54409c33d471e066ef6aba9f628a68 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Tue, 25 Oct 2022 09:47:30 +0200 Subject: [PATCH 0330/1575] Disable MMC interrupts MMC interrupts can cause firmware hangup - refer to: https://github.com/stm32-rs/stm32h7xx-hal/issues/275 for more information --- embassy-stm32/src/eth/v2/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index a81ee1183..5b76d1e7f 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)); From 6e1120e17e384a04cd3aef58a1467a4a0a862ba5 Mon Sep 17 00:00:00 2001 From: Sijmen Woutersen Date: Sun, 25 Sep 2022 20:10:11 +0200 Subject: [PATCH 0331/1575] riscv support --- embassy-executor/Cargo.toml | 1 + embassy-executor/src/arch/riscv32.rs | 15 +-------------- embassy-macros/Cargo.toml | 1 + embassy-macros/src/macros/main.rs | 15 +++++++++++++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d0f51646d..e6b4c596f 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -25,6 +25,7 @@ flavors = [ default = [] std = ["embassy-macros/std", "critical-section/std"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"] +riscv = ["embassy-macros/riscv"] # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 2a4b006da..e095c0ee0 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -54,20 +54,7 @@ impl Executor { loop { unsafe { self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - //will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - core::arch::asm!("wfi"); - } - }); - // if an interrupt occurred while waiting, it will be serviced here + core::arch::asm!("wfi"); } } } diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 91d5ec8a3..c5ed3b5da 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -16,6 +16,7 @@ proc-macro = true [features] std = [] wasm = [] +riscv = [] # Enabling this cause interrupt::take! to require embassy-executor rtos-trace-interrupt = [] diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index afe9bd3e2..54806847c 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -45,7 +45,7 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result ! { let mut executor = ::embassy_executor::Executor::new(); @@ -57,13 +57,24 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result ! { 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"), feature = "riscv"))] + let main = 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)); }) From 4a2e810485a996014999ad630a604c3fe4fc81a4 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 10 Nov 2022 23:13:01 +0100 Subject: [PATCH 0332/1575] Restrict to pacs supporting i2s --- embassy-nrf/Cargo.toml | 1 - embassy-nrf/src/lib.rs | 6 +++++- examples/nrf/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index aa1576fd4..67b6bec40 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -48,7 +48,6 @@ nrf9160-s = ["_nrf9160"] nrf9160-ns = ["_nrf9160"] gpiote = [] -i2s = [] time-driver-rtc1 = ["_time-driver"] # Features starting with `_` are for internal use only. They're not intended diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index ac797db9b..95bd5831b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,7 +74,11 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -// #[cfg(all(feature = "i2s", feature = "nrf52840"))] +#[cfg(any( + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", +))] pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a79044e8e..c633f82f5 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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"] } -embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "i2s", "unstable-pac"] } +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" From 10e3c3f2ec358da6d81f2bb9c05936c2ab6da567 Mon Sep 17 00:00:00 2001 From: Roman Valls Guimera Date: Fri, 11 Nov 2022 23:49:20 +0100 Subject: [PATCH 0333/1575] Cargo fmt --- embassy-nrf/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 95bd5831b..bdb911ecd 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,11 +74,7 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(any( - feature = "nrf52832", - feature = "nrf52833", - feature = "nrf52840", -))] +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840",))] pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; From e70ae71eccf370c42043fa323002c14f94a16679 Mon Sep 17 00:00:00 2001 From: Sijmen Woutersen Date: Sat, 12 Nov 2022 10:56:49 +0100 Subject: [PATCH 0334/1575] restore SIGNAL_WORK_THREAD_MODE --- embassy-executor/src/arch/riscv32.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index e095c0ee0..76eb8b114 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -54,7 +54,12 @@ impl Executor { loop { unsafe { self.inner.poll(); - core::arch::asm!("wfi"); + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + if !SIGNAL_WORK_THREAD_MODE.fetch_and(false, Ordering::SeqCst) { + core::arch::asm!("wfi"); + } } } } From 122a31d20877005c7201d4e7c98da5544666dd1d Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 12 Nov 2022 18:48:57 +0100 Subject: [PATCH 0335/1575] Interrupts, async, sine oscillator --- embassy-nrf/src/i2s.rs | 298 ++++++++++++++++++++++++++---------- embassy-nrf/src/lib.rs | 2 +- examples/nrf/src/bin/i2s.rs | 132 +++++++++++++--- 3 files changed, 330 insertions(+), 102 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index fb6fa4bdf..f5e36f0dd 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -2,17 +2,18 @@ //! I2S -//use core::future::poll_fn; -//use core::sync::atomic::{compiler_fence, Ordering}; -//use core::task::Poll; +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; -//use embassy_hal_common::drop::OnDrop; +use embassy_cortex_m::interrupt::{InterruptExt, Priority}; +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; //use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; -use crate::pac::i2s::CONFIG; +use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL}; use crate::Peripheral; // TODO: Define those in lib.rs somewhere else @@ -35,10 +36,39 @@ pub enum Error { // TODO: add other error variants. } +pub const MODE_MASTER_8000: Mode = Mode::Master { + freq: MckFreq::_32MDiv125, + ratio: Ratio::_32x, +}; // error = 0 +pub const MODE_MASTER_11025: Mode = Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_192x, +}; // error = 86 +pub const MODE_MASTER_16000: Mode = Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_96x, +}; // error = 127 +pub const MODE_MASTER_22050: Mode = Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_96x, +}; // error = 172 +pub const MODE_MASTER_32000: Mode = Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_48x, +}; // error = 254 +pub const MODE_MASTER_44100: Mode = Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_48x, +}; // error = 344 +pub const MODE_MASTER_48000: Mode = Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_32x, +}; // error = 381 + #[derive(Clone)] #[non_exhaustive] pub struct Config { - pub ratio: Ratio, + pub mode: Mode, pub swidth: SampleWidth, pub align: Align, pub format: Format, @@ -48,7 +78,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - ratio: Ratio::_32x, + mode: MODE_MASTER_32000, swidth: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, @@ -57,6 +87,66 @@ impl Default for Config { } } +/// I2S Mode +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Mode { + Master { freq: MckFreq, ratio: Ratio }, + Slave, +} + +impl Mode { + pub fn sample_rate(&self) -> Option { + match self { + Mode::Master { freq, ratio } => Some(freq.to_frequency() / ratio.to_divisor()), + Mode::Slave => None, + } + } +} + +/// 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: &[u32] = &[ + 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, + 0x08000000, 0x06000000, 0x04100000, 0x020C0000, + ]; + + const FREQUENCIES: &[u32] = &[ + 4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936, + 256000, + ]; + + pub fn to_register_value(&self) -> u32 { + Self::REGISTER_VALUES[usize::from(*self)] + } + + pub fn to_frequency(&self) -> u32 { + Self::FREQUENCIES[usize::from(*self)] + } +} + +impl From for usize { + fn from(variant: MckFreq) -> Self { + variant as _ + } +} + /// MCK / LRCK ratio. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Ratio { @@ -71,6 +161,14 @@ pub enum Ratio { _512x, } +impl Ratio { + const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; + + pub fn to_divisor(&self) -> u32 { + Self::RATIOS[u8::from(*self) as usize] + } +} + impl From for u8 { fn from(variant: Ratio) -> Self { variant as _ @@ -136,31 +234,6 @@ impl From for u8 { } } -/// I2S Mode -#[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub enum Mode { - Controller, - Peripheral, -} - -// /// Master clock generator frequency. -// #[derive(Debug, Eq, PartialEq, Clone, Copy)] -// pub enum MckFreq { -// _32MDiv8 = 0x20000000, -// _32MDiv10 = 0x18000000, -// _32MDiv11 = 0x16000000, -// _32MDiv15 = 0x11000000, -// _32MDiv16 = 0x10000000, -// _32MDiv21 = 0x0C000000, -// _32MDiv23 = 0x0B000000, -// _32MDiv30 = 0x08800000, -// _32MDiv31 = 0x08400000, -// _32MDiv32 = 0x08000000, -// _32MDiv42 = 0x06000000, -// _32MDiv63 = 0x04100000, -// _32MDiv125 = 0x020C0000, -// } - /// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. @@ -185,7 +258,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S pub fn new( i2s: impl Peripheral

+ 'd, - // irq: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, mck: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, @@ -196,7 +269,7 @@ impl<'d, T: Instance> I2S<'d, T> { into_ref!(mck, sck, lrck, sdin, sdout); Self::new_inner( i2s, - // irq, + irq, mck.map_into(), sck.map_into(), lrck.map_into(), @@ -208,7 +281,7 @@ impl<'d, T: Instance> I2S<'d, T> { fn new_inner( i2s: impl Peripheral

+ 'd, - // irq: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, mck: PeripheralRef<'d, AnyPin>, sck: PeripheralRef<'d, AnyPin>, lrck: PeripheralRef<'d, AnyPin>, @@ -216,36 +289,12 @@ impl<'d, T: Instance> I2S<'d, T> { sdout: PeripheralRef<'d, AnyPin>, config: Config, ) -> Self { - into_ref!(i2s, /* irq, */ mck, sck, lrck, sdin, sdout); + into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout); let r = T::regs(); - Self::apply_config(&r.config, &config); - - r.psel.mck.write(|w| { - unsafe { w.bits(mck.psel_bits()) }; - w.connect().connected() - }); - - r.psel.sck.write(|w| { - unsafe { w.bits(sck.psel_bits()) }; - w.connect().connected() - }); - - r.psel.lrck.write(|w| { - unsafe { w.bits(lrck.psel_bits()) }; - w.connect().connected() - }); - - r.psel.sdin.write(|w| { - unsafe { w.bits(sdin.psel_bits()) }; - w.connect().connected() - }); - - r.psel.sdout.write(|w| { - unsafe { w.bits(sdout.psel_bits()) }; - w.connect().connected() - }); + Self::select_pins(&r.psel, mck, sck, lrck, sdin, sdout); + Self::setup_interrupt(irq, r); r.enable.write(|w| w.enable().enabled()); @@ -322,19 +371,87 @@ impl<'d, T: Instance> I2S<'d, T> { self.input.rx(buffer).await } - fn apply_config(c: &CONFIG, config: &Config) { - // TODO support slave too - c.mcken.write(|w| w.mcken().enabled()); - c.mckfreq.write(|w| w.mckfreq()._32mdiv16()); - c.mode.write(|w| w.mode().master()); + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_txptrupd.read().bits() != 0 { + s.tx_waker.wake(); + r.intenclr.write(|w| w.txptrupd().clear()); + } + + if r.events_rxptrupd.read().bits() != 0 { + s.rx_waker.wake(); + r.intenclr.write(|w| w.rxptrupd().clear()); + } + } + + fn apply_config(c: &CONFIG, config: &Config) { + match config.mode { + Mode::Master { 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.into()) }); + } + Mode::Slave => { + c.mode.write(|w| w.mode().slave()); + } + }; - c.ratio.write(|w| unsafe { w.ratio().bits(config.ratio.into()) }); c.swidth.write(|w| unsafe { w.swidth().bits(config.swidth.into()) }); c.align.write(|w| w.align().bit(config.align.into())); c.format.write(|w| w.format().bit(config.format.into())); c.channels .write(|w| unsafe { w.channels().bits(config.channels.into()) }); } + + fn select_pins( + psel: &PSEL, + mck: PeripheralRef<'d, AnyPin>, + sck: PeripheralRef<'d, AnyPin>, + lrck: PeripheralRef<'d, AnyPin>, + sdin: PeripheralRef<'d, AnyPin>, + sdout: PeripheralRef<'d, AnyPin>, + ) { + psel.mck.write(|w| { + unsafe { w.bits(mck.psel_bits()) }; + w.connect().connected() + }); + + psel.sck.write(|w| { + unsafe { w.bits(sck.psel_bits()) }; + w.connect().connected() + }); + + psel.lrck.write(|w| { + unsafe { w.bits(lrck.psel_bits()) }; + w.connect().connected() + }); + + psel.sdin.write(|w| { + unsafe { w.bits(sdin.psel_bits()) }; + w.connect().connected() + }); + + psel.sdout.write(|w| { + unsafe { w.bits(sdout.psel_bits()) }; + w.connect().connected() + }); + } + + fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) { + irq.set_handler(Self::on_interrupt); + irq.set_priority(Priority::P1); // TODO review priorities + irq.unpend(); + irq.enable(); + + r.intenclr.write(|w| w.rxptrupd().clear()); + r.intenclr.write(|w| w.txptrupd().clear()); + r.events_rxptrupd.reset(); + r.events_txptrupd.reset(); + } } impl<'d, T: Instance> I2sOutput<'d, T> { @@ -360,15 +477,40 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } let r = T::regs(); - let _s = T::state(); + let s = T::state(); - // TODO we can not progress until the last buffer written in TXD.PTR - // has started the transmission. - // We can use some sync primitive from `embassy-sync`. + let drop = OnDrop::new(move || { + trace!("write drop: stopping"); + + r.intenclr.write(|w| w.txptrupd().clear()); + r.events_txptrupd.reset(); + r.config.txen.write(|w| w.txen().disabled()); + + // TX is stopped almost instantly, spinning is fine. + while r.events_txptrupd.read().bits() == 0 {} + trace!("write drop: stopped"); + }); r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + r.intenset.write(|w| w.txptrupd().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + s.tx_waker.register(cx.waker()); + if r.events_txptrupd.read().bits() != 0 { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + compiler_fence(Ordering::SeqCst); + drop.defuse(); + Ok(()) } } @@ -451,23 +593,19 @@ impl Buffer for &[i32] { } pub(crate) mod sealed { - use core::sync::atomic::AtomicU8; - use embassy_sync::waitqueue::AtomicWaker; //use super::*; pub struct State { - pub input_waker: AtomicWaker, - pub output_waker: AtomicWaker, - pub buffers_refcount: AtomicU8, + pub rx_waker: AtomicWaker, + pub tx_waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { - input_waker: AtomicWaker::new(), - output_waker: AtomicWaker::new(), - buffers_refcount: AtomicU8::new(0), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), } } } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bdb911ecd..dc018e08e 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -74,7 +74,7 @@ pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] pub mod gpiote; -#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840",))] +#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod nvmc; diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index e8ddb4a40..53ccb3b85 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -4,43 +4,133 @@ #![no_main] #![feature(type_alias_impl_trait)] -//use defmt::*; +use core::f32::consts::PI; + +use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s; +use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000}; +use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[repr(align(4))] -pub struct Aligned(T); +pub struct AlignedBuffer(T); + +impl AsRef for AlignedBuffer { + fn as_ref(&self) -> &T { + &self.0 + } +} + +impl AsMut for AlignedBuffer { + fn as_mut(&mut self) -> &mut T { + &mut self.0 + } +} #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let config = i2s::Config::default(); + let mut config = i2s::Config::default(); + // config.mode = MODE_MASTER_16000; + config.mode = Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_256x, + }; // 12500 Hz + let sample_rate = config.mode.sample_rate().expect("I2S Master"); + let inv_sample_rate = 1.0 / sample_rate as f32; - let mut i2s = i2s::I2S::new(p.I2S, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + info!("Sample rate: {}", sample_rate); - let mut signal_buf: Aligned<[i16; 32]> = Aligned([0i16; 32]); - let len = signal_buf.0.len() / 2; - for x in 0..len { - signal_buf.0[2 * x] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; - signal_buf.0[2 * x + 1] = triangle_wave(x as i32, len, 2048, 0, 1) as i16; - } + let irq = interrupt::take!(I2S); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + + const BUF_SAMPLES: usize = 250; + const BUF_SIZE: usize = BUF_SAMPLES * 2; + let mut buf = AlignedBuffer([0i16; BUF_SIZE]); + + let mut carrier = SineOsc::new(); + carrier.set_frequency(300.0, inv_sample_rate); + + let mut modulator = SineOsc::new(); + modulator.set_frequency(0.01, inv_sample_rate); + modulator.set_amplitude(0.2); i2s.set_tx_enabled(true); i2s.start(); loop { - match i2s.tx(signal_buf.0.as_slice()).await { - Ok(_) => todo!(), - Err(_) => todo!(), - }; + for sample in buf.as_mut().chunks_mut(2) { + let signal = carrier.generate(); + // let modulation = bipolar_to_unipolar(modulator.generate()); + // carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate); + // carrier.set_amplitude((modulation); + let value = (i16::MAX as f32 * signal) as i16; + sample[0] = value; + sample[1] = value; + // info!("{}", signal); + } + + if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { + error!("{}", err); + } } } -fn triangle_wave(x: i32, length: usize, amplitude: i32, phase: i32, periods: i32) -> i32 { - let length = length as i32; - amplitude - - ((2 * periods * (x + phase + length / (4 * periods)) * amplitude / length) % (2 * amplitude) - amplitude) - .abs() - - amplitude / 2 +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 } From d2e8794f29d3d0afef7a6bc610b2ee4a4d680643 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sun, 13 Nov 2022 01:41:32 +0100 Subject: [PATCH 0336/1575] Investigating discontinuities in the signal --- embassy-nrf/src/i2s.rs | 68 ++++++++++++++++++++++++++----------- examples/nrf/src/bin/i2s.rs | 27 ++++++++++----- 2 files changed, 66 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index f5e36f0dd..9a8f29e78 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -371,21 +371,6 @@ impl<'d, T: Instance> I2S<'d, T> { self.input.rx(buffer).await } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_txptrupd.read().bits() != 0 { - s.tx_waker.wake(); - r.intenclr.write(|w| w.txptrupd().clear()); - } - - if r.events_rxptrupd.read().bits() != 0 { - s.rx_waker.wake(); - r.intenclr.write(|w| w.rxptrupd().clear()); - } - } - fn apply_config(c: &CONFIG, config: &Config) { match config.mode { Mode::Master { freq, ratio } => { @@ -443,14 +428,36 @@ impl<'d, T: Instance> I2S<'d, T> { fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) { irq.set_handler(Self::on_interrupt); - irq.set_priority(Priority::P1); // TODO review priorities + // irq.set_priority(Priority::P1); // TODO review priorities irq.unpend(); irq.enable(); r.intenclr.write(|w| w.rxptrupd().clear()); r.intenclr.write(|w| w.txptrupd().clear()); + r.events_rxptrupd.reset(); r.events_txptrupd.reset(); + + r.intenset.write(|w| w.rxptrupd().set()); + r.intenset.write(|w| w.txptrupd().set()); + } + + fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let s = T::state(); + + if r.events_txptrupd.read().bits() != 0 { + trace!("[{}] INT", s.seq.load(Ordering::Relaxed)); + s.tx_waker.wake(); + r.intenclr.write(|w| w.txptrupd().clear()); + } + + if r.events_rxptrupd.read().bits() != 0 { + s.rx_waker.wake(); + r.intenclr.write(|w| w.rxptrupd().clear()); + } + + s.overruns.fetch_add(1, Ordering::Relaxed); } } @@ -479,6 +486,12 @@ impl<'d, T: Instance> I2sOutput<'d, T> { let r = T::regs(); let s = T::state(); + let seq = s.seq.fetch_add(1, Ordering::Relaxed); + if r.events_txptrupd.read().bits() != 0 && seq > 0 { + info!("XRUN!"); + loop {} + } + let drop = OnDrop::new(move || { trace!("write drop: stopping"); @@ -491,18 +504,26 @@ impl<'d, T: Instance> I2sOutput<'d, T> { trace!("write drop: stopped"); }); + trace!("[{}] PTR", s.seq.load(Ordering::Relaxed)); r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - r.intenset.write(|w| w.txptrupd().set()); - compiler_fence(Ordering::SeqCst); poll_fn(|cx| { s.tx_waker.register(cx.waker()); - if r.events_txptrupd.read().bits() != 0 { + if r.events_txptrupd.read().bits() != 0 || seq == 0 { + trace!("[{}] POLL Ready", s.seq.load(Ordering::Relaxed)); + r.events_txptrupd.reset(); + r.intenset.write(|w| w.txptrupd().set()); + let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed); + if overruns - 1 != 0 { + warn!("XRUN: {}", overruns); + s.overruns.store(0, Ordering::Relaxed) + } Poll::Ready(()) } else { + trace!("[{}] POLL Pending", s.seq.load(Ordering::Relaxed)); Poll::Pending } }) @@ -593,19 +614,26 @@ impl Buffer for &[i32] { } pub(crate) mod sealed { + use core::sync::atomic::AtomicI32; + use embassy_sync::waitqueue::AtomicWaker; - //use super::*; + use super::*; pub struct State { pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, + pub overruns: AtomicI32, + pub seq: AtomicI32, } + impl State { pub const fn new() -> Self { Self { rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), + overruns: AtomicI32::new(0), + seq: AtomicI32::new(0), } } } diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 53ccb3b85..7fb1ecb84 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -9,6 +9,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000}; +use embassy_nrf::pac::ficr::info; use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; @@ -49,16 +50,14 @@ async fn main(_spawner: Spawner) { let mut buf = AlignedBuffer([0i16; BUF_SIZE]); let mut carrier = SineOsc::new(); - carrier.set_frequency(300.0, inv_sample_rate); + carrier.set_frequency(240.0, inv_sample_rate); - let mut modulator = SineOsc::new(); - modulator.set_frequency(0.01, inv_sample_rate); - modulator.set_amplitude(0.2); + // let mut modulator = SineOsc::new(); + // modulator.set_frequency(0.01, inv_sample_rate); + // modulator.set_amplitude(0.2); - i2s.set_tx_enabled(true); - i2s.start(); - - loop { + let mut lastf = 0.0; + let mut generate = |buf: &mut [i16]| { for sample in buf.as_mut().chunks_mut(2) { let signal = carrier.generate(); // let modulation = bipolar_to_unipolar(modulator.generate()); @@ -67,8 +66,18 @@ async fn main(_spawner: Spawner) { let value = (i16::MAX as f32 * signal) as i16; sample[0] = value; sample[1] = value; - // info!("{}", signal); } + }; + + generate(buf.as_mut().as_mut_slice()); + + i2s.set_tx_enabled(true); + i2s.start(); + + loop { + // info!("--"); + + generate(buf.as_mut().as_mut_slice()); if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { error!("{}", err); From dca11095e2f41d50dbc96d48474a64d9198728c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Sun, 13 Nov 2022 01:49:55 +0100 Subject: [PATCH 0337/1575] Disable UARTE in embassy-nrf::init --- embassy-nrf/src/lib.rs | 6 ++++++ embassy-nrf/src/spis.rs | 39 +++++++++++------------------------- examples/nrf/src/bin/spis.rs | 8 +++++--- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 587e19be5..67b254989 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -268,5 +268,11 @@ 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) + 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/spis.rs b/embassy-nrf/src/spis.rs index 3f77c61d1..416db2d33 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -1,5 +1,4 @@ #![macro_use] - use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -10,7 +9,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; -use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; +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}; @@ -122,41 +121,26 @@ impl<'d, T: Instance> Spis<'d, T> { mosi: Option>, config: Config, ) -> Self { - into_ref!(cs, spis, irq); + 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()) }); } - match config.mode.polarity { - Polarity::IdleHigh => { - sck.set_high(); - if let Some(mosi) = &mosi { - mosi.set_high(); - } - } - Polarity::IdleLow => { - sck.set_low(); - if let Some(mosi) = &mosi { - mosi.set_low(); - } - } - } - - // Select pins. - r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); - r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); - r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); - r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) }); - // Enable SPIS instance. r.enable.write(|w| w.enable().enabled()); @@ -246,9 +230,8 @@ impl<'d, T: Instance> Spis<'d, T> { r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as _) }); r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); - // Reset and enable the end event. + // Reset end event. r.events_end.reset(); - r.intenset.write(|w| w.end().set()); // Release the semaphore. r.tasks_release.write(|w| unsafe { w.bits(1) }); @@ -316,6 +299,7 @@ impl<'d, T: Instance> Spis<'d, T> { if r.semstat.read().bits() == 1 { return Poll::Ready(()); } + r.intenset.write(|w| w.acquired().set()); Poll::Pending }) .await; @@ -324,12 +308,13 @@ impl<'d, T: Instance> Spis<'d, T> { self.prepare(rx, tx)?; // Wait for 'end' event. + r.intenset.write(|w| w.end().set()); poll_fn(|cx| { s.end_waker.register(cx.waker()); if r.events_end.read().bits() != 0 { return Poll::Ready(()); } - + r.intenset.write(|w| w.end().set()); Poll::Pending }) .await; diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf/src/bin/spis.rs index dade5fcbd..fe3b0c53d 100644 --- a/examples/nrf/src/bin/spis.rs +++ b/examples/nrf/src/bin/spis.rs @@ -17,9 +17,11 @@ async fn main(_spawner: Spawner) { 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 buf = [0_u8; 64]; - if let Ok(n) = spis.read(&mut buf).await { - info!("RX: {:?}", buf[..n]); + 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]); } } } From 17857bc18fee95be07ee0c51687d2eb109e5aea6 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sun, 13 Nov 2022 02:12:58 +0100 Subject: [PATCH 0338/1575] Minor changes --- embassy-nrf/src/i2s.rs | 9 +++++---- examples/nrf/src/bin/i2s.rs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 9a8f29e78..eed9e1956 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -327,6 +327,7 @@ impl<'d, T: Instance> I2S<'d, T> { pub fn start(&self) -> &Self { let r = T::regs(); self.enable(); + trace!("START"); r.tasks_start.write(|w| unsafe { w.bits(1) }); self } @@ -487,8 +488,8 @@ impl<'d, T: Instance> I2sOutput<'d, T> { let s = T::state(); let seq = s.seq.fetch_add(1, Ordering::Relaxed); - if r.events_txptrupd.read().bits() != 0 && seq > 0 { - info!("XRUN!"); + if r.events_txptrupd.read().bits() != 0 && seq > 1 { + warn!("XRUN!"); loop {} } @@ -505,8 +506,8 @@ impl<'d, T: Instance> I2sOutput<'d, T> { }); trace!("[{}] PTR", s.seq.load(Ordering::Relaxed)); - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); compiler_fence(Ordering::SeqCst); @@ -517,7 +518,7 @@ impl<'d, T: Instance> I2sOutput<'d, T> { r.events_txptrupd.reset(); r.intenset.write(|w| w.txptrupd().set()); let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed); - if overruns - 1 != 0 { + if overruns != 0 { warn!("XRUN: {}", overruns); s.overruns.store(0, Ordering::Relaxed) } diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 7fb1ecb84..33b5398d9 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(I2S); let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); - const BUF_SAMPLES: usize = 250; + const BUF_SAMPLES: usize = 500; const BUF_SIZE: usize = BUF_SAMPLES * 2; let mut buf = AlignedBuffer([0i16; BUF_SIZE]); @@ -56,7 +56,6 @@ async fn main(_spawner: Spawner) { // modulator.set_frequency(0.01, inv_sample_rate); // modulator.set_amplitude(0.2); - let mut lastf = 0.0; let mut generate = |buf: &mut [i16]| { for sample in buf.as_mut().chunks_mut(2) { let signal = carrier.generate(); @@ -71,12 +70,14 @@ async fn main(_spawner: Spawner) { generate(buf.as_mut().as_mut_slice()); + if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { + error!("{}", err); + } + i2s.set_tx_enabled(true); i2s.start(); loop { - // info!("--"); - generate(buf.as_mut().as_mut_slice()); if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { From 5cfad3f853280069e2927e4838d9b56980e28e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Als=C3=A9r?= Date: Sun, 13 Nov 2022 02:37:23 +0100 Subject: [PATCH 0339/1575] Feature gate UARTE disable --- embassy-nrf/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 67b254989..5726f1181 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -269,6 +269,7 @@ pub fn init(config: config::Config) -> Peripherals { 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()); From 4fe834db2f491179edf28105faf79d6fc89785c6 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sun, 13 Nov 2022 02:48:07 +0100 Subject: [PATCH 0340/1575] Mono channels --- examples/nrf/src/bin/i2s.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 33b5398d9..4b6f8ab2d 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -8,7 +8,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000}; +use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000, Channels}; use embassy_nrf::pac::ficr::info; use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; @@ -37,6 +37,7 @@ async fn main(_spawner: Spawner) { freq: MckFreq::_32MDiv10, ratio: Ratio::_256x, }; // 12500 Hz + config.channels = Channels::Left; let sample_rate = config.mode.sample_rate().expect("I2S Master"); let inv_sample_rate = 1.0 / sample_rate as f32; @@ -46,25 +47,24 @@ async fn main(_spawner: Spawner) { let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); const BUF_SAMPLES: usize = 500; - const BUF_SIZE: usize = BUF_SAMPLES * 2; + const BUF_SIZE: usize = BUF_SAMPLES; let mut buf = AlignedBuffer([0i16; BUF_SIZE]); let mut carrier = SineOsc::new(); - carrier.set_frequency(240.0, inv_sample_rate); + carrier.set_frequency(16.0, inv_sample_rate); // let mut modulator = SineOsc::new(); // modulator.set_frequency(0.01, inv_sample_rate); // modulator.set_amplitude(0.2); let mut generate = |buf: &mut [i16]| { - for sample in buf.as_mut().chunks_mut(2) { + for sample in buf.as_mut() { let signal = carrier.generate(); // let modulation = bipolar_to_unipolar(modulator.generate()); // carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate); // carrier.set_amplitude((modulation); let value = (i16::MAX as f32 * signal) as i16; - sample[0] = value; - sample[1] = value; + *sample = value; } }; From eba42cb5f4c4dc1be54c27729325e982d85fc8b0 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sun, 13 Nov 2022 22:15:19 +0100 Subject: [PATCH 0341/1575] embassy-nrf: Add TWIS module --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 3 + embassy-nrf/src/chips/nrf52832.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/chips/nrf5340_app.rs | 5 + embassy-nrf/src/chips/nrf5340_net.rs | 1 + embassy-nrf/src/chips/nrf9160.rs | 5 + embassy-nrf/src/lib.rs | 7 + embassy-nrf/src/twis.rs | 734 +++++++++++++++++++++++++++ examples/nrf/src/bin/twis.rs | 45 ++ 13 files changed, 815 insertions(+) create mode 100644 embassy-nrf/src/twis.rs create mode 100644 examples/nrf/src/bin/twis.rs diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index dec31a84c..0630c0fbf 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -133,6 +133,8 @@ impl_spim!(SPI0, SPIM0, 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 e57a4a383..3867fbd92 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -139,6 +139,8 @@ impl_spim!(SPI0, SPIM0, 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 918404cf1..36efd1dbd 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -140,6 +140,8 @@ impl_spim!(SPI1, SPIM1, 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 dba033b0f..33a07bbc4 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -139,6 +139,9 @@ impl_spim!(TWISPI1, SPIM1, 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 81e66c193..b1c33c395 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -149,6 +149,9 @@ impl_spim!(SPI2, SPIM2, 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); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 92499e3c9..db0a87bd9 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -177,6 +177,9 @@ impl_spim!(SPI3, SPIM3, SPIM3); 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); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4beadfba8..3f4c8b8f1 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -180,6 +180,9 @@ impl_spim!(SPI3, SPIM3, SPIM3); 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); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 7845d4a8e..632c02ccd 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -366,6 +366,11 @@ 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 ae136e09d..917d1a867 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -239,6 +239,7 @@ embassy_hal_common::peripherals! { impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); impl_spim!(UARTETWISPI0, SPIM0, 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 b5a53ed80..70285531f 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -280,6 +280,11 @@ 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/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f6..6c5a3202a 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -100,6 +100,7 @@ pub mod spim; pub mod temp; pub mod timer; pub mod twim; +pub mod twis; pub mod uarte; #[cfg(any( feature = "_nrf5340-app", @@ -267,5 +268,11 @@ 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/twis.rs b/embassy-nrf/src/twis.rs new file mode 100644 index 000000000..8c9cb54ea --- /dev/null +++ b/embassy-nrf/src/twis.rs @@ -0,0 +1,734 @@ +#![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 addr0: u8, + pub addr1: 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 { + addr0: 0x55, + addr1: 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.addr0) }); + r.config.modify(|_r, w| w.address0().enabled()); + if let Some(addr1) = config.addr1 { + r.address[1].write(|w| unsafe { w.address().bits(addr1) }); + 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)); + } + + /// Wait for 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 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<(), Error> { + 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(); + return Ok(()); + } + } + } + + /// Wait for stop or error + #[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 or error + #[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 + #[cfg(feature = "time")] + fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<(), Error> { + 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(); + return Ok(()); + } 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(); + return Poll::Ready(Ok(())); + } + + 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 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_write_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_write(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { + match self.setup_write_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_write_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(()) + } + + /// Listen for commands from an I2C master. + /// + /// 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_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) + } + + /// Write to an I2C master. + /// + /// 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_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write(buffer, false)?; + self.blocking_wait()?; + Ok(()) + } + + /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write_from_ram(buffer, false)?; + self.blocking_wait()?; + Ok(()) + } + + // =========================================== + + /// Listen for commands from an I2C master. + /// + /// The buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + #[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) + } + + /// Write to an I2C master with timeout. + /// + /// See [`blocking_write`]. + #[cfg(feature = "time")] + pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + self.setup_write(buffer, false)?; + self.blocking_wait_timeout(timeout)?; + Ok(()) + } + + /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + #[cfg(feature = "time")] + pub fn blocking_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + self.setup_write_from_ram(buffer, false)?; + self.blocking_wait_timeout(timeout)?; + Ok(()) + } + + // =========================================== + + 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) + } + + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write(buffer, true)?; + self.async_wait().await?; + Ok(()) + } + + /// Same as [`write`](Twis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.setup_write_from_ram(buffer, true)?; + self.async_wait().await?; + Ok(()) + } +} + +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/examples/nrf/src/bin/twis.rs b/examples/nrf/src/bin/twis.rs new file mode 100644 index 000000000..f85173c08 --- /dev/null +++ b/examples/nrf/src/bin/twis.rs @@ -0,0 +1,45 @@ +//! 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.addr0 = 0x55; + let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + + info!("Listening..."); + loop { + let mut buf = [0u8; 16]; + let tx_buf = [1, 2, 3, 4, 5, 6, 7, 8]; + match i2c.listen(&mut buf).await { + Ok(Command::Read) => { + info!("Got READ command. Writing back data:\n{:?}\n", tx_buf); + if let Err(e) = i2c.write(&tx_buf).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!("Writing back data:\n{:?}\n", tx_buf); + if let Err(e) = i2c.write(&tx_buf).await { + error!("{:?}", e); + } + } + Err(e) => error!("{:?}", e), + } + } +} From 43c1afb6a6b31a60f43b2faf8ca93bf5129e4d68 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 11:22:14 +0100 Subject: [PATCH 0342/1575] Return number of bytes written, add address match getter --- embassy-nrf/src/twis.rs | 141 +++++++++++++++++++---------------- examples/nrf/src/bin/twis.rs | 2 +- 2 files changed, 77 insertions(+), 66 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 8c9cb54ea..769522877 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -24,8 +24,8 @@ use crate::{gpio, pac, Peripheral}; #[non_exhaustive] pub struct Config { - pub addr0: u8, - pub addr1: Option, + pub address0: u8, + pub address1: Option, pub orc: u8, pub sda_high_drive: bool, pub sda_pullup: bool, @@ -36,8 +36,8 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - addr0: 0x55, - addr1: None, + address0: 0x55, + address1: None, orc: 0x00, scl_high_drive: false, sda_pullup: false, @@ -134,10 +134,10 @@ impl<'d, T: Instance> Twis<'d, T> { r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // Set address - r.address[0].write(|w| unsafe { w.address().bits(config.addr0) }); + r.address[0].write(|w| unsafe { w.address().bits(config.address0) }); r.config.modify(|_r, w| w.address0().enabled()); - if let Some(addr1) = config.addr1 { - r.address[1].write(|w| unsafe { w.address().bits(addr1) }); + if let Some(address1) = config.address1 { + r.address[1].write(|w| unsafe { w.address().bits(address1) }); r.config.modify(|_r, w| w.address1().enabled()); } @@ -242,7 +242,13 @@ impl<'d, T: Instance> Twis<'d, T> { .write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true)); } - /// Wait for stop or error + /// 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() + } + + /// Wait for read, write, stop or error fn blocking_listen_wait(&mut self) -> Result { let r = T::regs(); loop { @@ -267,7 +273,7 @@ impl<'d, T: Instance> Twis<'d, T> { } } - /// Wait for stop or error + /// Wait for stop, repeated start or error fn blocking_listen_wait_end(&mut self, status: Status) -> Result { let r = T::regs(); loop { @@ -294,7 +300,7 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Wait for stop or error - fn blocking_wait(&mut self) -> Result<(), Error> { + fn blocking_wait(&mut self) -> Result { let r = T::regs(); loop { // stop if an error occured @@ -311,12 +317,42 @@ impl<'d, T: Instance> Twis<'d, T> { } } else if r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); - return Ok(()); + let n = r.txd.amount.read().bits() as usize; + return Ok(n); } } } - /// Wait for stop or error + /// 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(); @@ -347,7 +383,7 @@ impl<'d, T: Instance> Twis<'d, T> { } } - /// Wait for stop or error + /// 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(); @@ -379,35 +415,7 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Wait for stop or error - #[cfg(feature = "time")] - fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<(), Error> { - 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(); - return Ok(()); - } 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> { + fn async_wait(&mut self) -> impl Future> { poll_fn(move |cx| { let r = T::regs(); let s = T::state(); @@ -428,7 +436,8 @@ impl<'d, T: Instance> Twis<'d, T> { } } else if r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); - return Poll::Ready(Ok(())); + let n = r.txd.amount.read().bits() as usize; + return Poll::Ready(Ok(n)); } Poll::Pending @@ -462,7 +471,7 @@ impl<'d, T: Instance> Twis<'d, T> { }) } - /// Wait for stop or error + /// 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(); @@ -595,25 +604,23 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Write to an I2C master. - /// + /// 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_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { self.setup_write(buffer, false)?; - self.blocking_wait()?; - Ok(()) + self.blocking_wait() } /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result { self.setup_write_from_ram(buffer, false)?; - self.blocking_wait()?; - Ok(()) + self.blocking_wait() } // =========================================== - /// Listen for commands from an I2C master. + /// Listen for commands from an I2C master with timeout. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. @@ -630,25 +637,27 @@ impl<'d, T: Instance> Twis<'d, T> { } /// Write to an I2C master with timeout. - /// + /// Returns the number of bytes written. /// See [`blocking_write`]. #[cfg(feature = "time")] - pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { self.setup_write(buffer, false)?; - self.blocking_wait_timeout(timeout)?; - Ok(()) + self.blocking_wait_timeout(timeout) } /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. #[cfg(feature = "time")] - pub fn blocking_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result<(), Error> { + pub fn blocking_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { self.setup_write_from_ram(buffer, false)?; - self.blocking_wait_timeout(timeout)?; - Ok(()) + self.blocking_wait_timeout(timeout) } // =========================================== + /// Listen asynchronously for commands from an I2C master. + /// + /// 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 listen(&mut self, buffer: &mut [u8]) -> Result { self.setup_listen(buffer, true)?; let status = self.async_listen_wait().await?; @@ -660,17 +669,19 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + /// Async write to an I2C master. + /// 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 write(&mut self, buffer: &[u8]) -> Result { self.setup_write(buffer, true)?; - self.async_wait().await?; - Ok(()) + self.async_wait().await } /// Same as [`write`](Twis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { + pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result { self.setup_write_from_ram(buffer, true)?; - self.async_wait().await?; - Ok(()) + self.async_wait().await } } diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf/src/bin/twis.rs index f85173c08..a34bb2711 100644 --- a/examples/nrf/src/bin/twis.rs +++ b/examples/nrf/src/bin/twis.rs @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); let mut config = twis::Config::default(); // Set i2c address - config.addr0 = 0x55; + config.address0 = 0x55; let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); info!("Listening..."); From 8d2d5a30a519de76c3c74e5e8066ac3bc9aa8f77 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 11:39:55 +0100 Subject: [PATCH 0343/1575] Single waker --- embassy-nrf/src/spis.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 416db2d33..f3105007f 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -201,12 +201,12 @@ impl<'d, T: Instance> Spis<'d, T> { let s = T::state(); if r.events_end.read().bits() != 0 { - s.end_waker.wake(); + s.waker.wake(); r.intenclr.write(|w| w.end().clear()); } if r.events_acquired.read().bits() != 0 { - s.acquire_waker.wake(); + s.waker.wake(); r.intenclr.write(|w| w.acquired().clear()); } } @@ -295,7 +295,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Wait until CPU has acquired the semaphore. poll_fn(|cx| { - s.acquire_waker.register(cx.waker()); + s.waker.register(cx.waker()); if r.semstat.read().bits() == 1 { return Poll::Ready(()); } @@ -310,7 +310,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Wait for 'end' event. r.intenset.write(|w| w.end().set()); poll_fn(|cx| { - s.end_waker.register(cx.waker()); + s.waker.register(cx.waker()); if r.events_end.read().bits() != 0 { return Poll::Ready(()); } @@ -451,15 +451,13 @@ pub(crate) mod sealed { use super::*; pub struct State { - pub end_waker: AtomicWaker, - pub acquire_waker: AtomicWaker, + pub waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { - end_waker: AtomicWaker::new(), - acquire_waker: AtomicWaker::new(), + waker: AtomicWaker::new(), } } } From 3a1ddd66c64db1c5efd79b89efd76ac97d9eccce Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 16:18:11 +0100 Subject: [PATCH 0344/1575] Cleanup interrupts --- embassy-nrf/src/spis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index f3105007f..d382ee0c5 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -297,9 +297,9 @@ impl<'d, T: Instance> Spis<'d, T> { poll_fn(|cx| { s.waker.register(cx.waker()); if r.semstat.read().bits() == 1 { + r.events_acquired.reset(); return Poll::Ready(()); } - r.intenset.write(|w| w.acquired().set()); Poll::Pending }) .await; @@ -312,9 +312,9 @@ impl<'d, T: Instance> Spis<'d, T> { poll_fn(|cx| { s.waker.register(cx.waker()); if r.events_end.read().bits() != 0 { + r.events_end.reset(); return Poll::Ready(()); } - r.intenset.write(|w| w.end().set()); Poll::Pending }) .await; From 0b066b22d11ee07ed64906b27e950ba83368aa49 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Mon, 14 Nov 2022 16:24:21 +0100 Subject: [PATCH 0345/1575] Check events_acquired --- embassy-nrf/src/spis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index d382ee0c5..f6f7db3a0 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -296,7 +296,7 @@ impl<'d, T: Instance> Spis<'d, T> { // Wait until CPU has acquired the semaphore. poll_fn(|cx| { s.waker.register(cx.waker()); - if r.semstat.read().bits() == 1 { + if r.events_acquired.read().bits() == 1 { r.events_acquired.reset(); return Poll::Ready(()); } From bcec55464f22069c45f07745efdd0fa45a4959c4 Mon Sep 17 00:00:00 2001 From: Johannes Neyer Date: Fri, 11 Nov 2022 11:06:38 +0100 Subject: [PATCH 0346/1575] [doc] Fix line indices of basic example --- docs/modules/ROOT/pages/basic_application.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index 4dc4a6359..f20ddf531 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,7 +45,7 @@ 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: @@ -76,7 +76,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). From ea61c192807df7a1422ea04480cca0562cbc3bb2 Mon Sep 17 00:00:00 2001 From: Johannes Neyer Date: Tue, 15 Nov 2022 10:07:35 +0100 Subject: [PATCH 0347/1575] [doc] Fix some grammar --- docs/modules/ROOT/pages/basic_application.adoc | 2 +- docs/modules/ROOT/pages/layer_by_layer.adoc | 8 ++++---- docs/modules/ROOT/pages/stm32.adoc | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index f20ddf531..d233d77c1 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc @@ -61,7 +61,7 @@ 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` diff --git a/docs/modules/ROOT/pages/layer_by_layer.adoc b/docs/modules/ROOT/pages/layer_by_layer.adoc index a96dd9fe2..a78a64a97 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 8ed9ab04b..7bfc0592b 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 From 9505a6f7527cee9af73117e49322169f2d568a39 Mon Sep 17 00:00:00 2001 From: Johannes Neyer Date: Tue, 15 Nov 2022 10:07:45 +0100 Subject: [PATCH 0348/1575] [doc] Remove obsolete code sample --- docs/modules/ROOT/pages/basic_application.adoc | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/docs/modules/ROOT/pages/basic_application.adoc b/docs/modules/ROOT/pages/basic_application.adoc index d233d77c1..3f4f16e28 100644 --- a/docs/modules/ROOT/pages/basic_application.adoc +++ b/docs/modules/ROOT/pages/basic_application.adoc @@ -48,19 +48,6 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera 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 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 From 551b54ddcbb104f881a3333215f4a828b33d029a Mon Sep 17 00:00:00 2001 From: Jaxter Kim Date: Mon, 14 Nov 2022 14:56:30 +0100 Subject: [PATCH 0349/1575] stm32g0: Fix ADC for channels above 14 --- embassy-stm32/src/adc/v3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8f81cb7a3..90aa7d3b9 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 From eb149a0bd42d7690e78e5f2b37579c1f68be613b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 15 Nov 2022 16:12:07 +0100 Subject: [PATCH 0350/1575] embassy-rp: Add basic ADC module --- embassy-rp/src/adc.rs | 178 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/lib.rs | 3 + examples/rp/src/bin/adc.rs | 33 +++++++ 3 files changed, 214 insertions(+) create mode 100644 embassy-rp/src/adc.rs create mode 100644 examples/rp/src/bin/adc.rs diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs new file mode 100644 index 000000000..acef4deaf --- /dev/null +++ b/embassy-rp/src/adc.rs @@ -0,0 +1,178 @@ +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().modify(|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().modify(|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(); + let ch = PIN::channel(); + unsafe { + if ch == 4 { + r.cs().modify(|w| w.set_ts_en(true)) + } + while !r.cs().read().ready() {} + r.cs().modify(|w| { + w.set_ainsel(ch); + 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/lib.rs b/embassy-rp/src/lib.rs index f608f1768..6c91b1adc 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -6,6 +6,7 @@ pub(crate) mod fmt; mod intrinsics; +pub mod adc; pub mod dma; pub mod gpio; pub mod i2c; @@ -98,6 +99,8 @@ embassy_hal_common::peripherals! { RTC, FLASH, + + ADC, } #[link_section = ".boot2"] diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs new file mode 100644 index 000000000..2a9e93732 --- /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; + } +} From 9f870a5edf08c4322a6535d07f6444341fbf4c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 15 Nov 2022 16:31:19 +0100 Subject: [PATCH 0351/1575] Cleanup --- embassy-rp/src/adc.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index acef4deaf..cdb752dcc 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -120,14 +120,9 @@ impl<'d> Adc<'d> { pub fn blocking_read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { let r = Self::regs(); - let ch = PIN::channel(); unsafe { - if ch == 4 { - r.cs().modify(|w| w.set_ts_en(true)) - } - while !r.cs().read().ready() {} r.cs().modify(|w| { - w.set_ainsel(ch); + w.set_ainsel(PIN::channel()); w.set_start_once(true) }); while !r.cs().read().ready() {} From 1ed260b1055fad6ddd89053ae3e1997ec34c6332 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 17 Nov 2022 00:19:22 +0100 Subject: [PATCH 0352/1575] Fix buffer overruns --- embassy-nrf/src/i2s.rs | 427 ++++++++++++++++++++---------------- examples/nrf/src/bin/i2s.rs | 88 +++++--- 2 files changed, 294 insertions(+), 221 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index eed9e1956..e6dfb690c 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -3,6 +3,7 @@ //! I2S use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -10,7 +11,6 @@ use embassy_cortex_m::interrupt::{InterruptExt, Priority}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -//use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::Interrupt; use crate::pac::i2s::{RegisterBlock, CONFIG, PSEL}; @@ -31,9 +31,9 @@ pub const SRAM_UPPER: usize = 0x3000_0000; pub enum Error { BufferTooLong, BufferZeroLength, - DMABufferNotInDataMemory, + BufferNotInDataMemory, BufferMisaligned, - // TODO: add other error variants. + BufferLengthMisaligned, } pub const MODE_MASTER_8000: Mode = Mode::Master { @@ -122,12 +122,12 @@ pub enum MckFreq { } impl MckFreq { - const REGISTER_VALUES: &[u32] = &[ + const REGISTER_VALUES: &'static [u32] = &[ 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, 0x08000000, 0x06000000, 0x04100000, 0x020C0000, ]; - const FREQUENCIES: &[u32] = &[ + const FREQUENCIES: &'static [u32] = &[ 4000000, 3200000, 2909090, 2133333, 2000000, 1523809, 1391304, 1066666, 1032258, 1000000, 761904, 507936, 256000, ]; @@ -162,7 +162,7 @@ pub enum Ratio { } impl Ratio { - const RATIOS: &[u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; + const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; pub fn to_divisor(&self) -> u32 { Self::RATIOS[u8::from(*self) as usize] @@ -234,23 +234,10 @@ impl From for u8 { } } -/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. +/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. /// /// For more details about EasyDMA, consult the module documentation. pub struct I2S<'d, T: Instance> { - output: I2sOutput<'d, T>, - input: I2sInput<'d, T>, -} - -/// Transmitter interface to the UARTE peripheral obtained -/// via [Uarte]::split. -pub struct I2sOutput<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - -/// Receiver interface to the UARTE peripheral obtained -/// via [Uarte]::split. -pub struct I2sInput<'d, T: Instance> { _p: PeripheralRef<'d, T>, } @@ -298,78 +285,19 @@ impl<'d, T: Instance> I2S<'d, T> { r.enable.write(|w| w.enable().enabled()); - Self { - output: I2sOutput { - _p: unsafe { i2s.clone_unchecked() }, - }, - input: I2sInput { _p: i2s }, - } + Self { _p: i2s } } - /// Enables the I2S module. - #[inline(always)] - pub fn enable(&self) -> &Self { - let r = T::regs(); - r.enable.write(|w| w.enable().enabled()); - self + pub fn output(self) -> Output<'d, T> { + Output { _p: self._p } } - /// Disables the I2S module. - #[inline(always)] - pub fn disable(&self) -> &Self { - let r = T::regs(); - r.enable.write(|w| w.enable().disabled()); - self + pub fn input(self) -> Input<'d, T> { + Input { _p: self._p } } - /// Starts I2S transfer. - #[inline(always)] - pub fn start(&self) -> &Self { - let r = T::regs(); - self.enable(); - trace!("START"); - r.tasks_start.write(|w| unsafe { w.bits(1) }); - self - } - - /// Stops the I2S transfer and waits until it has stopped. - #[inline(always)] - pub async fn stop(&self) { - todo!() - } - - /// Enables/disables I2S transmission (TX). - #[inline(always)] - pub fn set_tx_enabled(&self, enabled: bool) -> &Self { - let r = T::regs(); - r.config.txen.write(|w| w.txen().bit(enabled)); - self - } - - /// Enables/disables I2S reception (RX). - #[inline(always)] - pub fn set_rx_enabled(&self, enabled: bool) -> &Self { - let r = T::regs(); - r.config.rxen.write(|w| w.rxen().bit(enabled)); - self - } - - /// Transmits the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - pub async fn tx(&mut self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - self.output.tx(buffer).await - } - - /// Receives data into the given `buffer` until it's filled. - /// Buffer address must be 4 byte aligned and located in RAM. - pub async fn rx(&mut self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - self.input.rx(buffer).await + pub fn full_duplex(self) -> FullDuplex<'d, T> { + FullDuplex { _p: self._p } } fn apply_config(c: &CONFIG, config: &Config) { @@ -433,103 +361,94 @@ impl<'d, T: Instance> I2S<'d, T> { irq.unpend(); irq.enable(); - r.intenclr.write(|w| w.rxptrupd().clear()); - r.intenclr.write(|w| w.txptrupd().clear()); + let device = Device::::new(); + device.disable_tx_ptr_interrupt(); + device.disable_rx_ptr_interrupt(); - r.events_rxptrupd.reset(); - r.events_txptrupd.reset(); + device.reset_tx_ptr_event(); + device.reset_rx_ptr_event(); - r.intenset.write(|w| w.rxptrupd().set()); - r.intenset.write(|w| w.txptrupd().set()); + device.enable_tx_ptr_interrupt(); + device.enable_rx_ptr_interrupt(); } fn on_interrupt(_: *mut ()) { - let r = T::regs(); + let device = Device::::new(); let s = T::state(); - if r.events_txptrupd.read().bits() != 0 { - trace!("[{}] INT", s.seq.load(Ordering::Relaxed)); + if device.is_tx_ptr_updated() { + trace!("TX INT"); s.tx_waker.wake(); - r.intenclr.write(|w| w.txptrupd().clear()); + device.disable_tx_ptr_interrupt(); } - if r.events_rxptrupd.read().bits() != 0 { + if device.is_rx_ptr_updated() { + trace!("RX INT"); s.rx_waker.wake(); - r.intenclr.write(|w| w.rxptrupd().clear()); + device.disable_rx_ptr_interrupt(); } - - s.overruns.fetch_add(1, Ordering::Relaxed); } } -impl<'d, T: Instance> I2sOutput<'d, T> { - /// Transmits the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - #[allow(unused_mut)] - pub async fn tx(&mut self, buffer: B) -> Result<(), Error> +pub struct Output<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Output<'d, T> { + /// Starts I2S transfer. + #[inline(always)] + pub fn start(&self, buffer: B) -> Result<(), Error> where B: Buffer, { - let ptr = buffer.bytes_ptr(); - let len = buffer.bytes_len(); + // TODO what to do if it is started already? - if ptr as u32 % 4 != 0 { - return Err(Error::BufferMisaligned); - } - if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { - return Err(Error::DMABufferNotInDataMemory); - } - let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; - if maxcnt > MAX_DMA_MAXCNT { - return Err(Error::BufferTooLong); - } + let device = Device::::new(); + device.enable(); + device.set_tx_buffer(buffer)?; + device.enable_tx(); + device.start(); - let r = T::regs(); - let s = T::state(); + Ok(()) + } - let seq = s.seq.fetch_add(1, Ordering::Relaxed); - if r.events_txptrupd.read().bits() != 0 && seq > 1 { - warn!("XRUN!"); - loop {} - } + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + todo!() + } - let drop = OnDrop::new(move || { - trace!("write drop: stopping"); + /// Transmits the given `buffer`. + /// Buffer address must be 4 byte aligned and located in RAM. + #[allow(unused_mut)] + pub async fn send(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + trace!("SEND: {}", buffer.bytes_ptr() as u32); - r.intenclr.write(|w| w.txptrupd().clear()); - r.events_txptrupd.reset(); - r.config.txen.write(|w| w.txen().disabled()); - - // TX is stopped almost instantly, spinning is fine. - while r.events_txptrupd.read().bits() == 0 {} - trace!("write drop: stopped"); - }); - - trace!("[{}] PTR", s.seq.load(Ordering::Relaxed)); - r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + let device = Device::::new(); + let drop = device.on_tx_drop(); compiler_fence(Ordering::SeqCst); poll_fn(|cx| { - s.tx_waker.register(cx.waker()); - if r.events_txptrupd.read().bits() != 0 || seq == 0 { - trace!("[{}] POLL Ready", s.seq.load(Ordering::Relaxed)); - r.events_txptrupd.reset(); - r.intenset.write(|w| w.txptrupd().set()); - let overruns = s.overruns.fetch_sub(1, Ordering::Relaxed); - if overruns != 0 { - warn!("XRUN: {}", overruns); - s.overruns.store(0, Ordering::Relaxed) - } + T::state().tx_waker.register(cx.waker()); + + if device.is_tx_ptr_updated() { + trace!("TX POLL: Ready"); + device.reset_tx_ptr_event(); + device.enable_tx_ptr_interrupt(); Poll::Ready(()) } else { - trace!("[{}] POLL Pending", s.seq.load(Ordering::Relaxed)); + trace!("TX POLL: Pending"); Poll::Pending } }) .await; + device.set_tx_buffer(buffer)?; + compiler_fence(Ordering::SeqCst); drop.defuse(); @@ -537,40 +456,180 @@ impl<'d, T: Instance> I2sOutput<'d, T> { } } -impl<'d, T: Instance> I2sInput<'d, T> { - /// Receives into the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - #[allow(unused_mut)] - pub async fn rx(&mut self, buffer: B) -> Result<(), Error> +pub struct Input<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Input<'d, T> { + // TODO +} + +pub struct FullDuplex<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> FullDuplex<'d, T> { + // TODO +} + +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] + fn set_tx_buffer(&self, buffer: B) -> Result<(), Error> where B: Buffer, { - let ptr = buffer.bytes_ptr(); - let len = buffer.bytes_len(); - - if ptr as u32 % 4 != 0 { - return Err(Error::BufferMisaligned); - } - if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { - return Err(Error::DMABufferNotInDataMemory); - } - let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; - if maxcnt > MAX_DMA_MAXCNT { - return Err(Error::BufferTooLong); - } - - let r = T::regs(); - let _s = T::state(); - - // TODO we can not progress until the last buffer written in RXD.PTR - // has started the transmission. - // We can use some sync primitive from `embassy-sync`. - - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - + let (ptr, maxcnt) = Self::validate_buffer(buffer)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); Ok(()) } + + #[inline] + fn set_rx_buffer(&self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let (ptr, maxcnt) = Self::validate_buffer(buffer)?; + self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); + self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + Ok(()) + } + + #[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(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.intenclr.write(|w| w.rxptrupd().clear()); + } + + #[inline] + fn on_tx_drop(&self) -> OnDrop { + 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"); + }) + } + + fn validate_buffer(buffer: B) -> Result<(u32, u32), Error> + where + B: Buffer, + { + let ptr = buffer.bytes_ptr() as u32; + let len = buffer.bytes_len(); + let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + + trace!("PTR={}, MAXCNT={}", ptr, maxcnt); + + // TODO can we avoid repeating all those runtime checks for the same buffer again and again? + + if ptr % 4 != 0 { + Err(Error::BufferMisaligned) + } else if len % 4 != 0 { + Err(Error::BufferLengthMisaligned) + } else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { + Err(Error::BufferNotInDataMemory) + } else if maxcnt > MAX_DMA_MAXCNT { + Err(Error::BufferTooLong) + } else { + Ok((ptr, maxcnt)) + } + } } pub trait Buffer: Sized { @@ -578,10 +637,10 @@ pub trait Buffer: Sized { fn bytes_len(&self) -> usize; } -impl Buffer for &[u8] { +impl Buffer for &[i8] { #[inline] fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() + self.as_ptr() as *const u8 } #[inline] @@ -610,7 +669,7 @@ impl Buffer for &[i32] { #[inline] fn bytes_len(&self) -> usize { - self.len() * core::mem::size_of::() + self.len() * core::mem::size_of::() } } @@ -624,8 +683,6 @@ pub(crate) mod sealed { pub struct State { pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, - pub overruns: AtomicI32, - pub seq: AtomicI32, } impl State { @@ -633,8 +690,6 @@ pub(crate) mod sealed { Self { rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), - overruns: AtomicI32::new(0), - seq: AtomicI32::new(0), } } } @@ -654,7 +709,7 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::i2s::sealed::Instance for peripherals::$type { - fn regs() -> &'static pac::i2s::RegisterBlock { + fn regs() -> &'static crate::pac::i2s::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } fn state() -> &'static crate::i2s::sealed::State { diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s.rs index 4b6f8ab2d..9b3144f24 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s.rs @@ -1,14 +1,13 @@ -// Example inspired by RTIC's I2S demo: https://github.com/nrf-rs/nrf-hal/blob/master/examples/i2s-controller-demo/src/main.rs - #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use core::f32::consts::PI; -use defmt::{error, info}; +use defmt::{error, info, trace}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{MckFreq, Mode, Ratio, MODE_MASTER_16000, MODE_MASTER_8000, Channels}; +use embassy_nrf::gpio::{Input, Pin, Pull}; +use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000}; use embassy_nrf::pac::ficr::info; use embassy_nrf::{i2s, interrupt}; use {defmt_rtt as _, panic_probe as _}; @@ -32,60 +31,79 @@ impl AsMut for AlignedBuffer { async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let mut config = i2s::Config::default(); - // config.mode = MODE_MASTER_16000; - config.mode = Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_256x, - }; // 12500 Hz + config.mode = MODE_MASTER_32000; + // config.mode = Mode::Master { + // freq: MckFreq::_32MDiv10, + // ratio: Ratio::_256x, + // }; // 12500 Hz config.channels = Channels::Left; + config.swidth = SampleWidth::_16bit; let sample_rate = config.mode.sample_rate().expect("I2S Master"); let inv_sample_rate = 1.0 / sample_rate as f32; info!("Sample rate: {}", sample_rate); - let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config); + // Wait for a button press + // let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up); + // btn1.wait_for_low().await; - const BUF_SAMPLES: usize = 500; - const BUF_SIZE: usize = BUF_SAMPLES; - let mut buf = AlignedBuffer([0i16; BUF_SIZE]); + let irq = interrupt::take!(I2S); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config).output(); + + type Sample = i16; + const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample; + const NUM_SAMPLES: usize = 2000; + let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [ + AlignedBuffer([0; NUM_SAMPLES]), + AlignedBuffer([0; NUM_SAMPLES]), + AlignedBuffer([0; NUM_SAMPLES]), + ]; let mut carrier = SineOsc::new(); - carrier.set_frequency(16.0, inv_sample_rate); - // let mut modulator = SineOsc::new(); - // modulator.set_frequency(0.01, inv_sample_rate); - // modulator.set_amplitude(0.2); + let mut freq_mod = SineOsc::new(); + freq_mod.set_frequency(8.0, inv_sample_rate); + freq_mod.set_amplitude(1.0); - let mut generate = |buf: &mut [i16]| { - for sample in buf.as_mut() { + let mut amp_mod = SineOsc::new(); + amp_mod.set_frequency(4.0, inv_sample_rate); + amp_mod.set_amplitude(0.5); + + let mut generate = |buf: &mut [Sample]| { + let ptr = buf as *const [Sample] as *const Sample as u32; + trace!("GEN: {}", ptr); + + for sample in &mut buf.as_mut().chunks_mut(1) { let signal = carrier.generate(); - // let modulation = bipolar_to_unipolar(modulator.generate()); - // carrier.set_frequency(200.0 + 100.0 * modulation, inv_sample_rate); - // carrier.set_amplitude((modulation); - let value = (i16::MAX as f32 * signal) as i16; - *sample = value; + let freq_modulation = bipolar_to_unipolar(freq_mod.generate()); + carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate); + let amp_modulation = bipolar_to_unipolar(amp_mod.generate()); + carrier.set_amplitude(amp_modulation); + let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample; + sample[0] = value; } }; - generate(buf.as_mut().as_mut_slice()); + generate(buffers[0].as_mut().as_mut_slice()); + generate(buffers[1].as_mut().as_mut_slice()); - if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { - error!("{}", err); - } - - i2s.set_tx_enabled(true); - i2s.start(); + i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start"); + let mut index = 1; loop { - generate(buf.as_mut().as_mut_slice()); - - if let Err(err) = i2s.tx(buf.as_ref().as_slice()).await { + if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await { error!("{}", err); } + + index += 1; + if index >= 3 { + index = 0; + } + generate(buffers[index].as_mut().as_mut_slice()); } } +#[derive(Clone)] struct SineOsc { amplitude: f32, modulo: f32, From a444a65ebfcea674e74dcedc7f26a7aa37f59c69 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 17 Nov 2022 09:26:15 +0100 Subject: [PATCH 0353/1575] feat: embassy-usb-logger and example for rpi pico * Add embassy-usb-logger which allows logging over USB for any device implementing embassy-usb * Add example using logger for rpi pico. --- embassy-usb-logger/Cargo.toml | 13 +++ embassy-usb-logger/src/lib.rs | 146 ++++++++++++++++++++++++++++++ examples/rp/Cargo.toml | 2 + examples/rp/src/bin/usb_logger.rs | 30 ++++++ 4 files changed, 191 insertions(+) create mode 100644 embassy-usb-logger/Cargo.toml create mode 100644 embassy-usb-logger/src/lib.rs create mode 100644 examples/rp/src/bin/usb_logger.rs diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml new file mode 100644 index 000000000..2f4665922 --- /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/src/lib.rs b/embassy-usb-logger/src/lib.rs new file mode 100644 index 000000000..6386e2096 --- /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/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 31f688305..250228862 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,6 +13,7 @@ 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" @@ -32,6 +33,7 @@ embedded-hal-async = { version = "0.1.0-alpha.3" } embedded-io = { version = "0.3.1", 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/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs new file mode 100644 index 000000000..52417a02e --- /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; + } +} From 6b88057aef79e32a66c9c99cf048f905d10c2d3a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 00:29:05 +0100 Subject: [PATCH 0354/1575] Add missing parts and Cleanup --- embassy-nrf/src/i2s.rs | 551 +++++++++++++++--- .../nrf/src/bin/{i2s.rs => i2s-generate.rs} | 73 +-- 2 files changed, 493 insertions(+), 131 deletions(-) rename examples/nrf/src/bin/{i2s.rs => i2s-generate.rs} (62%) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index e6dfb690c..53d9f9a1b 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -1,27 +1,27 @@ #![macro_use] -//! I2S +//! Support for I2S audio use core::future::poll_fn; use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::{InterruptExt, Priority}; +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, CONFIG, PSEL}; +use crate::pac::i2s::RegisterBlock; use crate::Peripheral; // TODO: Define those in lib.rs somewhere else -// -// I2S EasyDMA MAXCNT bit length = 14 + +/// I2S EasyDMA MAXCNT bit length = 14 const MAX_DMA_MAXCNT: u32 = 1 << 14; -// Limits for Easy DMA - it can only read from data ram +/// Limits for Easy DMA - it can only read from data ram pub const SRAM_LOWER: usize = 0x2000_0000; pub const SRAM_UPPER: usize = 0x3000_0000; @@ -36,35 +36,144 @@ pub enum Error { BufferLengthMisaligned, } -pub const MODE_MASTER_8000: Mode = Mode::Master { - freq: MckFreq::_32MDiv125, - ratio: Ratio::_32x, -}; // error = 0 -pub const MODE_MASTER_11025: Mode = Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_192x, -}; // error = 86 -pub const MODE_MASTER_16000: Mode = Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_96x, -}; // error = 127 -pub const MODE_MASTER_22050: Mode = Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_96x, -}; // error = 172 -pub const MODE_MASTER_32000: Mode = Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_48x, -}; // error = 254 -pub const MODE_MASTER_44100: Mode = Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_48x, -}; // error = 344 -pub const MODE_MASTER_48000: Mode = Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_32x, -}; // error = 381 +/// 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 [Mode]. +#[derive(Clone, Copy)] +pub enum ApproxSampleRate { + _11025, + _16000, + _22050, + _32000, + _44100, + _48000, +} +impl From for Mode { + fn from(value: ApproxSampleRate) -> Self { + match value { + // error = 86 + ApproxSampleRate::_11025 => Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_192x, + }, + // error = 127 + ApproxSampleRate::_16000 => Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_96x, + }, + // error = 172 + ApproxSampleRate::_22050 => Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_96x, + }, + // error = 254 + ApproxSampleRate::_32000 => Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_48x, + }, + // error = 344 + ApproxSampleRate::_44100 => Mode::Master { + freq: MckFreq::_32MDiv15, + ratio: Ratio::_48x, + }, + // error = 381 + ApproxSampleRate::_48000 => Mode::Master { + freq: MckFreq::_32MDiv21, + ratio: Ratio::_32x, + }, + } + } +} + +impl ApproxSampleRate { + pub fn sample_rate(&self) -> u32 { + // This will always provide a Master mode, so it is safe to unwrap. + Mode::from(*self).sample_rate().unwrap() + } +} + +/// 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 { + // This will always provide a Master mode, so it is safe to unwrap. + Mode::from(*self).sample_rate().unwrap() + } +} + +impl From for Mode { + fn from(value: ExactSampleRate) -> Self { + match value { + ExactSampleRate::_8000 => Mode::Master { + freq: MckFreq::_32MDiv125, + ratio: Ratio::_32x, + }, + ExactSampleRate::_10582 => Mode::Master { + freq: MckFreq::_32MDiv63, + ratio: Ratio::_48x, + }, + ExactSampleRate::_12500 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_256x, + }, + ExactSampleRate::_15625 => Mode::Master { + freq: MckFreq::_32MDiv32, + ratio: Ratio::_64x, + }, + ExactSampleRate::_15873 => Mode::Master { + freq: MckFreq::_32MDiv63, + ratio: Ratio::_32x, + }, + ExactSampleRate::_25000 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_128x, + }, + ExactSampleRate::_31250 => Mode::Master { + freq: MckFreq::_32MDiv32, + ratio: Ratio::_32x, + }, + ExactSampleRate::_50000 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_64x, + }, + ExactSampleRate::_62500 => Mode::Master { + freq: MckFreq::_32MDiv16, + ratio: Ratio::_32x, + }, + ExactSampleRate::_100000 => Mode::Master { + freq: MckFreq::_32MDiv10, + ratio: Ratio::_32x, + }, + ExactSampleRate::_125000 => Mode::Master { + freq: MckFreq::_32MDiv8, + ratio: Ratio::_32x, + }, + } + } +} + +/// I2S configuration. #[derive(Clone)] #[non_exhaustive] pub struct Config { @@ -78,7 +187,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - mode: MODE_MASTER_32000, + mode: ExactSampleRate::_31250.into(), swidth: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, @@ -132,10 +241,12 @@ impl MckFreq { 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)] } @@ -147,7 +258,10 @@ impl From for usize { } } -/// MCK / LRCK ratio. +/// Master clock frequency ratio +/// +/// Sample Rate = LRCK = MCK / Ratio +/// #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Ratio { _32x, @@ -175,6 +289,7 @@ impl From for u8 { } } +/// Sample width. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SampleWidth { _8bit, @@ -188,7 +303,7 @@ impl From for u8 { } } -/// Alignment of sample within a frame. +/// Channel used for the most significant sample value in a frame. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Align { Left, @@ -220,11 +335,13 @@ impl From for bool { } } -/// Enable channels. +/// Channels #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Channels { Stereo, + /// Mono left Left, + /// Mono right Right, } @@ -235,8 +352,6 @@ impl From for u8 { } /// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. pub struct I2S<'d, T: Instance> { _p: PeripheralRef<'d, T>, } @@ -278,29 +393,32 @@ impl<'d, T: Instance> I2S<'d, T> { ) -> Self { into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout); - let r = T::regs(); - Self::apply_config(&r.config, &config); - Self::select_pins(&r.psel, mck, sck, lrck, sdin, sdout); - Self::setup_interrupt(irq, r); + Self::apply_config(&config); + Self::select_pins(mck, sck, lrck, sdin, sdout); + Self::setup_interrupt(irq); - r.enable.write(|w| w.enable().enabled()); + T::regs().enable.write(|w| w.enable().enabled()); Self { _p: i2s } } + /// I2S output only pub fn output(self) -> Output<'d, T> { Output { _p: self._p } } + /// I2S input only pub fn input(self) -> Input<'d, T> { Input { _p: self._p } } + /// I2S full duplex (input and output) pub fn full_duplex(self) -> FullDuplex<'d, T> { FullDuplex { _p: self._p } } - fn apply_config(c: &CONFIG, config: &Config) { + fn apply_config(config: &Config) { + let c = &T::regs().config; match config.mode { Mode::Master { freq, ratio } => { c.mode.write(|w| w.mode().master()); @@ -322,13 +440,14 @@ impl<'d, T: Instance> I2S<'d, T> { } fn select_pins( - psel: &PSEL, mck: PeripheralRef<'d, AnyPin>, sck: PeripheralRef<'d, AnyPin>, lrck: PeripheralRef<'d, AnyPin>, sdin: PeripheralRef<'d, AnyPin>, sdout: PeripheralRef<'d, AnyPin>, ) { + let psel = &T::regs().psel; + psel.mck.write(|w| { unsafe { w.bits(mck.psel_bits()) }; w.connect().connected() @@ -355,21 +474,23 @@ impl<'d, T: Instance> I2S<'d, T> { }); } - fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>, r: &RegisterBlock) { + fn setup_interrupt(irq: PeripheralRef<'d, T::Interrupt>) { irq.set_handler(Self::on_interrupt); - // irq.set_priority(Priority::P1); // TODO review priorities irq.unpend(); 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 ()) { @@ -387,41 +508,40 @@ impl<'d, T: Instance> I2S<'d, T> { s.rx_waker.wake(); device.disable_rx_ptr_interrupt(); } + + if device.is_stopped() { + trace!("STOPPED INT"); + s.stop_waker.wake(); + device.disable_stopped_interrupt(); + } } -} -pub struct Output<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - -impl<'d, T: Instance> Output<'d, T> { - /// Starts I2S transfer. - #[inline(always)] - pub fn start(&self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - // TODO what to do if it is started already? + async fn stop() { + compiler_fence(Ordering::SeqCst); let device = Device::::new(); - device.enable(); - device.set_tx_buffer(buffer)?; - device.enable_tx(); - device.start(); + device.stop(); - Ok(()) + 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(); } - /// Stops the I2S transfer and waits until it has stopped. - #[inline(always)] - pub async fn stop(&self) { - todo!() - } - - /// Transmits the given `buffer`. - /// Buffer address must be 4 byte aligned and located in RAM. - #[allow(unused_mut)] - pub async fn send(&mut self, buffer: B) -> Result<(), Error> + async fn send(buffer: B) -> Result<(), Error> where B: Buffer, { @@ -454,24 +574,191 @@ impl<'d, T: Instance> Output<'d, T> { Ok(()) } + + async fn receive(buffer: B) -> Result<(), Error> + where + B: Buffer, + { + trace!("RECEIVE: {}", buffer.bytes_ptr() as u32); + + let device = Device::::new(); + let drop = device.on_rx_drop(); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + T::state().rx_waker.register(cx.waker()); + + 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; + + device.set_rx_buffer(buffer)?; + + compiler_fence(Ordering::SeqCst); + drop.defuse(); + + Ok(()) + } } +/// I2S output +pub struct Output<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Output<'d, T> { + /// Prepare the initial buffer and start the I2S transfer. + pub async fn start(&self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_tx(); + device.set_tx_buffer(buffer)?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the given `buffer` for transmission in the DMA. + /// Buffer address must be 4 byte aligned and located in RAM. + /// The buffer must not be written while being used by the DMA, + /// which takes two other `send`s being awaited. + #[allow(unused_mut)] + pub async fn send(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + I2S::::send(buffer).await + } +} + +/// I2S input pub struct Input<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> Input<'d, T> { - // TODO + /// Prepare the initial buffer and start the I2S transfer. + pub async fn start(&self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + let device = Device::::new(); + + let s = T::state(); + if s.started.load(Ordering::Relaxed) { + self.stop().await; + } + + device.enable(); + device.enable_rx(); + device.set_rx_buffer(buffer)?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the given `buffer` for reception from the DMA. + /// Buffer address must be 4 byte aligned and located in RAM. + /// The buffer must not be read while being used by the DMA, + /// which takes two other `receive`s being awaited. + #[allow(unused_mut)] + pub async fn receive(&mut self, buffer: B) -> Result<(), Error> + where + B: Buffer, + { + I2S::::receive(buffer).await + } } +/// I2S ful duplex (input & output) pub struct FullDuplex<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> FullDuplex<'d, T> { - // TODO + /// Prepare the initial buffers and start the I2S transfer. + pub async fn start(&self, buffer_out: B, buffer_in: B) -> Result<(), Error> + where + B: Buffer, + { + 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.set_tx_buffer(buffer_out)?; + device.set_rx_buffer(buffer_in)?; + + s.started.store(true, Ordering::Relaxed); + + device.start(); + + Ok(()) + } + + /// Stops the I2S transfer and waits until it has stopped. + #[inline(always)] + pub async fn stop(&self) { + I2S::::stop().await + } + + /// Sets the given `buffer_out` and `buffer_in` for transmission/reception from the DMA. + /// Buffer address must be 4 byte aligned and located in RAM. + /// The buffers must not be written/read while being used by the DMA, + /// which takes two other `send_and_receive` operations being awaited. + #[allow(unused_mut)] + pub async fn send_and_receive(&mut self, buffer_out: B, buffer_in: B) -> Result<(), Error> + where + B: Buffer, + { + I2S::::send(buffer_out).await?; + I2S::::receive(buffer_in).await?; + Ok(()) + } } +/// Helper encapsulating common I2S device operations. struct Device(&'static RegisterBlock, PhantomData); impl Device { @@ -521,6 +808,34 @@ impl Device { 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] fn set_tx_buffer(&self, buffer: B) -> Result<(), Error> where @@ -606,6 +921,23 @@ impl Device { }) } + #[inline] + fn on_rx_drop(&self) -> OnDrop { + 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"); + }) + } + fn validate_buffer(buffer: B) -> Result<(u32, u32), Error> where B: Buffer, @@ -632,6 +964,56 @@ impl Device { } } +/// 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]. +#[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 AsRef<[T]> for AlignedBuffer { + fn as_ref(&self) -> &[T] { + self.0.as_slice() + } +} + +impl AsMut<[T]> for AlignedBuffer { + fn as_mut(&mut self) -> &mut [T] { + self.0.as_mut_slice() + } +} + +/// Common operations required for a buffer to be used by the DMA pub trait Buffer: Sized { fn bytes_ptr(&self) -> *const u8; fn bytes_len(&self) -> usize; @@ -674,22 +1056,25 @@ impl Buffer for &[i32] { } pub(crate) mod sealed { - use core::sync::atomic::AtomicI32; + use core::sync::atomic::AtomicBool; use embassy_sync::waitqueue::AtomicWaker; - use super::*; - + /// 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(), } } } @@ -704,8 +1089,6 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { type Interrupt: Interrupt; } -// TODO: Unsure why this macro is flagged as unused by CI when in fact it's used elsewhere? -#[allow(unused_macros)] macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::i2s::sealed::Instance for peripherals::$type { diff --git a/examples/nrf/src/bin/i2s.rs b/examples/nrf/src/bin/i2s-generate.rs similarity index 62% rename from examples/nrf/src/bin/i2s.rs rename to examples/nrf/src/bin/i2s-generate.rs index 9b3144f24..f59b63ce6 100644 --- a/examples/nrf/src/bin/i2s.rs +++ b/examples/nrf/src/bin/i2s-generate.rs @@ -4,59 +4,41 @@ use core::f32::consts::PI; -use defmt::{error, info, trace}; +use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::i2s::{Channels, MckFreq, Mode, Ratio, SampleWidth, MODE_MASTER_32000}; -use embassy_nrf::pac::ficr::info; -use embassy_nrf::{i2s, interrupt}; +use embassy_nrf::i2s::{self, Sample as _}; +use embassy_nrf::interrupt; use {defmt_rtt as _, panic_probe as _}; -#[repr(align(4))] -pub struct AlignedBuffer(T); - -impl AsRef for AlignedBuffer { - fn as_ref(&self) -> &T { - &self.0 - } -} - -impl AsMut for AlignedBuffer { - fn as_mut(&mut self) -> &mut T { - &mut self.0 - } -} - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); + let mut config = i2s::Config::default(); - config.mode = MODE_MASTER_32000; - // config.mode = Mode::Master { - // freq: MckFreq::_32MDiv10, - // ratio: Ratio::_256x, - // }; // 12500 Hz - config.channels = Channels::Left; - config.swidth = SampleWidth::_16bit; + config.mode = i2s::ExactSampleRate::_50000.into(); + config.channels = i2s::Channels::Left; + config.swidth = i2s::SampleWidth::_16bit; let sample_rate = config.mode.sample_rate().expect("I2S Master"); let inv_sample_rate = 1.0 / sample_rate as f32; info!("Sample rate: {}", sample_rate); // Wait for a button press + // use embassy_nrf::gpio::{Input, Pin, Pull}; // let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up); // btn1.wait_for_low().await; let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_11, p.P0_30, config).output(); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config) + .output(); type Sample = i16; - const MAX_UNIPOLAR_VALUE: Sample = (1 << 15) as Sample; - const NUM_SAMPLES: usize = 2000; - let mut buffers: [AlignedBuffer<[Sample; NUM_SAMPLES]>; 3] = [ - AlignedBuffer([0; NUM_SAMPLES]), - AlignedBuffer([0; NUM_SAMPLES]), - AlignedBuffer([0; NUM_SAMPLES]), + const NUM_SAMPLES: usize = 6000; + + let mut buffers: [i2s::AlignedBuffer; 3] = [ + i2s::AlignedBuffer::default(), + i2s::AlignedBuffer::default(), + i2s::AlignedBuffer::default(), ]; let mut carrier = SineOsc::new(); @@ -66,32 +48,29 @@ async fn main(_spawner: Spawner) { freq_mod.set_amplitude(1.0); let mut amp_mod = SineOsc::new(); - amp_mod.set_frequency(4.0, inv_sample_rate); + amp_mod.set_frequency(16.0, inv_sample_rate); amp_mod.set_amplitude(0.5); let mut generate = |buf: &mut [Sample]| { - let ptr = buf as *const [Sample] as *const Sample as u32; - trace!("GEN: {}", ptr); - - for sample in &mut buf.as_mut().chunks_mut(1) { - let signal = carrier.generate(); + for sample in &mut buf.chunks_mut(1) { let freq_modulation = bipolar_to_unipolar(freq_mod.generate()); - carrier.set_frequency(220.0 + 220.0 * freq_modulation, inv_sample_rate); + carrier.set_frequency(220.0 + 440.0 * freq_modulation, inv_sample_rate); let amp_modulation = bipolar_to_unipolar(amp_mod.generate()); carrier.set_amplitude(amp_modulation); - let value = (MAX_UNIPOLAR_VALUE as f32 * signal) as Sample; + let signal = carrier.generate(); + let value = (Sample::SCALE as f32 * signal) as Sample; sample[0] = value; } }; - generate(buffers[0].as_mut().as_mut_slice()); - generate(buffers[1].as_mut().as_mut_slice()); + generate(buffers[0].as_mut()); + generate(buffers[1].as_mut()); - i2s.start(buffers[0].as_ref().as_slice()).expect("I2S Start"); + i2s.start(buffers[0].as_ref()).await.expect("I2S Start"); let mut index = 1; loop { - if let Err(err) = i2s.send(buffers[index].as_ref().as_slice()).await { + if let Err(err) = i2s.send(buffers[index].as_ref()).await { error!("{}", err); } @@ -99,7 +78,7 @@ async fn main(_spawner: Spawner) { if index >= 3 { index = 0; } - generate(buffers[index].as_mut().as_mut_slice()); + generate(buffers[index].as_mut()); } } From 16838f8a66d8a3b74f0fe1ab9124532226e7166a Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 00:32:09 +0100 Subject: [PATCH 0355/1575] Fix format --- examples/nrf/src/bin/i2s-generate.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/nrf/src/bin/i2s-generate.rs b/examples/nrf/src/bin/i2s-generate.rs index f59b63ce6..c2b5578f3 100644 --- a/examples/nrf/src/bin/i2s-generate.rs +++ b/examples/nrf/src/bin/i2s-generate.rs @@ -29,8 +29,7 @@ async fn main(_spawner: Spawner) { // btn1.wait_for_low().await; let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config) - .output(); + let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config).output(); type Sample = i16; const NUM_SAMPLES: usize = 6000; From 64e8cfef8e53293b35d2c5ea2b822bdc3a12111e Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 01:38:03 +0100 Subject: [PATCH 0356/1575] Fix build --- embassy-nrf/src/chips/nrf52832.rs | 7 ++++++- embassy-nrf/src/chips/nrf52833.rs | 7 ++++++- embassy-nrf/src/chips/nrf52840.rs | 2 +- embassy-nrf/src/i2s.rs | 7 ++----- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 81e66c193..a0aaba9df 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); @@ -234,6 +237,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 +279,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 92499e3c9..9063f486a 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")] @@ -280,6 +283,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 +325,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 cf800c7b4..4047ee0ac 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -330,7 +330,6 @@ pub mod irqs { declare!(PWM2); declare!(SPIM2_SPIS2_SPI2); declare!(RTC2); - declare!(I2S); declare!(FPU); declare!(USBD); declare!(UARTE1); @@ -338,4 +337,5 @@ pub mod irqs { declare!(CRYPTOCELL); declare!(PWM3); declare!(SPIM3); + declare!(I2S); } diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 53d9f9a1b..52b72df27 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -14,13 +14,10 @@ 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::Peripheral; +use crate::{EASY_DMA_SIZE, Peripheral}; // TODO: Define those in lib.rs somewhere else -/// I2S EasyDMA MAXCNT bit length = 14 -const MAX_DMA_MAXCNT: u32 = 1 << 14; - /// Limits for Easy DMA - it can only read from data ram pub const SRAM_LOWER: usize = 0x2000_0000; pub const SRAM_UPPER: usize = 0x3000_0000; @@ -956,7 +953,7 @@ impl Device { Err(Error::BufferLengthMisaligned) } else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { Err(Error::BufferNotInDataMemory) - } else if maxcnt > MAX_DMA_MAXCNT { + } else if maxcnt as usize > EASY_DMA_SIZE { Err(Error::BufferTooLong) } else { Ok((ptr, maxcnt)) From f5391efe22374e0c55005f20c6a644bda3acd50c Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 02:17:58 +0100 Subject: [PATCH 0357/1575] Fix fmt --- embassy-nrf/src/i2s.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 52b72df27..11c09a229 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -14,7 +14,7 @@ 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::{EASY_DMA_SIZE, Peripheral}; +use crate::{Peripheral, EASY_DMA_SIZE}; // TODO: Define those in lib.rs somewhere else From 15a93246d6bb3e0bea268ff919bca073a9890247 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 19 Nov 2022 19:18:20 +0100 Subject: [PATCH 0358/1575] Buffer management in line with other peripherals. Constructor and config redesign --- embassy-nrf/src/i2s.rs | 721 ++++++++---------- .../bin/{i2s-generate.rs => i2s_waveform.rs} | 103 ++- 2 files changed, 395 insertions(+), 429 deletions(-) rename examples/nrf/src/bin/{i2s-generate.rs => i2s_waveform.rs} (50%) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 11c09a229..d5815160a 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -4,6 +4,8 @@ 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; @@ -14,14 +16,9 @@ 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}; -// TODO: Define those in lib.rs somewhere else - -/// Limits for Easy DMA - it can only read from data ram -pub const SRAM_LOWER: usize = 0x2000_0000; -pub const SRAM_UPPER: usize = 0x3000_0000; - #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -33,159 +30,42 @@ pub enum Error { BufferLengthMisaligned, } -/// 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 [Mode]. -#[derive(Clone, Copy)] -pub enum ApproxSampleRate { - _11025, - _16000, - _22050, - _32000, - _44100, - _48000, -} - -impl From for Mode { - fn from(value: ApproxSampleRate) -> Self { - match value { - // error = 86 - ApproxSampleRate::_11025 => Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_192x, - }, - // error = 127 - ApproxSampleRate::_16000 => Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_96x, - }, - // error = 172 - ApproxSampleRate::_22050 => Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_96x, - }, - // error = 254 - ApproxSampleRate::_32000 => Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_48x, - }, - // error = 344 - ApproxSampleRate::_44100 => Mode::Master { - freq: MckFreq::_32MDiv15, - ratio: Ratio::_48x, - }, - // error = 381 - ApproxSampleRate::_48000 => Mode::Master { - freq: MckFreq::_32MDiv21, - ratio: Ratio::_32x, - }, - } - } -} - -impl ApproxSampleRate { - pub fn sample_rate(&self) -> u32 { - // This will always provide a Master mode, so it is safe to unwrap. - Mode::from(*self).sample_rate().unwrap() - } -} - -/// 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 { - // This will always provide a Master mode, so it is safe to unwrap. - Mode::from(*self).sample_rate().unwrap() - } -} - -impl From for Mode { - fn from(value: ExactSampleRate) -> Self { - match value { - ExactSampleRate::_8000 => Mode::Master { - freq: MckFreq::_32MDiv125, - ratio: Ratio::_32x, - }, - ExactSampleRate::_10582 => Mode::Master { - freq: MckFreq::_32MDiv63, - ratio: Ratio::_48x, - }, - ExactSampleRate::_12500 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_256x, - }, - ExactSampleRate::_15625 => Mode::Master { - freq: MckFreq::_32MDiv32, - ratio: Ratio::_64x, - }, - ExactSampleRate::_15873 => Mode::Master { - freq: MckFreq::_32MDiv63, - ratio: Ratio::_32x, - }, - ExactSampleRate::_25000 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_128x, - }, - ExactSampleRate::_31250 => Mode::Master { - freq: MckFreq::_32MDiv32, - ratio: Ratio::_32x, - }, - ExactSampleRate::_50000 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_64x, - }, - ExactSampleRate::_62500 => Mode::Master { - freq: MckFreq::_32MDiv16, - ratio: Ratio::_32x, - }, - ExactSampleRate::_100000 => Mode::Master { - freq: MckFreq::_32MDiv10, - ratio: Ratio::_32x, - }, - ExactSampleRate::_125000 => Mode::Master { - freq: MckFreq::_32MDiv8, - ratio: Ratio::_32x, - }, - } - } -} - /// I2S configuration. #[derive(Clone)] #[non_exhaustive] pub struct Config { - pub mode: Mode, - pub swidth: SampleWidth, + 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 { - mode: ExactSampleRate::_31250.into(), - swidth: SampleWidth::_16bit, + sample_width: SampleWidth::_16bit, align: Align::Left, format: Format::I2S, channels: Channels::Stereo, @@ -195,17 +75,20 @@ impl Default for Config { /// I2S Mode #[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub enum Mode { - Master { freq: MckFreq, ratio: Ratio }, - Slave, +pub struct MasterClock { + freq: MckFreq, + ratio: Ratio, } -impl Mode { - pub fn sample_rate(&self) -> Option { - match self { - Mode::Master { freq, ratio } => Some(freq.to_frequency() / ratio.to_divisor()), - Mode::Slave => None, - } +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() } } @@ -275,17 +158,106 @@ pub enum Ratio { 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[u8::from(*self) as usize] + Self::RATIOS[usize::from(*self)] } } -impl From for u8 { +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 { @@ -336,10 +308,8 @@ impl From for bool { #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Channels { Stereo, - /// Mono left - Left, - /// Mono right - Right, + MonoLeft, + MonoRight, } impl From for u8 { @@ -350,131 +320,160 @@ impl From for u8 { /// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. pub struct I2S<'d, T: Instance> { - _p: PeripheralRef<'d, T>, + 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 - pub fn new( + /// 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, - sdin: impl Peripheral

+ 'd, - sdout: impl Peripheral

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

+ 'd, irq: impl Peripheral

+ 'd, - mck: PeripheralRef<'d, AnyPin>, - sck: PeripheralRef<'d, AnyPin>, - lrck: PeripheralRef<'d, AnyPin>, - sdin: PeripheralRef<'d, AnyPin>, - sdout: PeripheralRef<'d, AnyPin>, + sck: impl Peripheral

+ 'd, + lrck: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(i2s, irq, mck, sck, lrck, sdin, sdout); - - Self::apply_config(&config); - Self::select_pins(mck, sck, lrck, sdin, sdout); - Self::setup_interrupt(irq); - - T::regs().enable.write(|w| w.enable().enabled()); - - Self { _p: i2s } + 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(self) -> Output<'d, T> { - Output { _p: self._p } + pub fn output(mut self, sdout: impl Peripheral

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

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

+ 'd, + sdout: impl Peripheral

+ 'd, + ) -> FullDuplexStream<'d, T> { + self.sdout = Some(sdout.into_ref().map_into()); + self.sdin = Some(sdin.into_ref().map_into()); + FullDuplexStream { _p: self.build() } } - fn apply_config(config: &Config) { + 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 config.mode { - Mode::Master { freq, ratio } => { + 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.into()) }); + c.ratio.write(|w| unsafe { w.ratio().bits(ratio.to_register_value()) }); } - Mode::Slave => { + None => { c.mode.write(|w| w.mode().slave()); } }; - c.swidth.write(|w| unsafe { w.swidth().bits(config.swidth.into()) }); - c.align.write(|w| w.align().bit(config.align.into())); - c.format.write(|w| w.format().bit(config.format.into())); + 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(config.channels.into()) }); + .write(|w| unsafe { w.channels().bits(self.config.channels.into()) }); } - fn select_pins( - mck: PeripheralRef<'d, AnyPin>, - sck: PeripheralRef<'d, AnyPin>, - lrck: PeripheralRef<'d, AnyPin>, - sdin: PeripheralRef<'d, AnyPin>, - sdout: PeripheralRef<'d, AnyPin>, - ) { + fn select_pins(&self) { let psel = &T::regs().psel; - psel.mck.write(|w| { - unsafe { w.bits(mck.psel_bits()) }; - w.connect().connected() - }); + 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(sck.psel_bits()) }; + unsafe { w.bits(self.sck.psel_bits()) }; w.connect().connected() }); psel.lrck.write(|w| { - unsafe { w.bits(lrck.psel_bits()) }; + unsafe { w.bits(self.lrck.psel_bits()) }; w.connect().connected() }); - psel.sdin.write(|w| { - unsafe { w.bits(sdin.psel_bits()) }; - w.connect().connected() - }); + if let Some(sdin) = &self.sdin { + psel.sdin.write(|w| { + unsafe { w.bits(sdin.psel_bits()) }; + w.connect().connected() + }); + } - psel.sdout.write(|w| { - unsafe { w.bits(sdout.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(irq: PeripheralRef<'d, T::Interrupt>) { - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + 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(); @@ -538,17 +537,32 @@ impl<'d, T: Instance> I2S<'d, T> { device.disable(); } - async fn send(buffer: B) -> Result<(), Error> + async fn send_from_ram(buffer_ptr: *const [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - trace!("SEND: {}", buffer.bytes_ptr() as u32); + trace!("SEND: {}", buffer_ptr as *const S as u32); - let device = Device::::new(); - let drop = device.on_tx_drop(); + slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?; compiler_fence(Ordering::SeqCst); + let device = Device::::new(); + + 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()); @@ -564,7 +578,7 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.set_tx_buffer(buffer)?; + device.update_tx(buffer_ptr)?; compiler_fence(Ordering::SeqCst); drop.defuse(); @@ -572,17 +586,33 @@ impl<'d, T: Instance> I2S<'d, T> { Ok(()) } - async fn receive(buffer: B) -> Result<(), Error> + async fn receive_from_ram(buffer_ptr: *mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - trace!("RECEIVE: {}", buffer.bytes_ptr() as u32); + trace!("RECEIVE: {}", buffer_ptr as *const S as u32); - let device = Device::::new(); - let drop = device.on_rx_drop(); + // 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(); + + 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()); @@ -598,9 +628,10 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.set_rx_buffer(buffer)?; + device.update_rx(buffer_ptr)?; compiler_fence(Ordering::SeqCst); + drop.defuse(); Ok(()) @@ -608,15 +639,15 @@ impl<'d, T: Instance> I2S<'d, T> { } /// I2S output -pub struct Output<'d, T: Instance> { +pub struct OutputStream<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> Output<'d, T> { +impl<'d, T: Instance> OutputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: B) -> Result<(), Error> + pub async fn start(&self, buffer: &[S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { let device = Device::::new(); @@ -627,7 +658,8 @@ impl<'d, T: Instance> Output<'d, T> { device.enable(); device.enable_tx(); - device.set_tx_buffer(buffer)?; + + device.update_tx(buffer as *const [S])?; s.started.store(true, Ordering::Relaxed); @@ -647,24 +679,24 @@ impl<'d, T: Instance> Output<'d, T> { /// The buffer must not be written while being used by the DMA, /// which takes two other `send`s being awaited. #[allow(unused_mut)] - pub async fn send(&mut self, buffer: B) -> Result<(), Error> + pub async fn send_from_ram(&mut self, buffer: &[S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - I2S::::send(buffer).await + I2S::::send_from_ram(buffer as *const [S]).await } } /// I2S input -pub struct Input<'d, T: Instance> { +pub struct InputStream<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> Input<'d, T> { +impl<'d, T: Instance> InputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: B) -> Result<(), Error> + pub async fn start(&self, buffer: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { let device = Device::::new(); @@ -675,7 +707,8 @@ impl<'d, T: Instance> Input<'d, T> { device.enable(); device.enable_rx(); - device.set_rx_buffer(buffer)?; + + device.update_rx(buffer as *mut [S])?; s.started.store(true, Ordering::Relaxed); @@ -695,24 +728,24 @@ impl<'d, T: Instance> Input<'d, T> { /// The buffer must not be read while being used by the DMA, /// which takes two other `receive`s being awaited. #[allow(unused_mut)] - pub async fn receive(&mut self, buffer: B) -> Result<(), Error> + pub async fn receive_from_ram(&mut self, buffer: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - I2S::::receive(buffer).await + I2S::::receive_from_ram(buffer as *mut [S]).await } } -/// I2S ful duplex (input & output) -pub struct FullDuplex<'d, T: Instance> { +/// I2S full duplex stream (input & output) +pub struct FullDuplexStream<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -impl<'d, T: Instance> FullDuplex<'d, T> { +impl<'d, T: Instance> FullDuplexStream<'d, T> { /// Prepare the initial buffers and start the I2S transfer. - pub async fn start(&self, buffer_out: B, buffer_in: B) -> Result<(), Error> + pub async fn start(&self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { let device = Device::::new(); @@ -724,8 +757,9 @@ impl<'d, T: Instance> FullDuplex<'d, T> { device.enable(); device.enable_tx(); device.enable_rx(); - device.set_tx_buffer(buffer_out)?; - device.set_rx_buffer(buffer_in)?; + + device.update_tx(buffer_out as *const [S])?; + device.update_rx(buffer_in as *mut [S])?; s.started.store(true, Ordering::Relaxed); @@ -745,12 +779,12 @@ impl<'d, T: Instance> FullDuplex<'d, T> { /// The buffers must not be written/read while being used by the DMA, /// which takes two other `send_and_receive` operations being awaited. #[allow(unused_mut)] - pub async fn send_and_receive(&mut self, buffer_out: B, buffer_in: B) -> Result<(), Error> + pub async fn send_and_receive_from_ram(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> where - B: Buffer, + S: Sample, { - I2S::::send(buffer_out).await?; - I2S::::receive(buffer_in).await?; + I2S::::send_from_ram(buffer_out as *const [S]).await?; + I2S::::receive_from_ram(buffer_in as *mut [S]).await?; Ok(()) } } @@ -833,38 +867,6 @@ impl Device { self.0.intenset.write(|w| w.stopped().set()); } - #[inline] - fn set_tx_buffer(&self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - let (ptr, maxcnt) = Self::validate_buffer(buffer)?; - self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); - Ok(()) - } - - #[inline] - fn set_rx_buffer(&self, buffer: B) -> Result<(), Error> - where - B: Buffer, - { - let (ptr, maxcnt) = Self::validate_buffer(buffer)?; - self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); - Ok(()) - } - - #[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(always)] fn reset_tx_ptr_event(&self) { trace!("TX PTR EVENT: Reset"); @@ -901,58 +903,44 @@ impl Device { self.0.intenclr.write(|w| w.rxptrupd().clear()); } - #[inline] - fn on_tx_drop(&self) -> OnDrop { - OnDrop::new(move || { - trace!("TX DROP: Stopping"); + #[inline(always)] + fn is_tx_ptr_updated(&self) -> bool { + self.0.events_txptrupd.read().bits() != 0 + } - 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"); - }) + #[inline(always)] + fn is_rx_ptr_updated(&self) -> bool { + self.0.events_rxptrupd.read().bits() != 0 } #[inline] - fn on_rx_drop(&self) -> OnDrop { - 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"); - }) + 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(()) } - fn validate_buffer(buffer: B) -> Result<(u32, u32), Error> - where - B: Buffer, - { - let ptr = buffer.bytes_ptr() as u32; - let len = buffer.bytes_len(); - let maxcnt = ((len + core::mem::size_of::() - 1) / core::mem::size_of::()) as u32; + #[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); - // TODO can we avoid repeating all those runtime checks for the same buffer again and again? - if ptr % 4 != 0 { Err(Error::BufferMisaligned) - } else if len % 4 != 0 { + } else if bytes_len % 4 != 0 { Err(Error::BufferLengthMisaligned) - } else if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { - Err(Error::BufferNotInDataMemory) } else if maxcnt as usize > EASY_DMA_SIZE { Err(Error::BufferTooLong) } else { @@ -998,60 +986,19 @@ impl Default for AlignedBuffer { } } -impl AsRef<[T]> for AlignedBuffer { - fn as_ref(&self) -> &[T] { +impl Deref for AlignedBuffer { + type Target = [T]; + fn deref(&self) -> &Self::Target { self.0.as_slice() } } -impl AsMut<[T]> for AlignedBuffer { - fn as_mut(&mut self) -> &mut [T] { +impl DerefMut for AlignedBuffer { + fn deref_mut(&mut self) -> &mut Self::Target { self.0.as_mut_slice() } } -/// Common operations required for a buffer to be used by the DMA -pub trait Buffer: Sized { - fn bytes_ptr(&self) -> *const u8; - fn bytes_len(&self) -> usize; -} - -impl Buffer for &[i8] { - #[inline] - fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() as *const u8 - } - - #[inline] - fn bytes_len(&self) -> usize { - self.len() - } -} - -impl Buffer for &[i16] { - #[inline] - fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() as *const u8 - } - - #[inline] - fn bytes_len(&self) -> usize { - self.len() * core::mem::size_of::() - } -} - -impl Buffer for &[i32] { - #[inline] - fn bytes_ptr(&self) -> *const u8 { - self.as_ptr() as *const u8 - } - - #[inline] - fn bytes_len(&self) -> usize { - self.len() * core::mem::size_of::() - } -} - pub(crate) mod sealed { use core::sync::atomic::AtomicBool; diff --git a/examples/nrf/src/bin/i2s-generate.rs b/examples/nrf/src/bin/i2s_waveform.rs similarity index 50% rename from examples/nrf/src/bin/i2s-generate.rs rename to examples/nrf/src/bin/i2s_waveform.rs index c2b5578f3..81858ff59 100644 --- a/examples/nrf/src/bin/i2s-generate.rs +++ b/examples/nrf/src/bin/i2s_waveform.rs @@ -6,33 +6,29 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{self, Sample as _}; +use embassy_nrf::i2s::{self, Channels, Config, MasterClock, Sample as _, SampleWidth, I2S}; use embassy_nrf::interrupt; use {defmt_rtt as _, panic_probe as _}; +type Sample = i16; + +const NUM_SAMPLES: usize = 6000; + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let mut config = i2s::Config::default(); - config.mode = i2s::ExactSampleRate::_50000.into(); - config.channels = i2s::Channels::Left; - config.swidth = i2s::SampleWidth::_16bit; - let sample_rate = config.mode.sample_rate().expect("I2S Master"); - let inv_sample_rate = 1.0 / sample_rate as f32; + let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); + let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - // Wait for a button press - // use embassy_nrf::gpio::{Input, Pin, Pull}; - // let mut btn1 = Input::new(p.P1_00.degrade(), Pull::Up); - // btn1.wait_for_low().await; + let config = Config::default() + .sample_width(SampleWidth::_16bit) + .channels(Channels::MonoLeft); let irq = interrupt::take!(I2S); - let mut i2s = i2s::I2S::new(p.I2S, irq, p.P0_28, p.P0_29, p.P0_31, p.P0_27, p.P0_30, config).output(); - - type Sample = i16; - const NUM_SAMPLES: usize = 6000; + 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); let mut buffers: [i2s::AlignedBuffer; 3] = [ i2s::AlignedBuffer::default(), @@ -40,36 +36,16 @@ async fn main(_spawner: Spawner) { i2s::AlignedBuffer::default(), ]; - let mut carrier = SineOsc::new(); + let mut waveform = Waveform::new(1.0 / sample_rate as f32); - let mut freq_mod = SineOsc::new(); - freq_mod.set_frequency(8.0, inv_sample_rate); - freq_mod.set_amplitude(1.0); + waveform.process(&mut buffers[0]); + waveform.process(&mut buffers[1]); - let mut amp_mod = SineOsc::new(); - amp_mod.set_frequency(16.0, inv_sample_rate); - amp_mod.set_amplitude(0.5); - - let mut generate = |buf: &mut [Sample]| { - for sample in &mut buf.chunks_mut(1) { - let freq_modulation = bipolar_to_unipolar(freq_mod.generate()); - carrier.set_frequency(220.0 + 440.0 * freq_modulation, inv_sample_rate); - let amp_modulation = bipolar_to_unipolar(amp_mod.generate()); - carrier.set_amplitude(amp_modulation); - let signal = carrier.generate(); - let value = (Sample::SCALE as f32 * signal) as Sample; - sample[0] = value; - } - }; - - generate(buffers[0].as_mut()); - generate(buffers[1].as_mut()); - - i2s.start(buffers[0].as_ref()).await.expect("I2S Start"); + output_stream.start(&buffers[0]).await.expect("I2S Start"); let mut index = 1; loop { - if let Err(err) = i2s.send(buffers[index].as_ref()).await { + if let Err(err) = output_stream.send_from_ram(&buffers[index]).await { error!("{}", err); } @@ -77,11 +53,54 @@ async fn main(_spawner: Spawner) { if index >= 3 { index = 0; } - generate(buffers[index].as_mut()); + + waveform.process(&mut buffers[index]); + } +} + +struct Waveform { + inv_sample_rate: f32, + carrier: SineOsc, + freq_mod: SineOsc, + amp_mod: SineOsc, +} + +impl Waveform { + fn new(inv_sample_rate: f32) -> Self { + let carrier = SineOsc::new(); + + let mut freq_mod = SineOsc::new(); + freq_mod.set_frequency(8.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; + } } } -#[derive(Clone)] struct SineOsc { amplitude: f32, modulo: f32, From 06fb3e425198609131e149c39a2db25aaa8c273c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 21 Nov 2022 11:24:53 +0100 Subject: [PATCH 0359/1575] docs: add missing README for usb-logger --- embassy-usb-logger/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 embassy-usb-logger/README.md diff --git a/embassy-usb-logger/README.md b/embassy-usb-logger/README.md new file mode 100644 index 000000000..81b0dcd0e --- /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); +} +``` From 81dc532d2d041466f2e815f5b9e97a856c47073c Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Sun, 20 Nov 2022 16:08:43 -0500 Subject: [PATCH 0360/1575] Fix LoRaWAN PHY settings for SX126x driver * Set preamble length to 8 symbols * Set polarity to inverted for received messages --- embassy-lora/src/sx126x/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index ed8cb4059..b14d422a7 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?; From a6d941fac3d08512f7ef90131d7189ae3aa83bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 00:55:05 +0100 Subject: [PATCH 0361/1575] Fix txonly/rxonly data pin dir, _from_ram and doc --- embassy-nrf/src/spis.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 3f77c61d1..e005f7b92 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -78,7 +78,7 @@ impl<'d, T: Instance> Spis<'d, T> { irq: impl Peripheral

+ 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, - mosi: impl Peripheral

+ 'd, + miso: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(cs, sck, mosi); @@ -87,8 +87,8 @@ impl<'d, T: Instance> Spis<'d, T> { irq, cs.map_into(), sck.map_into(), + Some(miso.map_into()), None, - Some(mosi.map_into()), config, ) } @@ -98,7 +98,7 @@ impl<'d, T: Instance> Spis<'d, T> { 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); @@ -107,8 +107,8 @@ impl<'d, T: Instance> Spis<'d, T> { irq, cs.map_into(), sck.map_into(), - Some(miso.map_into()), None, + Some(mosi.map_into()), config, ) } @@ -355,7 +355,7 @@ impl<'d, T: Instance> Spis<'d, T> { } } - /// Reads data from the SPI bus without sending anything. Blocks until the buffer has been filled. + /// 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) @@ -371,7 +371,7 @@ impl<'d, T: Instance> Spis<'d, T> { /// 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(read, write) + self.blocking_inner_from_ram(read, write) } /// Simultaneously sends and receives data. @@ -391,7 +391,7 @@ impl<'d, T: Instance> Spis<'d, T> { /// 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(&mut [], data).map(|n| n.1) + self.blocking_inner_from_ram(&mut [], data).map(|n| n.1) } /// Reads data from the SPI bus without sending anything. From e6b9722a31fe0a61d1ef66aa796be89cd95b1ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 01:07:59 +0100 Subject: [PATCH 0362/1575] Remove nrf9160 UARTE fix --- embassy-nrf/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 5726f1181..587e19be5 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -268,12 +268,5 @@ 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 } From 633ffe46aea29bb4c8eec030cbfd6b93867fe79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 01:57:00 +0100 Subject: [PATCH 0363/1575] config write, docs, add address_match_index --- embassy-nrf/src/lib.rs | 6 ------ embassy-nrf/src/twis.rs | 22 +++++++++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 6c5a3202a..7f20f4fd6 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -268,11 +268,5 @@ 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/twis.rs b/embassy-nrf/src/twis.rs index 769522877..b8cb2eeb8 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -135,7 +135,7 @@ impl<'d, T: Instance> Twis<'d, T> { // Set address r.address[0].write(|w| unsafe { w.address().bits(config.address0) }); - r.config.modify(|_r, w| w.address0().enabled()); + 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()); @@ -248,6 +248,11 @@ impl<'d, T: Instance> Twis<'d, T> { 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(); @@ -588,10 +593,11 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(()) } - /// Listen for commands from an I2C master. - /// + /// 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()?; @@ -620,10 +626,11 @@ impl<'d, T: Instance> Twis<'d, T> { // =========================================== - /// Listen for commands from an I2C master with timeout. - /// + /// 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)?; @@ -654,10 +661,11 @@ impl<'d, T: Instance> Twis<'d, T> { // =========================================== - /// Listen asynchronously for commands from an I2C master. - /// + /// 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?; From 908eef2775df870a8c04138cb81400c5b3ecf9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 02:03:34 +0100 Subject: [PATCH 0364/1575] Change interrupt modify into write --- embassy-rp/src/adc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index cdb752dcc..025c6f917 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -65,7 +65,7 @@ impl<'d> Adc<'d> { irq.disable(); irq.set_handler(|_| unsafe { let r = Self::regs(); - r.inte().modify(|w| w.set_fifo(false)); + r.inte().write(|w| w.set_fifo(false)); WAKER.wake(); }); irq.unpend(); @@ -77,7 +77,7 @@ impl<'d> Adc<'d> { async fn wait_for_ready() { let r = Self::regs(); unsafe { - r.inte().modify(|w| w.set_fifo(true)); + r.inte().write(|w| w.set_fifo(true)); compiler_fence(Ordering::SeqCst); poll_fn(|cx| { WAKER.register(cx.waker()); From da9f82f5079ace916b8b5d26fe261cb98fb483fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 02:13:03 +0100 Subject: [PATCH 0365/1575] Fix pin refs --- embassy-nrf/src/spis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 61c5fe998..44af61a19 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -80,7 +80,7 @@ impl<'d, T: Instance> Spis<'d, T> { miso: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(cs, sck, mosi); + into_ref!(cs, sck, miso); Self::new_inner( spis, irq, @@ -100,7 +100,7 @@ impl<'d, T: Instance> Spis<'d, T> { mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(cs, sck, miso); + into_ref!(cs, sck, mosi); Self::new_inner( spis, irq, From f09745dfe122d723beeba5cd58ae9d6abdf6dddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 02:21:06 +0100 Subject: [PATCH 0366/1575] embassy-nrf: Default disable UARTE (nrf9160) --- embassy-nrf/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bc70fc2f6..b6fe046cf 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -267,5 +267,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 } From f47481787279ce0809cf983deb272815c3654b85 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 22 Nov 2022 09:35:42 +0100 Subject: [PATCH 0367/1575] doc: add README to embassy-macro Documents the main and task macros. --- embassy-macros/README.md | 21 +++++++++++++++++ embassy-macros/src/lib.rs | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 embassy-macros/README.md diff --git a/embassy-macros/README.md b/embassy-macros/README.md new file mode 100644 index 000000000..d1d6f4cc4 --- /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 ec8498f9f..f5df2a269 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,6 +45,24 @@ 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 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 { let args = syn::parse_macro_input!(args as syn::AttributeArgs); From 51233c0357c5ef694c6751c2cf62c54777e2baf0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 22 Nov 2022 14:48:42 +0100 Subject: [PATCH 0368/1575] doc: update cargo manifests with keywords --- embassy-executor/Cargo.toml | 8 +++++++- embassy-macros/Cargo.toml | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d0f51646d..910d6fa37 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -3,7 +3,13 @@ name = "embassy-executor" version = "0.1.0" 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/" diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 91d5ec8a3..98e4d1c70 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"] } From 536b6a2de5c5342a27dc1095f5642792fb6d860b Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 22 Nov 2022 21:55:10 +0800 Subject: [PATCH 0369/1575] sync/signal: Implement Default for Signal --- embassy-sync/src/signal.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index c3c10a8af..bea67d8be 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, From a074cd0625d68e72694c6575063ae53a840d12dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ardelean=20C=C4=83lin=20Petru?= Date: Tue, 22 Nov 2022 16:56:04 +0200 Subject: [PATCH 0370/1575] Update gpiote.rs Adding these changes enables us to define a channel using a mutable reference to `GPIOTE_CH(n)`, similar to how we can do with other drivers. So instead of using: ```rust let freq_in = InputChannel::new( p.GPIOTE_CH0, Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up), embassy_nrf::gpiote::InputChannelPolarity::HiToLo, ); ``` we can use: ```rust let freq_in = InputChannel::new( &mut p.GPIOTE_CH0, Input::new(&mut p.P0_19, embassy_nrf::gpio::Pull::Up), embassy_nrf::gpiote::InputChannelPolarity::HiToLo, ); ``` --- embassy-nrf/src/gpiote.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 25ad90496..4f11f33ef 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -419,6 +419,12 @@ macro_rules! impl_channel { $number as usize } } + impl sealed::Channel for &mut peripherals::$type {} + impl Channel for &mut peripherals::$type { + fn number(&self) -> usize { + $number as usize + } + } }; } From 64c2e1b9b670fda7446a0df6eeb2db5dce2aa1c2 Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Tue, 22 Nov 2022 17:35:38 +0200 Subject: [PATCH 0371/1575] Switched to PeripheralRef for channel. --- embassy-nrf/src/gpiote.rs | 43 ++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 4f11f33ef..6fac2c371 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, Peripheral, PeripheralRef, into_ref}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; @@ -148,21 +148,23 @@ 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>, } impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self.ch.number(); + let num = self._ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } } 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(); @@ -183,12 +185,12 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { g.events_in[num].reset(); - InputChannel { ch, pin } + InputChannel { _ch: ch, pin } } pub async fn wait(&self) { let g = regs(); - let num = self.ch.number(); + let num = self._ch.number(); // Enable interrupt g.events_in[num].reset(); @@ -209,27 +211,28 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { /// Returns the IN event, for use with PPI. pub fn event_in(&self) -> Event { let g = regs(); - Event::from_reg(&g.events_in[self.ch.number()]) + Event::from_reg(&g.events_in[self._ch.number()]) } } /// GPIOTE channel driver in output mode pub struct OutputChannel<'d, C: Channel, T: GpioPin> { - ch: C, + _ch: PeripheralRef<'d, C>, _pin: Output<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self.ch.number(); + let num = self._ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } } 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(); @@ -252,47 +255,47 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { unsafe { w.psel().bits(pin.pin.pin.pin()) } }); - OutputChannel { ch, _pin: pin } + OutputChannel { _ch: ch, _pin: pin } } /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { let g = regs(); - g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_out[self._ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task set` (set associated pin high). #[cfg(not(feature = "nrf51"))] pub fn set(&self) { let g = regs(); - g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_set[self._ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task clear` (set associated pin low). #[cfg(not(feature = "nrf51"))] pub fn clear(&self) { let g = regs(); - g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_clr[self._ch.number()].write(|w| unsafe { w.bits(1) }); } /// Returns the OUT task, for use with PPI. pub fn task_out(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_out[self.ch.number()]) + Task::from_reg(&g.tasks_out[self._ch.number()]) } /// Returns the CLR task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_clr(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_clr[self.ch.number()]) + Task::from_reg(&g.tasks_clr[self._ch.number()]) } /// Returns the SET task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_set(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_set[self.ch.number()]) + Task::from_reg(&g.tasks_set[self._ch.number()]) } } @@ -419,12 +422,6 @@ macro_rules! impl_channel { $number as usize } } - impl sealed::Channel for &mut peripherals::$type {} - impl Channel for &mut peripherals::$type { - fn number(&self) -> usize { - $number as usize - } - } }; } From e7c876d7444fb24ad854bd7339c480874f72dbe2 Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Tue, 22 Nov 2022 17:36:22 +0200 Subject: [PATCH 0372/1575] Changed pin to private as it is for OutputChannel --- embassy-nrf/src/gpiote.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 6fac2c371..e3be02407 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -149,7 +149,7 @@ impl Iterator for BitIter { /// GPIOTE channel driver in input mode pub struct InputChannel<'d, C: Channel, T: GpioPin> { _ch: PeripheralRef<'d, C>, - pin: Input<'d, T>, + _pin: Input<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { @@ -185,7 +185,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { g.events_in[num].reset(); - InputChannel { _ch: ch, pin } + InputChannel { _ch: ch, _pin: pin } } pub async fn wait(&self) { @@ -443,11 +443,11 @@ mod eh02 { type Error = Infallible; fn is_high(&self) -> Result { - Ok(self.pin.is_high()) + Ok(self._pin.is_high()) } fn is_low(&self) -> Result { - Ok(self.pin.is_low()) + Ok(self._pin.is_low()) } } } @@ -462,11 +462,11 @@ mod eh1 { impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> { fn is_high(&self) -> Result { - Ok(self.pin.is_high()) + Ok(self._pin.is_high()) } fn is_low(&self) -> Result { - Ok(self.pin.is_low()) + Ok(self._pin.is_low()) } } } From 4f2f3757773fb30700c3c6ee7ab98cd6e38406a3 Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Tue, 22 Nov 2022 17:45:05 +0200 Subject: [PATCH 0373/1575] Corrected order of use statements. --- embassy-nrf/src/gpiote.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index e3be02407..4d5fa62b0 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, into_ref}; +use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; From cf900a8a3f048428cc1209763f4188366818ab8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Nov 2022 22:10:04 +0100 Subject: [PATCH 0374/1575] Rename write to respond_to_read --- embassy-nrf/src/twis.rs | 52 ++++++++++++++++++++---------------- examples/nrf/src/bin/twis.rs | 11 ++++---- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index b8cb2eeb8..4091b017e 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -507,7 +507,7 @@ impl<'d, T: Instance> Twis<'d, T> { }) } - fn setup_write_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> { + fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> { let r = T::regs(); compiler_fence(SeqCst); @@ -532,14 +532,14 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(()) } - fn setup_write(&mut self, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { - match self.setup_write_from_ram(wr_buffer, inten) { + 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_write_from_ram(&tx_ram_buf, inten) + self.setup_respond_from_ram(&tx_ram_buf, inten) } Err(error) => Err(error), } @@ -609,18 +609,19 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - /// Write to an I2C master. + /// 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_write(&mut self, buffer: &[u8]) -> Result { - self.setup_write(buffer, false)?; + pub fn blocking_respond_to_read(&mut self, buffer: &[u8]) -> Result { + self.setup_respond(buffer, false)?; self.blocking_wait() } - /// Same as [`blocking_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result { - self.setup_write_from_ram(buffer, false)?; + /// 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() } @@ -643,19 +644,24 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - /// Write to an I2C master with timeout. + /// Respond to an I2C master READ command with timeout. /// Returns the number of bytes written. - /// See [`blocking_write`]. + /// See [`blocking_respond_to_read`]. #[cfg(feature = "time")] - pub fn blocking_write_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { - self.setup_write(buffer, false)?; + 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_write`](Twis::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + /// 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_write_from_ram_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { - self.setup_write_from_ram(buffer, false)?; + 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) } @@ -677,18 +683,18 @@ impl<'d, T: Instance> Twis<'d, T> { Ok(Command::Read) } - /// Async write to an I2C master. + /// 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 write(&mut self, buffer: &[u8]) -> Result { - self.setup_write(buffer, true)?; + pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { + self.setup_respond(buffer, true)?; self.async_wait().await } - /// Same as [`write`](Twis::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. - pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result { - self.setup_write_from_ram(buffer, true)?; + /// 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 } } diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf/src/bin/twis.rs index a34bb2711..54cba9494 100644 --- a/examples/nrf/src/bin/twis.rs +++ b/examples/nrf/src/bin/twis.rs @@ -22,20 +22,21 @@ async fn main(_spawner: Spawner) { 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]; - let tx_buf = [1, 2, 3, 4, 5, 6, 7, 8]; match i2c.listen(&mut buf).await { Ok(Command::Read) => { - info!("Got READ command. Writing back data:\n{:?}\n", tx_buf); - if let Err(e) = i2c.write(&tx_buf).await { + 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!("Writing back data:\n{:?}\n", tx_buf); - if let Err(e) = i2c.write(&tx_buf).await { + info!("Respond with data:\n{:?}\n", response); + if let Err(e) = i2c.respond_to_read(&response).await { error!("{:?}", e); } } From 28991d77941da2d77fcb242e025af06ceb936460 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 23 Nov 2022 17:30:58 +0530 Subject: [PATCH 0375/1575] added blinky example for stm32f0 --- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f0/src/bin/blinky.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f0/src/bin/blinky.rs diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a56c546ee..d4dc81cb5 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,5 +15,5 @@ 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 000000000..9f923399c --- /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; + } +} From eae67d0be888d12e71bc3340279bab85666c05ae Mon Sep 17 00:00:00 2001 From: Ardelean Calin Date: Wed, 23 Nov 2022 14:16:18 +0200 Subject: [PATCH 0376/1575] Review comments. Corrected unused fields. --- embassy-nrf/src/gpiote.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 4d5fa62b0..7f7468a20 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -148,14 +148,14 @@ impl Iterator for BitIter { /// GPIOTE channel driver in input mode pub struct InputChannel<'d, C: Channel, T: GpioPin> { - _ch: PeripheralRef<'d, C>, - _pin: Input<'d, T>, + ch: PeripheralRef<'d, C>, + pin: Input<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self._ch.number(); + let num = self.ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } @@ -185,12 +185,12 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { g.events_in[num].reset(); - InputChannel { _ch: ch, _pin: pin } + InputChannel { ch, pin } } pub async fn wait(&self) { let g = regs(); - let num = self._ch.number(); + let num = self.ch.number(); // Enable interrupt g.events_in[num].reset(); @@ -211,20 +211,20 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { /// Returns the IN event, for use with PPI. pub fn event_in(&self) -> Event { let g = regs(); - Event::from_reg(&g.events_in[self._ch.number()]) + Event::from_reg(&g.events_in[self.ch.number()]) } } /// GPIOTE channel driver in output mode pub struct OutputChannel<'d, C: Channel, T: GpioPin> { - _ch: PeripheralRef<'d, C>, + ch: PeripheralRef<'d, C>, _pin: Output<'d, T>, } impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { fn drop(&mut self) { let g = regs(); - let num = self._ch.number(); + let num = self.ch.number(); g.config[num].write(|w| w.mode().disabled()); g.intenclr.write(|w| unsafe { w.bits(1 << num) }); } @@ -255,47 +255,47 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { unsafe { w.psel().bits(pin.pin.pin.pin()) } }); - OutputChannel { _ch: ch, _pin: pin } + OutputChannel { ch, _pin: pin } } /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { let g = regs(); - g.tasks_out[self._ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task set` (set associated pin high). #[cfg(not(feature = "nrf51"))] pub fn set(&self) { let g = regs(); - g.tasks_set[self._ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); } /// Triggers `task clear` (set associated pin low). #[cfg(not(feature = "nrf51"))] pub fn clear(&self) { let g = regs(); - g.tasks_clr[self._ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); } /// Returns the OUT task, for use with PPI. pub fn task_out(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_out[self._ch.number()]) + Task::from_reg(&g.tasks_out[self.ch.number()]) } /// Returns the CLR task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_clr(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_clr[self._ch.number()]) + Task::from_reg(&g.tasks_clr[self.ch.number()]) } /// Returns the SET task, for use with PPI. #[cfg(not(feature = "nrf51"))] pub fn task_set(&self) -> Task { let g = regs(); - Task::from_reg(&g.tasks_set[self._ch.number()]) + Task::from_reg(&g.tasks_set[self.ch.number()]) } } @@ -443,11 +443,11 @@ mod eh02 { type Error = Infallible; fn is_high(&self) -> Result { - Ok(self._pin.is_high()) + Ok(self.pin.is_high()) } fn is_low(&self) -> Result { - Ok(self._pin.is_low()) + Ok(self.pin.is_low()) } } } @@ -462,11 +462,11 @@ mod eh1 { impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> { fn is_high(&self) -> Result { - Ok(self._pin.is_high()) + Ok(self.pin.is_high()) } fn is_low(&self) -> Result { - Ok(self._pin.is_low()) + Ok(self.pin.is_low()) } } } From 50c5cc5db64f7ddf8566626f92c0694ac9ad984e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 23 Nov 2022 13:17:05 +0100 Subject: [PATCH 0377/1575] fix: revert race condition introduced for riscv --- embassy-executor/src/arch/riscv32.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 76eb8b114..2a4b006da 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -55,11 +55,19 @@ impl Executor { unsafe { self.inner.poll(); // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. - // if there is work to do, loop back to polling - if !SIGNAL_WORK_THREAD_MODE.fetch_and(false, Ordering::SeqCst) { - core::arch::asm!("wfi"); - } + //will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } + // if not, wait for interrupt + else { + core::arch::asm!("wfi"); + } + }); + // if an interrupt occurred while waiting, it will be serviced here } } } From 5aad2129ef2eefd6ea2f2bda05f6c0e6172ba1c8 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 23 Nov 2022 17:51:43 +0530 Subject: [PATCH 0378/1575] added the runner for stm32f091rc --- examples/stm32f0/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f0/.cargo/config.toml b/examples/stm32f0/.cargo/config.toml index d1b1cd0bf..16abc29bc 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" From 04a7d976733e021395ff26e26dfa983e67b773a0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 22 Nov 2022 22:04:42 +0100 Subject: [PATCH 0379/1575] refactor: autodetect macro variant Export all main macro per target architecture from embassy-macros, and select the appropriate macro in embassy-executor. --- embassy-executor/Cargo.toml | 5 +- embassy-executor/src/lib.rs | 10 ++- embassy-macros/Cargo.toml | 4 -- embassy-macros/src/lib.rs | 81 +++++++++++++++++++++- embassy-macros/src/macros/main.rs | 108 ++++++++++++++++-------------- 5 files changed, 145 insertions(+), 63 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 45b0955bf..6fa1dd7fb 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -29,9 +29,8 @@ flavors = [ [features] default = [] -std = ["embassy-macros/std", "critical-section/std"] -wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"] -riscv = ["embassy-macros/riscv"] +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 e4cbd04b9..4c7e2f4cd 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-macros/Cargo.toml b/embassy-macros/Cargo.toml index 9b83771c8..5c612c997 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -21,9 +21,5 @@ proc-macro2 = "1.0.29" proc-macro = true [features] -std = [] -wasm = [] -riscv = [] - # Enabling this cause interrupt::take! to require embassy-executor rtos-trace-interrupt = [] diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index f5df2a269..d2c696c72 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -45,7 +45,7 @@ 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 spawning the corresponding function body as an async task. +/// 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: /// @@ -64,10 +64,85 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { /// } /// ``` #[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 54806847c..18f7c36c4 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,57 +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"), not(feature = "riscv")))] - 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"), not(feature = "riscv")))] - 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)); - }) - } - }; - - #[cfg(all(not(feature = "std"), not(feature = "wasm"), feature = "riscv"))] - let main = 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)); - }) - } - }; - let result = quote! { #[::embassy_executor::task()] async fn __embassy_main(#fargs) { From db7e153fc012aff0cc2c6529d5d26a3d184051a8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 23 Nov 2022 14:49:40 +0100 Subject: [PATCH 0380/1575] executor: enable features for docs.rs Otherwise the non-raw executor and the macros don't show up. --- embassy-executor/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 6fa1dd7fb..5acad95e5 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -27,6 +27,9 @@ flavors = [ { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, ] +[package.metadata.docs.rs] +features = ["std", "nightly", "defmt"] + [features] default = [] std = ["critical-section/std"] From 758f5d7ea29f1df14d5ef59c82e4b7f22545d775 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 23 Nov 2022 14:53:18 +0100 Subject: [PATCH 0381/1575] Release embassy-executor v0.1.1 --- embassy-executor/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 5acad95e5..c2868eb98 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [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" From 89821846d77d85d940b87cfa4f62171bd532b27c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 23 Nov 2022 14:48:51 +0100 Subject: [PATCH 0382/1575] fix: add required metadata for embassy-boot --- embassy-boot/boot/Cargo.toml | 12 +++++++++++- embassy-boot/{ => boot}/README.md | 0 embassy-boot/boot/src/lib.rs | 2 +- embassy-boot/nrf/README.md | 26 ++++++++++++++++++++++++++ embassy-boot/nrf/src/lib.rs | 2 +- embassy-boot/stm32/README.md | 27 ++++++++++++++++++++------- embassy-boot/stm32/src/lib.rs | 2 +- 7 files changed, 60 insertions(+), 11 deletions(-) rename embassy-boot/{ => boot}/README.md (100%) create mode 100644 embassy-boot/nrf/README.md diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 54c67a375..0cc6a0584 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -2,13 +2,23 @@ edition = "2021" name = "embassy-boot" version = "0.1.0" -description = "Bootloader using Embassy" +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 429323ec9..76b14bc8c 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 000000000..02f35c0a6 --- /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 82475d1e2..205bbd6df 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/stm32/README.md b/embassy-boot/stm32/README.md index a82b730b9..cb134b534 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 d549eccc6..82f712c4d 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}; From 09077f133d4962ef4fbe6ae7acc1420fb0da768c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 25 Nov 2022 15:51:31 +0100 Subject: [PATCH 0383/1575] fix: bump embassy-boot version --- embassy-boot/boot/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 0cc6a0584..ae4efbd2e 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot" -version = "0.1.0" +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" From 1e2fb0459d8546ba658bb9fe150be5f1f537b48e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 21 Nov 2022 23:31:31 +0100 Subject: [PATCH 0384/1575] Switch to async-fn-in-trait --- embassy-embedded-hal/Cargo.toml | 2 +- embassy-embedded-hal/src/adapter.rs | 79 +-- embassy-embedded-hal/src/lib.rs | 6 +- .../src/shared_bus/asynch/i2c.rs | 107 ++- .../src/shared_bus/asynch/spi.rs | 70 +- embassy-lora/Cargo.toml | 2 +- embassy-net/Cargo.toml | 4 +- embassy-net/src/lib.rs | 6 +- embassy-net/src/tcp.rs | 118 ++-- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/buffered_uarte.rs | 66 +- embassy-nrf/src/gpiote.rs | 62 +- embassy-nrf/src/lib.rs | 6 +- embassy-nrf/src/spim.rs | 31 +- embassy-nrf/src/twim.rs | 28 +- embassy-nrf/src/uarte.rs | 26 +- embassy-nrf/src/usb.rs | 393 +++++------ embassy-rp/Cargo.toml | 4 +- embassy-rp/src/gpio.rs | 63 +- embassy-rp/src/i2c.rs | 75 +-- embassy-rp/src/lib.rs | 3 +- embassy-rp/src/spi.rs | 32 +- embassy-rp/src/uart/buffered.rs | 60 +- embassy-rp/src/usb.rs | 583 ++++++++--------- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/exti.rs | 36 +- embassy-stm32/src/i2c/v2.rs | 34 +- embassy-stm32/src/lib.rs | 6 +- embassy-stm32/src/spi/mod.rs | 32 +- embassy-stm32/src/usart/buffered.rs | 66 +- embassy-stm32/src/usb/usb.rs | 612 ++++++++---------- embassy-sync/Cargo.toml | 2 +- embassy-time/Cargo.toml | 2 +- embassy-usb-driver/src/lib.rs | 69 +- examples/nrf/Cargo.toml | 4 +- examples/rp/Cargo.toml | 4 +- examples/std/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 6 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 5 - rust-toolchain.toml | 2 +- tests/rp/Cargo.toml | 4 +- tests/stm32/Cargo.toml | 2 +- 47 files changed, 1153 insertions(+), 1579 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 85ee856a6..fa74be8c4 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 1c43f015f..3680984f1 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 a12a3a3a0..8da042228 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 0bc6afd98..c5e1fd415 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 a3814d6d0..d25716655 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-lora/Cargo.toml b/embassy-lora/Cargo.toml index dc2004172..cbe78e592 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-net/Cargo.toml b/embassy-net/Cargo.toml index 76217075a..0ac53b50d 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,7 +52,7 @@ 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 = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0", optional = true } [dependencies.smoltcp] version = "0.8.0" diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 4d30550d3..edb969842 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/tcp.rs b/embassy-net/src/tcp.rs index f3bd2361c..85d9e5ee1 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -271,8 +271,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 +290,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 +310,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 +320,18 @@ 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::mem::MaybeUninit; use core::ptr::NonNull; @@ -385,28 +358,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 +419,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-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 67b6bec40..6b06d5d05 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 9c8fe65f4..ea25236f0 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/gpiote.rs b/embassy-nrf/src/gpiote.rs index 7f7468a20..7467dbc60 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -473,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/lib.rs b/embassy-nrf/src/lib.rs index dc6f16867..9054bc300 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", diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index d821d2353..7bb4e39f7 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/twim.rs b/embassy-nrf/src/twim.rs index 8d6171fac..4eafd18c2 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/uarte.rs b/embassy-nrf/src/uarte.rs index 636d6c7a3..63df1b682 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 eb1472fa5..ed4d5cf35 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 04b0c13ce..770d8e25a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -53,7 +53,7 @@ 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" } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } @@ -61,5 +61,5 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90 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/gpio.rs b/embassy-rp/src/gpio.rs index f79f592b4..71390306f 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -870,9 +870,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> { @@ -991,57 +988,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 d6742f6a6..e48e16d81 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 6c91b1adc..e5b07c903 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,5 +1,6 @@ #![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; diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 754e2dd30..2b7a818d9 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 4f0a55532..fa466c8a1 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,4 +1,4 @@ -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; @@ -355,11 +355,7 @@ 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> { + async fn read(&mut self, buf: &mut [u8]) -> Result { poll_fn(move |cx| { let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); @@ -372,15 +368,12 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { res }) + .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> { + async fn read(&mut self, buf: &mut [u8]) -> Result { poll_fn(move |cx| { let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); @@ -393,21 +386,19 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { res }) + .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> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); state.rx.fill_buf(cx.waker()) }) }) + .await } fn consume(&mut self, amt: usize) { @@ -419,17 +410,14 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } 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> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); state.fill_buf(cx.waker()) }) }) + .await } fn consume(&mut self, amt: usize) { @@ -441,11 +429,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } 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> { + async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); if empty { @@ -453,23 +437,16 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } poll }) + .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> { + poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))).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> { + async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); if empty { @@ -477,13 +454,10 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> } poll }) + .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> { + poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))).await } } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 6dc90b98e..32fc2632d 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<'a>(&'a 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 6b00518a6..b7f718c5f 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/exti.rs b/embassy-stm32/src/exti.rs index dca991859..f90785815 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/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index aa4e6bb08..47dc7d2a4 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 bcf2feee8..d7443eace 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; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 396427782..17198fc23 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -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 0a6d6e149..acd96d7c6 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}; @@ -339,32 +339,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 +361,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 +371,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 2654f156a..0ba06cce2 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<'a>(&'a 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-sync/Cargo.toml b/embassy-sync/Cargo.toml index b7fe1643c..1eeb94c9a 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-time/Cargo.toml b/embassy-time/Cargo.toml index 9487003cc..5701ab351 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-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 931e9c318..85e9267d3 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); @@ -209,85 +196,57 @@ pub trait Bus { /// /// * [`Unsupported`](crate::driver::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<'a>(&'a 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/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index c633f82f5..64dbc663e 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -17,7 +17,7 @@ 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 } @@ -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/rp/Cargo.toml b/examples/rp/Cargo.toml index 250228862..364f738dd 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -29,8 +29,8 @@ 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" diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 790258382..41680f8f4 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/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index b05457eaa..594b61612 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = "0.3" 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 b14afd2fe..caabe068e 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -10,7 +10,7 @@ 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" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 0dccff6e8..de9459998 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,7 +10,7 @@ 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" @@ -19,8 +19,8 @@ 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 = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0" } 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/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 8b00773be..f5fd18f11 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -22,7 +22,7 @@ defmt = "0.3" defmt-rtt = "0.3" 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/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 83d456b26..9092d85c7 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -20,7 +20,7 @@ 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 848723f8b..376e9e519 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -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 3d704011b..1cf1078dc 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -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/rust-toolchain.toml b/rust-toolchain.toml index c05aac3fc..55539405f 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 069b7fb8c..a6102b270 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -18,10 +18,10 @@ 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/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 602c1fb57..f74a3c700 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -26,7 +26,7 @@ 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] From 7b838d03369f94e09d652982f994c5013e81457e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 7 Nov 2022 00:27:21 +0100 Subject: [PATCH 0385/1575] rp/uart: use lockfree ringbuffer. This gets rid of another PeripheralMutex usage. --- embassy-hal-common/src/atomic_ring_buffer.rs | 331 +++++++++ embassy-hal-common/src/lib.rs | 1 + embassy-rp/Cargo.toml | 2 +- embassy-rp/src/uart/buffered.rs | 706 ++++++++++--------- embassy-rp/src/uart/mod.rs | 91 ++- tests/rp/src/bin/uart_buffered.rs | 13 +- 6 files changed, 758 insertions(+), 386 deletions(-) create mode 100644 embassy-hal-common/src/atomic_ring_buffer.rs 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 000000000..c5e444306 --- /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 5d2649d02..b2a35cd35 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-rp/Cargo.toml b/embassy-rp/Cargo.toml index 770d8e25a..daa60f9c5 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. diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index fa466c8a1..32029f81e 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,337 +1,421 @@ -use core::future::poll_fn; -use core::task::{Poll, Waker}; +use core::future::{poll_fn, Future}; +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); + }); } } } @@ -356,108 +440,52 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - 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 - }) - .await + BufferedUartRx::<'d, T>::read(buf).await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - 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 - }) - .await + Self::read(buf).await } } impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.rx.fill_buf(cx.waker()) - }) - }) - .await + 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> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - poll_fn(move |cx| { - self.inner.with(|state| { - compiler_fence(Ordering::SeqCst); - state.fill_buf(cx.waker()) - }) - }) - .await + 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> { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); - if empty { - self.inner.pend(); - } - poll - }) - .await + BufferedUartTx::<'d, T>::write(buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))).await + BufferedUartTx::<'d, T>::flush().await } } impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); - if empty { - self.inner.pend(); - } - poll - }) - .await + Self::write(buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))).await + Self::flush().await } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 56c25e189..7e7bcaf30 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/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 9cc20bb98..bea9283e7 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); From 6b8ab32536bf2e831ec424b5aaf489bb1f53d017 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Sat, 26 Nov 2022 15:22:31 +0100 Subject: [PATCH 0386/1575] Use &mut self for start methods --- embassy-nrf/src/i2s.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index d5815160a..bc90dbc98 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -645,7 +645,8 @@ pub struct OutputStream<'d, T: Instance> { impl<'d, T: Instance> OutputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: &[S]) -> Result<(), Error> + #[allow(unused_mut)] + pub async fn start(&mut self, buffer: &[S]) -> Result<(), Error> where S: Sample, { @@ -694,7 +695,8 @@ pub struct InputStream<'d, T: Instance> { impl<'d, T: Instance> InputStream<'d, T> { /// Prepare the initial buffer and start the I2S transfer. - pub async fn start(&self, buffer: &mut [S]) -> Result<(), Error> + #[allow(unused_mut)] + pub async fn start(&mut self, buffer: &mut [S]) -> Result<(), Error> where S: Sample, { @@ -743,7 +745,8 @@ pub struct FullDuplexStream<'d, T: Instance> { impl<'d, T: Instance> FullDuplexStream<'d, T> { /// Prepare the initial buffers and start the I2S transfer. - pub async fn start(&self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> + #[allow(unused_mut)] + pub async fn start(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> where S: Sample, { From d438d1b685acb41b29d01c64bc422836760cb3de Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 16:24:20 -0500 Subject: [PATCH 0387/1575] sync: Fix nightly feature compilation after upgrade to embedded-io 0.4.0 --- embassy-sync/src/lib.rs | 3 +- embassy-sync/src/pipe.rs | 74 ++++++++++------------------------------ 2 files changed, 20 insertions(+), 57 deletions(-) diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 80bb907a3..f9435ecff 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 cd577f34f..905686acd 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(()) } } } From 3ca14ba4e9feee2f0d34c8dd1c0e426d2090d8d8 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 16:26:58 -0500 Subject: [PATCH 0388/1575] usb-driver: Remove unncessary lifetime --- embassy-rp/src/usb.rs | 2 +- embassy-stm32/src/usb/usb.rs | 2 +- embassy-usb-driver/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 32fc2632d..dfc2e9da6 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -638,7 +638,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 64 } - async fn setup<'a>(&'a mut self) -> [u8; 8] { + async fn setup(&mut self) -> [u8; 8] { loop { trace!("SETUP read waiting"); let regs = T::regs(); diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 0ba06cce2..460abfe28 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -799,7 +799,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { usize::from(self.max_packet_size) } - async fn setup<'a>(&'a mut self) -> [u8; 8] { + async fn setup(&mut self) -> [u8; 8] { loop { trace!("SETUP read waiting"); poll_fn(|cx| { diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 85e9267d3..0a76ba6fb 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -220,7 +220,7 @@ pub trait ControlPipe { fn max_packet_size(&self) -> usize; /// Reads a single setup packet from the endpoint. - async fn setup<'a>(&'a mut self) -> [u8; 8]; + async fn setup(&mut self) -> [u8; 8]; /// Reads a DATA OUT packet into `buf` in response to a control write request. /// From 4d84b5469ece6e7ad1597b6da41972a0ea391672 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 16:32:18 -0500 Subject: [PATCH 0389/1575] Drive-by documentation link fixes --- embassy-rp/src/rtc/mod.rs | 2 +- embassy-usb-driver/src/lib.rs | 4 ++-- embassy-usb/src/class/hid.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index e4b6f0b1d..c173909c7 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-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 0a76ba6fb..9300ff812 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -184,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) @@ -194,7 +194,7 @@ 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. async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 4d1fa995f..b967aba0e 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. From aedcc472c9fa133f73fcf3a6139d178c81159452 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Sun, 27 Nov 2022 17:59:01 -0500 Subject: [PATCH 0390/1575] time: Fix nighly feature compilation after upgrade to embedded-hal-async 0.2.0-alpha.0 --- embassy-time/src/delay.rs | 16 ++++------------ embassy-time/src/lib.rs | 3 ++- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs index ff6b6869a..0ca176abd 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 586aa28de..8b0aebe19 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)] From 787e5d49078de4b09919711e6d9870a7c2a86b32 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 28 Nov 2022 00:12:13 +0100 Subject: [PATCH 0391/1575] Add -time, -sync to CI with all features. --- ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index cd1c0786c..a99a5f323 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 \ From 2a35a0944420ecf3d33e9684b9bb5756500cb851 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Tue, 6 Sep 2022 17:44:20 +0200 Subject: [PATCH 0392/1575] stm32: Fix H7 unaligned erase --- embassy-stm32/src/flash/h7.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 3f2129de8..3178b3be9 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; } From 4cc046312359d0a930eb7c6c78df25c1fc1ea6e6 Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 23 Nov 2022 10:11:19 +0100 Subject: [PATCH 0393/1575] stm32: Add basic support for DMA priority settings --- embassy-stm32/src/dma/bdma.rs | 7 +++++-- embassy-stm32/src/dma/dma.rs | 7 +++++-- embassy-stm32/src/dma/mod.rs | 8 +++++--- embassy-stm32/src/lib.rs | 17 ++++++++++++++++- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 674255ddc..e6ce05b7b 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 a45b8780b..97a3df088 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 cc030a93e..74bce6aa9 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/lib.rs b/embassy-stm32/src/lib.rs index d7443eace..16c46ca22 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -79,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")] @@ -91,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 { @@ -99,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, } } } @@ -137,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(); From 199504be564b231154e07c58bcc52b11afdc9fe7 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Tue, 29 Nov 2022 01:09:47 +0100 Subject: [PATCH 0394/1575] Optimization to be able to work with only 2 buffers --- embassy-nrf/src/i2s.rs | 48 ++++++++++++++++++++-------- examples/nrf/src/bin/i2s_waveform.rs | 20 ++++++------ 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index bc90dbc98..08d4093f2 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -398,6 +398,7 @@ impl<'d, T: Instance> I2S<'d, T> { ) -> FullDuplexStream<'d, T> { self.sdout = Some(sdout.into_ref().map_into()); self.sdin = Some(sdin.into_ref().map_into()); + FullDuplexStream { _p: self.build() } } @@ -549,6 +550,16 @@ impl<'d, T: Instance> I2S<'d, T> { 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"); @@ -566,6 +577,7 @@ impl<'d, T: Instance> I2S<'d, T> { 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(); @@ -578,12 +590,7 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.update_tx(buffer_ptr)?; - - compiler_fence(Ordering::SeqCst); drop.defuse(); - - Ok(()) } async fn receive_from_ram(buffer_ptr: *mut [S]) -> Result<(), Error> @@ -599,6 +606,16 @@ impl<'d, T: Instance> I2S<'d, T> { 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"); @@ -616,6 +633,7 @@ impl<'d, T: Instance> I2S<'d, T> { 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(); @@ -628,13 +646,7 @@ impl<'d, T: Instance> I2S<'d, T> { }) .await; - device.update_rx(buffer_ptr)?; - - compiler_fence(Ordering::SeqCst); - drop.defuse(); - - Ok(()) } } @@ -666,6 +678,8 @@ impl<'d, T: Instance> OutputStream<'d, T> { device.start(); + I2S::::wait_tx_ptr_update().await; + Ok(()) } @@ -716,6 +730,8 @@ impl<'d, T: Instance> InputStream<'d, T> { device.start(); + I2S::::wait_rx_ptr_update().await; + Ok(()) } @@ -746,7 +762,7 @@ pub struct FullDuplexStream<'d, T: Instance> { impl<'d, T: Instance> FullDuplexStream<'d, T> { /// Prepare the initial buffers and start the I2S transfer. #[allow(unused_mut)] - pub async fn start(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> + pub async fn start(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> where S: Sample, { @@ -768,6 +784,9 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { device.start(); + I2S::::wait_tx_ptr_update().await; + I2S::::wait_rx_ptr_update().await; + Ok(()) } @@ -782,7 +801,7 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { /// The buffers must not be written/read while being used by the DMA, /// which takes two other `send_and_receive` operations being awaited. #[allow(unused_mut)] - pub async fn send_and_receive_from_ram(&mut self, buffer_out: &[S], buffer_in: &mut [S]) -> Result<(), Error> + pub async fn send_and_receive_from_ram(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> where S: Sample, { @@ -903,7 +922,7 @@ impl Device { #[inline(always)] fn enable_rx_ptr_interrupt(&self) { trace!("RX PTR INTERRUPT: Enabled"); - self.0.intenclr.write(|w| w.rxptrupd().clear()); + self.0.intenset.write(|w| w.rxptrupd().set()); } #[inline(always)] @@ -974,6 +993,7 @@ impl Sample for i32 { } /// A 4-bytes aligned [Buffer]. +#[derive(Clone, Copy)] #[repr(align(4))] pub struct AlignedBuffer([T; N]); diff --git a/examples/nrf/src/bin/i2s_waveform.rs b/examples/nrf/src/bin/i2s_waveform.rs index 81858ff59..13b1300ea 100644 --- a/examples/nrf/src/bin/i2s_waveform.rs +++ b/examples/nrf/src/bin/i2s_waveform.rs @@ -12,7 +12,8 @@ use {defmt_rtt as _, panic_probe as _}; type Sample = i16; -const NUM_SAMPLES: usize = 6000; +const NUM_BUFFERS: usize = 2; +const NUM_SAMPLES: usize = 50; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -30,31 +31,27 @@ async fn main(_spawner: Spawner) { let irq = interrupt::take!(I2S); 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); - let mut buffers: [i2s::AlignedBuffer; 3] = [ - i2s::AlignedBuffer::default(), - i2s::AlignedBuffer::default(), - i2s::AlignedBuffer::default(), - ]; + let mut buffers: [i2s::AlignedBuffer; NUM_BUFFERS] = + [i2s::AlignedBuffer::default(); NUM_BUFFERS]; let mut waveform = Waveform::new(1.0 / sample_rate as f32); waveform.process(&mut buffers[0]); - waveform.process(&mut buffers[1]); output_stream.start(&buffers[0]).await.expect("I2S Start"); let mut index = 1; loop { + waveform.process(&mut buffers[index]); + if let Err(err) = output_stream.send_from_ram(&buffers[index]).await { error!("{}", err); } index += 1; - if index >= 3 { + if index >= NUM_BUFFERS { index = 0; } - - waveform.process(&mut buffers[index]); } } @@ -67,7 +64,8 @@ struct Waveform { impl Waveform { fn new(inv_sample_rate: f32) -> Self { - let carrier = SineOsc::new(); + let mut carrier = SineOsc::new(); + carrier.set_frequency(110.0, inv_sample_rate); let mut freq_mod = SineOsc::new(); freq_mod.set_frequency(8.0, inv_sample_rate); From 3135ad016d580a2a5912c4ade8a1f61c1d0375d7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 29 Nov 2022 12:05:46 +0100 Subject: [PATCH 0395/1575] Bump embedded-nal-async to 0.3.0 --- embassy-net/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 0ac53b50d..86d4aa105 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -52,7 +52,7 @@ 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 = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0", optional = true } +embedded-nal-async = { version = "0.3.0", optional = true } [dependencies.smoltcp] version = "0.8.0" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index de9459998..f665f7c14 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -20,7 +20,7 @@ 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.2.0-alpha.0" } -embedded-nal-async = { git = "https://github.com/embassy-rs/embedded-nal.git", rev = "691601e550449a53ab3a7c5eaa0411aee0a64ed0" } +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 } From 1dcb0ea1f5dbd9357c7c05627f06abbcaaedd134 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 29 Nov 2022 21:15:24 +0100 Subject: [PATCH 0396/1575] Bump defmt-rtt to 0.4 --- embassy-boot/stm32/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 2 +- examples/boot/bootloader/stm32/Cargo.toml | 2 +- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 31 files changed, 31 insertions(+), 31 deletions(-) diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index 9d12c6cfd..2fc169b32 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/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index a5d82b601..1e7a5a84b 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/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 3a1843562..aa279fb76 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 8d9c4490e..1ec0643a6 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 b4314aa72..a4eefe2a5 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 a17d336a6..36eada29b 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 683f2c860..67efda748 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 b879c0d76..4b2e02dd2 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 e3bc0e49c..fecbfc51d 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 b417a40d1..8a6f53643 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/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 4ddd1c99c..be659e02a 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 64dbc663e..8b95ac3a6 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -24,7 +24,7 @@ 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" static_cell = "1.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 364f738dd..60a8ba94d 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -16,7 +16,7 @@ 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" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a56c546ee..8f4100488 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -10,7 +10,7 @@ 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"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 6be131f30..53f369b3a 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 f6adda2a3..afaf9a0c9 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 27188dd19..69ebef786 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 594b61612..62d3f08df 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -12,7 +12,7 @@ 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" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index caabe068e..f4d674cdc 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -13,7 +13,7 @@ embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp 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 f5673718d..e7273c9fc 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 ecda28805..8a57a8ef0 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 f665f7c14..11ce35053 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -13,7 +13,7 @@ embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp 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/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index f5fd18f11..86933a629 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -19,7 +19,7 @@ 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.4.0" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 9460febf5..6e3b2103c 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 9092d85c7..45d3dd366 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -14,7 +14,7 @@ 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" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 376e9e519..73ad50787 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"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 1cf1078dc..d88fdda50 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" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 5b96fa191..e27b4527c 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 c827d2b71..690481bbf 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/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index a6102b270..a07b479e4 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -12,7 +12,7 @@ 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" diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index f74a3c700..08a775eae 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -20,7 +20,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", "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" From e0ea5dfdb2f32974ea4d278402dbdd42d1530bbd Mon Sep 17 00:00:00 2001 From: Guillaume MICHEL Date: Wed, 30 Nov 2022 09:26:16 +0100 Subject: [PATCH 0397/1575] embassy-stm32: Allow SPI with DMA to implement blocking embbeded-hal traits --- embassy-stm32/src/spi/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 17198fc23..ab4352a5c 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) } From 71df28e269ae3157b4b07df7f270b2a81c08803e Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Tue, 29 Nov 2022 20:46:04 -0500 Subject: [PATCH 0398/1575] rp: Add an RngCore impl based on ROSC.RANDOMBIT This has the potential to not be random, but it should not be an issue if default clock settings are used. --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/clocks.rs | 39 ++++++++++++++++++++++++++++++++++++++- embassy-rp/src/lib.rs | 2 +- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index daa60f9c5..284d458c6 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -55,6 +55,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa chrono = { version = "0.4", default-features = false, 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"] } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1c446f389..85c9bbb7a 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/lib.rs b/embassy-rp/src/lib.rs index e5b07c903..d21b5f7b0 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -21,7 +21,7 @@ pub mod uart; #[cfg(feature = "nightly")] pub mod usb; -mod clocks; +pub mod clocks; pub mod flash; mod reset; From e1d7d8d841c59015d4600a66c65b7a8efffc62fc Mon Sep 17 00:00:00 2001 From: Matous Hybl Date: Wed, 30 Nov 2022 14:11:32 +0100 Subject: [PATCH 0399/1575] stm32: Enable fifo for buffered uart --- embassy-stm32/src/usart/buffered.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index acd96d7c6..d024bedcf 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -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); }); From f4c9014fe4d5bb96f583d4b96122bcc536631d18 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 1 Dec 2022 22:09:45 +0100 Subject: [PATCH 0400/1575] feat: use async fn in trait --- Cargo.toml | 2 +- examples/rpi-pico-w/Cargo.toml | 16 +++--- examples/rpi-pico-w/src/main.rs | 95 ++++++++++++++------------------- rust-toolchain.toml | 2 +- 4 files changed, 50 insertions(+), 65 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e1eddc19..cc19c9389 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,5 @@ cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.3" } +embedded-hal-async = { version = "0.2.0-alpha.0" } num_enum = { version = "0.5.7", default-features = false } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 7ba22a69e..bb44667de 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -22,18 +22,18 @@ cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } -embedded-hal-async = { version = "0.1.0-alpha.3" } -embedded-io = { version = "0.3.0", features = ["async", "defmt"] } +embedded-hal-async = { version = "0.2.0-alpha.0" } +embedded-io = { version = "0.4.0", features = ["async", "defmt"] } heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "c53614f057cd7d9ac6e86aebd1fb6c1a1055d8b6" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 705c7accb..a19f38591 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -1,9 +1,10 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] use core::convert::Infallible; -use core::future::Future; use defmt::*; use embassy_executor::Spawner; @@ -155,74 +156,58 @@ impl ErrorType for MySpi { } impl SpiBusFlush for MySpi { - type FlushFuture<'a> = impl Future> - where - Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) } } impl SpiBusRead for MySpi { - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; + async fn read(&mut self, words: &mut [u32]) -> Result<(), Self::Error> { + self.dio.set_as_input(); + for word in words { + let mut w = 0; + for _ in 0..32 { + w = w << 1; - fn read<'a>(&'a mut self, words: &'a mut [u32]) -> Self::ReadFuture<'a> { - async move { - self.dio.set_as_input(); - for word in words { - let mut w = 0; - for _ in 0..32 { - w = w << 1; - - // rising edge, sample data - if self.dio.is_high() { - w |= 0x01; - } - self.clk.set_high(); - - // falling edge - self.clk.set_low(); + // rising edge, sample data + if self.dio.is_high() { + w |= 0x01; } - *word = w - } + self.clk.set_high(); - Ok(()) + // falling edge + self.clk.set_low(); + } + *word = w } + + Ok(()) } } impl SpiBusWrite for MySpi { - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn write<'a>(&'a mut self, words: &'a [u32]) -> Self::WriteFuture<'a> { - async move { - self.dio.set_as_output(); - for word in words { - let mut word = *word; - for _ in 0..32 { - // falling edge, setup data - self.clk.set_low(); - if word & 0x8000_0000 == 0 { - self.dio.set_low(); - } else { - self.dio.set_high(); - } - - // rising edge - self.clk.set_high(); - - word = word << 1; + async fn write(&mut self, words: &[u32]) -> Result<(), Self::Error> { + self.dio.set_as_output(); + for word in words { + let mut word = *word; + for _ in 0..32 { + // falling edge, setup data + self.clk.set_low(); + if word & 0x8000_0000 == 0 { + self.dio.set_low(); + } else { + self.dio.set_high(); } - } - self.clk.set_low(); - self.dio.set_as_input(); - Ok(()) + // rising edge + self.clk.set_high(); + + word = word << 1; + } } + self.clk.set_low(); + + self.dio.set_as_input(); + Ok(()) } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3e219b0c2..ffbcbd6f9 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 = [ "thumbv6m-none-eabi", From bb89a2341cca1aad79bc6d5f3532008541c9e428 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 1 Dec 2022 18:26:22 +0100 Subject: [PATCH 0401/1575] feat: embassy-boot for rp2040 Add embassy-boot support for RP2040, with examples for the Raspberry Pi Pico. Co-authored-by: Mathias Koch --- ci.sh | 3 + docs/modules/ROOT/pages/bootloader.adoc | 1 + embassy-boot/rp/Cargo.toml | 71 ++++++ embassy-boot/rp/README.md | 26 ++ embassy-boot/rp/build.rs | 8 + embassy-boot/rp/src/fmt.rs | 225 ++++++++++++++++++ embassy-boot/rp/src/lib.rs | 90 +++++++ embassy-rp/src/flash.rs | 5 + .../boot/application/rp/.cargo/config.toml | 12 + examples/boot/application/rp/Cargo.toml | 33 +++ examples/boot/application/rp/README.md | 28 +++ examples/boot/application/rp/build.rs | 35 +++ examples/boot/application/rp/memory.x | 15 ++ examples/boot/application/rp/src/bin/a.rs | 52 ++++ examples/boot/application/rp/src/bin/b.rs | 23 ++ .../boot/bootloader/rp/.cargo/config.toml | 8 + examples/boot/bootloader/rp/Cargo.toml | 29 +++ examples/boot/bootloader/rp/README.md | 17 ++ examples/boot/bootloader/rp/build.rs | 28 +++ examples/boot/bootloader/rp/memory.x | 19 ++ examples/boot/bootloader/rp/src/main.rs | 51 ++++ 21 files changed, 779 insertions(+) create mode 100644 embassy-boot/rp/Cargo.toml create mode 100644 embassy-boot/rp/README.md create mode 100644 embassy-boot/rp/build.rs create mode 100644 embassy-boot/rp/src/fmt.rs create mode 100644 embassy-boot/rp/src/lib.rs create mode 100644 examples/boot/application/rp/.cargo/config.toml create mode 100644 examples/boot/application/rp/Cargo.toml create mode 100644 examples/boot/application/rp/README.md create mode 100644 examples/boot/application/rp/build.rs create mode 100644 examples/boot/application/rp/memory.x create mode 100644 examples/boot/application/rp/src/bin/a.rs create mode 100644 examples/boot/application/rp/src/bin/b.rs create mode 100644 examples/boot/bootloader/rp/.cargo/config.toml create mode 100644 examples/boot/bootloader/rp/Cargo.toml create mode 100644 examples/boot/bootloader/rp/README.md create mode 100644 examples/boot/bootloader/rp/build.rs create mode 100644 examples/boot/bootloader/rp/memory.x create mode 100644 examples/boot/bootloader/rp/src/main.rs diff --git a/ci.sh b/ci.sh index a99a5f323..9f79c87b9 100755 --- a/ci.sh +++ b/ci.sh @@ -81,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 \ @@ -106,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 \ @@ -114,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/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index 7dbfeb3eb..b50de5abd 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/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml new file mode 100644 index 000000000..93099b233 --- /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 000000000..c0c2d85fa --- /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 000000000..2cbc7ef5e --- /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 000000000..066970813 --- /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 000000000..85fc81827 --- /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-rp/src/flash.rs b/embassy-rp/src/flash.rs index d09cc62fb..a972d5f69 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/examples/boot/application/rp/.cargo/config.toml b/examples/boot/application/rp/.cargo/config.toml new file mode 100644 index 000000000..edbd0a867 --- /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 000000000..8d826790b --- /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 000000000..41304c526 --- /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 000000000..30691aa97 --- /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 000000000..c19473114 --- /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 000000000..3736c9141 --- /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 000000000..47dec329c --- /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/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml new file mode 100644 index 000000000..18bd4dfe8 --- /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 000000000..580ced22e --- /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 000000000..064e87273 --- /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 000000000..c201704ad --- /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 000000000..d6ef38469 --- /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 000000000..5028ec688 --- /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(); +} From 02abe00439ba873945bd6b60546a200b3da751f1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 3 Dec 2022 00:56:16 +0100 Subject: [PATCH 0402/1575] net: don't use UnsafeCell. The "must not be called reentrantly" invariant is too "global" to maintain comfortably, and the cost of the RefCell is negligible, so this was a case of premature optimization. --- embassy-net/src/stack.rs | 32 ++++++++++------------ embassy-net/src/tcp.rs | 59 ++++++++++++++++++---------------------- embassy-net/src/udp.rs | 41 ++++++++++++---------------- 3 files changed, 57 insertions(+), 75 deletions(-) diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 3a7610758..631087405 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; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 85d9e5ee1..60386535a 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,15 @@ 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, 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 +118,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 +145,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 +195,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 +215,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 +236,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) => { @@ -332,6 +324,7 @@ mod embedded_io_impls { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] pub mod client { + use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index f2e33493c..4ddad77d4 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); } } From a2712caab15a673b455184b6f56a7a8d7cdb9290 Mon Sep 17 00:00:00 2001 From: miathedev Date: Sun, 4 Dec 2022 09:38:57 +0100 Subject: [PATCH 0403/1575] Add random example --- examples/stm32wl/src/bin/random.rs | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 examples/stm32wl/src/bin/random.rs diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs new file mode 100644 index 000000000..182c607f9 --- /dev/null +++ b/examples/stm32wl/src/bin/random.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::pac; +use embassy_stm32::rng::Rng; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; + config.rcc.enable_lsi = true; //Needed for RNG to work + + let p = embassy_stm32::init(config); + unsafe { + pac::RCC.ccipr().modify(|w| { + w.set_rngsel(0b01); + }); + } + + info!("Hello World!"); + + let mut rng = Rng::new(p.RNG); + + let mut buf = [0u8; 16]; + unwrap!(rng.async_fill_bytes(&mut buf).await); + info!("random bytes: {:02x}", buf); + + loop {} +} From ef2b83cc032cd39841f0219144b95259dc5cac79 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Tue, 6 Dec 2022 16:36:07 +0200 Subject: [PATCH 0404/1575] Reset sdmmc clock on card init --- embassy-stm32/src/sdmmc/mod.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index c91f3c8bf..0f8483d5b 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 less 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)?; From 7cbc3aefe6540599e1361a8c3dc51e93e6f83dcd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 6 Dec 2022 19:54:39 +0100 Subject: [PATCH 0405/1575] rp: implement input for OutputOpenDrain --- embassy-rp/src/gpio.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 71390306f..930de2068 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; @@ -946,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; } From 54c153673d9e79ea36f687fbdfa3e6f56ff62fc8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 6 Dec 2022 21:09:27 +0100 Subject: [PATCH 0406/1575] rp: add OutputOpenDrain input test. --- tests/rp/src/bin/gpio.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index af22fe27d..80e92d0fd 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 From 7bda01ec240339f8b26d78910c64ad63a2bfc726 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Tue, 6 Dec 2022 23:31:58 +0200 Subject: [PATCH 0407/1575] Fix comment --- embassy-stm32/src/sdmmc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 0f8483d5b..a52c65b92 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -601,7 +601,7 @@ 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 less than 400 kHz. + // 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; From f7fe0c1441843b04fa17ba0fe94f8c8d4f851882 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 00:28:38 +0100 Subject: [PATCH 0408/1575] net: update smoltcp --- embassy-net/Cargo.toml | 2 +- embassy-net/src/device.rs | 14 ++++++-------- embassy-net/src/stack.rs | 21 ++++++--------------- embassy-net/src/tcp.rs | 5 ++++- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 86d4aa105..ac338843d 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -57,7 +57,7 @@ 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 c183bd58a..4bdfd7720 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/stack.rs b/embassy-net/src/stack.rs index 631087405..5c4fb0442 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -266,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 60386535a..73cf2d4e4 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -94,7 +94,10 @@ impl<'a> TcpSocket<'a> { { let local_port = self.io.stack.borrow_mut().get_local_port(); - match { 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), From 5fdd521a767fd8825a2d55d6b833fd99627353d7 Mon Sep 17 00:00:00 2001 From: Christian Perez Llamas <932644+chris-zen@users.noreply.github.com> Date: Thu, 8 Dec 2022 20:22:50 +0100 Subject: [PATCH 0409/1575] Move the responsibility to manage buffers to the I2S stream --- embassy-nrf/src/i2s.rs | 157 +++++++++++++++++++-------- examples/nrf/src/bin/i2s_effect.rs | 117 ++++++++++++++++++++ examples/nrf/src/bin/i2s_monitor.rs | 115 ++++++++++++++++++++ examples/nrf/src/bin/i2s_waveform.rs | 26 ++--- 4 files changed, 353 insertions(+), 62 deletions(-) create mode 100644 examples/nrf/src/bin/i2s_effect.rs create mode 100644 examples/nrf/src/bin/i2s_monitor.rs diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 08d4093f2..7e9507751 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -19,6 +19,8 @@ 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] @@ -379,27 +381,47 @@ impl<'d, T: Instance> I2S<'d, T> { } /// I2S output only - pub fn output(mut self, sdout: impl Peripheral

+ 'd) -> OutputStream<'d, T> { + 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() } + OutputStream { + _p: self.build(), + buffers, + } } /// I2S input only - pub fn input(mut self, sdin: impl Peripheral

+ 'd) -> InputStream<'d, T> { + 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() } + InputStream { + _p: self.build(), + buffers, + } } /// I2S full duplex (input and output) - pub fn full_duplex( + pub fn full_duplex( mut self, sdin: impl Peripheral

+ 'd, sdout: impl Peripheral

+ 'd, - ) -> FullDuplexStream<'d, T> { + 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() } + FullDuplexStream { + _p: self.build(), + buffers_out, + buffers_in, + } } fn build(self) -> PeripheralRef<'d, T> { @@ -651,14 +673,19 @@ impl<'d, T: Instance> I2S<'d, T> { } /// I2S output -pub struct OutputStream<'d, T: Instance> { +pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { _p: PeripheralRef<'d, T>, + buffers: MultiBuffering, } -impl<'d, T: Instance> OutputStream<'d, T> { +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. - #[allow(unused_mut)] - pub async fn start(&mut self, buffer: &[S]) -> Result<(), Error> + pub async fn start(&mut self) -> Result<(), Error> where S: Sample, { @@ -672,7 +699,7 @@ impl<'d, T: Instance> OutputStream<'d, T> { device.enable(); device.enable_tx(); - device.update_tx(buffer as *const [S])?; + device.update_tx(self.buffers.switch())?; s.started.store(true, Ordering::Relaxed); @@ -689,28 +716,30 @@ impl<'d, T: Instance> OutputStream<'d, T> { I2S::::stop().await } - /// Sets the given `buffer` for transmission in the DMA. - /// Buffer address must be 4 byte aligned and located in RAM. - /// The buffer must not be written while being used by the DMA, - /// which takes two other `send`s being awaited. - #[allow(unused_mut)] - pub async fn send_from_ram(&mut self, buffer: &[S]) -> Result<(), Error> + /// 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(buffer as *const [S]).await + I2S::::send_from_ram(self.buffers.switch()).await } } /// I2S input -pub struct InputStream<'d, T: Instance> { +pub struct InputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { _p: PeripheralRef<'d, T>, + buffers: MultiBuffering, } -impl<'d, T: Instance> InputStream<'d, T> { +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. - #[allow(unused_mut)] - pub async fn start(&mut self, buffer: &mut [S]) -> Result<(), Error> + pub async fn start(&mut self) -> Result<(), Error> where S: Sample, { @@ -724,7 +753,7 @@ impl<'d, T: Instance> InputStream<'d, T> { device.enable(); device.enable_rx(); - device.update_rx(buffer as *mut [S])?; + device.update_rx(self.buffers.switch())?; s.started.store(true, Ordering::Relaxed); @@ -741,28 +770,32 @@ impl<'d, T: Instance> InputStream<'d, T> { I2S::::stop().await } - /// Sets the given `buffer` for reception from the DMA. - /// Buffer address must be 4 byte aligned and located in RAM. - /// The buffer must not be read while being used by the DMA, - /// which takes two other `receive`s being awaited. + /// Sets the current buffer for reception from the DMA. + /// Switches to use the next available buffer. #[allow(unused_mut)] - pub async fn receive_from_ram(&mut self, buffer: &mut [S]) -> Result<(), Error> + pub async fn receive(&mut self) -> Result<(), Error> where S: Sample, { - I2S::::receive_from_ram(buffer as *mut [S]).await + I2S::::receive_from_ram(self.buffers.switch_mut()).await } } /// I2S full duplex stream (input & output) -pub struct FullDuplexStream<'d, T: Instance> { +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> FullDuplexStream<'d, T> { +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. - #[allow(unused_mut)] - pub async fn start(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> + pub async fn start(&mut self) -> Result<(), Error> where S: Sample, { @@ -777,8 +810,8 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { device.enable_tx(); device.enable_rx(); - device.update_tx(buffer_out as *const [S])?; - device.update_rx(buffer_in as *mut [S])?; + device.update_tx(self.buffers_out.switch())?; + device.update_rx(self.buffers_in.switch_mut())?; s.started.store(true, Ordering::Relaxed); @@ -796,17 +829,14 @@ impl<'d, T: Instance> FullDuplexStream<'d, T> { I2S::::stop().await } - /// Sets the given `buffer_out` and `buffer_in` for transmission/reception from the DMA. - /// Buffer address must be 4 byte aligned and located in RAM. - /// The buffers must not be written/read while being used by the DMA, - /// which takes two other `send_and_receive` operations being awaited. - #[allow(unused_mut)] - pub async fn send_and_receive_from_ram(&mut self, buffer_in: &mut [S], buffer_out: &[S]) -> Result<(), Error> + /// 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(buffer_out as *const [S]).await?; - I2S::::receive_from_ram(buffer_in as *mut [S]).await?; + I2S::::send_from_ram(self.buffers_out.switch()).await?; + I2S::::receive_from_ram(self.buffers_in.switch_mut()).await?; Ok(()) } } @@ -992,7 +1022,7 @@ impl Sample for i32 { const SCALE: Self = 1 << (Self::WIDTH - 1); } -/// A 4-bytes aligned [Buffer]. +/// A 4-bytes aligned buffer. #[derive(Clone, Copy)] #[repr(align(4))] pub struct AlignedBuffer([T; N]); @@ -1022,6 +1052,43 @@ impl DerefMut for AlignedBuffer { } } +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; diff --git a/examples/nrf/src/bin/i2s_effect.rs b/examples/nrf/src/bin/i2s_effect.rs new file mode 100644 index 000000000..3cca005b1 --- /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 000000000..48eb7d581 --- /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 index 13b1300ea..1b0e8ebc8 100644 --- a/examples/nrf/src/bin/i2s_waveform.rs +++ b/examples/nrf/src/bin/i2s_waveform.rs @@ -6,13 +6,12 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{self, Channels, Config, MasterClock, Sample as _, SampleWidth, I2S}; +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_BUFFERS: usize = 2; const NUM_SAMPLES: usize = 50; #[embassy_executor::main] @@ -29,29 +28,22 @@ async fn main(_spawner: Spawner) { .channels(Channels::MonoLeft); let irq = interrupt::take!(I2S); - 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); - - let mut buffers: [i2s::AlignedBuffer; NUM_BUFFERS] = - [i2s::AlignedBuffer::default(); NUM_BUFFERS]; + 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(&mut buffers[0]); + waveform.process(output_stream.buffer()); - output_stream.start(&buffers[0]).await.expect("I2S Start"); + output_stream.start().await.expect("I2S Start"); - let mut index = 1; loop { - waveform.process(&mut buffers[index]); + waveform.process(output_stream.buffer()); - if let Err(err) = output_stream.send_from_ram(&buffers[index]).await { + if let Err(err) = output_stream.send().await { error!("{}", err); } - - index += 1; - if index >= NUM_BUFFERS { - index = 0; - } } } @@ -68,7 +60,7 @@ impl Waveform { carrier.set_frequency(110.0, inv_sample_rate); let mut freq_mod = SineOsc::new(); - freq_mod.set_frequency(8.0, inv_sample_rate); + freq_mod.set_frequency(1.0, inv_sample_rate); freq_mod.set_amplitude(1.0); let mut amp_mod = SineOsc::new(); From 1d2f97b4e226871014c2cf470070343df15d74a0 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 9 Dec 2022 11:02:16 +0100 Subject: [PATCH 0410/1575] Fixed erase bug --- embassy-nrf/src/nvmc.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index ba6a59129..405ea3171 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -72,18 +72,13 @@ impl<'d> Nvmc<'d> { } #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] - fn erase_page(&mut self, page: u32) { - Self::regs().erasepage().write(|w| unsafe { w.bits(page) }); + fn erase_page(&mut self, page_addr: u32) { + Self::regs().erasepage().write(|w| unsafe { w.bits(page_addr) }); } #[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] - fn erase_page(&mut self, page: u32) { - #[cfg(not(feature = "_nrf5340-net"))] - const FLASH_START_ADDR: u32 = 0; - #[cfg(feature = "_nrf5340-net")] - const FLASH_START_ADDR: u32 = 0x100_0000; - - let first_page_word = (FLASH_START_ADDR + page * PAGE_SIZE as u32) as *mut u32; + fn erase_page(&mut self, page_addr: u32) { + let first_page_word = page_addr as *mut u32; unsafe { first_page_word.write_volatile(0xFFFF_FFFF); } @@ -150,8 +145,8 @@ impl<'d> NorFlash for Nvmc<'d> { self.enable_erase(); self.wait_ready(); - for page in (from..to).step_by(PAGE_SIZE) { - self.erase_page(page); + for page_addr in (from..to).step_by(PAGE_SIZE) { + self.erase_page(page_addr); self.wait_ready(); } From 236d1048444fbaeddf1210d237bf9ab319f60079 Mon Sep 17 00:00:00 2001 From: Vincent Stakenburg Date: Fri, 9 Dec 2022 14:26:09 +0100 Subject: [PATCH 0411/1575] embassy-stm32: add rs485 driver enable to uart --- embassy-stm32/build.rs | 2 ++ embassy-stm32/src/usart/buffered.rs | 27 +++++++++++++++++++++++++++ embassy-stm32/src/usart/mod.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index a4709f4ca..ddfa97b29 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -244,11 +244,13 @@ fn main() { (("usart", "CTS"), quote!(crate::usart::CtsPin)), (("usart", "RTS"), quote!(crate::usart::RtsPin)), (("usart", "CK"), quote!(crate::usart::CkPin)), + (("usart", "DE"), quote!(crate::usart::DePin)), (("lpuart", "TX"), quote!(crate::usart::TxPin)), (("lpuart", "RX"), quote!(crate::usart::RxPin)), (("lpuart", "CTS"), quote!(crate::usart::CtsPin)), (("lpuart", "RTS"), quote!(crate::usart::RtsPin)), (("lpuart", "CK"), quote!(crate::usart::CkPin)), + (("lpuart", "DE"), quote!(crate::usart::DePin)), (("spi", "SCK"), quote!(crate::spi::SckPin)), (("spi", "MOSI"), quote!(crate::spi::MosiPin)), (("spi", "MISO"), quote!(crate::spi::MisoPin)), diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index d024bedcf..874af1d73 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -89,6 +89,33 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) } + #[cfg(not(usart_v1))] + pub fn new_with_de( + state: &'d mut State<'d, T>, + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + de: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> BufferedUart<'d, T> { + into_ref!(de); + + T::enable(); + T::reset(); + + unsafe { + de.set_as_af(de.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_dem(true); + }); + } + + Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + } + fn new_inner( state: &'d mut State<'d, T>, _peri: impl Peripheral

+ 'd, diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index aea054a4b..ea75361fa 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -646,6 +646,31 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } + #[cfg(not(usart_v1))] + pub fn new_with_de( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, + de: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(de); + + T::enable(); + T::reset(); + + unsafe { + de.set_as_af(de.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_dem(true); + }); + } + Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) + } + fn new_inner( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -1040,6 +1065,7 @@ pin_trait!(TxPin, BasicInstance); pin_trait!(CtsPin, BasicInstance); pin_trait!(RtsPin, BasicInstance); pin_trait!(CkPin, BasicInstance); +pin_trait!(DePin, BasicInstance); dma_trait!(TxDma, BasicInstance); dma_trait!(RxDma, BasicInstance); From 35db6e639bf5de017551164e9cbfaa2e5e46fca3 Mon Sep 17 00:00:00 2001 From: Simon Berg Date: Wed, 27 Jul 2022 22:45:46 +0200 Subject: [PATCH 0412/1575] PIO support for RPi Pico --- embassy-rp/Cargo.toml | 8 + embassy-rp/src/gpio.rs | 42 +- embassy-rp/src/lib.rs | 9 + embassy-rp/src/pio.rs | 1250 ++++++++++++++++++++++++++++++ embassy-rp/src/pio_instr_util.rs | 90 +++ examples/rp/Cargo.toml | 7 +- examples/rp/src/bin/pio_async.rs | 105 +++ examples/rp/src/bin/pio_dma.rs | 67 ++ 8 files changed, 1576 insertions(+), 2 deletions(-) create mode 100644 embassy-rp/src/pio.rs create mode 100644 embassy-rp/src/pio_instr_util.rs create mode 100644 examples/rp/src/bin/pio_async.rs create mode 100644 examples/rp/src/bin/pio_dma.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 284d458c6..751875710 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -26,6 +26,7 @@ time-driver = [] rom-func-cache = [] intrinsics = [] rom-v2-intrinsics = [] +pio = ["dep:pio", "dep:pio-proc"] # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] @@ -64,3 +65,10 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} + +paste = "1.0" +pio-proc = {version= "0.2", optional = true} +pio = {version= "0.2", optional = true} + +[patch.crates-io] +pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 930de2068..315cd2aca 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -48,6 +48,21 @@ pub enum Pull { Down, } +/// Drive strength of an output +#[derive(Debug, Eq, PartialEq)] +pub enum Drive { + _2mA, + _4mA, + _8mA, + _12mA, +} +/// Slew rate of an output +#[derive(Debug, Eq, PartialEq)] +pub enum SlewRate { + Fast, + Slow, +} + /// A GPIO bank with up to 32 pins. #[derive(Debug, Eq, PartialEq)] pub enum Bank { @@ -459,7 +474,7 @@ impl<'d, T: Pin> Flex<'d, T> { #[inline] pub fn set_pull(&mut self, pull: Pull) { unsafe { - self.pin.pad_ctrl().write(|w| { + self.pin.pad_ctrl().modify(|w| { w.set_ie(true); match pull { Pull::Up => w.set_pue(true), @@ -470,6 +485,31 @@ impl<'d, T: Pin> Flex<'d, T> { } } + /// Set the pin's drive strength. + #[inline] + pub fn set_drive_strength(&mut self, strength: Drive) { + unsafe { + self.pin.pad_ctrl().modify(|w| { + w.set_drive(match strength { + Drive::_2mA => pac::pads::vals::Drive::_2MA, + Drive::_4mA => pac::pads::vals::Drive::_4MA, + Drive::_8mA => pac::pads::vals::Drive::_8MA, + Drive::_12mA => pac::pads::vals::Drive::_12MA, + }); + }); + } + } + + // Set the pin's slew rate. + #[inline] + pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { + unsafe { + self.pin.pad_ctrl().modify(|w| { + w.set_slewfast(slew_rate == SlewRate::Fast); + }); + } + } + /// Put the pin into input mode. /// /// The pull setting is left unchanged. diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d21b5f7b0..67eb29d5e 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -12,6 +12,12 @@ pub mod dma; pub mod gpio; pub mod i2c; pub mod interrupt; + +#[cfg(feature = "pio")] +pub mod pio; +#[cfg(feature = "pio")] +pub mod pio_instr_util; + pub mod rom_data; pub mod rtc; pub mod spi; @@ -102,6 +108,9 @@ embassy_hal_common::peripherals! { FLASH, ADC, + + PIO0, + PIO1, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs new file mode 100644 index 000000000..f049a16e7 --- /dev/null +++ b/embassy-rp/src/pio.rs @@ -0,0 +1,1250 @@ +use core::future::Future; +use core::marker::PhantomData; +use core::pin::Pin as FuturePin; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; + +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_hal_common::PeripheralRef; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::dma::{self, Channel, Transfer}; +use crate::gpio::sealed::Pin as SealedPin; +use crate::gpio::{Drive, Pin, Pull, SlewRate}; +use crate::pac::dma::vals::{DataSize, TreqSel}; +use crate::{interrupt, pac, peripherals}; + +const PIOS: [&pac::pio::Pio; 2] = [&pac::PIO0, &pac::PIO1]; +const NEW_AW: AtomicWaker = AtomicWaker::new(); +const PIO_WAKERS_INIT: [AtomicWaker; 4] = [NEW_AW; 4]; +static FIFO_OUT_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; +static FIFO_IN_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; +static IRQ_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; + +pub enum FifoJoin { + /// Both TX and RX fifo is enabled + Duplex, + /// Rx fifo twice as deep. TX fifo disabled + RxOnly, + /// Tx fifo twice as deep. RX fifo disabled + TxOnly, +} + +#[derive(PartialEq)] +pub enum ShiftDirection { + Right = 1, + Left = 0, +} + +const RXNEMPTY_MASK: u32 = 1 << 0; +const TXNFULL_MASK: u32 = 1 << 4; +const SMIRQ_MASK: u32 = 1 << 8; + +#[interrupt] +unsafe fn PIO0_IRQ_1() { + use crate::pac; + let ints = pac::PIO0.irqs(1).ints().read().0; + let inte = pac::PIO0.irqs(1).inte(); + for i in 0..4 { + // Check RXNEMPTY + if ints & (RXNEMPTY_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(RXNEMPTY_MASK << i); + }); + FIFO_IN_WAKERS[0][i].wake(); + } + // Check IRQ flgs + if ints & (SMIRQ_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(SMIRQ_MASK << i); + }); + IRQ_WAKERS[0][i].wake(); + } + } +} + +#[interrupt] +unsafe fn PIO1_IRQ_1() { + use crate::pac; + let ints = pac::PIO1.irqs(1).ints().read().0; + let inte = pac::PIO1.irqs(1).inte(); + for i in 0..4 { + // Check all RXNEMPTY + if ints & (RXNEMPTY_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(RXNEMPTY_MASK << i); + }); + FIFO_IN_WAKERS[1][i].wake(); + } + // Check IRQ flgs + if ints & (SMIRQ_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(SMIRQ_MASK << i); + }); + IRQ_WAKERS[1][i].wake(); + } + } +} + +#[interrupt] +unsafe fn PIO0_IRQ_0() { + use crate::pac; + let ints = pac::PIO0.irqs(0).ints().read().0; + let inte = pac::PIO0.irqs(0).inte(); + //debug!("!{:04x}",ints); + // Check all TXNFULL + for i in 0..4 { + if ints & (TXNFULL_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(TXNFULL_MASK << i); + }); + FIFO_OUT_WAKERS[0][i].wake(); + } + } +} + +#[interrupt] +unsafe fn PIO1_IRQ_0() { + use crate::pac; + let ints = pac::PIO1.irqs(0).ints().read().0; + let inte = pac::PIO1.irqs(0).inte(); + // Check all TXNFULL + for i in 0..4 { + if ints & (TXNFULL_MASK << i) != 0 { + inte.modify(|m| { + m.0 &= !(TXNFULL_MASK << i); + }); + FIFO_OUT_WAKERS[1][i].wake(); + } + } +} + +/// Future that waits for TX-FIFO to become writable +pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { + sm: &'a mut SM, + pio: PhantomData, + value: u32, +} + +impl<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> FifoOutFuture<'a, PIO, SM> { + pub fn new(sm: &'a mut SM, value: u32) -> Self { + unsafe { + critical_section::with(|_| { + let irq = PIO::IrqOut::steal(); + irq.set_priority(interrupt::Priority::P3); + + irq.enable(); + }); + } + FifoOutFuture { + sm, + pio: PhantomData::default(), + value, + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture<'d, PIO, SM> { + type Output = (); + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + //debug!("Poll {},{}", PIO::PIO_NO, SM); + let value = self.value; + if self.get_mut().sm.try_push_tx(value) { + Poll::Ready(()) + } else { + FIFO_OUT_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); + unsafe { + let irq = PIO::IrqOut::steal(); + irq.disable(); + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { + m.0 |= TXNFULL_MASK << SM::Sm::SM_NO; + }); + }); + irq.enable(); + } + // debug!("Pending"); + Poll::Pending + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { + fn drop(&mut self) { + unsafe { + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { + m.0 &= !(TXNFULL_MASK << SM::Sm::SM_NO); + }); + }); + } + } +} + +/// Future that waits for RX-FIFO to become readable +pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachine> { + sm: &'a mut SM, + pio: PhantomData, +} + +impl<'a, PIO: PioInstance, SM: PioStateMachine> FifoInFuture<'a, PIO, SM> { + pub fn new(sm: &'a mut SM) -> Self { + unsafe { + critical_section::with(|_| { + let irq = PIO::IrqIn::steal(); + irq.set_priority(interrupt::Priority::P3); + + irq.enable(); + }); + } + FifoInFuture { + sm, + pio: PhantomData::default(), + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, SM> { + type Output = u32; + fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + //debug!("Poll {},{}", PIO::PIO_NO, SM); + if let Some(v) = self.sm.try_pull_rx() { + Poll::Ready(v) + } else { + FIFO_IN_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); + unsafe { + let irq = PIO::IrqIn::steal(); + irq.disable(); + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 |= RXNEMPTY_MASK << SM::Sm::SM_NO; + }); + }); + irq.enable(); + } + //debug!("Pending"); + Poll::Pending + } + } +} + +impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, SM> { + fn drop(&mut self) { + unsafe { + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 &= !(RXNEMPTY_MASK << SM::Sm::SM_NO); + }); + }); + } + } +} + +/// Future that waits for IRQ +pub struct IrqFuture { + pio: PhantomData, + irq_no: u8, +} + +impl<'a, PIO: PioInstance> IrqFuture { + pub fn new(irq_no: u8) -> Self { + unsafe { + critical_section::with(|_| { + let irq = PIO::IrqSm::steal(); + irq.set_priority(interrupt::Priority::P3); + + irq.enable(); + }); + } + IrqFuture { + pio: PhantomData::default(), + irq_no, + } + } +} + +impl<'d, PIO: PioInstance> Future for IrqFuture { + type Output = (); + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + //debug!("Poll {},{}", PIO::PIO_NO, SM); + + // Check if IRQ flag is already set + if critical_section::with(|_| unsafe { + let irq_flags = PIOS[PIO::PIO_NO as usize].irq(); + if irq_flags.read().0 & (1 << self.irq_no) != 0 { + irq_flags.write(|m| { + m.0 = 1 << self.irq_no; + }); + true + } else { + false + } + }) { + return Poll::Ready(()); + } + + IRQ_WAKERS[PIO::PIO_NO as usize][self.irq_no as usize].register(cx.waker()); + unsafe { + let irq = PIO::IrqSm::steal(); + irq.disable(); + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 |= SMIRQ_MASK << self.irq_no; + }); + }); + irq.enable(); + } + Poll::Pending + } +} + +impl<'d, PIO: PioInstance> Drop for IrqFuture { + fn drop(&mut self) { + unsafe { + critical_section::with(|_| { + PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + m.0 &= !(SMIRQ_MASK << self.irq_no); + }); + }); + } + } +} + +pub struct PioPin { + pin_bank: u8, + pio: PhantomData, +} + +impl PioPin { + /// Set the pin's drive strength. + #[inline] + pub fn set_drive_strength(&mut self, strength: Drive) { + unsafe { + self.pad_ctrl().modify(|w| { + w.set_drive(match strength { + Drive::_2mA => pac::pads::vals::Drive::_2MA, + Drive::_4mA => pac::pads::vals::Drive::_4MA, + Drive::_8mA => pac::pads::vals::Drive::_8MA, + Drive::_12mA => pac::pads::vals::Drive::_12MA, + }); + }); + } + } + + // Set the pin's slew rate. + #[inline] + pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { + unsafe { + self.pad_ctrl().modify(|w| { + w.set_slewfast(slew_rate == SlewRate::Fast); + }); + } + } + + /// Set the pin's pull. + #[inline] + pub fn set_pull(&mut self, pull: Pull) { + unsafe { + self.pad_ctrl().modify(|w| match pull { + Pull::Up => w.set_pue(true), + Pull::Down => w.set_pde(true), + Pull::None => {} + }); + } + } + + /// Set the pin's pull. + #[inline] + pub fn set_schmitt(&mut self, enable: bool) { + unsafe { + self.pad_ctrl().modify(|w| { + w.set_schmitt(enable); + }); + } + } + + pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { + let mask = 1 << self.pin(); + unsafe { + PIOS[PIO::PIO_NO as usize] + .input_sync_bypass() + .modify(|w| *w = if bypass { *w & !mask } else { *w | mask }); + } + } + + pub fn pin(&self) -> u8 { + self._pin() + } +} + +impl SealedPin for PioPin { + fn pin_bank(&self) -> u8 { + self.pin_bank + } +} + +pub struct PioStateMachineInstance { + pio: PhantomData, + sm: PhantomData, +} + +impl PioStateMachine for PioStateMachineInstance { + type Pio = PIO; + type Sm = SM; +} + +pub trait PioStateMachine: Sized + Unpin { + type Pio: PioInstance; + type Sm: SmInstance; + fn pio_no(&self) -> u8 { + let _ = self; + Self::Pio::PIO_NO + } + + fn sm_no(&self) -> u8 { + Self::Sm::SM_NO + } + + fn restart(&mut self) { + let _ = self; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .ctrl() + .modify(|w| w.set_sm_restart(1u8 << Self::Sm::SM_NO)); + } + } + fn set_enable(&mut self, enable: bool) { + let _ = self; + let mask = 1u8 << Self::Sm::SM_NO; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .ctrl() + .modify(|w| w.set_sm_enable((w.sm_enable() & !mask) | (if enable { mask } else { 0 }))); + } + } + + fn is_enabled(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } + } + + fn is_tx_empty(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } + } + fn is_tx_full(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } + } + + fn is_rx_empty(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } + } + fn is_rx_full(&self) -> bool { + let _ = self; + unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } + } + + fn tx_level(&self) -> u8 { + unsafe { + let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; + (flevel >> (Self::Sm::SM_NO * 8)) as u8 & 0x0f + } + } + + fn rx_level(&self) -> u8 { + unsafe { + let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; + (flevel >> (Self::Sm::SM_NO * 8 + 4)) as u8 & 0x0f + } + } + + fn push_tx(&mut self, v: u32) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .txf(Self::Sm::SM_NO as usize) + .write_value(v); + } + } + + fn try_push_tx(&mut self, v: u32) -> bool { + if self.is_tx_full() { + return false; + } + self.push_tx(v); + true + } + + fn pull_rx(&mut self) -> u32 { + unsafe { PIOS[Self::Pio::PIO_NO as usize].rxf(Self::Sm::SM_NO as usize).read() } + } + + fn try_pull_rx(&mut self) -> Option { + if self.is_rx_empty() { + return None; + } + Some(self.pull_rx()) + } + + fn set_clkdiv(&mut self, div_x_256: u32) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .clkdiv() + .write(|w| w.0 = div_x_256 << 8); + } + } + + fn get_clkdiv(&self) -> u32 { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .clkdiv() + .read() + .0 + >> 8 + } + } + + fn clkdiv_restart(&mut self) { + let _ = self; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .ctrl() + .modify(|w| w.set_clkdiv_restart(1u8 << Self::Sm::SM_NO)); + } + } + + fn set_side_enable(&self, enable: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| w.set_side_en(enable)); + } + } + + fn is_side_enabled(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read() + .side_en() + } + } + + fn set_side_pindir(&mut self, pindir: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| w.set_side_pindir(pindir)); + } + } + + fn is_side_pindir(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read() + .side_pindir() + } + } + + fn set_jmp_pin(&mut self, pin: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| w.set_jmp_pin(pin)); + } + } + + fn get_jmp_pin(&mut self) -> u8 { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read() + .jmp_pin() + } + } + + fn set_wrap(&self, source: u8, target: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .modify(|w| { + w.set_wrap_top(source); + w.set_wrap_bottom(target) + }); + } + } + + /// Get wrapping addresses. Returns (source, target). + fn get_wrap(&self) -> (u8, u8) { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .execctrl() + .read(); + (r.wrap_top(), r.wrap_bottom()) + } + } + + fn set_fifo_join(&mut self, join: FifoJoin) { + let (rx, tx) = match join { + FifoJoin::Duplex => (false, false), + FifoJoin::RxOnly => (true, false), + FifoJoin::TxOnly => (false, true), + }; + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| { + w.set_fjoin_rx(rx); + w.set_fjoin_tx(tx) + }); + } + } + fn get_fifo_join(&self) -> FifoJoin { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read(); + // Ignores the invalid state when both bits are set + if r.fjoin_rx() { + FifoJoin::RxOnly + } else if r.fjoin_tx() { + FifoJoin::TxOnly + } else { + FifoJoin::Duplex + } + } + } + + fn clear_fifos(&mut self) { + // Toggle FJOIN_RX to flush FIFOs + unsafe { + let shiftctrl = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl(); + shiftctrl.modify(|w| { + w.set_fjoin_rx(!w.fjoin_rx()); + }); + shiftctrl.modify(|w| { + w.set_fjoin_rx(!w.fjoin_rx()); + }); + } + } + + fn set_pull_threshold(&mut self, threshold: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_pull_thresh(threshold)); + } + } + + fn get_pull_threshold(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read(); + r.pull_thresh() + } + } + fn set_push_threshold(&mut self, threshold: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_push_thresh(threshold)); + } + } + + fn get_push_threshold(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read(); + r.push_thresh() + } + } + + fn set_out_shift_dir(&mut self, dir: ShiftDirection) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); + } + } + fn get_out_shiftdir(&self) -> ShiftDirection { + unsafe { + if PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .out_shiftdir() + { + ShiftDirection::Right + } else { + ShiftDirection::Left + } + } + } + + fn set_in_shift_dir(&mut self, dir: ShiftDirection) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); + } + } + fn get_in_shiftdir(&self) -> ShiftDirection { + unsafe { + if PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .in_shiftdir() + { + ShiftDirection::Right + } else { + ShiftDirection::Left + } + } + } + + fn set_autopull(&mut self, auto: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_autopull(auto)); + } + } + + fn is_autopull(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .autopull() + } + } + + fn set_autopush(&mut self, auto: bool) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .modify(|w| w.set_autopush(auto)); + } + } + + fn is_autopush(&self) -> bool { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .shiftctrl() + .read() + .autopush() + } + } + + fn get_addr(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .addr() + .read(); + r.addr() + } + } + fn set_sideset_count(&mut self, count: u8) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| w.set_sideset_count(count)); + } + } + + fn get_sideset_count(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + r.sideset_count() + } + } + + fn make_pio_pin(&self, pin: impl Pin) -> PioPin { + unsafe { + pin.io().ctrl().write(|w| { + w.set_funcsel( + if Self::Pio::PIO_NO == 1 { + pac::io::vals::Gpio0ctrlFuncsel::PIO1_0 + } else { + // PIO == 0 + pac::io::vals::Gpio0ctrlFuncsel::PIO0_0 + } + .0, + ); + }); + } + PioPin { + pin_bank: pin.pin_bank(), + pio: PhantomData::default(), + } + } + + fn set_sideset_base_pin(&mut self, base_pin: &PioPin) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| w.set_sideset_base(base_pin.pin())); + } + } + + fn get_sideset_base(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + r.sideset_base() + } + } + + /// Set the range of out pins affected by a set instruction. + fn set_set_range(&mut self, base: u8, count: u8) { + assert!(base + count < 32); + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| { + w.set_set_base(base); + w.set_set_count(count) + }); + } + } + + /// Get the range of out pins affected by a set instruction. Returns (base, count). + fn get_set_range(&self) -> (u8, u8) { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + (r.set_base(), r.set_count()) + } + } + + fn set_in_base_pin(&mut self, base: &PioPin) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| w.set_in_base(base.pin())); + } + } + + fn get_in_base(&self) -> u8 { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + r.in_base() + } + } + + fn set_out_range(&mut self, base: u8, count: u8) { + assert!(base + count < 32); + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .modify(|w| { + w.set_out_base(base); + w.set_out_count(count) + }); + } + } + + /// Get the range of out pins affected by a set instruction. Returns (base, count). + fn get_out_range(&self) -> (u8, u8) { + unsafe { + let r = PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .pinctrl() + .read(); + (r.out_base(), r.out_count()) + } + } + + fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin]) { + let count = pins.len(); + assert!(count >= 1); + let start = pins[0].pin() as usize; + assert!(start + pins.len() <= 32); + for i in 0..count { + assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); + } + self.set_out_range(start as u8, count as u8); + } + + fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin]) { + let count = pins.len(); + assert!(count >= 1); + let start = pins[0].pin() as usize; + assert!(start + pins.len() <= 32); + for i in 0..count { + assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); + } + self.set_set_range(start as u8, count as u8); + } + + fn get_current_instr() -> u32 { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .instr() + .read() + .0 + } + } + + fn exec_instr(&mut self, instr: u16) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .sm(Self::Sm::SM_NO as usize) + .instr() + .write(|w| w.set_instr(instr)); + } + } + + fn write_instr(&mut self, start: usize, instrs: &[u16]) { + let _ = self; + write_instr( + Self::Pio::PIO_NO, + start, + instrs, + MEM_USED_BY_STATEMACHINE | Self::Sm::SM_NO as u32, + ); + } + + fn is_irq_set(&self, irq_no: u8) -> bool { + assert!(irq_no < 8); + unsafe { + let irq_flags = PIOS[Self::Pio::PIO_NO as usize].irq(); + irq_flags.read().0 & (1 << irq_no) != 0 + } + } + + fn clear_irq(&mut self, irq_no: usize) { + assert!(irq_no < 8); + unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } + } + + fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, Self::Pio, Self> { + FifoOutFuture::new(self, value) + } + + fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, Self::Pio, Self> { + FifoInFuture::new(self) + } + + fn wait_irq(&self, irq_no: u8) -> IrqFuture { + IrqFuture::new(irq_no) + } + + fn has_tx_stalled(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().txstall() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_txstall(1 << Self::Sm::SM_NO)); + ret + } + } + + fn has_tx_overflowed(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().txover() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_txover(1 << Self::Sm::SM_NO)); + ret + } + } + + fn has_rx_stalled(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().rxstall() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_rxstall(1 << Self::Sm::SM_NO)); + ret + } + } + + fn has_rx_underflowed(&self) -> bool { + unsafe { + let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let ret = fdebug.read().rxunder() & (1 << Self::Sm::SM_NO) != 0; + fdebug.write(|w| w.set_rxunder(1 << Self::Sm::SM_NO)); + ret + } + } + + fn dma_push<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [u32]) -> Transfer<'a, C> { + unsafe { + dma::init(); + let pio_no = Self::Pio::PIO_NO; + let sm_no = Self::Sm::SM_NO; + let p = ch.regs(); + p.read_addr().write_value(data.as_ptr() as u32); + p.write_addr() + .write_value(PIOS[pio_no as usize].txf(sm_no as usize).ptr() as u32); + p.trans_count().write_value(data.len() as u32); + p.ctrl_trig().write(|w| { + // Set TX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no)); + w.set_data_size(DataSize::SIZE_WORD); + w.set_chain_to(ch.number()); + w.set_incr_read(true); + w.set_incr_write(false); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); + } + Transfer::new(ch) + } + + fn dma_pull<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [u32]) -> Transfer<'a, C> { + unsafe { + dma::init(); + let pio_no = Self::Pio::PIO_NO; + let sm_no = Self::Sm::SM_NO; + let p = ch.regs(); + p.write_addr().write_value(data.as_ptr() as u32); + p.read_addr() + .write_value(PIOS[pio_no as usize].rxf(sm_no as usize).ptr() as u32); + p.trans_count().write_value(data.len() as u32); + p.ctrl_trig().write(|w| { + // Set TX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); + w.set_data_size(DataSize::SIZE_WORD); + w.set_chain_to(ch.number()); + w.set_incr_read(false); + w.set_incr_write(true); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); + } + Transfer::new(ch) + } +} + +/* +This is a bit array containing 4 bits for every word in the PIO instruction memory. +*/ +// Bit 3-2 +//const MEM_USE_MASK: u32 = 0b1100; +const MEM_NOT_USED: u32 = 0b0000; +const MEM_USED_BY_STATEMACHINE: u32 = 0b0100; +const MEM_USED_BY_COMMON: u32 = 0b1000; + +// Bit 1-0 is the number of the state machine +//const MEM_STATE_MASK: u32 = 0b0011; + +// Should use mutex if running on multiple cores +static mut INSTR_MEM_STATUS: &'static mut [[u32; 4]; 2] = &mut [[0; 4]; 2]; + +fn instr_mem_get_status(pio_no: u8, addr: u8) -> u32 { + ((unsafe { INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }) >> ((addr & 0x07) * 4)) & 0xf +} + +fn instr_mem_set_status(pio_no: u8, addr: u8, status: u32) { + let w = unsafe { &mut INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }; + let shift = (addr & 0x07) * 4; + *w = (*w & !(0xf << shift)) | (status << shift); +} + +fn instr_mem_is_free(pio_no: u8, addr: u8) -> bool { + instr_mem_get_status(pio_no, addr) == MEM_NOT_USED +} + +pub struct PioCommonInstance { + pio: PhantomData, +} + +impl PioCommon for PioCommonInstance { + type Pio = PIO; +} + +fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { + for (i, instr) in instrs.iter().enumerate() { + let addr = (i + start) as u8; + assert!( + instr_mem_is_free(pio_no, addr), + "Trying to write already used PIO instruction memory at {}", + addr + ); + unsafe { + PIOS[pio_no as usize].instr_mem(addr as usize).write(|w| { + w.set_instr_mem(*instr); + }); + instr_mem_set_status(pio_no, addr, mem_user); + } + } +} + +pub trait PioCommon: Sized { + type Pio: PioInstance; + + fn write_instr(&mut self, start: usize, instrs: &[u16]) { + let _ = self; + write_instr(Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); + } + + fn clear_irq(&mut self, irq_no: usize) { + assert!(irq_no < 8); + unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } + } + + fn clear_irqs(&mut self, mask: u8) { + unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(mask)) } + } + + fn force_irq(&mut self, irq_no: usize) { + assert!(irq_no < 8); + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .irq_force() + .write(|w| w.set_irq_force(1 << irq_no)) + } + } + + fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { + unsafe { + PIOS[Self::Pio::PIO_NO as usize] + .input_sync_bypass() + .modify(|w| *w = (*w & !mask) | (bypass & mask)); + } + } + + fn get_input_sync_bypass(&self) -> u32 { + unsafe { PIOS[Self::Pio::PIO_NO as usize].input_sync_bypass().read() } + } +} + +// Identifies a specific state machine inside a PIO device +pub struct SmInstanceBase {} + +pub trait SmInstance: Unpin { + const SM_NO: u8; +} + +impl SmInstance for SmInstanceBase { + const SM_NO: u8 = SM_NO; +} + +pub trait PioPeripherial: Sized { + type Pio: PioInstance; + fn pio(&self) -> u8 { + let _ = self; + Self::Pio::PIO_NO + } + + fn split( + self, + ) -> ( + PioCommonInstance, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, + ) { + let _ = self; + ( + PioCommonInstance { + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + PioStateMachineInstance { + sm: PhantomData::default(), + pio: PhantomData::default(), + }, + ) + } +} + +// Identifies a specific PIO device +pub struct PioInstanceBase {} + +pub trait PioInstance: Unpin { + const PIO_NO: u8; + type IrqOut: Interrupt; + type IrqIn: Interrupt; + type IrqSm: Interrupt; +} + +impl PioInstance for PioInstanceBase<0> { + const PIO_NO: u8 = 0; + type IrqOut = interrupt::PIO0_IRQ_0; + type IrqIn = interrupt::PIO0_IRQ_1; + type IrqSm = interrupt::PIO0_IRQ_1; +} + +impl PioInstance for PioInstanceBase<1> { + const PIO_NO: u8 = 1; + type IrqOut = interrupt::PIO1_IRQ_0; + type IrqIn = interrupt::PIO1_IRQ_1; + type IrqSm = interrupt::PIO1_IRQ_1; +} + +pub type Pio0 = PioInstanceBase<0>; +pub type Pio1 = PioInstanceBase<1>; + +pub type Sm0 = SmInstanceBase<0>; +pub type Sm1 = SmInstanceBase<1>; +pub type Sm2 = SmInstanceBase<2>; +pub type Sm3 = SmInstanceBase<3>; + +macro_rules! impl_pio_sm { + ($name:ident, $pio:expr) => { + impl PioPeripherial for peripherals::$name { + type Pio = PioInstanceBase<$pio>; + } + }; +} + +impl_pio_sm!(PIO0, 0); +impl_pio_sm!(PIO1, 1); diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs new file mode 100644 index 000000000..ae26ff1dc --- /dev/null +++ b/embassy-rp/src/pio_instr_util.rs @@ -0,0 +1,90 @@ +use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; + +use crate::pio::PioStateMachine; + +pub fn set_x(sm: &mut SM, value: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::X, + bit_count: 32, + } + .encode(); + sm.push_tx(value); + sm.exec_instr(OUT); +} + +pub fn get_x(sm: &mut SM) -> u32 { + const IN: u16 = InstructionOperands::IN { + source: InSource::X, + bit_count: 32, + } + .encode(); + sm.exec_instr(IN); + sm.pull_rx() +} + +pub fn set_y(sm: &mut SM, value: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::Y, + bit_count: 32, + } + .encode(); + sm.push_tx(value); + sm.exec_instr(OUT); +} + +pub fn get_y(sm: &mut SM) -> u32 { + const IN: u16 = InstructionOperands::IN { + source: InSource::Y, + bit_count: 32, + } + .encode(); + sm.exec_instr(IN); + + sm.pull_rx() +} + +pub fn set_pindir(sm: &mut SM, data: u8) { + let set: u16 = InstructionOperands::SET { + destination: SetDestination::PINDIRS, + data, + } + .encode(); + sm.exec_instr(set); +} + +pub fn set_pin(sm: &mut SM, data: u8) { + let set: u16 = InstructionOperands::SET { + destination: SetDestination::PINS, + data, + } + .encode(); + sm.exec_instr(set); +} + +pub fn set_out_pin(sm: &mut SM, data: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::PINS, + bit_count: 32, + } + .encode(); + sm.push_tx(data); + sm.exec_instr(OUT); +} +pub fn set_out_pindir(sm: &mut SM, data: u32) { + const OUT: u16 = InstructionOperands::OUT { + destination: OutDestination::PINDIRS, + bit_count: 32, + } + .encode(); + sm.push_tx(data); + sm.exec_instr(OUT); +} + +pub fn exec_jmp(sm: &mut SM, to_addr: u8) { + let jmp: u16 = InstructionOperands::JMP { + address: to_addr, + condition: JmpCondition::Always, + } + .encode(); + sm.exec_instr(jmp); +} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 60a8ba94d..b07c471af 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" 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"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } 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" } @@ -34,6 +34,11 @@ embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" log = "0.4" +pio-proc = "0.2" +pio = "0.2" [profile.release] debug = true + +[patch.crates-io] +pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs new file mode 100644 index 000000000..e9211db3b --- /dev/null +++ b/examples/rp/src/bin/pio_async.rs @@ -0,0 +1,105 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::gpio::{AnyPin, Pin}; +use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; +use embassy_rp::pio_instr_util; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn pio_task_sm0(mut sm: PioStateMachineInstance, pin: AnyPin) { + // Setup sm0 + + // Send data serially to pin + let prg = pio_proc::pio_asm!( + ".origin 16", + "set pindirs, 1", + ".wrap_target", + "out pins,1 [19]", + ".wrap", + ); + + let origin = prg.program.origin.unwrap_or(0); + let out_pin = sm.make_pio_pin(pin); + let pio_pins = [&out_pin]; + sm.set_out_pins(&pio_pins); + sm.write_instr(origin as usize, &prg.program.code); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); + sm.set_set_range(0, 1); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + sm.set_autopull(true); + sm.set_out_shift_dir(ShiftDirection::Left); + + sm.set_enable(true); + + let mut v = 0x0f0caffa; + loop { + sm.wait_push(v).await; + v ^= 0xffff; + info!("Pushed {:032b} to FIFO", v); + } +} + +#[embassy_executor::task] +async fn pio_task_sm1(mut sm: PioStateMachineInstance) { + // Setupm sm1 + + // Read 0b10101 repeatedly until ISR is full + let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); + + let origin = prg.program.origin.unwrap_or(0); + sm.write_instr(origin as usize, &prg.program.code); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); + sm.set_set_range(0, 0); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + sm.set_autopush(true); + sm.set_in_shift_dir(ShiftDirection::Right); + sm.set_enable(true); + loop { + let rx = sm.wait_pull().await; + info!("Pulled {:032b} from FIFO", rx); + } +} + +#[embassy_executor::task] +async fn pio_task_sm2(mut sm: PioStateMachineInstance) { + // Setup sm2 + + // Repeatedly trigger IRQ 3 + let prg = pio_proc::pio_asm!( + ".origin 0", + ".wrap_target", + "set x,10", + "delay:", + "jmp x-- delay [15]", + "irq 3 [15]", + ".wrap", + ); + let origin = prg.program.origin.unwrap_or(0); + + sm.write_instr(origin as usize, &prg.program.code); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); + sm.set_enable(true); + loop { + sm.wait_irq(3).await; + info!("IRQ trigged"); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + + let (_, sm0, sm1, sm2, ..) = pio.split(); + + spawner.spawn(pio_task_sm0(sm0, p.PIN_0.degrade())).unwrap(); + spawner.spawn(pio_task_sm1(sm1)).unwrap(); + spawner.spawn(pio_task_sm2(sm2)).unwrap(); +} diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs new file mode 100644 index 000000000..bdcdf200c --- /dev/null +++ b/examples/rp/src/bin/pio_dma.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +use defmt::info; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection}; +use embassy_rp::{pio_instr_util, Peripheral}; +use {defmt_rtt as _, panic_probe as _}; + +fn swap_nibbles(v: u32) -> u32 { + let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; + let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; + (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16 +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + let (_, mut sm, ..) = pio.split(); + + let prg = pio_proc::pio_asm!( + ".origin 0", + "set pindirs,1", + ".wrap_target", + "set y,7", + "loop:", + "out x,4", + "in x,4", + "jmp y--, loop", + ".wrap", + ); + + let origin = prg.program.origin.unwrap_or(0); + sm.write_instr(origin as usize, &prg.program.code); + pio_instr_util::exec_jmp(&mut sm, origin); + sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); + sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + sm.set_autopull(true); + sm.set_autopush(true); + sm.set_pull_threshold(32); + sm.set_push_threshold(32); + sm.set_out_shift_dir(ShiftDirection::Right); + sm.set_in_shift_dir(ShiftDirection::Left); + + sm.set_enable(true); + + let mut dma_out_ref = p.DMA_CH0.into_ref(); + let mut dma_in_ref = p.DMA_CH1.into_ref(); + let mut dout = [0x12345678u32; 29]; + for i in 1..dout.len() { + dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; + } + let mut din = [0u32; 29]; + loop { + join( + sm.dma_push(dma_out_ref.reborrow(), &dout), + sm.dma_pull(dma_in_ref.reborrow(), &mut din), + ) + .await; + for i in 0..din.len() { + assert_eq!(din[i], swap_nibbles(dout[i])); + } + info!("Swapped {} words", dout.len()); + } +} From cd59046e6c179809ee19fb2592e65cf5a0a07980 Mon Sep 17 00:00:00 2001 From: Simon Berg Date: Tue, 6 Dec 2022 21:42:30 +0100 Subject: [PATCH 0413/1575] Added RelocateProgram class for adjusting PIO-programs for different origins. --- embassy-rp/src/lib.rs | 2 + embassy-rp/src/pio.rs | 19 +++++--- embassy-rp/src/relocate.rs | 77 ++++++++++++++++++++++++++++++++ examples/rp/src/bin/pio_async.rs | 31 ++++++++----- examples/rp/src/bin/pio_dma.rs | 10 +++-- 5 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 embassy-rp/src/relocate.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 67eb29d5e..551392725 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -17,6 +17,8 @@ pub mod interrupt; pub mod pio; #[cfg(feature = "pio")] pub mod pio_instr_util; +#[cfg(feature = "pio")] +pub mod relocate; pub mod rom_data; pub mod rtc; diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index f049a16e7..1a067e54f 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -941,7 +941,10 @@ pub trait PioStateMachine: Sized + Unpin { } } - fn write_instr(&mut self, start: usize, instrs: &[u16]) { + fn write_instr(&mut self, start: usize, instrs: I) + where + I: Iterator, + { let _ = self; write_instr( Self::Pio::PIO_NO, @@ -1098,8 +1101,11 @@ impl PioCommon for PioCommonInstance { type Pio = PIO; } -fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { - for (i, instr) in instrs.iter().enumerate() { +fn write_instr(pio_no: u8, start: usize, instrs: I, mem_user: u32) +where + I: Iterator, +{ + for (i, instr) in instrs.enumerate() { let addr = (i + start) as u8; assert!( instr_mem_is_free(pio_no, addr), @@ -1108,7 +1114,7 @@ fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { ); unsafe { PIOS[pio_no as usize].instr_mem(addr as usize).write(|w| { - w.set_instr_mem(*instr); + w.set_instr_mem(instr); }); instr_mem_set_status(pio_no, addr, mem_user); } @@ -1118,7 +1124,10 @@ fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { pub trait PioCommon: Sized { type Pio: PioInstance; - fn write_instr(&mut self, start: usize, instrs: &[u16]) { + fn write_instr(&mut self, start: usize, instrs: I) + where + I: Iterator, + { let _ = self; write_instr(Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); } diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs new file mode 100644 index 000000000..f36170e03 --- /dev/null +++ b/embassy-rp/src/relocate.rs @@ -0,0 +1,77 @@ +use core::iter::Iterator; + +use pio::{Program, SideSet, Wrap}; + +pub struct CodeIterator<'a, I> +where + I: Iterator, +{ + iter: I, + offset: u8, +} + +impl<'a, I: Iterator> CodeIterator<'a, I> { + pub fn new(iter: I, offset: u8) -> CodeIterator<'a, I> { + CodeIterator { iter, offset } + } +} + +impl<'a, I> Iterator for CodeIterator<'a, I> +where + I: Iterator, +{ + type Item = u16; + fn next(&mut self) -> Option { + self.iter.next().and_then(|&instr| { + Some(if instr & 0b1110_0000_0000_0000 == 0 { + // this is a JMP instruction -> add offset to address + let address = (instr & 0b1_1111) as u8; + let address = address + self.offset; + assert!( + address < pio::RP2040_MAX_PROGRAM_SIZE as u8, + "Invalid JMP out of the program after offset addition" + ); + instr & (!0b11111) | address as u16 + } else { + instr + }) + }) + } +} + +pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> { + program: &'a Program, + origin: u8, +} + +impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { + pub fn new(program: &Program) -> RelocatedProgram { + let origin = program.origin.unwrap_or(0); + RelocatedProgram { program, origin } + } + + pub fn new_with_origin(program: &Program, origin: u8) -> RelocatedProgram { + RelocatedProgram { program, origin } + } + + pub fn code(&'a self) -> CodeIterator<'a, core::slice::Iter<'a, u16>> { + CodeIterator::new(self.program.code.iter(), self.origin) + } + + pub fn wrap(&self) -> Wrap { + let wrap = self.program.wrap; + let origin = self.origin; + Wrap { + source: wrap.source + origin, + target: wrap.target + origin, + } + } + + pub fn side_set(&self) -> SideSet { + self.program.side_set + } + + pub fn origin(&self) -> u8 { + self.origin + } +} diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index e9211db3b..45a8c73f7 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -6,6 +6,7 @@ use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; use embassy_rp::pio_instr_util; +use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] @@ -21,15 +22,17 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance, pin: AnyPin) { ".wrap", ); - let origin = prg.program.origin.unwrap_or(0); + let relocated = RelocatedProgram::new(&prg.program); let out_pin = sm.make_pio_pin(pin); let pio_pins = [&out_pin]; sm.set_out_pins(&pio_pins); - sm.write_instr(origin as usize, &prg.program.code); - pio_instr_util::exec_jmp(&mut sm, origin); + sm.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); sm.set_set_range(0, 1); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + sm.set_autopull(true); sm.set_out_shift_dir(ShiftDirection::Left); @@ -50,12 +53,14 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance) { // Read 0b10101 repeatedly until ISR is full let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); - let origin = prg.program.origin.unwrap_or(0); - sm.write_instr(origin as usize, &prg.program.code); - pio_instr_util::exec_jmp(&mut sm, origin); + let relocated = RelocatedProgram::new(&prg.program); + sm.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); sm.set_set_range(0, 0); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + sm.set_autopush(true); sm.set_in_shift_dir(ShiftDirection::Right); sm.set_enable(true); @@ -79,11 +84,13 @@ async fn pio_task_sm2(mut sm: PioStateMachineInstance) { "irq 3 [15]", ".wrap", ); - let origin = prg.program.origin.unwrap_or(0); + let relocated = RelocatedProgram::new(&prg.program); + sm.write_instr(relocated.origin() as usize, relocated.code()); - sm.write_instr(origin as usize, &prg.program.code); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); - pio_instr_util::exec_jmp(&mut sm, origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); sm.set_enable(true); loop { diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index bdcdf200c..b19ef4083 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -5,6 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection}; +use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; @@ -32,11 +33,12 @@ async fn main(_spawner: Spawner) { ".wrap", ); - let origin = prg.program.origin.unwrap_or(0); - sm.write_instr(origin as usize, &prg.program.code); - pio_instr_util::exec_jmp(&mut sm, origin); + let relocated = RelocatedProgram::new(&prg.program); + sm.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); - sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); sm.set_autopull(true); sm.set_autopush(true); sm.set_pull_threshold(32); From 488e3224783abe07b2be8046642fd9b81f5714b9 Mon Sep 17 00:00:00 2001 From: Simon Berg Date: Fri, 9 Dec 2022 20:30:17 +0100 Subject: [PATCH 0414/1575] Clear unused pulls in set_pull. --- embassy-rp/src/gpio.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 315cd2aca..ec05de611 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -476,11 +476,13 @@ impl<'d, T: Pin> Flex<'d, T> { unsafe { self.pin.pad_ctrl().modify(|w| { w.set_ie(true); - match pull { - Pull::Up => w.set_pue(true), - Pull::Down => w.set_pde(true), - Pull::None => {} - } + let (pu, pd) = match pull { + Pull::Up => (true, false), + Pull::Down => (false, true), + Pull::None => (false, false), + }; + w.set_pue(pu); + w.set_pde(pd); }); } } From 5d4f09156af094732edc5c01332af8b13db10e0f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Dec 2022 23:33:53 +0100 Subject: [PATCH 0415/1575] nrf: add manually_create_executor example. --- .../nrf/src/bin/manually_create_executor.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/nrf/src/bin/manually_create_executor.rs diff --git a/examples/nrf/src/bin/manually_create_executor.rs b/examples/nrf/src/bin/manually_create_executor.rs new file mode 100644 index 000000000..12ce660f9 --- /dev/null +++ b/examples/nrf/src/bin/manually_create_executor.rs @@ -0,0 +1,49 @@ +// This example showcases how to manually create an executor. +// This is what the #[embassy::main] macro does behind the scenes. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::{info, unwrap}; +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run1() { + loop { + info!("BIG INFREQUENT TICK"); + Timer::after(Duration::from_ticks(64000)).await; + } +} + +#[embassy_executor::task] +async fn run2() { + loop { + info!("tick"); + Timer::after(Duration::from_ticks(13000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let _p = embassy_nrf::init(Default::default()); + + // Create the executor and put it in a StaticCell, because `run` needs `&'static mut Executor`. + let executor = EXECUTOR.init(Executor::new()); + + // Run it. + // `run` calls the closure then runs the executor forever. It never returns. + executor.run(|spawner| { + // Here we get access to a spawner to spawn the initial tasks. + unwrap!(spawner.spawn(run1())); + unwrap!(spawner.spawn(run2())); + }); +} From 1ee58492fbc58b721dc5ed9037c6787af257cbeb Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 08:26:35 +0100 Subject: [PATCH 0416/1575] embassy-rp: Add multicore support --- embassy-rp/Cargo.toml | 2 +- embassy-rp/src/critical_section_impl.rs | 142 +++++++++++++ embassy-rp/src/lib.rs | 2 + embassy-rp/src/multicore.rs | 266 ++++++++++++++++++++++++ examples/rp/Cargo.toml | 3 +- examples/rp/src/bin/multicore.rs | 62 ++++++ 6 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 embassy-rp/src/critical_section_impl.rs create mode 100644 embassy-rp/src/multicore.rs create mode 100644 examples/rp/src/bin/multicore.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 284d458c6..07cd1bc1b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -50,7 +50,7 @@ nb = "1.0.0" cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -critical-section = "1.1" +critical-section = { version = "1.1", features = ["restore-state-u8"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } embedded-io = { version = "0.4.0", features = ["async"], optional = true } diff --git a/embassy-rp/src/critical_section_impl.rs b/embassy-rp/src/critical_section_impl.rs new file mode 100644 index 000000000..ce284c856 --- /dev/null +++ b/embassy-rp/src/critical_section_impl.rs @@ -0,0 +1,142 @@ +use core::sync::atomic::{AtomicU8, Ordering}; + +use crate::pac; + +struct RpSpinlockCs; +critical_section::set_impl!(RpSpinlockCs); + +/// Marker value to indicate no-one has the lock. +/// +/// Initialising `LOCK_OWNER` to 0 means cheaper static initialisation so it's the best choice +const LOCK_UNOWNED: u8 = 0; + +/// Indicates which core owns the lock so that we can call critical_section recursively. +/// +/// 0 = no one has the lock, 1 = core0 has the lock, 2 = core1 has the lock +static LOCK_OWNER: AtomicU8 = AtomicU8::new(LOCK_UNOWNED); + +/// Marker value to indicate that we already owned the lock when we started the `critical_section`. +/// +/// Since we can't take the spinlock when we already have it, we need some other way to keep track of `critical_section` ownership. +/// `critical_section` provides a token for communicating between `acquire` and `release` so we use that. +/// If we're the outermost call to `critical_section` we use the values 0 and 1 to indicate we should release the spinlock and set the interrupts back to disabled and enabled, respectively. +/// The value 2 indicates that we aren't the outermost call, and should not release the spinlock or re-enable interrupts in `release` +const LOCK_ALREADY_OWNED: u8 = 2; + +unsafe impl critical_section::Impl for RpSpinlockCs { + unsafe fn acquire() -> u8 { + RpSpinlockCs::acquire() + } + + unsafe fn release(token: u8) { + RpSpinlockCs::release(token); + } +} + +impl RpSpinlockCs { + unsafe fn acquire() -> u8 { + // Store the initial interrupt state and current core id in stack variables + let interrupts_active = cortex_m::register::primask::read().is_active(); + // We reserved 0 as our `LOCK_UNOWNED` value, so add 1 to core_id so we get 1 for core0, 2 for core1. + let core = pac::SIO.cpuid().read() as u8 + 1; + // Do we already own the spinlock? + if LOCK_OWNER.load(Ordering::Acquire) == core { + // We already own the lock, so we must have called acquire within a critical_section. + // Return the magic inner-loop value so that we know not to re-enable interrupts in release() + LOCK_ALREADY_OWNED + } else { + // Spin until we get the lock + loop { + // Need to disable interrupts to ensure that we will not deadlock + // if an interrupt enters critical_section::Impl after we acquire the lock + cortex_m::interrupt::disable(); + // Ensure the compiler doesn't re-order accesses and violate safety here + core::sync::atomic::compiler_fence(Ordering::SeqCst); + // Read the spinlock reserved for `critical_section` + if let Some(lock) = Spinlock31::try_claim() { + // We just acquired the lock. + // 1. Forget it, so we don't immediately unlock + core::mem::forget(lock); + // 2. Store which core we are so we can tell if we're called recursively + LOCK_OWNER.store(core, Ordering::Relaxed); + break; + } + // We didn't get the lock, enable interrupts if they were enabled before we started + if interrupts_active { + cortex_m::interrupt::enable(); + } + } + // If we broke out of the loop we have just acquired the lock + // As the outermost loop, we want to return the interrupt status to restore later + interrupts_active as _ + } + } + + unsafe fn release(token: u8) { + // Did we already own the lock at the start of the `critical_section`? + if token != LOCK_ALREADY_OWNED { + // No, it wasn't owned at the start of this `critical_section`, so this core no longer owns it. + // Set `LOCK_OWNER` back to `LOCK_UNOWNED` to ensure the next critical section tries to obtain the spinlock instead + LOCK_OWNER.store(LOCK_UNOWNED, Ordering::Relaxed); + // Ensure the compiler doesn't re-order accesses and violate safety here + core::sync::atomic::compiler_fence(Ordering::SeqCst); + // Release the spinlock to allow others to enter critical_section again + Spinlock31::release(); + // Re-enable interrupts if they were enabled when we first called acquire() + // We only do this on the outermost `critical_section` to ensure interrupts stay disabled + // for the whole time that we have the lock + if token != 0 { + cortex_m::interrupt::enable(); + } + } + } +} + +pub struct Spinlock(core::marker::PhantomData<()>) +where + Spinlock: SpinlockValid; + +impl Spinlock +where + Spinlock: SpinlockValid, +{ + /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is + /// already in use somewhere else. + pub fn try_claim() -> Option { + // Safety: We're only reading from this register + unsafe { + let lock = pac::SIO.spinlock(N).read(); + if lock > 0 { + Some(Self(core::marker::PhantomData)) + } else { + None + } + } + } + + /// Clear a locked spin-lock. + /// + /// # Safety + /// + /// Only call this function if you hold the spin-lock. + pub unsafe fn release() { + unsafe { + // Write (any value): release the lock + pac::SIO.spinlock(N).write_value(1); + } + } +} + +impl Drop for Spinlock +where + Spinlock: SpinlockValid, +{ + fn drop(&mut self) { + // This is safe because we own the object, and hence hold the lock. + unsafe { Self::release() } + } +} + +pub(crate) type Spinlock31 = Spinlock<31>; +pub trait SpinlockValid {} +impl SpinlockValid for Spinlock<31> {} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d21b5f7b0..9a2fb7fc3 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -5,6 +5,7 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod critical_section_impl; mod intrinsics; pub mod adc; @@ -23,6 +24,7 @@ pub mod usb; pub mod clocks; pub mod flash; +pub mod multicore; mod reset; // Reexports diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs new file mode 100644 index 000000000..dd3b70a67 --- /dev/null +++ b/embassy-rp/src/multicore.rs @@ -0,0 +1,266 @@ +//! Multicore support +//! +//! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1. +//! It provides functionality for setting up the stack, and starting core1. +//! +//! The entrypoint for core1 can be any function that never returns, including closures. + +use core::mem::ManuallyDrop; +use core::sync::atomic::{compiler_fence, Ordering}; + +use crate::pac; + +/// Errors for multicore operations. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// Operation is invalid on this core. + InvalidCore, + /// Core was unresponsive to commands. + Unresponsive, +} + +/// Core ID +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CoreId { + Core0, + Core1, +} + +#[inline(always)] +fn install_stack_guard(stack_bottom: *mut usize) { + let core = unsafe { cortex_m::Peripherals::steal() }; + + // Trap if MPU is already configured + if core.MPU.ctrl.read() != 0 { + cortex_m::asm::udf(); + } + + // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will + // just shorten the valid stack range a tad. + let addr = (stack_bottom as u32 + 31) & !31; + // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want + let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); + unsafe { + core.MPU.ctrl.write(5); // enable mpu with background default map + core.MPU.rbar.write((addr & !0xff) | 0x8); + core.MPU.rasr.write( + 1 // enable region + | (0x7 << 1) // size 2^(7 + 1) = 256 + | (subregion_select << 8) + | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions + ); + } +} + +#[inline(always)] +fn core1_setup(stack_bottom: *mut usize) { + install_stack_guard(stack_bottom); + // TODO: irq priorities +} + +/// Multicore execution management. +pub struct Multicore { + cores: (Core, Core), +} + +/// Data type for a properly aligned stack of N 32-bit (usize) words +#[repr(C, align(32))] +pub struct Stack { + /// Memory to be used for the stack + pub mem: [usize; SIZE], +} + +impl Stack { + /// Construct a stack of length SIZE, initialized to 0 + pub const fn new() -> Stack { + Stack { mem: [0; SIZE] } + } +} + +impl Multicore { + /// Create a new |Multicore| instance. + pub fn new() -> Self { + Self { + cores: (Core { id: CoreId::Core0 }, Core { id: CoreId::Core1 }), + } + } + + /// Get the available |Core| instances. + pub fn cores(&mut self) -> &mut (Core, Core) { + &mut self.cores + } +} + +/// A handle for controlling a logical core. +pub struct Core { + pub id: CoreId, +} + +impl Core { + /// Spawn a function on this core. + pub fn spawn(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> + where + F: FnOnce() -> bad::Never + Send + 'static, + { + fn fifo_write(value: u32) { + unsafe { + let sio = pac::SIO; + // Wait for the FIFO to have some space + while !sio.fifo().st().read().rdy() { + cortex_m::asm::nop(); + } + // Signal that it's safe for core 0 to get rid of the original value now. + sio.fifo().wr().write_value(value); + } + + // Fire off an event to the other core. + // This is required as the other core may be `wfe` (waiting for event) + cortex_m::asm::sev(); + } + + fn fifo_read() -> u32 { + unsafe { + let sio = pac::SIO; + // Keep trying until FIFO has data + loop { + if sio.fifo().st().read().vld() { + return sio.fifo().rd().read(); + } else { + // We expect the sending core to `sev` on write. + cortex_m::asm::wfe(); + } + } + } + } + + fn fifo_drain() { + unsafe { + let sio = pac::SIO; + while sio.fifo().st().read().vld() { + let _ = sio.fifo().rd().read(); + } + } + } + + match self.id { + CoreId::Core1 => { + // The first two ignored `u64` parameters are there to take up all of the registers, + // which means that the rest of the arguments are taken from the stack, + // where we're able to put them from core 0. + extern "C" fn core1_startup bad::Never>( + _: u64, + _: u64, + entry: &mut ManuallyDrop, + stack_bottom: *mut usize, + ) -> ! { + core1_setup(stack_bottom); + let entry = unsafe { ManuallyDrop::take(entry) }; + // Signal that it's safe for core 0 to get rid of the original value now. + fifo_write(1); + entry() + } + + // Reset the core + unsafe { + let psm = pac::PSM; + psm.frce_off().modify(|w| w.set_proc1(true)); + while !psm.frce_off().read().proc1() { + cortex_m::asm::nop(); + } + psm.frce_off().modify(|w| w.set_proc1(false)); + } + + // Set up the stack + let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; + + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + // Push the arguments to `core1_startup` onto the stack. + unsafe { + // Push `stack_bottom`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); + + // Push `entry`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + } + + // Make sure the compiler does not reorder the stack writes after to after the + // below FIFO writes, which would result in them not being seen by the second + // core. + // + // From the compiler perspective, this doesn't guarantee that the second core + // actually sees those writes. However, we know that the RP2040 doesn't have + // memory caches, and writes happen in-order. + compiler_fence(Ordering::Release); + + let p = unsafe { cortex_m::Peripherals::steal() }; + let vector_table = p.SCB.vtor.read(); + + // After reset, core 1 is waiting to receive commands over FIFO. + // This is the sequence to have it jump to some code. + let cmd_seq = [ + 0, + 0, + 1, + vector_table as usize, + stack_ptr as usize, + core1_startup:: as usize, + ]; + + let mut seq = 0; + let mut fails = 0; + loop { + let cmd = cmd_seq[seq] as u32; + if cmd == 0 { + fifo_drain(); + cortex_m::asm::sev(); + } + fifo_write(cmd); + + let response = fifo_read(); + if cmd == response { + seq += 1; + } else { + seq = 0; + fails += 1; + if fails > 16 { + // The second core isn't responding, and isn't going to take the entrypoint, + // so we have to drop it ourselves. + drop(ManuallyDrop::into_inner(entry)); + return Err(Error::Unresponsive); + } + } + if seq >= cmd_seq.len() { + break; + } + } + + // Wait until the other core has copied `entry` before returning. + fifo_read(); + + Ok(()) + } + _ => Err(Error::InvalidCore), + } + } +} + +// https://github.com/nvzqz/bad-rs/blob/master/src/never.rs +mod bad { + pub(crate) type Never = ::Output; + + pub trait HasOutput { + type Output; + } + + impl HasOutput for fn() -> O { + type Output = O; + } + + type F = fn() -> !; +} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 60a8ba94d..bd624a329 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -18,7 +18,8 @@ embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs new file mode 100644 index 000000000..46d3cd17c --- /dev/null +++ b/examples/rp/src/bin/multicore.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::PIN_25; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use embassy_time::{Duration, Timer}; +use embassy_rp::multicore::{Multicore, Stack}; +use {defmt_rtt as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<4096> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); +static CHANNEL: Channel = Channel::new(); + +enum LedState { + On, + Off, +} + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + let led = Output::new(p.PIN_25, Level::Low); + + let mut mc = Multicore::new(); + let (_, core1) = mc.cores(); + let _ = core1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); + }); + + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +} + +#[embassy_executor::task] +async fn core0_task() { + info!("Hello from core 0"); + loop { + CHANNEL.send(LedState::On).await; + Timer::after(Duration::from_millis(100)).await; + CHANNEL.send(LedState::Off).await; + Timer::after(Duration::from_millis(400)).await; + } +} + +#[embassy_executor::task] +async fn core1_task(mut led: Output<'static, PIN_25>) { + info!("Hello from core 1"); + loop { + match CHANNEL.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} \ No newline at end of file From 34eaade14fbf521d2e0e37bdeb870787a184ee2e Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 08:33:09 +0100 Subject: [PATCH 0417/1575] fmt --- examples/rp/src/bin/multicore.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 46d3cd17c..cfb91e21f 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -6,11 +6,11 @@ use defmt::*; use embassy_executor::Executor; use embassy_executor::_export::StaticCell; use embassy_rp::gpio::{Level, Output}; +use embassy_rp::multicore::{Multicore, Stack}; use embassy_rp::peripherals::PIN_25; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Timer}; -use embassy_rp::multicore::{Multicore, Stack}; use {defmt_rtt as _, panic_probe as _}; static mut CORE1_STACK: Stack<4096> = Stack::new(); @@ -59,4 +59,4 @@ async fn core1_task(mut led: Output<'static, PIN_25>) { LedState::Off => led.set_low(), } } -} \ No newline at end of file +} From cc0248d83ad34941480cd71d0255054d5317ab74 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 12:42:08 +0100 Subject: [PATCH 0418/1575] Select critical-section in tests --- tests/rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index a07b479e4..ffde4c7fe 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } From d8821cfd41eb1776b904a5766be43f242af938f7 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 12:57:45 +0100 Subject: [PATCH 0419/1575] Feature gate critical-section-impl --- embassy-rp/Cargo.toml | 5 ++++- embassy-rp/src/lib.rs | 2 ++ embassy-rp/src/multicore.rs | 4 +++- examples/rp/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 07cd1bc1b..694f0dc7b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -15,6 +15,9 @@ flavors = [ [features] defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] +# critical section that is safe for multicore use +critical-section-impl = ["critical-section/restore-state-u8"] + # 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. # If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. @@ -50,7 +53,7 @@ nb = "1.0.0" cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -critical-section = { version = "1.1", features = ["restore-state-u8"] } +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.4.0", features = ["async"], optional = true } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9a2fb7fc3..2cd99f456 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -5,7 +5,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +#[cfg(feature = "critical-section-impl")] mod critical_section_impl; + mod intrinsics; pub mod adc; diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index dd3b70a67..cc5c192b1 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -4,6 +4,9 @@ //! It provides functionality for setting up the stack, and starting core1. //! //! The entrypoint for core1 can be any function that never returns, including closures. +//! +//! Enable the `critical-section-impl` feature in embassy-rp when sharing data across cores using +//! the `embassy-sync` primitives and `CriticalSectionRawMutex`. use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; @@ -57,7 +60,6 @@ fn install_stack_guard(stack_bottom: *mut usize) { #[inline(always)] fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); - // TODO: irq priorities } /// Multicore execution management. diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index bd624a329..34a2d36d8 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" 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"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index ffde4c7fe..572a9ce88 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" 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"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" From 96d6c7243b7b5f7f8c90dab666ded0ca0cf29c75 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 10 Dec 2022 13:43:29 +0100 Subject: [PATCH 0420/1575] Cleanup --- embassy-rp/src/multicore.rs | 12 ++++++------ examples/rp/src/bin/multicore.rs | 7 +++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index cc5c192b1..bf635db2d 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -1,4 +1,4 @@ -//! Multicore support +//! MultiCore support //! //! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1. //! It provides functionality for setting up the stack, and starting core1. @@ -62,9 +62,9 @@ fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); } -/// Multicore execution management. -pub struct Multicore { - cores: (Core, Core), +/// MultiCore execution management. +pub struct MultiCore { + pub cores: (Core, Core), } /// Data type for a properly aligned stack of N 32-bit (usize) words @@ -81,8 +81,8 @@ impl Stack { } } -impl Multicore { - /// Create a new |Multicore| instance. +impl MultiCore { + /// Create a new |MultiCore| instance. pub fn new() -> Self { Self { cores: (Core { id: CoreId::Core0 }, Core { id: CoreId::Core1 }), diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index cfb91e21f..53941da60 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_executor::_export::StaticCell; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::multicore::{Multicore, Stack}; +use embassy_rp::multicore::{MultiCore, Stack}; use embassy_rp::peripherals::PIN_25; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -28,9 +28,8 @@ fn main() -> ! { let p = embassy_rp::init(Default::default()); let led = Output::new(p.PIN_25, Level::Low); - let mut mc = Multicore::new(); - let (_, core1) = mc.cores(); - let _ = core1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { + let mut mc = MultiCore::new(); + let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { let executor1 = EXECUTOR1.init(Executor::new()); executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); }); From eb1d2e1295cfd2f0580355d69c93387898eaabd4 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 04:02:28 +0100 Subject: [PATCH 0421/1575] Pause CORE1 execution during flash operations --- embassy-rp/src/flash.rs | 29 ++- embassy-rp/src/multicore.rs | 343 +++++++++++++++++++++--------------- 2 files changed, 223 insertions(+), 149 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index a972d5f69..f2137ebe1 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -6,6 +6,7 @@ use embedded_storage::nor_flash::{ ReadNorFlash, }; +use crate::pac; use crate::peripherals::FLASH; pub const FLASH_BASE: usize = 0x10000000; @@ -28,6 +29,7 @@ pub enum Error { OutOfBounds, /// Unaligned operation or using unaligned buffers. Unaligned, + InvalidCore, Other, } @@ -46,7 +48,7 @@ impl NorFlashError for Error { match self { Self::OutOfBounds => NorFlashErrorKind::OutOfBounds, Self::Unaligned => NorFlashErrorKind::NotAligned, - Self::Other => NorFlashErrorKind::Other, + _ => NorFlashErrorKind::Other, } } } @@ -87,7 +89,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let len = to - from; - unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true)) }; + unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true))? }; Ok(()) } @@ -112,7 +114,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let unaligned_offset = offset as usize - start; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } } let remaining_len = bytes.len() - start_padding; @@ -130,12 +132,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { if bytes.as_ptr() as usize >= 0x2000_0000 { let aligned_data = &bytes[start_padding..end_padding]; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true))? } } else { for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) { let mut ram_buf = [0xFF_u8; PAGE_SIZE]; ram_buf.copy_from_slice(chunk); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true))? } aligned_offset += PAGE_SIZE; } } @@ -150,7 +152,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true)) } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } } Ok(()) @@ -159,10 +161,17 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { /// Make sure to uphold the contract points with rp2040-flash. /// - interrupts must be disabled /// - DMA must not access flash memory - unsafe fn in_ram(&mut self, operation: impl FnOnce()) { + unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> { let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; - // TODO: Make sure CORE1 is paused during the entire duration of the RAM function + // Make sure we're running on CORE0 + let core_id: u32 = unsafe { pac::SIO.cpuid().read() }; + if core_id != 0 { + return Err(Error::InvalidCore); + } + + // Make sure CORE1 is paused during the entire duration of the RAM function + crate::multicore::pause_core1(); critical_section::with(|_| { // Pause all DMA channels for the duration of the ram operation @@ -185,6 +194,10 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } } }); + + // Resume CORE1 execution + crate::multicore::resume_core1(); + Ok(()) } } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index bf635db2d..09d40b2ef 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -11,14 +11,19 @@ use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; -use crate::pac; +use atomic_polyfill::AtomicBool; + +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::{interrupt, pac}; + +const PAUSE_TOKEN: u32 = 0xDEADBEEF; +const RESUME_TOKEN: u32 = !0xDEADBEEF; +static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); /// Errors for multicore operations. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// Operation is invalid on this core. - InvalidCore, /// Core was unresponsive to commands. Unresponsive, } @@ -64,7 +69,7 @@ fn core1_setup(stack_bottom: *mut usize) { /// MultiCore execution management. pub struct MultiCore { - pub cores: (Core, Core), + pub cores: (Core0, Core1), } /// Data type for a properly aligned stack of N 32-bit (usize) words @@ -85,169 +90,225 @@ impl MultiCore { /// Create a new |MultiCore| instance. pub fn new() -> Self { Self { - cores: (Core { id: CoreId::Core0 }, Core { id: CoreId::Core1 }), + cores: (Core0 {}, Core1 {}), } } /// Get the available |Core| instances. - pub fn cores(&mut self) -> &mut (Core, Core) { + pub fn cores(&mut self) -> &mut (Core0, Core1) { &mut self.cores } } /// A handle for controlling a logical core. -pub struct Core { - pub id: CoreId, +pub struct Core0 {} +/// A handle for controlling a logical core. +pub struct Core1 {} + +#[interrupt] +#[link_section = ".data.ram_func"] +unsafe fn SIO_IRQ_PROC1() { + let sio = pac::SIO; + // Clear IRQ + sio.fifo().st().write(|w| w.set_wof(false)); + + while sio.fifo().st().read().vld() { + // Pause CORE1 execution and disable interrupts + if fifo_read_wfe() == PAUSE_TOKEN { + cortex_m::interrupt::disable(); + // Signal to CORE0 that execution is paused + fifo_write(PAUSE_TOKEN); + // Wait for `resume` signal from CORE0 + while fifo_read_wfe() != RESUME_TOKEN { + cortex_m::asm::nop(); + } + cortex_m::interrupt::enable(); + // Signal to CORE0 that execution is resumed + fifo_write(RESUME_TOKEN); + } + } } -impl Core { - /// Spawn a function on this core. +impl Core1 { + /// Spawn a function on this core pub fn spawn(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> where F: FnOnce() -> bad::Never + Send + 'static, { - fn fifo_write(value: u32) { - unsafe { - let sio = pac::SIO; - // Wait for the FIFO to have some space - while !sio.fifo().st().read().rdy() { - cortex_m::asm::nop(); - } - // Signal that it's safe for core 0 to get rid of the original value now. - sio.fifo().wr().write_value(value); - } + // The first two ignored `u64` parameters are there to take up all of the registers, + // which means that the rest of the arguments are taken from the stack, + // where we're able to put them from core 0. + extern "C" fn core1_startup bad::Never>( + _: u64, + _: u64, + entry: &mut ManuallyDrop, + stack_bottom: *mut usize, + ) -> ! { + core1_setup(stack_bottom); + let entry = unsafe { ManuallyDrop::take(entry) }; + // Signal that it's safe for core 0 to get rid of the original value now. + fifo_write(1); - // Fire off an event to the other core. - // This is required as the other core may be `wfe` (waiting for event) - cortex_m::asm::sev(); + IS_CORE1_INIT.store(true, Ordering::Release); + // Enable fifo interrupt on CORE1 for `pause` functionality. + let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; + irq.enable(); + + entry() } - fn fifo_read() -> u32 { - unsafe { - let sio = pac::SIO; - // Keep trying until FIFO has data - loop { - if sio.fifo().st().read().vld() { - return sio.fifo().rd().read(); - } else { - // We expect the sending core to `sev` on write. - cortex_m::asm::wfe(); - } + // Reset the core + unsafe { + let psm = pac::PSM; + psm.frce_off().modify(|w| w.set_proc1(true)); + while !psm.frce_off().read().proc1() { + cortex_m::asm::nop(); + } + psm.frce_off().modify(|w| w.set_proc1(false)); + } + + // Set up the stack + let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; + + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + // Push the arguments to `core1_startup` onto the stack. + unsafe { + // Push `stack_bottom`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); + + // Push `entry`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + } + + // Make sure the compiler does not reorder the stack writes after to after the + // below FIFO writes, which would result in them not being seen by the second + // core. + // + // From the compiler perspective, this doesn't guarantee that the second core + // actually sees those writes. However, we know that the RP2040 doesn't have + // memory caches, and writes happen in-order. + compiler_fence(Ordering::Release); + + let p = unsafe { cortex_m::Peripherals::steal() }; + let vector_table = p.SCB.vtor.read(); + + // After reset, core 1 is waiting to receive commands over FIFO. + // This is the sequence to have it jump to some code. + let cmd_seq = [ + 0, + 0, + 1, + vector_table as usize, + stack_ptr as usize, + core1_startup:: as usize, + ]; + + let mut seq = 0; + let mut fails = 0; + loop { + let cmd = cmd_seq[seq] as u32; + if cmd == 0 { + fifo_drain(); + cortex_m::asm::sev(); + } + fifo_write(cmd); + + let response = fifo_read(); + if cmd == response { + seq += 1; + } else { + seq = 0; + fails += 1; + if fails > 16 { + // The second core isn't responding, and isn't going to take the entrypoint, + // so we have to drop it ourselves. + drop(ManuallyDrop::into_inner(entry)); + return Err(Error::Unresponsive); } } + if seq >= cmd_seq.len() { + break; + } } - fn fifo_drain() { - unsafe { - let sio = pac::SIO; - while sio.fifo().st().read().vld() { - let _ = sio.fifo().rd().read(); - } - } + // Wait until the other core has copied `entry` before returning. + fifo_read(); + + Ok(()) + } +} + +/// Pause execution on CORE1. +pub fn pause_core1() { + if IS_CORE1_INIT.load(Ordering::Acquire) { + fifo_write(PAUSE_TOKEN); + // Wait for CORE1 to signal it has paused execution. + while fifo_read() != PAUSE_TOKEN {} + } +} + +/// Resume CORE1 execution. +pub fn resume_core1() { + if IS_CORE1_INIT.load(Ordering::Acquire) { + fifo_write(RESUME_TOKEN); + // Wait for CORE1 to signal it has resumed execution. + while fifo_read() != RESUME_TOKEN {} + } +} + +// Push a value to the inter-core FIFO, block until space is available +#[inline(always)] +fn fifo_write(value: u32) { + unsafe { + let sio = pac::SIO; + // Wait for the FIFO to have enough space + while !sio.fifo().st().read().rdy() { + cortex_m::asm::nop(); } + sio.fifo().wr().write_value(value); + } + // Fire off an event to the other core. + // This is required as the other core may be `wfe` (waiting for event) + cortex_m::asm::sev(); +} - match self.id { - CoreId::Core1 => { - // The first two ignored `u64` parameters are there to take up all of the registers, - // which means that the rest of the arguments are taken from the stack, - // where we're able to put them from core 0. - extern "C" fn core1_startup bad::Never>( - _: u64, - _: u64, - entry: &mut ManuallyDrop, - stack_bottom: *mut usize, - ) -> ! { - core1_setup(stack_bottom); - let entry = unsafe { ManuallyDrop::take(entry) }; - // Signal that it's safe for core 0 to get rid of the original value now. - fifo_write(1); - entry() - } +// Pop a value from inter-core FIFO, block until available +#[inline(always)] +fn fifo_read() -> u32 { + unsafe { + let sio = pac::SIO; + // Wait until FIFO has data + while !sio.fifo().st().read().vld() { + cortex_m::asm::nop(); + } + sio.fifo().rd().read() + } +} - // Reset the core - unsafe { - let psm = pac::PSM; - psm.frce_off().modify(|w| w.set_proc1(true)); - while !psm.frce_off().read().proc1() { - cortex_m::asm::nop(); - } - psm.frce_off().modify(|w| w.set_proc1(false)); - } +// Pop a value from inter-core FIFO, `wfe` until available +#[inline(always)] +fn fifo_read_wfe() -> u32 { + unsafe { + let sio = pac::SIO; + // Wait until FIFO has data + while !sio.fifo().st().read().vld() { + cortex_m::asm::wfe(); + } + sio.fifo().rd().read() + } +} - // Set up the stack - let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; - - // We don't want to drop this, since it's getting moved to the other core. - let mut entry = ManuallyDrop::new(entry); - - // Push the arguments to `core1_startup` onto the stack. - unsafe { - // Push `stack_bottom`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); - - // Push `entry`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); - } - - // Make sure the compiler does not reorder the stack writes after to after the - // below FIFO writes, which would result in them not being seen by the second - // core. - // - // From the compiler perspective, this doesn't guarantee that the second core - // actually sees those writes. However, we know that the RP2040 doesn't have - // memory caches, and writes happen in-order. - compiler_fence(Ordering::Release); - - let p = unsafe { cortex_m::Peripherals::steal() }; - let vector_table = p.SCB.vtor.read(); - - // After reset, core 1 is waiting to receive commands over FIFO. - // This is the sequence to have it jump to some code. - let cmd_seq = [ - 0, - 0, - 1, - vector_table as usize, - stack_ptr as usize, - core1_startup:: as usize, - ]; - - let mut seq = 0; - let mut fails = 0; - loop { - let cmd = cmd_seq[seq] as u32; - if cmd == 0 { - fifo_drain(); - cortex_m::asm::sev(); - } - fifo_write(cmd); - - let response = fifo_read(); - if cmd == response { - seq += 1; - } else { - seq = 0; - fails += 1; - if fails > 16 { - // The second core isn't responding, and isn't going to take the entrypoint, - // so we have to drop it ourselves. - drop(ManuallyDrop::into_inner(entry)); - return Err(Error::Unresponsive); - } - } - if seq >= cmd_seq.len() { - break; - } - } - - // Wait until the other core has copied `entry` before returning. - fifo_read(); - - Ok(()) - } - _ => Err(Error::InvalidCore), +// Drain inter-core FIFO +#[inline(always)] +fn fifo_drain() { + unsafe { + let sio = pac::SIO; + while sio.fifo().st().read().vld() { + let _ = sio.fifo().rd().read(); } } } From 95fdc7c55237d749f11d94f1e3f7602f3c057483 Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Tue, 13 Dec 2022 14:06:37 +0800 Subject: [PATCH 0422/1575] fix net README.md --- embassy-net/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-net/README.md b/embassy-net/README.md index 9657e3589..470926c58 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -17,11 +17,13 @@ sudo ip -6 route add fe80::/64 dev tap0 sudo ip -6 route add fdaa::/64 dev tap0 ``` +Second, have something listening there. For example `nc -l 8000` + Then run the example located in the `examples` folder: ```sh cd $EMBASSY_ROOT/examples/std/ -cargo run --bin net +cargo run --bin net -- --static-ip ``` ## License From aea28c8aa0bc13251835c6e38f4e1fbcbd30f4db Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 09:45:11 +0100 Subject: [PATCH 0423/1575] Add usage in to docs --- embassy-rp/src/multicore.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 09d40b2ef..3703fe819 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -1,4 +1,4 @@ -//! MultiCore support +//! Multicore support //! //! This module handles setup of the 2nd cpu core on the rp2040, which we refer to as core1. //! It provides functionality for setting up the stack, and starting core1. @@ -7,6 +7,28 @@ //! //! Enable the `critical-section-impl` feature in embassy-rp when sharing data across cores using //! the `embassy-sync` primitives and `CriticalSectionRawMutex`. +//! +//! # Usage +//! ```no_run +//! static mut CORE1_STACK: Stack<4096> = Stack::new(); +//! static EXECUTOR0: StaticCell = StaticCell::new(); +//! static EXECUTOR1: StaticCell = StaticCell::new(); +//! +//! #[cortex_m_rt::entry] +//! fn main() -> ! { +//! let p = embassy_rp::init(Default::default()); +//! +//! let mut mc = MultiCore::new(); +//! let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { +//! let executor1 = EXECUTOR1.init(Executor::new()); +//! executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); +//! }); +//! +//! let executor0 = EXECUTOR0.init(Executor::new()); +//! executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +//! } +//! ``` +//! use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; @@ -28,14 +50,6 @@ pub enum Error { Unresponsive, } -/// Core ID -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CoreId { - Core0, - Core1, -} - #[inline(always)] fn install_stack_guard(stack_bottom: *mut usize) { let core = unsafe { cortex_m::Peripherals::steal() }; From 13d9d8fde109c09e310cbf1735917a768f4a1cf6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 13:49:51 +0100 Subject: [PATCH 0424/1575] Refactor after review --- embassy-rp/src/lib.rs | 2 + embassy-rp/src/multicore.rs | 251 +++++++++++++------------------ examples/rp/src/bin/multicore.rs | 5 +- tests/rp/src/bin/multicore.rs | 47 ++++++ 4 files changed, 159 insertions(+), 146 deletions(-) create mode 100644 tests/rp/src/bin/multicore.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 2cd99f456..c6442c56e 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -106,6 +106,8 @@ embassy_hal_common::peripherals! { FLASH, ADC, + + CORE1, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 3703fe819..2dfa215b5 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -36,20 +36,13 @@ use core::sync::atomic::{compiler_fence, Ordering}; use atomic_polyfill::AtomicBool; use crate::interrupt::{Interrupt, InterruptExt}; +use crate::peripherals::CORE1; use crate::{interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); -/// Errors for multicore operations. -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// Core was unresponsive to commands. - Unresponsive, -} - #[inline(always)] fn install_stack_guard(stack_bottom: *mut usize) { let core = unsafe { cortex_m::Peripherals::steal() }; @@ -81,44 +74,20 @@ fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); } -/// MultiCore execution management. -pub struct MultiCore { - pub cores: (Core0, Core1), -} - -/// Data type for a properly aligned stack of N 32-bit (usize) words +/// Data type for a properly aligned stack of N bytes #[repr(C, align(32))] pub struct Stack { /// Memory to be used for the stack - pub mem: [usize; SIZE], + pub mem: [u8; SIZE], } impl Stack { /// Construct a stack of length SIZE, initialized to 0 pub const fn new() -> Stack { - Stack { mem: [0; SIZE] } + Stack { mem: [0_u8; SIZE] } } } -impl MultiCore { - /// Create a new |MultiCore| instance. - pub fn new() -> Self { - Self { - cores: (Core0 {}, Core1 {}), - } - } - - /// Get the available |Core| instances. - pub fn cores(&mut self) -> &mut (Core0, Core1) { - &mut self.cores - } -} - -/// A handle for controlling a logical core. -pub struct Core0 {} -/// A handle for controlling a logical core. -pub struct Core1 {} - #[interrupt] #[link_section = ".data.ram_func"] unsafe fn SIO_IRQ_PROC1() { @@ -143,117 +112,113 @@ unsafe fn SIO_IRQ_PROC1() { } } -impl Core1 { - /// Spawn a function on this core - pub fn spawn(&mut self, stack: &'static mut [usize], entry: F) -> Result<(), Error> - where - F: FnOnce() -> bad::Never + Send + 'static, - { - // The first two ignored `u64` parameters are there to take up all of the registers, - // which means that the rest of the arguments are taken from the stack, - // where we're able to put them from core 0. - extern "C" fn core1_startup bad::Never>( - _: u64, - _: u64, - entry: &mut ManuallyDrop, - stack_bottom: *mut usize, - ) -> ! { - core1_setup(stack_bottom); - let entry = unsafe { ManuallyDrop::take(entry) }; - // Signal that it's safe for core 0 to get rid of the original value now. - fifo_write(1); +/// Spawn a function on this core +pub fn spawn_core1(_core1: CORE1, stack: &'static mut Stack, entry: F) +where + F: FnOnce() -> bad::Never + Send + 'static, +{ + // The first two ignored `u64` parameters are there to take up all of the registers, + // which means that the rest of the arguments are taken from the stack, + // where we're able to put them from core 0. + extern "C" fn core1_startup bad::Never>( + _: u64, + _: u64, + entry: &mut ManuallyDrop, + stack_bottom: *mut usize, + ) -> ! { + core1_setup(stack_bottom); + let entry = unsafe { ManuallyDrop::take(entry) }; + // Signal that it's safe for core 0 to get rid of the original value now. + fifo_write(1); - IS_CORE1_INIT.store(true, Ordering::Release); - // Enable fifo interrupt on CORE1 for `pause` functionality. - let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; - irq.enable(); + IS_CORE1_INIT.store(true, Ordering::Release); + // Enable fifo interrupt on CORE1 for `pause` functionality. + let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; + irq.enable(); - entry() - } - - // Reset the core - unsafe { - let psm = pac::PSM; - psm.frce_off().modify(|w| w.set_proc1(true)); - while !psm.frce_off().read().proc1() { - cortex_m::asm::nop(); - } - psm.frce_off().modify(|w| w.set_proc1(false)); - } - - // Set up the stack - let mut stack_ptr = unsafe { stack.as_mut_ptr().add(stack.len()) }; - - // We don't want to drop this, since it's getting moved to the other core. - let mut entry = ManuallyDrop::new(entry); - - // Push the arguments to `core1_startup` onto the stack. - unsafe { - // Push `stack_bottom`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<*mut usize>().write(stack.as_mut_ptr()); - - // Push `entry`. - stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); - } - - // Make sure the compiler does not reorder the stack writes after to after the - // below FIFO writes, which would result in them not being seen by the second - // core. - // - // From the compiler perspective, this doesn't guarantee that the second core - // actually sees those writes. However, we know that the RP2040 doesn't have - // memory caches, and writes happen in-order. - compiler_fence(Ordering::Release); - - let p = unsafe { cortex_m::Peripherals::steal() }; - let vector_table = p.SCB.vtor.read(); - - // After reset, core 1 is waiting to receive commands over FIFO. - // This is the sequence to have it jump to some code. - let cmd_seq = [ - 0, - 0, - 1, - vector_table as usize, - stack_ptr as usize, - core1_startup:: as usize, - ]; - - let mut seq = 0; - let mut fails = 0; - loop { - let cmd = cmd_seq[seq] as u32; - if cmd == 0 { - fifo_drain(); - cortex_m::asm::sev(); - } - fifo_write(cmd); - - let response = fifo_read(); - if cmd == response { - seq += 1; - } else { - seq = 0; - fails += 1; - if fails > 16 { - // The second core isn't responding, and isn't going to take the entrypoint, - // so we have to drop it ourselves. - drop(ManuallyDrop::into_inner(entry)); - return Err(Error::Unresponsive); - } - } - if seq >= cmd_seq.len() { - break; - } - } - - // Wait until the other core has copied `entry` before returning. - fifo_read(); - - Ok(()) + entry() } + + // Reset the core + unsafe { + let psm = pac::PSM; + psm.frce_off().modify(|w| w.set_proc1(true)); + while !psm.frce_off().read().proc1() { + cortex_m::asm::nop(); + } + psm.frce_off().modify(|w| w.set_proc1(false)); + } + + let mem = unsafe { core::slice::from_raw_parts_mut(stack.mem.as_mut_ptr() as *mut usize, stack.mem.len() / 4) }; + + // Set up the stack + let mut stack_ptr = unsafe { mem.as_mut_ptr().add(mem.len()) }; + + // We don't want to drop this, since it's getting moved to the other core. + let mut entry = ManuallyDrop::new(entry); + + // Push the arguments to `core1_startup` onto the stack. + unsafe { + // Push `stack_bottom`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<*mut usize>().write(mem.as_mut_ptr()); + + // Push `entry`. + stack_ptr = stack_ptr.sub(1); + stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + } + + // Make sure the compiler does not reorder the stack writes after to after the + // below FIFO writes, which would result in them not being seen by the second + // core. + // + // From the compiler perspective, this doesn't guarantee that the second core + // actually sees those writes. However, we know that the RP2040 doesn't have + // memory caches, and writes happen in-order. + compiler_fence(Ordering::Release); + + let p = unsafe { cortex_m::Peripherals::steal() }; + let vector_table = p.SCB.vtor.read(); + + // After reset, core 1 is waiting to receive commands over FIFO. + // This is the sequence to have it jump to some code. + let cmd_seq = [ + 0, + 0, + 1, + vector_table as usize, + stack_ptr as usize, + core1_startup:: as usize, + ]; + + let mut seq = 0; + let mut fails = 0; + loop { + let cmd = cmd_seq[seq] as u32; + if cmd == 0 { + fifo_drain(); + cortex_m::asm::sev(); + } + fifo_write(cmd); + + let response = fifo_read(); + if cmd == response { + seq += 1; + } else { + seq = 0; + fails += 1; + if fails > 16 { + // The second core isn't responding, and isn't going to take the entrypoint + panic!("CORE1 not responding"); + } + } + if seq >= cmd_seq.len() { + break; + } + } + + // Wait until the other core has copied `entry` before returning. + fifo_read(); } /// Pause execution on CORE1. diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 53941da60..376b2b61e 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_executor::_export::StaticCell; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::multicore::{MultiCore, Stack}; +use embassy_rp::multicore::{spawn_core1, Stack}; use embassy_rp::peripherals::PIN_25; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -28,8 +28,7 @@ fn main() -> ! { let p = embassy_rp::init(Default::default()); let led = Output::new(p.PIN_25, Level::Low); - let mut mc = MultiCore::new(); - let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { let executor1 = EXECUTOR1.init(Executor::new()); executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); }); diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs new file mode 100644 index 000000000..da78e887a --- /dev/null +++ b/tests/rp/src/bin/multicore.rs @@ -0,0 +1,47 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<1024> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); +static CHANNEL0: Channel = Channel::new(); +static CHANNEL1: Channel = Channel::new(); + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); + }); + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +} + +#[embassy_executor::task] +async fn core0_task() { + info!("CORE0 is running"); + let ping = true; + CHANNEL0.send(ping).await; + let pong = CHANNEL1.recv().await; + assert_eq!(ping, pong); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[embassy_executor::task] +async fn core1_task() { + info!("CORE1 is running"); + let ping = CHANNEL0.recv().await; + CHANNEL1.send(ping).await; +} From 731eb3c6e31ca7218636c1fba266b2c643e8fb44 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 13:55:23 +0100 Subject: [PATCH 0425/1575] fmt --- embassy-rp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 248884e7c..0ea97aecf 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -116,7 +116,7 @@ embassy_hal_common::peripherals! { ADC, CORE1, - + PIO0, PIO1, } From c4d8f3579e4927446421730d52a409d724f2800c Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 13 Dec 2022 14:15:04 +0100 Subject: [PATCH 0426/1575] Update usage in docs --- embassy-rp/src/multicore.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 2dfa215b5..8290c62a7 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -18,8 +18,7 @@ //! fn main() -> ! { //! let p = embassy_rp::init(Default::default()); //! -//! let mut mc = MultiCore::new(); -//! let _ = mc.cores.1.spawn(unsafe { &mut CORE1_STACK.mem }, move || { +//! spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { //! let executor1 = EXECUTOR1.init(Executor::new()); //! executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); //! }); @@ -28,7 +27,6 @@ //! executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); //! } //! ``` -//! use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, Ordering}; From ac74613b5a7be72acd8d5259055963f8b4aba7fd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 15:55:46 +0100 Subject: [PATCH 0427/1575] net: remove packet pool. The pool was prone to deadlocks, especially due to having a single pool for rx+tx. If the pool got full with rx'd packets it would deadlock because processing a rx packet requires doing another allocation on the pool, for the possibly tx'd response, before deallocating the rx'd packet. This also allows Device impls to allocate the packet memory in a particular RAM kind, if needed for example to do DMA. The `Device` trait is now token-based, like smoltcp's. In the end, this is better because it allows callers to manage memory however they want (including with a pool if they want to). --- embassy-net/src/device.rs | 163 ++++++++++++++++++--------------- embassy-net/src/lib.rs | 6 +- embassy-net/src/packet_pool.rs | 107 ---------------------- embassy-net/src/stack.rs | 24 +++-- embassy-net/src/tcp.rs | 2 +- 5 files changed, 105 insertions(+), 197 deletions(-) delete mode 100644 embassy-net/src/packet_pool.rs diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 4bdfd7720..5d86ca91e 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -1,10 +1,7 @@ -use core::task::Waker; +use core::task::Context; -use smoltcp::phy::{Device as SmolDevice, DeviceCapabilities}; -use smoltcp::time::Instant as SmolInstant; - -use crate::packet_pool::PacketBoxExt; -use crate::{Packet, PacketBox, PacketBuf}; +use smoltcp::phy; +pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium}; #[derive(PartialEq, Eq, Clone, Copy)] pub enum LinkState { @@ -13,115 +10,133 @@ pub enum LinkState { } pub trait Device { - fn is_transmit_ready(&mut self) -> bool; - fn transmit(&mut self, pkt: PacketBuf); - fn receive(&mut self) -> Option; + type RxToken<'a>: RxToken + where + Self: 'a; + type TxToken<'a>: TxToken + where + Self: 'a; - fn register_waker(&mut self, waker: &Waker); - fn capabilities(&self) -> DeviceCapabilities; - fn link_state(&mut self) -> LinkState; + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + fn transmit(&mut self, cx: &mut Context) -> Option>; + fn link_state(&mut self, cx: &mut Context) -> LinkState; + + fn capabilities(&self) -> phy::DeviceCapabilities; fn ethernet_address(&self) -> [u8; 6]; } impl Device for &mut T { - fn is_transmit_ready(&mut self) -> bool { - T::is_transmit_ready(self) + type RxToken<'a> = T::RxToken<'a> + where + Self: 'a; + type TxToken<'a> = T::TxToken<'a> + where + Self: 'a; + + fn transmit(&mut self, cx: &mut Context) -> Option> { + T::transmit(self, cx) } - fn transmit(&mut self, pkt: PacketBuf) { - T::transmit(self, pkt) + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + T::receive(self, cx) } - fn receive(&mut self) -> Option { - T::receive(self) - } - fn register_waker(&mut self, waker: &Waker) { - T::register_waker(self, waker) - } - fn capabilities(&self) -> DeviceCapabilities { + fn capabilities(&self) -> phy::DeviceCapabilities { T::capabilities(self) } - fn link_state(&mut self) -> LinkState { - T::link_state(self) + fn link_state(&mut self, cx: &mut Context) -> LinkState { + T::link_state(self, cx) } fn ethernet_address(&self) -> [u8; 6] { T::ethernet_address(self) } } -pub struct DeviceAdapter { - pub device: D, - caps: DeviceCapabilities, +/// A token to receive a single network packet. +pub trait RxToken { + /// Consumes the token to receive a single network packet. + /// + /// This method receives a packet and then calls the given closure `f` with the raw + /// packet bytes as argument. + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; } -impl DeviceAdapter { - pub(crate) fn new(device: D) -> Self { - Self { - caps: device.capabilities(), - device, - } - } +/// A token to transmit a single network packet. +pub trait TxToken { + /// Consumes the token to send a single network packet. + /// + /// This method constructs a transmit buffer of size `len` and calls the passed + /// closure `f` with a mutable reference to that buffer. The closure should construct + /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure + /// returns, the transmit buffer is sent out. + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; } -impl SmolDevice for DeviceAdapter { - type RxToken<'a> = RxToken where Self: 'a; - type TxToken<'a> = TxToken<'a, D> where Self: 'a; +/////////////////////////// + +pub(crate) struct DeviceAdapter<'d, 'c, T> +where + T: Device, +{ + // must be Some when actually using this to rx/tx + pub cx: Option<&'d mut Context<'c>>, + pub inner: &'d mut T, +} + +impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T> +where + T: Device, +{ + type RxToken<'a> = RxTokenAdapter> where Self: 'a; + type TxToken<'a> = TxTokenAdapter> where Self: 'a; 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 }; - let tx_token = TxToken { - device: &mut self.device, - pkt: tx_pkt, - }; - - Some((rx_token, tx_token)) + self.inner + .receive(self.cx.as_deref_mut().unwrap()) + .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) } /// Construct a transmit token. fn transmit(&mut self) -> Option> { - if !self.device.is_transmit_ready() { - return None; - } - - let tx_pkt = PacketBox::new(Packet::new())?; - Some(TxToken { - device: &mut self.device, - pkt: tx_pkt, - }) + self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) } /// Get a description of device capabilities. - fn capabilities(&self) -> DeviceCapabilities { - self.caps.clone() + fn capabilities(&self) -> phy::DeviceCapabilities { + self.inner.capabilities() } } -pub struct RxToken { - pkt: PacketBuf, -} +pub(crate) struct RxTokenAdapter(T) +where + T: RxToken; -impl smoltcp::phy::RxToken for RxToken { - fn consume(mut self, _timestamp: SmolInstant, f: F) -> smoltcp::Result +impl phy::RxToken for RxTokenAdapter +where + T: RxToken, +{ + fn consume(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result where F: FnOnce(&mut [u8]) -> smoltcp::Result, { - f(&mut self.pkt) + self.0.consume(|buf| f(buf)) } } -pub struct TxToken<'a, D: Device> { - device: &'a mut D, - pkt: PacketBox, -} +pub(crate) struct TxTokenAdapter(T) +where + T: TxToken; -impl<'a, D: Device> smoltcp::phy::TxToken for TxToken<'a, D> { - fn consume(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result +impl phy::TxToken for TxTokenAdapter +where + T: TxToken, +{ + fn consume(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result where F: FnOnce(&mut [u8]) -> smoltcp::Result, { - let mut buf = self.pkt.slice(0..len); - let r = f(&mut buf)?; - self.device.transmit(buf); - Ok(r) + self.0.consume(len, |buf| f(buf)) } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index edb969842..e18e819cb 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -8,12 +8,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -mod device; -mod packet_pool; +pub mod device; mod stack; -pub use device::{Device, LinkState}; -pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU}; pub use stack::{Config, ConfigStrategy, Stack, StackResources}; #[cfg(feature = "tcp")] @@ -23,7 +20,6 @@ pub mod tcp; pub mod udp; // smoltcp reexports -pub use smoltcp::phy::{DeviceCapabilities, Medium}; pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs deleted file mode 100644 index cb8a1316c..000000000 --- a/embassy-net/src/packet_pool.rs +++ /dev/null @@ -1,107 +0,0 @@ -use core::ops::{Deref, DerefMut, Range}; - -use as_slice::{AsMutSlice, AsSlice}; -use atomic_pool::{pool, Box}; - -pub const MTU: usize = 1516; - -#[cfg(feature = "pool-4")] -pub const PACKET_POOL_SIZE: usize = 4; - -#[cfg(feature = "pool-8")] -pub const PACKET_POOL_SIZE: usize = 8; - -#[cfg(feature = "pool-16")] -pub const PACKET_POOL_SIZE: usize = 16; - -#[cfg(feature = "pool-32")] -pub const PACKET_POOL_SIZE: usize = 32; - -#[cfg(feature = "pool-64")] -pub const PACKET_POOL_SIZE: usize = 64; - -#[cfg(feature = "pool-128")] -pub const PACKET_POOL_SIZE: usize = 128; - -pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]); -pub type PacketBox = Box; - -#[repr(align(4))] -pub struct Packet(pub [u8; MTU]); - -impl Packet { - pub const fn new() -> Self { - Self([0; MTU]) - } -} - -pub trait PacketBoxExt { - fn slice(self, range: Range) -> PacketBuf; -} - -impl PacketBoxExt for PacketBox { - fn slice(self, range: Range) -> PacketBuf { - PacketBuf { packet: self, range } - } -} - -impl AsSlice for Packet { - type Element = u8; - - fn as_slice(&self) -> &[Self::Element] { - &self.deref()[..] - } -} - -impl AsMutSlice for Packet { - fn as_mut_slice(&mut self) -> &mut [Self::Element] { - &mut self.deref_mut()[..] - } -} - -impl Deref for Packet { - type Target = [u8; MTU]; - - fn deref(&self) -> &[u8; MTU] { - &self.0 - } -} - -impl DerefMut for Packet { - fn deref_mut(&mut self) -> &mut [u8; MTU] { - &mut self.0 - } -} - -pub struct PacketBuf { - packet: PacketBox, - range: Range, -} - -impl AsSlice for PacketBuf { - type Element = u8; - - fn as_slice(&self) -> &[Self::Element] { - &self.packet[self.range.clone()] - } -} - -impl AsMutSlice for PacketBuf { - fn as_mut_slice(&mut self) -> &mut [Self::Element] { - &mut self.packet[self.range.clone()] - } -} - -impl Deref for PacketBuf { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - &self.packet[self.range.clone()] - } -} - -impl DerefMut for PacketBuf { - fn deref_mut(&mut self) -> &mut [u8] { - &mut self.packet[self.range.clone()] - } -} diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 5c4fb0442..21316e485 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -12,7 +12,7 @@ use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; #[cfg(feature = "medium-ethernet")] use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; #[cfg(feature = "medium-ethernet")] -use smoltcp::phy::{Device as _, Medium}; +use smoltcp::phy::Medium; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; use smoltcp::time::Instant as SmolInstant; @@ -67,7 +67,7 @@ pub struct Stack { } struct Inner { - device: DeviceAdapter, + device: D, link_up: bool, config: Option, #[cfg(feature = "dhcpv4")] @@ -83,7 +83,7 @@ pub(crate) struct SocketStack { impl Stack { pub fn new( - device: D, + mut device: D, config: ConfigStrategy, resources: &'static mut StackResources, random_seed: u64, @@ -98,8 +98,6 @@ impl Stack { [0, 0, 0, 0, 0, 0] }; - let mut device = DeviceAdapter::new(device); - let mut b = InterfaceBuilder::new(); b = b.ip_addrs(&mut resources.addresses[..]); b = b.random_seed(random_seed); @@ -111,7 +109,10 @@ impl Stack { b = b.routes(Routes::new(&mut resources.routes[..])); } - let iface = b.finalize(&mut device); + let iface = b.finalize(&mut DeviceAdapter { + inner: &mut device, + cx: None, + }); let sockets = SocketSet::new(&mut resources.sockets[..]); @@ -155,7 +156,7 @@ impl Stack { } pub fn ethernet_address(&self) -> [u8; 6] { - self.with(|_s, i| i.device.device.ethernet_address()) + self.with(|_s, i| i.device.ethernet_address()) } pub fn is_link_up(&self) -> bool { @@ -238,11 +239,14 @@ impl Inner { } fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { - self.device.device.register_waker(cx.waker()); s.waker.register(cx.waker()); let timestamp = instant_to_smoltcp(Instant::now()); - if s.iface.poll(timestamp, &mut self.device, &mut s.sockets).is_err() { + let mut smoldev = DeviceAdapter { + cx: Some(cx), + inner: &mut self.device, + }; + if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { // If poll() returns error, it may not be done yet, so poll again later. cx.waker().wake_by_ref(); return; @@ -250,7 +254,7 @@ impl Inner { // Update link up let old_link_up = self.link_up; - self.link_up = self.device.device.link_state() == LinkState::Up; + self.link_up = self.device.link_state(cx) == LinkState::Up; // Print when changed if old_link_up != self.link_up { diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 73cf2d4e4..0ed4b81e2 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -9,8 +9,8 @@ use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use super::stack::Stack; +use crate::device::Device; use crate::stack::SocketStack; -use crate::Device; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From aaaf5f23a8a3a0df1ad2186802e2afc061a74b72 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 16:02:28 +0100 Subject: [PATCH 0428/1575] net: move stack into lib.rs --- embassy-net/src/lib.rs | 307 ++++++++++++++++++++++++++++++++++++++- embassy-net/src/stack.rs | 306 -------------------------------------- embassy-net/src/tcp.rs | 3 +- embassy-net/src/udp.rs | 3 +- 4 files changed, 304 insertions(+), 315 deletions(-) delete mode 100644 embassy-net/src/stack.rs diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index e18e819cb..afe0d6da0 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -9,16 +9,28 @@ pub(crate) mod fmt; pub mod device; -mod stack; - -pub use stack::{Config, ConfigStrategy, Stack, StackResources}; - #[cfg(feature = "tcp")] pub mod tcp; - #[cfg(feature = "udp")] pub mod udp; +use core::cell::RefCell; +use core::future::{poll_fn, Future}; +use core::task::{Context, Poll}; + +use embassy_sync::waitqueue::WakerRegistration; +use embassy_time::{Instant, Timer}; +use futures::pin_mut; +use heapless::Vec; +#[cfg(feature = "dhcpv4")] +use smoltcp::iface::SocketHandle; +use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; +#[cfg(feature = "medium-ethernet")] +use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; +#[cfg(feature = "medium-ethernet")] +use smoltcp::phy::Medium; +#[cfg(feature = "dhcpv4")] +use smoltcp::socket::dhcpv4; // smoltcp reexports pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; #[cfg(feature = "medium-ethernet")] @@ -28,3 +40,288 @@ pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; #[cfg(feature = "udp")] pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; + +use crate::device::{Device, DeviceAdapter, LinkState}; + +const LOCAL_PORT_MIN: u16 = 1025; +const LOCAL_PORT_MAX: u16 = 65535; + +pub struct StackResources { + addresses: [IpCidr; ADDR], + sockets: [SocketStorage<'static>; SOCK], + + #[cfg(feature = "medium-ethernet")] + routes: [Option<(IpCidr, Route)>; 1], + #[cfg(feature = "medium-ethernet")] + neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], +} + +impl StackResources { + pub fn new() -> Self { + Self { + addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], + sockets: [SocketStorage::EMPTY; SOCK], + #[cfg(feature = "medium-ethernet")] + routes: [None; 1], + #[cfg(feature = "medium-ethernet")] + neighbor_cache: [None; NEIGHBOR], + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Config { + pub address: Ipv4Cidr, + pub gateway: Option, + pub dns_servers: Vec, +} + +pub enum ConfigStrategy { + Static(Config), + #[cfg(feature = "dhcpv4")] + Dhcp, +} + +pub struct Stack { + pub(crate) socket: RefCell, + inner: RefCell>, +} + +struct Inner { + device: D, + link_up: bool, + config: Option, + #[cfg(feature = "dhcpv4")] + dhcp_socket: Option, +} + +pub(crate) struct SocketStack { + pub(crate) sockets: SocketSet<'static>, + pub(crate) iface: Interface<'static>, + pub(crate) waker: WakerRegistration, + next_local_port: u16, +} + +impl Stack { + pub fn new( + mut device: D, + config: ConfigStrategy, + resources: &'static mut StackResources, + random_seed: u64, + ) -> Self { + #[cfg(feature = "medium-ethernet")] + let medium = device.capabilities().medium; + + #[cfg(feature = "medium-ethernet")] + let ethernet_addr = if medium == Medium::Ethernet { + device.ethernet_address() + } else { + [0, 0, 0, 0, 0, 0] + }; + + let mut b = InterfaceBuilder::new(); + b = b.ip_addrs(&mut resources.addresses[..]); + b = b.random_seed(random_seed); + + #[cfg(feature = "medium-ethernet")] + if medium == Medium::Ethernet { + b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); + b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); + b = b.routes(Routes::new(&mut resources.routes[..])); + } + + let iface = b.finalize(&mut DeviceAdapter { + inner: &mut device, + cx: None, + }); + + let sockets = SocketSet::new(&mut resources.sockets[..]); + + let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; + + let mut inner = Inner { + device, + link_up: false, + config: None, + #[cfg(feature = "dhcpv4")] + dhcp_socket: None, + }; + let mut socket = SocketStack { + sockets, + iface, + waker: WakerRegistration::new(), + next_local_port, + }; + + match config { + ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), + #[cfg(feature = "dhcpv4")] + ConfigStrategy::Dhcp => { + let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); + inner.dhcp_socket = Some(handle); + } + } + + Self { + socket: RefCell::new(socket), + inner: RefCell::new(inner), + } + } + + fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { + f(&*self.socket.borrow(), &*self.inner.borrow()) + } + + 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] { + self.with(|_s, i| i.device.ethernet_address()) + } + + pub fn is_link_up(&self) -> bool { + self.with(|_s, i| i.link_up) + } + + pub fn is_config_up(&self) -> bool { + self.with(|_s, i| i.config.is_some()) + } + + pub fn config(&self) -> Option { + self.with(|_s, i| i.config.clone()) + } + + pub async fn run(&self) -> ! { + poll_fn(|cx| { + self.with_mut(|s, i| i.poll(cx, s)); + Poll::<()>::Pending + }) + .await; + unreachable!() + } +} + +impl SocketStack { + #[allow(clippy::absurd_extreme_comparisons)] + pub fn get_local_port(&mut self) -> u16 { + let res = self.next_local_port; + self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; + res + } +} + +impl Inner { + fn apply_config(&mut self, s: &mut SocketStack, config: Config) { + #[cfg(feature = "medium-ethernet")] + let medium = self.device.capabilities().medium; + + debug!("Acquired IP configuration:"); + + debug!(" IP address: {}", config.address); + self.set_ipv4_addr(s, config.address); + + #[cfg(feature = "medium-ethernet")] + if medium == Medium::Ethernet { + if let Some(gateway) = config.gateway { + debug!(" Default gateway: {}", gateway); + s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); + } else { + debug!(" Default gateway: None"); + s.iface.routes_mut().remove_default_ipv4_route(); + } + } + for (i, s) in config.dns_servers.iter().enumerate() { + debug!(" DNS server {}: {}", i, s); + } + + self.config = Some(config) + } + + #[allow(unused)] // used only with dhcp + fn unapply_config(&mut self, s: &mut SocketStack) { + #[cfg(feature = "medium-ethernet")] + let medium = self.device.capabilities().medium; + + debug!("Lost IP configuration"); + self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + #[cfg(feature = "medium-ethernet")] + if medium == Medium::Ethernet { + s.iface.routes_mut().remove_default_ipv4_route(); + } + self.config = None + } + + fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { + s.iface.update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next().unwrap(); + *dest = IpCidr::Ipv4(cidr); + }); + } + + fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { + s.waker.register(cx.waker()); + + let timestamp = instant_to_smoltcp(Instant::now()); + let mut smoldev = DeviceAdapter { + cx: Some(cx), + inner: &mut self.device, + }; + if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { + // If poll() returns error, it may not be done yet, so poll again later. + cx.waker().wake_by_ref(); + return; + } + + // Update link up + let old_link_up = self.link_up; + self.link_up = self.device.link_state(cx) == LinkState::Up; + + // Print when changed + if old_link_up != self.link_up { + info!("link_up = {:?}", self.link_up); + } + + #[cfg(feature = "dhcpv4")] + if let Some(dhcp_handle) = self.dhcp_socket { + let socket = s.sockets.get_mut::(dhcp_handle); + + if self.link_up { + match socket.poll() { + None => {} + Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), + Some(dhcpv4::Event::Configured(config)) => { + let config = Config { + address: config.address, + gateway: config.router, + dns_servers: config.dns_servers, + }; + self.apply_config(s, config) + } + } + } else if old_link_up { + socket.reset(); + self.unapply_config(s); + } + } + //if old_link_up || self.link_up { + // self.poll_configurator(timestamp) + //} + + if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { + let t = Timer::at(instant_from_smoltcp(poll_at)); + pin_mut!(t); + if t.poll(cx).is_ready() { + cx.waker().wake_by_ref(); + } + } + } +} + +fn instant_to_smoltcp(instant: Instant) -> SmolInstant { + SmolInstant::from_millis(instant.as_millis() as i64) +} + +fn instant_from_smoltcp(instant: SmolInstant) -> Instant { + Instant::from_millis(instant.total_millis() as u64) +} diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs deleted file mode 100644 index 21316e485..000000000 --- a/embassy-net/src/stack.rs +++ /dev/null @@ -1,306 +0,0 @@ -use core::cell::RefCell; -use core::future::{poll_fn, Future}; -use core::task::{Context, Poll}; - -use embassy_sync::waitqueue::WakerRegistration; -use embassy_time::{Instant, Timer}; -use futures::pin_mut; -use heapless::Vec; -#[cfg(feature = "dhcpv4")] -use smoltcp::iface::SocketHandle; -use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::phy::Medium; -#[cfg(feature = "dhcpv4")] -use smoltcp::socket::dhcpv4; -use smoltcp::time::Instant as SmolInstant; -#[cfg(feature = "medium-ethernet")] -use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress}; -use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr}; - -use crate::device::{Device, DeviceAdapter, LinkState}; - -const LOCAL_PORT_MIN: u16 = 1025; -const LOCAL_PORT_MAX: u16 = 65535; - -pub struct StackResources { - addresses: [IpCidr; ADDR], - sockets: [SocketStorage<'static>; SOCK], - - #[cfg(feature = "medium-ethernet")] - routes: [Option<(IpCidr, Route)>; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], -} - -impl StackResources { - pub fn new() -> Self { - Self { - addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], - sockets: [SocketStorage::EMPTY; SOCK], - #[cfg(feature = "medium-ethernet")] - routes: [None; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [None; NEIGHBOR], - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Config { - pub address: Ipv4Cidr, - pub gateway: Option, - pub dns_servers: Vec, -} - -pub enum ConfigStrategy { - Static(Config), - #[cfg(feature = "dhcpv4")] - Dhcp, -} - -pub struct Stack { - pub(crate) socket: RefCell, - inner: RefCell>, -} - -struct Inner { - device: D, - link_up: bool, - config: Option, - #[cfg(feature = "dhcpv4")] - dhcp_socket: Option, -} - -pub(crate) struct SocketStack { - pub(crate) sockets: SocketSet<'static>, - pub(crate) iface: Interface<'static>, - pub(crate) waker: WakerRegistration, - next_local_port: u16, -} - -impl Stack { - pub fn new( - mut device: D, - config: ConfigStrategy, - resources: &'static mut StackResources, - random_seed: u64, - ) -> Self { - #[cfg(feature = "medium-ethernet")] - let medium = device.capabilities().medium; - - #[cfg(feature = "medium-ethernet")] - let ethernet_addr = if medium == Medium::Ethernet { - device.ethernet_address() - } else { - [0, 0, 0, 0, 0, 0] - }; - - let mut b = InterfaceBuilder::new(); - b = b.ip_addrs(&mut resources.addresses[..]); - b = b.random_seed(random_seed); - - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); - b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); - b = b.routes(Routes::new(&mut resources.routes[..])); - } - - let iface = b.finalize(&mut DeviceAdapter { - inner: &mut device, - cx: None, - }); - - let sockets = SocketSet::new(&mut resources.sockets[..]); - - let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - - let mut inner = Inner { - device, - link_up: false, - config: None, - #[cfg(feature = "dhcpv4")] - dhcp_socket: None, - }; - let mut socket = SocketStack { - sockets, - iface, - waker: WakerRegistration::new(), - next_local_port, - }; - - match config { - ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), - #[cfg(feature = "dhcpv4")] - ConfigStrategy::Dhcp => { - let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); - inner.dhcp_socket = Some(handle); - } - } - - Self { - socket: RefCell::new(socket), - inner: RefCell::new(inner), - } - } - - fn with(&self, f: impl FnOnce(&SocketStack, &Inner) -> R) -> R { - f(&*self.socket.borrow(), &*self.inner.borrow()) - } - - 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] { - self.with(|_s, i| i.device.ethernet_address()) - } - - pub fn is_link_up(&self) -> bool { - self.with(|_s, i| i.link_up) - } - - pub fn is_config_up(&self) -> bool { - self.with(|_s, i| i.config.is_some()) - } - - pub fn config(&self) -> Option { - self.with(|_s, i| i.config.clone()) - } - - pub async fn run(&self) -> ! { - poll_fn(|cx| { - self.with_mut(|s, i| i.poll(cx, s)); - Poll::<()>::Pending - }) - .await; - unreachable!() - } -} - -impl SocketStack { - #[allow(clippy::absurd_extreme_comparisons)] - pub fn get_local_port(&mut self) -> u16 { - let res = self.next_local_port; - self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; - res - } -} - -impl Inner { - fn apply_config(&mut self, s: &mut SocketStack, config: Config) { - #[cfg(feature = "medium-ethernet")] - let medium = self.device.capabilities().medium; - - debug!("Acquired IP configuration:"); - - debug!(" IP address: {}", config.address); - self.set_ipv4_addr(s, config.address); - - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - if let Some(gateway) = config.gateway { - debug!(" Default gateway: {}", gateway); - s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); - } else { - debug!(" Default gateway: None"); - s.iface.routes_mut().remove_default_ipv4_route(); - } - } - for (i, s) in config.dns_servers.iter().enumerate() { - debug!(" DNS server {}: {}", i, s); - } - - self.config = Some(config) - } - - #[allow(unused)] // used only with dhcp - fn unapply_config(&mut self, s: &mut SocketStack) { - #[cfg(feature = "medium-ethernet")] - let medium = self.device.capabilities().medium; - - debug!("Lost IP configuration"); - self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - s.iface.routes_mut().remove_default_ipv4_route(); - } - self.config = None - } - - fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { - s.iface.update_ip_addrs(|addrs| { - let dest = addrs.iter_mut().next().unwrap(); - *dest = IpCidr::Ipv4(cidr); - }); - } - - fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { - s.waker.register(cx.waker()); - - let timestamp = instant_to_smoltcp(Instant::now()); - let mut smoldev = DeviceAdapter { - cx: Some(cx), - inner: &mut self.device, - }; - if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { - // If poll() returns error, it may not be done yet, so poll again later. - cx.waker().wake_by_ref(); - return; - } - - // Update link up - let old_link_up = self.link_up; - self.link_up = self.device.link_state(cx) == LinkState::Up; - - // Print when changed - if old_link_up != self.link_up { - info!("link_up = {:?}", self.link_up); - } - - #[cfg(feature = "dhcpv4")] - if let Some(dhcp_handle) = self.dhcp_socket { - let socket = s.sockets.get_mut::(dhcp_handle); - - if self.link_up { - match socket.poll() { - None => {} - Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), - Some(dhcpv4::Event::Configured(config)) => { - let config = Config { - address: config.address, - gateway: config.router, - dns_servers: config.dns_servers, - }; - self.apply_config(s, config) - } - } - } else if old_link_up { - socket.reset(); - self.unapply_config(s); - } - } - //if old_link_up || self.link_up { - // self.poll_configurator(timestamp) - //} - - if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { - let t = Timer::at(instant_from_smoltcp(poll_at)); - pin_mut!(t); - if t.poll(cx).is_ready() { - cx.waker().wake_by_ref(); - } - } - } -} - -fn instant_to_smoltcp(instant: Instant) -> SmolInstant { - SmolInstant::from_millis(instant.as_millis() as i64) -} - -fn instant_from_smoltcp(instant: SmolInstant) -> Instant { - Instant::from_millis(instant.total_millis() as u64) -} diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0ed4b81e2..0dc8da73a 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -8,9 +8,8 @@ use smoltcp::socket::tcp; use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use super::stack::Stack; use crate::device::Device; -use crate::stack::SocketStack; +use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 4ddad77d4..2f5334df3 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -7,8 +7,7 @@ use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use super::stack::SocketStack; -use crate::{Device, Stack}; +use crate::{Device, SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From e9219405ca04e23b6543fb841fd97df54cf72f94 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Dec 2022 16:03:03 +0100 Subject: [PATCH 0429/1575] usb/cdc-ncm: add embassy-net Device implementation. --- embassy-usb/Cargo.toml | 1 + embassy-usb/src/class/cdc_ncm/embassy_net.rs | 449 ++++++++++++++++++ .../src/class/{cdc_ncm.rs => cdc_ncm/mod.rs} | 18 + examples/nrf/Cargo.toml | 2 +- examples/nrf/src/bin/usb_ethernet.rs | 139 +----- examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/usb_ethernet.rs | 140 +----- examples/stm32l5/Cargo.toml | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 140 +----- 9 files changed, 522 insertions(+), 371 deletions(-) create mode 100644 embassy-usb/src/class/cdc_ncm/embassy_net.rs rename embassy-usb/src/class/{cdc_ncm.rs => cdc_ncm/mod.rs} (94%) diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index b59ba8a22..1e72ce682 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -19,6 +19,7 @@ default = ["usbd-hid"] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs new file mode 100644 index 000000000..60bbfd8d4 --- /dev/null +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -0,0 +1,449 @@ +use core::cell::RefCell; +use core::mem::MaybeUninit; +use core::task::Context; + +use embassy_futures::select::{select, Either}; +use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::waitqueue::WakerRegistration; +use embassy_usb_driver::Driver; + +use super::{CdcNcmClass, Receiver, Sender}; + +pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> { + rx: [PacketBuf; N_RX], + tx: [PacketBuf; N_TX], + inner: MaybeUninit>, +} + +impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { + const NEW_PACKET: PacketBuf = PacketBuf::new(); + + pub const fn new() -> Self { + Self { + rx: [Self::NEW_PACKET; N_RX], + tx: [Self::NEW_PACKET; N_TX], + inner: MaybeUninit::uninit(), + } + } +} + +struct StateInner<'d, const MTU: usize> { + rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + link_state: Mutex>, +} + +/// State of the LinkState +struct LinkStateState { + state: LinkState, + waker: WakerRegistration, +} + +pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { + tx_usb: Sender<'d, D>, + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + rx_usb: Receiver<'d, D>, + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { + pub async fn run(mut self) -> ! { + let rx_fut = async move { + loop { + trace!("WAITING for connection"); + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = LinkState::Down; + s.waker.wake(); + }); + + self.rx_usb.wait_connection().await.unwrap(); + + trace!("Connected"); + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = LinkState::Up; + s.waker.wake(); + }); + + loop { + let p = self.rx_chan.send().await; + match self.rx_usb.read_packet(&mut p.buf).await { + Ok(n) => { + p.len = n; + self.rx_chan.send_done(); + } + Err(e) => { + warn!("error reading packet: {:?}", e); + break; + } + }; + } + } + }; + let tx_fut = async move { + loop { + let p = self.tx_chan.recv().await; + if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await { + warn!("Failed to TX packet: {:?}", e); + } + self.tx_chan.recv_done(); + } + }; + match select(rx_fut, tx_fut).await { + Either::First(x) => x, + Either::Second(x) => x, + } + } +} + +impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { + pub fn into_embassy_net_device( + self, + state: &'d mut State<'d, MTU, N_RX, N_TX>, + ethernet_address: [u8; 6], + ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { + let (tx_usb, rx_usb) = self.split(); + + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header + caps.medium = Medium::Ethernet; + + let state = state.inner.write(StateInner { + rx: zerocopy_channel::Channel::new(&mut state.rx[..]), + tx: zerocopy_channel::Channel::new(&mut state.tx[..]), + link_state: Mutex::new(RefCell::new(LinkStateState { + state: LinkState::Down, + waker: WakerRegistration::new(), + })), + }); + + let (rx_sender, rx_receiver) = state.rx.split(); + let (tx_sender, tx_receiver) = state.tx.split(); + + ( + Runner { + tx_usb, + tx_chan: tx_receiver, + rx_usb, + rx_chan: rx_sender, + link_state: &state.link_state, + }, + Device { + caps, + ethernet_address, + link_state: &state.link_state, + rx: rx_receiver, + tx: tx_sender, + }, + ) + } +} + +pub struct PacketBuf { + len: usize, + buf: [u8; MTU], +} + +impl PacketBuf { + pub const fn new() -> Self { + Self { len: 0, buf: [0; MTU] } + } +} + +pub struct Device<'d, const MTU: usize> { + rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, + caps: DeviceCapabilities, + ethernet_address: [u8; 6], +} + +impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { + type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; + type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { + Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) + } else { + None + } + } + + /// Construct a transmit token. + fn transmit(&mut self, cx: &mut Context) -> Option> { + if self.tx.poll_send(cx).is_ready() { + Some(TxToken { tx: self.tx.borrow() }) + } else { + None + } + } + + /// Get a description of device capabilities. + fn capabilities(&self) -> DeviceCapabilities { + self.caps.clone() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.ethernet_address + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.waker.register(cx.waker()); + s.state + }) + } +} + +pub struct RxToken<'a, const MTU: usize> { + rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.try_recv()); + let r = f(&mut pkt.buf[..pkt.len]); + self.rx.recv_done(); + r + } +} + +pub struct TxToken<'a, const MTU: usize> { + tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> { + fn consume(mut self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.try_send()); + let r = f(&mut pkt.buf[..len]); + pkt.len = len; + self.tx.send_done(); + r + } +} + +mod zerocopy_channel { + use core::cell::RefCell; + use core::future::poll_fn; + use core::marker::PhantomData; + use core::task::{Context, Poll}; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use embassy_sync::blocking_mutex::Mutex; + use embassy_sync::waitqueue::WakerRegistration; + + pub struct Channel<'a, M: RawMutex, T> { + buf: *mut T, + phantom: PhantomData<&'a mut T>, + state: Mutex>, + } + + impl<'a, M: RawMutex, T> Channel<'a, M, T> { + pub fn new(buf: &'a mut [T]) -> Self { + let len = buf.len(); + assert!(len != 0); + + Self { + buf: buf.as_mut_ptr(), + phantom: PhantomData, + state: Mutex::new(RefCell::new(State { + len, + front: 0, + back: 0, + full: false, + send_waker: WakerRegistration::new(), + recv_waker: WakerRegistration::new(), + })), + } + } + + pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { + (Sender { channel: self }, Receiver { channel: self }) + } + } + + pub struct Sender<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Sender<'a, M, T> { + pub fn borrow(&mut self) -> Sender<'_, M, T> { + Sender { channel: self.channel } + } + + pub fn try_send(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn send(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(i), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn send_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().push_done()) + } + } + pub struct Receiver<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Receiver<'a, M, T> { + pub fn borrow(&mut self) -> Receiver<'_, M, T> { + Receiver { channel: self.channel } + } + + pub fn try_recv(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn recv(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(i), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn recv_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().pop_done()) + } + } + + struct State { + len: usize, + + /// Front index. Always 0..=(N-1) + front: usize, + /// Back index. Always 0..=(N-1). + back: usize, + + /// Used to distinguish "empty" and "full" cases when `front == back`. + /// May only be `true` if `front == back`, always `false` otherwise. + full: bool, + + send_waker: WakerRegistration, + recv_waker: WakerRegistration, + } + + impl State { + fn increment(&self, i: usize) -> usize { + if i + 1 == self.len { + 0 + } else { + i + 1 + } + } + + fn is_full(&self) -> bool { + self.full + } + + fn is_empty(&self) -> bool { + self.front == self.back && !self.full + } + + fn push_index(&mut self) -> Option { + match self.is_full() { + true => None, + false => Some(self.back), + } + } + + fn push_done(&mut self) { + assert!(!self.is_full()); + self.back = self.increment(self.back); + if self.back == self.front { + self.full = true; + } + self.send_waker.wake(); + } + + fn pop_index(&mut self) -> Option { + match self.is_empty() { + true => None, + false => Some(self.front), + } + } + + fn pop_done(&mut self) { + assert!(!self.is_empty()); + self.front = self.increment(self.front); + self.full = false; + self.recv_waker.wake(); + } + } +} diff --git a/embassy-usb/src/class/cdc_ncm.rs b/embassy-usb/src/class/cdc_ncm/mod.rs similarity index 94% rename from embassy-usb/src/class/cdc_ncm.rs rename to embassy-usb/src/class/cdc_ncm/mod.rs index a39b87e9b..2ee47f68c 100644 --- a/embassy-usb/src/class/cdc_ncm.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -1,3 +1,18 @@ +/// CDC-NCM, aka Ethernet over USB. +/// +/// # Compatibility +/// +/// Windows: NOT supported in Windows 10. Supported in Windows 11. +/// +/// Linux: Well-supported since forever. +/// +/// Android: Support for CDC-NCM is spotty and varies across manufacturers. +/// +/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. +/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), +/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. +/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 +/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; @@ -6,6 +21,9 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; use crate::Builder; +#[cfg(feature = "embassy-net")] +pub mod embassy_net; + /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 8b95ac3a6..a980a505c 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -16,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 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 } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index de93a2b45..e5f704524 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -3,19 +3,16 @@ #![feature(type_alias_impl_trait)] use core::mem; -use core::sync::atomic::{AtomicBool, Ordering}; -use core::task::Waker; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; -use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; +use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; use embedded_io::asynch::Write; use static_cell::StaticCell; @@ -27,56 +24,25 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } +const MTU: usize = 1514; + #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } #[embassy_executor::task] -async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { - loop { - warn!("WAITING for connection"); - LINK_UP.store(false, Ordering::Relaxed); - - class.wait_connection().await.unwrap(); - - warn!("Connected"); - LINK_UP.store(true, Ordering::Relaxed); - - loop { - let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); - let n = match class.read_packet(&mut p[..]).await { - Ok(n) => n, - Err(e) => { - warn!("error reading packet: {:?}", e); - break; - } - }; - - let buf = p.slice(0..n); - if RX_CHANNEL.try_send(buf).is_err() { - warn!("Failed pushing rx'd packet to channel."); - } - } - } +async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { + class.run().await } #[embassy_executor::task] -async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { - loop { - let pkt = TX_CHANNEL.recv().await; - if let Err(e) = class.write_packet(&pkt[..]).await { - warn!("Failed to TX packet: {:?}", e); - } - } -} - -#[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { +async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } @@ -108,55 +74,32 @@ async fn main(spawner: Spawner) { config.device_sub_class = 0x02; config.device_protocol = 0x01; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 128], - serial_state: State<'static>, - } - let res: &mut Resources = singleton!(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 128], - serial_state: State::new(), - }); - // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], None, ); - // WARNINGS for Android ethernet tethering: - // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. - // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), - // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. - // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 - // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 - // Our MAC addr. let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (tx, rx) = class.split(); - unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); - unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -172,7 +115,6 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let device = Device { mac_addr: our_mac_addr }; let stack = &*singleton!(Stack::new( device, config, @@ -225,50 +167,3 @@ async fn main(spawner: Spawner) { } } } - -static TX_CHANNEL: Channel = Channel::new(); -static RX_CHANNEL: Channel = Channel::new(); -static LINK_UP: AtomicBool = AtomicBool::new(false); - -struct Device { - mac_addr: [u8; 6], -} - -impl embassy_net::Device for Device { - fn register_waker(&mut self, waker: &Waker) { - // loopy loopy wakey wakey - waker.wake_by_ref() - } - - fn link_state(&mut self) -> embassy_net::LinkState { - match LINK_UP.load(Ordering::Relaxed) { - true => embassy_net::LinkState::Up, - false => embassy_net::LinkState::Down, - } - } - - fn capabilities(&self) -> embassy_net::DeviceCapabilities { - let mut caps = embassy_net::DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = embassy_net::Medium::Ethernet; - caps - } - - fn is_transmit_ready(&mut self) -> bool { - true - } - - fn transmit(&mut self, pkt: PacketBuf) { - if TX_CHANNEL.try_send(pkt).is_err() { - warn!("TX failed") - } - } - - fn receive<'a>(&mut self) -> Option { - RX_CHANNEL.try_recv().ok() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index dfbc26426..31a08cfb6 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,7 +10,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } 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" } diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 1057fe7fd..d0aec874a 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -2,18 +2,14 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::sync::atomic::{AtomicBool, Ordering}; -use core::task::Waker; - use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; use embassy_rp::usb::Driver; use embassy_rp::{interrupt, peripherals}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; -use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; +use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; use embedded_io::asynch::Write; use static_cell::StaticCell; @@ -25,56 +21,25 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } +const MTU: usize = 1514; + #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } #[embassy_executor::task] -async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { - loop { - warn!("WAITING for connection"); - LINK_UP.store(false, Ordering::Relaxed); - - class.wait_connection().await.unwrap(); - - warn!("Connected"); - LINK_UP.store(true, Ordering::Relaxed); - - loop { - let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); - let n = match class.read_packet(&mut p[..]).await { - Ok(n) => n, - Err(e) => { - warn!("error reading packet: {:?}", e); - break; - } - }; - - let buf = p.slice(0..n); - if RX_CHANNEL.try_send(buf).is_err() { - warn!("Failed pushing rx'd packet to channel."); - } - } - } +async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { + class.run().await } #[embassy_executor::task] -async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { - loop { - let pkt = TX_CHANNEL.recv().await; - if let Err(e) = class.write_packet(&pkt[..]).await { - warn!("Failed to TX packet: {:?}", e); - } - } -} - -#[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { +async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } @@ -100,55 +65,32 @@ async fn main(spawner: Spawner) { config.device_sub_class = 0x02; config.device_protocol = 0x01; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 128], - serial_state: State<'static>, - } - let res: &mut Resources = singleton!(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 128], - serial_state: State::new(), - }); - // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], None, ); - // WARNINGS for Android ethernet tethering: - // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. - // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), - // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. - // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 - // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 - // Our MAC addr. let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (tx, rx) = class.split(); - unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); - unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -161,7 +103,6 @@ async fn main(spawner: Spawner) { let seed = 1234; // guaranteed random, chosen by a fair dice roll // Init network stack - let device = Device { mac_addr: our_mac_addr }; let stack = &*singleton!(Stack::new( device, config, @@ -214,50 +155,3 @@ async fn main(spawner: Spawner) { } } } - -static TX_CHANNEL: Channel = Channel::new(); -static RX_CHANNEL: Channel = Channel::new(); -static LINK_UP: AtomicBool = AtomicBool::new(false); - -struct Device { - mac_addr: [u8; 6], -} - -impl embassy_net::Device for Device { - fn register_waker(&mut self, waker: &Waker) { - // loopy loopy wakey wakey - waker.wake_by_ref() - } - - fn link_state(&mut self) -> embassy_net::LinkState { - match LINK_UP.load(Ordering::Relaxed) { - true => embassy_net::LinkState::Up, - false => embassy_net::LinkState::Down, - } - } - - fn capabilities(&self) -> embassy_net::DeviceCapabilities { - let mut caps = embassy_net::DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = embassy_net::Medium::Ethernet; - caps - } - - fn is_transmit_ready(&mut self) -> bool { - true - } - - fn transmit(&mut self, pkt: PacketBuf) { - if TX_CHANNEL.try_send(pkt).is_err() { - warn!("TX failed") - } - } - - fn receive<'a>(&mut self) -> Option { - RX_CHANNEL.try_recv().ok() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 73ad50787..bb8515312 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } 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" } usbd-hid = "0.6.0" diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 4f36d3f5a..b49329ea4 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -2,20 +2,16 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::sync::atomic::{AtomicBool, Ordering}; -use core::task::Waker; - use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; +use embassy_net::{Stack, StackResources}; use embassy_stm32::rcc::*; use embassy_stm32::rng::Rng; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; -use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; +use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; use embedded_io::asynch::Write; use rand_core::RngCore; @@ -28,56 +24,25 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } +const MTU: usize = 1514; + #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await } #[embassy_executor::task] -async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { - loop { - warn!("WAITING for connection"); - LINK_UP.store(false, Ordering::Relaxed); - - class.wait_connection().await.unwrap(); - - warn!("Connected"); - LINK_UP.store(true, Ordering::Relaxed); - - loop { - let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); - let n = match class.read_packet(&mut p[..]).await { - Ok(n) => n, - Err(e) => { - warn!("error reading packet: {:?}", e); - break; - } - }; - - let buf = p.slice(0..n); - if RX_CHANNEL.try_send(buf).is_err() { - warn!("Failed pushing rx'd packet to channel."); - } - } - } +async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { + class.run().await } #[embassy_executor::task] -async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { - loop { - let pkt = TX_CHANNEL.recv().await; - if let Err(e) = class.write_packet(&pkt[..]).await { - warn!("Failed to TX packet: {:?}", e); - } - } -} - -#[embassy_executor::task] -async fn net_task(stack: &'static Stack) -> ! { +async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } @@ -106,55 +71,32 @@ async fn main(spawner: Spawner) { config.device_sub_class = 0x02; config.device_protocol = 0x01; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 128], - serial_state: State<'static>, - } - let res: &mut Resources = singleton!(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 128], - serial_state: State::new(), - }); - // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], None, ); - // WARNINGS for Android ethernet tethering: - // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. - // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), - // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. - // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 - // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 - // Our MAC addr. let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (tx, rx) = class.split(); - unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); - unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -168,7 +110,6 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let device = Device { mac_addr: our_mac_addr }; let stack = &*singleton!(Stack::new( device, config, @@ -221,50 +162,3 @@ async fn main(spawner: Spawner) { } } } - -static TX_CHANNEL: Channel = Channel::new(); -static RX_CHANNEL: Channel = Channel::new(); -static LINK_UP: AtomicBool = AtomicBool::new(false); - -struct Device { - mac_addr: [u8; 6], -} - -impl embassy_net::Device for Device { - fn register_waker(&mut self, waker: &Waker) { - // loopy loopy wakey wakey - waker.wake_by_ref() - } - - fn link_state(&mut self) -> embassy_net::LinkState { - match LINK_UP.load(Ordering::Relaxed) { - true => embassy_net::LinkState::Up, - false => embassy_net::LinkState::Down, - } - } - - fn capabilities(&self) -> embassy_net::DeviceCapabilities { - let mut caps = embassy_net::DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = embassy_net::Medium::Ethernet; - caps - } - - fn is_transmit_ready(&mut self) -> bool { - true - } - - fn transmit(&mut self, pkt: PacketBuf) { - if TX_CHANNEL.try_send(pkt).is_err() { - warn!("TX failed") - } - } - - fn receive<'a>(&mut self) -> Option { - RX_CHANNEL.try_recv().ok() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} From 8f3065210927b6e92f6d727741189155b2eab91e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Dec 2022 03:18:45 +0100 Subject: [PATCH 0430/1575] stm32/eth_v1: update to new embassy-net trait, remove PeripheralMutex. --- embassy-stm32/src/eth/mod.rs | 121 +++++++- embassy-stm32/src/eth/v1/descriptors.rs | 21 -- embassy-stm32/src/eth/v1/mod.rs | 391 ++++++++++-------------- embassy-stm32/src/eth/v1/rx_desc.rs | 210 +++++-------- embassy-stm32/src/eth/v1/tx_desc.rs | 157 +++------- examples/stm32f7/src/bin/eth.rs | 50 +-- 6 files changed, 424 insertions(+), 526 deletions(-) delete mode 100644 embassy-stm32/src/eth/v1/descriptors.rs diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 76a3dfab4..fd1b48530 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -1,14 +1,131 @@ #![macro_use] +#![cfg_attr(not(feature = "embassy-net"), allow(unused))] -#[cfg(feature = "net")] #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] #[cfg_attr(eth_v2, path = "v2/mod.rs")] mod _version; pub mod generic_smi; -#[cfg(feature = "net")] pub use _version::*; +use embassy_sync::waitqueue::AtomicWaker; +#[allow(unused)] +const MTU: usize = 1514; +const TX_BUFFER_SIZE: usize = 1514; +const RX_BUFFER_SIZE: usize = 1536; + +#[repr(C, align(8))] +#[derive(Copy, Clone)] +pub(crate) struct Packet([u8; N]); + +pub struct PacketQueue { + tx_desc: [TDes; TX], + rx_desc: [RDes; RX], + tx_buf: [Packet; TX], + rx_buf: [Packet; RX], +} + +impl PacketQueue { + pub const fn new() -> Self { + const NEW_TDES: TDes = TDes::new(); + const NEW_RDES: RDes = RDes::new(); + Self { + tx_desc: [NEW_TDES; TX], + rx_desc: [NEW_RDES; RX], + tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], + rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], + } + } +} + +static WAKER: AtomicWaker = AtomicWaker::new(); + +#[cfg(feature = "embassy-net")] +mod embassy_net_impl { + use core::task::Context; + + use embassy_net::device::{Device, DeviceCapabilities, LinkState}; + + use super::*; + + impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> { + type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + WAKER.register(cx.waker()); + if self.rx.available().is_some() && self.tx.available().is_some() { + Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + } else { + None + } + } + + fn transmit(&mut self, cx: &mut Context) -> Option> { + WAKER.register(cx.waker()); + if self.tx.available().is_some() { + Some(TxToken { tx: &mut self.tx }) + } else { + None + } + } + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(self.tx.len()); + caps + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // TODO: wake cx.waker on link state change + cx.waker().wake_by_ref(); + if P::poll_link(self) { + LinkState::Up + } else { + LinkState::Down + } + } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } + } + + pub struct RxToken<'a, 'd> { + rx: &'a mut RDesRing<'d>, + } + + impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.available()); + let r = f(pkt); + self.rx.pop_packet(); + r + } + } + + pub struct TxToken<'a, 'd> { + tx: &'a mut TDesRing<'d>, + } + + impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.available()); + let r = f(&mut pkt[..len]); + self.tx.transmit(len); + r + } + } +} /// Station Management Interface (SMI) on an ethernet PHY /// /// # Safety diff --git a/embassy-stm32/src/eth/v1/descriptors.rs b/embassy-stm32/src/eth/v1/descriptors.rs deleted file mode 100644 index 25f21ce19..000000000 --- a/embassy-stm32/src/eth/v1/descriptors.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::eth::_version::rx_desc::RDesRing; -use crate::eth::_version::tx_desc::TDesRing; - -pub struct DescriptorRing { - pub(crate) tx: TDesRing, - pub(crate) rx: RDesRing, -} - -impl DescriptorRing { - pub const fn new() -> Self { - Self { - tx: TDesRing::new(), - rx: RDesRing::new(), - } - } - - pub fn init(&mut self) { - self.tx.init(); - self.rx.init(); - } -} diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 38629a932..de36d3da1 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -1,14 +1,17 @@ // The v1c ethernet driver was ported to embassy from the awesome stm32-eth project (https://github.com/stm32-rs/stm32-eth). -use core::marker::PhantomData; +mod rx_desc; +mod tx_desc; + use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; -use embassy_sync::waitqueue::AtomicWaker; +use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; +pub(crate) use self::rx_desc::{RDes, RDesRing}; +pub(crate) use self::tx_desc::{TDes, TDesRing}; +use super::*; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; use crate::gpio::{AnyPin, Speed}; #[cfg(eth_v1a)] @@ -18,29 +21,16 @@ use crate::pac::SYSCFG; use crate::pac::{ETH, RCC}; use crate::Peripheral; -mod descriptors; -mod rx_desc; -mod tx_desc; +pub struct Ethernet<'d, T: Instance, P: PHY> { + _peri: PeripheralRef<'d, T>, + pub(crate) tx: TDesRing<'d>, + pub(crate) rx: RDesRing<'d>, -use descriptors::DescriptorRing; -use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; - -use super::*; - -pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); -impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub const fn new() -> Self { - Self(StateStorage::new()) - } -} - -pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { - state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>, pins: [PeripheralRef<'d, AnyPin>; 9], _phy: P, clock_range: Cr, phy_addr: u8, - mac_addr: [u8; 6], + pub(crate) mac_addr: [u8; 6], } #[cfg(eth_v1a)] @@ -82,10 +72,10 @@ macro_rules! config_pins { }; } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { /// safety: the returned instance is not leak-safe - pub unsafe fn new( - state: &'d mut State<'d, T, TX, RX>, + pub fn new( + queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, interrupt: impl Peripheral

+ 'd, ref_clk: impl Peripheral

> + 'd, @@ -101,134 +91,131 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, mac_addr: [u8; 6], phy_addr: u8, ) -> Self { - into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - // Enable the necessary Clocks - // NOTE(unsafe) We have exclusive access to the registers - #[cfg(eth_v1a)] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_afioen(true)); + unsafe { + // Enable the necessary Clocks + // NOTE(unsafe) We have exclusive access to the registers + #[cfg(eth_v1a)] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_afioen(true)); - // Select RMII (Reduced Media Independent Interface) - // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); + // Select RMII (Reduced Media Independent Interface) + // Must be done prior to enabling peripheral clock + AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); - RCC.ahbenr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - }); - - #[cfg(any(eth_v1b, eth_v1c))] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); + RCC.ahbenr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); }); - // RMII (Reduced Media Independent Interface) - SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); - }); + #[cfg(any(eth_v1b, eth_v1c))] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_syscfgen(true)); + RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); - #[cfg(eth_v1a)] - { - config_in_pins!(ref_clk, rx_d0, rx_d1); - config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); - } + // RMII (Reduced Media Independent Interface) + SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); + }); - #[cfg(any(eth_v1b, eth_v1c))] - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - - // NOTE(unsafe) We are ourselves not leak-safe. - let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri)); - - // NOTE(unsafe) We have exclusive access to the registers - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - - // Reset and wait - dma.dmabmr().modify(|w| w.set_sr(true)); - while dma.dmabmr().read().sr() {} - - mac.maccr().modify(|w| { - w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times - w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping - w.set_fes(Fes::FES100); // fast ethernet speed - w.set_dm(Dm::FULLDUPLEX); // full duplex - // TODO: Carrier sense ? ECRSFD - }); - - // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, - // so the LR write must happen after the HR write. - mac.maca0hr() - .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); - mac.maca0lr().write(|w| { - w.set_maca0l( - u32::from(mac_addr[0]) - | (u32::from(mac_addr[1]) << 8) - | (u32::from(mac_addr[2]) << 16) - | (u32::from(mac_addr[3]) << 24), - ) - }); - - // pause time - mac.macfcr().modify(|w| w.set_pt(0x100)); - - // Transfer and Forward, Receive and Forward - dma.dmaomr().modify(|w| { - w.set_tsf(Tsf::STOREFORWARD); - w.set_rsf(Rsf::STOREFORWARD); - }); - - dma.dmabmr().modify(|w| { - w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? - }); - - // TODO MTU size setting not found for v1 ethernet, check if correct - - // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called - let hclk = crate::rcc::get_freqs().ahb1; - let hclk_mhz = hclk.0 / 1_000_000; - - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), - 25..=34 => Cr::CR_20_35, // Divide by 16 - 35..=59 => Cr::CR_35_60, // Divide by 26 - 60..=99 => Cr::CR_60_100, // Divide by 42 - 100..=149 => Cr::CR_100_150, // Divide by 62 - 150..=216 => Cr::CR_150_168, // Divide by 102 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + #[cfg(eth_v1a)] + { + config_in_pins!(ref_clk, rx_d0, rx_d1); + config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); } - }; - let pins = [ - ref_clk.map_into(), - mdio.map_into(), - mdc.map_into(), - crs.map_into(), - rx_d0.map_into(), - rx_d1.map_into(), - tx_d0.map_into(), - tx_d1.map_into(), - tx_en.map_into(), - ]; + #[cfg(any(eth_v1b, eth_v1c))] + config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - let mut this = Self { - state, - pins, - _phy: phy, - clock_range, - phy_addr, - mac_addr, - }; + // NOTE(unsafe) We have exclusive access to the registers + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); - this.state.with(|s| { - s.desc_ring.init(); + // Reset and wait + dma.dmabmr().modify(|w| w.set_sr(true)); + while dma.dmabmr().read().sr() {} + + mac.maccr().modify(|w| { + w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times + w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping + w.set_fes(Fes::FES100); // fast ethernet speed + w.set_dm(Dm::FULLDUPLEX); // full duplex + // TODO: Carrier sense ? ECRSFD + }); + + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, + // so the LR write must happen after the HR write. + mac.maca0hr() + .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); + mac.maca0lr().write(|w| { + w.set_maca0l( + u32::from(mac_addr[0]) + | (u32::from(mac_addr[1]) << 8) + | (u32::from(mac_addr[2]) << 16) + | (u32::from(mac_addr[3]) << 24), + ) + }); + + // pause time + mac.macfcr().modify(|w| w.set_pt(0x100)); + + // Transfer and Forward, Receive and Forward + dma.dmaomr().modify(|w| { + w.set_tsf(Tsf::STOREFORWARD); + w.set_rsf(Rsf::STOREFORWARD); + }); + + dma.dmabmr().modify(|w| { + w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? + }); + + // TODO MTU size setting not found for v1 ethernet, check if correct + + // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called + let hclk = crate::rcc::get_freqs().ahb1; + let hclk_mhz = hclk.0 / 1_000_000; + + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), + 25..=34 => Cr::CR_20_35, // Divide by 16 + 35..=59 => Cr::CR_35_60, // Divide by 26 + 60..=99 => Cr::CR_60_100, // Divide by 42 + 100..=149 => Cr::CR_100_150, // Divide by 62 + 150..=216 => Cr::CR_150_168, // Divide by 102 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; + + let pins = [ + ref_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + crs.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_en.map_into(), + ]; + + let mut this = Self { + _peri: peri, + pins, + _phy: phy, + clock_range, + phy_addr, + mac_addr, + tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), + rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), + }; fence(Ordering::SeqCst); @@ -245,23 +232,45 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, w.set_sr(DmaomrSr::STARTED); // start receiving channel }); + this.rx.demand_poll(); + // Enable interrupts dma.dmaier().modify(|w| { w.set_nise(true); w.set_rie(true); w.set_tie(true); }); - }); - P::phy_reset(&mut this); - P::phy_init(&mut this); - this + P::phy_reset(&mut this); + P::phy_init(&mut this); + + interrupt.set_handler(Self::on_interrupt); + interrupt.enable(); + + this + } + } + + fn on_interrupt(_cx: *mut ()) { + WAKER.wake(); + + // TODO: Check and clear more flags + unsafe { + let dma = ETH.ethernet_dma(); + + dma.dmasr().modify(|w| { + w.set_ts(true); + w.set_rs(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmasr().read(); + dma.dmasr().read(); + } } } -unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement - for Ethernet<'d, T, P, TX, RX> -{ +unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { fn smi_read(&mut self, reg: u8) -> u16 { // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` unsafe { @@ -297,44 +306,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa } } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> { - fn is_transmit_ready(&mut self) -> bool { - self.state.with(|s| s.desc_ring.tx.available()) - } - - fn transmit(&mut self, pkt: PacketBuf) { - self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt))); - } - - fn receive(&mut self) -> Option { - self.state.with(|s| s.desc_ring.rx.pop_packet()) - } - - fn register_waker(&mut self, waker: &Waker) { - WAKER.register(waker); - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(TX.min(RX)); - caps - } - - fn link_state(&mut self) -> LinkState { - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} - -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers unsafe { @@ -361,46 +333,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne }) } } - -//---------------------------------------------------------------------- - -struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { - _peri: PhantomData<&'d mut T>, - desc_ring: DescriptorRing, -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { - pub fn new(_peri: impl Peripheral

+ 'd) -> Self { - Self { - _peri: PhantomData, - desc_ring: DescriptorRing::new(), - } - } -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { - type Interrupt = crate::interrupt::ETH; - - fn on_interrupt(&mut self) { - unwrap!(self.desc_ring.tx.on_interrupt()); - self.desc_ring.rx.on_interrupt(); - - WAKER.wake(); - - // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmasr().modify(|w| { - w.set_ts(true); - w.set_rs(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmasr().read(); - dma.dmasr().read(); - } - } -} - -static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index d482590a1..8b8566d92 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs @@ -1,9 +1,9 @@ use core::sync::atomic::{compiler_fence, fence, Ordering}; -use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; -use stm32_metapac::eth::vals::{DmaomrSr, Rpd, Rps}; +use stm32_metapac::eth::vals::{Rpd, Rps}; use vcell::VolatileCell; +use crate::eth::RX_BUFFER_SIZE; use crate::pac::ETH; mod rx_consts { @@ -28,6 +28,8 @@ mod rx_consts { use rx_consts::*; +use super::Packet; + /// Receive Descriptor representation /// /// * rdes0: OWN and Status @@ -35,7 +37,7 @@ use rx_consts::*; /// * rdes2: data buffer address /// * rdes3: next descriptor address #[repr(C)] -struct RDes { +pub(crate) struct RDes { rdes0: VolatileCell, rdes1: VolatileCell, rdes2: VolatileCell, @@ -54,7 +56,7 @@ impl RDes { /// Return true if this RDes is acceptable to us #[inline(always)] - pub fn valid(&self) -> bool { + fn valid(&self) -> bool { // Write-back descriptor is valid if: // // Contains first buffer of packet AND contains last buf of @@ -64,15 +66,16 @@ impl RDes { /// Return true if this RDes is not currently owned by the DMA #[inline(always)] - pub fn available(&self) -> bool { + fn available(&self) -> bool { self.rdes0.get() & RXDESC_0_OWN == 0 // Owned by us } /// Configures the reception buffer address and length and passed descriptor ownership to the DMA #[inline(always)] - pub fn set_ready(&mut self, buf_addr: u32, buf_len: usize) { - self.rdes1.set(self.rdes1.get() | (buf_len as u32) & RXDESC_1_RBS_MASK); - self.rdes2.set(buf_addr); + fn set_ready(&self, buf: *mut u8) { + self.rdes1 + .set(self.rdes1.get() | (RX_BUFFER_SIZE as u32) & RXDESC_1_RBS_MASK); + self.rdes2.set(buf as u32); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); @@ -88,12 +91,12 @@ impl RDes { // points to next descriptor (RCH) #[inline(always)] - fn set_buffer2(&mut self, buffer: *const u8) { + fn set_buffer2(&self, buffer: *const u8) { self.rdes3.set(buffer as u32); } #[inline(always)] - fn set_end_of_ring(&mut self) { + fn set_end_of_ring(&self) { self.rdes1.set(self.rdes1.get() | RXDESC_1_RER); } @@ -102,7 +105,7 @@ impl RDes { ((self.rdes0.get() >> RXDESC_0_FL_SHIFT) & RXDESC_0_FL_MASK) as usize } - pub fn setup(&mut self, next: Option<&Self>) { + fn setup(&self, next: Option<&Self>, buf: *mut u8) { // Defer this initialization to this function, so we can have `RingEntry` on bss. self.rdes1.set(self.rdes1.get() | RXDESC_1_RCH); @@ -113,8 +116,11 @@ impl RDes { self.set_end_of_ring(); } } + + self.set_ready(buf); } } + /// Running state of the `RxRing` #[derive(PartialEq, Eq, Debug)] pub enum RunningState { @@ -123,116 +129,42 @@ pub enum RunningState { Running, } -impl RunningState { - /// whether self equals to `RunningState::Running` - pub fn is_running(&self) -> bool { - *self == RunningState::Running - } -} - /// Rx ring of descriptors and packets -/// -/// This ring has three major locations that work in lock-step. The DMA will never write to the tail -/// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1 -/// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite -/// a packet still to be passed to the application. -/// -/// nt can't pass r (no alloc) -/// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+ -/// ^ ^t ^t ^ ^t ^ -/// |r |r |r -/// |nt |nt |nt -/// -/// -/// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+ -/// ^ ^t ^ ^t ^ ^ ^ ^t -/// |r | |r | | |r -/// |nt |nt |nt -/// -pub(crate) struct RDesRing { - descriptors: [RDes; N], - buffers: [Option; N], - read_index: usize, - next_tail_index: usize, +pub(crate) struct RDesRing<'a> { + descriptors: &'a mut [RDes], + buffers: &'a mut [Packet], + index: usize, } -impl RDesRing { - pub const fn new() -> Self { - const RDES: RDes = RDes::new(); - const BUFFERS: Option = None; +impl<'a> RDesRing<'a> { + pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 1); + assert!(descriptors.len() == buffers.len()); - Self { - descriptors: [RDES; N], - buffers: [BUFFERS; N], - read_index: 0, - next_tail_index: 0, - } - } - - pub(crate) fn init(&mut self) { - assert!(N > 1); - let mut last_index = 0; - for (index, buf) in self.buffers.iter_mut().enumerate() { - let pkt = match PacketBox::new(Packet::new()) { - Some(p) => p, - None => { - if index == 0 { - panic!("Could not allocate at least one buffer for Ethernet receiving"); - } else { - break; - } - } - }; - self.descriptors[index].set_ready(pkt.as_ptr() as u32, pkt.len()); - *buf = Some(pkt); - last_index = index; - } - self.next_tail_index = (last_index + 1) % N; - - // not sure if this is supposed to span all of the descriptor or just those that contain buffers - { - let mut previous: Option<&mut RDes> = None; - for entry in self.descriptors.iter_mut() { - if let Some(prev) = &mut previous { - prev.setup(Some(entry)); - } - previous = Some(entry); - } - - if let Some(entry) = &mut previous { - entry.setup(None); - } + for (i, entry) in descriptors.iter().enumerate() { + entry.setup(descriptors.get(i + 1), buffers[i].0.as_mut_ptr()); } - // Register txdescriptor start + // Register rx descriptor start // NOTE (unsafe) Used for atomic writes unsafe { ETH.ethernet_dma() .dmardlar() - .write(|w| w.0 = &self.descriptors as *const _ as u32); + .write(|w| w.0 = descriptors.as_ptr() as u32); }; // We already have fences in `set_owned`, which is called in `setup` - // Start receive - unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_sr(DmaomrSr::STARTED)) }; - - self.demand_poll(); + Self { + descriptors, + buffers, + index: 0, + } } - fn demand_poll(&self) { + pub(crate) fn demand_poll(&self) { unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) }; } - pub(crate) fn on_interrupt(&mut self) { - // XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it - // would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt - // which should try to pop a packet... - } - /// Get current `RunningState` fn running_state(&self) -> RunningState { match unsafe { ETH.ethernet_dma().dmasr().read().rps() } { @@ -252,52 +184,52 @@ impl RDesRing { } } - pub(crate) fn pop_packet(&mut self) -> Option { - if !self.running_state().is_running() { + /// Get a received packet if any, or None. + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + if self.running_state() != RunningState::Running { self.demand_poll(); } + // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the // buffer (I think .-.) fence(Ordering::SeqCst); - let read_available = self.descriptors[self.read_index].available(); - let tail_index = (self.next_tail_index + N - 1) % N; - - let pkt = if read_available && self.read_index != tail_index { - let pkt = self.buffers[self.read_index].take(); - let len = self.descriptors[self.read_index].packet_len(); - - assert!(pkt.is_some()); - let valid = self.descriptors[self.read_index].valid(); - - self.read_index = (self.read_index + 1) % N; - if valid { - pkt.map(|p| p.slice(0..len)) - } else { - None + // We might have to process many packets, in case some have been rx'd but are invalid. + loop { + let descriptor = &mut self.descriptors[self.index]; + if !descriptor.available() { + return None; } - } else { - None - }; - // Try to advance the tail_index - if self.next_tail_index != self.read_index { - match PacketBox::new(Packet::new()) { - Some(b) => { - let addr = b.as_ptr() as u32; - let buffer_len = b.len(); - self.buffers[self.next_tail_index].replace(b); - self.descriptors[self.next_tail_index].set_ready(addr, buffer_len); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - self.next_tail_index = (self.next_tail_index + 1) % N; - } - None => {} + // If packet is invalid, pop it and try again. + if !descriptor.valid() { + warn!("invalid packet: {:08x}", descriptor.rdes0.get()); + self.pop_packet(); + continue; } + + break; + } + + let descriptor = &mut self.descriptors[self.index]; + let len = descriptor.packet_len(); + return Some(&mut self.buffers[self.index].0[..len]); + } + + /// Pop the packet previously returned by `available`. + pub(crate) fn pop_packet(&mut self) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); + + self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); + + self.demand_poll(); + + // Increment index. + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 } - pkt } } diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index f2889b550..0e63c5443 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs @@ -1,20 +1,10 @@ use core::sync::atomic::{compiler_fence, fence, Ordering}; -use embassy_net::PacketBuf; -use stm32_metapac::eth::vals::St; use vcell::VolatileCell; +use crate::eth::TX_BUFFER_SIZE; use crate::pac::ETH; -#[non_exhaustive] -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - NoBufferAvailable, - // TODO: Break down this error into several others - TransmissionError, -} - /// Transmit and Receive Descriptor fields #[allow(dead_code)] mod tx_consts { @@ -37,6 +27,8 @@ mod tx_consts { } use tx_consts::*; +use super::Packet; + /// Transmit Descriptor representation /// /// * tdes0: control @@ -44,7 +36,7 @@ use tx_consts::*; /// * tdes2: data buffer address /// * tdes3: next descriptor address #[repr(C)] -struct TDes { +pub(crate) struct TDes { tdes0: VolatileCell, tdes1: VolatileCell, tdes2: VolatileCell, @@ -62,7 +54,7 @@ impl TDes { } /// Return true if this TDes is not currently owned by the DMA - pub fn available(&self) -> bool { + fn available(&self) -> bool { (self.tdes0.get() & TXDESC_0_OWN) == 0 } @@ -79,26 +71,26 @@ impl TDes { fence(Ordering::SeqCst); } - fn set_buffer1(&mut self, buffer: *const u8) { + fn set_buffer1(&self, buffer: *const u8) { self.tdes2.set(buffer as u32); } - fn set_buffer1_len(&mut self, len: usize) { + fn set_buffer1_len(&self, len: usize) { self.tdes1 .set((self.tdes1.get() & !TXDESC_1_TBS_MASK) | ((len as u32) << TXDESC_1_TBS_SHIFT)); } // points to next descriptor (RCH) - fn set_buffer2(&mut self, buffer: *const u8) { + fn set_buffer2(&self, buffer: *const u8) { self.tdes3.set(buffer as u32); } - fn set_end_of_ring(&mut self) { + fn set_end_of_ring(&self) { self.tdes0.set(self.tdes0.get() | TXDESC_0_TER); } // set up as a part fo the ring buffer - configures the tdes - pub fn setup(&mut self, next: Option<&Self>) { + fn setup(&self, next: Option<&Self>) { // Defer this initialization to this function, so we can have `RingEntry` on bss. self.tdes0.set(TXDESC_0_TCH | TXDESC_0_IOC | TXDESC_0_FS | TXDESC_0_LS); match next { @@ -111,85 +103,58 @@ impl TDes { } } -pub(crate) struct TDesRing { - descriptors: [TDes; N], - buffers: [Option; N], - next_entry: usize, +pub(crate) struct TDesRing<'a> { + descriptors: &'a mut [TDes], + buffers: &'a mut [Packet], + index: usize, } -impl TDesRing { - pub const fn new() -> Self { - const TDES: TDes = TDes::new(); - const BUFFERS: Option = None; - - Self { - descriptors: [TDES; N], - buffers: [BUFFERS; N], - next_entry: 0, - } - } - +impl<'a> TDesRing<'a> { /// Initialise this TDesRing. Assume TDesRing is corrupt - /// - /// The current memory address of the buffers inside this TDesRing - /// will be stored in the descriptors, so ensure the TDesRing is - /// not moved after initialisation. - pub(crate) fn init(&mut self) { - assert!(N > 0); + pub(crate) fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 0); + assert!(descriptors.len() == buffers.len()); - { - let mut previous: Option<&mut TDes> = None; - for entry in self.descriptors.iter_mut() { - if let Some(prev) = &mut previous { - prev.setup(Some(entry)); - } - previous = Some(entry); - } - - if let Some(entry) = &mut previous { - entry.setup(None); - } + for (i, entry) in descriptors.iter().enumerate() { + entry.setup(descriptors.get(i + 1)); } - self.next_entry = 0; // Register txdescriptor start // NOTE (unsafe) Used for atomic writes unsafe { ETH.ethernet_dma() .dmatdlar() - .write(|w| w.0 = &self.descriptors as *const _ as u32); + .write(|w| w.0 = descriptors.as_ptr() as u32); } - // "Preceding reads and writes cannot be moved past subsequent writes." - #[cfg(feature = "fence")] - fence(Ordering::Release); - - // We don't need a compiler fence here because all interactions with `Descriptor` are - // volatiles - - // Start transmission - unsafe { ETH.ethernet_dma().dmaomr().modify(|w| w.set_st(St::STARTED)) }; - } - - /// Return true if a TDes is available for use - pub(crate) fn available(&self) -> bool { - self.descriptors[self.next_entry].available() - } - - pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { - if !self.available() { - return Err(Error::NoBufferAvailable); + Self { + descriptors, + buffers, + index: 0, } + } - let descriptor = &mut self.descriptors[self.next_entry]; + pub(crate) fn len(&self) -> usize { + self.descriptors.len() + } - let pkt_len = pkt.len(); - let address = pkt.as_ptr() as *const u8; + /// Return the next available packet buffer for transmitting, or None + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + let descriptor = &mut self.descriptors[self.index]; + if descriptor.available() { + Some(&mut self.buffers[self.index].0) + } else { + None + } + } - descriptor.set_buffer1(address); - descriptor.set_buffer1_len(pkt_len); + /// Transmit the packet written in a buffer returned by `available`. + pub(crate) fn transmit(&mut self, len: usize) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); - self.buffers[self.next_entry].replace(pkt); + descriptor.set_buffer1(self.buffers[self.index].0.as_ptr()); + descriptor.set_buffer1_len(len); descriptor.set_owned(); @@ -198,36 +163,12 @@ impl TDesRing { // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); - // Move the tail pointer (TPR) to the next descriptor - self.next_entry = (self.next_entry + 1) % N; - + // Move the index to the next descriptor + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 + } // Request the DMA engine to poll the latest tx descriptor unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) } - Ok(()) - } - - pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { - let previous = (self.next_entry + N - 1) % N; - let td = &self.descriptors[previous]; - - // DMB to ensure that we are reading an updated value, probably not needed at the hardware - // level, but this is also a hint to the compiler that we're syncing on the buffer. - fence(Ordering::SeqCst); - - let tdes0 = td.tdes0.get(); - - if tdes0 & TXDESC_0_OWN != 0 { - // Transmission isn't done yet, probably a receive interrupt that fired this - return Ok(()); - } - - // Release the buffer - self.buffers[previous].take(); - - if tdes0 & TXDESC_0_ES != 0 { - Err(Error::TransmissionError) - } else { - Ok(()) - } } } diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5202edf62..224cc202b 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; -use embassy_stm32::eth::{Ethernet, State}; +use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; @@ -22,11 +22,12 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } -type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; +type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { @@ -50,25 +51,23 @@ async fn main(spawner: Spawner) -> ! { let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - let device = unsafe { - Ethernet::new( - singleton!(State::new()), - p.ETH, - eth_int, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, - GenericSMI, - mac_addr, - 0, - ) - }; + let device = Ethernet::new( + singleton!(PacketQueue::<16, 16>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { @@ -91,8 +90,8 @@ async fn main(spawner: Spawner) -> ! { info!("Network task initialized"); // Then we can use it! - let mut rx_buffer = [0; 1024]; - let mut tx_buffer = [0; 1024]; + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); @@ -107,8 +106,9 @@ async fn main(spawner: Spawner) -> ! { continue; } info!("connected!"); + let buf = [0; 1024]; loop { - let r = socket.write_all(b"Hello\n").await; + let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); return; From 3005ee0178af46ef8a2b7f8b02f4aea69ed9202b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Dec 2022 02:04:33 +0100 Subject: [PATCH 0431/1575] stm32/eth_v2: update to new embassy-net trait, remove PeripheralMutex. --- embassy-stm32/src/eth/v2/descriptors.rs | 333 +++++++--------------- embassy-stm32/src/eth/v2/mod.rs | 357 ++++++++++-------------- examples/stm32h7/src/bin/eth.rs | 43 ++- examples/stm32h7/src/bin/eth_client.rs | 43 ++- 4 files changed, 289 insertions(+), 487 deletions(-) diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index c6c06a9ce..2426596fb 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs @@ -1,19 +1,10 @@ use core::sync::atomic::{fence, Ordering}; -use embassy_net::{Packet, PacketBox, PacketBoxExt, PacketBuf}; use vcell::VolatileCell; +use crate::eth::{Packet, RX_BUFFER_SIZE, TX_BUFFER_SIZE}; use crate::pac::ETH; -#[non_exhaustive] -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - NoBufferAvailable, - // TODO: Break down this error into several others - TransmissionError, -} - /// Transmit and Receive Descriptor fields #[allow(dead_code)] mod emac_consts { @@ -41,7 +32,7 @@ use emac_consts::*; /// * tdes2: buffer lengths /// * tdes3: control and payload/frame length #[repr(C)] -struct TDes { +pub(crate) struct TDes { tdes0: VolatileCell, tdes1: VolatileCell, tdes2: VolatileCell, @@ -59,41 +50,26 @@ impl TDes { } /// Return true if this TDes is not currently owned by the DMA - pub fn available(&self) -> bool { + fn available(&self) -> bool { self.tdes3.get() & EMAC_DES3_OWN == 0 } } -pub(crate) struct TDesRing { - td: [TDes; N], - buffers: [Option; N], - tdidx: usize, +pub(crate) struct TDesRing<'a> { + descriptors: &'a mut [TDes], + buffers: &'a mut [Packet], + index: usize, } -impl TDesRing { - pub const fn new() -> Self { - const TDES: TDes = TDes::new(); - const BUFFERS: Option = None; +impl<'a> TDesRing<'a> { + /// Initialise this TDesRing. Assume TDesRing is corrupt. + pub fn new(descriptors: &'a mut [TDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 0); + assert!(descriptors.len() == buffers.len()); - Self { - td: [TDES; N], - buffers: [BUFFERS; N], - tdidx: 0, - } - } - - /// Initialise this TDesRing. Assume TDesRing is corrupt - /// - /// The current memory address of the buffers inside this TDesRing - /// will be stored in the descriptors, so ensure the TDesRing is - /// not moved after initialisation. - pub(crate) fn init(&mut self) { - assert!(N > 0); - - for td in self.td.iter_mut() { + for td in descriptors.iter_mut() { *td = TDes::new(); } - self.tdidx = 0; // Initialize the pointers in the DMA engine. (There will be a memory barrier later // before the DMA engine is enabled.) @@ -101,80 +77,60 @@ impl TDesRing { unsafe { let dma = ETH.ethernet_dma(); - dma.dmactx_dlar().write(|w| w.0 = &self.td as *const _ as u32); - dma.dmactx_rlr().write(|w| w.set_tdrl((N as u16) - 1)); - dma.dmactx_dtpr().write(|w| w.0 = &self.td[0] as *const _ as u32); + dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); + dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1)); + dma.dmactx_dtpr().write(|w| w.0 = 0); + } + + Self { + descriptors, + buffers, + index: 0, } } - /// Return true if a TDes is available for use - pub(crate) fn available(&self) -> bool { - self.td[self.tdidx].available() + pub(crate) fn len(&self) -> usize { + self.descriptors.len() } - pub(crate) fn transmit(&mut self, pkt: PacketBuf) -> Result<(), Error> { - if !self.available() { - return Err(Error::NoBufferAvailable); + /// Return the next available packet buffer for transmitting, or None + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { + let d = &mut self.descriptors[self.index]; + if d.available() { + Some(&mut self.buffers[self.index].0) + } else { + None } - let x = self.tdidx; - let td = &mut self.td[x]; + } - let pkt_len = pkt.len(); - assert!(pkt_len as u32 <= EMAC_TDES2_B1L); - let address = pkt.as_ptr() as u32; + /// Transmit the packet written in a buffer returned by `available`. + pub(crate) fn transmit(&mut self, len: usize) { + let td = &mut self.descriptors[self.index]; + assert!(td.available()); + assert!(len as u32 <= EMAC_TDES2_B1L); // Read format - td.tdes0.set(address); - td.tdes2.set(pkt_len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC); + td.tdes0.set(self.buffers[self.index].0.as_ptr() as u32); + td.tdes2.set(len as u32 & EMAC_TDES2_B1L | EMAC_TDES2_IOC); // FD: Contains first buffer of packet // LD: Contains last buffer of packet // Give the DMA engine ownership td.tdes3.set(EMAC_DES3_FD | EMAC_DES3_LD | EMAC_DES3_OWN); - self.buffers[x].replace(pkt); - // Ensure changes to the descriptor are committed before DMA engine sees tail pointer store. // This will generate an DMB instruction. // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::Release); - // Move the tail pointer (TPR) to the next descriptor - let x = (x + 1) % N; + self.index = self.index + 1; + if self.index == self.descriptors.len() { + self.index = 0; + } + + // signal DMA it can try again. // NOTE(unsafe) Atomic write - unsafe { - ETH.ethernet_dma() - .dmactx_dtpr() - .write(|w| w.0 = &self.td[x] as *const _ as u32); - } - self.tdidx = x; - Ok(()) - } - - pub(crate) fn on_interrupt(&mut self) -> Result<(), Error> { - let previous = (self.tdidx + N - 1) % N; - let td = &self.td[previous]; - - // DMB to ensure that we are reading an updated value, probably not needed at the hardware - // level, but this is also a hint to the compiler that we're syncing on the buffer. - fence(Ordering::SeqCst); - - let tdes3 = td.tdes3.get(); - - if tdes3 & EMAC_DES3_OWN != 0 { - // Transmission isn't done yet, probably a receive interrupt that fired this - return Ok(()); - } - assert!(tdes3 & EMAC_DES3_CTXT == 0); - - // Release the buffer - self.buffers[previous].take(); - - if tdes3 & EMAC_DES3_ES != 0 { - Err(Error::TransmissionError) - } else { - Ok(()) - } + unsafe { ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) } } } @@ -185,7 +141,7 @@ impl TDesRing { /// * rdes2: /// * rdes3: OWN and Status #[repr(C)] -struct RDes { +pub(crate) struct RDes { rdes0: VolatileCell, rdes1: VolatileCell, rdes2: VolatileCell, @@ -204,7 +160,7 @@ impl RDes { /// Return true if this RDes is acceptable to us #[inline(always)] - pub fn valid(&self) -> bool { + fn valid(&self) -> bool { // Write-back descriptor is valid if: // // Contains first buffer of packet AND contains last buf of @@ -215,177 +171,96 @@ impl RDes { /// Return true if this RDes is not currently owned by the DMA #[inline(always)] - pub fn available(&self) -> bool { + fn available(&self) -> bool { self.rdes3.get() & EMAC_DES3_OWN == 0 // Owned by us } #[inline(always)] - pub fn set_ready(&mut self, buf_addr: u32) { - self.rdes0.set(buf_addr); + fn set_ready(&mut self, buf: *mut u8) { + self.rdes0.set(buf as u32); self.rdes3.set(EMAC_RDES3_BUF1V | EMAC_RDES3_IOC | EMAC_DES3_OWN); } } /// Rx ring of descriptors and packets -/// -/// This ring has three major locations that work in lock-step. The DMA will never write to the tail -/// index, so the `read_index` must never pass the tail index. The `next_tail_index` is always 1 -/// slot ahead of the real tail index, and it must never pass the `read_index` or it could overwrite -/// a packet still to be passed to the application. -/// -/// nt can't pass r (no alloc) -/// +---+---+---+---+ Read ok +---+---+---+---+ No Read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation ok +---+---+---+---+ +---+---+---+---+ -/// ^ ^t ^t ^ ^t ^ -/// |r |r |r -/// |nt |nt |nt -/// -/// -/// +---+---+---+---+ Read ok +---+---+---+---+ Can't read +---+---+---+---+ -/// | | | | | ------------> | | | | | ------------> | | | | | -/// +---+---+---+---+ Allocation fail +---+---+---+---+ Allocation ok +---+---+---+---+ -/// ^ ^t ^ ^t ^ ^ ^ ^t -/// |r | |r | | |r -/// |nt |nt |nt -/// -pub(crate) struct RDesRing { - rd: [RDes; N], - buffers: [Option; N], - read_idx: usize, - next_tail_idx: usize, +pub(crate) struct RDesRing<'a> { + descriptors: &'a mut [RDes], + buffers: &'a mut [Packet], + index: usize, } -impl RDesRing { - pub const fn new() -> Self { - const RDES: RDes = RDes::new(); - const BUFFERS: Option = None; +impl<'a> RDesRing<'a> { + pub(crate) fn new(descriptors: &'a mut [RDes], buffers: &'a mut [Packet]) -> Self { + assert!(descriptors.len() > 1); + assert!(descriptors.len() == buffers.len()); - Self { - rd: [RDES; N], - buffers: [BUFFERS; N], - read_idx: 0, - next_tail_idx: 0, - } - } - - pub(crate) fn init(&mut self) { - assert!(N > 1); - - for desc in self.rd.iter_mut() { + for (i, desc) in descriptors.iter_mut().enumerate() { *desc = RDes::new(); + desc.set_ready(buffers[i].0.as_mut_ptr()); } - let mut last_index = 0; - for (index, buf) in self.buffers.iter_mut().enumerate() { - let pkt = match PacketBox::new(Packet::new()) { - Some(p) => p, - None => { - if index == 0 { - panic!("Could not allocate at least one buffer for Ethernet receiving"); - } else { - break; - } - } - }; - let addr = pkt.as_ptr() as u32; - *buf = Some(pkt); - self.rd[index].set_ready(addr); - last_index = index; - } - self.next_tail_idx = (last_index + 1) % N; - unsafe { let dma = ETH.ethernet_dma(); - dma.dmacrx_dlar().write(|w| w.0 = self.rd.as_ptr() as u32); - dma.dmacrx_rlr().write(|w| w.set_rdrl((N as u16) - 1)); + dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); + dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1)); + dma.dmacrx_dtpr().write(|w| w.0 = 0); + } - // We manage to allocate all buffers, set the index to the last one, that means - // that the DMA won't consider the last one as ready, because it (unfortunately) - // stops at the tail ptr and wraps at the end of the ring, which means that we - // can't tell it to stop after the last buffer. - let tail_ptr = &self.rd[last_index] as *const _ as u32; - fence(Ordering::Release); - - dma.dmacrx_dtpr().write(|w| w.0 = tail_ptr); + Self { + descriptors, + buffers, + index: 0, } } - pub(crate) fn on_interrupt(&mut self) { - // XXX: Do we need to do anything here ? Maybe we should try to advance the tail ptr, but it - // would soon hit the read ptr anyway, and we will wake smoltcp's stack on the interrupt - // which should try to pop a packet... - } - - pub(crate) fn pop_packet(&mut self) -> Option { + /// Get a received packet if any, or None. + pub(crate) fn available(&mut self) -> Option<&mut [u8]> { // Not sure if the contents of the write buffer on the M7 can affects reads, so we are using // a DMB here just in case, it also serves as a hint to the compiler that we're syncing the // buffer (I think .-.) fence(Ordering::SeqCst); - let read_available = self.rd[self.read_idx].available(); - let tail_index = (self.next_tail_idx + N - 1) % N; - - let pkt = if read_available && self.read_idx != tail_index { - let pkt = self.buffers[self.read_idx].take(); - let len = (self.rd[self.read_idx].rdes3.get() & EMAC_RDES3_PKTLEN) as usize; - - assert!(pkt.is_some()); - let valid = self.rd[self.read_idx].valid(); - - self.read_idx = (self.read_idx + 1) % N; - if valid { - pkt.map(|p| p.slice(0..len)) - } else { - None + // We might have to process many packets, in case some have been rx'd but are invalid. + loop { + let descriptor = &mut self.descriptors[self.index]; + if !descriptor.available() { + return None; } - } else { - None - }; - // Try to advance the tail_idx - if self.next_tail_idx != self.read_idx { - match PacketBox::new(Packet::new()) { - Some(b) => { - let addr = b.as_ptr() as u32; - self.buffers[self.next_tail_idx].replace(b); - self.rd[self.next_tail_idx].set_ready(addr); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::Release); - - // NOTE(unsafe) atomic write - unsafe { - ETH.ethernet_dma() - .dmacrx_dtpr() - .write(|w| w.0 = &self.rd[self.next_tail_idx] as *const _ as u32); - } - - self.next_tail_idx = (self.next_tail_idx + 1) % N; - } - None => {} + // If packet is invalid, pop it and try again. + if !descriptor.valid() { + warn!("invalid packet: {:08x}", descriptor.rdes0.get()); + self.pop_packet(); + continue; } + + break; } - pkt + + let descriptor = &mut self.descriptors[self.index]; + let len = (descriptor.rdes3.get() & EMAC_RDES3_PKTLEN) as usize; + return Some(&mut self.buffers[self.index].0[..len]); } -} -pub struct DescriptorRing { - pub(crate) tx: TDesRing, - pub(crate) rx: RDesRing, -} + /// Pop the packet previously returned by `available`. + pub(crate) fn pop_packet(&mut self) { + let descriptor = &mut self.descriptors[self.index]; + assert!(descriptor.available()); -impl DescriptorRing { - pub const fn new() -> Self { - Self { - tx: TDesRing::new(), - rx: RDesRing::new(), + self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::Release); + + // signal DMA it can try again. + // NOTE(unsafe) Atomic write + unsafe { ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0) } + + // Increment index. + self.index += 1; + if self.index == self.descriptors.len() { + self.index = 0 } } - - pub fn init(&mut self) { - self.tx.init(); - self.rx.init(); - } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 5b76d1e7f..fcb4a296c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -1,35 +1,28 @@ -use core::marker::PhantomData; +mod descriptors; + use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_net::{Device, DeviceCapabilities, LinkState, PacketBuf, MTU}; -use embassy_sync::waitqueue::AtomicWaker; +pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; +use super::*; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Speed}; use crate::pac::{ETH, RCC, SYSCFG}; use crate::Peripheral; -mod descriptors; -use descriptors::DescriptorRing; +const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet -use super::*; - -pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); -impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub const fn new() -> Self { - Self(StateStorage::new()) - } -} -pub struct Ethernet<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> { - state: PeripheralMutex<'d, Inner<'d, T, TX, RX>>, +pub struct Ethernet<'d, T: Instance, P: PHY> { + _peri: PeripheralRef<'d, T>, + pub(crate) tx: TDesRing<'d>, + pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], _phy: P, clock_range: u8, phy_addr: u8, - mac_addr: [u8; 6], + pub(crate) mac_addr: [u8; 6], } macro_rules! config_pins { @@ -44,10 +37,9 @@ macro_rules! config_pins { }; } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, P, TX, RX> { - /// safety: the returned instance is not leak-safe - pub unsafe fn new( - state: &'d mut State<'d, T, TX, RX>, +impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { + pub fn new( + queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, interrupt: impl Peripheral

+ 'd, ref_clk: impl Peripheral

> + 'd, @@ -63,126 +55,123 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, mac_addr: [u8; 6], phy_addr: u8, ) -> Self { - into_ref!(interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - // Enable the necessary Clocks - // NOTE(unsafe) We have exclusive access to the registers - critical_section::with(|_| { - RCC.apb4enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { - w.set_eth1macen(true); - w.set_eth1txen(true); - w.set_eth1rxen(true); + unsafe { + // Enable the necessary Clocks + // NOTE(unsafe) We have exclusive access to the registers + critical_section::with(|_| { + RCC.apb4enr().modify(|w| w.set_syscfgen(true)); + RCC.ahb1enr().modify(|w| { + w.set_eth1macen(true); + w.set_eth1txen(true); + w.set_eth1rxen(true); + }); + + // RMII + SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); }); - // RMII - SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); - }); + config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + // NOTE(unsafe) We have exclusive access to the registers + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); + let mtl = ETH.ethernet_mtl(); - // NOTE(unsafe) We are ourselves not leak-safe. - let state = PeripheralMutex::new(interrupt, &mut state.0, || Inner::new(peri)); + // Reset and wait + dma.dmamr().modify(|w| w.set_swr(true)); + while dma.dmamr().read().swr() {} - // NOTE(unsafe) We have exclusive access to the registers - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); + mac.maccr().modify(|w| { + w.set_ipg(0b000); // 96 bit times + w.set_acs(true); + w.set_fes(true); + w.set_dm(true); + // TODO: Carrier sense ? ECRSFD + }); - // Reset and wait - dma.dmamr().modify(|w| w.set_swr(true)); - while dma.dmamr().read().swr() {} + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, + // so the LR write must happen after the HR write. + mac.maca0hr() + .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); + mac.maca0lr().write(|w| { + w.set_addrlo( + u32::from(mac_addr[0]) + | (u32::from(mac_addr[1]) << 8) + | (u32::from(mac_addr[2]) << 16) + | (u32::from(mac_addr[3]) << 24), + ) + }); - mac.maccr().modify(|w| { - w.set_ipg(0b000); // 96 bit times - w.set_acs(true); - w.set_fes(true); - w.set_dm(true); - // TODO: Carrier sense ? ECRSFD - }); + mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); - // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, - // so the LR write must happen after the HR write. - mac.maca0hr() - .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); - mac.maca0lr().write(|w| { - w.set_addrlo( - u32::from(mac_addr[0]) - | (u32::from(mac_addr[1]) << 8) - | (u32::from(mac_addr[2]) << 16) - | (u32::from(mac_addr[3]) << 24), - ) - }); + // 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) + }); - mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); + // 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); + }); - // 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) - }); + mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); + mtl.mtltx_qomr().modify(|w| w.set_tsf(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); - }); + dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? + dma.dmacrx_cr().modify(|w| { + w.set_rxpbl(1); // 32 ? + w.set_rbsz(MTU as u16); + }); - mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); - mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); + // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called + let hclk = crate::rcc::get_freqs().ahb1; + let hclk_mhz = hclk.0 / 1_000_000; - dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? - dma.dmacrx_cr().modify(|w| { - w.set_rxpbl(1); // 32 ? - w.set_rbsz(MTU as u16); - }); + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=34 => 2, // Divide by 16 + 35..=59 => 3, // Divide by 26 + 60..=99 => 0, // Divide by 42 + 100..=149 => 1, // Divide by 62 + 150..=249 => 4, // Divide by 102 + 250..=310 => 5, // Divide by 124 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; - // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called - let hclk = crate::rcc::get_freqs().ahb1; - let hclk_mhz = hclk.0 / 1_000_000; + let pins = [ + ref_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + crs.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_en.map_into(), + ]; - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=34 => 2, // Divide by 16 - 35..=59 => 3, // Divide by 26 - 60..=99 => 0, // Divide by 42 - 100..=149 => 1, // Divide by 62 - 150..=249 => 4, // Divide by 102 - 250..=310 => 5, // Divide by 124 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") - } - }; - - let pins = [ - ref_clk.map_into(), - mdio.map_into(), - mdc.map_into(), - crs.map_into(), - rx_d0.map_into(), - rx_d1.map_into(), - tx_d0.map_into(), - tx_d1.map_into(), - tx_en.map_into(), - ]; - - let mut this = Self { - state, - pins, - _phy: phy, - clock_range, - phy_addr, - mac_addr, - }; - - this.state.with(|s| { - s.desc_ring.init(); + let mut this = Self { + _peri: peri, + tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), + rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), + pins, + _phy: phy, + clock_range, + phy_addr, + mac_addr, + }; fence(Ordering::SeqCst); @@ -205,17 +194,37 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Ethernet<'d, T, w.set_rie(true); w.set_tie(true); }); - }); - P::phy_reset(&mut this); - P::phy_init(&mut this); - this + P::phy_reset(&mut this); + P::phy_init(&mut this); + + interrupt.set_handler(Self::on_interrupt); + interrupt.enable(); + + this + } + } + + fn on_interrupt(_cx: *mut ()) { + WAKER.wake(); + + // TODO: Check and clear more flags + unsafe { + let dma = ETH.ethernet_dma(); + + dma.dmacsr().modify(|w| { + w.set_ti(true); + w.set_ri(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmacsr().read(); + dma.dmacsr().read(); + } } } -unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationManagement - for Ethernet<'d, T, P, TX, RX> -{ +unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { fn smi_read(&mut self, reg: u8) -> u16 { // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` unsafe { @@ -251,44 +260,7 @@ unsafe impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> StationMa } } -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device for Ethernet<'d, T, P, TX, RX> { - fn is_transmit_ready(&mut self) -> bool { - self.state.with(|s| s.desc_ring.tx.available()) - } - - fn transmit(&mut self, pkt: PacketBuf) { - self.state.with(|s| unwrap!(s.desc_ring.tx.transmit(pkt))); - } - - fn receive(&mut self) -> Option { - self.state.with(|s| s.desc_ring.rx.pop_packet()) - } - - fn register_waker(&mut self, waker: &Waker) { - WAKER.register(waker); - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(TX.min(RX)); - caps - } - - fn link_state(&mut self) -> LinkState { - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} - -impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Ethernet<'d, T, P, TX, RX> { +impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers unsafe { @@ -325,46 +297,3 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Drop for Etherne }) } } - -//---------------------------------------------------------------------- - -struct Inner<'d, T: Instance, const TX: usize, const RX: usize> { - _peri: PhantomData<&'d mut T>, - desc_ring: DescriptorRing, -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> Inner<'d, T, TX, RX> { - pub fn new(_peri: impl Peripheral

+ 'd) -> Self { - Self { - _peri: PhantomData, - desc_ring: DescriptorRing::new(), - } - } -} - -impl<'d, T: Instance, const TX: usize, const RX: usize> PeripheralState for Inner<'d, T, TX, RX> { - type Interrupt = crate::interrupt::ETH; - - fn on_interrupt(&mut self) { - unwrap!(self.desc_ring.tx.on_interrupt()); - self.desc_ring.rx.on_interrupt(); - - WAKER.wake(); - - // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmacsr().modify(|w| { - w.set_ti(true); - w.set_ri(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmacsr().read(); - dma.dmacsr().read(); - } - } -} - -static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 4ccc0b5ef..551325ca4 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; -use embassy_stm32::eth::{Ethernet, State}; +use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; @@ -22,11 +22,12 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } -type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; +type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { @@ -51,25 +52,23 @@ async fn main(spawner: Spawner) -> ! { let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - let device = unsafe { - Ethernet::new( - singleton!(State::new()), - p.ETH, - eth_int, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, - GenericSMI, - mac_addr, - 0, - ) - }; + let device = Ethernet::new( + singleton!(PacketQueue::<16, 16>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 64fd84141..61a08ae10 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::client::{TcpClient, TcpClientState}; use embassy_net::{Stack, StackResources}; use embassy_stm32::eth::generic_smi::GenericSMI; -use embassy_stm32::eth::{Ethernet, State}; +use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; @@ -23,11 +23,12 @@ macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) + let (x,) = STATIC_CELL.init(($val,)); + x }}; } -type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; +type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] async fn net_task(stack: &'static Stack) -> ! { @@ -52,25 +53,23 @@ async fn main(spawner: Spawner) -> ! { let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - let device = unsafe { - Ethernet::new( - singleton!(State::new()), - p.ETH, - eth_int, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, - GenericSMI, - mac_addr, - 0, - ) - }; + let device = Ethernet::new( + singleton!(PacketQueue::<16, 16>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); let config = embassy_net::ConfigStrategy::Dhcp; //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { From 5eae295c8ad8da529490b89f914f2cca07ae6972 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Dec 2022 16:14:50 +0100 Subject: [PATCH 0432/1575] stm32: rename feature `net` to `embassy-net`. The eth code is always built and available, but has no own API (other than the embassy-net Device impl) to rx/tx packets. We could add this API in the future, so the feature only means "embassy-net support". --- embassy-stm32/Cargo.toml | 1 - examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b7f718c5f..0c491ee46 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -77,7 +77,6 @@ stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features [features] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] sdmmc-rs = ["embedded-sdmmc"] -net = ["embassy-net" ] memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index f4d674cdc..afdf87000 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" 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", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-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.4.0", features = ["async"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 11ce35053..5acf0035d 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" 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", "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-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-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.4.0", features = ["async"] } From 790e4e1594d455d06c7303a628172244e78af0da Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Dec 2022 16:18:16 +0100 Subject: [PATCH 0433/1575] examples/std: update to new embassy-net trait. --- examples/std/src/tuntap.rs | 113 +++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index a0cace7f7..bb3e194cc 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs @@ -1,8 +1,10 @@ use std::io; use std::io::{Read, Write}; use std::os::unix::io::{AsRawFd, RawFd}; +use std::task::Context; use async_io::Async; +use embassy_net::device::{self, Device, DeviceCapabilities, LinkState}; use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -125,54 +127,35 @@ impl io::Write for TunTap { pub struct TunTapDevice { device: Async, - waker: Option, } impl TunTapDevice { pub fn new(name: &str) -> io::Result { Ok(Self { device: Async::new(TunTap::new(name)?)?, - waker: None, }) } } -use core::task::Waker; -use std::task::Context; - -use embassy_net::{Device, DeviceCapabilities, LinkState, Packet, PacketBox, PacketBoxExt, PacketBuf}; - impl Device for TunTapDevice { - fn is_transmit_ready(&mut self) -> bool { - true - } + type RxToken<'a> = RxToken where Self: 'a; + type TxToken<'a> = TxToken<'a> where Self: 'a; - fn transmit(&mut self, pkt: PacketBuf) { - // todo handle WouldBlock - match self.device.get_mut().write(&pkt) { - Ok(_) => {} - Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - info!("transmit WouldBlock"); - } - Err(e) => panic!("transmit error: {:?}", e), - } - } - - fn receive(&mut self) -> Option { - let mut pkt = PacketBox::new(Packet::new()).unwrap(); + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let mut buf = vec![0; self.device.get_ref().mtu]; loop { - match self.device.get_mut().read(&mut pkt[..]) { + match self.device.get_mut().read(&mut buf) { Ok(n) => { - return Some(pkt.slice(0..n)); + buf.truncate(n); + return Some(( + RxToken { buffer: buf }, + TxToken { + device: &mut self.device, + }, + )); } Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - let ready = if let Some(w) = self.waker.as_ref() { - let mut cx = Context::from_waker(w); - self.device.poll_readable(&mut cx).is_ready() - } else { - false - }; - if !ready { + if !self.device.poll_readable(cx).is_ready() { return None; } } @@ -181,28 +164,10 @@ impl Device for TunTapDevice { } } - fn register_waker(&mut self, w: &Waker) { - match self.waker { - // Optimization: If both the old and new Wakers wake the same task, we can simply - // keep the old waker, skipping the clone. (In most executor implementations, - // cloning a waker is somewhat expensive, comparable to cloning an Arc). - Some(ref w2) if (w2.will_wake(w)) => {} - _ => { - // clone the new waker and store it - if let Some(old_waker) = core::mem::replace(&mut self.waker, Some(w.clone())) { - // We had a waker registered for another task. Wake it, so the other task can - // reregister itself if it's still interested. - // - // If two tasks are waiting on the same thing concurrently, this will cause them - // to wake each other in a loop fighting over this WakerRegistration. This wastes - // CPU but things will still work. - // - // If the user wants to have two tasks waiting on the same thing they should use - // a more appropriate primitive that can store multiple wakers. - old_waker.wake() - } - } - } + fn transmit(&mut self, _cx: &mut Context) -> Option> { + Some(TxToken { + device: &mut self.device, + }) } fn capabilities(&self) -> DeviceCapabilities { @@ -211,7 +176,7 @@ impl Device for TunTapDevice { caps } - fn link_state(&mut self) -> LinkState { + fn link_state(&mut self, _cx: &mut Context) -> LinkState { LinkState::Up } @@ -219,3 +184,41 @@ impl Device for TunTapDevice { [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] } } + +#[doc(hidden)] +pub struct RxToken { + buffer: Vec, +} + +impl device::RxToken for RxToken { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(&mut self.buffer) + } +} + +#[doc(hidden)] +pub struct TxToken<'a> { + device: &'a mut Async, +} + +impl<'a> device::TxToken for TxToken<'a> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut buffer = vec![0; len]; + let result = f(&mut buffer); + + // todo handle WouldBlock with async + match self.device.get_mut().write(&buffer) { + Ok(_) => {} + Err(e) if e.kind() == io::ErrorKind::WouldBlock => info!("transmit WouldBlock"), + Err(e) => panic!("transmit error: {:?}", e), + } + + result + } +} From bffa5be2f49fdd5e223df8dae7df88f52bde83e8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 15 Dec 2022 19:43:36 +0100 Subject: [PATCH 0434/1575] nrf/uart: do not error on zero length transfers. It's a perfectly fine thing to do, should be just a noop. Erroring is really annoying when you're writing a payload to uart that might be zero-length or not. --- embassy-nrf/src/uarte.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 63df1b682..4703c1a50 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -53,7 +53,6 @@ impl Default for Config { #[non_exhaustive] pub enum Error { BufferTooLong, - BufferZeroLength, DMABufferNotInDataMemory, // TODO: add other error variants. } @@ -370,10 +369,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { } pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } + + slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -437,10 +437,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { } pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } + + slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -550,7 +551,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -603,7 +604,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(()); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -672,7 +673,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(0); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -728,7 +729,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { - return Err(Error::BufferZeroLength); + return Ok(0); } if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); @@ -918,7 +919,6 @@ mod eh1 { fn kind(&self) -> embedded_hal_1::serial::ErrorKind { match *self { Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, - Self::BufferZeroLength => embedded_hal_1::serial::ErrorKind::Other, Self::DMABufferNotInDataMemory => embedded_hal_1::serial::ErrorKind::Other, } } From feaeb533fb5963e9d603ccb5143662b21daed2d8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 19 Dec 2022 01:15:32 +0100 Subject: [PATCH 0435/1575] hal-common/atomic_ring_buffer: fix crashes when len=0 --- embassy-hal-common/src/atomic_ring_buffer.rs | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index c5e444306..3e90b0870 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -82,10 +82,11 @@ impl RingBuffer { } pub fn is_full(&self) -> bool { + let len = self.len.load(Ordering::Relaxed); let start = self.start.load(Ordering::Relaxed); let end = self.end.load(Ordering::Relaxed); - self.wrap(end + 1) == start + len == 0 || self.wrap(end + 1) == start } pub fn is_empty(&self) -> bool { @@ -154,7 +155,7 @@ impl<'a> Writer<'a> { let end = self.0.end.load(Ordering::Relaxed); let n = if start <= end { - len - end - (start == 0) as usize + len - end - (start == 0 && len != 0) as usize } else { start - end - 1 }; @@ -328,4 +329,23 @@ mod tests { assert_eq!(rb.is_full(), true); } } + + #[test] + fn zero_len() { + let rb = RingBuffer::new(); + unsafe { + assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_full(), true); + + rb.writer().push(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + + rb.reader().pop(|buf| { + assert_eq!(0, buf.len()); + 0 + }); + } + } } From 5b72410828309bd0d37c8f85c0fb4f88d2a105f8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 19 Dec 2022 01:16:41 +0100 Subject: [PATCH 0436/1575] hal-common/atomic_ring_buffer: Add push_slice, pop_slice. --- embassy-hal-common/src/atomic_ring_buffer.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index 3e90b0870..a8a6a2166 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -135,6 +135,14 @@ impl<'a> Writer<'a> { n != 0 } + /// Get a buffer where data can be pushed to. + /// + /// Equivalent to [`Self::push_buf`] but returns a slice. + pub fn push_slice(&mut self) -> &mut [u8] { + let (data, len) = self.push_buf(); + unsafe { slice::from_raw_parts_mut(data, len) } + } + /// Get a buffer where data can be pushed to. /// /// Write data to the start of the buffer, then call `push_done` with @@ -204,6 +212,14 @@ impl<'a> Reader<'a> { res } + /// Get a buffer where data can be popped from. + /// + /// Equivalent to [`Self::pop_buf`] but returns a slice. + pub fn pop_slice(&mut self) -> &mut [u8] { + let (data, len) = self.pop_buf(); + unsafe { slice::from_raw_parts_mut(data, len) } + } + /// Get a buffer where data can be popped from. /// /// Read data from the start of the buffer, then call `pop_done` with From 849a0e174fa1601236050afb72290174675c585f Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Tue, 20 Dec 2022 09:11:39 +0800 Subject: [PATCH 0437/1575] add convert_to_celsius function in the adc module modify RP2040 adc example to get inside biased bipolar diode voltage, then convert this temperature sensor data into Celsius degree, according to chapter 4.9.5. Temperature Sensor in RP2040 datasheet. --- examples/rp/src/bin/adc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 2a9e93732..25e5126b4 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -27,7 +27,12 @@ async fn main(_spawner: Spawner) { let level = adc.read(&mut p28).await; info!("Pin 28 ADC: {}", level); let temp = adc.read_temperature().await; - info!("Temp: {}", temp); + info!("Temp: {} degrees", convert_to_celsius(temp)); Timer::after(Duration::from_secs(1)).await; } } + +fn convert_to_celsius(raw_temp: u16) -> f32 { + // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet + 27.0 - (raw_temp as f32 * 3.3 / 4096.0 -0.706)/0.001721 as f32 +} \ No newline at end of file From 5ae91ed3b64a086b15a949477a5762a3876307c5 Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Tue, 20 Dec 2022 14:59:49 +0800 Subject: [PATCH 0438/1575] cargo fmt --- examples/rp/src/bin/adc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 25e5126b4..4202fd394 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -34,5 +34,5 @@ async fn main(_spawner: Spawner) { fn convert_to_celsius(raw_temp: u16) -> f32 { // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet - 27.0 - (raw_temp as f32 * 3.3 / 4096.0 -0.706)/0.001721 as f32 -} \ No newline at end of file + 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32 +} From 63122f6d7e9b6a3ec9e246a7171444bde41ce9f0 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:52:40 +0530 Subject: [PATCH 0439/1575] button controlled example --- .../src/bin/button_controlled_blink.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 examples/stm32f0/src/bin/button_controlled_blink.rs diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs new file mode 100644 index 000000000..e1f223433 --- /dev/null +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs @@ -0,0 +1,64 @@ +//! This example showcases how to create task + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::sync::atomic::{AtomicU32, Ordering}; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{AnyPin, Input, Level, Output, Pin, Pull, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +static BLINK_MS: AtomicU32 = AtomicU32::new(0); + +#[embassy_executor::task] +async fn led_task(led: AnyPin) { + // Configure the LED pin as a push pull ouput and obtain handler. + // On the Nucleo F091RC theres an on-board LED connected to pin PA5. + let mut led = Output::new(led, Level::Low, Speed::Low); + + loop { + let del = BLINK_MS.load(Ordering::Relaxed); + info!("Value of del is {}", del); + Timer::after(Duration::from_millis(del.into())).await; + info!("LED toggling"); + led.toggle(); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + // Initialize and create handle for devicer peripherals + let p = embassy_stm32::init(Default::default()); + + // Configure the button pin and obtain handler. + // On the Nucleo F091RC there is a button connected to pin PC13. + let button = Input::new(p.PC13, Pull::None); + let mut button = ExtiInput::new(button, p.EXTI13); + + // Create and initialize a delay variable to manage delay loop + let mut del_var = 2000; + + // Blink duration value to global context + BLINK_MS.store(del_var, Ordering::Relaxed); + + // Spawn LED blinking task + spawner.spawn(led_task(p.PA5.degrade())).unwrap(); + + loop { + // Check if button got pressed + button.wait_for_rising_edge().await; + info!("rising_edge"); + del_var = del_var - 200; + // If updated delay value drops below 200 then reset it back to starting value + if del_var < 200 { + del_var = 2000; + } + // Updated delay value to global context + BLINK_MS.store(del_var, Ordering::Relaxed); + } +} From c0f3610581164e12446053706b6dbef98b1f1089 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:53:55 +0530 Subject: [PATCH 0440/1575] added interrupt example --- examples/stm32f0/src/bin/button_exti.rs | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/stm32f0/src/bin/button_exti.rs diff --git a/examples/stm32f0/src/bin/button_exti.rs b/examples/stm32f0/src/bin/button_exti.rs new file mode 100644 index 000000000..40c0d5848 --- /dev/null +++ b/examples/stm32f0/src/bin/button_exti.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize and create handle for devicer peripherals + let p = embassy_stm32::init(Default::default()); + // Configure the button pin and obtain handler. + // On the Nucleo F091RC there is a button connected to pin PC13. + let button = Input::new(p.PC13, Pull::Down); + let mut button = ExtiInput::new(button, p.EXTI13); + + info!("Press the USER button..."); + loop { + button.wait_for_falling_edge().await; + info!("Pressed!"); + button.wait_for_rising_edge().await; + info!("Released!"); + } +} From 0db3837dccb9c0ee2fdb236937a6a20b63fb89f8 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:54:36 +0530 Subject: [PATCH 0441/1575] added priority based example --- examples/stm32f0/src/bin/priority.rs | 139 +++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 examples/stm32f0/src/bin/priority.rs diff --git a/examples/stm32f0/src/bin/priority.rs b/examples/stm32f0/src/bin/priority.rs new file mode 100644 index 000000000..7fed6a773 --- /dev/null +++ b/examples/stm32f0/src/bin/priority.rs @@ -0,0 +1,139 @@ +//! This example showcases how to create multiple Executor instances to run tasks at +//! different priority levels. +//! +//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling +//! there's work in the queue, and `wfe` for waiting for work. +//! +//! Medium and high priority executors run in two interrupts with different priorities. +//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since +//! when there's work the interrupt will trigger and run the executor. +//! +//! Sample output below. Note that high priority ticks can interrupt everything else, and +//! medium priority computations can interrupt low priority computations, making them to appear +//! to take significantly longer time. +//! +//! ```not_rust +//! [med] Starting long computation +//! [med] done in 992 ms +//! [high] tick! +//! [low] Starting long computation +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [low] done in 3972 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! ``` +//! +//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. +//! You will get an output like the following. Note that no computation is ever interrupted. +//! +//! ```not_rust +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [low] Starting long computation +//! [low] done in 992 ms +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! [low] Starting long computation +//! [low] done in 992 ms +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! ``` +//! + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_stm32::interrupt; +use embassy_stm32::interrupt::InterruptExt; +use embassy_time::{Duration, Instant, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run_high() { + loop { + // info!(" [high] tick!"); + Timer::after(Duration::from_ticks(27374)).await; + } +} + +#[embassy_executor::task] +async fn run_med() { + loop { + let start = Instant::now(); + info!(" [med] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(8_000_000); // ~1 second + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() / 33; + info!(" [med] done in {} ms", ms); + + Timer::after(Duration::from_ticks(23421)).await; + } +} + +#[embassy_executor::task] +async fn run_low() { + loop { + let start = Instant::now(); + info!("[low] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(16_000_000); // ~2 seconds + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() / 33; + info!("[low] done in {} ms", ms); + + Timer::after(Duration::from_ticks(32983)).await; + } +} + +static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); +static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_LOW: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + // Initialize and create handle for devicer peripherals + let _p = embassy_stm32::init(Default::default()); + + // High-priority executor: USART1, priority level 6 + let irq = interrupt::take!(USART1); + irq.set_priority(interrupt::Priority::P6); + let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); + let spawner = executor.start(); + unwrap!(spawner.spawn(run_high())); + + // Medium-priority executor: USART2, priority level 7 + let irq = interrupt::take!(USART2); + irq.set_priority(interrupt::Priority::P7); + let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); + let spawner = executor.start(); + unwrap!(spawner.spawn(run_med())); + + // Low priority executor: runs in thread mode, using WFE/SEV + let executor = EXECUTOR_LOW.init(Executor::new()); + executor.run(|spawner| { + unwrap!(spawner.spawn(run_low())); + }); +} From 395b5fed6497e901c4275f90afbffaf069931f84 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:55:09 +0530 Subject: [PATCH 0442/1575] added watchdog example --- examples/stm32f0/src/bin/wdg.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 examples/stm32f0/src/bin/wdg.rs diff --git a/examples/stm32f0/src/bin/wdg.rs b/examples/stm32f0/src/bin/wdg.rs new file mode 100644 index 000000000..80e76f901 --- /dev/null +++ b/examples/stm32f0/src/bin/wdg.rs @@ -0,0 +1,25 @@ +#![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) { + // Initialize and create handle for devicer peripherals + let p = embassy_stm32::init(Default::default()); + // Configure the independent watchdog timer + let mut wdg = IndependentWatchdog::new(p.IWDG, 20_000_00); + + info!("Watchdog start"); + unsafe { wdg.unleash() }; + + loop { + Timer::after(Duration::from_secs(1)).await; + unsafe { wdg.pet() }; + } +} From 55d9af71e3eecd188ed01dcb6684ec4baf6d5868 Mon Sep 17 00:00:00 2001 From: "@imrank03" Date: Wed, 21 Dec 2022 11:56:30 +0530 Subject: [PATCH 0443/1575] enabled interrupt feature --- examples/stm32f0/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index c10af1713..d4afbb8f8 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,5 +15,5 @@ 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", "stm32f091rc", "time-driver-any"] } - +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti"] } +static_cell = "1.0" From aa92ce6dc71cf176eb6e99632fda72c875d071e6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Thu, 22 Dec 2022 23:03:05 +0100 Subject: [PATCH 0444/1575] embassy-rp: Add split() to BufferedUart --- embassy-rp/src/uart/buffered.rs | 7 +++ examples/rp/.cargo/config.toml | 2 +- examples/rp/src/bin/uart_buffered_split.rs | 57 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 examples/rp/src/bin/uart_buffered_split.rs diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 32029f81e..e89970d11 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -126,6 +126,13 @@ impl<'d, T: Instance> BufferedUart<'d, T> { Self { phantom: PhantomData } } + + pub fn split(&mut self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { + ( + BufferedUartRx { phantom: PhantomData }, + BufferedUartTx { phantom: PhantomData }, + ) + } } impl<'d, T: Instance> BufferedUartRx<'d, T> { diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 3d6051389..7880c0955 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml @@ -5,4 +5,4 @@ runner = "probe-run --chip RP2040" target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ [env] -DEFMT_LOG = "trace" +DEFMT_LOG = "info" diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs new file mode 100644 index 000000000..36f31c906 --- /dev/null +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_executor::_export::StaticCell; +use embassy_rp::interrupt; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let irq = interrupt::take!(UART0_IRQ); + let tx_buf = &mut singleton!([0u8; 16])[..]; + let rx_buf = &mut singleton!([0u8; 16])[..]; + let mut uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); + let (rx, mut tx) = uart.split(); + + unwrap!(spawner.spawn(reader(rx))); + + info!("Writing..."); + loop { + let data = [ + 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, + ]; + info!("TX {:?}", data); + tx.write_all(&data).await.unwrap(); + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::task] +async fn reader(mut rx: BufferedUartRx<'static, UART0>) { + info!("Reading..."); + loop { + let mut buf = [0; 31]; + rx.read_exact(&mut buf).await.unwrap(); + info!("RX {:?}", buf); + } +} From da9ee837561694a7749e17d727e56da7ddb3e9b2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 09:32:18 +0100 Subject: [PATCH 0445/1575] fix(stm32): Fix write buffer lifetime for repeated writes --- embassy-stm32/src/dma/bdma.rs | 5 ++--- embassy-stm32/src/dma/dma.rs | 5 ++--- embassy-stm32/src/dma/gpdma.rs | 5 ++--- embassy-stm32/src/dma/mod.rs | 4 ++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index e6ce05b7b..7da22ec12 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -78,8 +78,7 @@ foreach_dma_channel! { ); } - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) { - let buf = [repeated]; + unsafe fn start_write_repeated(&mut self, _request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, @@ -87,7 +86,7 @@ foreach_dma_channel! { _request, vals::Dir::FROMMEMORY, reg_addr as *const u32, - buf.as_ptr() as *mut u32, + repeated.as_ptr() as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 97a3df088..45a38dda4 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -102,15 +102,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) { - let buf = [repeated]; + unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, vals::Dir::MEMORYTOPERIPHERAL, reg_addr as *const u32, - buf.as_ptr() as *mut u32, + repeated.as_ptr() as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index bde8c3ef3..46d8715b9 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -75,15 +75,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: W, count: usize, reg_addr: *mut W, options: TransferOptions) { - let buf = [repeated]; + unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, low_level_api::Dir::MemoryToPeripheral, reg_addr as *const u32, - buf.as_ptr() as *mut u32, + repeated.as_ptr() as *mut u32, count, false, W::bits(), diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 74bce6aa9..31f55b868 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -59,7 +59,7 @@ pub(crate) mod sealed { unsafe fn start_write_repeated( &mut self, request: Request, - repeated: W, + repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions, @@ -246,7 +246,7 @@ mod transfers { pub fn write_repeated<'a, W: Word>( channel: impl Peripheral

+ 'a, request: Request, - repeated: W, + repeated: &[W; 1], count: usize, reg_addr: *mut W, ) -> impl Future + 'a { From 2457fcaa35f7ceafcaee44f5e4b2a63c83674c89 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 09:33:34 +0100 Subject: [PATCH 0446/1575] fix(stm32): Align with updated dma::write_repeated signature --- embassy-stm32/src/spi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index ab4352a5c..1d4baf46d 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -473,8 +473,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - let clock_byte = 0x00u8; - let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, clock_byte, clock_byte_count, tx_dst); + let clock_byte = [0x00u8]; + let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst); unsafe { set_txdmaen(T::REGS, true); From 662bb5797f7723f16b0d5211c17e6a9fdeb2cf50 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 09:34:42 +0100 Subject: [PATCH 0447/1575] fix(stm32): Ensure that gpio speed is VeryHigh for all spi versions This fixes #1095 --- embassy-stm32/src/spi/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 1d4baf46d..439d92b97 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -95,13 +95,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { into_ref!(peri, sck, mosi, miso); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] mosi.set_speed(crate::gpio::Speed::VeryHigh); miso.set_as_af(miso.af_num(), AFType::Input); - #[cfg(any(spi_v2, spi_v3, spi_v4))] miso.set_speed(crate::gpio::Speed::VeryHigh); } @@ -129,10 +126,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { into_ref!(sck, miso); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); miso.set_as_af(miso.af_num(), AFType::Input); - #[cfg(any(spi_v2, spi_v3, spi_v4))] miso.set_speed(crate::gpio::Speed::VeryHigh); } @@ -160,10 +155,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { into_ref!(sck, mosi); unsafe { sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - #[cfg(any(spi_v2, spi_v3, spi_v4))] mosi.set_speed(crate::gpio::Speed::VeryHigh); } @@ -772,10 +765,13 @@ fn finish_dma(regs: Regs) { #[cfg(not(any(spi_v3, spi_v4)))] while regs.sr().read().bsy() {} + // Disable the spi peripheral regs.cr1().modify(|w| { w.set_spe(false); }); + // The peripheral automatically disables the DMA stream on completion without error, + // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. #[cfg(not(any(spi_v3, spi_v4)))] regs.cr2().modify(|reg| { reg.set_txdmaen(false); From e9a2c4a9e38ada8a6927e6dba66d486d190edf21 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 15:40:09 +0100 Subject: [PATCH 0448/1575] Let start_write_repeated accept pointer instead of slice --- embassy-stm32/src/dma/bdma.rs | 4 ++-- embassy-stm32/src/dma/dma.rs | 4 ++-- embassy-stm32/src/dma/gpdma.rs | 4 ++-- embassy-stm32/src/dma/mod.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 7da22ec12..bc51cdc43 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -78,7 +78,7 @@ foreach_dma_channel! { ); } - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, @@ -86,7 +86,7 @@ foreach_dma_channel! { _request, vals::Dir::FROMMEMORY, reg_addr as *const u32, - repeated.as_ptr() as *mut u32, + repeated as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 45a38dda4..250505859 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -102,14 +102,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, vals::Dir::MEMORYTOPERIPHERAL, reg_addr as *const u32, - repeated.as_ptr() as *mut u32, + repeated as *mut u32, count, false, vals::Size::from(W::bits()), diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 46d8715b9..87c0dfdf0 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -75,14 +75,14 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: &[W; 1], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, request, low_level_api::Dir::MemoryToPeripheral, reg_addr as *const u32, - repeated.as_ptr() as *mut u32, + repeated as *mut u32, count, false, W::bits(), diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 31f55b868..706bcff30 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -59,7 +59,7 @@ pub(crate) mod sealed { unsafe fn start_write_repeated( &mut self, request: Request, - repeated: &[W; 1], + repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions, @@ -246,7 +246,7 @@ mod transfers { pub fn write_repeated<'a, W: Word>( channel: impl Peripheral

+ 'a, request: Request, - repeated: &[W; 1], + repeated: *const [W], count: usize, reg_addr: *mut W, ) -> impl Future + 'a { From 47a0769fc287afa8c982514ea34de0d4f18a3f99 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 23 Dec 2022 15:49:22 +0100 Subject: [PATCH 0449/1575] Let repeated clock byte be singular pointer and not array pointer --- embassy-stm32/src/dma/bdma.rs | 2 +- embassy-stm32/src/dma/dma.rs | 2 +- embassy-stm32/src/dma/gpdma.rs | 2 +- embassy-stm32/src/dma/mod.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index bc51cdc43..6160b9f40 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -78,7 +78,7 @@ foreach_dma_channel! { ); } - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 250505859..fec60f708 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -102,7 +102,7 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 87c0dfdf0..d252cef3e 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -75,7 +75,7 @@ foreach_dma_channel! { ) } - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const [W], count: usize, reg_addr: *mut W, options: TransferOptions) { + unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { low_level_api::start_transfer( pac::$dma_peri, $channel_num, diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 706bcff30..b51e0d40b 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -59,7 +59,7 @@ pub(crate) mod sealed { unsafe fn start_write_repeated( &mut self, request: Request, - repeated: *const [W], + repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions, @@ -246,7 +246,7 @@ mod transfers { pub fn write_repeated<'a, W: Word>( channel: impl Peripheral

+ 'a, request: Request, - repeated: *const [W], + repeated: *const W, count: usize, reg_addr: *mut W, ) -> impl Future + 'a { diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 439d92b97..5b81c791a 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -466,7 +466,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - let clock_byte = [0x00u8]; + let clock_byte = 0x00u8; let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst); unsafe { From cd9a65ba3969460cd7ecb9200f12a828c26205aa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 23 Dec 2022 20:45:51 +0100 Subject: [PATCH 0450/1575] stm32/usb: use separate irq flags. - Fixes race condition that could cause losing irqs (because `if flags != 0` was clearing all) - Doesn't need CAS, which is nice for thumbv6. --- embassy-stm32/src/usb/usb.rs | 66 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 460abfe28..cba4915c1 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -2,10 +2,9 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::Ordering; +use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; -use atomic_polyfill::{AtomicBool, AtomicU8}; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; @@ -35,10 +34,9 @@ static BUS_WAKER: AtomicWaker = NEW_AW; static EP0_SETUP: AtomicBool = AtomicBool::new(false); static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; -static IRQ_FLAGS: AtomicU8 = AtomicU8::new(0); -const IRQ_FLAG_RESET: u8 = 0x01; -const IRQ_FLAG_SUSPEND: u8 = 0x02; -const IRQ_FLAG_RESUME: u8 = 0x04; +static IRQ_RESET: AtomicBool = AtomicBool::new(false); +static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); +static IRQ_RESUME: AtomicBool = AtomicBool::new(false); fn convert_type(t: EndpointType) -> EpType { match t { @@ -184,47 +182,51 @@ impl<'d, T: Instance> Driver<'d, T> { let istr = regs.istr().read(); - let mut flags: u8 = 0; - if istr.susp() { //trace!("USB IRQ: susp"); - flags |= IRQ_FLAG_SUSPEND; + IRQ_SUSPEND.store(true, Ordering::Relaxed); regs.cntr().modify(|w| { w.set_fsusp(true); w.set_lpmode(true); - }) + }); + + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_susp(false); + regs.istr().write_value(clear); + + // Wake main thread. + BUS_WAKER.wake(); } if istr.wkup() { //trace!("USB IRQ: wkup"); - flags |= IRQ_FLAG_RESUME; + IRQ_RESUME.store(true, Ordering::Relaxed); regs.cntr().modify(|w| { w.set_fsusp(false); w.set_lpmode(false); - }) + }); + + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_wkup(false); + regs.istr().write_value(clear); + + // Wake main thread. + BUS_WAKER.wake(); } if istr.reset() { //trace!("USB IRQ: reset"); - flags |= IRQ_FLAG_RESET; + IRQ_RESET.store(true, Ordering::Relaxed); // Write 0 to clear. let mut clear = regs::Istr(!0); clear.set_reset(false); regs.istr().write_value(clear); - } - if flags != 0 { - // Send irqs to main thread. - IRQ_FLAGS.fetch_or(flags, Ordering::AcqRel); + // Wake main thread. BUS_WAKER.wake(); - - // Clear them - let mut mask = regs::Istr(0); - mask.set_wkup(true); - mask.set_susp(true); - mask.set_reset(true); - regs.istr().write_value(regs::Istr(!(istr.0 & mask.0))); } if istr.ctr() { @@ -436,17 +438,15 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { if self.inited { let regs = T::regs(); - let flags = IRQ_FLAGS.load(Ordering::Acquire); - - if flags & IRQ_FLAG_RESUME != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel); + if IRQ_RESUME.load(Ordering::Acquire) { + IRQ_RESUME.store(false, Ordering::Relaxed); return Poll::Ready(Event::Resume); } - if flags & IRQ_FLAG_RESET != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel); + if IRQ_RESET.load(Ordering::Acquire) { + IRQ_RESET.store(false, Ordering::Relaxed); - trace!("RESET REGS WRITINGINGING"); + trace!("RESET"); regs.daddr().write(|w| { w.set_ef(true); w.set_add(0); @@ -475,8 +475,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { return Poll::Ready(Event::Reset); } - if flags & IRQ_FLAG_SUSPEND != 0 { - IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel); + if IRQ_SUSPEND.load(Ordering::Acquire) { + IRQ_SUSPEND.store(false, Ordering::Relaxed); return Poll::Ready(Event::Suspend); } From 10c9cc31b14a356e58833fd6c81456251ab3fce9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 23 Dec 2022 20:46:49 +0100 Subject: [PATCH 0451/1575] Remove unnecessary use of atomic-polyfill. Only use it when CAS is actually needed. --- .vscode/settings.json | 11 ++++++----- embassy-executor/src/arch/riscv32.rs | 3 +-- embassy-executor/src/arch/xtensa.rs | 3 +-- embassy-net/Cargo.toml | 1 - embassy-net/src/tcp.rs | 2 +- embassy-rp/src/multicore.rs | 4 +--- embassy-rp/src/usb.rs | 3 +-- embassy-stm32/src/adc/v4.rs | 3 ++- embassy-stm32/src/flash/f4.rs | 3 +-- embassy-stm32/src/flash/f7.rs | 3 +-- embassy-stm32/src/flash/h7.rs | 4 ++-- embassy-stm32/src/i2c/v2.rs | 5 +++-- embassy-stm32/src/usart/buffered.rs | 2 +- embassy-stm32/src/usart/mod.rs | 2 +- embassy-sync/Cargo.toml | 1 - 15 files changed, 22 insertions(+), 28 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 62e5a362f..fd2e4afb1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,8 @@ "rust-analyzer.checkOnSave.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.procMacro.enable": true, - "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + //"rust-analyzer.cargo.target": "thumbv7em-none-eabi", + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ // These are needed to prevent embassy-net from failing to build //"embassy-net/medium-ethernet", @@ -12,19 +13,19 @@ //"embassy-net/pool-16", //"time-tick-16mhz", //"defmt-timestamp-uptime", - "nightly", + //"nightly", //"unstable-traits", ], "rust-analyzer.linkedProjects": [ // Declare for the target you wish to develop //"embassy-executor/Cargo.toml", //"embassy-sync/Cargo.toml", - "examples/nrf/Cargo.toml", + //"examples/nrf/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", - // "examples/stm32f1/Cargo.toml", + //"examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", @@ -35,7 +36,7 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", - // "examples/stm32l5/Cargo.toml", + "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 2a4b006da..e97a56cda 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use core::ptr; - -use atomic_polyfill::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use super::{raw, Spawner}; diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index f908aaa70..4ee0d9f78 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use core::ptr; - -use atomic_polyfill::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering}; use super::{raw, Spawner}; diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index ac338843d..2d853762e 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -51,7 +51,6 @@ generic-array = { version = "0.14.4", default-features = false } 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.3.0", optional = true } [dependencies.smoltcp] diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0dc8da73a..55cbda455 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -329,8 +329,8 @@ pub mod client { use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; + use core::sync::atomic::{AtomicBool, Ordering}; - use atomic_polyfill::{AtomicBool, Ordering}; use embedded_nal_async::IpAddr; use super::*; diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 8290c62a7..e51fc218a 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -29,9 +29,7 @@ //! ``` use core::mem::ManuallyDrop; -use core::sync::atomic::{compiler_fence, Ordering}; - -use atomic_polyfill::AtomicBool; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::peripherals::CORE1; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index dfc2e9da6..8361736a9 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,10 +1,9 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::slice; -use core::sync::atomic::Ordering; +use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use atomic_polyfill::compiler_fence; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index f5aa0e63d..4707b7c95 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -1,4 +1,5 @@ -use atomic_polyfill::{AtomicU8, Ordering}; +use core::sync::atomic::{AtomicU8, Ordering}; + use embedded_hal_02::blocking::delay::DelayUs; use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel}; use pac::adccommon::vals::Presc; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index b8327ce4e..9e23a8adf 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 6d47b78a0..dd0d8439d 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use crate::flash::Error; use crate::pac; diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 3178b3be9..7de95ac11 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -41,7 +41,7 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error cortex_m::asm::isb(); cortex_m::asm::dsb(); - atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); let ret = { let mut ret: Result<(), Error> = Ok(()); @@ -70,7 +70,7 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error cortex_m::asm::isb(); cortex_m::asm::dsb(); - atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst); + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); ret } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 47dc7d2a4..4622635bf 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,8 +1,8 @@ use core::cmp; use core::future::poll_fn; +use core::sync::atomic::{AtomicUsize, Ordering}; use core::task::Poll; -use atomic_polyfill::{AtomicUsize, Ordering}; use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -131,7 +131,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if isr.tcr() || isr.tc() { let state = T::state(); - state.chunks_transferred.fetch_add(1, Ordering::Relaxed); + let transferred = state.chunks_transferred.load(Ordering::Relaxed); + state.chunks_transferred.store(transferred + 1, Ordering::Relaxed); state.waker.wake(); } // The flag can only be cleared by writting to nbytes, we won't do that here, so disable diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 874af1d73..a27fcc1ca 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,8 +1,8 @@ use core::cell::RefCell; use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; 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; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ea75361fa..6f8b6a9e8 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -2,9 +2,9 @@ use core::future::poll_fn; use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::interrupt::InterruptExt; use embassy_futures::select::{select, Either}; use embassy_hal_common::drop::OnDrop; diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 1eeb94c9a..7b5d3ce48 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -31,7 +31,6 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } futures-util = { version = "0.3.17", default-features = false } -atomic-polyfill = "1.0.1" critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" From 787745188c3b62a0170d15ba39f3e58929ae203b Mon Sep 17 00:00:00 2001 From: kalkyl Date: Fri, 23 Dec 2022 23:14:58 +0100 Subject: [PATCH 0452/1575] Change log level to debug --- examples/rp/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 7880c0955..d1c8c1c5a 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml @@ -5,4 +5,4 @@ runner = "probe-run --chip RP2040" target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ [env] -DEFMT_LOG = "info" +DEFMT_LOG = "debug" From eaad0cc1dc09e604d137b3a1bdfd3438ff990621 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 24 Dec 2022 02:51:06 +0100 Subject: [PATCH 0453/1575] embassy-rp: Add Watchdog --- embassy-rp/src/lib.rs | 3 + embassy-rp/src/watchdog.rs | 111 ++++++++++++++++++++++++++++++++ examples/rp/src/bin/watchdog.rs | 48 ++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 embassy-rp/src/watchdog.rs create mode 100644 examples/rp/src/bin/watchdog.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 0ea97aecf..9e99b2fbb 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -36,6 +36,7 @@ pub mod clocks; pub mod flash; pub mod multicore; mod reset; +pub mod watchdog; // Reexports @@ -119,6 +120,8 @@ embassy_hal_common::peripherals! { PIO0, PIO1, + + WATCHDOG, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs new file mode 100644 index 000000000..01509ff4e --- /dev/null +++ b/embassy-rp/src/watchdog.rs @@ -0,0 +1,111 @@ +//! Watchdog +//! +//! The watchdog is a countdown timer that can restart parts of the chip if it reaches zero. This can be used to restart the +//! processor if software gets stuck in an infinite loop. The programmer must periodically write a value to the watchdog to +//! stop it from reaching zero. +//! +//! Credit: based on `rp-hal` implementation (also licensed Apache+MIT) + +use core::marker::PhantomData; + +use embassy_time::Duration; + +use crate::pac; +use crate::peripherals::WATCHDOG; + +/// Watchdog peripheral +pub struct Watchdog<'d> { + phantom: PhantomData<&'d WATCHDOG>, + load_value: u32, // decremented by 2 per tick (µs) +} + +impl<'d> Watchdog<'d> { + /// Create a new `Watchdog` + pub fn new(_watchdog: WATCHDOG) -> Self { + Self { + phantom: PhantomData, + load_value: 0, + } + } + + /// Start tick generation on clk_tick which is driven from clk_ref. + /// + /// # Arguments + /// + /// * `cycles` - Total number of tick cycles before the next tick is generated. + /// It is expected to be the frequency in MHz of clk_ref. + pub fn enable_tick_generation(&mut self, cycles: u8) { + const WATCHDOG_TICK_ENABLE_BITS: u32 = 0x200; + unsafe { + let watchdog = pac::WATCHDOG; + watchdog + .tick() + .write_value(pac::watchdog::regs::Tick(WATCHDOG_TICK_ENABLE_BITS | cycles as u32)) + } + } + + /// Defines whether or not the watchdog timer should be paused when processor(s) are in debug mode + /// or when JTAG is accessing bus fabric + pub fn pause_on_debug(&mut self, pause: bool) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| { + w.set_pause_dbg0(pause); + w.set_pause_dbg1(pause); + w.set_pause_jtag(pause); + }) + } + } + + fn load_counter(&self, counter: u32) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.load().write_value(pac::watchdog::regs::Load(counter)); + } + } + + fn enable(&self, bit: bool) { + unsafe { + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| w.set_enable(bit)) + } + } + + // Configure which hardware will be reset by the watchdog + // (everything except ROSC, XOSC) + unsafe fn configure_wdog_reset_triggers(&self) { + let psm = pac::PSM; + psm.wdsel().write_value(pac::psm::regs::Wdsel( + 0x0001ffff & !(0x01 << 0usize) & !(0x01 << 1usize), + )); + } + + /// Feed the watchdog timer + pub fn feed(&mut self) { + self.load_counter(self.load_value) + } + + /// Start the watchdog timer + pub fn start(&mut self, period: Duration) { + const MAX_PERIOD: u32 = 0xFFFFFF; + + let delay_us = period.as_micros() as u32; + if delay_us > MAX_PERIOD / 2 { + panic!( + "Period cannot exceed maximum load value of {} ({} microseconds))", + MAX_PERIOD, + MAX_PERIOD / 2 + ); + } + // Due to a logic error, the watchdog decrements by 2 and + // the load value must be compensated; see RP2040-E1 + self.load_value = delay_us * 2; + + self.enable(false); + unsafe { + self.configure_wdog_reset_triggers(); + } + self.load_counter(self.load_value); + self.enable(true); + } +} diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs new file mode 100644 index 000000000..13af22a2d --- /dev/null +++ b/examples/rp/src/bin/watchdog.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::gpio; +use embassy_rp::watchdog::*; +use embassy_time::{Duration, Timer}; +use gpio::{Level, Output}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello world!"); + + let mut watchdog = Watchdog::new(p.WATCHDOG); + let mut led = Output::new(p.PIN_25, Level::Low); + + // Set the LED high for 2 seconds so we know when we're about to start the watchdog + led.set_high(); + Timer::after(Duration::from_secs(2)).await; + + // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it + watchdog.start(Duration::from_millis(1_050)); + info!("Started the watchdog timer"); + + // Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset + for _ in 1..=5 { + led.set_low(); + Timer::after(Duration::from_millis(500)).await; + led.set_high(); + Timer::after(Duration::from_millis(500)).await; + info!("Feeding watchdog"); + watchdog.feed(); + } + + info!("Stopped feeding, device will reset in 1.05 seconds"); + // Blink 10 times per second, not feeding the watchdog. + // The processor should reset in 1.05 seconds, or 5 blinks time + loop { + led.set_low(); + Timer::after(Duration::from_millis(100)).await; + led.set_high(); + Timer::after(Duration::from_millis(100)).await; + } +} From e090ab19151fbea6736d5eac0e1497ef9c36626b Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 24 Dec 2022 03:22:51 +0100 Subject: [PATCH 0454/1575] Remove lifetime, use pac fields --- embassy-rp/src/watchdog.rs | 14 +++++++------- examples/rp/src/bin/watchdog.rs | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index 01509ff4e..f4f165b29 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -14,12 +14,12 @@ use crate::pac; use crate::peripherals::WATCHDOG; /// Watchdog peripheral -pub struct Watchdog<'d> { - phantom: PhantomData<&'d WATCHDOG>, +pub struct Watchdog { + phantom: PhantomData, load_value: u32, // decremented by 2 per tick (µs) } -impl<'d> Watchdog<'d> { +impl Watchdog { /// Create a new `Watchdog` pub fn new(_watchdog: WATCHDOG) -> Self { Self { @@ -35,12 +35,12 @@ impl<'d> Watchdog<'d> { /// * `cycles` - Total number of tick cycles before the next tick is generated. /// It is expected to be the frequency in MHz of clk_ref. pub fn enable_tick_generation(&mut self, cycles: u8) { - const WATCHDOG_TICK_ENABLE_BITS: u32 = 0x200; unsafe { let watchdog = pac::WATCHDOG; - watchdog - .tick() - .write_value(pac::watchdog::regs::Tick(WATCHDOG_TICK_ENABLE_BITS | cycles as u32)) + watchdog.tick().write(|w| { + w.set_enable(true); + w.set_cycles(cycles.into()) + }); } } diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs index 13af22a2d..ece5cfe38 100644 --- a/examples/rp/src/bin/watchdog.rs +++ b/examples/rp/src/bin/watchdog.rs @@ -22,11 +22,11 @@ async fn main(_spawner: Spawner) { led.set_high(); Timer::after(Duration::from_secs(2)).await; - // Set to watchdog to reset if it's not reloaded within 1.05 seconds, and start it + // Set to watchdog to reset if it's not fed within 1.05 seconds, and start it watchdog.start(Duration::from_millis(1_050)); info!("Started the watchdog timer"); - // Blink once a second for 5 seconds, refreshing the watchdog timer once a second to avoid a reset + // Blink once a second for 5 seconds, feed the watchdog timer once a second to avoid a reset for _ in 1..=5 { led.set_low(); Timer::after(Duration::from_millis(500)).await; @@ -38,7 +38,7 @@ async fn main(_spawner: Spawner) { info!("Stopped feeding, device will reset in 1.05 seconds"); // Blink 10 times per second, not feeding the watchdog. - // The processor should reset in 1.05 seconds, or 5 blinks time + // The processor should reset in 1.05 seconds. loop { led.set_low(); Timer::after(Duration::from_millis(100)).await; From 056eac998a5668c6737aafb44edc5bbe23e2df46 Mon Sep 17 00:00:00 2001 From: Brendon Fallquist Date: Sat, 24 Dec 2022 21:21:07 -0600 Subject: [PATCH 0455/1575] Hide doc comments from inner function include doc comments on outer function --- embassy-macros/src/macros/task.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 573776f8c..90d2cd893 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs @@ -1,6 +1,7 @@ use darling::FromMeta; use proc_macro2::TokenStream; use quote::{format_ident, quote}; +use syn::{parse_quote, ItemFn}; use crate::util::ctxt::Ctxt; @@ -57,13 +58,7 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken { type Fut = impl ::core::future::Future + 'static; static POOL: ::embassy_executor::raw::TaskPool = ::embassy_executor::raw::TaskPool::new(); @@ -71,5 +66,18 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result Date: Sun, 25 Dec 2022 22:02:20 +0100 Subject: [PATCH 0456/1575] fix bp_read. It was broken since the switch from u8 to u32. --- src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ba8acd347..8e30522be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1071,13 +1071,15 @@ where } #[allow(unused)] - async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u32]) { + async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { // It seems the HW force-aligns the addr // to 2 if data.len() >= 2 // to 4 if data.len() >= 4 // To simplify, enforce 4-align for now. assert!(addr % 4 == 0); + let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; + while !data.is_empty() { // Ensure transfer doesn't cross a window boundary. let window_offs = addr & BACKPLANE_ADDRESS_MASK; @@ -1097,15 +1099,17 @@ where bus.read(&mut junk).await?; // Read data - bus.read(&mut data[..len / 4]).await?; + bus.read(&mut buf[..(len + 3) / 4]).await?; Ok(()) }) .await .unwrap(); + data[..len].copy_from_slice(&slice8_mut(&mut buf)[..len]); + // Advance ptr. addr += len as u32; - data = &mut data[len / 4..]; + data = &mut data[len..]; } } From 42cc0c6d736f6d296ef2a6a636ddf8733cdcd7c6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 25 Dec 2022 22:02:50 +0100 Subject: [PATCH 0457/1575] print ioctl error as signed. --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8e30522be..430821752 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -858,7 +858,10 @@ where if let IoctlState::Sent { buf } = self.state.ioctl_state.get() { if cdc_header.id == self.ioctl_id { - assert_eq!(cdc_header.status, 0); // todo propagate error instead + if cdc_header.status != 0 { + // TODO: propagate error instead + panic!("IOCTL error {=i32}", cdc_header.status as i32); + } let resp_len = cdc_header.len as usize; info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); From 076ada4c0233d2f89c89cda4c01910a86add90ac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 25 Dec 2022 22:50:27 +0100 Subject: [PATCH 0458/1575] Add feature to display console logs from the wifi firmware. --- Cargo.toml | 3 ++ examples/rpi-pico-w/Cargo.toml | 2 +- src/lib.rs | 81 ++++++++++++++++++++++++++++++++++ src/structs.rs | 26 +++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cc19c9389..dadfb5c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,9 @@ edition = "2021" defmt = ["dep:defmt"] log = ["dep:log"] +# Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`. +firmware-logs = [] + [dependencies] embassy-time = { version = "0.1.0" } embassy-sync = { version = "0.1.0" } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index bb44667de..b817289e5 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] -cyw43 = { path = "../../", features = ["defmt"]} +cyw43 = { path = "../../", features = ["defmt", "firmware-logs"]} embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } diff --git a/src/lib.rs b/src/lib.rs index 430821752..883e669de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -575,6 +575,17 @@ pub struct Runner<'a, PWR, SPI> { backplane_window: u32, sdpcm_seq_max: u8, + + #[cfg(feature = "firmware-logs")] + log: LogState, +} + +#[cfg(feature = "firmware-logs")] +struct LogState { + addr: u32, + last_idx: usize, + buf: [u8; 256], + buf_count: usize, } pub async fn new<'a, PWR, SPI>( @@ -598,6 +609,14 @@ where backplane_window: 0xAAAA_AAAA, sdpcm_seq_max: 1, + + #[cfg(feature = "firmware-logs")] + log: LogState { + addr: 0, + last_idx: 0, + buf: [0; 256], + buf_count: 0, + }, }; runner.init(firmware).await; @@ -715,12 +734,74 @@ where //while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} //info!("clock ok"); + #[cfg(feature = "firmware-logs")] + self.log_init().await; + info!("init done "); } + #[cfg(feature = "firmware-logs")] + async fn log_init(&mut self) { + // Initialize shared memory for logging. + + let shared_addr = self + .bp_read32(CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size) + .await; + info!("shared_addr {:08x}", shared_addr); + + let mut shared = [0; SharedMemData::SIZE]; + self.bp_read(shared_addr, &mut shared).await; + let shared = SharedMemData::from_bytes(&shared); + info!("shared: {:08x}", shared); + + self.log.addr = shared.console_addr + 8; + } + + #[cfg(feature = "firmware-logs")] + async fn log_read(&mut self) { + // Read log struct + let mut log = [0; SharedMemLog::SIZE]; + self.bp_read(self.log.addr, &mut log).await; + let log = SharedMemLog::from_bytes(&log); + + let idx = log.idx as usize; + + // If pointer hasn't moved, no need to do anything. + if idx == self.log.last_idx { + return; + } + + // Read entire buf for now. We could read only what we need, but then we + // run into annoying alignment issues in `bp_read`. + let mut buf = [0; 0x400]; + self.bp_read(log.buf, &mut buf).await; + + while self.log.last_idx != idx as usize { + let b = buf[self.log.last_idx]; + if b == b'\r' || b == b'\n' { + if self.log.buf_count != 0 { + let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; + debug!("LOGS: {}", s); + self.log.buf_count = 0; + } + } else if self.log.buf_count < self.log.buf.len() { + self.log.buf[self.log.buf_count] = b; + self.log.buf_count += 1; + } + + self.log.last_idx += 1; + if self.log.last_idx == 0x400 { + self.log.last_idx = 0; + } + } + } + pub async fn run(mut self) -> ! { let mut buf = [0; 512]; loop { + #[cfg(feature = "firmware-logs")] + self.log_read().await; + // Send stuff // TODO flow control not yet complete if !self.has_credit() { diff --git a/src/structs.rs b/src/structs.rs index 6d4525a46..41a340661 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -18,6 +18,32 @@ macro_rules! impl_bytes { }; } +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct SharedMemData { + pub flags: u32, + pub trap_addr: u32, + pub assert_exp_addr: u32, + pub assert_file_addr: u32, + pub assert_line: u32, + pub console_addr: u32, + pub msgtrace_addr: u32, + pub fwid: u32, +} +impl_bytes!(SharedMemData); + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct SharedMemLog { + pub buf: u32, + pub buf_size: u32, + pub idx: u32, + pub out_idx: u32, +} +impl_bytes!(SharedMemLog); + #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] From f2fb9a2ca631b2f11888b9e71e2b7d279014ada1 Mon Sep 17 00:00:00 2001 From: Pedro Ferreira Date: Sun, 25 Dec 2022 23:49:04 +0000 Subject: [PATCH 0459/1575] Add missing SPI pins --- embassy-rp/src/spi.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 2b7a818d9..584370d56 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -473,6 +473,16 @@ impl_pin!(PIN_16, SPI0, MisoPin); impl_pin!(PIN_17, SPI0, CsPin); impl_pin!(PIN_18, SPI0, ClkPin); impl_pin!(PIN_19, SPI0, MosiPin); +impl_pin!(PIN_20, SPI0, MisoPin); +impl_pin!(PIN_21, SPI0, CsPin); +impl_pin!(PIN_22, SPI0, ClkPin); +impl_pin!(PIN_23, SPI0, MosiPin); +impl_pin!(PIN_24, SPI1, MisoPin); +impl_pin!(PIN_25, SPI1, CsPin); +impl_pin!(PIN_26, SPI1, ClkPin); +impl_pin!(PIN_27, SPI1, MosiPin); +impl_pin!(PIN_28, SPI1, MisoPin); +impl_pin!(PIN_29, SPI1, CsPin); macro_rules! impl_mode { ($name:ident) => { From 72bb9b53a239464500ad9dc5db462bcb7917d53a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 02:42:36 +0100 Subject: [PATCH 0460/1575] net: remove unused pool-x features --- .vscode/settings.json | 1 - ci.sh | 8 ++++---- ci_stable.sh | 4 ++-- embassy-net/Cargo.toml | 9 +-------- examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- 10 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index fd2e4afb1..6b8183f89 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,6 @@ // These are needed to prevent embassy-net from failing to build //"embassy-net/medium-ethernet", //"embassy-net/tcp", - //"embassy-net/pool-16", //"time-tick-16mhz", //"defmt-timestamp-uptime", //"nightly", diff --git a/ci.sh b/ci.sh index 9f79c87b9..14a48454d 100755 --- a/ci.sh +++ b/ci.sh @@ -38,10 +38,10 @@ cargo batch \ --- 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 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ diff --git a/ci_stable.sh b/ci_stable.sh index 0332c3faf..a1d507d71 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -13,8 +13,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- 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 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2d853762e..1faf603be 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] +features = ["defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] target = "thumbv7em-none-eabi" [features] @@ -28,13 +28,6 @@ proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] -pool-4 = [] -pool-8 = [] -pool-16 = [] -pool-32 = [] -pool-64 = [] -pool-128 = [] - [dependencies] defmt = { version = "0.3", optional = true } diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a980a505c..54a477b49 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -15,7 +15,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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"] } 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-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 31a08cfb6..57cddd432 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -11,7 +11,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 41680f8f4..649e39747 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 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"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index afdf87000..a8b2baf29 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 5acf0035d..63e6caa77 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-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"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index bb8515312..ce999f67e 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -12,7 +12,7 @@ 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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" From 5655c6093f8938c5ba637785b8bd572345f4c42e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 02:42:54 +0100 Subject: [PATCH 0461/1575] net: use atomic-polyfill on tcp client pool, for thumbv6m support. --- embassy-net/Cargo.toml | 1 + embassy-net/src/tcp.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 1faf603be..357f87922 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -45,6 +45,7 @@ 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" embedded-nal-async = { version = "0.3.0", optional = true } +atomic-polyfill = { version = "1.0" } [dependencies.smoltcp] version = "0.8.0" diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 55cbda455..0dc8da73a 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -329,8 +329,8 @@ pub mod client { use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; - use core::sync::atomic::{AtomicBool, Ordering}; + use atomic_polyfill::{AtomicBool, Ordering}; use embedded_nal_async::IpAddr; use super::*; From 639b3f1d5b4b2897b326edc52f66f18caaa3bd3e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 02:56:41 +0100 Subject: [PATCH 0462/1575] usb-driver: remove unused `log` feature. --- embassy-usb-driver/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index d22bf7d72..d658f9ec7 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -14,4 +14,3 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } From 1f033d509afb4e590a81896de66af683fda4e706 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 03:33:49 +0100 Subject: [PATCH 0463/1575] net: split driver trait to a separate crate. --- .github/workflows/doc.yml | 1 + .vscode/settings.json | 22 +-- embassy-net-driver/Cargo.toml | 15 ++ embassy-net-driver/src/lib.rs | 175 +++++++++++++++++++ embassy-net/Cargo.toml | 3 +- embassy-net/src/device.rs | 117 ++++--------- embassy-net/src/lib.rs | 17 +- embassy-net/src/tcp.rs | 12 +- embassy-net/src/udp.rs | 5 +- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/eth/mod.rs | 147 ++++++++-------- embassy-stm32/src/eth/v1/mod.rs | 4 +- embassy-usb/Cargo.toml | 2 +- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 14 +- embassy-usb/src/class/cdc_ncm/mod.rs | 1 - examples/nrf/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 1 + examples/std/src/tuntap.rs | 12 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- 22 files changed, 351 insertions(+), 211 deletions(-) create mode 100644 embassy-net-driver/Cargo.toml create mode 100644 embassy-net-driver/src/lib.rs diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index eb460e738..8a341b8f7 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -69,6 +69,7 @@ jobs: builder ./embassy-futures crates/embassy-futures/git.zup builder ./embassy-lora crates/embassy-lora/git.zup builder ./embassy-net crates/embassy-net/git.zup + builder ./embassy-net-driver crates/embassy-net-driver/git.zup builder ./embassy-nrf crates/embassy-nrf/git.zup builder ./embassy-rp crates/embassy-rp/git.zup builder ./embassy-sync crates/embassy-sync/git.zup diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b8183f89..086f435da 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,27 +4,21 @@ "rust-analyzer.checkOnSave.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.procMacro.enable": true, - //"rust-analyzer.cargo.target": "thumbv7em-none-eabi", - "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - // These are needed to prevent embassy-net from failing to build - //"embassy-net/medium-ethernet", - //"embassy-net/tcp", - //"time-tick-16mhz", - //"defmt-timestamp-uptime", - //"nightly", - //"unstable-traits", + "nightly", ], "rust-analyzer.linkedProjects": [ // Declare for the target you wish to develop - //"embassy-executor/Cargo.toml", - //"embassy-sync/Cargo.toml", - //"examples/nrf/Cargo.toml", + // "embassy-executor/Cargo.toml", + // "embassy-sync/Cargo.toml", + "examples/nrf/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", - //"examples/stm32f1/Cargo.toml", + // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", @@ -35,7 +29,7 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", - "examples/stm32l5/Cargo.toml", + // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml new file mode 100644 index 000000000..7ab9d1194 --- /dev/null +++ b/embassy-net-driver/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "embassy-net-driver" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + +[dependencies] +defmt = { version = "0.3", optional = true } \ No newline at end of file diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs new file mode 100644 index 000000000..a39cfecc1 --- /dev/null +++ b/embassy-net-driver/src/lib.rs @@ -0,0 +1,175 @@ +#![no_std] + +use core::task::Context; + +pub trait Driver { + type RxToken<'a>: RxToken + where + Self: 'a; + type TxToken<'a>: TxToken + where + Self: 'a; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + fn transmit(&mut self, cx: &mut Context) -> Option>; + fn link_state(&mut self, cx: &mut Context) -> LinkState; + + fn capabilities(&self) -> Capabilities; + fn ethernet_address(&self) -> [u8; 6]; +} + +impl Driver for &mut T { + type RxToken<'a> = T::RxToken<'a> + where + Self: 'a; + type TxToken<'a> = T::TxToken<'a> + where + Self: 'a; + + fn transmit(&mut self, cx: &mut Context) -> Option> { + T::transmit(self, cx) + } + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + T::receive(self, cx) + } + fn capabilities(&self) -> Capabilities { + T::capabilities(self) + } + fn link_state(&mut self, cx: &mut Context) -> LinkState { + T::link_state(self, cx) + } + fn ethernet_address(&self) -> [u8; 6] { + T::ethernet_address(self) + } +} + +/// A token to receive a single network packet. +pub trait RxToken { + /// Consumes the token to receive a single network packet. + /// + /// This method receives a packet and then calls the given closure `f` with the raw + /// packet bytes as argument. + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; +} + +/// A token to transmit a single network packet. +pub trait TxToken { + /// Consumes the token to send a single network packet. + /// + /// This method constructs a transmit buffer of size `len` and calls the passed + /// closure `f` with a mutable reference to that buffer. The closure should construct + /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure + /// returns, the transmit buffer is sent out. + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; +} + +/// A description of device capabilities. +/// +/// Higher-level protocols may achieve higher throughput or lower latency if they consider +/// the bandwidth or packet size limitations. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct Capabilities { + /// Medium of the device. + /// + /// This indicates what kind of packet the sent/received bytes are, and determines + /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done + /// for Ethernet mediums. + pub medium: Medium, + + /// Maximum transmission unit. + /// + /// The network device is unable to send or receive frames larger than the value returned + /// by this function. + /// + /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but + /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14. + /// + /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet + /// devices. This is a common source of confusion. + /// + /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets. + pub max_transmission_unit: usize, + + /// Maximum burst size, in terms of MTU. + /// + /// The network device is unable to send or receive bursts large than the value returned + /// by this function. + /// + /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are + /// dynamically allocated. + pub max_burst_size: Option, + + /// Checksum behavior. + /// + /// If the network device is capable of verifying or computing checksums for some protocols, + /// it can request that the stack not do so in software to improve performance. + pub checksum: ChecksumCapabilities, +} + +/// Type of medium of a device. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Medium { + /// Ethernet medium. Devices of this type send and receive Ethernet frames, + /// and interfaces using it must do neighbor discovery via ARP or NDISC. + /// + /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode. + Ethernet, + + /// IP medium. Devices of this type send and receive IP frames, without an + /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done. + /// + /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. + Ip, +} + +impl Default for Medium { + fn default() -> Medium { + Medium::Ethernet + } +} + +/// A description of checksum behavior for every supported protocol. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ChecksumCapabilities { + pub ipv4: Checksum, + pub udp: Checksum, + pub tcp: Checksum, + pub icmpv4: Checksum, + pub icmpv6: Checksum, +} + +/// A description of checksum behavior for a particular protocol. +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Checksum { + /// Verify checksum when receiving and compute checksum when sending. + Both, + /// Verify checksum when receiving. + Rx, + /// Compute checksum before sending. + Tx, + /// Ignore checksum completely. + None, +} + +impl Default for Checksum { + fn default() -> Checksum { + Checksum::Both + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum LinkState { + Down, + Up, +} diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 357f87922..9214fd17e 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -15,7 +15,7 @@ target = "thumbv7em-none-eabi" default = [] std = [] -defmt = ["dep:defmt", "smoltcp/defmt"] +defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"] unstable-traits = [] @@ -33,6 +33,7 @@ medium-ip = ["smoltcp/medium-ip"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 5d86ca91e..44f7dc7bd 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -1,93 +1,20 @@ use core::task::Context; +use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken}; use smoltcp::phy; -pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium}; -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum LinkState { - Down, - Up, -} - -pub trait Device { - type RxToken<'a>: RxToken - where - Self: 'a; - type TxToken<'a>: TxToken - where - Self: 'a; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; - fn transmit(&mut self, cx: &mut Context) -> Option>; - fn link_state(&mut self, cx: &mut Context) -> LinkState; - - fn capabilities(&self) -> phy::DeviceCapabilities; - fn ethernet_address(&self) -> [u8; 6]; -} - -impl Device for &mut T { - type RxToken<'a> = T::RxToken<'a> - where - Self: 'a; - type TxToken<'a> = T::TxToken<'a> - where - Self: 'a; - - fn transmit(&mut self, cx: &mut Context) -> Option> { - T::transmit(self, cx) - } - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - T::receive(self, cx) - } - fn capabilities(&self) -> phy::DeviceCapabilities { - T::capabilities(self) - } - fn link_state(&mut self, cx: &mut Context) -> LinkState { - T::link_state(self, cx) - } - fn ethernet_address(&self) -> [u8; 6] { - T::ethernet_address(self) - } -} - -/// A token to receive a single network packet. -pub trait RxToken { - /// Consumes the token to receive a single network packet. - /// - /// This method receives a packet and then calls the given closure `f` with the raw - /// packet bytes as argument. - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R; -} - -/// A token to transmit a single network packet. -pub trait TxToken { - /// Consumes the token to send a single network packet. - /// - /// This method constructs a transmit buffer of size `len` and calls the passed - /// closure `f` with a mutable reference to that buffer. The closure should construct - /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure - /// returns, the transmit buffer is sent out. - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R; -} - -/////////////////////////// - -pub(crate) struct DeviceAdapter<'d, 'c, T> +pub(crate) struct DriverAdapter<'d, 'c, T> where - T: Device, + T: Driver, { // must be Some when actually using this to rx/tx pub cx: Option<&'d mut Context<'c>>, pub inner: &'d mut T, } -impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T> +impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T> where - T: Device, + T: Driver, { type RxToken<'a> = RxTokenAdapter> where Self: 'a; type TxToken<'a> = TxTokenAdapter> where Self: 'a; @@ -105,7 +32,39 @@ where /// Get a description of device capabilities. fn capabilities(&self) -> phy::DeviceCapabilities { - self.inner.capabilities() + fn convert(c: Checksum) -> phy::Checksum { + match c { + Checksum::Both => phy::Checksum::Both, + Checksum::Tx => phy::Checksum::Tx, + Checksum::Rx => phy::Checksum::Rx, + Checksum::None => phy::Checksum::None, + } + } + let caps: Capabilities = self.inner.capabilities(); + let mut smolcaps = phy::DeviceCapabilities::default(); + + smolcaps.max_transmission_unit = caps.max_transmission_unit; + smolcaps.max_burst_size = caps.max_burst_size; + smolcaps.medium = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => phy::Medium::Ethernet, + #[cfg(feature = "medium-ip")] + Medium::Ip => phy::Medium::Ip, + _ => panic!( + "Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.", + caps.medium + ), + }; + smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); + #[cfg(feature = "proto-ipv6")] + { + smolcaps.checksum.ipv6 = convert(caps.checksum.ipv6); + } + smolcaps.checksum.tcp = convert(caps.checksum.tcp); + smolcaps.checksum.udp = convert(caps.checksum.udp); + smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + + smolcaps } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index afe0d6da0..b58c9cf36 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -18,6 +18,7 @@ use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; +use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; @@ -27,8 +28,6 @@ use smoltcp::iface::SocketHandle; use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; #[cfg(feature = "medium-ethernet")] use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::phy::Medium; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; // smoltcp reexports @@ -41,7 +40,7 @@ pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; #[cfg(feature = "udp")] pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; -use crate::device::{Device, DeviceAdapter, LinkState}; +use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; @@ -82,12 +81,12 @@ pub enum ConfigStrategy { Dhcp, } -pub struct Stack { +pub struct Stack { pub(crate) socket: RefCell, inner: RefCell>, } -struct Inner { +struct Inner { device: D, link_up: bool, config: Option, @@ -102,7 +101,7 @@ pub(crate) struct SocketStack { next_local_port: u16, } -impl Stack { +impl Stack { pub fn new( mut device: D, config: ConfigStrategy, @@ -130,7 +129,7 @@ impl Stack { b = b.routes(Routes::new(&mut resources.routes[..])); } - let iface = b.finalize(&mut DeviceAdapter { + let iface = b.finalize(&mut DriverAdapter { inner: &mut device, cx: None, }); @@ -211,7 +210,7 @@ impl SocketStack { } } -impl Inner { +impl Inner { fn apply_config(&mut self, s: &mut SocketStack, config: Config) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -263,7 +262,7 @@ impl Inner { s.waker.register(cx.waker()); let timestamp = instant_to_smoltcp(Instant::now()); - let mut smoldev = DeviceAdapter { + let mut smoldev = DriverAdapter { cx: Some(cx), inner: &mut self.device, }; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0dc8da73a..0fbf0c91b 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -3,12 +3,12 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::device::Device; use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -66,7 +66,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 { + pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { 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) }; @@ -336,19 +336,19 @@ pub mod client { use super::*; /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ. - pub struct TcpClient<'d, D: Device, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { + pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { stack: &'d Stack, state: &'d TcpClientState, } - impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { + impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { /// Create a new TcpClient pub fn new(stack: &'d Stack, state: &'d TcpClientState) -> Self { Self { stack, state } } } - impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect + impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect for TcpClient<'d, D, N, TX_SZ, RX_SZ> { type Error = Error; @@ -386,7 +386,7 @@ pub mod client { } impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> { - fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { + fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?; Ok(Self { socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) }, diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 2f5334df3..0ee8c6e19 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -3,11 +3,12 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::{Device, SocketStack, Stack}; +use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -31,7 +32,7 @@ pub struct UdpSocket<'a> { } impl<'a> UdpSocket<'a> { - pub fn new( + pub fn new( stack: &'a Stack, rx_meta: &'a mut [PacketMetadata], rx_buffer: &'a mut [u8], diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0c491ee46..67996cca4 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -39,7 +39,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } @@ -75,7 +75,7 @@ quote = "1.0.15" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} [features] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] sdmmc-rs = ["embedded-sdmmc"] memory-x = ["stm32-metapac/memory-x"] subghz = [] diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index fd1b48530..9f62b61ee 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -1,14 +1,17 @@ #![macro_use] -#![cfg_attr(not(feature = "embassy-net"), allow(unused))] #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] #[cfg_attr(eth_v2, path = "v2/mod.rs")] mod _version; pub mod generic_smi; -pub use _version::*; +use core::task::Context; + +use embassy_net_driver::{Capabilities, LinkState}; use embassy_sync::waitqueue::AtomicWaker; +pub use self::_version::*; + #[allow(unused)] const MTU: usize = 1514; const TX_BUFFER_SIZE: usize = 1514; @@ -40,92 +43,84 @@ impl PacketQueue { static WAKER: AtomicWaker = AtomicWaker::new(); -#[cfg(feature = "embassy-net")] -mod embassy_net_impl { - use core::task::Context; +impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> { + type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - use embassy_net::device::{Device, DeviceCapabilities, LinkState}; - - use super::*; - - impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> { - type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; - type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - WAKER.register(cx.waker()); - if self.rx.available().is_some() && self.tx.available().is_some() { - Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) - } else { - None - } - } - - fn transmit(&mut self, cx: &mut Context) -> Option> { - WAKER.register(cx.waker()); - if self.tx.available().is_some() { - Some(TxToken { tx: &mut self.tx }) - } else { - None - } - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(self.tx.len()); - caps - } - - fn link_state(&mut self, cx: &mut Context) -> LinkState { - // TODO: wake cx.waker on link state change - cx.waker().wake_by_ref(); - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + WAKER.register(cx.waker()); + if self.rx.available().is_some() && self.tx.available().is_some() { + Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + } else { + None } } - pub struct RxToken<'a, 'd> { - rx: &'a mut RDesRing<'d>, - } - - impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.rx.available()); - let r = f(pkt); - self.rx.pop_packet(); - r + fn transmit(&mut self, cx: &mut Context) -> Option> { + WAKER.register(cx.waker()); + if self.tx.available().is_some() { + Some(TxToken { tx: &mut self.tx }) + } else { + None } } - pub struct TxToken<'a, 'd> { - tx: &'a mut TDesRing<'d>, + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(self.tx.len()); + caps } - impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.tx.available()); - let r = f(&mut pkt[..len]); - self.tx.transmit(len); - r + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // TODO: wake cx.waker on link state change + cx.waker().wake_by_ref(); + if P::poll_link(self) { + LinkState::Up + } else { + LinkState::Down } } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } } + +pub struct RxToken<'a, 'd> { + rx: &'a mut RDesRing<'d>, +} + +impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.available()); + let r = f(pkt); + self.rx.pop_packet(); + r + } +} + +pub struct TxToken<'a, 'd> { + tx: &'a mut TDesRing<'d>, +} + +impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.available()); + let r = f(&mut pkt[..len]); + self.tx.transmit(len); + r + } +} + /// Station Management Interface (SMI) on an ethernet PHY /// /// # Safety diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index de36d3da1..9c0f4d66d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -13,7 +13,7 @@ pub(crate) use self::rx_desc::{RDes, RDesRing}; pub(crate) use self::tx_desc::{TDes, TDesRing}; use super::*; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; -use crate::gpio::{AnyPin, Speed}; +use crate::gpio::AnyPin; #[cfg(eth_v1a)] use crate::pac::AFIO; #[cfg(any(eth_v1b, eth_v1c))] @@ -66,7 +66,7 @@ macro_rules! config_pins { critical_section::with(|_| { $( $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); - $pin.set_speed(Speed::VeryHigh); + $pin.set_speed(crate::gpio::Speed::VeryHigh); )* }) }; diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1e72ce682..d0742ceb3 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -19,7 +19,7 @@ default = ["usbd-hid"] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 60bbfd8d4..093afeff9 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -3,7 +3,7 @@ use core::mem::MaybeUninit; use core::task::Context; use embassy_futures::select::{select, Either}; -use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium}; +use embassy_net_driver::{Capabilities, LinkState, Medium}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_sync::waitqueue::WakerRegistration; @@ -108,7 +108,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { let (tx_usb, rx_usb) = self.split(); - let mut caps = DeviceCapabilities::default(); + let mut caps = Capabilities::default(); caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header caps.medium = Medium::Ethernet; @@ -158,11 +158,11 @@ pub struct Device<'d, const MTU: usize> { rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, link_state: &'d Mutex>, - caps: DeviceCapabilities, + caps: Capabilities, ethernet_address: [u8; 6], } -impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { +impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; @@ -184,7 +184,7 @@ impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { } /// Get a description of device capabilities. - fn capabilities(&self) -> DeviceCapabilities { + fn capabilities(&self) -> Capabilities { self.caps.clone() } @@ -205,7 +205,7 @@ pub struct RxToken<'a, const MTU: usize> { rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, } -impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> { +impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { fn consume(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -222,7 +222,7 @@ pub struct TxToken<'a, const MTU: usize> { tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, } -impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> { +impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { fn consume(mut self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 2ee47f68c..4954a65bc 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -21,7 +21,6 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; use crate::Builder; -#[cfg(feature = "embassy-net")] pub mod embassy_net; /// This should be used as `device_class` when building the `UsbDevice`. diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 54a477b49..994823a9e 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -16,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 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"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57cddd432..afd1042a1 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,7 +10,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } +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"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 649e39747..45b2a4a4f 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,6 +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"] } +embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index bb3e194cc..d918a2e62 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs @@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::task::Context; use async_io::Async; -use embassy_net::device::{self, Device, DeviceCapabilities, LinkState}; +use embassy_net_driver::{self, Capabilities, Driver, LinkState}; use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -137,7 +137,7 @@ impl TunTapDevice { } } -impl Device for TunTapDevice { +impl Driver for TunTapDevice { type RxToken<'a> = RxToken where Self: 'a; type TxToken<'a> = TxToken<'a> where Self: 'a; @@ -170,8 +170,8 @@ impl Device for TunTapDevice { }) } - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); caps.max_transmission_unit = self.device.get_ref().mtu; caps } @@ -190,7 +190,7 @@ pub struct RxToken { buffer: Vec, } -impl device::RxToken for RxToken { +impl embassy_net_driver::RxToken for RxToken { fn consume(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -204,7 +204,7 @@ pub struct TxToken<'a> { device: &'a mut Async, } -impl<'a> device::TxToken for TxToken<'a> { +impl<'a> embassy_net_driver::TxToken for TxToken<'a> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a8b2baf29..b80dbbf9c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" 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", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 63e6caa77..d30c42b1f 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" 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", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index ce999f67e..c0accb0d6 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } +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"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" From 007246b16036c3aa874e78d0665567a27ab35fa9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 04:31:49 +0100 Subject: [PATCH 0464/1575] net: split channel-based driver impl from usb cdc-ncm into a separate crate. --- embassy-net-driver-channel/Cargo.toml | 12 + embassy-net-driver-channel/src/fmt.rs | 225 ++++++++ embassy-net-driver-channel/src/lib.rs | 525 +++++++++++++++++++ embassy-usb/Cargo.toml | 2 +- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 408 +------------- 5 files changed, 787 insertions(+), 385 deletions(-) create mode 100644 embassy-net-driver-channel/Cargo.toml create mode 100644 embassy-net-driver-channel/src/fmt.rs create mode 100644 embassy-net-driver-channel/src/lib.rs diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml new file mode 100644 index 000000000..700a4e8a0 --- /dev/null +++ b/embassy-net-driver-channel/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "embassy-net-driver-channel" +version = "0.1.0" +edition = "2021" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } diff --git a/embassy-net-driver-channel/src/fmt.rs b/embassy-net-driver-channel/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-net-driver-channel/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-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs new file mode 100644 index 000000000..369dc5a9d --- /dev/null +++ b/embassy-net-driver-channel/src/lib.rs @@ -0,0 +1,525 @@ +#![no_std] + +// must go first! +mod fmt; + +use core::cell::RefCell; +use core::mem::MaybeUninit; +use core::task::{Context, Poll}; + +pub use embassy_net_driver as driver; +use embassy_net_driver::{Capabilities, LinkState, Medium}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::waitqueue::WakerRegistration; + +pub struct State { + rx: [PacketBuf; N_RX], + tx: [PacketBuf; N_TX], + inner: MaybeUninit>, +} + +impl State { + const NEW_PACKET: PacketBuf = PacketBuf::new(); + + pub const fn new() -> Self { + Self { + rx: [Self::NEW_PACKET; N_RX], + tx: [Self::NEW_PACKET; N_TX], + inner: MaybeUninit::uninit(), + } + } +} + +struct StateInner<'d, const MTU: usize> { + rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + link_state: Mutex>, +} + +/// State of the LinkState +struct LinkStateState { + state: LinkState, + waker: WakerRegistration, +} + +pub struct Runner<'d, const MTU: usize> { + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +pub struct RxRunner<'d, const MTU: usize> { + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +pub struct TxRunner<'d, const MTU: usize> { + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, +} + +impl<'d, const MTU: usize> Runner<'d, MTU> { + pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) { + ( + RxRunner { + link_state: self.link_state, + rx_chan: self.rx_chan, + }, + TxRunner { tx_chan: self.tx_chan }, + ) + } + + pub fn set_link_state(&mut self, state: LinkState) { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = state; + s.waker.wake(); + }); + } + + pub async fn rx_buf(&mut self) -> &mut [u8] { + let p = self.rx_chan.send().await; + &mut p.buf + } + + pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.rx_chan.try_send()?; + Some(&mut p.buf) + } + + pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.rx_chan.poll_send(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf), + Poll::Pending => Poll::Pending, + } + } + + pub fn rx_done(&mut self, len: usize) { + let p = self.rx_chan.try_send().unwrap(); + p.len = len; + self.rx_chan.send_done(); + } + + pub async fn tx_buf(&mut self) -> &mut [u8] { + let p = self.tx_chan.recv().await; + &mut p.buf[..p.len] + } + + pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.tx_chan.try_recv()?; + Some(&mut p.buf[..p.len]) + } + + pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.tx_chan.poll_recv(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), + Poll::Pending => Poll::Pending, + } + } + + pub fn tx_done(&mut self) { + self.tx_chan.recv_done(); + } +} + +impl<'d, const MTU: usize> RxRunner<'d, MTU> { + pub fn set_link_state(&mut self, state: LinkState) { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = state; + s.waker.wake(); + }); + } + + pub async fn rx_buf(&mut self) -> &mut [u8] { + let p = self.rx_chan.send().await; + &mut p.buf + } + + pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.rx_chan.try_send()?; + Some(&mut p.buf) + } + + pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.rx_chan.poll_send(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf), + Poll::Pending => Poll::Pending, + } + } + + pub fn rx_done(&mut self, len: usize) { + let p = self.rx_chan.try_send().unwrap(); + p.len = len; + self.rx_chan.send_done(); + } +} + +impl<'d, const MTU: usize> TxRunner<'d, MTU> { + pub async fn tx_buf(&mut self) -> &mut [u8] { + let p = self.tx_chan.recv().await; + &mut p.buf[..p.len] + } + + pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.tx_chan.try_recv()?; + Some(&mut p.buf[..p.len]) + } + + pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.tx_chan.poll_recv(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), + Poll::Pending => Poll::Pending, + } + } + + pub fn tx_done(&mut self) { + self.tx_chan.recv_done(); + } +} + +pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( + state: &'d mut State, + ethernet_address: [u8; 6], +) -> (Runner<'d, MTU>, Device<'d, MTU>) { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.medium = Medium::Ethernet; + + // safety: this is a self-referential struct, however: + // - it can't move while the `'d` borrow is active. + // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again. + let state_uninit: *mut MaybeUninit> = + (&mut state.inner as *mut MaybeUninit>).cast(); + let state = unsafe { &mut *state_uninit }.write(StateInner { + rx: zerocopy_channel::Channel::new(&mut state.rx[..]), + tx: zerocopy_channel::Channel::new(&mut state.tx[..]), + link_state: Mutex::new(RefCell::new(LinkStateState { + state: LinkState::Down, + waker: WakerRegistration::new(), + })), + }); + + let (rx_sender, rx_receiver) = state.rx.split(); + let (tx_sender, tx_receiver) = state.tx.split(); + + ( + Runner { + tx_chan: tx_receiver, + rx_chan: rx_sender, + link_state: &state.link_state, + }, + Device { + caps, + ethernet_address, + link_state: &state.link_state, + rx: rx_receiver, + tx: tx_sender, + }, + ) +} + +pub struct PacketBuf { + len: usize, + buf: [u8; MTU], +} + +impl PacketBuf { + pub const fn new() -> Self { + Self { len: 0, buf: [0; MTU] } + } +} + +pub struct Device<'d, const MTU: usize> { + rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, + caps: Capabilities, + ethernet_address: [u8; 6], +} + +impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { + type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; + type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { + Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) + } else { + None + } + } + + /// Construct a transmit token. + fn transmit(&mut self, cx: &mut Context) -> Option> { + if self.tx.poll_send(cx).is_ready() { + Some(TxToken { tx: self.tx.borrow() }) + } else { + None + } + } + + /// Get a description of device capabilities. + fn capabilities(&self) -> Capabilities { + self.caps.clone() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.ethernet_address + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.waker.register(cx.waker()); + s.state + }) + } +} + +pub struct RxToken<'a, const MTU: usize> { + rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.try_recv()); + let r = f(&mut pkt.buf[..pkt.len]); + self.rx.recv_done(); + r + } +} + +pub struct TxToken<'a, const MTU: usize> { + tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { + fn consume(mut self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.try_send()); + let r = f(&mut pkt.buf[..len]); + pkt.len = len; + self.tx.send_done(); + r + } +} + +mod zerocopy_channel { + use core::cell::RefCell; + use core::future::poll_fn; + use core::marker::PhantomData; + use core::task::{Context, Poll}; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use embassy_sync::blocking_mutex::Mutex; + use embassy_sync::waitqueue::WakerRegistration; + + pub struct Channel<'a, M: RawMutex, T> { + buf: *mut T, + phantom: PhantomData<&'a mut T>, + state: Mutex>, + } + + impl<'a, M: RawMutex, T> Channel<'a, M, T> { + pub fn new(buf: &'a mut [T]) -> Self { + let len = buf.len(); + assert!(len != 0); + + Self { + buf: buf.as_mut_ptr(), + phantom: PhantomData, + state: Mutex::new(RefCell::new(State { + len, + front: 0, + back: 0, + full: false, + send_waker: WakerRegistration::new(), + recv_waker: WakerRegistration::new(), + })), + } + } + + pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { + (Sender { channel: self }, Receiver { channel: self }) + } + } + + pub struct Sender<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Sender<'a, M, T> { + pub fn borrow(&mut self) -> Sender<'_, M, T> { + Sender { channel: self.channel } + } + + pub fn try_send(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn send(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(i), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn send_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().push_done()) + } + } + pub struct Receiver<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Receiver<'a, M, T> { + pub fn borrow(&mut self) -> Receiver<'_, M, T> { + Receiver { channel: self.channel } + } + + pub fn try_recv(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn recv(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(i), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn recv_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().pop_done()) + } + } + + struct State { + len: usize, + + /// Front index. Always 0..=(N-1) + front: usize, + /// Back index. Always 0..=(N-1). + back: usize, + + /// Used to distinguish "empty" and "full" cases when `front == back`. + /// May only be `true` if `front == back`, always `false` otherwise. + full: bool, + + send_waker: WakerRegistration, + recv_waker: WakerRegistration, + } + + impl State { + fn increment(&self, i: usize) -> usize { + if i + 1 == self.len { + 0 + } else { + i + 1 + } + } + + fn is_full(&self) -> bool { + self.full + } + + fn is_empty(&self) -> bool { + self.front == self.back && !self.full + } + + fn push_index(&mut self) -> Option { + match self.is_full() { + true => None, + false => Some(self.back), + } + } + + fn push_done(&mut self) { + assert!(!self.is_full()); + self.back = self.increment(self.back); + if self.back == self.front { + self.full = true; + } + self.send_waker.wake(); + } + + fn pop_index(&mut self) -> Option { + match self.is_empty() { + true => None, + false => Some(self.front), + } + } + + fn pop_done(&mut self) { + assert!(!self.is_empty()); + self.front = self.increment(self.front); + self.full = false; + self.recv_waker.wake(); + } + } +} diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index d0742ceb3..1e567bb94 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -19,7 +19,7 @@ default = ["usbd-hid"] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 093afeff9..7ecf693d2 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -1,81 +1,45 @@ -use core::cell::RefCell; -use core::mem::MaybeUninit; -use core::task::Context; - use embassy_futures::select::{select, Either}; -use embassy_net_driver::{Capabilities, LinkState, Medium}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::blocking_mutex::Mutex; -use embassy_sync::waitqueue::WakerRegistration; +use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; use embassy_usb_driver::Driver; use super::{CdcNcmClass, Receiver, Sender}; -pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> { - rx: [PacketBuf; N_RX], - tx: [PacketBuf; N_TX], - inner: MaybeUninit>, +pub struct State { + ch_state: ch::State, } -impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { - const NEW_PACKET: PacketBuf = PacketBuf::new(); - +impl State { pub const fn new() -> Self { Self { - rx: [Self::NEW_PACKET; N_RX], - tx: [Self::NEW_PACKET; N_TX], - inner: MaybeUninit::uninit(), + ch_state: ch::State::new(), } } } -struct StateInner<'d, const MTU: usize> { - rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - link_state: Mutex>, -} - -/// State of the LinkState -struct LinkStateState { - state: LinkState, - waker: WakerRegistration, -} - pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { tx_usb: Sender<'d, D>, - tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, rx_usb: Receiver<'d, D>, - rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + ch: ch::Runner<'d, MTU>, } impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { pub async fn run(mut self) -> ! { + let (mut rx_chan, mut tx_chan) = self.ch.split(); let rx_fut = async move { loop { trace!("WAITING for connection"); - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.state = LinkState::Down; - s.waker.wake(); - }); + rx_chan.set_link_state(LinkState::Down); self.rx_usb.wait_connection().await.unwrap(); trace!("Connected"); - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.state = LinkState::Up; - s.waker.wake(); - }); + rx_chan.set_link_state(LinkState::Up); loop { - let p = self.rx_chan.send().await; - match self.rx_usb.read_packet(&mut p.buf).await { - Ok(n) => { - p.len = n; - self.rx_chan.send_done(); - } + let p = rx_chan.rx_buf().await; + match self.rx_usb.read_packet(p).await { + Ok(n) => rx_chan.rx_done(n), Err(e) => { warn!("error reading packet: {:?}", e); break; @@ -86,11 +50,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { }; let tx_fut = async move { loop { - let p = self.tx_chan.recv().await; - if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await { + let p = tx_chan.tx_buf().await; + if let Err(e) = self.tx_usb.write_packet(p).await { warn!("Failed to TX packet: {:?}", e); } - self.tx_chan.recv_done(); + tx_chan.tx_done(); } }; match select(rx_fut, tx_fut).await { @@ -100,350 +64,26 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { } } +// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? +//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; +pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; + impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { pub fn into_embassy_net_device( self, - state: &'d mut State<'d, MTU, N_RX, N_TX>, + state: &'d mut State, ethernet_address: [u8; 6], ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { let (tx_usb, rx_usb) = self.split(); - - let mut caps = Capabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = Medium::Ethernet; - - let state = state.inner.write(StateInner { - rx: zerocopy_channel::Channel::new(&mut state.rx[..]), - tx: zerocopy_channel::Channel::new(&mut state.tx[..]), - link_state: Mutex::new(RefCell::new(LinkStateState { - state: LinkState::Down, - waker: WakerRegistration::new(), - })), - }); - - let (rx_sender, rx_receiver) = state.rx.split(); - let (tx_sender, tx_receiver) = state.tx.split(); + let (runner, device) = ch::new(&mut state.ch_state, ethernet_address); ( Runner { tx_usb, - tx_chan: tx_receiver, rx_usb, - rx_chan: rx_sender, - link_state: &state.link_state, - }, - Device { - caps, - ethernet_address, - link_state: &state.link_state, - rx: rx_receiver, - tx: tx_sender, + ch: runner, }, + device, ) } } - -pub struct PacketBuf { - len: usize, - buf: [u8; MTU], -} - -impl PacketBuf { - pub const fn new() -> Self { - Self { len: 0, buf: [0; MTU] } - } -} - -pub struct Device<'d, const MTU: usize> { - rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, - tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, - caps: Capabilities, - ethernet_address: [u8; 6], -} - -impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { - type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; - type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { - Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) - } else { - None - } - } - - /// Construct a transmit token. - fn transmit(&mut self, cx: &mut Context) -> Option> { - if self.tx.poll_send(cx).is_ready() { - Some(TxToken { tx: self.tx.borrow() }) - } else { - None - } - } - - /// Get a description of device capabilities. - fn capabilities(&self) -> Capabilities { - self.caps.clone() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.ethernet_address - } - - fn link_state(&mut self, cx: &mut Context) -> LinkState { - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.waker.register(cx.waker()); - s.state - }) - } -} - -pub struct RxToken<'a, const MTU: usize> { - rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, -} - -impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { - fn consume(mut self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.rx.try_recv()); - let r = f(&mut pkt.buf[..pkt.len]); - self.rx.recv_done(); - r - } -} - -pub struct TxToken<'a, const MTU: usize> { - tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, -} - -impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { - fn consume(mut self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.tx.try_send()); - let r = f(&mut pkt.buf[..len]); - pkt.len = len; - self.tx.send_done(); - r - } -} - -mod zerocopy_channel { - use core::cell::RefCell; - use core::future::poll_fn; - use core::marker::PhantomData; - use core::task::{Context, Poll}; - - use embassy_sync::blocking_mutex::raw::RawMutex; - use embassy_sync::blocking_mutex::Mutex; - use embassy_sync::waitqueue::WakerRegistration; - - pub struct Channel<'a, M: RawMutex, T> { - buf: *mut T, - phantom: PhantomData<&'a mut T>, - state: Mutex>, - } - - impl<'a, M: RawMutex, T> Channel<'a, M, T> { - pub fn new(buf: &'a mut [T]) -> Self { - let len = buf.len(); - assert!(len != 0); - - Self { - buf: buf.as_mut_ptr(), - phantom: PhantomData, - state: Mutex::new(RefCell::new(State { - len, - front: 0, - back: 0, - full: false, - send_waker: WakerRegistration::new(), - recv_waker: WakerRegistration::new(), - })), - } - } - - pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { - (Sender { channel: self }, Receiver { channel: self }) - } - } - - pub struct Sender<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Sender<'a, M, T> { - pub fn borrow(&mut self) -> Sender<'_, M, T> { - Sender { channel: self.channel } - } - - pub fn try_send(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn send(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(i), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn send_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().push_done()) - } - } - pub struct Receiver<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Receiver<'a, M, T> { - pub fn borrow(&mut self) -> Receiver<'_, M, T> { - Receiver { channel: self.channel } - } - - pub fn try_recv(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn recv(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(i), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn recv_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().pop_done()) - } - } - - struct State { - len: usize, - - /// Front index. Always 0..=(N-1) - front: usize, - /// Back index. Always 0..=(N-1). - back: usize, - - /// Used to distinguish "empty" and "full" cases when `front == back`. - /// May only be `true` if `front == back`, always `false` otherwise. - full: bool, - - send_waker: WakerRegistration, - recv_waker: WakerRegistration, - } - - impl State { - fn increment(&self, i: usize) -> usize { - if i + 1 == self.len { - 0 - } else { - i + 1 - } - } - - fn is_full(&self) -> bool { - self.full - } - - fn is_empty(&self) -> bool { - self.front == self.back && !self.full - } - - fn push_index(&mut self) -> Option { - match self.is_full() { - true => None, - false => Some(self.back), - } - } - - fn push_done(&mut self) { - assert!(!self.is_full()); - self.back = self.increment(self.back); - if self.back == self.front { - self.full = true; - } - self.send_waker.wake(); - } - - fn pop_index(&mut self) -> Option { - match self.is_empty() { - true => None, - false => Some(self.front), - } - } - - fn pop_done(&mut self) { - assert!(!self.is_empty()); - self.front = self.increment(self.front); - self.full = false; - self.recv_waker.wake(); - } - } -} From 97f9f248f4ca09ab8b2bbb3806d9c23ccc038224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 26 Dec 2022 09:35:23 +0100 Subject: [PATCH 0465/1575] usb cdc-acm: Remove unused call management descriptor --- embassy-usb/src/class/cdc_acm.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 09bb1cc8d..7f66c5d52 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -18,7 +18,6 @@ const CDC_PROTOCOL_NONE: u8 = 0x00; const CS_INTERFACE: u8 = 0x24; const CDC_TYPE_HEADER: u8 = 0x00; -const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; const CDC_TYPE_ACM: u8 = 0x02; const CDC_TYPE_UNION: u8 = 0x06; @@ -197,14 +196,6 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { data_if.into(), // bSubordinateInterface ], ); - alt.descriptor( - CS_INTERFACE, - &[ - CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype - 0x00, // bmCapabilities - data_if.into(), // bDataInterface - ], - ); let comm_ep = alt.endpoint_interrupt_in(8, 255); From 4e0d563997017492aa46025e8809653c14e0f875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 26 Dec 2022 09:36:04 +0100 Subject: [PATCH 0466/1575] usb cdc-acm: Set flag for supported capabilities --- embassy-usb/src/class/cdc_acm.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 7f66c5d52..84db20621 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -185,7 +185,10 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { CS_INTERFACE, &[ CDC_TYPE_ACM, // bDescriptorSubtype - 0x00, // bmCapabilities + 0x02, // bmCapabilities: + // D1: Device supports the request combination of + // Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, + // and the Notification Serial_State. ], ); alt.descriptor( From 41d63169849e380a353a524c4b3591a3551bc4a8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 22:30:22 +0100 Subject: [PATCH 0467/1575] rp: switch to released 0.2.1 pio crate. --- embassy-rp/Cargo.toml | 5 +---- examples/rp/Cargo.toml | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 6d5bc91ee..209c665b0 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -71,7 +71,4 @@ embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} paste = "1.0" pio-proc = {version= "0.2", optional = true} -pio = {version= "0.2", optional = true} - -[patch.crates-io] -pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file +pio = {version= "0.2.1", optional = true} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index afd1042a1..f07684f29 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -36,10 +36,7 @@ embedded-storage = { version = "0.3" } static_cell = "1.0.0" log = "0.4" pio-proc = "0.2" -pio = "0.2" +pio = "0.2.1" [profile.release] debug = true - -[patch.crates-io] -pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file From 1b6799d93f0bbd6154c124d51aa47aeed0acf15d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Dec 2022 23:21:58 +0100 Subject: [PATCH 0468/1575] split bus, consts into separate mods. --- src/bus.rs | 321 ++++++++++++++++++++++++++++++ src/consts.rs | 105 ++++++++++ src/lib.rs | 536 +++++++------------------------------------------- 3 files changed, 496 insertions(+), 466 deletions(-) create mode 100644 src/bus.rs create mode 100644 src/consts.rs diff --git a/src/bus.rs b/src/bus.rs new file mode 100644 index 000000000..f220cffcd --- /dev/null +++ b/src/bus.rs @@ -0,0 +1,321 @@ +use core::slice; + +use embassy_time::{Duration, Timer}; +use embedded_hal_1::digital::OutputPin; +use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice}; + +use crate::consts::*; + +pub(crate) struct Bus { + backplane_window: u32, + pwr: PWR, + spi: SPI, +} + +impl Bus +where + PWR: OutputPin, + SPI: SpiDevice, + SPI::Bus: SpiBusRead + SpiBusWrite, +{ + pub(crate) fn new(pwr: PWR, spi: SPI) -> Self { + Self { + backplane_window: 0xAAAA_AAAA, + pwr, + spi, + } + } + + pub async fn init(&mut self) { + // Reset + self.pwr.set_low().unwrap(); + Timer::after(Duration::from_millis(20)).await; + self.pwr.set_high().unwrap(); + Timer::after(Duration::from_millis(250)).await; + + while self.read32_swapped(REG_BUS_TEST_RO).await != FEEDBEAD {} + + self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await; + let val = self.read32_swapped(REG_BUS_TEST_RW).await; + assert_eq!(val, TEST_PATTERN); + + // 32-bit word length, little endian (which is the default endianess). + self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await; + + let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await; + assert_eq!(val, FEEDBEAD); + let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await; + assert_eq!(val, TEST_PATTERN); + } + + pub async fn wlan_read(&mut self, buf: &mut [u32]) { + let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + bus.read(buf).await?; + Ok(()) + }) + .await + .unwrap(); + } + + pub async fn wlan_write(&mut self, buf: &[u32]) { + let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4); + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + bus.write(buf).await?; + Ok(()) + }) + .await + .unwrap(); + } + + #[allow(unused)] + pub async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { + // It seems the HW force-aligns the addr + // to 2 if data.len() >= 2 + // to 4 if data.len() >= 4 + // To simplify, enforce 4-align for now. + assert!(addr % 4 == 0); + + let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; + + while !data.is_empty() { + // Ensure transfer doesn't cross a window boundary. + let window_offs = addr & BACKPLANE_ADDRESS_MASK; + let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; + + let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); + + self.backplane_set_window(addr).await; + + let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); + + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + + // 4-byte response delay. + let mut junk = [0; 1]; + bus.read(&mut junk).await?; + + // Read data + bus.read(&mut buf[..(len + 3) / 4]).await?; + Ok(()) + }) + .await + .unwrap(); + + data[..len].copy_from_slice(&slice8_mut(&mut buf)[..len]); + + // Advance ptr. + addr += len as u32; + data = &mut data[len..]; + } + } + + pub async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { + // It seems the HW force-aligns the addr + // to 2 if data.len() >= 2 + // to 4 if data.len() >= 4 + // To simplify, enforce 4-align for now. + assert!(addr % 4 == 0); + + let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; + + while !data.is_empty() { + // Ensure transfer doesn't cross a window boundary. + let window_offs = addr & BACKPLANE_ADDRESS_MASK; + let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; + + let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); + slice8_mut(&mut buf)[..len].copy_from_slice(&data[..len]); + + self.backplane_set_window(addr).await; + + let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); + + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + bus.write(&buf[..(len + 3) / 4]).await?; + Ok(()) + }) + .await + .unwrap(); + + // Advance ptr. + addr += len as u32; + data = &data[len..]; + } + } + + pub async fn bp_read8(&mut self, addr: u32) -> u8 { + self.backplane_readn(addr, 1).await as u8 + } + + pub async fn bp_write8(&mut self, addr: u32, val: u8) { + self.backplane_writen(addr, val as u32, 1).await + } + + pub async fn bp_read16(&mut self, addr: u32) -> u16 { + self.backplane_readn(addr, 2).await as u16 + } + + #[allow(unused)] + pub async fn bp_write16(&mut self, addr: u32, val: u16) { + self.backplane_writen(addr, val as u32, 2).await + } + + #[allow(unused)] + pub async fn bp_read32(&mut self, addr: u32) -> u32 { + self.backplane_readn(addr, 4).await + } + + pub async fn bp_write32(&mut self, addr: u32, val: u32) { + self.backplane_writen(addr, val, 4).await + } + + async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 { + self.backplane_set_window(addr).await; + + let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; + if len == 4 { + bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG + } + self.readn(FUNC_BACKPLANE, bus_addr, len).await + } + + async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) { + self.backplane_set_window(addr).await; + + let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; + if len == 4 { + bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG + } + self.writen(FUNC_BACKPLANE, bus_addr, val, len).await + } + + async fn backplane_set_window(&mut self, addr: u32) { + let new_window = addr & !BACKPLANE_ADDRESS_MASK; + + if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 { + self.write8( + FUNC_BACKPLANE, + REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH, + (new_window >> 24) as u8, + ) + .await; + } + if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 { + self.write8( + FUNC_BACKPLANE, + REG_BACKPLANE_BACKPLANE_ADDRESS_MID, + (new_window >> 16) as u8, + ) + .await; + } + if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 { + self.write8( + FUNC_BACKPLANE, + REG_BACKPLANE_BACKPLANE_ADDRESS_LOW, + (new_window >> 8) as u8, + ) + .await; + } + self.backplane_window = new_window; + } + + pub async fn read8(&mut self, func: u32, addr: u32) -> u8 { + self.readn(func, addr, 1).await as u8 + } + + pub async fn write8(&mut self, func: u32, addr: u32, val: u8) { + self.writen(func, addr, val as u32, 1).await + } + + pub async fn read16(&mut self, func: u32, addr: u32) -> u16 { + self.readn(func, addr, 2).await as u16 + } + + #[allow(unused)] + pub async fn write16(&mut self, func: u32, addr: u32, val: u16) { + self.writen(func, addr, val as u32, 2).await + } + + pub async fn read32(&mut self, func: u32, addr: u32) -> u32 { + self.readn(func, addr, 4).await + } + + #[allow(unused)] + pub async fn write32(&mut self, func: u32, addr: u32, val: u32) { + self.writen(func, addr, val, 4).await + } + + async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { + let cmd = cmd_word(READ, INC_ADDR, func, addr, len); + let mut buf = [0; 1]; + + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd]).await?; + if func == FUNC_BACKPLANE { + // 4-byte response delay. + bus.read(&mut buf).await?; + } + bus.read(&mut buf).await?; + Ok(()) + }) + .await + .unwrap(); + + buf[0] + } + + async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { + let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); + + transaction!(&mut self.spi, |bus| async { + bus.write(&[cmd, val]).await?; + Ok(()) + }) + .await + .unwrap(); + } + + async fn read32_swapped(&mut self, addr: u32) -> u32 { + let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4); + let mut buf = [0; 1]; + + transaction!(&mut self.spi, |bus| async { + bus.write(&[swap16(cmd)]).await?; + bus.read(&mut buf).await?; + Ok(()) + }) + .await + .unwrap(); + + swap16(buf[0]) + } + + async fn write32_swapped(&mut self, addr: u32, val: u32) { + let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); + + transaction!(&mut self.spi, |bus| async { + bus.write(&[swap16(cmd), swap16(val)]).await?; + Ok(()) + }) + .await + .unwrap(); + } +} + +fn swap16(x: u32) -> u32 { + x.rotate_left(16) +} + +fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { + (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) +} + +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +} diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 000000000..bee706600 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,105 @@ +#![allow(unused)] +pub(crate) const FUNC_BUS: u32 = 0; +pub(crate) const FUNC_BACKPLANE: u32 = 1; +pub(crate) const FUNC_WLAN: u32 = 2; +pub(crate) const FUNC_BT: u32 = 3; + +pub(crate) const REG_BUS_CTRL: u32 = 0x0; +pub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status +pub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask +pub(crate) const REG_BUS_STATUS: u32 = 0x8; +pub(crate) const REG_BUS_TEST_RO: u32 = 0x14; +pub(crate) const REG_BUS_TEST_RW: u32 = 0x18; +pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c; +pub(crate) const WORD_LENGTH_32: u32 = 0x1; +pub(crate) const HIGH_SPEED: u32 = 0x10; + +// SPI_STATUS_REGISTER bits +pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; +pub(crate) const STATUS_UNDERFLOW: u32 = 0x00000002; +pub(crate) const STATUS_OVERFLOW: u32 = 0x00000004; +pub(crate) const STATUS_F2_INTR: u32 = 0x00000008; +pub(crate) const STATUS_F3_INTR: u32 = 0x00000010; +pub(crate) const STATUS_F2_RX_READY: u32 = 0x00000020; +pub(crate) const STATUS_F3_RX_READY: u32 = 0x00000040; +pub(crate) const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080; +pub(crate) const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100; +pub(crate) const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00; +pub(crate) const STATUS_F2_PKT_LEN_SHIFT: u32 = 9; +pub(crate) const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000; +pub(crate) const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000; +pub(crate) const STATUS_F3_PKT_LEN_SHIFT: u32 = 21; + +pub(crate) const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005; +pub(crate) const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006; +pub(crate) const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007; +pub(crate) const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008; +pub(crate) const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009; +pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A; +pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B; +pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C; +pub(crate) const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D; +pub(crate) const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E; +pub(crate) const REG_BACKPLANE_PULL_UP: u32 = 0x1000F; +pub(crate) const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B; +pub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C; +pub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E; +pub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F; + +pub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000; +pub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; +pub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; +pub(crate) const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; +// Active Low Power (ALP) clock constants +pub(crate) const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08; +pub(crate) const BACKPLANE_ALP_AVAIL: u8 = 0x40; + +// Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect +// (AI) pub (crate) constants +pub(crate) const AI_IOCTRL_OFFSET: u32 = 0x408; +pub(crate) const AI_IOCTRL_BIT_FGC: u8 = 0x0002; +pub(crate) const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; +pub(crate) const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020; + +pub(crate) const AI_RESETCTRL_OFFSET: u32 = 0x800; +pub(crate) const AI_RESETCTRL_BIT_RESET: u8 = 1; + +pub(crate) const AI_RESETSTATUS_OFFSET: u32 = 0x804; + +pub(crate) const TEST_PATTERN: u32 = 0x12345678; +pub(crate) const FEEDBEAD: u32 = 0xFEEDBEAD; + +// SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits +pub(crate) const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1" +pub(crate) const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002; +pub(crate) const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004; +pub(crate) const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1 +pub(crate) const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1 +pub(crate) const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020; +pub(crate) const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040; +pub(crate) const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests +pub(crate) const IRQ_MISC_INTR0: u16 = 0x0100; +pub(crate) const IRQ_MISC_INTR1: u16 = 0x0200; +pub(crate) const IRQ_MISC_INTR2: u16 = 0x0400; +pub(crate) const IRQ_MISC_INTR3: u16 = 0x0800; +pub(crate) const IRQ_MISC_INTR4: u16 = 0x1000; +pub(crate) const IRQ_F1_INTR: u16 = 0x2000; +pub(crate) const IRQ_F2_INTR: u16 = 0x4000; +pub(crate) const IRQ_F3_INTR: u16 = 0x8000; + +pub(crate) const IOCTL_CMD_UP: u32 = 2; +pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; +pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64; +pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; +pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262; +pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; + +pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0; +pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1; +pub(crate) const CHANNEL_TYPE_DATA: u8 = 2; + +// CYW_SPID command structure constants. +pub(crate) const WRITE: bool = true; +pub(crate) const READ: bool = false; +pub(crate) const INC_ADDR: bool = true; +pub(crate) const FIXED_ADDR: bool = false; diff --git a/src/lib.rs b/src/lib.rs index 883e669de..fa73b32e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,8 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod bus; +mod consts; mod countries; mod events; mod structs; @@ -23,132 +25,12 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::Channel; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; -use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice}; +use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; -use self::structs::*; +use crate::bus::Bus; +use crate::consts::*; use crate::events::Event; - -fn swap16(x: u32) -> u32 { - x.rotate_left(16) -} - -fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { - (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) -} - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } -} - -mod constants { - #![allow(unused)] - pub(crate) const FUNC_BUS: u32 = 0; - pub(crate) const FUNC_BACKPLANE: u32 = 1; - pub(crate) const FUNC_WLAN: u32 = 2; - pub(crate) const FUNC_BT: u32 = 3; - - pub(crate) const REG_BUS_CTRL: u32 = 0x0; - pub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status - pub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask - pub(crate) const REG_BUS_STATUS: u32 = 0x8; - pub(crate) const REG_BUS_TEST_RO: u32 = 0x14; - pub(crate) const REG_BUS_TEST_RW: u32 = 0x18; - pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c; - pub(crate) const WORD_LENGTH_32: u32 = 0x1; - pub(crate) const HIGH_SPEED: u32 = 0x10; - - // SPI_STATUS_REGISTER bits - pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; - pub(crate) const STATUS_UNDERFLOW: u32 = 0x00000002; - pub(crate) const STATUS_OVERFLOW: u32 = 0x00000004; - pub(crate) const STATUS_F2_INTR: u32 = 0x00000008; - pub(crate) const STATUS_F3_INTR: u32 = 0x00000010; - pub(crate) const STATUS_F2_RX_READY: u32 = 0x00000020; - pub(crate) const STATUS_F3_RX_READY: u32 = 0x00000040; - pub(crate) const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080; - pub(crate) const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100; - pub(crate) const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00; - pub(crate) const STATUS_F2_PKT_LEN_SHIFT: u32 = 9; - pub(crate) const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000; - pub(crate) const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000; - pub(crate) const STATUS_F3_PKT_LEN_SHIFT: u32 = 21; - - pub(crate) const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005; - pub(crate) const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006; - pub(crate) const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007; - pub(crate) const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008; - pub(crate) const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009; - pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A; - pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B; - pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C; - pub(crate) const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D; - pub(crate) const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E; - pub(crate) const REG_BACKPLANE_PULL_UP: u32 = 0x1000F; - pub(crate) const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B; - pub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C; - pub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E; - pub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F; - - pub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000; - pub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; - pub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; - pub(crate) const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; - // Active Low Power (ALP) clock constants - pub(crate) const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08; - pub(crate) const BACKPLANE_ALP_AVAIL: u8 = 0x40; - - // Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect - // (AI) pub (crate) constants - pub(crate) const AI_IOCTRL_OFFSET: u32 = 0x408; - pub(crate) const AI_IOCTRL_BIT_FGC: u8 = 0x0002; - pub(crate) const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; - pub(crate) const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020; - - pub(crate) const AI_RESETCTRL_OFFSET: u32 = 0x800; - pub(crate) const AI_RESETCTRL_BIT_RESET: u8 = 1; - - pub(crate) const AI_RESETSTATUS_OFFSET: u32 = 0x804; - - pub(crate) const TEST_PATTERN: u32 = 0x12345678; - pub(crate) const FEEDBEAD: u32 = 0xFEEDBEAD; - - // SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits - pub(crate) const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1" - pub(crate) const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002; - pub(crate) const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004; - pub(crate) const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1 - pub(crate) const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1 - pub(crate) const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020; - pub(crate) const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040; - pub(crate) const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests - pub(crate) const IRQ_MISC_INTR0: u16 = 0x0100; - pub(crate) const IRQ_MISC_INTR1: u16 = 0x0200; - pub(crate) const IRQ_MISC_INTR2: u16 = 0x0400; - pub(crate) const IRQ_MISC_INTR3: u16 = 0x0800; - pub(crate) const IRQ_MISC_INTR4: u16 = 0x1000; - pub(crate) const IRQ_F1_INTR: u16 = 0x2000; - pub(crate) const IRQ_F2_INTR: u16 = 0x4000; - pub(crate) const IRQ_F3_INTR: u16 = 0x8000; - - pub(crate) const IOCTL_CMD_UP: u32 = 2; - pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; - pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64; - pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; - pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262; - pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; - - pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0; - pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1; - pub(crate) const CHANNEL_TYPE_DATA: u8 = 2; - - // CYW_SPID command structure constants. - pub(crate) const WRITE: bool = true; - pub(crate) const READ: bool = false; - pub(crate) const INC_ADDR: bool = true; - pub(crate) const FIXED_ADDR: bool = false; -} -use crate::constants::*; +use crate::structs::*; #[derive(Clone, Copy)] pub enum IoctlType { @@ -565,15 +447,11 @@ impl<'a> embassy_net::Device for NetDevice<'a> { } pub struct Runner<'a, PWR, SPI> { + bus: Bus, + state: &'a State, - - pwr: PWR, - spi: SPI, - ioctl_id: u16, sdpcm_seq: u8, - backplane_window: u32, - sdpcm_seq_max: u8, #[cfg(feature = "firmware-logs")] @@ -600,14 +478,11 @@ where SPI::Bus: SpiBusRead + SpiBusWrite, { let mut runner = Runner { - state, - pwr, - spi, + bus: Bus::new(pwr, spi), + state, ioctl_id: 0, sdpcm_seq: 0, - backplane_window: 0xAAAA_AAAA, - sdpcm_seq_max: 1, #[cfg(feature = "firmware-logs")] @@ -631,62 +506,41 @@ where SPI::Bus: SpiBusRead + SpiBusWrite, { async fn init(&mut self, firmware: &[u8]) { - // Reset - self.pwr.set_low().unwrap(); - Timer::after(Duration::from_millis(20)).await; - self.pwr.set_high().unwrap(); - Timer::after(Duration::from_millis(250)).await; - - info!("waiting for ping..."); - while self.read32_swapped(REG_BUS_TEST_RO).await != FEEDBEAD {} - info!("ping ok"); - - self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await; - let val = self.read32_swapped(REG_BUS_TEST_RW).await; - assert_eq!(val, TEST_PATTERN); - - // 32-bit word length, little endian (which is the default endianess). - self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await; - - let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await; - assert_eq!(val, FEEDBEAD); - let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await; - assert_eq!(val, TEST_PATTERN); - - // No response delay in any of the funcs. - // seems to break backplane??? eat the 4-byte delay instead, that's what the vendor drivers do... - //self.write32(FUNC_BUS, REG_BUS_RESP_DELAY, 0).await; + self.bus.init().await; // Init ALP (Active Low Power) clock - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) + self.bus + .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) .await; info!("waiting for clock..."); - while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} + while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} info!("clock ok"); - let chip_id = self.bp_read16(0x1800_0000).await; + let chip_id = self.bus.bp_read16(0x1800_0000).await; info!("chip ID: {}", chip_id); // Upload firmware. self.core_disable(Core::WLAN).await; self.core_reset(Core::SOCSRAM).await; - self.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; - self.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; + self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; + self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; let ram_addr = CHIP.atcm_ram_base_address; info!("loading fw"); - self.bp_write(ram_addr, firmware).await; + self.bus.bp_write(ram_addr, firmware).await; info!("loading nvram"); // Round up to 4 bytes. let nvram_len = (NVRAM.len() + 3) / 4 * 4; - self.bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) + self.bus + .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) .await; let nvram_len_words = nvram_len as u32 / 4; let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; - self.bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) + self.bus + .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) .await; // Start core! @@ -694,18 +548,20 @@ where self.core_reset(Core::WLAN).await; assert!(self.core_is_up(Core::WLAN).await); - while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} + while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} // "Set up the interrupt mask and enable interrupts" - self.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; + self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." // Sounds scary... - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32).await; + self.bus + .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32) + .await; // wait for wifi startup info!("waiting for wifi init..."); - while self.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} + while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} // Some random configs related to sleep. // These aren't needed if we don't want to sleep the bus. @@ -713,25 +569,25 @@ where // being on the same pin as MOSI/MISO? /* - let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; + let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; val |= 0x02; // WAKE_TILL_HT_AVAIL - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; - self.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; + self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT - let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; + let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; */ // clear pulls - self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; - let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; + let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; // start HT clock - //self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; + //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; //info!("waiting for HT clock..."); - //while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} + //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} //info!("clock ok"); #[cfg(feature = "firmware-logs")] @@ -744,13 +600,12 @@ where async fn log_init(&mut self) { // Initialize shared memory for logging. - let shared_addr = self - .bp_read32(CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size) - .await; + let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; + let shared_addr = self.bus.bp_read32(addr).await; info!("shared_addr {:08x}", shared_addr); let mut shared = [0; SharedMemData::SIZE]; - self.bp_read(shared_addr, &mut shared).await; + self.bus.bp_read(shared_addr, &mut shared).await; let shared = SharedMemData::from_bytes(&shared); info!("shared: {:08x}", shared); @@ -761,7 +616,7 @@ where async fn log_read(&mut self) { // Read log struct let mut log = [0; SharedMemLog::SIZE]; - self.bp_read(self.log.addr, &mut log).await; + self.bus.bp_read(self.log.addr, &mut log).await; let log = SharedMemLog::from_bytes(&log); let idx = log.idx as usize; @@ -774,7 +629,7 @@ where // Read entire buf for now. We could read only what we need, but then we // run into annoying alignment issues in `bp_read`. let mut buf = [0; 0x400]; - self.bp_read(log.buf, &mut buf).await; + self.bus.bp_read(log.buf, &mut buf).await; while self.log.last_idx != idx as usize { let b = buf[self.log.last_idx]; @@ -821,29 +676,19 @@ where } // Receive stuff - let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; + let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; if irq & IRQ_F2_PACKET_AVAILABLE != 0 { let mut status = 0xFFFF_FFFF; while status == 0xFFFF_FFFF { - status = self.read32(FUNC_BUS, REG_BUS_STATUS).await; + status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; } if status & STATUS_F2_PKT_AVAILABLE != 0 { let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len); - - transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - bus.read(&mut buf[..(len as usize + 3) / 4]).await?; - Ok(()) - }) - .await - .unwrap(); - + self.bus.wlan_read(&mut buf[..(len as usize + 3) / 4]).await; trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); - self.rx(&slice8_mut(&mut buf)[..len as usize]); } } @@ -893,14 +738,7 @@ where trace!(" {:02x}", &buf8[..total_len.min(48)]); - let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _); - transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - bus.write(&buf[..(total_len / 4)]).await?; - Ok(()) - }) - .await - .unwrap(); + self.bus.wlan_write(&buf[..(total_len / 4)]).await; } fn rx(&mut self, packet: &[u8]) { @@ -1086,52 +924,49 @@ where trace!(" {:02x}", &buf8[..total_len.min(48)]); - let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _); - - transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - bus.write(&buf[..total_len / 4]).await?; - Ok(()) - }) - .await - .unwrap(); + self.bus.wlan_write(&buf[..total_len / 4]).await; } async fn core_disable(&mut self, core: Core) { let base = core.base_addr(); // Dummy read? - let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; + let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; // Check it isn't already reset - let r = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; + let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; if r & AI_RESETCTRL_BIT_RESET != 0 { return; } - self.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; - let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await; + self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; block_for(Duration::from_millis(1)); - self.bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET).await; - let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; + self.bus + .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) + .await; + let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; } async fn core_reset(&mut self, core: Core) { self.core_disable(core).await; let base = core.base_addr(); - self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) + self.bus + .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) .await; - let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - self.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; + self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; Timer::after(Duration::from_millis(1)).await; - self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN).await; - let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await; + self.bus + .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) + .await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; Timer::after(Duration::from_millis(1)).await; } @@ -1139,13 +974,13 @@ where async fn core_is_up(&mut self, core: Core) -> bool { let base = core.base_addr(); - let io = self.bp_read8(base + AI_IOCTRL_OFFSET).await; + let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); return false; } - let r = self.bp_read8(base + AI_RESETCTRL_OFFSET).await; + let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; if r & (AI_RESETCTRL_BIT_RESET) != 0 { debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); return false; @@ -1153,242 +988,11 @@ where true } +} - #[allow(unused)] - async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { - // It seems the HW force-aligns the addr - // to 2 if data.len() >= 2 - // to 4 if data.len() >= 4 - // To simplify, enforce 4-align for now. - assert!(addr % 4 == 0); - - let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; - - while !data.is_empty() { - // Ensure transfer doesn't cross a window boundary. - let window_offs = addr & BACKPLANE_ADDRESS_MASK; - let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; - - let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); - - self.backplane_set_window(addr).await; - - let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); - - transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - - // 4-byte response delay. - let mut junk = [0; 1]; - bus.read(&mut junk).await?; - - // Read data - bus.read(&mut buf[..(len + 3) / 4]).await?; - Ok(()) - }) - .await - .unwrap(); - - data[..len].copy_from_slice(&slice8_mut(&mut buf)[..len]); - - // Advance ptr. - addr += len as u32; - data = &mut data[len..]; - } - } - - async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { - // It seems the HW force-aligns the addr - // to 2 if data.len() >= 2 - // to 4 if data.len() >= 4 - // To simplify, enforce 4-align for now. - assert!(addr % 4 == 0); - - let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; - - while !data.is_empty() { - // Ensure transfer doesn't cross a window boundary. - let window_offs = addr & BACKPLANE_ADDRESS_MASK; - let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; - - let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); - slice8_mut(&mut buf)[..len].copy_from_slice(&data[..len]); - - self.backplane_set_window(addr).await; - - let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); - - transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - bus.write(&buf[..(len + 3) / 4]).await?; - Ok(()) - }) - .await - .unwrap(); - - // Advance ptr. - addr += len as u32; - data = &data[len..]; - } - } - - async fn bp_read8(&mut self, addr: u32) -> u8 { - self.backplane_readn(addr, 1).await as u8 - } - - async fn bp_write8(&mut self, addr: u32, val: u8) { - self.backplane_writen(addr, val as u32, 1).await - } - - async fn bp_read16(&mut self, addr: u32) -> u16 { - self.backplane_readn(addr, 2).await as u16 - } - - #[allow(unused)] - async fn bp_write16(&mut self, addr: u32, val: u16) { - self.backplane_writen(addr, val as u32, 2).await - } - - #[allow(unused)] - async fn bp_read32(&mut self, addr: u32) -> u32 { - self.backplane_readn(addr, 4).await - } - - async fn bp_write32(&mut self, addr: u32, val: u32) { - self.backplane_writen(addr, val, 4).await - } - - async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 { - self.backplane_set_window(addr).await; - - let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; - if len == 4 { - bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG - } - self.readn(FUNC_BACKPLANE, bus_addr, len).await - } - - async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) { - self.backplane_set_window(addr).await; - - let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; - if len == 4 { - bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG - } - self.writen(FUNC_BACKPLANE, bus_addr, val, len).await - } - - async fn backplane_set_window(&mut self, addr: u32) { - let new_window = addr & !BACKPLANE_ADDRESS_MASK; - - if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 { - self.write8( - FUNC_BACKPLANE, - REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH, - (new_window >> 24) as u8, - ) - .await; - } - if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 { - self.write8( - FUNC_BACKPLANE, - REG_BACKPLANE_BACKPLANE_ADDRESS_MID, - (new_window >> 16) as u8, - ) - .await; - } - if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 { - self.write8( - FUNC_BACKPLANE, - REG_BACKPLANE_BACKPLANE_ADDRESS_LOW, - (new_window >> 8) as u8, - ) - .await; - } - self.backplane_window = new_window; - } - - async fn read8(&mut self, func: u32, addr: u32) -> u8 { - self.readn(func, addr, 1).await as u8 - } - - async fn write8(&mut self, func: u32, addr: u32, val: u8) { - self.writen(func, addr, val as u32, 1).await - } - - async fn read16(&mut self, func: u32, addr: u32) -> u16 { - self.readn(func, addr, 2).await as u16 - } - - #[allow(unused)] - async fn write16(&mut self, func: u32, addr: u32, val: u16) { - self.writen(func, addr, val as u32, 2).await - } - - async fn read32(&mut self, func: u32, addr: u32) -> u32 { - self.readn(func, addr, 4).await - } - - #[allow(unused)] - async fn write32(&mut self, func: u32, addr: u32, val: u32) { - self.writen(func, addr, val, 4).await - } - - async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { - let cmd = cmd_word(READ, INC_ADDR, func, addr, len); - let mut buf = [0; 1]; - - transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - if func == FUNC_BACKPLANE { - // 4-byte response delay. - bus.read(&mut buf).await?; - } - bus.read(&mut buf).await?; - Ok(()) - }) - .await - .unwrap(); - - buf[0] - } - - async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { - let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); - - transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd, val]).await?; - Ok(()) - }) - .await - .unwrap(); - } - - async fn read32_swapped(&mut self, addr: u32) -> u32 { - let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4); - let mut buf = [0; 1]; - - transaction!(&mut self.spi, |bus| async { - bus.write(&[swap16(cmd)]).await?; - bus.read(&mut buf).await?; - Ok(()) - }) - .await - .unwrap(); - - swap16(buf[0]) - } - - async fn write32_swapped(&mut self, addr: u32, val: u32) { - let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); - - transaction!(&mut self.spi, |bus| async { - bus.write(&[swap16(cmd), swap16(val)]).await?; - Ok(()) - }) - .await - .unwrap(); - } +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } } macro_rules! nvram { From 4a4b59369482572c415487b9d90166e7c19b592a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Dec 2022 01:04:55 +0100 Subject: [PATCH 0469/1575] net; allow changing MAC addr at runtime. --- embassy-net-driver-channel/src/lib.rs | 56 +++++++++++++++++---------- embassy-net/src/lib.rs | 16 ++++---- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 369dc5a9d..073edb41a 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -34,24 +34,25 @@ impl State { rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - link_state: Mutex>, + shared: Mutex>, } /// State of the LinkState -struct LinkStateState { - state: LinkState, +struct Shared { + link_state: LinkState, waker: WakerRegistration, + ethernet_address: [u8; 6], } pub struct Runner<'d, const MTU: usize> { tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + shared: &'d Mutex>, } pub struct RxRunner<'d, const MTU: usize> { rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + shared: &'d Mutex>, } pub struct TxRunner<'d, const MTU: usize> { @@ -62,7 +63,7 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) { ( RxRunner { - link_state: self.link_state, + shared: self.shared, rx_chan: self.rx_chan, }, TxRunner { tx_chan: self.tx_chan }, @@ -70,9 +71,17 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { } pub fn set_link_state(&mut self, state: LinkState) { - self.link_state.lock(|s| { + self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.state = state; + s.link_state = state; + s.waker.wake(); + }); + } + + pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + self.shared.lock(|s| { + let s = &mut *s.borrow_mut(); + s.ethernet_address = address; s.waker.wake(); }); } @@ -124,9 +133,17 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { impl<'d, const MTU: usize> RxRunner<'d, MTU> { pub fn set_link_state(&mut self, state: LinkState) { - self.link_state.lock(|s| { + self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.state = state; + s.link_state = state; + s.waker.wake(); + }); + } + + pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + self.shared.lock(|s| { + let s = &mut *s.borrow_mut(); + s.ethernet_address = address; s.waker.wake(); }); } @@ -194,8 +211,9 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( let state = unsafe { &mut *state_uninit }.write(StateInner { rx: zerocopy_channel::Channel::new(&mut state.rx[..]), tx: zerocopy_channel::Channel::new(&mut state.tx[..]), - link_state: Mutex::new(RefCell::new(LinkStateState { - state: LinkState::Down, + shared: Mutex::new(RefCell::new(Shared { + link_state: LinkState::Down, + ethernet_address, waker: WakerRegistration::new(), })), }); @@ -207,12 +225,11 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( Runner { tx_chan: tx_receiver, rx_chan: rx_sender, - link_state: &state.link_state, + shared: &state.shared, }, Device { caps, - ethernet_address, - link_state: &state.link_state, + shared: &state.shared, rx: rx_receiver, tx: tx_sender, }, @@ -233,9 +250,8 @@ impl PacketBuf { pub struct Device<'d, const MTU: usize> { rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + shared: &'d Mutex>, caps: Capabilities, - ethernet_address: [u8; 6], } impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { @@ -265,14 +281,14 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { } fn ethernet_address(&self) -> [u8; 6] { - self.ethernet_address + self.shared.lock(|s| s.borrow().ethernet_address) } fn link_state(&mut self, cx: &mut Context) -> LinkState { - self.link_state.lock(|s| { + self.shared.lock(|s| { let s = &mut *s.borrow_mut(); s.waker.register(cx.waker()); - s.state + s.link_state }) } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b58c9cf36..e4a4218e3 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -111,20 +111,13 @@ impl Stack { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; - #[cfg(feature = "medium-ethernet")] - let ethernet_addr = if medium == Medium::Ethernet { - device.ethernet_address() - } else { - [0, 0, 0, 0, 0, 0] - }; - let mut b = InterfaceBuilder::new(); b = b.ip_addrs(&mut resources.addresses[..]); b = b.random_seed(random_seed); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { - b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); + b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); b = b.routes(Routes::new(&mut resources.routes[..])); } @@ -261,6 +254,13 @@ impl Inner { fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { s.waker.register(cx.waker()); + #[cfg(feature = "medium-ethernet")] + if self.device.capabilities().medium == Medium::Ethernet { + s.iface.set_hardware_addr(HardwareAddress::Ethernet(EthernetAddress( + self.device.ethernet_address(), + ))); + } + let timestamp = instant_to_smoltcp(Instant::now()); let mut smoldev = DriverAdapter { cx: Some(cx), From 771806be790a2758f1314d6460defe7c2f0d3e99 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Dec 2022 01:07:58 +0100 Subject: [PATCH 0470/1575] net/chan: split state runner. --- embassy-net-driver-channel/src/lib.rs | 26 +++++++++++++------- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 6 ++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 073edb41a..0c8dcc22b 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -50,9 +50,13 @@ pub struct Runner<'d, const MTU: usize> { shared: &'d Mutex>, } +#[derive(Clone, Copy)] +pub struct StateRunner<'d> { + shared: &'d Mutex>, +} + pub struct RxRunner<'d, const MTU: usize> { rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - shared: &'d Mutex>, } pub struct TxRunner<'d, const MTU: usize> { @@ -60,16 +64,18 @@ pub struct TxRunner<'d, const MTU: usize> { } impl<'d, const MTU: usize> Runner<'d, MTU> { - pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) { + pub fn split(self) -> (StateRunner<'d>, RxRunner<'d, MTU>, TxRunner<'d, MTU>) { ( - RxRunner { - shared: self.shared, - rx_chan: self.rx_chan, - }, + StateRunner { shared: self.shared }, + RxRunner { rx_chan: self.rx_chan }, TxRunner { tx_chan: self.tx_chan }, ) } + pub fn state_runner(&self) -> StateRunner<'d> { + StateRunner { shared: self.shared } + } + pub fn set_link_state(&mut self, state: LinkState) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); @@ -131,8 +137,8 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { } } -impl<'d, const MTU: usize> RxRunner<'d, MTU> { - pub fn set_link_state(&mut self, state: LinkState) { +impl<'d> StateRunner<'d> { + pub fn set_link_state(&self, state: LinkState) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); s.link_state = state; @@ -140,14 +146,16 @@ impl<'d, const MTU: usize> RxRunner<'d, MTU> { }); } - pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + pub fn set_ethernet_address(&self, address: [u8; 6]) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); s.ethernet_address = address; s.waker.wake(); }); } +} +impl<'d, const MTU: usize> RxRunner<'d, MTU> { pub async fn rx_buf(&mut self) -> &mut [u8] { let p = self.rx_chan.send().await; &mut p.buf diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 7ecf693d2..501df2d8c 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -25,16 +25,16 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { pub async fn run(mut self) -> ! { - let (mut rx_chan, mut tx_chan) = self.ch.split(); + let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); let rx_fut = async move { loop { trace!("WAITING for connection"); - rx_chan.set_link_state(LinkState::Down); + state_chan.set_link_state(LinkState::Down); self.rx_usb.wait_connection().await.unwrap(); trace!("Connected"); - rx_chan.set_link_state(LinkState::Up); + state_chan.set_link_state(LinkState::Up); loop { let p = rx_chan.rx_buf().await; From 2548bbdd65fc3094f624bd043a1a9a296f9184b5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Dec 2022 01:19:26 +0100 Subject: [PATCH 0471/1575] Update Embassy. --- Cargo.toml | 2 +- examples/rpi-pico-w/Cargo.toml | 18 +-- examples/rpi-pico-w/src/main.rs | 6 +- src/lib.rs | 218 +++++++++++++------------------- 4 files changed, 101 insertions(+), 143 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dadfb5c5a..6e3237448 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ firmware-logs = [] embassy-time = { version = "0.1.0" } embassy-sync = { version = "0.1.0" } embassy-futures = { version = "0.1.0" } -embassy-net = { version = "0.1.0" } +embassy-net-driver-channel = { version = "0.1.0" } atomic-polyfill = "0.1.5" defmt = { version = "0.3", optional = true } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index b817289e5..fa1cad8c7 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -9,7 +9,7 @@ cyw43 = { path = "../../", features = ["defmt", "firmware-logs"]} embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } -embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits", "nightly"] } +embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } atomic-polyfill = "0.1.5" static_cell = "1.0" @@ -28,12 +28,14 @@ heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "645fb66a5122bdc8180e0e65d076ca103431a426" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } [profile.dev] debug = 2 @@ -43,7 +45,7 @@ overflow-checks = true [profile.release] codegen-units = 1 -debug = 2 +debug = 1 debug-assertions = false incremental = false lto = 'fat' diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index a19f38591..fd58e46df 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -34,7 +34,7 @@ async fn wifi_task( } #[embassy_executor::task] -async fn net_task(stack: &'static Stack>) -> ! { +async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } @@ -66,11 +66,11 @@ async fn main(spawner: Spawner) { let spi = ExclusiveDevice::new(bus, cs); let state = singleton!(cyw43::State::new()); - let (mut control, runner) = cyw43::new(state, pwr, spi, fw).await; + let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; spawner.spawn(wifi_task(runner)).unwrap(); - let net_device = control.init(clm).await; + control.init(clm).await; //control.join_open(env!("WIFI_NETWORK")).await; control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; diff --git a/src/lib.rs b/src/lib.rs index fa73b32e0..25e6f8f16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,14 +15,10 @@ mod structs; use core::cell::Cell; use core::cmp::{max, min}; use core::slice; -use core::sync::atomic::Ordering; -use core::task::Waker; -use atomic_polyfill::AtomicBool; +use ch::driver::LinkState; use embassy_futures::yield_now; -use embassy_net::{PacketBoxExt, PacketBuf}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::channel::Channel; +use embassy_net_driver_channel as ch; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; @@ -32,6 +28,8 @@ use crate::consts::*; use crate::events::Event; use crate::structs::*; +const MTU: usize = 1514; + #[derive(Clone, Copy)] pub enum IoctlType { Get = 0, @@ -128,30 +126,25 @@ enum IoctlState { pub struct State { ioctl_state: Cell, - - tx_channel: Channel, - rx_channel: Channel, - link_up: AtomicBool, + ch: ch::State, } impl State { pub fn new() -> Self { Self { ioctl_state: Cell::new(IoctlState::Idle), - - tx_channel: Channel::new(), - rx_channel: Channel::new(), - link_up: AtomicBool::new(true), // TODO set up/down as we join/deassociate + ch: ch::State::new(), } } } pub struct Control<'a> { - state: &'a State, + state_ch: ch::StateRunner<'a>, + ioctl_state: &'a Cell, } impl<'a> Control<'a> { - pub async fn init(&mut self, clm: &[u8]) -> NetDevice<'a> { + pub async fn init(&mut self, clm: &[u8]) { const CHUNK_SIZE: usize = 1024; info!("Downloading CLM..."); @@ -258,12 +251,10 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; - info!("INIT DONE"); + self.state_ch.set_ethernet_address(mac_addr); + self.state_ch.set_link_state(LinkState::Up); // TODO do on join/leave - NetDevice { - state: self.state, - mac_addr, - } + info!("INIT DONE"); } pub async fn join_open(&mut self, ssid: &str) { @@ -381,75 +372,30 @@ impl<'a> Control<'a> { async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { // TODO cancel ioctl on future drop. - while !matches!(self.state.ioctl_state.get(), IoctlState::Idle) { + while !matches!(self.ioctl_state.get(), IoctlState::Idle) { yield_now().await; } - self.state - .ioctl_state - .set(IoctlState::Pending { kind, cmd, iface, buf }); + self.ioctl_state.set(IoctlState::Pending { kind, cmd, iface, buf }); let resp_len = loop { - if let IoctlState::Done { resp_len } = self.state.ioctl_state.get() { + if let IoctlState::Done { resp_len } = self.ioctl_state.get() { break resp_len; } yield_now().await; }; - self.state.ioctl_state.set(IoctlState::Idle); + self.ioctl_state.set(IoctlState::Idle); resp_len } } -pub struct NetDevice<'a> { - state: &'a State, - mac_addr: [u8; 6], -} - -impl<'a> embassy_net::Device for NetDevice<'a> { - fn register_waker(&mut self, waker: &Waker) { - // loopy loopy wakey wakey - waker.wake_by_ref() - } - - fn link_state(&mut self) -> embassy_net::LinkState { - match self.state.link_up.load(Ordering::Relaxed) { - true => embassy_net::LinkState::Up, - false => embassy_net::LinkState::Down, - } - } - - fn capabilities(&self) -> embassy_net::DeviceCapabilities { - let mut caps = embassy_net::DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = embassy_net::Medium::Ethernet; - caps - } - - fn is_transmit_ready(&mut self) -> bool { - true - } - - fn transmit(&mut self, pkt: PacketBuf) { - if self.state.tx_channel.try_send(pkt).is_err() { - warn!("TX failed") - } - } - - fn receive(&mut self) -> Option { - self.state.rx_channel.try_recv().ok() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr - } -} - pub struct Runner<'a, PWR, SPI> { + ch: ch::Runner<'a, MTU>, bus: Bus, - state: &'a State, + ioctl_state: &'a Cell, ioctl_id: u16, sdpcm_seq: u8, sdpcm_seq_max: u8, @@ -466,21 +412,27 @@ struct LogState { buf_count: usize, } +pub type NetDriver<'a> = ch::Device<'a, MTU>; + pub async fn new<'a, PWR, SPI>( - state: &'a State, + state: &'a mut State, pwr: PWR, spi: SPI, firmware: &[u8], -) -> (Control<'a>, Runner<'a, PWR, SPI>) +) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) where PWR: OutputPin, SPI: SpiDevice, SPI::Bus: SpiBusRead + SpiBusWrite, { + let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let state_ch = ch_runner.state_runner(); + let mut runner = Runner { + ch: ch_runner, bus: Bus::new(pwr, spi), - state, + ioctl_state: &state.ioctl_state, ioctl_id: 0, sdpcm_seq: 0, sdpcm_seq_max: 1, @@ -496,7 +448,14 @@ where runner.init(firmware).await; - (Control { state }, runner) + ( + device, + Control { + state_ch, + ioctl_state: &state.ioctl_state, + }, + runner, + ) } impl<'a, PWR, SPI> Runner<'a, PWR, SPI> @@ -662,15 +621,55 @@ where if !self.has_credit() { warn!("TX stalled"); } else { - if let IoctlState::Pending { kind, cmd, iface, buf } = self.state.ioctl_state.get() { + if let IoctlState::Pending { kind, cmd, iface, buf } = self.ioctl_state.get() { self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; - self.state.ioctl_state.set(IoctlState::Sent { buf }); + self.ioctl_state.set(IoctlState::Sent { buf }); } if !self.has_credit() { warn!("TX stalled"); } else { - if let Ok(p) = self.state.tx_channel.try_recv() { - self.send_packet(&p).await; + if let Some(packet) = self.ch.try_tx_buf() { + trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); + + let mut buf = [0; 512]; + let buf8 = slice8_mut(&mut buf); + + let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); + + let seq = self.sdpcm_seq; + self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, // TODO does this len need to be rounded up to u32? + len_inv: !total_len as u16, + sequence: seq, + channel_and_flags: CHANNEL_TYPE_DATA, + next_length: 0, + header_length: SdpcmHeader::SIZE as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let bcd_header = BcdHeader { + flags: BDC_VERSION << BDC_VERSION_SHIFT, + priority: 0, + flags2: 0, + data_offset: 0, + }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", bcd_header); + + buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); + buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); + + let total_len = (total_len + 3) & !3; // round up to 4byte + + trace!(" {:02x}", &buf8[..total_len.min(48)]); + + self.bus.wlan_write(&buf[..(total_len / 4)]).await; + self.ch.tx_done(); } } } @@ -686,7 +685,6 @@ where if status & STATUS_F2_PKT_AVAILABLE != 0 { let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(&mut buf[..(len as usize + 3) / 4]).await; trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); self.rx(&slice8_mut(&mut buf)[..len as usize]); @@ -698,49 +696,6 @@ where } } - async fn send_packet(&mut self, packet: &[u8]) { - trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); - - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); - - let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); - - let seq = self.sdpcm_seq; - self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); - - let sdpcm_header = SdpcmHeader { - len: total_len as u16, // TODO does this len need to be rounded up to u32? - len_inv: !total_len as u16, - sequence: seq, - channel_and_flags: CHANNEL_TYPE_DATA, - next_length: 0, - header_length: SdpcmHeader::SIZE as _, - wireless_flow_control: 0, - bus_data_credit: 0, - reserved: [0, 0], - }; - - let bcd_header = BcdHeader { - flags: BDC_VERSION << BDC_VERSION_SHIFT, - priority: 0, - flags2: 0, - data_offset: 0, - }; - trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", bcd_header); - - buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); - buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); - - let total_len = (total_len + 3) & !3; // round up to 4byte - - trace!(" {:02x}", &buf8[..total_len.min(48)]); - - self.bus.wlan_write(&buf[..(total_len / 4)]).await; - } - fn rx(&mut self, packet: &[u8]) { if packet.len() < SdpcmHeader::SIZE { warn!("packet too short, len={}", packet.len()); @@ -775,7 +730,7 @@ where let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); trace!(" {:?}", cdc_header); - if let IoctlState::Sent { buf } = self.state.ioctl_state.get() { + if let IoctlState::Sent { buf } = self.ioctl_state.get() { if cdc_header.id == self.ioctl_id { if cdc_header.status != 0 { // TODO: propagate error instead @@ -786,7 +741,7 @@ where info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); - self.state.ioctl_state.set(IoctlState::Done { resp_len }); + self.ioctl_state.set(IoctlState::Done { resp_len }); } } } @@ -859,11 +814,12 @@ where let packet = &payload[packet_start..]; trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); - let mut p = unwrap!(embassy_net::PacketBox::new(embassy_net::Packet::new())); - p[..packet.len()].copy_from_slice(packet); - - if let Err(_) = self.state.rx_channel.try_send(p.slice(0..packet.len())) { - warn!("failed to push rxd packet to the channel.") + match self.ch.try_rx_buf() { + Some(buf) => { + buf[..packet.len()].copy_from_slice(packet); + self.ch.rx_done(packet.len()) + } + None => warn!("failed to push rxd packet to the channel."), } } _ => {} From e4f457646f216f36cd86fcfe54af0d7956d17932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 27 Dec 2022 10:20:51 +0100 Subject: [PATCH 0472/1575] rp: Fill and empty FIFOs in buffered uart interrupt Fixes an issue where only the first byte was transmitted. Should improve throughput aswell. --- embassy-rp/src/uart/buffered.rs | 40 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index e89970d11..1d1fe37c2 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -402,27 +402,45 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { } 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"); + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + for rx_byte in rx_buf { + if r.uartfr().read().rxfe() { + break; } + *rx_byte = r.uartdr().read().data(); + n_read += 1; + } + if n_read > 0 { + rx_writer.push_done(n_read); 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 { + let tx_buf = tx_reader.pop_slice(); + if tx_buf.len() == 0 { // Disable interrupt until we have something to transmit again r.uartimsc().modify(|w| { w.set_txim(false); }); + } else { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; + } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; + } + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } } } } From 2c8080b0aed4d998dfe2b158c63121b515213a54 Mon Sep 17 00:00:00 2001 From: Matthias Devlamynck Date: Thu, 29 Dec 2022 17:26:49 +0100 Subject: [PATCH 0473/1575] Add Clone & Copy on embassy_nrf::gpio::Level --- embassy-nrf/src/gpio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index bb64e41e9..b7230302a 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -70,7 +70,7 @@ impl<'d, T: Pin> Input<'d, T> { } /// Digital input or output level. -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Level { /// Logical low. From 72bb4f8798a4f42a8d0e409261e06f9a2d2f03ca Mon Sep 17 00:00:00 2001 From: Kentaro Okuda Date: Fri, 30 Dec 2022 08:53:16 -0500 Subject: [PATCH 0474/1575] Fix a typo --- embassy-nrf/src/uarte.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 4703c1a50..031cf82fd 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -166,7 +166,7 @@ impl<'d, T: Instance> Uarte<'d, T> { } /// Split the Uarte into a transmitter and receiver, which is - /// particuarly useful when having two tasks correlating to + /// particularly useful when having two tasks correlating to /// transmitting and receiving. pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { (self.tx, self.rx) From 871700f05dbd30aac71d6a3b5446e7743a18b90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Gr=C3=B6nlund?= Date: Sat, 31 Dec 2022 16:25:37 +0100 Subject: [PATCH 0475/1575] Fixed length for wlan_read. The length provided in command word for FUNC_WLAN READ, should describe the actual bytes requested, not the size of the buffer which is sized in u32. --- src/bus.rs | 7 ++++--- src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index f220cffcd..f64c0abba 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -48,11 +48,12 @@ where assert_eq!(val, TEST_PATTERN); } - pub async fn wlan_read(&mut self, buf: &mut [u32]) { - let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4); + pub async fn wlan_read(&mut self, buf: &mut [u32], len_in_u8: u32) { + let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len_in_u8); + let len_in_u32 = (len_in_u8 as usize + 3) / 4; transaction!(&mut self.spi, |bus| async { bus.write(&[cmd]).await?; - bus.read(buf).await?; + bus.read(&mut buf[..len_in_u32]).await?; Ok(()) }) .await diff --git a/src/lib.rs b/src/lib.rs index fa73b32e0..a606d6be3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -687,7 +687,7 @@ where if status & STATUS_F2_PKT_AVAILABLE != 0 { let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(&mut buf[..(len as usize + 3) / 4]).await; + self.bus.wlan_read(&mut buf, len).await; trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); self.rx(&slice8_mut(&mut buf)[..len as usize]); } From 001610f0d0b94859b8c8800dcdfa255343f2ea05 Mon Sep 17 00:00:00 2001 From: Lukas Krejci Date: Wed, 30 Nov 2022 15:57:52 +0100 Subject: [PATCH 0476/1575] Be able to specify the power management mode at init time. --- examples/rpi-pico-w/src/main.rs | 2 +- src/lib.rs | 106 +++++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index fd58e46df..73cfdf42f 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -70,7 +70,7 @@ async fn main(spawner: Spawner) { spawner.spawn(wifi_task(runner)).unwrap(); - control.init(clm).await; + control.init(clm, cyw43::PowerManagementMode::PowerSave).await; //control.join_open(env!("WIFI_NETWORK")).await; control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; diff --git a/src/lib.rs b/src/lib.rs index 940322715..884cb082b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,8 +143,92 @@ pub struct Control<'a> { ioctl_state: &'a Cell, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PowerManagementMode { + /// Custom, officially unsupported mode. Use at your own risk. + /// All power-saving features set to their max at only a marginal decrease in power consumption + /// as oppposed to `Aggressive`. + SuperSave, + + /// Aggressive power saving mode. + Aggressive, + + /// The default mode. + PowerSave, + + /// Performance is prefered over power consumption but still some power is conserved as opposed to + /// `None`. + Performance, + + /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of + /// a much lower throughput. + ThroughputThrottling, + + /// No power management is configured. This consumes the most power. + None, +} + +impl Default for PowerManagementMode { + fn default() -> Self { + Self::PowerSave + } +} + +impl PowerManagementMode { + fn sleep_ret_ms(&self) -> u16 { + match self { + PowerManagementMode::SuperSave => 2000, + PowerManagementMode::Aggressive => 2000, + PowerManagementMode::PowerSave => 200, + PowerManagementMode::Performance => 20, + PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter + PowerManagementMode::None => 0, // value doesn't matter + } + } + + fn beacon_period(&self) -> u8 { + match self { + PowerManagementMode::SuperSave => 255, + PowerManagementMode::Aggressive => 1, + PowerManagementMode::PowerSave => 1, + PowerManagementMode::Performance => 1, + PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter + PowerManagementMode::None => 0, // value doesn't matter + } + } + + fn dtim_period(&self) -> u8 { + match self { + PowerManagementMode::SuperSave => 255, + PowerManagementMode::Aggressive => 1, + PowerManagementMode::PowerSave => 1, + PowerManagementMode::Performance => 1, + PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter + PowerManagementMode::None => 0, // value doesn't matter + } + } + + fn assoc(&self) -> u8 { + match self { + PowerManagementMode::SuperSave => 255, + PowerManagementMode::Aggressive => 10, + PowerManagementMode::PowerSave => 10, + PowerManagementMode::Performance => 1, + PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter + PowerManagementMode::None => 0, // value doesn't matter + } + } + + fn mode(&self) -> u32 { + match self { + PowerManagementMode::ThroughputThrottling => 1, + _ => 2, + } + } +} + impl<'a> Control<'a> { - pub async fn init(&mut self, clm: &[u8]) { + pub async fn init(&mut self, clm: &[u8], power_save_mode: PowerManagementMode) { const CHUNK_SIZE: usize = 1024; info!("Downloading CLM..."); @@ -239,12 +323,20 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; - // power save mode 2 - self.set_iovar_u32("pm2_sleep_ret", 0xc8).await; - self.set_iovar_u32("bcn_li_bcn", 1).await; - self.set_iovar_u32("bcn_li_dtim", 1).await; - self.set_iovar_u32("assoc_listen", 10).await; - self.ioctl_set_u32(0x86, 0, 2).await; + // power save mode + if power_save_mode != PowerManagementMode::None { + let mode = power_save_mode.mode(); + if mode == 2 { + self.set_iovar_u32("pm2_sleep_ret", power_save_mode.sleep_ret_ms() as u32) + .await; + self.set_iovar_u32("bcn_li_bcn", power_save_mode.beacon_period() as u32) + .await; + self.set_iovar_u32("bcn_li_dtim", power_save_mode.dtim_period() as u32) + .await; + self.set_iovar_u32("assoc_listen", power_save_mode.assoc() as u32).await; + } + self.ioctl_set_u32(86, 0, mode).await; + } self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any From a2bae33d8460eee6c3af6f20a790f725cf2c5602 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Jan 2023 21:36:17 +0100 Subject: [PATCH 0477/1575] Add separate function to set power management mode. --- examples/rpi-pico-w/src/main.rs | 5 ++++- src/lib.rs | 29 +++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 73cfdf42f..d2f47fd6c 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -70,7 +70,10 @@ async fn main(spawner: Spawner) { spawner.spawn(wifi_task(runner)).unwrap(); - control.init(clm, cyw43::PowerManagementMode::PowerSave).await; + control.init(clm).await; + control + .set_power_management(cyw43::PowerManagementMode::PowerSave) + .await; //control.join_open(env!("WIFI_NETWORK")).await; control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; diff --git a/src/lib.rs b/src/lib.rs index 884cb082b..5733506ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,7 @@ impl PowerManagementMode { } impl<'a> Control<'a> { - pub async fn init(&mut self, clm: &[u8], power_save_mode: PowerManagementMode) { + pub async fn init(&mut self, clm: &[u8]) { const CHUNK_SIZE: usize = 1024; info!("Downloading CLM..."); @@ -323,21 +323,6 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; - // power save mode - if power_save_mode != PowerManagementMode::None { - let mode = power_save_mode.mode(); - if mode == 2 { - self.set_iovar_u32("pm2_sleep_ret", power_save_mode.sleep_ret_ms() as u32) - .await; - self.set_iovar_u32("bcn_li_bcn", power_save_mode.beacon_period() as u32) - .await; - self.set_iovar_u32("bcn_li_dtim", power_save_mode.dtim_period() as u32) - .await; - self.set_iovar_u32("assoc_listen", power_save_mode.assoc() as u32).await; - } - self.ioctl_set_u32(86, 0, mode).await; - } - self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any @@ -349,6 +334,18 @@ impl<'a> Control<'a> { info!("INIT DONE"); } + pub async fn set_power_management(&mut self, mode: PowerManagementMode) { + // power save mode + let mode_num = mode.mode(); + if mode_num == 2 { + self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; + self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; + self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; + self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; + } + self.ioctl_set_u32(86, 0, mode_num).await; + } + pub async fn join_open(&mut self, ssid: &str) { self.set_iovar_u32("ampdu_ba_wsize", 8).await; From 9428c40c8de1285271a5e6ba9ad2a7fed8a9475e Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 3 Jan 2023 22:58:56 +0100 Subject: [PATCH 0478/1575] embassy-boot (rp): Add WatchdogFlash --- embassy-boot/rp/Cargo.toml | 2 + embassy-boot/rp/src/lib.rs | 51 ++++++++++++++++++++++- examples/boot/application/rp/src/bin/a.rs | 7 ++++ examples/boot/bootloader/rp/Cargo.toml | 2 + examples/boot/bootloader/rp/src/main.rs | 6 +-- 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml index 93099b233..ffc36a4e0 100644 --- a/embassy-boot/rp/Cargo.toml +++ b/embassy-boot/rp/Cargo.toml @@ -20,6 +20,8 @@ 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 } +embassy-time = { path = "../../embassy-time", features = ["nightly"] } + cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 85fc81827..6eb429aaa 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -5,7 +5,11 @@ mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; -use embassy_rp::flash::{ERASE_SIZE, WRITE_SIZE}; +use embassy_rp::flash::{Flash, ERASE_SIZE, WRITE_SIZE}; +use embassy_rp::peripherals::{FLASH, WATCHDOG}; +use embassy_rp::watchdog::Watchdog; +use embassy_time::Duration; +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. pub struct BootLoader { @@ -88,3 +92,48 @@ impl Default for BootLoader { Self::new(active, dfu, state) } } + +/// A flash implementation that wraps FLASH and will pet a watchdog when touching flash. +pub struct WatchdogFlash<'d, const SIZE: usize> { + flash: Flash<'d, FLASH, SIZE>, + watchdog: Watchdog, +} + +impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { + /// Start a new watchdog with a given flash and watchdog peripheral and a timeout + pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { + let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash); + let mut watchdog = Watchdog::new(watchdog); + watchdog.start(timeout); + Self { flash, watchdog } + } +} + +impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> { + type Error = as ErrorType>::Error; +} + +impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { + const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; + const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.watchdog.feed(); + self.flash.erase(from, to) + } + fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { + self.watchdog.feed(); + self.flash.write(offset, data) + } +} + +impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { + const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; + fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { + self.watchdog.feed(); + self.flash.read(offset, data) + } + fn capacity(&self) -> usize { + self.flash.capacity() + } +} diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 3736c9141..e3ac634c2 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -7,6 +7,7 @@ use embassy_boot_rp::*; use embassy_executor::Spawner; use embassy_rp::flash::Flash; use embassy_rp::gpio::{Level, Output}; +use embassy_rp::watchdog::Watchdog; use embassy_time::{Duration, Timer}; #[cfg(feature = "panic-probe")] use panic_probe as _; @@ -21,11 +22,16 @@ async fn main(_s: Spawner) { let p = embassy_rp::init(Default::default()); let mut led = Output::new(p.PIN_25, Level::Low); + // Override bootloader watchdog + let mut watchdog = Watchdog::new(p.WATCHDOG); + watchdog.start(Duration::from_secs(8)); + let mut flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); let mut updater = FirmwareUpdater::default(); Timer::after(Duration::from_secs(5)).await; + watchdog.feed(); led.set_high(); let mut offset = 0; let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); @@ -43,6 +49,7 @@ async fn main(_s: Spawner) { .unwrap(); offset += chunk.len(); } + watchdog.feed(); defmt::info!("firmware written, marking update"); updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); Timer::after(Duration::from_secs(2)).await; diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index 580ced22e..c0b576cff 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -11,6 +11,8 @@ 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 } +embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } + cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 5028ec688..4defa01fd 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -5,8 +5,8 @@ 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; +use embassy_rp::flash::ERASE_SIZE; +use embassy_time::Duration; const FLASH_SIZE: usize = 2 * 1024 * 1024; @@ -23,7 +23,7 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let flash: Flash<'_, FLASH, FLASH_SIZE> = Flash::new(p.FLASH); + let flash: WatchdogFlash<'_, FLASH_SIZE> = WatchdogFlash::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); From 413f33948927b4b82a7a9cdd835d226a69fdcefc Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 3 Jan 2023 23:34:50 +0100 Subject: [PATCH 0479/1575] Cleanup --- embassy-boot/rp/src/lib.rs | 2 +- examples/boot/bootloader/rp/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 6eb429aaa..6df34133e 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -93,7 +93,7 @@ impl Default for BootLoader { } } -/// A flash implementation that wraps FLASH and will pet a watchdog when touching flash. +/// A flash implementation that will feed a watchdog when touching flash. pub struct WatchdogFlash<'d, const SIZE: usize> { flash: Flash<'d, FLASH, SIZE>, watchdog: Watchdog, diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 4defa01fd..fb7f0522b 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -23,7 +23,7 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let flash: WatchdogFlash<'_, FLASH_SIZE> = WatchdogFlash::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); + let flash = WatchdogFlash::::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); From 651eec02423c42afb80e8f2eaedb4b618148a26e Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 4 Jan 2023 10:19:39 +1100 Subject: [PATCH 0480/1575] Pass WDT config around By passing WDT config around we can control it more easily and promote sharing it between files. --- embassy-boot/nrf/src/lib.rs | 6 +----- examples/boot/bootloader/nrf/src/main.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 205bbd6df..f40ae62d6 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -149,11 +149,7 @@ pub struct WatchdogFlash<'d> { impl<'d> WatchdogFlash<'d> { /// Start a new watchdog with a given flash and WDT peripheral and a timeout - pub fn start(flash: Nvmc<'d>, wdt: WDT, timeout: u32) -> Self { - let mut config = wdt::Config::default(); - config.timeout_ticks = 32768 * timeout; // timeout seconds - config.run_during_sleep = true; - config.run_during_debug_halt = false; + pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self { let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) { Ok(x) => x, Err(_) => { diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 8266206b3..aca3b857a 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -6,6 +6,7 @@ use cortex_m_rt::{entry, exception}; use defmt_rtt as _; use embassy_boot_nrf::*; use embassy_nrf::nvmc::Nvmc; +use embassy_nrf::wdt; #[entry] fn main() -> ! { @@ -20,8 +21,14 @@ fn main() -> ! { */ let mut bl = BootLoader::default(); + + let mut wdt_config = wdt::Config::default(); + wdt_config.timeout_ticks = 32768 * 5; // timeout seconds + wdt_config.run_during_sleep = true; + wdt_config.run_during_debug_halt = false; + let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), + WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config), ))); unsafe { bl.load(start) } } From 6e6c3cbebcf0cfdb07622df803584f3fbc6a491a Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 4 Jan 2023 10:27:16 +1100 Subject: [PATCH 0481/1575] Cleaned up some doc and memory layout The memory layout of the s140 crept into a number of memory files, which can cause confusion. --- examples/boot/application/nrf/README.md | 4 ++++ examples/boot/application/nrf/memory-bl.x | 2 +- examples/boot/application/nrf/memory.x | 2 +- examples/boot/bootloader/nrf/memory-bm.x | 2 +- examples/boot/bootloader/nrf/memory.x | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md index 703377a20..a6719b505 100644 --- a/examples/boot/application/nrf/README.md +++ b/examples/boot/application/nrf/README.md @@ -32,3 +32,7 @@ cargo objcopy --release --bin b -- -O binary b.bin ``` cargo flash --release --bin a --chip nRF52840_xxAA ``` + +You should then see a solid LED. Pressing button 1 will cause the DFU to be loaded by the bootloader. Upon +successfully loading, you'll see the LED flash. After 5 seconds, because there is no petting of the watchdog, +you'll see the LED go solid again. This indicates that the bootloader has reverted the update. \ No newline at end of file diff --git a/examples/boot/application/nrf/memory-bl.x b/examples/boot/application/nrf/memory-bl.x index 8a32b905f..257d65644 100644 --- a/examples/boot/application/nrf/memory-bl.x +++ b/examples/boot/application/nrf/memory-bl.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); diff --git a/examples/boot/application/nrf/memory.x b/examples/boot/application/nrf/memory.x index 3a54ca460..c6926e422 100644 --- a/examples/boot/application/nrf/memory.x +++ b/examples/boot/application/nrf/memory.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K FLASH : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); diff --git a/examples/boot/bootloader/nrf/memory-bm.x b/examples/boot/bootloader/nrf/memory-bm.x index 8a32b905f..257d65644 100644 --- a/examples/boot/bootloader/nrf/memory-bm.x +++ b/examples/boot/bootloader/nrf/memory-bm.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); diff --git a/examples/boot/bootloader/nrf/memory.x b/examples/boot/bootloader/nrf/memory.x index 8a32b905f..257d65644 100644 --- a/examples/boot/bootloader/nrf/memory.x +++ b/examples/boot/bootloader/nrf/memory.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K ACTIVE : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); From 8497f98de244f0f8800df78d6e83a2fb886016bf Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 4 Jan 2023 11:07:07 +1100 Subject: [PATCH 0482/1575] Provides a means of obtaining the current WDT config Obtaining the current WDT config is important so that we do not have to duplication configuration around the place. A constructor method has been introduced that returns WDT config in accordance with how the register is presently configured. The bootloader example has also been updated to show the watchdog can be obtained and used. --- embassy-nrf/src/wdt.rs | 24 ++++++++++++++++++++++ examples/boot/application/nrf/src/bin/a.rs | 18 ++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index 8760aa301..330ca98bf 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -23,6 +23,30 @@ pub struct Config { pub run_during_debug_halt: bool, } +impl Config { + /// Create a config structure from the current configuration of the WDT + /// peripheral. + pub fn try_new(_wdt: &peripherals::WDT) -> Option { + let r = unsafe { &*WDT::ptr() }; + + #[cfg(not(feature = "_nrf9160"))] + let runstatus = r.runstatus.read().runstatus().bit(); + #[cfg(feature = "_nrf9160")] + let runstatus = r.runstatus.read().runstatuswdt().bit(); + + if runstatus { + let config = r.config.read(); + Some(Self { + timeout_ticks: r.crv.read().bits(), + run_during_sleep: config.sleep().bit(), + run_during_debug_halt: config.halt().bit(), + }) + } else { + None + } + } +} + impl Default for Config { fn default() -> Self { Self { diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 7a404a914..83191f388 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -8,6 +8,7 @@ use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::nvmc::Nvmc; +use embassy_nrf::wdt::{self, Watchdog}; use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); @@ -20,6 +21,23 @@ async fn main(_spawner: Spawner) { //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); //let mut button = Input::new(p.P1_02, Pull::Up); + // The following code block illustrates how to obtain a watchdog that is configured + // as per the existing watchdog. Ordinarily, we'd use the handle returned to "pet" the + // watchdog periodically. If we don't, and we're not going to for this example, then + // the watchdog will cause the device to reset as per its configured timeout in the bootloader. + // This helps is avoid a situation where new firmware might be bad and block our executor. + // If firmware is bad in this way then the bootloader will revert to any previous version. + let wdt_config = wdt::Config::try_new(&p.WDT).unwrap(); + let (_wdt, [_wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) { + Ok(x) => x, + Err(_) => { + // Watchdog already active with the wrong number of handles, waiting for it to timeout... + loop { + cortex_m::asm::wfe(); + } + } + }; + let nvmc = Nvmc::new(p.NVMC); let mut nvmc = BlockingAsync::new(nvmc); From 5aa59e97372e2efed4e35625b754364b1b4839a3 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 4 Jan 2023 12:57:19 +0100 Subject: [PATCH 0483/1575] feat(stm32): Let uart implement embedded-io Read/Write --- embassy-stm32/src/usart/mod.rs | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6f8b6a9e8..233b56baa 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -910,6 +910,46 @@ mod eh1 { } } +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eio { + use embedded_io::asynch::{Read, Write}; + use embedded_io::Io; + + use super::*; + + impl Io for Uart<'_, T, TxDma, RxDma> + where + T: BasicInstance, + { + type Error = Error; + } + + impl Read for Uart<'_, T, TxDma, RxDma> + where + T: BasicInstance, + RxDma: super::RxDma, + { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_until_idle(buf).await + } + } + + impl Write for Uart<'_, T, TxDma, RxDma> + where + T: BasicInstance, + TxDma: super::TxDma, + { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } +} + #[cfg(all( feature = "unstable-traits", feature = "nightly", From 2332d8cd2396a01fdeaf61dce814da79503c8f70 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 4 Jan 2023 13:35:01 +0100 Subject: [PATCH 0484/1575] feat: compile bootloader examples for nRF91 * Add nRF91 as target in CI builds * Add example linker scripts for nrf91 * Make less nRF52 assumptions example config * Add llvm-tools-preview required for cargo objcopy example --- ci.sh | 5 ++++- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/nrf/README.md | 10 +++++----- .../boot/application/nrf/memory-bl-nrf91.x | 19 +++++++++++++++++++ examples/boot/application/nrf/memory-nrf91.x | 16 ++++++++++++++++ examples/boot/application/nrf/src/bin/a.rs | 6 ++++++ examples/boot/application/nrf/src/bin/b.rs | 5 ++++- rust-toolchain.toml | 2 +- 8 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 examples/boot/application/nrf/memory-bl-nrf91.x create mode 100644 examples/boot/application/nrf/memory-nrf91.x diff --git a/ci.sh b/ci.sh index 14a48454d..f59f3f46c 100755 --- a/ci.sh +++ b/ci.sh @@ -81,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/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- 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 \ @@ -106,7 +107,8 @@ cargo batch \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- 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/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 --out-dir out/examples/boot/nrf --bin b \ + --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns --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 \ @@ -116,6 +118,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/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \ --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 1e7a5a84b..9679bbc5e 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" 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-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly", "nrf52840"] } +embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md index a6719b505..5d45f6290 100644 --- a/examples/boot/application/nrf/README.md +++ b/examples/boot/application/nrf/README.md @@ -1,6 +1,6 @@ # Examples using bootloader -Example for nRF52 demonstrating the bootloader. The example consists of application binaries, 'a' +Example for nRF demonstrating the bootloader. The example consists of application binaries, 'a' which allows you to press a button to start the DFU process, and 'b' which is the updated application. @@ -20,19 +20,19 @@ application. cp memory-bl.x ../../bootloader/nrf/memory.x # Flash bootloader -cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --release --chip nRF52840_xxAA +cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA # Build 'b' cargo build --release --bin b # Generate binary for 'b' -cargo objcopy --release --bin b -- -O binary b.bin +cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin ``` # Flash `a` (which includes b.bin) ``` -cargo flash --release --bin a --chip nRF52840_xxAA +cargo flash --release --bin a --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --chip nRF52840_xxAA ``` You should then see a solid LED. Pressing button 1 will cause the DFU to be loaded by the bootloader. Upon successfully loading, you'll see the LED flash. After 5 seconds, because there is no petting of the watchdog, -you'll see the LED go solid again. This indicates that the bootloader has reverted the update. \ No newline at end of file +you'll see the LED go solid again. This indicates that the bootloader has reverted the update. diff --git a/examples/boot/application/nrf/memory-bl-nrf91.x b/examples/boot/application/nrf/memory-bl-nrf91.x new file mode 100644 index 000000000..14ceffa73 --- /dev/null +++ b/examples/boot/application/nrf/memory-bl-nrf91.x @@ -0,0 +1,19 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* Assumes Secure Partition Manager (SPM) flashed at the start */ + FLASH : ORIGIN = 0x00050000, LENGTH = 24K + BOOTLOADER_STATE : ORIGIN = 0x00056000, LENGTH = 4K + ACTIVE : ORIGIN = 0x00057000, LENGTH = 64K + DFU : ORIGIN = 0x00067000, LENGTH = 68K + RAM (rwx) : ORIGIN = 0x20018000, LENGTH = 32K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); + +__bootloader_active_start = ORIGIN(ACTIVE); +__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE); + +__bootloader_dfu_start = ORIGIN(DFU); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); diff --git a/examples/boot/application/nrf/memory-nrf91.x b/examples/boot/application/nrf/memory-nrf91.x new file mode 100644 index 000000000..2bc13c0d6 --- /dev/null +++ b/examples/boot/application/nrf/memory-nrf91.x @@ -0,0 +1,16 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* Assumes Secure Partition Manager (SPM) flashed at the start */ + BOOTLOADER : ORIGIN = 0x00050000, LENGTH = 24K + BOOTLOADER_STATE : ORIGIN = 0x00056000, LENGTH = 4K + FLASH : ORIGIN = 0x00057000, LENGTH = 64K + DFU : ORIGIN = 0x00067000, LENGTH = 68K + RAM (rwx) : ORIGIN = 0x20018000, LENGTH = 32K +} + +__bootloader_state_start = ORIGIN(BOOTLOADER_STATE); +__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); + +__bootloader_dfu_start = ORIGIN(DFU); +__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 83191f388..090a05b23 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -16,11 +16,17 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); + let mut button = Input::new(p.P0_11, Pull::Up); let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); + //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); //let mut button = Input::new(p.P1_02, Pull::Up); + // nRF91 DK + // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); + // let mut button = Input::new(p.P0_06, Pull::Up); + // The following code block illustrates how to obtain a watchdog that is configured // as per the existing watchdog. Ordinarily, we'd use the handle returned to "pet" the // watchdog periodically. If we don't, and we're not going to for this example, then diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 1373f277d..15ebce5fa 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs @@ -12,7 +12,10 @@ use panic_reset as _; async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); - //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); + // let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); + + // nRF91 DK + // let mut led = Output::new(p.P0_02, Level::Low, OutputDrive::Standard); loop { led.set_high(); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 55539405f..f2adc4357 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,7 +2,7 @@ # https://rust-lang.github.io/rustup-components-history [toolchain] channel = "nightly-2022-11-22" -components = [ "rust-src", "rustfmt" ] +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", "thumbv7m-none-eabi", From 68c186309f5da13266118a5c6b90c9082f73cbfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 1 Jan 2023 21:34:20 +0100 Subject: [PATCH 0485/1575] rp: Common init function for BufferedUart BufferedUart, BufferedUartRx and BufferedUartTX can all use the same init code. --- embassy-rp/src/uart/buffered.rs | 186 +++++++++++--------------------- 1 file changed, 64 insertions(+), 122 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 1d1fe37c2..d853618a0 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -38,6 +38,38 @@ pub struct BufferedUartTx<'d, T: Instance> { phantom: PhantomData<&'d mut T>, } +fn init<'d, T: Instance + 'd>( + irq: PeripheralRef<'d, T::Interrupt>, + tx: Option>, + rx: Option>, + rts: Option>, + cts: Option>, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, +) { + let regs = T::regs(); + unsafe { + regs.uartimsc().modify(|w| { + w.set_rxim(true); + w.set_rtim(true); + w.set_txim(true); + }); + } + + super::Uart::<'d, T, Async>::init(tx, rx, rts, cts, config); + + let state = T::state(); + 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) }; + + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); +} + impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, @@ -48,17 +80,18 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(tx, rx); - Self::new_inner( + into_ref!(irq, tx, rx); + init::( irq, - tx.map_into(), - rx.map_into(), + Some(tx.map_into()), + Some(rx.map_into()), None, None, tx_buffer, rx_buffer, config, - ) + ); + Self { phantom: PhantomData } } pub fn new_with_rtscts( @@ -72,58 +105,17 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(tx, rx, cts, rts); - Self::new_inner( + into_ref!(irq, tx, rx, cts, rts); + init::( irq, - tx.map_into(), - rx.map_into(), + Some(tx.map_into()), + Some(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) }; - - unsafe { - regs.uartimsc().modify(|w| { - w.set_rxim(true); - w.set_rtim(true); - w.set_txim(true); - }); - } - - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); - Self { phantom: PhantomData } } @@ -143,8 +135,9 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(rx); - Self::new_inner(irq, rx.map_into(), None, rx_buffer, config) + into_ref!(irq, rx); + init::(irq, None, Some(rx.map_into()), None, None, &mut [], rx_buffer, config); + Self { phantom: PhantomData } } pub fn new_with_rts( @@ -155,43 +148,17 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { 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( + into_ref!(irq, rx, rts); + init::( + irq, None, - Some(rx.reborrow()), - rts.as_mut().map(|x| x.reborrow()), + Some(rx.map_into()), + Some(rts.map_into()), None, + &mut [], + rx_buffer, 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) }; - - unsafe { - regs.uartimsc().modify(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); - } - - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); - Self { phantom: PhantomData } } @@ -231,7 +198,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn consume(amt: usize) { let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; - rx_reader.pop_done(amt) + rx_reader.pop_done(amt); } } @@ -243,8 +210,9 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { tx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(tx); - Self::new_inner(irq, tx.map_into(), None, tx_buffer, config) + into_ref!(irq, tx); + init::(irq, Some(tx.map_into()), None, None, None, tx_buffer, &mut [], config); + Self { phantom: PhantomData } } pub fn new_with_cts( @@ -255,42 +223,17 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { 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()), + into_ref!(irq, tx, cts); + init::( + irq, + Some(tx.map_into()), None, None, - cts.as_mut().map(|x| x.reborrow()), + Some(cts.map_into()), + tx_buffer, + &mut [], 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) }; - - unsafe { - regs.uartimsc().modify(|w| { - w.set_txim(true); - }); - } - - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); - Self { phantom: PhantomData } } @@ -306,10 +249,9 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { if n == 0 { state.tx_waker.register(cx.waker()); return Poll::Pending; - } else { - unsafe { T::Interrupt::steal() }.pend(); } + unsafe { T::Interrupt::steal() }.pend(); Poll::Ready(Ok(n)) }) } From a24037edf9d04087111c1d1dc71c92cc0ad83709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 1 Jan 2023 22:02:45 +0100 Subject: [PATCH 0486/1575] rp: Fix BufferedUart drop code Only unregister the interrupt handler when both parts are inactive --- embassy-hal-common/src/atomic_ring_buffer.rs | 4 ++ embassy-rp/src/uart/buffered.rs | 51 ++++++++++---------- examples/rp/src/bin/uart_buffered_split.rs | 2 +- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index a8a6a2166..4c944d763 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -81,6 +81,10 @@ impl RingBuffer { Writer(self) } + pub fn len(&self) -> usize { + self.len.load(Ordering::Relaxed) + } + pub fn is_full(&self) -> bool { let len = self.len.load(Ordering::Relaxed); let start = self.start.load(Ordering::Relaxed); diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index d853618a0..9164794b1 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -27,7 +27,8 @@ impl State { } pub struct BufferedUart<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, + rx: BufferedUartRx<'d, T>, + tx: BufferedUartTx<'d, T>, } pub struct BufferedUartRx<'d, T: Instance> { @@ -91,7 +92,10 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer, config, ); - Self { phantom: PhantomData } + Self { + rx: BufferedUartRx { phantom: PhantomData }, + tx: BufferedUartTx { phantom: PhantomData }, + } } pub fn new_with_rtscts( @@ -116,14 +120,14 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer, config, ); - Self { phantom: PhantomData } + Self { + rx: BufferedUartRx { phantom: PhantomData }, + tx: BufferedUartTx { phantom: PhantomData }, + } } - pub fn split(&mut self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { - ( - BufferedUartRx { phantom: PhantomData }, - BufferedUartTx { phantom: PhantomData }, - ) + pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { + (self.rx, self.tx) } } @@ -269,35 +273,32 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } } -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) { + let state = T::state(); unsafe { - T::Interrupt::steal().disable(); - let state = T::state(); - state.tx_buf.deinit(); state.rx_buf.deinit(); + + // TX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.tx_buf.len() == 0 { + T::Interrupt::steal().disable(); + } } } } impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { fn drop(&mut self) { + let state = T::state(); unsafe { - T::Interrupt::steal().disable(); - let state = T::state(); state.tx_buf.deinit(); - state.rx_buf.deinit(); + + // RX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.rx_buf.len() == 0 { + T::Interrupt::steal().disable(); + } } } } diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index 36f31c906..a8a682274 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -29,7 +29,7 @@ async fn main(spawner: Spawner) { let irq = interrupt::take!(UART0_IRQ); let tx_buf = &mut singleton!([0u8; 16])[..]; let rx_buf = &mut singleton!([0u8; 16])[..]; - let mut uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); + let uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); let (rx, mut tx) = uart.split(); unwrap!(spawner.spawn(reader(rx))); From 840a75674b632485d5b45cfece27870ded0edfcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Wed, 4 Jan 2023 16:40:54 +0100 Subject: [PATCH 0487/1575] rp: Disable RX interrupts when ring buffer is full When data is in the RX fifo the RX timeout interrupt goes high again even after clearing it. The result is a deadlock because execution is stuck in the interrupt handler. No other code can run to clear the receive buffer. Enable and disable RX interrupts based on the buffer fill level. Use the same approach for the TX code path. --- embassy-rp/src/uart/buffered.rs | 87 +++++++++++++-------------------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9164794b1..6465a20c6 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -49,15 +49,6 @@ fn init<'d, T: Instance + 'd>( rx_buffer: &'d mut [u8], config: Config, ) { - let regs = T::regs(); - unsafe { - regs.uartimsc().modify(|w| { - w.set_rxim(true); - w.set_rtim(true); - w.set_txim(true); - }); - } - super::Uart::<'d, T, Async>::init(tx, rx, rts, cts, config); let state = T::state(); @@ -168,6 +159,8 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { poll_fn(move |cx| { + unsafe { T::Interrupt::steal() }.pend(); + let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { @@ -186,6 +179,8 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn fill_buf<'a>() -> impl Future> { poll_fn(move |cx| { + unsafe { T::Interrupt::steal() }.pend(); + let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let (p, n) = rx_reader.pop_buf(); @@ -310,40 +305,31 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { 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); - }); + let mut mis = r.uartimsc().read(); - if ris.peris() { - warn!("Parity error"); - r.uarticr().write(|w| { - w.set_peic(true); - }); - } + // Errors if ris.feris() { warn!("Framing error"); - r.uarticr().write(|w| { - w.set_feic(true); - }); + } + if ris.peris() { + warn!("Parity error"); } 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); - }); } + // Clear any error flags + r.uarticr().write(|w| { + w.set_feic(true); + w.set_peic(true); + w.set_beic(true); + w.set_oeic(true); + }); + // RX let mut rx_writer = s.rx_buf.writer(); let rx_buf = rx_writer.push_slice(); let mut n_read = 0; @@ -358,33 +344,30 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { rx_writer.push_done(n_read); s.rx_waker.wake(); } + // Disable any further RX interrupts when the buffer becomes full. + mis.set_rxim(!s.rx_buf.is_full()); + mis.set_rtim(!s.rx_buf.is_full()); // TX let mut tx_reader = s.tx_buf.reader(); let tx_buf = tx_reader.pop_slice(); - if tx_buf.len() == 0 { - // Disable interrupt until we have something to transmit again - r.uartimsc().modify(|w| { - w.set_txim(false); - }); - } else { - r.uartimsc().modify(|w| { - w.set_txim(true); - }); - - let mut n_written = 0; - for tx_byte in tx_buf.iter_mut() { - if r.uartfr().read().txff() { - break; - } - r.uartdr().write(|w| w.set_data(*tx_byte)); - n_written += 1; - } - if n_written > 0 { - tx_reader.pop_done(n_written); - s.tx_waker.wake(); + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; } + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } + // Disable the TX interrupt when we do not have more data to send. + mis.set_txim(!s.tx_buf.is_empty()); + + // Update interrupt mask. + r.uartimsc().write_value(mis); } } From 6d4c6e0481f0738b8306172e7c5d54aa2758b74e Mon Sep 17 00:00:00 2001 From: Pedro Ferreira Date: Wed, 4 Jan 2023 20:27:07 +0100 Subject: [PATCH 0488/1575] rp2040: add {tx,rx}-only constructors to UART --- embassy-rp/src/uart/mod.rs | 32 ++++++++++++++++++++--- examples/rp/src/bin/uart_unidir.rs | 42 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 examples/rp/src/bin/uart_unidir.rs diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7e7bcaf30..bbbf97c01 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -98,7 +98,19 @@ pub struct UartRx<'d, T: Instance, M: Mode> { } impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { - fn new(tx_dma: Option>) -> Self { + /// Create a new DMA-enabled UART which can only send data + pub fn new( + _uart: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(tx, tx_dma); + Uart::::init(Some(tx.map_into()), None, None, None, config); + Self::new_inner(Some(tx_dma.map_into())) + } + + fn new_inner(tx_dma: Option>) -> Self { Self { tx_dma, phantom: PhantomData, @@ -140,7 +152,19 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { } impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { - fn new(rx_dma: Option>) -> Self { + /// Create a new DMA-enabled UART which can only send data + pub fn new( + _uart: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(rx, rx_dma); + Uart::::init(Some(rx.map_into()), None, None, None, config); + Self::new_inner(Some(rx_dma.map_into())) + } + + fn new_inner(rx_dma: Option>) -> Self { Self { rx_dma, phantom: PhantomData, @@ -295,8 +319,8 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { ); Self { - tx: UartTx::new(tx_dma), - rx: UartRx::new(rx_dma), + tx: UartTx::new_inner(tx_dma), + rx: UartRx::new_inner(rx_dma), } } diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs new file mode 100644 index 000000000..f56e7009f --- /dev/null +++ b/examples/rp/src/bin/uart_unidir.rs @@ -0,0 +1,42 @@ +//! test TX-only and RX-only UARTs. You need to connect GPIO0 to GPIO5 for +//! this to work + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::peripherals::UART1; +use embassy_rp::uart::{Async, Config, UartRx, UartTx}; +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 mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); + let uart_rx = UartRx::new(p.UART1, p.PIN_5, p.DMA_CH1, Config::default()); + + unwrap!(spawner.spawn(reader(uart_rx))); + + info!("Writing..."); + loop { + let data = [1u8, 2, 3, 4, 5, 6, 7, 8]; + info!("TX {:?}", data); + uart_tx.write(&data).await.unwrap(); + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::task] +async fn reader(mut rx: UartRx<'static, UART1, Async>) { + info!("Reading..."); + loop { + // read a total of 4 transmissions (32 / 8) and then print the result + let mut buf = [0; 32]; + rx.read(&mut buf).await.unwrap(); + info!("RX {:?}", buf); + } +} From 0ecc54f58c7a75bbaf89c875a23d500137dd52b6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 5 Jan 2023 01:46:35 +0100 Subject: [PATCH 0489/1575] usb/driver: document ControlPipe. --- embassy-usb-driver/src/lib.rs | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 9300ff812..71e5267c6 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -215,6 +215,70 @@ pub trait EndpointOut: Endpoint { async fn read(&mut self, buf: &mut [u8]) -> Result; } +/// Trait for USB control pipe. +/// +/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single +/// unit, and manages them together to implement the control pipe state machine. +/// +/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that +/// many USB peripherals treat the control pipe endpoints differently (different registers, +/// different procedures), usually to accelerate processing in hardware somehow. A separate +/// trait allows the driver to handle it specially. +/// +/// The call sequences made by the USB stack to the ControlPipe are the following: +/// +/// - control in/out with len=0: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// accept() or reject() +/// ``` +/// +/// - control out with len != 0: +/// +/// ```not_rust +/// setup() +/// data_out(first=true, last=false) +/// data_out(first=false, last=false) +/// ... +/// data_out(first=false, last=false) +/// data_out(first=false, last=true) +/// (...processing...) +/// accept() or reject() +/// ``` +/// +/// - control in with len != 0, accepted: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// data_in(first=true, last=false) +/// data_in(first=false, last=false) +/// ... +/// data_in(first=false, last=false) +/// data_in(first=false, last=true) +/// (NO `accept()`!!! This is because calling `data_in` already implies acceptance.) +/// ``` +/// +/// - control in with len != 0, rejected: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// reject() +/// ``` +/// +/// The driver is responsible for handling the status stage. The stack DOES NOT do zero-length +/// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should +/// be triggered by either `accept()`, or `data_in` with `last = true`. +/// +/// Note that the host can abandon a control request and send a new STATUS packet any time. If +/// a STATUS packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`, +/// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`) +/// to let the stack call `setup()` again to start handling the new control request. Not doing +/// so will cause things to get stuck, because the host will never read/send the packet we're +/// waiting for. pub trait ControlPipe { /// Maximum packet size for the control pipe fn max_packet_size(&self) -> usize; From 3c537a9fae5ed808a2efcc02a11cfdb8eebe0c6c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 5 Jan 2023 14:46:31 +0100 Subject: [PATCH 0490/1575] usb/driver: fix STATUS -> SETUP --- embassy-usb-driver/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 71e5267c6..d7238dc7d 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -273,8 +273,8 @@ pub trait EndpointOut: Endpoint { /// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should /// be triggered by either `accept()`, or `data_in` with `last = true`. /// -/// Note that the host can abandon a control request and send a new STATUS packet any time. If -/// a STATUS packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`, +/// Note that the host can abandon a control request and send a new SETUP packet any time. If +/// a SETUP packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`, /// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`) /// to let the stack call `setup()` again to start handling the new control request. Not doing /// so will cause things to get stuck, because the host will never read/send the packet we're From 1096a9746c0843e4a26fcec3fca7f9d6d9d57f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 5 Jan 2023 18:45:58 +0100 Subject: [PATCH 0491/1575] rp: Improve BufferedUart interrupt handling * Only clear interrupt flags that have fired (so that we do not lose any error flags) * Enable RX interrupt when a read is requested, disable it when the RX buffer is full * Rework TX interrupt handling: its "edge" triggered by a FIFO threshold --- embassy-rp/src/uart/buffered.rs | 79 ++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6465a20c6..b9fc33eae 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -7,6 +7,7 @@ use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use super::*; +use crate::RegExt; pub struct State { tx_waker: AtomicWaker, @@ -57,6 +58,19 @@ fn init<'d, T: Instance + 'd>( let len = rx_buffer.len(); unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + // From the datasheet: + // "The transmit interrupt is based on a transition through a level, rather + // than on the level itself. When the interrupt and the UART is enabled + // before any data is written to the transmit FIFO the interrupt is not set. + // The interrupt is only set, after written data leaves the single location + // of the transmit FIFO and it becomes empty." + // + // This means we can leave the interrupt enabled the whole time as long as + // we clear it after it happens. The downside is that the we manually have + // to pend the ISR when we want data transmission to start. + let regs = T::regs(); + unsafe { regs.uartimsc().write_set(|w| w.set_txim(true)) }; + irq.set_handler(on_interrupt::); irq.unpend(); irq.enable(); @@ -159,8 +173,6 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { poll_fn(move |cx| { - unsafe { T::Interrupt::steal() }.pend(); - let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { @@ -173,14 +185,22 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { return Poll::Pending; } + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + let regs = T::regs(); + unsafe { + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } + Poll::Ready(Ok(n)) }) } fn fill_buf<'a>() -> impl Future> { poll_fn(move |cx| { - unsafe { T::Interrupt::steal() }.pend(); - let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let (p, n) = rx_reader.pop_buf(); @@ -198,6 +218,16 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; rx_reader.pop_done(amt); + + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + let regs = T::regs(); + unsafe { + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } } } @@ -250,6 +280,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { return Poll::Pending; } + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. unsafe { T::Interrupt::steal() }.pend(); Poll::Ready(Ok(n)) }) @@ -299,14 +333,22 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { } pub(crate) unsafe fn on_interrupt(_: *mut ()) { - trace!("on_interrupt"); - let r = T::regs(); let s = T::state(); unsafe { + // Clear TX and error interrupt flags + // RX interrupt flags are cleared by reading from the FIFO. let ris = r.uartris().read(); - let mut mis = r.uartimsc().read(); + r.uarticr().write(|w| { + w.set_txic(ris.txris()); + w.set_feic(ris.feris()); + w.set_peic(ris.peris()); + w.set_beic(ris.beris()); + w.set_oeic(ris.oeris()); + }); + + trace!("on_interrupt ris={=u32:#X}", ris.0); // Errors if ris.feris() { @@ -321,13 +363,6 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { if ris.oeris() { warn!("Overrun error"); } - // Clear any error flags - r.uarticr().write(|w| { - w.set_feic(true); - w.set_peic(true); - w.set_beic(true); - w.set_oeic(true); - }); // RX let mut rx_writer = s.rx_buf.writer(); @@ -345,8 +380,12 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { s.rx_waker.wake(); } // Disable any further RX interrupts when the buffer becomes full. - mis.set_rxim(!s.rx_buf.is_full()); - mis.set_rtim(!s.rx_buf.is_full()); + if s.rx_buf.is_full() { + r.uartimsc().write_clear(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } // TX let mut tx_reader = s.tx_buf.reader(); @@ -363,11 +402,9 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { tx_reader.pop_done(n_written); s.tx_waker.wake(); } - // Disable the TX interrupt when we do not have more data to send. - mis.set_txim(!s.tx_buf.is_empty()); - - // Update interrupt mask. - r.uartimsc().write_value(mis); + // The TX interrupt only triggers once when the FIFO threshold is + // crossed. No need to disable it when the buffer becomes empty + // as it does re-trigger anymore once we have cleared it. } } From 539f97da535edaf7607890658b21223ff6e02cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 5 Jan 2023 22:00:44 +0100 Subject: [PATCH 0492/1575] rp: Fix formatting string to please CI --- embassy-rp/src/uart/buffered.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index b9fc33eae..0da5bfca1 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -348,7 +348,7 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { w.set_oeic(ris.oeris()); }); - trace!("on_interrupt ris={=u32:#X}", ris.0); + trace!("on_interrupt ris={:#X}", ris.0); // Errors if ris.feris() { From 4c4b47f78a57e5bf3d05b319759351a005e69206 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 6 Jan 2023 14:24:29 +0100 Subject: [PATCH 0493/1575] feat(stm32): Add embedded-io traits for UartRx and UartTx --- embassy-stm32/src/usart/mod.rs | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 233b56baa..1c13d9eca 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -948,6 +948,45 @@ mod eio { self.blocking_flush() } } + + impl Io for UartRx<'_, T, RxDma> + where + T: BasicInstance, + { + type Error = Error; + } + + impl Read for UartRx<'_, T, RxDma> + where + T: BasicInstance, + RxDma: super::RxDma, + { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_until_idle(buf).await + } + } + + impl Io for UartTx<'_, T, TxDma> + where + T: BasicInstance, + { + type Error = Error; + } + + impl Write for UartTx<'_, T, TxDma> + where + T: BasicInstance, + TxDma: super::TxDma, + { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } } #[cfg(all( From f8afc3c8828c334baaa399f73764ba45a3f69799 Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Sun, 8 Jan 2023 15:36:35 +0100 Subject: [PATCH 0494/1575] Add samples for nrf5340 --- examples/nrf5340/.cargo/config.toml | 9 +++ examples/nrf5340/Cargo.toml | 64 ++++++++++++++++++ examples/nrf5340/build.rs | 35 ++++++++++ examples/nrf5340/memory.x | 7 ++ examples/nrf5340/src/bin/awaitable_timer.rs | 26 ++++++++ examples/nrf5340/src/bin/blinky.rs | 21 ++++++ examples/nrf5340/src/bin/buffered_uart.rs | 57 ++++++++++++++++ examples/nrf5340/src/bin/channel.rs | 43 ++++++++++++ .../src/bin/channel_sender_receiver.rs | 50 ++++++++++++++ examples/nrf5340/src/bin/gpiote_channel.rs | 66 +++++++++++++++++++ examples/nrf5340/src/bin/gpiote_port.rs | 34 ++++++++++ examples/nrf5340/src/bin/uart.rs | 35 ++++++++++ examples/nrf5340/src/bin/uart_idle.rs | 35 ++++++++++ examples/nrf5340/src/bin/uart_split.rs | 60 +++++++++++++++++ 14 files changed, 542 insertions(+) create mode 100644 examples/nrf5340/.cargo/config.toml create mode 100644 examples/nrf5340/Cargo.toml create mode 100644 examples/nrf5340/build.rs create mode 100644 examples/nrf5340/memory.x create mode 100644 examples/nrf5340/src/bin/awaitable_timer.rs create mode 100644 examples/nrf5340/src/bin/blinky.rs create mode 100644 examples/nrf5340/src/bin/buffered_uart.rs create mode 100644 examples/nrf5340/src/bin/channel.rs create mode 100644 examples/nrf5340/src/bin/channel_sender_receiver.rs create mode 100644 examples/nrf5340/src/bin/gpiote_channel.rs create mode 100644 examples/nrf5340/src/bin/gpiote_port.rs create mode 100644 examples/nrf5340/src/bin/uart.rs create mode 100644 examples/nrf5340/src/bin/uart_idle.rs create mode 100644 examples/nrf5340/src/bin/uart_split.rs diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml new file mode 100644 index 000000000..ff0879c8c --- /dev/null +++ b/examples/nrf5340/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF5340_xxAA with your chip as listed in `probe-run --list-chips` +runner = "probe-run --chip nRF5340_xxAA" + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml new file mode 100644 index 000000000..03485711e --- /dev/null +++ b/examples/nrf5340/Cargo.toml @@ -0,0 +1,64 @@ +[package] +edition = "2021" +name = "embassy-nrf-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[features] +default = ["nightly"] +nightly = [ + "embassy-executor/nightly", + "embassy-nrf/nightly", + "embassy-net/nightly", + "embassy-nrf/unstable-traits", + "embassy-usb", + "embedded-io/async", + "embassy-net", +] + +[dependencies] +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +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", +] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ + "defmt", + "nrf5340-app-s", + "time-driver-rtc1", + "gpiote", + "unstable-pac", +] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ + "defmt", + "tcp", + "dhcpv4", + "medium-ethernet", +], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ + "defmt", +], optional = true } +embedded-io = "0.4.0" + + +defmt = "0.3" +defmt-rtt = "0.4" + +static_cell = "1.0" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", +] } +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 } diff --git a/examples/nrf5340/build.rs b/examples/nrf5340/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf5340/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/nrf5340/memory.x b/examples/nrf5340/memory.x new file mode 100644 index 000000000..a122dc24a --- /dev/null +++ b/examples/nrf5340/memory.x @@ -0,0 +1,7 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the NRF5340 */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/nrf5340/src/bin/awaitable_timer.rs b/examples/nrf5340/src/bin/awaitable_timer.rs new file mode 100644 index 000000000..b32af236c --- /dev/null +++ b/examples/nrf5340/src/bin/awaitable_timer.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::timer::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); + // default frequency is 1MHz, so this triggers every second + t.cc(0).write(1_000_000); + // clear the timer value on cc[0] compare match + t.cc(0).short_compare_clear(); + t.start(); + + loop { + // wait for compare match + t.cc(0).wait().await; + info!("hardware timer tick"); + } +} diff --git a/examples/nrf5340/src/bin/blinky.rs b/examples/nrf5340/src/bin/blinky.rs new file mode 100644 index 000000000..3422cedf0 --- /dev/null +++ b/examples/nrf5340/src/bin/blinky.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} diff --git a/examples/nrf5340/src/bin/buffered_uart.rs b/examples/nrf5340/src/bin/buffered_uart.rs new file mode 100644 index 000000000..25a0ca237 --- /dev/null +++ b/examples/nrf5340/src/bin/buffered_uart.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::buffered_uarte::{BufferedUarte, State}; +use embassy_nrf::{interrupt, uarte}; +use embedded_io::asynch::{BufRead, Write}; +use futures::pin_mut; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let mut tx_buffer = [0u8; 4096]; + let mut rx_buffer = [0u8; 4096]; + + let irq = interrupt::take!(SERIAL0); + let mut state = State::new(); + // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) + let u = BufferedUarte::new( + &mut state, + p.UARTETWISPI0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + irq, + p.P0_08, + p.P0_06, + p.P0_07, + p.P0_05, + config, + &mut rx_buffer, + &mut tx_buffer, + ); + pin_mut!(u); + + info!("uarte initialized!"); + + unwrap!(u.write_all(b"Hello!\r\n").await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + let buf = unwrap!(u.fill_buf().await); + info!("read done, got {}", buf); + + // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again + let n = buf.len(); + u.consume(n); + } +} diff --git a/examples/nrf5340/src/bin/channel.rs b/examples/nrf5340/src/bin/channel.rs new file mode 100644 index 000000000..425d43051 --- /dev/null +++ b/examples/nrf5340/src/bin/channel.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +enum LedState { + On, + Off, +} + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::task] +async fn my_task() { + loop { + CHANNEL.send(LedState::On).await; + Timer::after(Duration::from_secs(1)).await; + CHANNEL.send(LedState::Off).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); + + unwrap!(spawner.spawn(my_task())); + + loop { + match CHANNEL.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} diff --git a/examples/nrf5340/src/bin/channel_sender_receiver.rs b/examples/nrf5340/src/bin/channel_sender_receiver.rs new file mode 100644 index 000000000..9628c0525 --- /dev/null +++ b/examples/nrf5340/src/bin/channel_sender_receiver.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::channel::{Channel, Receiver, Sender}; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +enum LedState { + On, + Off, +} + +static CHANNEL: StaticCell> = StaticCell::new(); + +#[embassy_executor::task] +async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { + loop { + sender.send(LedState::On).await; + Timer::after(Duration::from_secs(1)).await; + sender.send(LedState::Off).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::task] +async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { + let mut led = Output::new(led, Level::Low, OutputDrive::Standard); + + loop { + match receiver.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let channel = CHANNEL.init(Channel::new()); + + unwrap!(spawner.spawn(send_task(channel.sender()))); + unwrap!(spawner.spawn(recv_task(p.P0_28.degrade(), channel.receiver()))); +} diff --git a/examples/nrf5340/src/bin/gpiote_channel.rs b/examples/nrf5340/src/bin/gpiote_channel.rs new file mode 100644 index 000000000..ceab1194a --- /dev/null +++ b/examples/nrf5340/src/bin/gpiote_channel.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Input, Pull}; +use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Starting!"); + + let ch1 = InputChannel::new( + p.GPIOTE_CH0, + Input::new(p.P0_23, Pull::Up), + InputChannelPolarity::HiToLo, + ); + let ch2 = InputChannel::new( + p.GPIOTE_CH1, + Input::new(p.P0_24, Pull::Up), + InputChannelPolarity::LoToHi, + ); + let ch3 = InputChannel::new( + p.GPIOTE_CH2, + Input::new(p.P0_08, Pull::Up), + InputChannelPolarity::Toggle, + ); + let ch4 = InputChannel::new( + p.GPIOTE_CH3, + Input::new(p.P0_09, Pull::Up), + InputChannelPolarity::Toggle, + ); + + let button1 = async { + loop { + ch1.wait().await; + info!("Button 1 pressed") + } + }; + + let button2 = async { + loop { + ch2.wait().await; + info!("Button 2 released") + } + }; + + let button3 = async { + loop { + ch3.wait().await; + info!("Button 3 toggled") + } + }; + + let button4 = async { + loop { + ch4.wait().await; + info!("Button 4 toggled") + } + }; + + futures::join!(button1, button2, button3, button4); +} diff --git a/examples/nrf5340/src/bin/gpiote_port.rs b/examples/nrf5340/src/bin/gpiote_port.rs new file mode 100644 index 000000000..0cc911ad2 --- /dev/null +++ b/examples/nrf5340/src/bin/gpiote_port.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task(pool_size = 4)] +async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { + loop { + pin.wait_for_low().await; + info!("Button {:?} pressed!", n); + pin.wait_for_high().await; + info!("Button {:?} released!", n); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Starting!"); + + let btn1 = Input::new(p.P0_23.degrade(), Pull::Up); + let btn2 = Input::new(p.P0_24.degrade(), Pull::Up); + let btn3 = Input::new(p.P0_08.degrade(), Pull::Up); + let btn4 = Input::new(p.P0_09.degrade(), Pull::Up); + + unwrap!(spawner.spawn(button_task(1, btn1))); + unwrap!(spawner.spawn(button_task(2, btn2))); + unwrap!(spawner.spawn(button_task(3, btn3))); + unwrap!(spawner.spawn(button_task(4, btn4))); +} diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs new file mode 100644 index 000000000..1db450576 --- /dev/null +++ b/examples/nrf5340/src/bin/uart.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + + info!("uarte initialized!"); + + // Message must be in SRAM + let mut buf = [0; 8]; + buf.copy_from_slice(b"Hello!\r\n"); + + unwrap!(uart.write(&buf).await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + unwrap!(uart.read(&mut buf).await); + info!("writing..."); + unwrap!(uart.write(&buf).await); + } +} diff --git a/examples/nrf5340/src/bin/uart_idle.rs b/examples/nrf5340/src/bin/uart_idle.rs new file mode 100644 index 000000000..327fc4b23 --- /dev/null +++ b/examples/nrf5340/src/bin/uart_idle.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); + + info!("uarte initialized!"); + + // Message must be in SRAM + let mut buf = [0; 8]; + buf.copy_from_slice(b"Hello!\r\n"); + + unwrap!(tx.write(&buf).await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + let n = unwrap!(rx.read_until_idle(&mut buf).await); + info!("got {} bytes", n); + } +} diff --git a/examples/nrf5340/src/bin/uart_split.rs b/examples/nrf5340/src/bin/uart_split.rs new file mode 100644 index 000000000..1bff382fb --- /dev/null +++ b/examples/nrf5340/src/bin/uart_split.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::peripherals::UARTETWISPI0; +use embassy_nrf::uarte::UarteRx; +use embassy_nrf::{interrupt, uarte}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let (mut tx, rx) = uart.split(); + + info!("uarte initialized!"); + + // Spawn a task responsible purely for reading + + unwrap!(spawner.spawn(reader(rx))); + + // Message must be in SRAM + { + let mut buf = [0; 23]; + buf.copy_from_slice(b"Type 8 chars to echo!\r\n"); + + unwrap!(tx.write(&buf).await); + info!("wrote hello in uart!"); + } + + // Continue reading in this main task and write + // back out the buffer we receive from the read + // task. + loop { + let buf = CHANNEL.recv().await; + info!("writing..."); + unwrap!(tx.write(&buf).await); + } +} + +#[embassy_executor::task] +async fn reader(mut rx: UarteRx<'static, UARTETWISPI0>) { + let mut buf = [0; 8]; + loop { + info!("reading..."); + unwrap!(rx.read(&mut buf).await); + CHANNEL.send(buf).await; + } +} From 401185b1d95a2519ee94e5d5654cc9325fe85eec Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Sun, 8 Jan 2023 16:25:51 +0100 Subject: [PATCH 0495/1575] Change UART pins for nRF5340 DK --- examples/nrf5340/src/bin/uart.rs | 2 +- examples/nrf5340/src/bin/uart_split.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs index 1db450576..0f2b7b1e3 100644 --- a/examples/nrf5340/src/bin/uart.rs +++ b/examples/nrf5340/src/bin/uart.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(SERIAL0); - let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); info!("uarte initialized!"); diff --git a/examples/nrf5340/src/bin/uart_split.rs b/examples/nrf5340/src/bin/uart_split.rs index 1bff382fb..0bbbfeaa5 100644 --- a/examples/nrf5340/src/bin/uart_split.rs +++ b/examples/nrf5340/src/bin/uart_split.rs @@ -21,7 +21,7 @@ async fn main(spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(SERIAL0); - let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); let (mut tx, rx) = uart.split(); info!("uarte initialized!"); From 0a27b6cedb52453123190671f294bbd34918e09a Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Mon, 9 Jan 2023 22:29:58 +0100 Subject: [PATCH 0496/1575] Rename examples/nrf to examples/nrf52840 --- README.md | 7 ++++--- ci.sh | 3 ++- ci_stable.sh | 2 +- docs/modules/ROOT/pages/getting_started.adoc | 2 +- examples/{nrf => nrf52840}/.cargo/config.toml | 0 examples/{nrf => nrf52840}/Cargo.toml | 0 examples/{nrf => nrf52840}/build.rs | 0 examples/{nrf => nrf52840}/memory.x | 0 examples/{nrf => nrf52840}/src/bin/awaitable_timer.rs | 0 examples/{nrf => nrf52840}/src/bin/blinky.rs | 0 examples/{nrf => nrf52840}/src/bin/buffered_uart.rs | 0 examples/{nrf => nrf52840}/src/bin/channel.rs | 0 .../{nrf => nrf52840}/src/bin/channel_sender_receiver.rs | 0 .../{nrf => nrf52840}/src/bin/executor_fairness_test.rs | 0 examples/{nrf => nrf52840}/src/bin/gpiote_channel.rs | 0 examples/{nrf => nrf52840}/src/bin/gpiote_port.rs | 0 examples/{nrf => nrf52840}/src/bin/i2s_effect.rs | 0 examples/{nrf => nrf52840}/src/bin/i2s_monitor.rs | 0 examples/{nrf => nrf52840}/src/bin/i2s_waveform.rs | 0 examples/{nrf => nrf52840}/src/bin/lora_p2p_report.rs | 0 examples/{nrf => nrf52840}/src/bin/lora_p2p_sense.rs | 0 .../{nrf => nrf52840}/src/bin/manually_create_executor.rs | 0 examples/{nrf => nrf52840}/src/bin/multiprio.rs | 0 examples/{nrf => nrf52840}/src/bin/mutex.rs | 0 examples/{nrf => nrf52840}/src/bin/nvmc.rs | 0 examples/{nrf => nrf52840}/src/bin/pdm.rs | 0 examples/{nrf => nrf52840}/src/bin/ppi.rs | 0 examples/{nrf => nrf52840}/src/bin/pubsub.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_double_sequence.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_sequence.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_sequence_ppi.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_sequence_ws2812b.rs | 0 examples/{nrf => nrf52840}/src/bin/pwm_servo.rs | 0 examples/{nrf => nrf52840}/src/bin/qdec.rs | 0 examples/{nrf => nrf52840}/src/bin/qspi.rs | 0 examples/{nrf => nrf52840}/src/bin/qspi_lowpower.rs | 0 examples/{nrf => nrf52840}/src/bin/raw_spawn.rs | 0 examples/{nrf => nrf52840}/src/bin/rng.rs | 0 examples/{nrf => nrf52840}/src/bin/saadc.rs | 0 examples/{nrf => nrf52840}/src/bin/saadc_continuous.rs | 0 examples/{nrf => nrf52840}/src/bin/self_spawn.rs | 0 .../src/bin/self_spawn_current_executor.rs | 0 examples/{nrf => nrf52840}/src/bin/spim.rs | 0 examples/{nrf => nrf52840}/src/bin/spis.rs | 0 examples/{nrf => nrf52840}/src/bin/temp.rs | 0 examples/{nrf => nrf52840}/src/bin/timer.rs | 0 examples/{nrf => nrf52840}/src/bin/twim.rs | 0 examples/{nrf => nrf52840}/src/bin/twim_lowpower.rs | 0 examples/{nrf => nrf52840}/src/bin/twis.rs | 0 examples/{nrf => nrf52840}/src/bin/uart.rs | 0 examples/{nrf => nrf52840}/src/bin/uart_idle.rs | 0 examples/{nrf => nrf52840}/src/bin/uart_split.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_ethernet.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_hid_keyboard.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_hid_mouse.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_serial.rs | 0 examples/{nrf => nrf52840}/src/bin/usb_serial_multitask.rs | 0 examples/{nrf => nrf52840}/src/bin/wdt.rs | 0 59 files changed, 8 insertions(+), 6 deletions(-) rename examples/{nrf => nrf52840}/.cargo/config.toml (100%) rename examples/{nrf => nrf52840}/Cargo.toml (100%) rename examples/{nrf => nrf52840}/build.rs (100%) rename examples/{nrf => nrf52840}/memory.x (100%) rename examples/{nrf => nrf52840}/src/bin/awaitable_timer.rs (100%) rename examples/{nrf => nrf52840}/src/bin/blinky.rs (100%) rename examples/{nrf => nrf52840}/src/bin/buffered_uart.rs (100%) rename examples/{nrf => nrf52840}/src/bin/channel.rs (100%) rename examples/{nrf => nrf52840}/src/bin/channel_sender_receiver.rs (100%) rename examples/{nrf => nrf52840}/src/bin/executor_fairness_test.rs (100%) rename examples/{nrf => nrf52840}/src/bin/gpiote_channel.rs (100%) rename examples/{nrf => nrf52840}/src/bin/gpiote_port.rs (100%) rename examples/{nrf => nrf52840}/src/bin/i2s_effect.rs (100%) rename examples/{nrf => nrf52840}/src/bin/i2s_monitor.rs (100%) rename examples/{nrf => nrf52840}/src/bin/i2s_waveform.rs (100%) rename examples/{nrf => nrf52840}/src/bin/lora_p2p_report.rs (100%) rename examples/{nrf => nrf52840}/src/bin/lora_p2p_sense.rs (100%) rename examples/{nrf => nrf52840}/src/bin/manually_create_executor.rs (100%) rename examples/{nrf => nrf52840}/src/bin/multiprio.rs (100%) rename examples/{nrf => nrf52840}/src/bin/mutex.rs (100%) rename examples/{nrf => nrf52840}/src/bin/nvmc.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pdm.rs (100%) rename examples/{nrf => nrf52840}/src/bin/ppi.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pubsub.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_double_sequence.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_sequence.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_sequence_ppi.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_sequence_ws2812b.rs (100%) rename examples/{nrf => nrf52840}/src/bin/pwm_servo.rs (100%) rename examples/{nrf => nrf52840}/src/bin/qdec.rs (100%) rename examples/{nrf => nrf52840}/src/bin/qspi.rs (100%) rename examples/{nrf => nrf52840}/src/bin/qspi_lowpower.rs (100%) rename examples/{nrf => nrf52840}/src/bin/raw_spawn.rs (100%) rename examples/{nrf => nrf52840}/src/bin/rng.rs (100%) rename examples/{nrf => nrf52840}/src/bin/saadc.rs (100%) rename examples/{nrf => nrf52840}/src/bin/saadc_continuous.rs (100%) rename examples/{nrf => nrf52840}/src/bin/self_spawn.rs (100%) rename examples/{nrf => nrf52840}/src/bin/self_spawn_current_executor.rs (100%) rename examples/{nrf => nrf52840}/src/bin/spim.rs (100%) rename examples/{nrf => nrf52840}/src/bin/spis.rs (100%) rename examples/{nrf => nrf52840}/src/bin/temp.rs (100%) rename examples/{nrf => nrf52840}/src/bin/timer.rs (100%) rename examples/{nrf => nrf52840}/src/bin/twim.rs (100%) rename examples/{nrf => nrf52840}/src/bin/twim_lowpower.rs (100%) rename examples/{nrf => nrf52840}/src/bin/twis.rs (100%) rename examples/{nrf => nrf52840}/src/bin/uart.rs (100%) rename examples/{nrf => nrf52840}/src/bin/uart_idle.rs (100%) rename examples/{nrf => nrf52840}/src/bin/uart_split.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_ethernet.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_hid_keyboard.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_hid_mouse.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_serial.rs (100%) rename examples/{nrf => nrf52840}/src/bin/usb_serial_multitask.rs (100%) rename examples/{nrf => nrf52840}/src/bin/wdt.rs (100%) diff --git a/README.md b/README.md index eaa91012c..938f2f4a6 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Rust's async/await allows No more messing with hardware timers. embassy_time provides Instant, Duration and Timer types that are globally available and never overflow. - **Real-time ready** - -Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the example. +Tasks on the same async executor run cooperatively, but you can create multiple executors with different priorities, so that higher priority tasks preempt lower priority ones. See the example. - **Low-power ready** - Easily build devices with years of battery life. The async executor automatically puts the core to sleep when there's no work to do. Tasks are woken by interrupts, there is no busy-loop polling while waiting. @@ -87,7 +87,8 @@ async fn main(spawner: Spawner) { Examples are found in the `examples/` folder seperated by the chip manufacturer they are designed to run on. For example: -* `examples/nrf` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. +* `examples/nrf52840` run on the `nrf52840-dk` board (PCA10056) but should be easily adaptable to other nRF52 chips and boards. +* `examples/nrf5340` run on the `nrf5340-dk` board (PCA10095). * `examples/stm32xx` for the various STM32 families. * `examples/rp` are for the RP2040 chip. * `examples/std` are designed to run locally on your PC. @@ -110,7 +111,7 @@ cargo install probe-run - Change directory to the sample's base directory. For example: ```bash -cd examples/nrf +cd examples/nrf52840 ``` - Run the example diff --git a/ci.sh b/ci.sh index f59f3f46c..30e664a2b 100755 --- a/ci.sh +++ b/ci.sh @@ -89,7 +89,8 @@ cargo batch \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ - --- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf \ + --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \ + --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \ --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \ --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \ --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ diff --git a/ci_stable.sh b/ci_stable.sh index a1d507d71..60ddb659d 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -65,5 +65,5 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ - --- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \ + --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \ --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \ diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/modules/ROOT/pages/getting_started.adoc index f3492a3d0..9015d7845 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/modules/ROOT/pages/getting_started.adoc @@ -45,7 +45,7 @@ You can run an example by opening a terminal and entering the following commands [source, bash] ---- -cd examples/nrf +cd examples/nrf52840 cargo run --bin blinky --release ---- diff --git a/examples/nrf/.cargo/config.toml b/examples/nrf52840/.cargo/config.toml similarity index 100% rename from examples/nrf/.cargo/config.toml rename to examples/nrf52840/.cargo/config.toml diff --git a/examples/nrf/Cargo.toml b/examples/nrf52840/Cargo.toml similarity index 100% rename from examples/nrf/Cargo.toml rename to examples/nrf52840/Cargo.toml diff --git a/examples/nrf/build.rs b/examples/nrf52840/build.rs similarity index 100% rename from examples/nrf/build.rs rename to examples/nrf52840/build.rs diff --git a/examples/nrf/memory.x b/examples/nrf52840/memory.x similarity index 100% rename from examples/nrf/memory.x rename to examples/nrf52840/memory.x diff --git a/examples/nrf/src/bin/awaitable_timer.rs b/examples/nrf52840/src/bin/awaitable_timer.rs similarity index 100% rename from examples/nrf/src/bin/awaitable_timer.rs rename to examples/nrf52840/src/bin/awaitable_timer.rs diff --git a/examples/nrf/src/bin/blinky.rs b/examples/nrf52840/src/bin/blinky.rs similarity index 100% rename from examples/nrf/src/bin/blinky.rs rename to examples/nrf52840/src/bin/blinky.rs diff --git a/examples/nrf/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs similarity index 100% rename from examples/nrf/src/bin/buffered_uart.rs rename to examples/nrf52840/src/bin/buffered_uart.rs diff --git a/examples/nrf/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs similarity index 100% rename from examples/nrf/src/bin/channel.rs rename to examples/nrf52840/src/bin/channel.rs diff --git a/examples/nrf/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs similarity index 100% rename from examples/nrf/src/bin/channel_sender_receiver.rs rename to examples/nrf52840/src/bin/channel_sender_receiver.rs diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf52840/src/bin/executor_fairness_test.rs similarity index 100% rename from examples/nrf/src/bin/executor_fairness_test.rs rename to examples/nrf52840/src/bin/executor_fairness_test.rs diff --git a/examples/nrf/src/bin/gpiote_channel.rs b/examples/nrf52840/src/bin/gpiote_channel.rs similarity index 100% rename from examples/nrf/src/bin/gpiote_channel.rs rename to examples/nrf52840/src/bin/gpiote_channel.rs diff --git a/examples/nrf/src/bin/gpiote_port.rs b/examples/nrf52840/src/bin/gpiote_port.rs similarity index 100% rename from examples/nrf/src/bin/gpiote_port.rs rename to examples/nrf52840/src/bin/gpiote_port.rs diff --git a/examples/nrf/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs similarity index 100% rename from examples/nrf/src/bin/i2s_effect.rs rename to examples/nrf52840/src/bin/i2s_effect.rs diff --git a/examples/nrf/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs similarity index 100% rename from examples/nrf/src/bin/i2s_monitor.rs rename to examples/nrf52840/src/bin/i2s_monitor.rs diff --git a/examples/nrf/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs similarity index 100% rename from examples/nrf/src/bin/i2s_waveform.rs rename to examples/nrf52840/src/bin/i2s_waveform.rs diff --git a/examples/nrf/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs similarity index 100% rename from examples/nrf/src/bin/lora_p2p_report.rs rename to examples/nrf52840/src/bin/lora_p2p_report.rs diff --git a/examples/nrf/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs similarity index 100% rename from examples/nrf/src/bin/lora_p2p_sense.rs rename to examples/nrf52840/src/bin/lora_p2p_sense.rs diff --git a/examples/nrf/src/bin/manually_create_executor.rs b/examples/nrf52840/src/bin/manually_create_executor.rs similarity index 100% rename from examples/nrf/src/bin/manually_create_executor.rs rename to examples/nrf52840/src/bin/manually_create_executor.rs diff --git a/examples/nrf/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs similarity index 100% rename from examples/nrf/src/bin/multiprio.rs rename to examples/nrf52840/src/bin/multiprio.rs diff --git a/examples/nrf/src/bin/mutex.rs b/examples/nrf52840/src/bin/mutex.rs similarity index 100% rename from examples/nrf/src/bin/mutex.rs rename to examples/nrf52840/src/bin/mutex.rs diff --git a/examples/nrf/src/bin/nvmc.rs b/examples/nrf52840/src/bin/nvmc.rs similarity index 100% rename from examples/nrf/src/bin/nvmc.rs rename to examples/nrf52840/src/bin/nvmc.rs diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs similarity index 100% rename from examples/nrf/src/bin/pdm.rs rename to examples/nrf52840/src/bin/pdm.rs diff --git a/examples/nrf/src/bin/ppi.rs b/examples/nrf52840/src/bin/ppi.rs similarity index 100% rename from examples/nrf/src/bin/ppi.rs rename to examples/nrf52840/src/bin/ppi.rs diff --git a/examples/nrf/src/bin/pubsub.rs b/examples/nrf52840/src/bin/pubsub.rs similarity index 100% rename from examples/nrf/src/bin/pubsub.rs rename to examples/nrf52840/src/bin/pubsub.rs diff --git a/examples/nrf/src/bin/pwm.rs b/examples/nrf52840/src/bin/pwm.rs similarity index 100% rename from examples/nrf/src/bin/pwm.rs rename to examples/nrf52840/src/bin/pwm.rs diff --git a/examples/nrf/src/bin/pwm_double_sequence.rs b/examples/nrf52840/src/bin/pwm_double_sequence.rs similarity index 100% rename from examples/nrf/src/bin/pwm_double_sequence.rs rename to examples/nrf52840/src/bin/pwm_double_sequence.rs diff --git a/examples/nrf/src/bin/pwm_sequence.rs b/examples/nrf52840/src/bin/pwm_sequence.rs similarity index 100% rename from examples/nrf/src/bin/pwm_sequence.rs rename to examples/nrf52840/src/bin/pwm_sequence.rs diff --git a/examples/nrf/src/bin/pwm_sequence_ppi.rs b/examples/nrf52840/src/bin/pwm_sequence_ppi.rs similarity index 100% rename from examples/nrf/src/bin/pwm_sequence_ppi.rs rename to examples/nrf52840/src/bin/pwm_sequence_ppi.rs diff --git a/examples/nrf/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs similarity index 100% rename from examples/nrf/src/bin/pwm_sequence_ws2812b.rs rename to examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs diff --git a/examples/nrf/src/bin/pwm_servo.rs b/examples/nrf52840/src/bin/pwm_servo.rs similarity index 100% rename from examples/nrf/src/bin/pwm_servo.rs rename to examples/nrf52840/src/bin/pwm_servo.rs diff --git a/examples/nrf/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs similarity index 100% rename from examples/nrf/src/bin/qdec.rs rename to examples/nrf52840/src/bin/qdec.rs diff --git a/examples/nrf/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs similarity index 100% rename from examples/nrf/src/bin/qspi.rs rename to examples/nrf52840/src/bin/qspi.rs diff --git a/examples/nrf/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs similarity index 100% rename from examples/nrf/src/bin/qspi_lowpower.rs rename to examples/nrf52840/src/bin/qspi_lowpower.rs diff --git a/examples/nrf/src/bin/raw_spawn.rs b/examples/nrf52840/src/bin/raw_spawn.rs similarity index 100% rename from examples/nrf/src/bin/raw_spawn.rs rename to examples/nrf52840/src/bin/raw_spawn.rs diff --git a/examples/nrf/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs similarity index 100% rename from examples/nrf/src/bin/rng.rs rename to examples/nrf52840/src/bin/rng.rs diff --git a/examples/nrf/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs similarity index 100% rename from examples/nrf/src/bin/saadc.rs rename to examples/nrf52840/src/bin/saadc.rs diff --git a/examples/nrf/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs similarity index 100% rename from examples/nrf/src/bin/saadc_continuous.rs rename to examples/nrf52840/src/bin/saadc_continuous.rs diff --git a/examples/nrf/src/bin/self_spawn.rs b/examples/nrf52840/src/bin/self_spawn.rs similarity index 100% rename from examples/nrf/src/bin/self_spawn.rs rename to examples/nrf52840/src/bin/self_spawn.rs diff --git a/examples/nrf/src/bin/self_spawn_current_executor.rs b/examples/nrf52840/src/bin/self_spawn_current_executor.rs similarity index 100% rename from examples/nrf/src/bin/self_spawn_current_executor.rs rename to examples/nrf52840/src/bin/self_spawn_current_executor.rs diff --git a/examples/nrf/src/bin/spim.rs b/examples/nrf52840/src/bin/spim.rs similarity index 100% rename from examples/nrf/src/bin/spim.rs rename to examples/nrf52840/src/bin/spim.rs diff --git a/examples/nrf/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs similarity index 100% rename from examples/nrf/src/bin/spis.rs rename to examples/nrf52840/src/bin/spis.rs diff --git a/examples/nrf/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs similarity index 100% rename from examples/nrf/src/bin/temp.rs rename to examples/nrf52840/src/bin/temp.rs diff --git a/examples/nrf/src/bin/timer.rs b/examples/nrf52840/src/bin/timer.rs similarity index 100% rename from examples/nrf/src/bin/timer.rs rename to examples/nrf52840/src/bin/timer.rs diff --git a/examples/nrf/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs similarity index 100% rename from examples/nrf/src/bin/twim.rs rename to examples/nrf52840/src/bin/twim.rs diff --git a/examples/nrf/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs similarity index 100% rename from examples/nrf/src/bin/twim_lowpower.rs rename to examples/nrf52840/src/bin/twim_lowpower.rs diff --git a/examples/nrf/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs similarity index 100% rename from examples/nrf/src/bin/twis.rs rename to examples/nrf52840/src/bin/twis.rs diff --git a/examples/nrf/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs similarity index 100% rename from examples/nrf/src/bin/uart.rs rename to examples/nrf52840/src/bin/uart.rs diff --git a/examples/nrf/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs similarity index 100% rename from examples/nrf/src/bin/uart_idle.rs rename to examples/nrf52840/src/bin/uart_idle.rs diff --git a/examples/nrf/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs similarity index 100% rename from examples/nrf/src/bin/uart_split.rs rename to examples/nrf52840/src/bin/uart_split.rs diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs similarity index 100% rename from examples/nrf/src/bin/usb_ethernet.rs rename to examples/nrf52840/src/bin/usb_ethernet.rs diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs similarity index 100% rename from examples/nrf/src/bin/usb_hid_keyboard.rs rename to examples/nrf52840/src/bin/usb_hid_keyboard.rs diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs similarity index 100% rename from examples/nrf/src/bin/usb_hid_mouse.rs rename to examples/nrf52840/src/bin/usb_hid_mouse.rs diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs similarity index 100% rename from examples/nrf/src/bin/usb_serial.rs rename to examples/nrf52840/src/bin/usb_serial.rs diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs similarity index 100% rename from examples/nrf/src/bin/usb_serial_multitask.rs rename to examples/nrf52840/src/bin/usb_serial_multitask.rs diff --git a/examples/nrf/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs similarity index 100% rename from examples/nrf/src/bin/wdt.rs rename to examples/nrf52840/src/bin/wdt.rs From 2baebabf4dd2abecfd08ca078ecf59060d5ad585 Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Mon, 9 Jan 2023 22:57:40 +0100 Subject: [PATCH 0497/1575] Reduce amount of samples for nrf5340 --- .vscode/settings.json | 3 +- examples/nrf5340/src/bin/awaitable_timer.rs | 26 -------- examples/nrf5340/src/bin/buffered_uart.rs | 57 ------------------ examples/nrf5340/src/bin/channel.rs | 43 ------------- .../src/bin/channel_sender_receiver.rs | 50 ---------------- examples/nrf5340/src/bin/gpiote_port.rs | 34 ----------- examples/nrf5340/src/bin/uart_idle.rs | 35 ----------- examples/nrf5340/src/bin/uart_split.rs | 60 ------------------- 8 files changed, 2 insertions(+), 306 deletions(-) delete mode 100644 examples/nrf5340/src/bin/awaitable_timer.rs delete mode 100644 examples/nrf5340/src/bin/buffered_uart.rs delete mode 100644 examples/nrf5340/src/bin/channel.rs delete mode 100644 examples/nrf5340/src/bin/channel_sender_receiver.rs delete mode 100644 examples/nrf5340/src/bin/gpiote_port.rs delete mode 100644 examples/nrf5340/src/bin/uart_idle.rs delete mode 100644 examples/nrf5340/src/bin/uart_split.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 086f435da..402ed2417 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,8 @@ // Declare for the target you wish to develop // "embassy-executor/Cargo.toml", // "embassy-sync/Cargo.toml", - "examples/nrf/Cargo.toml", + "examples/nrf52840/Cargo.toml", + //"examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", diff --git a/examples/nrf5340/src/bin/awaitable_timer.rs b/examples/nrf5340/src/bin/awaitable_timer.rs deleted file mode 100644 index b32af236c..000000000 --- a/examples/nrf5340/src/bin/awaitable_timer.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::info; -use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::timer::Timer; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); - // default frequency is 1MHz, so this triggers every second - t.cc(0).write(1_000_000); - // clear the timer value on cc[0] compare match - t.cc(0).short_compare_clear(); - t.start(); - - loop { - // wait for compare match - t.cc(0).wait().await; - info!("hardware timer tick"); - } -} diff --git a/examples/nrf5340/src/bin/buffered_uart.rs b/examples/nrf5340/src/bin/buffered_uart.rs deleted file mode 100644 index 25a0ca237..000000000 --- a/examples/nrf5340/src/bin/buffered_uart.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::{BufferedUarte, State}; -use embassy_nrf::{interrupt, uarte}; -use embedded_io::asynch::{BufRead, Write}; -use futures::pin_mut; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut config = uarte::Config::default(); - config.parity = uarte::Parity::EXCLUDED; - config.baudrate = uarte::Baudrate::BAUD115200; - - let mut tx_buffer = [0u8; 4096]; - let mut rx_buffer = [0u8; 4096]; - - let irq = interrupt::take!(SERIAL0); - let mut state = State::new(); - // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) - let u = BufferedUarte::new( - &mut state, - p.UARTETWISPI0, - p.TIMER0, - p.PPI_CH0, - p.PPI_CH1, - irq, - p.P0_08, - p.P0_06, - p.P0_07, - p.P0_05, - config, - &mut rx_buffer, - &mut tx_buffer, - ); - pin_mut!(u); - - info!("uarte initialized!"); - - unwrap!(u.write_all(b"Hello!\r\n").await); - info!("wrote hello in uart!"); - - loop { - info!("reading..."); - let buf = unwrap!(u.fill_buf().await); - info!("read done, got {}", buf); - - // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again - let n = buf.len(); - u.consume(n); - } -} diff --git a/examples/nrf5340/src/bin/channel.rs b/examples/nrf5340/src/bin/channel.rs deleted file mode 100644 index 425d43051..000000000 --- a/examples/nrf5340/src/bin/channel.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::unwrap; -use embassy_executor::Spawner; -use embassy_nrf::gpio::{Level, Output, OutputDrive}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; -use embassy_time::{Duration, Timer}; -use {defmt_rtt as _, panic_probe as _}; - -enum LedState { - On, - Off, -} - -static CHANNEL: Channel = Channel::new(); - -#[embassy_executor::task] -async fn my_task() { - loop { - CHANNEL.send(LedState::On).await; - Timer::after(Duration::from_secs(1)).await; - CHANNEL.send(LedState::Off).await; - Timer::after(Duration::from_secs(1)).await; - } -} - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); - - unwrap!(spawner.spawn(my_task())); - - loop { - match CHANNEL.recv().await { - LedState::On => led.set_high(), - LedState::Off => led.set_low(), - } - } -} diff --git a/examples/nrf5340/src/bin/channel_sender_receiver.rs b/examples/nrf5340/src/bin/channel_sender_receiver.rs deleted file mode 100644 index 9628c0525..000000000 --- a/examples/nrf5340/src/bin/channel_sender_receiver.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::unwrap; -use embassy_executor::Spawner; -use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::channel::{Channel, Receiver, Sender}; -use embassy_time::{Duration, Timer}; -use static_cell::StaticCell; -use {defmt_rtt as _, panic_probe as _}; - -enum LedState { - On, - Off, -} - -static CHANNEL: StaticCell> = StaticCell::new(); - -#[embassy_executor::task] -async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { - loop { - sender.send(LedState::On).await; - Timer::after(Duration::from_secs(1)).await; - sender.send(LedState::Off).await; - Timer::after(Duration::from_secs(1)).await; - } -} - -#[embassy_executor::task] -async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { - let mut led = Output::new(led, Level::Low, OutputDrive::Standard); - - loop { - match receiver.recv().await { - LedState::On => led.set_high(), - LedState::Off => led.set_low(), - } - } -} - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let channel = CHANNEL.init(Channel::new()); - - unwrap!(spawner.spawn(send_task(channel.sender()))); - unwrap!(spawner.spawn(recv_task(p.P0_28.degrade(), channel.receiver()))); -} diff --git a/examples/nrf5340/src/bin/gpiote_port.rs b/examples/nrf5340/src/bin/gpiote_port.rs deleted file mode 100644 index 0cc911ad2..000000000 --- a/examples/nrf5340/src/bin/gpiote_port.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::{info, unwrap}; -use embassy_executor::Spawner; -use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::task(pool_size = 4)] -async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { - loop { - pin.wait_for_low().await; - info!("Button {:?} pressed!", n); - pin.wait_for_high().await; - info!("Button {:?} released!", n); - } -} - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - info!("Starting!"); - - let btn1 = Input::new(p.P0_23.degrade(), Pull::Up); - let btn2 = Input::new(p.P0_24.degrade(), Pull::Up); - let btn3 = Input::new(p.P0_08.degrade(), Pull::Up); - let btn4 = Input::new(p.P0_09.degrade(), Pull::Up); - - unwrap!(spawner.spawn(button_task(1, btn1))); - unwrap!(spawner.spawn(button_task(2, btn2))); - unwrap!(spawner.spawn(button_task(3, btn3))); - unwrap!(spawner.spawn(button_task(4, btn4))); -} diff --git a/examples/nrf5340/src/bin/uart_idle.rs b/examples/nrf5340/src/bin/uart_idle.rs deleted file mode 100644 index 327fc4b23..000000000 --- a/examples/nrf5340/src/bin/uart_idle.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut config = uarte::Config::default(); - config.parity = uarte::Parity::EXCLUDED; - config.baudrate = uarte::Baudrate::BAUD115200; - - let irq = interrupt::take!(SERIAL0); - let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); - let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); - - info!("uarte initialized!"); - - // Message must be in SRAM - let mut buf = [0; 8]; - buf.copy_from_slice(b"Hello!\r\n"); - - unwrap!(tx.write(&buf).await); - info!("wrote hello in uart!"); - - loop { - info!("reading..."); - let n = unwrap!(rx.read_until_idle(&mut buf).await); - info!("got {} bytes", n); - } -} diff --git a/examples/nrf5340/src/bin/uart_split.rs b/examples/nrf5340/src/bin/uart_split.rs deleted file mode 100644 index 0bbbfeaa5..000000000 --- a/examples/nrf5340/src/bin/uart_split.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_nrf::peripherals::UARTETWISPI0; -use embassy_nrf::uarte::UarteRx; -use embassy_nrf::{interrupt, uarte}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; -use {defmt_rtt as _, panic_probe as _}; - -static CHANNEL: Channel = Channel::new(); - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut config = uarte::Config::default(); - config.parity = uarte::Parity::EXCLUDED; - config.baudrate = uarte::Baudrate::BAUD115200; - - let irq = interrupt::take!(SERIAL0); - let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); - let (mut tx, rx) = uart.split(); - - info!("uarte initialized!"); - - // Spawn a task responsible purely for reading - - unwrap!(spawner.spawn(reader(rx))); - - // Message must be in SRAM - { - let mut buf = [0; 23]; - buf.copy_from_slice(b"Type 8 chars to echo!\r\n"); - - unwrap!(tx.write(&buf).await); - info!("wrote hello in uart!"); - } - - // Continue reading in this main task and write - // back out the buffer we receive from the read - // task. - loop { - let buf = CHANNEL.recv().await; - info!("writing..."); - unwrap!(tx.write(&buf).await); - } -} - -#[embassy_executor::task] -async fn reader(mut rx: UarteRx<'static, UARTETWISPI0>) { - let mut buf = [0; 8]; - loop { - info!("reading..."); - unwrap!(rx.read(&mut buf).await); - CHANNEL.send(buf).await; - } -} From 96b97c4711af0851f7d3a6785be13b0202b6e902 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Jan 2023 17:43:12 +0100 Subject: [PATCH 0498/1575] Update vscode settings for latest RA. --- .vscode/settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 402ed2417..db37b64ce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "editor.formatOnSave": true, - "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.checkOnSave.noDefaultFeatures": true, + "rust-analyzer.check.allTargets": false, + "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.procMacro.enable": true, "rust-analyzer.cargo.target": "thumbv7em-none-eabi", From ce842fe28c27e6f57a05efc92bc417f6f7d7af64 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 11 Jan 2023 17:47:12 +0100 Subject: [PATCH 0499/1575] Refactor embassy-usb address handling to allow reordering of status resoponse --- embassy-nrf/src/usb.rs | 10 +++++----- embassy-rp/src/usb.rs | 15 ++++++++------- embassy-stm32/src/usb/usb.rs | 25 +++++++++++++------------ embassy-usb-driver/src/lib.rs | 9 ++++++--- embassy-usb/src/lib.rs | 23 ++++++++++++----------- 5 files changed, 44 insertions(+), 38 deletions(-) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index ed4d5cf35..6be4fec8c 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -391,11 +391,6 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { .await } - #[inline] - fn set_address(&mut self, _addr: u8) { - // Nothing to do, the peripheral handles this. - } - fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { let regs = T::regs(); unsafe { @@ -841,6 +836,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { let regs = T::regs(); regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); } + + async fn accept_set_address(&mut self, _addr: u8) { + self.accept().await; + // Nothing to do, the peripheral handles this. + } } fn dma_start() { diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 8361736a9..2710c4ad9 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -406,13 +406,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { .await } - #[inline] - fn set_address(&mut self, addr: u8) { - let regs = T::regs(); - trace!("setting addr: {}", addr); - unsafe { regs.addr_endp().write(|w| w.set_address(addr)) } - } - fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) { todo!(); } @@ -812,4 +805,12 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true)); } } + + async fn accept_set_address(&mut self, addr: u8) { + self.accept().await; + + let regs = T::regs(); + trace!("setting addr: {}", addr); + unsafe { regs.addr_endp().write(|w| w.set_address(addr)) } + } } diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index cba4915c1..062c7ef77 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -489,18 +489,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { .await } - #[inline] - fn set_address(&mut self, addr: u8) { - let regs = T::regs(); - trace!("setting addr: {}", addr); - unsafe { - regs.daddr().write(|w| { - w.set_ef(true); - w.set_add(addr); - }) - } - } - fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { // This can race, so do a retry loop. let reg = T::regs().epr(ep_addr.index() as _); @@ -1017,4 +1005,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { }); } } + + async fn accept_set_address(&mut self, addr: u8) { + self.accept().await; + + let regs = T::regs(); + trace!("setting addr: {}", addr); + unsafe { + regs.daddr().write(|w| { + w.set_ef(true); + w.set_add(addr); + }) + } + } } diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index d7238dc7d..64d647351 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -164,9 +164,6 @@ pub trait Bus { async fn poll(&mut self) -> Event; - /// Sets the device USB address to `addr`. - fn set_address(&mut self, addr: u8); - /// Enables or disables an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); @@ -306,6 +303,12 @@ pub trait ControlPipe { /// /// Sets a STALL condition on the pipe to indicate an error. async fn reject(&mut self); + + /// Accept SET_ADDRESS control and change bus address. + /// + /// For most drivers this function should firstly call `accept()` and then change the bus address. + /// However, there are peripherals (Synopsys USB OTG) that have reverse order. + async fn accept_set_address(&mut self, addr: u8); } pub trait EndpointIn: Endpoint { diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 661b84119..096e8b07a 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -122,10 +122,9 @@ struct Inner<'d, D: Driver<'d>> { /// Our device address, or 0 if none. address: u8, - /// When receiving a set addr control request, we have to apply it AFTER we've - /// finished handling the control request, as the status stage still has to be - /// handled with addr 0. - /// If true, do a set_addr after finishing the current control req. + /// SET_ADDRESS requests have special handling depending on the driver. + /// This flag indicates that requests must be handled by `ControlPipe::accept_set_address()` + /// instead of regular `accept()`. set_address_pending: bool, interfaces: Vec, MAX_INTERFACE_COUNT>, @@ -254,11 +253,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { Direction::In => self.handle_control_in(req).await, Direction::Out => self.handle_control_out(req).await, } - - if self.inner.set_address_pending { - self.inner.bus.set_address(self.inner.address); - self.inner.set_address_pending = false; - } } async fn handle_control_in(&mut self, req: Request) { @@ -328,7 +322,14 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { trace!(" control out data: {:02x?}", data); match self.inner.handle_control_out(req, data) { - OutResponse::Accepted => self.control.accept().await, + OutResponse::Accepted => { + if self.inner.set_address_pending { + self.control.accept_set_address(self.inner.address).await; + self.inner.set_address_pending = false; + } else { + self.control.accept().await + } + } OutResponse::Rejected => self.control.reject().await, } } @@ -655,7 +656,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { buf[1] = descriptor_type::STRING; let mut pos = 2; for c in s.encode_utf16() { - if pos >= buf.len() { + if pos + 2 >= buf.len() { panic!("control buffer too small"); } From 065a0a1ee71c3e4333f2e38140e9ca86b61b59d2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Jan 2023 17:51:30 +0100 Subject: [PATCH 0500/1575] Update stm32-data. --- embassy-stm32/build.rs | 30 ++--- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/usb/usb.rs | 1 + embassy-stm32/src/usb_otg.rs | 213 ------------------------------- embassy-stm32/src/usb_otg/mod.rs | 138 ++++++++++++++++++++ stm32-data | 2 +- 6 files changed, 155 insertions(+), 231 deletions(-) delete mode 100644 embassy-stm32/src/usb_otg.rs create mode 100644 embassy-stm32/src/usb_otg/mod.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ddfa97b29..dbfc1370d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -277,22 +277,20 @@ fn main() { (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), (("usb", "DP"), quote!(crate::usb::DpPin)), (("usb", "DM"), quote!(crate::usb::DmPin)), - (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)), - (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)), - (("otghs", "DP"), quote!(crate::usb_otg::DpPin)), - (("otghs", "DM"), quote!(crate::usb_otg::DmPin)), - (("otghs", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)), - (("otghs", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)), - (("otghs", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)), - (("otghs", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)), - (("otghs", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)), - (("otghs", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)), - (("otghs", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)), - (("otghs", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)), - (("otghs", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)), - (("otghs", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)), - (("otghs", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)), - (("otghs", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)), + (("otg", "DP"), quote!(crate::usb_otg::DpPin)), + (("otg", "DM"), quote!(crate::usb_otg::DmPin)), + (("otg", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)), + (("otg", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)), + (("otg", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)), + (("otg", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)), + (("otg", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)), + (("otg", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)), + (("otg", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)), + (("otg", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)), + (("otg", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)), + (("otg", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)), + (("otg", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)), + (("otg", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)), (("can", "TX"), quote!(crate::can::TxPin)), (("can", "RX"), quote!(crate::can::RxPin)), (("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 16c46ca22..610c24888 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -58,7 +58,7 @@ pub mod spi; pub mod usart; #[cfg(all(usb, feature = "time"))] pub mod usb; -#[cfg(any(otgfs, otghs))] +#[cfg(otg)] pub mod usb_otg; #[cfg(iwdg)] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 062c7ef77..03e792a21 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -154,6 +154,7 @@ impl<'d, T: Instance> Driver<'d, T> { block_for(Duration::from_millis(100)); + #[cfg(not(usb_v4))] regs.btable().write(|w| w.set_btable(0)); dp.set_as_af(dp.af_num(), AFType::OutputPushPull); diff --git a/embassy-stm32/src/usb_otg.rs b/embassy-stm32/src/usb_otg.rs deleted file mode 100644 index f7faf12a8..000000000 --- a/embassy-stm32/src/usb_otg.rs +++ /dev/null @@ -1,213 +0,0 @@ -use core::marker::PhantomData; - -use embassy_hal_common::into_ref; - -use crate::gpio::sealed::AFType; -use crate::rcc::RccPeripheral; -use crate::{peripherals, Peripheral}; - -macro_rules! config_ulpi_pins { - ($($pin:ident),*) => { - into_ref!($($pin),*); - // NOTE(unsafe) Exclusive access to the registers - critical_section::with(|_| unsafe { - $( - $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); - #[cfg(gpio_v2)] - $pin.set_speed(crate::gpio::Speed::VeryHigh); - )* - }) - }; -} - -/// USB PHY type -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum PhyType { - /// Internal Full-Speed PHY - /// - /// Available on most High-Speed peripherals. - InternalFullSpeed, - /// Internal High-Speed PHY - /// - /// Available on a few STM32 chips. - InternalHighSpeed, - /// External ULPI High-Speed PHY - ExternalHighSpeed, -} - -pub struct UsbOtg<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - _phy_type: PhyType, -} - -impl<'d, T: Instance> UsbOtg<'d, T> { - /// Initializes USB OTG peripheral with internal Full-Speed PHY - pub fn new_fs( - _peri: impl Peripheral

+ 'd, - dp: impl Peripheral

> + 'd, - dm: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(dp, dm); - - unsafe { - dp.set_as_af(dp.af_num(), AFType::OutputPushPull); - dm.set_as_af(dm.af_num(), AFType::OutputPushPull); - } - - Self { - phantom: PhantomData, - _phy_type: PhyType::InternalFullSpeed, - } - } - - /// Initializes USB OTG peripheral with external High-Speed PHY - pub fn new_hs_ulpi( - _peri: impl Peripheral

+ 'd, - ulpi_clk: impl Peripheral

> + 'd, - ulpi_dir: impl Peripheral

> + 'd, - ulpi_nxt: impl Peripheral

> + 'd, - ulpi_stp: impl Peripheral

> + 'd, - ulpi_d0: impl Peripheral

> + 'd, - ulpi_d1: impl Peripheral

> + 'd, - ulpi_d2: impl Peripheral

> + 'd, - ulpi_d3: impl Peripheral

> + 'd, - ulpi_d4: impl Peripheral

> + 'd, - ulpi_d5: impl Peripheral

> + 'd, - ulpi_d6: impl Peripheral

> + 'd, - ulpi_d7: impl Peripheral

> + 'd, - ) -> Self { - config_ulpi_pins!( - ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, - ulpi_d7 - ); - - Self { - phantom: PhantomData, - _phy_type: PhyType::ExternalHighSpeed, - } - } -} - -impl<'d, T: Instance> Drop for UsbOtg<'d, T> { - fn drop(&mut self) { - T::reset(); - T::disable(); - } -} - -pub(crate) mod sealed { - pub trait Instance { - const REGISTERS: *const (); - const HIGH_SPEED: bool; - const FIFO_DEPTH_WORDS: usize; - const ENDPOINT_COUNT: usize; - } -} - -pub trait Instance: sealed::Instance + RccPeripheral {} - -// Internal PHY pins -pin_trait!(DpPin, Instance); -pin_trait!(DmPin, Instance); - -// External PHY pins -pin_trait!(UlpiClkPin, Instance); -pin_trait!(UlpiDirPin, Instance); -pin_trait!(UlpiNxtPin, Instance); -pin_trait!(UlpiStpPin, Instance); -pin_trait!(UlpiD0Pin, Instance); -pin_trait!(UlpiD1Pin, Instance); -pin_trait!(UlpiD2Pin, Instance); -pin_trait!(UlpiD3Pin, Instance); -pin_trait!(UlpiD4Pin, Instance); -pin_trait!(UlpiD5Pin, Instance); -pin_trait!(UlpiD6Pin, Instance); -pin_trait!(UlpiD7Pin, Instance); - -foreach_peripheral!( - (otgfs, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - const REGISTERS: *const () = crate::pac::$inst.0 as *const (); - const HIGH_SPEED: bool = false; - - cfg_if::cfg_if! { - if #[cfg(stm32f1)] { - const FIFO_DEPTH_WORDS: usize = 128; - const ENDPOINT_COUNT: usize = 8; - } else if #[cfg(any( - stm32f2, - stm32f401, - stm32f405, - stm32f407, - stm32f411, - stm32f415, - stm32f417, - stm32f427, - stm32f429, - stm32f437, - stm32f439, - ))] { - const FIFO_DEPTH_WORDS: usize = 320; - const ENDPOINT_COUNT: usize = 4; - } else if #[cfg(any( - stm32f412, - stm32f413, - stm32f423, - stm32f446, - stm32f469, - stm32f479, - stm32f7, - stm32l4, - stm32u5, - ))] { - const FIFO_DEPTH_WORDS: usize = 320; - const ENDPOINT_COUNT: usize = 6; - } else if #[cfg(stm32g0x1)] { - const FIFO_DEPTH_WORDS: usize = 512; - const ENDPOINT_COUNT: usize = 8; - } else { - compile_error!("USB_OTG_FS peripheral is not supported by this chip."); - } - } - } - - impl Instance for peripherals::$inst {} - }; - - (otghs, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - const REGISTERS: *const () = crate::pac::$inst.0 as *const (); - const HIGH_SPEED: bool = true; - - cfg_if::cfg_if! { - if #[cfg(any( - stm32f2, - stm32f405, - stm32f407, - stm32f415, - stm32f417, - stm32f427, - stm32f429, - stm32f437, - stm32f439, - ))] { - const FIFO_DEPTH_WORDS: usize = 1024; - const ENDPOINT_COUNT: usize = 6; - } else if #[cfg(any( - stm32f446, - stm32f469, - stm32f479, - stm32f7, - stm32h7, - ))] { - const FIFO_DEPTH_WORDS: usize = 1024; - const ENDPOINT_COUNT: usize = 9; - } else { - compile_error!("USB_OTG_HS peripheral is not supported by this chip."); - } - } - } - - impl Instance for peripherals::$inst {} - }; -); diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs new file mode 100644 index 000000000..2ee2891df --- /dev/null +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -0,0 +1,138 @@ +use embassy_cortex_m::interrupt::Interrupt; + +use crate::peripherals; +use crate::rcc::RccPeripheral; + +pub(crate) mod sealed { + pub trait Instance { + const HIGH_SPEED: bool; + const FIFO_DEPTH_WORDS: u16; + const ENDPOINT_COUNT: usize; + + fn regs() -> crate::pac::otg::Otg; + } +} + +pub trait Instance: sealed::Instance + RccPeripheral { + type Interrupt: Interrupt; +} + +// Internal PHY pins +pin_trait!(DpPin, Instance); +pin_trait!(DmPin, Instance); + +// External PHY pins +pin_trait!(UlpiClkPin, Instance); +pin_trait!(UlpiDirPin, Instance); +pin_trait!(UlpiNxtPin, Instance); +pin_trait!(UlpiStpPin, Instance); +pin_trait!(UlpiD0Pin, Instance); +pin_trait!(UlpiD1Pin, Instance); +pin_trait!(UlpiD2Pin, Instance); +pin_trait!(UlpiD3Pin, Instance); +pin_trait!(UlpiD4Pin, Instance); +pin_trait!(UlpiD5Pin, Instance); +pin_trait!(UlpiD6Pin, Instance); +pin_trait!(UlpiD7Pin, Instance); + +foreach_interrupt!( + (USB_OTG_FS, otg, $block:ident, GLOBAL, $irq:ident) => { + impl sealed::Instance for peripherals::USB_OTG_FS { + const HIGH_SPEED: bool = false; + + cfg_if::cfg_if! { + if #[cfg(stm32f1)] { + const FIFO_DEPTH_WORDS: u16 = 128; + const ENDPOINT_COUNT: usize = 8; + } else if #[cfg(any( + stm32f2, + stm32f401, + stm32f405, + stm32f407, + stm32f411, + stm32f415, + stm32f417, + stm32f427, + stm32f429, + stm32f437, + stm32f439, + ))] { + const FIFO_DEPTH_WORDS: u16 = 320; + const ENDPOINT_COUNT: usize = 4; + } else if #[cfg(any( + stm32f412, + stm32f413, + stm32f423, + stm32f446, + stm32f469, + stm32f479, + stm32f7, + stm32l4, + stm32u5, + ))] { + const FIFO_DEPTH_WORDS: u16 = 320; + const ENDPOINT_COUNT: usize = 6; + } else if #[cfg(stm32g0x1)] { + const FIFO_DEPTH_WORDS: u16 = 512; + const ENDPOINT_COUNT: usize = 8; + } else if #[cfg(stm32h7)] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 9; + } else { + compile_error!("USB_OTG_FS peripheral is not supported by this chip."); + } + } + + fn regs() -> crate::pac::otg::Otg { + crate::pac::USB_OTG_FS + } + } + + impl Instance for peripherals::USB_OTG_FS { + type Interrupt = crate::interrupt::$irq; + } + }; + + (USB_OTG_HS, otg, $block:ident, GLOBAL, $irq:ident) => { + impl sealed::Instance for peripherals::USB_OTG_HS { + const HIGH_SPEED: bool = true; + + cfg_if::cfg_if! { + if #[cfg(any( + stm32f2, + stm32f405, + stm32f407, + stm32f415, + stm32f417, + stm32f427, + stm32f429, + stm32f437, + stm32f439, + ))] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 6; + } else if #[cfg(any( + stm32f446, + stm32f469, + stm32f479, + stm32f7, + stm32h7, + ))] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 9; + } else { + compile_error!("USB_OTG_HS peripheral is not supported by this chip."); + } + } + + fn regs() -> crate::pac::otg::Otg { + // OTG HS registers are a superset of FS registers + crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0) + } + } + + impl Instance for peripherals::USB_OTG_HS { + type Interrupt = crate::interrupt::$irq; + } + }; +); diff --git a/stm32-data b/stm32-data index 14a448c31..844793fc3 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 14a448c318192fe9da1c95a4de1beb4ec4892f1c +Subproject commit 844793fc3da2ba3f12ab6a69b78cd8e6fb5497b4 From 0feecd5cded86d29870330341d5c2c8eb40688d0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 11 Jan 2023 17:56:47 +0100 Subject: [PATCH 0501/1575] stm32: add USB OTG support. Co-authored-by: Dario Nieuwenhuis --- embassy-stm32/src/usb_otg/mod.rs | 23 + embassy-stm32/src/usb_otg/usb.rs | 1346 ++++++++++++++++++++++++++++++ 2 files changed, 1369 insertions(+) create mode 100644 embassy-stm32/src/usb_otg/usb.rs diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs index 2ee2891df..84fef78cb 100644 --- a/embassy-stm32/src/usb_otg/mod.rs +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -3,6 +3,15 @@ use embassy_cortex_m::interrupt::Interrupt; use crate::peripherals; use crate::rcc::RccPeripheral; +#[cfg(feature = "nightly")] +mod usb; +#[cfg(feature = "nightly")] +pub use usb::*; + +// Using Instance::ENDPOINT_COUNT requires feature(const_generic_expr) so just define maximum eps +#[cfg(feature = "nightly")] +const MAX_EP_COUNT: usize = 9; + pub(crate) mod sealed { pub trait Instance { const HIGH_SPEED: bool; @@ -10,6 +19,8 @@ pub(crate) mod sealed { const ENDPOINT_COUNT: usize; fn regs() -> crate::pac::otg::Otg; + #[cfg(feature = "nightly")] + fn state() -> &'static super::State<{ super::MAX_EP_COUNT }>; } } @@ -86,6 +97,12 @@ foreach_interrupt!( fn regs() -> crate::pac::otg::Otg { crate::pac::USB_OTG_FS } + + #[cfg(feature = "nightly")] + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } } impl Instance for peripherals::USB_OTG_FS { @@ -129,6 +146,12 @@ foreach_interrupt!( // OTG HS registers are a superset of FS registers crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0) } + + #[cfg(feature = "nightly")] + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } } impl Instance for peripherals::USB_OTG_HS { diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs new file mode 100644 index 000000000..2d9b613ea --- /dev/null +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -0,0 +1,1346 @@ +use core::cell::UnsafeCell; +use core::marker::PhantomData; +use core::task::Poll; + +use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use embassy_usb_driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, + EndpointType, Event, Unsupported, +}; +use futures::future::poll_fn; + +use super::*; +use crate::gpio::sealed::AFType; +use crate::pac::otg::{regs, vals}; +use crate::rcc::sealed::RccPeripheral; +use crate::time::Hertz; + +macro_rules! config_ulpi_pins { + ($($pin:ident),*) => { + into_ref!($($pin),*); + // NOTE(unsafe) Exclusive access to the registers + critical_section::with(|_| unsafe { + $( + $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + $pin.set_speed(crate::gpio::Speed::VeryHigh); + )* + }) + }; +} + +// From `synopsys-usb-otg` crate: +// This calculation doesn't correspond to one in a Reference Manual. +// In fact, the required number of words is higher than indicated in RM. +// The following numbers are pessimistic and were figured out empirically. +const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30; + +/// USB PHY type +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PhyType { + /// Internal Full-Speed PHY + /// + /// Available on most High-Speed peripherals. + InternalFullSpeed, + /// Internal High-Speed PHY + /// + /// Available on a few STM32 chips. + InternalHighSpeed, + /// External ULPI High-Speed PHY + ExternalHighSpeed, +} + +impl PhyType { + pub fn internal(&self) -> bool { + match self { + PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true, + PhyType::ExternalHighSpeed => false, + } + } + + pub fn high_speed(&self) -> bool { + match self { + PhyType::InternalFullSpeed => false, + PhyType::ExternalHighSpeed | PhyType::InternalHighSpeed => true, + } + } + + pub fn to_dspd(&self) -> vals::Dspd { + match self { + PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL, + PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED, + PhyType::ExternalHighSpeed => vals::Dspd::HIGH_SPEED, + } + } +} + +/// Indicates that [State::ep_out_buffers] is empty. +const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX; + +pub struct State { + /// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true. + ep0_setup_data: UnsafeCell<[u8; 8]>, + ep0_setup_ready: AtomicBool, + ep_in_wakers: [AtomicWaker; EP_COUNT], + ep_out_wakers: [AtomicWaker; EP_COUNT], + /// RX FIFO is shared so extra buffers are needed to dequeue all data without waiting on each endpoint. + /// Buffers are ready when associated [State::ep_out_size] != [EP_OUT_BUFFER_EMPTY]. + ep_out_buffers: [UnsafeCell<*mut u8>; EP_COUNT], + ep_out_size: [AtomicU16; EP_COUNT], + bus_waker: AtomicWaker, +} + +unsafe impl Send for State {} +unsafe impl Sync for State {} + +impl State { + pub const fn new() -> Self { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _); + const NEW_SIZE: AtomicU16 = AtomicU16::new(EP_OUT_BUFFER_EMPTY); + + Self { + ep0_setup_data: UnsafeCell::new([0u8; 8]), + ep0_setup_ready: AtomicBool::new(false), + ep_in_wakers: [NEW_AW; EP_COUNT], + ep_out_wakers: [NEW_AW; EP_COUNT], + ep_out_buffers: [NEW_BUF; EP_COUNT], + ep_out_size: [NEW_SIZE; EP_COUNT], + bus_waker: NEW_AW, + } + } +} + +#[derive(Debug, Clone, Copy)] +struct EndpointData { + ep_type: EndpointType, + max_packet_size: u16, + fifo_size_words: u16, +} + +pub struct Driver<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + irq: PeripheralRef<'d, T::Interrupt>, + ep_in: [Option; MAX_EP_COUNT], + ep_out: [Option; MAX_EP_COUNT], + ep_out_buffer: &'d mut [u8], + ep_out_buffer_offset: usize, + phy_type: PhyType, +} + +impl<'d, T: Instance> Driver<'d, T> { + /// Initializes USB OTG peripheral with internal Full-Speed PHY. + /// + /// # Arguments + /// + /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// Must be large enough to fit all OUT endpoint max packet sizes. + /// Endpoint allocation will fail if it is too small. + pub fn new_fs( + _peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + dp: impl Peripheral

> + 'd, + dm: impl Peripheral

> + 'd, + ep_out_buffer: &'d mut [u8], + ) -> Self { + into_ref!(dp, dm, irq); + + unsafe { + dp.set_as_af(dp.af_num(), AFType::OutputPushPull); + dm.set_as_af(dm.af_num(), AFType::OutputPushPull); + } + + Self { + phantom: PhantomData, + irq, + ep_in: [None; MAX_EP_COUNT], + ep_out: [None; MAX_EP_COUNT], + ep_out_buffer, + ep_out_buffer_offset: 0, + phy_type: PhyType::InternalFullSpeed, + } + } + + /// Initializes USB OTG peripheral with external High-Speed PHY. + /// + /// # Arguments + /// + /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// Must be large enough to fit all OUT endpoint max packet sizes. + /// Endpoint allocation will fail if it is too small. + pub fn new_hs_ulpi( + _peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + ulpi_clk: impl Peripheral

> + 'd, + ulpi_dir: impl Peripheral

> + 'd, + ulpi_nxt: impl Peripheral

> + 'd, + ulpi_stp: impl Peripheral

> + 'd, + ulpi_d0: impl Peripheral

> + 'd, + ulpi_d1: impl Peripheral

> + 'd, + ulpi_d2: impl Peripheral

> + 'd, + ulpi_d3: impl Peripheral

> + 'd, + ulpi_d4: impl Peripheral

> + 'd, + ulpi_d5: impl Peripheral

> + 'd, + ulpi_d6: impl Peripheral

> + 'd, + ulpi_d7: impl Peripheral

> + 'd, + ep_out_buffer: &'d mut [u8], + ) -> Self { + assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB"); + + config_ulpi_pins!( + ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, + ulpi_d7 + ); + + into_ref!(irq); + + Self { + phantom: PhantomData, + irq, + ep_in: [None; MAX_EP_COUNT], + ep_out: [None; MAX_EP_COUNT], + ep_out_buffer, + ep_out_buffer_offset: 0, + phy_type: PhyType::ExternalHighSpeed, + } + } + + // Returns total amount of words (u32) allocated in dedicated FIFO + fn allocated_fifo_words(&self) -> u16 { + RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out) + ep_fifo_size(&self.ep_in) + } + + fn alloc_endpoint( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result, EndpointAllocError> { + trace!( + "allocating type={:?} mps={:?} interval={}, dir={:?}", + ep_type, + max_packet_size, + interval, + D::dir() + ); + + if D::dir() == Direction::Out { + if self.ep_out_buffer_offset + max_packet_size as usize >= self.ep_out_buffer.len() { + error!("Not enough endpoint out buffer capacity"); + return Err(EndpointAllocError); + } + }; + + let fifo_size_words = match D::dir() { + Direction::Out => (max_packet_size + 3) / 4, + // INEPTXFD requires minimum size of 16 words + Direction::In => u16::max((max_packet_size + 3) / 4, 16), + }; + + if fifo_size_words + self.allocated_fifo_words() > T::FIFO_DEPTH_WORDS { + error!("Not enough FIFO capacity"); + return Err(EndpointAllocError); + } + + let eps = match D::dir() { + Direction::Out => &mut self.ep_out, + Direction::In => &mut self.ep_in, + }; + + // Find free endpoint slot + let slot = eps.iter_mut().enumerate().find(|(i, ep)| { + if *i == 0 && ep_type != EndpointType::Control { + // reserved for control pipe + false + } else { + ep.is_none() + } + }); + + let index = match slot { + Some((index, ep)) => { + *ep = Some(EndpointData { + ep_type, + max_packet_size, + fifo_size_words, + }); + index + } + None => { + error!("No free endpoints available"); + return Err(EndpointAllocError); + } + }; + + trace!(" index={}", index); + + if D::dir() == Direction::Out { + // Buffer capacity check was done above, now allocation cannot fail + unsafe { + *T::state().ep_out_buffers[index].get() = + self.ep_out_buffer.as_mut_ptr().offset(self.ep_out_buffer_offset as _); + } + self.ep_out_buffer_offset += max_packet_size as usize; + } + + Ok(Endpoint { + _phantom: PhantomData, + info: EndpointInfo { + addr: EndpointAddress::from_parts(index, D::dir()), + ep_type, + max_packet_size, + interval, + }, + }) + } +} + +impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { + type EndpointOut = Endpoint<'d, T, Out>; + type EndpointIn = Endpoint<'d, T, In>; + type ControlPipe = ControlPipe<'d, T>; + type Bus = Bus<'d, T>; + + fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval) + } + + fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval) + } + + fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + let ep_out = self + .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + let ep_in = self + .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + assert_eq!(ep_out.info.addr.index(), 0); + assert_eq!(ep_in.info.addr.index(), 0); + + trace!("start"); + + ( + Bus { + phantom: PhantomData, + irq: self.irq, + ep_in: self.ep_in, + ep_out: self.ep_out, + phy_type: self.phy_type, + enabled: false, + }, + ControlPipe { + _phantom: PhantomData, + max_packet_size: control_max_packet_size, + ep_out, + ep_in, + }, + ) + } +} + +pub struct Bus<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + irq: PeripheralRef<'d, T::Interrupt>, + ep_in: [Option; MAX_EP_COUNT], + ep_out: [Option; MAX_EP_COUNT], + phy_type: PhyType, + enabled: bool, +} + +impl<'d, T: Instance> Bus<'d, T> { + fn restore_irqs() { + // SAFETY: atomic write + unsafe { + T::regs().gintmsk().write(|w| { + w.set_usbrst(true); + w.set_enumdnem(true); + w.set_usbsuspm(true); + w.set_wuim(true); + w.set_iepint(true); + w.set_oepint(true); + w.set_rxflvlm(true); + }); + } + } +} + +impl<'d, T: Instance> Bus<'d, T> { + fn init_fifo(&mut self) { + trace!("init_fifo"); + + let r = T::regs(); + + // Configure RX fifo size. All endpoints share the same FIFO area. + let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); + trace!("configuring rx fifo size={}", rx_fifo_size_words); + + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)) }; + + // Configure TX (USB in direction) fifo size for each endpoint + let mut fifo_top = rx_fifo_size_words; + for i in 0..T::ENDPOINT_COUNT { + if let Some(ep) = self.ep_in[i] { + trace!( + "configuring tx fifo ep={}, offset={}, size={}", + i, + fifo_top, + ep.fifo_size_words + ); + + let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; + + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { + dieptxf.write(|w| { + w.set_fd(ep.fifo_size_words); + w.set_sa(fifo_top); + }); + } + + fifo_top += ep.fifo_size_words; + } + } + + assert!( + fifo_top <= T::FIFO_DEPTH_WORDS, + "FIFO allocations exceeded maximum capacity" + ); + } + + fn configure_endpoints(&mut self) { + trace!("configure_endpoints"); + + let r = T::regs(); + + // Configure IN endpoints + for (index, ep) in self.ep_in.iter().enumerate() { + if let Some(ep) = ep { + // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.diepctl(index).write(|w| { + if index == 0 { + w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); + } else { + w.set_mpsiz(ep.max_packet_size); + w.set_eptyp(to_eptyp(ep.ep_type)); + w.set_sd0pid_sevnfrm(true); + } + }); + }); + } + } + + // Configure OUT endpoints + for (index, ep) in self.ep_out.iter().enumerate() { + if let Some(ep) = ep { + // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.doepctl(index).write(|w| { + if index == 0 { + w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); + } else { + w.set_mpsiz(ep.max_packet_size); + w.set_eptyp(to_eptyp(ep.ep_type)); + w.set_sd0pid_sevnfrm(true); + } + }); + + r.doeptsiz(index).modify(|w| { + w.set_xfrsiz(ep.max_packet_size as _); + if index == 0 { + w.set_rxdpid_stupcnt(1); + } else { + w.set_pktcnt(1); + } + }); + }); + } + } + + // Enable IRQs for allocated endpoints + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { + r.daintmsk().modify(|w| { + w.set_iepm(ep_irq_mask(&self.ep_in)); + // OUT interrupts not used, handled in RXFLVL + // w.set_oepm(ep_irq_mask(&self.ep_out)); + }); + } + } + + fn disable(&mut self) { + self.irq.disable(); + self.irq.remove_handler(); + + ::disable(); + + #[cfg(stm32l4)] + unsafe { + crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); + // Cannot disable PWR, because other peripherals might be using it + } + } + + fn on_interrupt(_: *mut ()) { + trace!("irq"); + let r = T::regs(); + let state = T::state(); + + // SAFETY: atomic read/write + let ints = unsafe { r.gintsts().read() }; + if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() { + // Mask interrupts and notify `Bus` to process them + unsafe { r.gintmsk().write(|_| {}) }; + T::state().bus_waker.wake(); + } + + // Handle RX + // SAFETY: atomic read with no side effects + while unsafe { r.gintsts().read().rxflvl() } { + // SAFETY: atomic "pop" register + let status = unsafe { r.grxstsp().read() }; + let ep_num = status.epnum() as usize; + let len = status.bcnt() as usize; + + assert!(ep_num < T::ENDPOINT_COUNT); + + match status.pktstsd() { + vals::Pktstsd::SETUP_DATA_RX => { + trace!("SETUP_DATA_RX"); + assert!(len == 8, "invalid SETUP packet length={}", len); + assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num); + + if state.ep0_setup_ready.load(Ordering::Relaxed) == false { + // SAFETY: exclusive access ensured by atomic bool + let data = unsafe { &mut *state.ep0_setup_data.get() }; + // SAFETY: FIFO reads are exclusive to this IRQ + unsafe { + data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); + } + state.ep0_setup_ready.store(true, Ordering::Release); + state.ep_out_wakers[0].wake(); + } else { + error!("received SETUP before previous finished processing"); + // discard FIFO + // SAFETY: FIFO reads are exclusive to IRQ + unsafe { + r.fifo(0).read(); + r.fifo(0).read(); + } + } + } + vals::Pktstsd::OUT_DATA_RX => { + trace!("OUT_DATA_RX ep={} len={}", ep_num, len); + + if state.ep_out_size[ep_num].load(Ordering::Acquire) == EP_OUT_BUFFER_EMPTY { + // SAFETY: Buffer size is allocated to be equal to endpoint's maximum packet size + // We trust the peripheral to not exceed its configured MPSIZ + let buf = unsafe { core::slice::from_raw_parts_mut(*state.ep_out_buffers[ep_num].get(), len) }; + + for chunk in buf.chunks_mut(4) { + // RX FIFO is shared so always read from fifo(0) + // SAFETY: FIFO reads are exclusive to IRQ + let data = unsafe { r.fifo(0).read().0 }; + chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]); + } + + state.ep_out_size[ep_num].store(len as u16, Ordering::Release); + state.ep_out_wakers[ep_num].wake(); + } else { + error!("ep_out buffer overflow index={}", ep_num); + + // discard FIFO data + let len_words = (len + 3) / 4; + for _ in 0..len_words { + // SAFETY: FIFO reads are exclusive to IRQ + unsafe { r.fifo(0).read().data() }; + } + } + } + vals::Pktstsd::OUT_DATA_DONE => { + trace!("OUT_DATA_DONE ep={}", ep_num); + } + vals::Pktstsd::SETUP_DATA_DONE => { + trace!("SETUP_DATA_DONE ep={}", ep_num); + } + x => trace!("unknown PKTSTS: {}", x.0), + } + } + + // IN endpoint interrupt + if ints.iepint() { + // SAFETY: atomic read with no side effects + let mut ep_mask = unsafe { r.daint().read().iepint() }; + let mut ep_num = 0; + + // Iterate over endpoints while there are non-zero bits in the mask + while ep_mask != 0 { + if ep_mask & 1 != 0 { + // SAFETY: atomic read with no side effects + let ep_ints = unsafe { r.diepint(ep_num).read() }; + + // clear all + // SAFETY: DIEPINT is exclusive to IRQ + unsafe { r.diepint(ep_num).write_value(ep_ints) }; + + // TXFE is cleared in DIEPEMPMSK + if ep_ints.txfe() { + // SAFETY: DIEPEMPMSK is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.diepempmsk().modify(|w| { + w.set_ineptxfem(w.ineptxfem() & !(1 << ep_num)); + }); + }); + } + + state.ep_in_wakers[ep_num].wake(); + trace!("in ep={} irq val={:b}", ep_num, ep_ints.0); + } + + ep_mask >>= 1; + ep_num += 1; + } + } + + // not needed? reception handled in rxflvl + // OUT endpoint interrupt + // if ints.oepint() { + // let mut ep_mask = r.daint().read().oepint(); + // let mut ep_num = 0; + + // while ep_mask != 0 { + // if ep_mask & 1 != 0 { + // let ep_ints = r.doepint(ep_num).read(); + // // clear all + // r.doepint(ep_num).write_value(ep_ints); + // state.ep_out_wakers[ep_num].wake(); + // trace!("out ep={} irq val={=u32:b}", ep_num, ep_ints.0); + // } + + // ep_mask >>= 1; + // ep_num += 1; + // } + // } + } +} + +impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { + async fn poll(&mut self) -> Event { + poll_fn(move |cx| { + // TODO: implement VBUS detection + if !self.enabled { + return Poll::Ready(Event::PowerDetected); + } + + let r = T::regs(); + + T::state().bus_waker.register(cx.waker()); + + let ints = unsafe { r.gintsts().read() }; + if ints.usbrst() { + trace!("reset"); + + self.init_fifo(); + self.configure_endpoints(); + + // Reset address + // SAFETY: DCFG is shared with `ControlPipe` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.dcfg().modify(|w| { + w.set_dad(0); + }); + }); + + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_usbrst(true)) }; // clear + Self::restore_irqs(); + } + + if ints.enumdne() { + trace!("enumdne"); + + // SAFETY: atomic read with no side effects + let speed = unsafe { r.dsts().read().enumspd() }; + trace!(" speed={}", speed.0); + + // SAFETY: register is only accessed by `Bus` under `&mut self` + unsafe { + r.gusbcfg().modify(|w| { + w.set_trdt(calculate_trdt(speed, T::frequency())); + }) + }; + + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_enumdne(true)) }; // clear + Self::restore_irqs(); + + return Poll::Ready(Event::Reset); + } + + if ints.usbsusp() { + trace!("suspend"); + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_usbsusp(true)) }; // clear + Self::restore_irqs(); + return Poll::Ready(Event::Suspend); + } + + if ints.wkupint() { + trace!("resume"); + // SAFETY: atomic clear on rc_w1 register + unsafe { r.gintsts().write(|w| w.set_wkupint(true)) }; // clear + Self::restore_irqs(); + return Poll::Ready(Event::Resume); + } + + Poll::Pending + }) + .await + } + + fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { + trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled); + + assert!( + ep_addr.index() < T::ENDPOINT_COUNT, + "endpoint_set_stalled index {} out of range", + ep_addr.index() + ); + + let regs = T::regs(); + match ep_addr.direction() { + Direction::Out => { + // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + regs.doepctl(ep_addr.index()).modify(|w| { + w.set_stall(stalled); + }); + }); + + T::state().ep_out_wakers[ep_addr.index()].wake(); + } + Direction::In => { + // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + regs.diepctl(ep_addr.index()).modify(|w| { + w.set_stall(stalled); + }); + }); + + T::state().ep_in_wakers[ep_addr.index()].wake(); + } + } + } + + fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { + assert!( + ep_addr.index() < T::ENDPOINT_COUNT, + "endpoint_is_stalled index {} out of range", + ep_addr.index() + ); + + let regs = T::regs(); + + // SAFETY: atomic read with no side effects + match ep_addr.direction() { + Direction::Out => unsafe { regs.doepctl(ep_addr.index()).read().stall() }, + Direction::In => unsafe { regs.diepctl(ep_addr.index()).read().stall() }, + } + } + + fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { + trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled); + + assert!( + ep_addr.index() < T::ENDPOINT_COUNT, + "endpoint_set_enabled index {} out of range", + ep_addr.index() + ); + + let r = T::regs(); + match ep_addr.direction() { + Direction::Out => { + // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + // cancel transfer if active + if !enabled && r.doepctl(ep_addr.index()).read().epena() { + r.doepctl(ep_addr.index()).modify(|w| { + w.set_snak(true); + w.set_epdis(true); + }) + } + + r.doepctl(ep_addr.index()).modify(|w| { + w.set_usbaep(enabled); + }) + }); + } + Direction::In => { + // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + // cancel transfer if active + if !enabled && r.diepctl(ep_addr.index()).read().epena() { + r.diepctl(ep_addr.index()).modify(|w| { + w.set_snak(true); + w.set_epdis(true); + }) + } + + r.diepctl(ep_addr.index()).modify(|w| { + w.set_usbaep(enabled); + }) + }); + } + } + } + + async fn enable(&mut self) { + trace!("enable"); + + // SAFETY: registers are only accessed by `Bus` under `&mut self` + unsafe { + #[cfg(stm32l4)] + { + crate::peripherals::PWR::enable(); + critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); + } + + #[cfg(stm32h7)] + { + // If true, VDD33USB is generated by internal regulator from VDD50USB + // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) + // TODO: unhardcode + let internal_regulator = false; + + // Enable USB power + critical_section::with(|_| { + crate::pac::PWR.cr3().modify(|w| { + w.set_usb33den(true); + w.set_usbregen(internal_regulator); + }) + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.cr3().read().usb33rdy() {} + + // Use internal 48MHz HSI clock. Should be enabled in RCC by default. + critical_section::with(|_| { + crate::pac::RCC + .d2ccip2r() + .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) + }); + + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpien(ulpien); + } else { + w.set_usb_otg_fs_ulpien(ulpien); + } + }); + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpilpen(ulpien); + } else { + w.set_usb_otg_fs_ulpilpen(ulpien); + } + }); + }); + } + + #[cfg(stm32u5)] + { + // Enable USB power + critical_section::with(|_| { + crate::pac::RCC.ahb3enr().modify(|w| { + w.set_pwren(true); + }); + cortex_m::asm::delay(2); + + crate::pac::PWR.svmcr().modify(|w| { + w.set_usv(true); + w.set_uvmen(true); + }); + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.svmsr().read().vddusbrdy() {} + + // Select HSI48 as USB clock source. + critical_section::with(|_| { + crate::pac::RCC.ccipr1().modify(|w| { + w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); + }) + }); + } + + ::enable(); + ::reset(); + + self.irq.set_handler(Self::on_interrupt); + self.irq.unpend(); + self.irq.enable(); + + let r = T::regs(); + let core_id = r.cid().read().0; + info!("Core id {:08x}", core_id); + + // Wait for AHB ready. + while !r.grstctl().read().ahbidl() {} + + // Configure as device. + r.gusbcfg().write(|w| { + // Force device mode + w.set_fdmod(true); + // Enable internal full-speed PHY + w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); + }); + + // Configuring Vbus sense and SOF output + match core_id { + 0x0000_1200 | 0x0000_1100 => { + assert!(self.phy_type != PhyType::InternalHighSpeed); + + r.gccfg_v1().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal()); + }); + + // F429-like chips have the GCCFG.NOVBUSSENS bit + r.gccfg_v1().modify(|w| { + w.set_novbussens(true); + w.set_vbusasen(false); + w.set_vbusbsen(false); + w.set_sofouten(false); + }); + } + 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { + // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning + r.gccfg_v2().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); + w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); + }); + + r.gccfg_v2().modify(|w| { + w.set_vbden(false); + }); + + // Force B-peripheral session + r.gotgctl().modify(|w| { + w.set_bvaloen(true); + w.set_bvaloval(true); + }); + } + _ => unimplemented!("Unknown USB core id {:X}", core_id), + } + + // Soft disconnect. + r.dctl().write(|w| w.set_sdis(true)); + + // Set speed. + r.dcfg().write(|w| { + w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); + w.set_dspd(self.phy_type.to_dspd()); + }); + + // Unmask transfer complete EP interrupt + r.diepmsk().write(|w| { + w.set_xfrcm(true); + }); + + // Unmask and clear core interrupts + Bus::::restore_irqs(); + r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); + + // Unmask global interrupt + r.gahbcfg().write(|w| { + w.set_gint(true); // unmask global interrupt + }); + + // Connect + r.dctl().write(|w| w.set_sdis(false)); + } + + self.enabled = true; + } + + async fn disable(&mut self) { + trace!("disable"); + + Bus::disable(self); + + self.enabled = false; + } + + async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { + Err(Unsupported) + } +} + +impl<'d, T: Instance> Drop for Bus<'d, T> { + fn drop(&mut self) { + Bus::disable(self); + } +} + +trait Dir { + fn dir() -> Direction; +} + +pub enum In {} +impl Dir for In { + fn dir() -> Direction { + Direction::In + } +} + +pub enum Out {} +impl Dir for Out { + fn dir() -> Direction { + Direction::Out + } +} + +pub struct Endpoint<'d, T: Instance, D> { + _phantom: PhantomData<(&'d mut T, D)>, + info: EndpointInfo, +} + +impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> { + fn info(&self) -> &EndpointInfo { + &self.info + } + + async fn wait_enabled(&mut self) {} +} + +impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { + fn info(&self) -> &EndpointInfo { + &self.info + } + + async fn wait_enabled(&mut self) {} +} + +impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + trace!("read start len={}", buf.len()); + + poll_fn(|cx| { + let index = self.info.addr.index(); + let state = T::state(); + + state.ep_out_wakers[index].register(cx.waker()); + + let len = state.ep_out_size[index].load(Ordering::Relaxed); + if len != EP_OUT_BUFFER_EMPTY { + trace!("read done len={}", len); + + if len as usize > buf.len() { + return Poll::Ready(Err(EndpointError::BufferOverflow)); + } + + // SAFETY: exclusive access ensured by `ep_out_size` atomic variable + let data = unsafe { core::slice::from_raw_parts(*state.ep_out_buffers[index].get(), len as usize) }; + buf[..len as usize].copy_from_slice(data); + + // Release buffer + state.ep_out_size[index].store(EP_OUT_BUFFER_EMPTY, Ordering::Release); + + // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Bus` so a critical section is needed for RMW + critical_section::with(|_| unsafe { + // Receive 1 packet + T::regs().doeptsiz(index).modify(|w| { + w.set_xfrsiz(self.info.max_packet_size as _); + w.set_pktcnt(1); + }); + + // Clear NAK to indicate we are ready to receive more data + T::regs().doepctl(index).modify(|w| { + w.set_cnak(true); + }); + }); + + Poll::Ready(Ok(len as usize)) + } else { + Poll::Pending + } + }) + .await + } +} + +impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { + async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + if buf.len() > self.info.max_packet_size as usize { + return Err(EndpointError::BufferOverflow); + } + + let r = T::regs(); + let index = self.info.addr.index(); + let state = T::state(); + + // Wait for previous transfer to complete + poll_fn(|cx| { + state.ep_in_wakers[index].register(cx.waker()); + + // SAFETY: atomic read with no side effects + if unsafe { r.diepctl(index).read().epena() } { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + + if buf.len() > 0 { + poll_fn(|cx| { + state.ep_in_wakers[index].register(cx.waker()); + + let size_words = (buf.len() + 3) / 4; + + // SAFETY: atomic read with no side effects + let fifo_space = unsafe { r.dtxfsts(index).read().ineptfsav() as usize }; + if size_words > fifo_space { + // Not enough space in fifo, enable tx fifo empty interrupt + // SAFETY: DIEPEMPMSK is shared with IRQ so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.diepempmsk().modify(|w| { + w.set_ineptxfem(w.ineptxfem() | (1 << index)); + }); + }); + + trace!("tx fifo for ep={} full, waiting for txfe", index); + + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await + } + + // SAFETY: DIEPTSIZ is exclusive to this endpoint under `&mut self` + unsafe { + // Setup transfer size + r.dieptsiz(index).write(|w| { + w.set_mcnt(1); + w.set_pktcnt(1); + w.set_xfrsiz(buf.len() as _); + }); + } + + // SAFETY: DIEPCTL is shared with `Bus` so a critical section is needed for RMW + critical_section::with(|_| unsafe { + // Enable endpoint + r.diepctl(index).modify(|w| { + w.set_cnak(true); + w.set_epena(true); + }); + }); + + // Write data to FIFO + for chunk in buf.chunks(4) { + let mut tmp = [0u8; 4]; + tmp[0..chunk.len()].copy_from_slice(chunk); + // SAFETY: FIFO is exclusive to this endpoint under `&mut self` + unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; + } + + trace!("WRITE OK"); + + Ok(()) + } +} + +pub struct ControlPipe<'d, T: Instance> { + _phantom: PhantomData<&'d mut T>, + max_packet_size: u16, + ep_in: Endpoint<'d, T, In>, + ep_out: Endpoint<'d, T, Out>, +} + +impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { + fn max_packet_size(&self) -> usize { + usize::from(self.max_packet_size) + } + + async fn setup(&mut self) -> [u8; 8] { + poll_fn(|cx| { + let state = T::state(); + + state.ep_out_wakers[0].register(cx.waker()); + + if state.ep0_setup_ready.load(Ordering::Relaxed) { + let data = unsafe { *state.ep0_setup_data.get() }; + state.ep0_setup_ready.store(false, Ordering::Release); + + // EP0 should not be controlled by `Bus` so this RMW does not need a critical section + unsafe { + // Receive 1 SETUP packet + T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| { + w.set_rxdpid_stupcnt(1); + }); + + // Clear NAK to indicate we are ready to receive more data + T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { + w.set_cnak(true); + }) + }; + + trace!("SETUP received: {:?}", data); + Poll::Ready(data) + } else { + trace!("SETUP waiting"); + Poll::Pending + } + }) + .await + } + + async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { + trace!("control: data_out"); + let len = self.ep_out.read(buf).await?; + trace!("control: data_out read: {:?}", &buf[..len]); + Ok(len) + } + + async fn data_in(&mut self, data: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { + trace!("control: data_in write: {:?}", data); + self.ep_in.write(data).await?; + + // wait for status response from host after sending the last packet + if last { + trace!("control: data_in waiting for status"); + self.ep_out.read(&mut []).await?; + trace!("control: complete"); + } + + Ok(()) + } + + async fn accept(&mut self) { + trace!("control: accept"); + + self.ep_in.write(&[]).await.ok(); + + trace!("control: accept OK"); + } + + async fn reject(&mut self) { + trace!("control: reject"); + + // EP0 should not be controlled by `Bus` so this RMW does not need a critical section + unsafe { + let regs = T::regs(); + regs.diepctl(self.ep_in.info.addr.index()).modify(|w| { + w.set_stall(true); + }); + regs.doepctl(self.ep_out.info.addr.index()).modify(|w| { + w.set_stall(true); + }); + } + } + + async fn accept_set_address(&mut self, addr: u8) { + trace!("setting addr: {}", addr); + critical_section::with(|_| unsafe { + T::regs().dcfg().modify(|w| { + w.set_dad(addr); + }); + }); + + // synopsys driver requires accept to be sent after changing address + self.accept().await + } +} + +/// Translates HAL [EndpointType] into PAC [vals::Eptyp] +fn to_eptyp(ep_type: EndpointType) -> vals::Eptyp { + match ep_type { + EndpointType::Control => vals::Eptyp::CONTROL, + EndpointType::Isochronous => vals::Eptyp::ISOCHRONOUS, + EndpointType::Bulk => vals::Eptyp::BULK, + EndpointType::Interrupt => vals::Eptyp::INTERRUPT, + } +} + +/// Calculates total allocated FIFO size in words +fn ep_fifo_size(eps: &[Option]) -> u16 { + eps.iter().map(|ep| ep.map(|ep| ep.fifo_size_words).unwrap_or(0)).sum() +} + +/// Generates IRQ mask for enabled endpoints +fn ep_irq_mask(eps: &[Option]) -> u16 { + eps.iter().enumerate().fold( + 0, + |mask, (index, ep)| { + if ep.is_some() { + mask | (1 << index) + } else { + mask + } + }, + ) +} + +/// Calculates MPSIZ value for EP0, which uses special values. +fn ep0_mpsiz(max_packet_size: u16) -> u16 { + match max_packet_size { + 8 => 0b11, + 16 => 0b10, + 32 => 0b01, + 64 => 0b00, + other => panic!("Unsupported EP0 size: {}", other), + } +} + +fn calculate_trdt(speed: vals::Dspd, ahb_freq: Hertz) -> u8 { + match speed { + vals::Dspd::HIGH_SPEED => { + // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) + if ahb_freq.0 >= 30_000_000 { + 0x9 + } else { + panic!("AHB frequency is too low") + } + } + vals::Dspd::FULL_SPEED_EXTERNAL | vals::Dspd::FULL_SPEED_INTERNAL => { + // From RM0431 (F72xx), RM0090 (F429) + match ahb_freq.0 { + 0..=14_199_999 => panic!("AHB frequency is too low"), + 14_200_000..=14_999_999 => 0xF, + 15_000_000..=15_999_999 => 0xE, + 16_000_000..=17_199_999 => 0xD, + 17_200_000..=18_499_999 => 0xC, + 18_500_000..=19_999_999 => 0xB, + 20_000_000..=21_799_999 => 0xA, + 21_800_000..=23_999_999 => 0x9, + 24_000_000..=27_499_999 => 0x8, + 27_500_000..=31_999_999 => 0x7, // 27.7..32 in code from CubeIDE + 32_000_000..=u32::MAX => 0x6, + } + } + _ => unimplemented!(), + } +} From 041531c82911053671e71b7554d1020021f45921 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 11 Jan 2023 17:57:22 +0100 Subject: [PATCH 0502/1575] stm32/rcc: fix u5 pll, add hsi48. --- embassy-stm32/src/rcc/u5.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 960c45322..2ba339ecf 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -295,6 +295,7 @@ pub struct Config { pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, pub apb3_pre: APBPrescaler, + pub hsi48: bool, } impl Default for Config { @@ -305,6 +306,7 @@ impl Default for Config { apb1_pre: Default::default(), apb2_pre: Default::default(), apb3_pre: Default::default(), + hsi48: false, } } } @@ -320,7 +322,6 @@ pub(crate) unsafe fn init(config: Config) { RCC.cr().write(|w| { w.set_msipllen(false); w.set_msison(true); - w.set_msison(true); }); while !RCC.cr().read().msisrdy() {} @@ -340,9 +341,20 @@ pub(crate) unsafe fn init(config: Config) { } ClockSrc::PLL1R(src, m, n, div) => { let freq = match src { - PllSrc::MSI(_) => MSIRange::default().into(), - PllSrc::HSE(hertz) => hertz.0, - PllSrc::HSI16 => HSI_FREQ.0, + PllSrc::MSI(_) => { + // TODO: enable MSI + MSIRange::default().into() + } + PllSrc::HSE(hertz) => { + // TODO: enable HSE + hertz.0 + } + PllSrc::HSI16 => { + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + HSI_FREQ.0 + } }; // disable @@ -355,6 +367,7 @@ pub(crate) unsafe fn init(config: Config) { RCC.pll1cfgr().write(|w| { w.set_pllm(m.into()); w.set_pllsrc(src.into()); + w.set_pllren(true); }); RCC.pll1divr().modify(|w| { @@ -365,15 +378,16 @@ pub(crate) unsafe fn init(config: Config) { // Enable PLL RCC.cr().modify(|w| w.set_pllon(0, true)); while !RCC.cr().read().pllrdy(0) {} - RCC.pll1cfgr().modify(|w| w.set_pllren(true)); - - RCC.cr().write(|w| w.set_pllon(0, true)); - while !RCC.cr().read().pllrdy(0) {} pll_ck } }; + if config.hsi48 { + RCC.cr().modify(|w| w.set_hsi48on(true)); + while !RCC.cr().read().hsi48rdy() {} + } + // TODO make configurable let power_vos = VoltageScale::Range4; From 1af102a1aaa11d03bfa37831a3284546b605efd8 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 11 Jan 2023 17:58:15 +0100 Subject: [PATCH 0503/1575] stm32 otg: add examples. --- examples/stm32f4/Cargo.toml | 5 +- examples/stm32f4/src/bin/usb_serial.rs | 106 ++++++++++++++++++++++++ examples/stm32f7/Cargo.toml | 1 + examples/stm32f7/src/bin/usb_serial.rs | 107 ++++++++++++++++++++++++ examples/stm32h7/Cargo.toml | 1 + examples/stm32h7/src/bin/usb_serial.rs | 106 ++++++++++++++++++++++++ examples/stm32l4/Cargo.toml | 4 +- examples/stm32l4/src/bin/usb_serial.rs | 108 +++++++++++++++++++++++++ examples/stm32u5/Cargo.toml | 1 + examples/stm32u5/src/bin/usb_serial.rs | 108 +++++++++++++++++++++++++ 10 files changed, 542 insertions(+), 5 deletions(-) create mode 100644 examples/stm32f4/src/bin/usb_serial.rs create mode 100644 examples/stm32f7/src/bin/usb_serial.rs create mode 100644 examples/stm32h7/src/bin/usb_serial.rs create mode 100644 examples/stm32l4/src/bin/usb_serial.rs create mode 100644 examples/stm32u5/src/bin/usb_serial.rs diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 62d3f08df..252d60855 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -10,6 +10,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -26,5 +27,5 @@ embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" -usb-device = "0.2" -usbd-serial = "0.1.1" +[profile.release] +debug = 2 diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs new file mode 100644 index 000000000..014647762 --- /dev/null +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::{Driver, Instance}; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.pll48 = true; + config.rcc.sys_ck = Some(mhz(48)); + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index b80dbbf9c..ea4cbd808 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -11,6 +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", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs new file mode 100644 index 000000000..688bd0dab --- /dev/null +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -0,0 +1,107 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::{Driver, Instance}; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.hse = Some(mhz(8)); + config.rcc.pll48 = true; + config.rcc.sys_ck = Some(mhz(200)); + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d30c42b1f..ff38440a7 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -11,6 +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", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs new file mode 100644 index 000000000..b319d12c3 --- /dev/null +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::{Driver, Instance}; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.sys_ck = Some(mhz(400)); + config.rcc.hclk = Some(mhz(200)); + config.rcc.pll1.q_ck = Some(mhz(100)); + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 45d3dd366..5627760ef 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -12,6 +12,7 @@ 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-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"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -26,6 +27,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" -usb-device = "0.2" -usbd-serial = "0.1.1" - diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs new file mode 100644 index 000000000..3e38b10a3 --- /dev/null +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -0,0 +1,108 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_stm32::rcc::*; +use embassy_stm32::usb_otg::{Driver, Instance}; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use panic_probe as _; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None); + config.rcc.hsi48 = true; + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.max_packet_size_0 = 64; + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index d88fdda50..2b02eda92 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -9,6 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs new file mode 100644 index 000000000..c846836b0 --- /dev/null +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -0,0 +1,108 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_stm32::rcc::*; +use embassy_stm32::usb_otg::{Driver, Instance}; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use panic_probe as _; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided); + //config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); + config.rcc.hsi48 = true; + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let mut ep_out_buffer = [0u8; 256]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + None, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From b0529bc943c9da0eb5f43335d06779d6064b765a Mon Sep 17 00:00:00 2001 From: huntc Date: Fri, 6 Jan 2023 22:21:39 +1100 Subject: [PATCH 0504/1575] Support codesigning in the firmware updater This commit provides a method to verify that firmware has been signed with a private key given its public key. The implementation uses ed25519-dalek as the signature verifier. An "ed25519" feature is required to enable the functionality. When disabled (the default), calling the firmware updater's verify method will return a failure. --- .github/workflows/rust.yml | 10 +- embassy-boot/boot/Cargo.toml | 16 +- embassy-boot/boot/src/lib.rs | 367 ++++++++++++++++++++++- examples/boot/application/nrf/Cargo.toml | 5 + examples/boot/application/nrf/README.md | 2 +- 5 files changed, 382 insertions(+), 18 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b93c8783d..3bfe5ef03 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -68,5 +68,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Test - run: cd embassy-sync && cargo test + + - name: Test boot + working-directory: ./embassy-boot/boot + run: cargo test && cargo test --features "ed25519-dalek" && cargo test --features "ed25519-salty" + + - name: Test sync + working-directory: ./embassy-sync + run: cargo test diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index ae4efbd2e..0b0c77b1e 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -25,12 +25,26 @@ features = ["defmt"] [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4", optional = true } +ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" embedded-storage-async = "0.3.0" +salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } +signature = { version = "1.6.4", default-features = false } [dev-dependencies] log = "0.4" env_logger = "0.9" -rand = "0.8" +rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } + +[dev-dependencies.ed25519-dalek] +default_features = false +features = ["rand", "std", "u32_backend"] + +[features] +ed25519-dalek = ["dep:ed25519-dalek", "_verify"] +ed25519-salty = ["dep:salty", "_verify"] + +#Internal features +_verify = [] \ No newline at end of file diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 76b14bc8c..be254e9d7 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -52,6 +52,16 @@ pub enum BootError { BadMagic, } +#[cfg(feature = "defmt")] +impl defmt::Format for BootError { + fn format(&self, fmt: defmt::Formatter) { + match self { + BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), + BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), + } + } +} + impl From for BootError where E: NorFlashError, @@ -557,6 +567,33 @@ where self.state } } +/// Errors returned by FirmwareUpdater +#[derive(Debug)] +pub enum FirmwareUpdaterError { + /// Error from flash. + Flash(NorFlashErrorKind), + /// Signature errors. + Signature(signature::Error), +} + +#[cfg(feature = "defmt")] +impl defmt::Format for FirmwareUpdaterError { + fn format(&self, fmt: defmt::Formatter) { + match self { + FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), + FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), + } + } +} + +impl From for FirmwareUpdaterError +where + E: NorFlashError, +{ + fn from(error: E) -> Self { + FirmwareUpdaterError::Flash(error.kind()) + } +} /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state @@ -609,7 +646,11 @@ impl FirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub async fn get_state(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + pub async fn get_state( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result { flash.read(self.state.from as u32, aligned).await?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { @@ -619,12 +660,126 @@ impl FirmwareUpdater { } } + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub async fn verify_and_mark_updated( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic(_aligned, SWAP_MAGIC, _flash).await + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety /// /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_updated(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + #[cfg(not(feature = "_verify"))] + pub async fn mark_updated( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic(aligned, SWAP_MAGIC, flash).await } @@ -634,7 +789,11 @@ impl FirmwareUpdater { /// # Safety /// /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + pub async fn mark_booted( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic(aligned, BOOT_MAGIC, flash).await } @@ -644,7 +803,7 @@ impl FirmwareUpdater { aligned: &mut [u8], magic: u8, flash: &mut F, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { flash.read(self.state.from as u32, aligned).await?; if aligned.iter().any(|&b| b != magic) { @@ -672,7 +831,7 @@ impl FirmwareUpdater { data: &[u8], flash: &mut F, block_size: usize, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); flash @@ -700,7 +859,10 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub async fn prepare_update(&mut self, flash: &mut F) -> Result { + pub async fn prepare_update( + &mut self, + flash: &mut F, + ) -> Result { flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); @@ -717,7 +879,11 @@ impl FirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub fn get_state_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + pub fn get_state_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result { flash.read(self.state.from as u32, aligned)?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { @@ -727,12 +893,126 @@ impl FirmwareUpdater { } } + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub fn verify_and_mark_updated_blocking( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety /// /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_updated_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + #[cfg(not(feature = "_verify"))] + pub fn mark_updated_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic_blocking(aligned, SWAP_MAGIC, flash) } @@ -742,7 +1022,11 @@ impl FirmwareUpdater { /// # Safety /// /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + pub fn mark_booted_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); self.set_magic_blocking(aligned, BOOT_MAGIC, flash) } @@ -752,7 +1036,7 @@ impl FirmwareUpdater { aligned: &mut [u8], magic: u8, flash: &mut F, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { flash.read(self.state.from as u32, aligned)?; if aligned.iter().any(|&b| b != magic) { @@ -780,7 +1064,7 @@ impl FirmwareUpdater { data: &[u8], flash: &mut F, block_size: usize, - ) -> Result<(), F::Error> { + ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); flash.erase( @@ -804,7 +1088,10 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware_blocking` allows for an optimized /// API in exchange for added complexity. - pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { + pub fn prepare_update_blocking( + &mut self, + flash: &mut F, + ) -> Result { flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); @@ -953,6 +1240,7 @@ mod tests { } #[test] + #[cfg(not(feature = "_verify"))] fn test_swap_state() { const STATE: Partition = Partition::new(0, 4096); const ACTIVE: Partition = Partition::new(4096, 61440); @@ -1022,6 +1310,7 @@ mod tests { } #[test] + #[cfg(not(feature = "_verify"))] fn test_separate_flash_active_page_biggest() { const STATE: Partition = Partition::new(2048, 4096); const ACTIVE: Partition = Partition::new(4096, 16384); @@ -1074,6 +1363,7 @@ mod tests { } #[test] + #[cfg(not(feature = "_verify"))] fn test_separate_flash_dfu_page_biggest() { const STATE: Partition = Partition::new(2048, 4096); const ACTIVE: Partition = Partition::new(4096, 16384); @@ -1133,6 +1423,55 @@ mod tests { assert_partitions(ACTIVE, DFU, STATE, 4096, 4); } + #[test] + #[cfg(feature = "_verify")] + fn test_verify() { + // The following key setup is based on: + // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example + + use ed25519_dalek::Keypair; + use rand::rngs::OsRng; + + let mut csprng = OsRng {}; + let keypair: Keypair = Keypair::generate(&mut csprng); + + use ed25519_dalek::{Digest, Sha512, Signature, Signer}; + let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU."; + let mut digest = Sha512::new(); + digest.update(&firmware); + let message = digest.finalize(); + let signature: Signature = keypair.sign(&message); + + use ed25519_dalek::PublicKey; + let public_key: PublicKey = keypair.public; + + // Setup flash + + const STATE: Partition = Partition::new(0, 4096); + const DFU: Partition = Partition::new(4096, 8192); + let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); + + let firmware_len = firmware.len(); + + let mut write_buf = [0; 4096]; + write_buf[0..firmware_len].copy_from_slice(firmware); + NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); + + // On with the test + + let mut updater = FirmwareUpdater::new(DFU, STATE); + + let mut aligned = [0; 4]; + + assert!(block_on(updater.verify_and_mark_updated( + &mut flash, + &public_key.to_bytes(), + &signature.to_bytes(), + firmware_len, + &mut aligned, + )) + .is_ok()); + } struct MemFlash([u8; SIZE]); impl NorFlash @@ -1171,7 +1510,7 @@ mod tests { impl ReadNorFlash for MemFlash { - const READ_SIZE: usize = 4; + const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { let len = buf.len(); @@ -1194,7 +1533,7 @@ mod tests { impl AsyncReadNorFlash for MemFlash { - const READ_SIZE: usize = 4; + const READ_SIZE: usize = 1; type ReadFuture<'a> = impl Future> + 'a; fn read<'a>(&'a mut self, offset: u32, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 9679bbc5e..888993255 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -9,6 +9,7 @@ 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-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } +embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } @@ -19,3 +20,7 @@ embedded-hal = { version = "0.2.6" } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" + +[features] +ed25519-dalek = ["embassy-boot/ed25519-dalek"] +ed25519-salty = ["embassy-boot/ed25519-salty"] \ No newline at end of file diff --git a/examples/boot/application/nrf/README.md b/examples/boot/application/nrf/README.md index 5d45f6290..9d6d20336 100644 --- a/examples/boot/application/nrf/README.md +++ b/examples/boot/application/nrf/README.md @@ -22,7 +22,7 @@ cp memory-bl.x ../../bootloader/nrf/memory.x # Flash bootloader cargo flash --manifest-path ../../bootloader/nrf/Cargo.toml --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi --release --chip nRF52840_xxAA # Build 'b' -cargo build --release --bin b +cargo build --release --bin b --features embassy-nrf/nrf52840 # Generate binary for 'b' cargo objcopy --release --bin b --features embassy-nrf/nrf52840 --target thumbv7em-none-eabi -- -O binary b.bin ``` From 7ecb05ff77ba8cd87d87c1c6bc3b8c1a1624ad15 Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 13 Jan 2023 12:08:25 -0600 Subject: [PATCH 0505/1575] usb: allow setting the interface string for interface alt settings --- embassy-usb/src/builder.rs | 19 +++++++++++++++---- embassy-usb/src/class/cdc_acm.rs | 4 ++-- embassy-usb/src/class/cdc_ncm/mod.rs | 6 +++--- embassy-usb/src/class/hid.rs | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 87a8333bb..785dc6f87 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -306,14 +306,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { /// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0. /// /// The first alternate setting, with number 0, is the default one. - pub fn alt_setting(&mut self, class: u8, subclass: u8, protocol: u8) -> InterfaceAltBuilder<'_, 'd, D> { + pub fn alt_setting( + &mut self, + class: u8, + subclass: u8, + protocol: u8, + interface_string: Option, + ) -> InterfaceAltBuilder<'_, 'd, D> { let number = self.next_alt_setting_number; self.next_alt_setting_number += 1; self.builder.interfaces[self.interface_number.0 as usize].num_alt_settings += 1; - self.builder - .config_descriptor - .interface_alt(self.interface_number, number, class, subclass, protocol, None); + self.builder.config_descriptor.interface_alt( + self.interface_number, + number, + class, + subclass, + protocol, + interface_string, + ); InterfaceAltBuilder { builder: self.builder, diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 84db20621..09d7f774d 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -171,7 +171,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { iface.handler(control); let comm_if = iface.interface_number(); let data_if = u8::from(comm_if) + 1; - let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); alt.descriptor( CS_INTERFACE, @@ -205,7 +205,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { // Data interface let mut iface = func.interface(); let data_if = iface.interface_number(); - let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None); let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 4954a65bc..d946eb113 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -254,7 +254,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { mac_addr_str: [0; 12], })); let comm_if = iface.interface_number(); - let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); alt.descriptor( CS_INTERFACE, @@ -304,8 +304,8 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { let mut iface = func.interface(); iface.handler(state.data_control.write(DataControl {})); let data_if = iface.interface_number(); - let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB); - let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB); + let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); + let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index b967aba0e..12e49b7de 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -101,7 +101,7 @@ fn build<'d, D: Driver<'d>>( let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); let mut iface = func.interface(); iface.handler(control); - let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); + let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); // HID descriptor alt.descriptor( From 816b214403b7ce524cbb6ce85732df70f5b8f19e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 14 Jan 2023 07:12:43 +0100 Subject: [PATCH 0506/1575] Only implement Write --- embassy-stm32/src/usart/mod.rs | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 1c13d9eca..1f50e5dcb 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -912,7 +912,7 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::{Read, Write}; + use embedded_io::asynch::Read; use embedded_io::Io; use super::*; @@ -924,16 +924,6 @@ mod eio { type Error = Error; } - impl Read for Uart<'_, T, TxDma, RxDma> - where - T: BasicInstance, - RxDma: super::RxDma, - { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_until_idle(buf).await - } - } - impl Write for Uart<'_, T, TxDma, RxDma> where T: BasicInstance, @@ -949,23 +939,6 @@ mod eio { } } - impl Io for UartRx<'_, T, RxDma> - where - T: BasicInstance, - { - type Error = Error; - } - - impl Read for UartRx<'_, T, RxDma> - where - T: BasicInstance, - RxDma: super::RxDma, - { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_until_idle(buf).await - } - } - impl Io for UartTx<'_, T, TxDma> where T: BasicInstance, From 16590732f8708f4f55cbb9bc126bf5218b5e3c58 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 14 Jan 2023 07:13:29 +0100 Subject: [PATCH 0507/1575] Update mod.rs --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 1f50e5dcb..d71aa61a4 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -912,7 +912,7 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::Read; + use embedded_io::asynch::Write; use embedded_io::Io; use super::*; From 868d01889b7976ca4e4d2d775da19a1392208579 Mon Sep 17 00:00:00 2001 From: huntc Date: Sat, 14 Jan 2023 17:36:22 +1100 Subject: [PATCH 0508/1575] Documentation on verifying firmware The documentation has been enhanced to describe the verification of firmware with the firmware updater. Examples have also been provided that describe how keys can be generated and how firmware can be signed. --- docs/modules/ROOT/pages/bootloader.adoc | 50 ++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc index b50de5abd..b7215e52a 100644 --- a/docs/modules/ROOT/pages/bootloader.adoc +++ b/docs/modules/ROOT/pages/bootloader.adoc @@ -6,7 +6,7 @@ The bootloader can be used either as a library or be flashed directly if you are By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself. -The bootloader supports both internal and external flash by relying on the `embedded-storage` traits. +The bootloader supports both internal and external flash by relying on the `embedded-storage` traits. The bootloader optionally supports the verification of firmware that has been digitally signed (recommended). == Hardware support @@ -44,3 +44,51 @@ The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. + +=== FirmwareUpdater + +The `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash "write block" (typically 4KiB), and `mark_updated`, which is the final call. + +=== Verification + +The bootloader supports the verification of firmware that has been flashed to the DFU partition. Verification requires that firmware has been signed digitally using link:https://ed25519.cr.yp.to/[`ed25519`] signatures. With verification enabled, the `FirmwareUpdater::verify_and_mark_updated` method is called in place of `mark_updated`. A public key and signature are required, along with the actual length of the firmware that has been flashed. If verification fails then the firmware will not be marked as updated and therefore be rejected. + +Signatures are normally conveyed with the firmware to be updated and not written to flash. How signatures are provided is a firmware responsibility. + +To enable verification use either the `ed25519-dalek` or `ed25519-salty` features when depending on the `embassy-boot` crate. We recommend `ed25519-salty` at this time due to its small size. + +==== Tips on keys and signing with ed25519 + +Ed25519 is a public key signature system where you are responsible for keeping the private key secure. We recommend embedding the *public* key in your program so that it can be easily passed to `verify_and_mark_updated`. An example declaration of the public key in your firmware: + +[source, rust] +---- +static PUBLIC_SIGNING_KEY: &[u8] = include_bytes!("key.pub"); +---- + +Signatures are often conveyed along with firmware by appending them. + +Ed25519 keys can be generated by a variety of tools. We recommend link:https://man.openbsd.org/signify[`signify`] as it is in wide use to sign and verify OpenBSD distributions, and is straightforward to use. + +The following set of Bash commands can be used to generate public and private keys on Unix platforms, and also generate a local `key.pub` file with the `signify` file headers removed. Declare a `SECRETS_DIR` environment variable in a secure location. + +[source, bash] +---- +signify -G -n -p $SECRETS_DIR/key.pub -s $SECRETS_DIR/key.sec +tail -n1 $SECRETS_DIR/key.pub | base64 -d -i - | dd ibs=10 skip=1 > key.pub +chmod 700 $SECRETS_DIR/key.sec +export SECRET_SIGNING_KEY=$(tail -n1 $SECRETS_DIR/key.sec) +---- + +Then, to sign your firmware given a declaration of `FIRMWARE_DIR` and a firmware filename of `myfirmware`: + +[source, bash] +---- +shasum -a 512 -b $FIRMWARE_DIR/myfirmware > $SECRETS_DIR/message.txt +cat $SECRETS_DIR/message.txt | dd ibs=128 count=1 | xxd -p -r > $SECRETS_DIR/message.txt +signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig +cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed +tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed +---- + +Remember, guard the `$SECRETS_DIR/key.sec` key as compromising it means that another party can sign your firmware. \ No newline at end of file From 2a349afea791d2cb262497433a2ad24c3010a696 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 17 Jan 2023 18:54:23 +0100 Subject: [PATCH 0509/1575] stm32: add stm32c0 support. --- ci.sh | 2 +- ci_stable.sh | 2 +- embassy-stm32/Cargo.toml | 21 ++-- embassy-stm32/src/exti.rs | 16 +-- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/rcc/c0.rs | 233 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/mod.rs | 5 +- stm32-data | 2 +- stm32-metapac-gen/Cargo.toml | 2 +- stm32-metapac/Cargo.toml | 21 ++-- 10 files changed, 277 insertions(+), 29 deletions(-) create mode 100644 embassy-stm32/src/rcc/c0.rs diff --git a/ci.sh b/ci.sh index 30e664a2b..fc802f858 100755 --- a/ci.sh +++ b/ci.sh @@ -76,7 +76,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ --- 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 \ diff --git a/ci_stable.sh b/ci_stable.sh index 60ddb659d..b4b0b83e7 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -36,7 +36,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 67996cca4..d9c1f6dcf 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -19,6 +19,7 @@ flavors = [ { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f42.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, @@ -110,6 +111,19 @@ unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # BEGIN GENERATED FEATURES # Generated by stm32-gen-features. DO NOT EDIT. +stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] +stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] +stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] +stm32c011j4 = [ "stm32-metapac/stm32c011j4" ] +stm32c011j6 = [ "stm32-metapac/stm32c011j6" ] +stm32c031c4 = [ "stm32-metapac/stm32c031c4" ] +stm32c031c6 = [ "stm32-metapac/stm32c031c6" ] +stm32c031f4 = [ "stm32-metapac/stm32c031f4" ] +stm32c031f6 = [ "stm32-metapac/stm32c031f6" ] +stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] +stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] +stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] +stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] stm32f030cc = [ "stm32-metapac/stm32f030cc" ] @@ -1318,11 +1332,9 @@ stm32u575zi = [ "stm32-metapac/stm32u575zi" ] stm32u585ai = [ "stm32-metapac/stm32u585ai" ] stm32u585ci = [ "stm32-metapac/stm32u585ci" ] stm32u585oi = [ "stm32-metapac/stm32u585oi" ] -stm32u585qe = [ "stm32-metapac/stm32u585qe" ] stm32u585qi = [ "stm32-metapac/stm32u585qi" ] stm32u585ri = [ "stm32-metapac/stm32u585ri" ] stm32u585vi = [ "stm32-metapac/stm32u585vi" ] -stm32u585ze = [ "stm32-metapac/stm32u585ze" ] stm32u585zi = [ "stm32-metapac/stm32u585zi" ] stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] @@ -1340,7 +1352,6 @@ stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ] stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ] stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ] stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ] -stm32wb5mmg = [ "stm32-metapac/stm32wb5mmg" ] stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ] stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ] stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ] @@ -1349,8 +1360,6 @@ stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4" ] stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p" ] stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4" ] stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p" ] -stm32wl55uc-cm4 = [ "stm32-metapac/stm32wl55uc-cm4" ] -stm32wl55uc-cm0p = [ "stm32-metapac/stm32wl55uc-cm0p" ] stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] @@ -1363,6 +1372,4 @@ stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] -stm32wle5u8 = [ "stm32-metapac/stm32wle5u8" ] -stm32wle5ub = [ "stm32-metapac/stm32wle5ub" ] # END GENERATED FEATURES diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index f90785815..c9c3ef62a 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -25,11 +25,11 @@ fn cpu_regs() -> pac::exti::Exti { EXTI } -#[cfg(not(any(exti_g0, exti_l5, gpio_v1, exti_u5)))] +#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5)))] fn exticr_regs() -> pac::syscfg::Syscfg { pac::SYSCFG } -#[cfg(any(exti_g0, exti_l5, exti_u5))] +#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] fn exticr_regs() -> pac::exti::Exti { EXTI } @@ -39,9 +39,9 @@ fn exticr_regs() -> pac::afio::Afio { } pub unsafe fn on_irq() { - #[cfg(not(any(exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] let bits = EXTI.pr(0).read().0; - #[cfg(any(exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; // Mask all the channels that fired. @@ -53,9 +53,9 @@ pub unsafe fn on_irq() { } // Clear pending - #[cfg(not(any(exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] EXTI.pr(0).write_value(Lines(bits)); - #[cfg(any(exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] { EXTI.rpr(0).write_value(Lines(bits)); EXTI.fpr(0).write_value(Lines(bits)); @@ -212,9 +212,9 @@ impl<'a> ExtiInputFuture<'a> { EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); // clear pending bit - #[cfg(not(any(exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] EXTI.pr(0).write(|w| w.set_line(pin, true)); - #[cfg(any(exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] { EXTI.rpr(0).write(|w| w.set_line(pin, true)); EXTI.fpr(0).write(|w| w.set_line(pin, true)); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 610c24888..eeaa04f67 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -121,7 +121,7 @@ pub fn init(config: Config) -> Peripherals { #[cfg(dbgmcu)] if config.enable_debug_during_sleep { crate::pac::DBGMCU.cr().modify(|cr| { - #[cfg(any(dbgmcu_f0, dbgmcu_g0, dbgmcu_u5))] + #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))] { cr.set_dbg_stop(true); cr.set_dbg_standby(true); diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs new file mode 100644 index 000000000..6c7b36647 --- /dev/null +++ b/embassy-stm32/src/rcc/c0.rs @@ -0,0 +1,233 @@ +use crate::pac::flash::vals::Latency; +use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::{FLASH, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; + +/// HSI speed +pub const HSI_FREQ: Hertz = Hertz(48_000_000); + +/// LSI speed +pub const LSI_FREQ: Hertz = Hertz(32_000); + +/// System clock mux source +#[derive(Clone, Copy)] +pub enum ClockSrc { + HSE(Hertz), + HSI(HSIPrescaler), + LSI, +} + +#[derive(Clone, Copy)] +pub enum HSIPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +impl Into for HSIPrescaler { + fn into(self) -> Hsidiv { + match self { + HSIPrescaler::NotDivided => Hsidiv::DIV1, + HSIPrescaler::Div2 => Hsidiv::DIV2, + HSIPrescaler::Div4 => Hsidiv::DIV4, + HSIPrescaler::Div8 => Hsidiv::DIV8, + HSIPrescaler::Div16 => Hsidiv::DIV16, + HSIPrescaler::Div32 => Hsidiv::DIV32, + HSIPrescaler::Div64 => Hsidiv::DIV64, + HSIPrescaler::Div128 => Hsidiv::DIV128, + } + } +} + +/// AHB prescaler +#[derive(Clone, Copy, PartialEq)] +pub enum AHBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div64, + Div128, + Div256, + Div512, +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum APBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, +} + +impl Into for APBPrescaler { + fn into(self) -> Ppre { + match self { + APBPrescaler::NotDivided => Ppre::DIV1, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +impl Into for AHBPrescaler { + fn into(self) -> Hpre { + match self { + AHBPrescaler::NotDivided => Hpre::DIV1, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, + } + } +} + +/// Clocks configutation +pub struct Config { + pub mux: ClockSrc, + pub ahb_pre: AHBPrescaler, + pub apb_pre: APBPrescaler, +} + +impl Default for Config { + #[inline] + fn default() -> Config { + Config { + mux: ClockSrc::HSI(HSIPrescaler::NotDivided), + ahb_pre: AHBPrescaler::NotDivided, + apb_pre: APBPrescaler::NotDivided, + } + } +} + +pub(crate) unsafe fn init(config: Config) { + let (sys_clk, sw) = match config.mux { + ClockSrc::HSI(div) => { + // Enable HSI + let div: Hsidiv = div.into(); + RCC.cr().write(|w| { + w.set_hsidiv(div); + w.set_hsion(true) + }); + while !RCC.cr().read().hsirdy() {} + + (HSI_FREQ.0 >> div.0, Sw::HSI) + } + ClockSrc::HSE(freq) => { + // Enable HSE + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + + (freq.0, Sw::HSE) + } + ClockSrc::LSI => { + // Enable LSI + RCC.csr2().write(|w| w.set_lsion(true)); + while !RCC.csr2().read().lsirdy() {} + (LSI_FREQ.0, Sw::LSI) + } + }; + + // Determine the flash latency implied by the target clock speed + // RM0454 § 3.3.4: + let target_flash_latency = if sys_clk <= 24_000_000 { + Latency::WS0 + } else { + Latency::WS1 + }; + + // Increase the number of cycles we wait for flash if the new value is higher + // There's no harm in waiting a little too much before the clock change, but we'll + // crash immediately if we don't wait enough after the clock change + let mut set_flash_latency_after = false; + FLASH.acr().modify(|w| { + // Is the current flash latency less than what we need at the new SYSCLK? + if w.latency().0 <= target_flash_latency.0 { + // We must increase the number of wait states now + w.set_latency(target_flash_latency) + } else { + // We may decrease the number of wait states later + set_flash_latency_after = true; + } + + // RM0490 § 3.3.4: + // > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register + // > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the + // > Flash memory. + // + // Enable flash prefetching if we have at least one wait state, and disable it otherwise. + w.set_prften(target_flash_latency.0 > 0); + }); + + if !set_flash_latency_after { + // Spin until the effective flash latency is compatible with the clock change + while FLASH.acr().read().latency().0 < target_flash_latency.0 {} + } + + // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once + let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into()); + RCC.cfgr().modify(|w| { + w.set_sw(sw); + w.set_hpre(hpre); + w.set_ppre(ppre); + }); + + if set_flash_latency_after { + // We can make the flash require fewer wait states + // Spin until the SYSCLK changes have taken effect + loop { + let cfgr = RCC.cfgr().read(); + if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre { + break; + } + } + + // Set the flash latency to require fewer wait states + FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); + } + + let ahb_div = match config.ahb_pre { + AHBPrescaler::NotDivided => 1, + AHBPrescaler::Div2 => 2, + AHBPrescaler::Div4 => 4, + AHBPrescaler::Div8 => 8, + AHBPrescaler::Div16 => 16, + AHBPrescaler::Div64 => 64, + AHBPrescaler::Div128 => 128, + AHBPrescaler::Div256 => 256, + AHBPrescaler::Div512 => 512, + }; + let ahb_freq = sys_clk / ahb_div; + + let (apb_freq, apb_tim_freq) = match config.apb_pre { + APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + pre => { + let pre: Ppre = pre.into(); + let pre: u8 = 1 << (pre.0 - 3); + let freq = ahb_freq / pre as u32; + (freq, freq * 2) + } + }; + + set_freqs(Clocks { + sys: Hertz(sys_clk), + ahb1: Hertz(ahb_freq), + apb1: Hertz(apb_freq), + apb1_tim: Hertz(apb_tim_freq), + }); +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 1b1180c03..0a52089d1 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -10,6 +10,7 @@ use crate::time::Hertz; #[cfg_attr(rcc_f3, path = "f3.rs")] #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] #[cfg_attr(rcc_f7, path = "f7.rs")] +#[cfg_attr(rcc_c0, path = "c0.rs")] #[cfg_attr(rcc_g0, path = "g0.rs")] #[cfg_attr(rcc_g4, path = "g4.rs")] #[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")] @@ -30,9 +31,9 @@ pub struct Clocks { // APB pub apb1: Hertz, pub apb1_tim: Hertz, - #[cfg(not(rcc_g0))] + #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2: Hertz, - #[cfg(not(rcc_g0))] + #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2_tim: Hertz, #[cfg(any(rcc_wl5, rcc_wle, rcc_u5))] pub apb3: Hertz, diff --git a/stm32-data b/stm32-data index 844793fc3..96decdd61 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 844793fc3da2ba3f12ab6a69b78cd8e6fb5497b4 +Subproject commit 96decdd6114d78813c1f748fb878a45e1b03bf73 diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml index 9598a5945..6d136ba6b 100644 --- a/stm32-metapac-gen/Cargo.toml +++ b/stm32-metapac-gen/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] regex = "1.5.4" -chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "28ffa8a19d84914089547f52900ffb5877a5dc23" } +chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "1d9e0a39a6acc291e50cabc4ed617a87f06d5e89" } serde = { version = "1.0.130", features = [ "derive" ] } serde_json = "1.0.87" serde_yaml = "0.8.21" diff --git a/stm32-metapac/Cargo.toml b/stm32-metapac/Cargo.toml index 9d5aba0c0..2605cf3d3 100644 --- a/stm32-metapac/Cargo.toml +++ b/stm32-metapac/Cargo.toml @@ -27,6 +27,7 @@ flavors = [ { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, @@ -67,6 +68,19 @@ memory-x = [] # BEGIN GENERATED FEATURES # Generated by stm32-gen-features. DO NOT EDIT. +stm32c011d6 = [] +stm32c011f4 = [] +stm32c011f6 = [] +stm32c011j4 = [] +stm32c011j6 = [] +stm32c031c4 = [] +stm32c031c6 = [] +stm32c031f4 = [] +stm32c031f6 = [] +stm32c031g4 = [] +stm32c031g6 = [] +stm32c031k4 = [] +stm32c031k6 = [] stm32f030c6 = [] stm32f030c8 = [] stm32f030cc = [] @@ -1275,11 +1289,9 @@ stm32u575zi = [] stm32u585ai = [] stm32u585ci = [] stm32u585oi = [] -stm32u585qe = [] stm32u585qi = [] stm32u585ri = [] stm32u585vi = [] -stm32u585ze = [] stm32u585zi = [] stm32wb10cc = [] stm32wb15cc = [] @@ -1297,7 +1309,6 @@ stm32wb55vc = [] stm32wb55ve = [] stm32wb55vg = [] stm32wb55vy = [] -stm32wb5mmg = [] stm32wl54cc-cm4 = [] stm32wl54cc-cm0p = [] stm32wl54jc-cm4 = [] @@ -1306,8 +1317,6 @@ stm32wl55cc-cm4 = [] stm32wl55cc-cm0p = [] stm32wl55jc-cm4 = [] stm32wl55jc-cm0p = [] -stm32wl55uc-cm4 = [] -stm32wl55uc-cm0p = [] stm32wle4c8 = [] stm32wle4cb = [] stm32wle4cc = [] @@ -1320,6 +1329,4 @@ stm32wle5cc = [] stm32wle5j8 = [] stm32wle5jb = [] stm32wle5jc = [] -stm32wle5u8 = [] -stm32wle5ub = [] # END GENERATED FEATURES From 355761fd68a238033bcb3441d949bf5115b1a84a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 17 Jan 2023 18:54:43 +0100 Subject: [PATCH 0510/1575] stm32: add stm32c0 examples. --- ci.sh | 1 + examples/stm32c0/.cargo/config.toml | 9 +++++++++ examples/stm32c0/Cargo.toml | 21 +++++++++++++++++++ examples/stm32c0/build.rs | 5 +++++ examples/stm32c0/src/bin/blinky.rs | 27 +++++++++++++++++++++++++ examples/stm32c0/src/bin/button.rs | 25 +++++++++++++++++++++++ examples/stm32c0/src/bin/button_exti.rs | 27 +++++++++++++++++++++++++ 7 files changed, 115 insertions(+) create mode 100644 examples/stm32c0/.cargo/config.toml create mode 100644 examples/stm32c0/Cargo.toml create mode 100644 examples/stm32c0/build.rs create mode 100644 examples/stm32c0/src/bin/blinky.rs create mode 100644 examples/stm32c0/src/bin/button.rs create mode 100644 examples/stm32c0/src/bin/button_exti.rs diff --git a/ci.sh b/ci.sh index fc802f858..323e752d2 100755 --- a/ci.sh +++ b/ci.sh @@ -98,6 +98,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \ --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \ + --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ diff --git a/examples/stm32c0/.cargo/config.toml b/examples/stm32c0/.cargo/config.toml new file mode 100644 index 000000000..eb07f6190 --- /dev/null +++ b/examples/stm32c0/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32G071C8Rx with your chip as listed in `probe-run --list-chips` +runner = "probe-rs-cli run --speed 100 --chip STM32c031c6tx" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml new file mode 100644 index 000000000..0095a680c --- /dev/null +++ b/examples/stm32c0/Cargo.toml @@ -0,0 +1,21 @@ +[package] +edition = "2021" +name = "embassy-stm32c0-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +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", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } + +defmt = "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" +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/stm32c0/build.rs b/examples/stm32c0/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32c0/build.rs @@ -0,0 +1,5 @@ +fn main() { + 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/stm32c0/src/bin/blinky.rs b/examples/stm32c0/src/bin/blinky.rs new file mode 100644 index 000000000..8a65b0692 --- /dev/null +++ b/examples/stm32c0/src/bin/blinky.rs @@ -0,0 +1,27 @@ +#![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 _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + 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/stm32c0/src/bin/button.rs b/examples/stm32c0/src/bin/button.rs new file mode 100644 index 000000000..72a3f5cbf --- /dev/null +++ b/examples/stm32c0/src/bin/button.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let button = Input::new(p.PC13, Pull::Up); + + loop { + if button.is_high() { + info!("high"); + } else { + info!("low"); + } + } +} diff --git a/examples/stm32c0/src/bin/button_exti.rs b/examples/stm32c0/src/bin/button_exti.rs new file mode 100644 index 000000000..ef32d4c4a --- /dev/null +++ b/examples/stm32c0/src/bin/button_exti.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Input, Pull}; +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 button = Input::new(p.PC13, Pull::Up); + let mut button = ExtiInput::new(button, p.EXTI13); + + info!("Press the USER button..."); + + loop { + button.wait_for_falling_edge().await; + info!("Pressed!"); + button.wait_for_rising_edge().await; + info!("Released!"); + } +} From d2f2b451d084448d442c6f9cb1c6b9f94a4fd447 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 02:29:49 +0200 Subject: [PATCH 0511/1575] stm32/usb_otg: implement endpoint wait_enabled --- embassy-stm32/src/usb_otg/usb.rs | 38 ++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 2d9b613ea..504a90f25 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -791,6 +791,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { w.set_usbaep(enabled); }) }); + + // Wake `Endpoint::wait_enabled()` + T::state().ep_out_wakers[ep_addr.index()].wake(); } Direction::In => { // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW @@ -807,6 +810,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { w.set_usbaep(enabled); }) }); + + // Wake `Endpoint::wait_enabled()` + T::state().ep_in_wakers[ep_addr.index()].wake(); } } } @@ -1031,7 +1037,21 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> { &self.info } - async fn wait_enabled(&mut self) {} + async fn wait_enabled(&mut self) { + poll_fn(|cx| { + let ep_index = self.info.addr.index(); + + T::state().ep_in_wakers[ep_index].register(cx.waker()); + + // SAFETY: atomic read without side effects + if unsafe { T::regs().diepctl(ep_index).read().usbaep() } { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } } impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { @@ -1039,7 +1059,21 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { &self.info } - async fn wait_enabled(&mut self) {} + async fn wait_enabled(&mut self) { + poll_fn(|cx| { + let ep_index = self.info.addr.index(); + + T::state().ep_out_wakers[ep_index].register(cx.waker()); + + // SAFETY: atomic read without side effects + if unsafe { T::regs().doepctl(ep_index).read().usbaep() } { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } } impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { From f07e59b24a2fff6dfbfd52ac5f55fb2667dae0d8 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 02:31:28 +0200 Subject: [PATCH 0512/1575] stm32/usb_otg: prevent writes on disabled endpoint --- embassy-stm32/src/usb_otg/usb.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 504a90f25..48c3ec019 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -1126,6 +1126,8 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { + trace!("write ep={} data={}", self.info.addr, buf); + if buf.len() > self.info.max_packet_size as usize { return Err(EndpointError::BufferOverflow); } @@ -1134,18 +1136,21 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { let index = self.info.addr.index(); let state = T::state(); - // Wait for previous transfer to complete + // Wait for previous transfer to complete and check if endpoint is disabled poll_fn(|cx| { state.ep_in_wakers[index].register(cx.waker()); // SAFETY: atomic read with no side effects - if unsafe { r.diepctl(index).read().epena() } { - Poll::Pending + let diepctl = unsafe { r.diepctl(index).read() }; + if !diepctl.usbaep() { + Poll::Ready(Err(EndpointError::Disabled)) + } else if !diepctl.epena() { + Poll::Ready(Ok(())) } else { - Poll::Ready(()) + Poll::Pending } }) - .await; + .await?; if buf.len() > 0 { poll_fn(|cx| { @@ -1201,7 +1206,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; } - trace!("WRITE OK"); + trace!("write done ep={}", self.info.addr); Ok(()) } @@ -1239,8 +1244,8 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { // Clear NAK to indicate we are ready to receive more data T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { w.set_cnak(true); - }) - }; + }); + } trace!("SETUP received: {:?}", data); Poll::Ready(data) From 7d34f4f538f88d84d499173e15f63e676d4af9c0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 02:37:02 +0200 Subject: [PATCH 0513/1575] stm32/usb_otg: Add F4 usb_ethernet example --- examples/stm32f4/Cargo.toml | 6 +- examples/stm32f4/src/bin/usb_ethernet.rs | 169 +++++++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f4/src/bin/usb_ethernet.rs diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 252d60855..e2b17bfcb 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -4,13 +4,13 @@ name = "embassy-stm32f4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" - [dependencies] 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", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -27,5 +27,9 @@ embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" +[[bin]] +name = "usb_ethernet" +required-features = ["embassy-net"] + [profile.release] debug = 2 diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..cf2885ae5 --- /dev/null +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -0,0 +1,169 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, StackResources}; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::mhz; +use embassy_stm32::usb_otg::Driver; +use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; +use embassy_usb::{Builder, UsbDevice}; +use embedded_io::asynch::Write; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +type UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +const MTU: usize = 1514; + +#[embassy_executor::task] +async fn usb_task(mut device: UsbDevice<'static, UsbDriver>) -> ! { + device.run().await +} + +#[embassy_executor::task] +async fn usb_ncm_task(class: Runner<'static, UsbDriver, MTU>) -> ! { + class.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.pll48 = true; + config.rcc.sys_ck = Some(mhz(48)); + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let irq = interrupt::take!(OTG_FS); + let ep_out_buffer = &mut singleton!([0; 256])[..]; + let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, ep_out_buffer); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-Ethernet example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for Windows support. + config.composite_with_iads = true; + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + + // Create embassy-usb DeviceBuilder using the driver and config. + let mut builder = Builder::new( + driver, + config, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], + None, + ); + + // Our MAC addr. + let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; + // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. + let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; + + // Create classes on the builder. + let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); + + // Build the builder. + let usb = builder.build(); + + unwrap!(spawner.spawn(usb_task(usb))); + + let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + unwrap!(spawner.spawn(usb_ncm_task(runner))); + + let config = embassy_net::ConfigStrategy::Dhcp; + //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Generate random seed + let mut rng = Rng::new(p.RNG); + let mut seed = [0; 8]; + unwrap!(rng.async_fill_bytes(&mut seed).await); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + config, + singleton!(StackResources::<1, 2, 8>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} From b1203bf036454bf28903fc079086e3cc93a18446 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Wed, 18 Jan 2023 03:06:32 +0200 Subject: [PATCH 0514/1575] stm32/usb_otg: fix core formatter --- embassy-stm32/src/usb_otg/usb.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 48c3ec019..fd2f67073 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -1126,7 +1126,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { - trace!("write ep={} data={}", self.info.addr, buf); + trace!("write ep={:?} data={:?}", self.info.addr, buf); if buf.len() > self.info.max_packet_size as usize { return Err(EndpointError::BufferOverflow); @@ -1206,7 +1206,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; } - trace!("write done ep={}", self.info.addr); + trace!("write done ep={:?}", self.info.addr); Ok(()) } From 6ab4ecaf8379820984242a4bfb6ff4421c01b11e Mon Sep 17 00:00:00 2001 From: huntc Date: Wed, 18 Jan 2023 14:17:50 +1100 Subject: [PATCH 0515/1575] Stop sampling when exiting the Saadc methods Prior to this commit, the onDrop function was being dropped immediately and not on exiting the Saadc sampling methods. --- embassy-nrf/src/saadc.rs | 12 +++++++++--- embassy-stm32/src/i2c/v2.rs | 10 ++++++++-- embassy-stm32/src/usart/mod.rs | 10 +++++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index d1c82423e..4592d4687 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -225,7 +225,7 @@ impl<'d, const N: usize> Saadc<'d, N> { /// also cause the sampling to be stopped. pub async fn sample(&mut self, buf: &mut [i16; N]) { // In case the future is dropped, stop the task and wait for it to end. - OnDrop::new(Self::stop_sampling_immediately); + let on_drop = OnDrop::new(Self::stop_sampling_immediately); let r = Self::regs(); @@ -258,6 +258,8 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; + + drop(on_drop); } /// Continuous sampling with double buffers. @@ -335,7 +337,7 @@ impl<'d, const N: usize> Saadc<'d, N> { S: FnMut(&[[i16; N]]) -> SamplerState, { // In case the future is dropped, stop the task and wait for it to end. - OnDrop::new(Self::stop_sampling_immediately); + let on_drop = OnDrop::new(Self::stop_sampling_immediately); let r = Self::regs(); @@ -382,7 +384,7 @@ impl<'d, const N: usize> Saadc<'d, N> { let mut current_buffer = 0; // Wait for events and complete when the sampler indicates it has had enough. - poll_fn(|cx| { + let r = poll_fn(|cx| { let r = Self::regs(); WAKER.register(cx.waker()); @@ -419,6 +421,10 @@ impl<'d, const N: usize> Saadc<'d, N> { Poll::Pending }) .await; + + drop(on_drop); + + r } // Stop sampling and wait for it to stop in a blocking fashion diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4622635bf..06ff07b21 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -483,7 +483,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; - let _on_drop = OnDrop::new(|| { + let on_drop = OnDrop::new(|| { let regs = T::regs(); unsafe { regs.cr1().modify(|w| { @@ -542,6 +542,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.wait_tc(&check_timeout)?; self.master_stop(); } + + drop(on_drop); + Ok(()) } @@ -580,7 +583,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; - let _on_drop = OnDrop::new(|| { + let on_drop = OnDrop::new(|| { let regs = T::regs(); unsafe { regs.cr1().modify(|w| { @@ -629,6 +632,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // This should be done already self.wait_tc(&check_timeout)?; self.master_stop(); + + drop(on_drop); + Ok(()) } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index d71aa61a4..20f4eedeb 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -405,7 +405,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let r = T::regs(); // make sure USART state is restored to neutral state when this future is dropped - let _drop = OnDrop::new(move || { + let on_drop = OnDrop::new(move || { // defmt::trace!("Clear all USART interrupts and DMA Read Request"); // clear all interrupts and DMA Rx Request // SAFETY: only clears Rx related flags @@ -563,7 +563,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // wait for the first of DMA request or idle line detected to completes // select consumes its arguments // when transfer is dropped, it will stop the DMA request - match select(transfer, idle).await { + let r = match select(transfer, idle).await { // DMA transfer completed first Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), @@ -572,7 +572,11 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // error occurred Either::Second(Err(e)) => Err(e), - } + }; + + drop(on_drop); + + r } async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result From 83af513424b3fcff98ca7bc12cc4e24261eb08e7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 19 Jan 2023 13:29:51 +0800 Subject: [PATCH 0516/1575] rp gpio: make pin_bank() inline This allows set_high() etc to be inlined, toggling pins should be much faster. --- embassy-rp/src/gpio.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ec05de611..4abb98394 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -743,6 +743,7 @@ macro_rules! impl_pin { ($name:ident, $bank:expr, $pin_num:expr) => { impl Pin for peripherals::$name {} impl sealed::Pin for peripherals::$name { + #[inline] fn pin_bank(&self) -> u8 { ($bank as u8) * 32 + $pin_num } From 2eae12b7f1cbe38d850ebf30f23a776323815af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Wed, 18 Jan 2023 09:56:38 +0100 Subject: [PATCH 0517/1575] Update smoltcp to the newest master --- embassy-net/Cargo.toml | 2 +- embassy-net/src/device.rs | 13 +++++++------ embassy-net/src/lib.rs | 30 ++++++++++-------------------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 9214fd17e..a2ef3b4fe 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -51,7 +51,7 @@ atomic-polyfill = { version = "1.0" } [dependencies.smoltcp] version = "0.8.0" git = "https://github.com/smoltcp-rs/smoltcp" -rev = "b7a7c4b1c56e8d4c2524c1e3a056c745a13cc09f" +rev = "7b631c27cefe9b29e322a507c076b4156f477ee2" default-features = false features = [ "proto-ipv4", diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 44f7dc7bd..d0c8a62db 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -2,6 +2,7 @@ use core::task::Context; use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken}; use smoltcp::phy; +use smoltcp::time::Instant; pub(crate) struct DriverAdapter<'d, 'c, T> where @@ -19,14 +20,14 @@ where type RxToken<'a> = RxTokenAdapter> where Self: 'a; type TxToken<'a> = TxTokenAdapter> where Self: 'a; - fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { self.inner .receive(self.cx.as_deref_mut().unwrap()) .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) } /// Construct a transmit token. - fn transmit(&mut self) -> Option> { + fn transmit(&mut self, _timestamp: Instant) -> Option> { self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) } @@ -76,9 +77,9 @@ impl phy::RxToken for RxTokenAdapter where T: RxToken, { - fn consume(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result + fn consume(self, f: F) -> R where - F: FnOnce(&mut [u8]) -> smoltcp::Result, + F: FnOnce(&mut [u8]) -> R, { self.0.consume(|buf| f(buf)) } @@ -92,9 +93,9 @@ impl phy::TxToken for TxTokenAdapter where T: TxToken, { - fn consume(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result + fn consume(self, len: usize, f: F) -> R where - F: FnOnce(&mut [u8]) -> smoltcp::Result, + F: FnOnce(&mut [u8]) -> R, { self.0.consume(len, |buf| f(buf)) } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index e4a4218e3..757d3e27d 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -23,11 +23,11 @@ use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; +#[cfg(feature = "medium-ethernet")] +use smoltcp::iface::Routes; #[cfg(feature = "dhcpv4")] use smoltcp::iface::SocketHandle; use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; // smoltcp reexports @@ -45,25 +45,16 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; -pub struct StackResources { - addresses: [IpCidr; ADDR], +pub struct StackResources { + addresses: [IpCidr; 5], sockets: [SocketStorage<'static>; SOCK], - - #[cfg(feature = "medium-ethernet")] - routes: [Option<(IpCidr, Route)>; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR], } -impl StackResources { +impl StackResources { pub fn new() -> Self { Self { - addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR], + addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); 5], sockets: [SocketStorage::EMPTY; SOCK], - #[cfg(feature = "medium-ethernet")] - routes: [None; 1], - #[cfg(feature = "medium-ethernet")] - neighbor_cache: [None; NEIGHBOR], } } } @@ -105,21 +96,20 @@ impl Stack { pub fn new( mut device: D, config: ConfigStrategy, - resources: &'static mut StackResources, + resources: &'static mut StackResources, random_seed: u64, ) -> Self { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; let mut b = InterfaceBuilder::new(); - b = b.ip_addrs(&mut resources.addresses[..]); + b = b.ip_addrs(Vec::::from_iter(resources.addresses)); b = b.random_seed(random_seed); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); - b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); - b = b.routes(Routes::new(&mut resources.routes[..])); + b = b.routes(Routes::new()); } let iface = b.finalize(&mut DriverAdapter { @@ -266,7 +256,7 @@ impl Inner { cx: Some(cx), inner: &mut self.device, }; - if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() { + if !s.iface.poll(timestamp, &mut smoldev, &mut s.sockets) { // If poll() returns error, it may not be done yet, so poll again later. cx.waker().wake_by_ref(); return; From 8f4fae9b36f017a8ab65491ef49b72499a9486dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Wed, 18 Jan 2023 10:10:33 +0100 Subject: [PATCH 0518/1575] Add smoltcp dhcp socket configuration --- embassy-net/src/lib.rs | 72 ++++++++++++++++------- examples/nrf52840/src/bin/usb_ethernet.rs | 11 +--- examples/rp/src/bin/usb_ethernet.rs | 11 +--- examples/std/Cargo.toml | 2 +- examples/std/src/bin/net.rs | 15 ++--- examples/std/src/bin/net_udp.rs | 13 ++-- examples/stm32f7/src/bin/eth.rs | 11 +--- examples/stm32h7/src/bin/eth.rs | 11 +--- examples/stm32h7/src/bin/eth_client.rs | 11 +--- examples/stm32l5/src/bin/usb_ethernet.rs | 11 +--- 10 files changed, 81 insertions(+), 87 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 757d3e27d..c419ec1f9 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -30,6 +30,8 @@ use smoltcp::iface::SocketHandle; use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; +use smoltcp::socket::dhcpv4::RetryConfig; +use smoltcp::time::Duration; // smoltcp reexports pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; #[cfg(feature = "medium-ethernet")] @@ -45,31 +47,53 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; -pub struct StackResources { - addresses: [IpCidr; 5], +pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], } -impl StackResources { +impl StackResources { pub fn new() -> Self { Self { - addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); 5], sockets: [SocketStorage::EMPTY; SOCK], } } } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Config { +pub struct StaticConfig { pub address: Ipv4Cidr, pub gateway: Option, pub dns_servers: Vec, } -pub enum ConfigStrategy { - Static(Config), +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DhcpConfig { + pub max_lease_duration: Option, + pub retry_config: RetryConfig, + /// Ignore NAKs. + pub ignore_naks: bool, + /// Server port config + pub server_port: u16, + /// Client port config + pub client_port: u16, +} + +impl Default for DhcpConfig { + fn default() -> Self { + Self { + max_lease_duration: Default::default(), + retry_config: Default::default(), + ignore_naks: Default::default(), + server_port: smoltcp::wire::DHCP_SERVER_PORT, + client_port: smoltcp::wire::DHCP_CLIENT_PORT, + } + } +} + +pub enum Config { + Static(StaticConfig), #[cfg(feature = "dhcpv4")] - Dhcp, + Dhcp(DhcpConfig), } pub struct Stack { @@ -80,7 +104,7 @@ pub struct Stack { struct Inner { device: D, link_up: bool, - config: Option, + config: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, } @@ -93,17 +117,16 @@ pub(crate) struct SocketStack { } impl Stack { - pub fn new( + pub fn new( mut device: D, - config: ConfigStrategy, - resources: &'static mut StackResources, + config: Config, + resources: &'static mut StackResources, random_seed: u64, ) -> Self { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; let mut b = InterfaceBuilder::new(); - b = b.ip_addrs(Vec::::from_iter(resources.addresses)); b = b.random_seed(random_seed); #[cfg(feature = "medium-ethernet")] @@ -136,10 +159,12 @@ impl Stack { }; match config { - ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), + Config::Static(config) => inner.apply_config(&mut socket, config), #[cfg(feature = "dhcpv4")] - ConfigStrategy::Dhcp => { - let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); + Config::Dhcp(config) => { + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + inner.apply_dhcp_config(&mut dhcp_socket, config); + let handle = socket.sockets.add(dhcp_socket); inner.dhcp_socket = Some(handle); } } @@ -170,7 +195,7 @@ impl Stack { self.with(|_s, i| i.config.is_some()) } - pub fn config(&self) -> Option { + pub fn config(&self) -> Option { self.with(|_s, i| i.config.clone()) } @@ -185,7 +210,7 @@ impl Stack { } impl SocketStack { - #[allow(clippy::absurd_extreme_comparisons)] + #[allow(clippy::absurd_extreme_comparisons, dead_code)] pub fn get_local_port(&mut self) -> u16 { let res = self.next_local_port; self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; @@ -194,7 +219,7 @@ impl SocketStack { } impl Inner { - fn apply_config(&mut self, s: &mut SocketStack, config: Config) { + fn apply_config(&mut self, s: &mut SocketStack, config: StaticConfig) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -220,6 +245,13 @@ impl Inner { self.config = Some(config) } + fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { + socket.set_ignore_naks(config.ignore_naks); + socket.set_max_lease_duration(config.max_lease_duration); + socket.set_ports(config.server_port, config.client_port); + socket.set_retry_config(config.retry_config); + } + #[allow(unused)] // used only with dhcp fn unapply_config(&mut self, s: &mut SocketStack) { #[cfg(feature = "medium-ethernet")] @@ -280,7 +312,7 @@ impl Inner { None => {} Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), Some(dhcpv4::Event::Configured(config)) => { - let config = Config { + let config = StaticConfig { address: config.address, gateway: config.router, dns_servers: config.dns_servers, diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index e5f704524..1390bfc8f 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -101,8 +101,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -115,12 +115,7 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index d0aec874a..e9b727127 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -92,8 +92,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -103,12 +103,7 @@ async fn main(spawner: Spawner) { let seed = 1234; // guaranteed random, chosen by a fair dice roll // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 45b2a4a4f..af1481e08 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -17,7 +17,7 @@ async-io = "1.6.0" env_logger = "0.9.0" futures = { version = "0.3.17" } log = "0.4.14" -nix = "0.22.1" +nix = "0.26.2" libc = "0.2.101" clap = { version = "3.0.0-beta.5", features = ["derive"] } rand_core = { version = "0.6.3", features = ["std"] } diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 9b1450b72..1ae39cace 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -1,9 +1,11 @@ #![feature(type_alias_impl_trait)] +use std::default::Default; + use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; -use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use embedded_io::asynch::Write; use heapless::Vec; use log::*; @@ -48,13 +50,13 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - ConfigStrategy::Static(embassy_net::Config { + Config::Static(embassy_net::StaticConfig { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }) } else { - ConfigStrategy::Dhcp + Config::Dhcp(Default::default()) }; // Generate random seed @@ -63,12 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 392a97f0d..d1a8fe1e0 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -3,7 +3,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::udp::UdpSocket; -use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; @@ -47,13 +47,13 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - ConfigStrategy::Static(embassy_net::Config { + Config::Static(embassy_net::StaticConfig { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }) } else { - ConfigStrategy::Dhcp + Config::Dhcp(Default::default()) }; // Generate random seed @@ -62,12 +62,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 224cc202b..6f33a4f8b 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -69,20 +69,15 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), //}); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 551325ca4..ad7fcc5c0 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -70,20 +70,15 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), //}); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 61a08ae10..7d3904f68 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -71,20 +71,15 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::StaticConfig(embassy_net::Config { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), //}); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index b49329ea4..ff44c2fcb 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -98,8 +98,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -110,12 +110,7 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); From 570ffab6703083f4e7f08a982afbf1c6de93b801 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 Jan 2023 13:30:51 +0100 Subject: [PATCH 0519/1575] net: poll returning false is not an error. No need to repoll. --- embassy-net/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index c419ec1f9..b8e766b8b 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -288,11 +288,7 @@ impl Inner { cx: Some(cx), inner: &mut self.device, }; - if !s.iface.poll(timestamp, &mut smoldev, &mut s.sockets) { - // If poll() returns error, it may not be done yet, so poll again later. - cx.waker().wake_by_ref(); - return; - } + s.iface.poll(timestamp, &mut smoldev, &mut s.sockets); // Update link up let old_link_up = self.link_up; From fe15a7beee5f948b1e4c1cb8ab8e5cc85efb4662 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 Jan 2023 14:40:58 +0100 Subject: [PATCH 0520/1575] net: allocate space for 2 sockets, needed for dhcp. --- examples/nrf52840/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/std/src/bin/net.rs | 2 +- examples/std/src/bin/net_udp.rs | 2 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 1390bfc8f..a8d53e460 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -115,7 +115,7 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index e9b727127..104b25d39 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -103,7 +103,7 @@ async fn main(spawner: Spawner) { let seed = 1234; // guaranteed random, chosen by a fair dice roll // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 1ae39cace..451850d99 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -65,7 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index d1a8fe1e0..f1923f180 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -62,7 +62,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 6f33a4f8b..571a6c1b9 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -77,7 +77,7 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index ad7fcc5c0..cb245c325 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -78,7 +78,7 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 7d3904f68..cce85a083 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -79,7 +79,7 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index ff44c2fcb..e5a46b064 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -110,7 +110,7 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<1>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); From 78c2c1709b8c111468fd7dd2ec1f3792d2fcea67 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 19 Jan 2023 14:41:39 +0100 Subject: [PATCH 0521/1575] net: update smoltcp. --- embassy-net/Cargo.toml | 2 +- embassy-net/src/lib.rs | 42 ++++++++++++++++++++---------------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index a2ef3b4fe..15cbb5954 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -51,7 +51,7 @@ atomic-polyfill = { version = "1.0" } [dependencies.smoltcp] version = "0.8.0" git = "https://github.com/smoltcp-rs/smoltcp" -rev = "7b631c27cefe9b29e322a507c076b4156f477ee2" +rev = "5740b765749b95c18aace5de8dc21cab75ba33d4" default-features = false features = [ "proto-ipv4", diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b8e766b8b..8d0119f67 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -23,11 +23,9 @@ use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; -#[cfg(feature = "medium-ethernet")] -use smoltcp::iface::Routes; #[cfg(feature = "dhcpv4")] use smoltcp::iface::SocketHandle; -use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; +use smoltcp::iface::{Interface, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; use smoltcp::socket::dhcpv4::RetryConfig; @@ -111,7 +109,7 @@ struct Inner { pub(crate) struct SocketStack { pub(crate) sockets: SocketSet<'static>, - pub(crate) iface: Interface<'static>, + pub(crate) iface: Interface, pub(crate) waker: WakerRegistration, next_local_port: u16, } @@ -126,19 +124,20 @@ impl Stack { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; - let mut b = InterfaceBuilder::new(); - b = b.random_seed(random_seed); - + let mut iface_cfg = smoltcp::iface::Config::new(); + iface_cfg.random_seed = random_seed; #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { - b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); - b = b.routes(Routes::new()); + iface_cfg.hardware_addr = Some(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); } - let iface = b.finalize(&mut DriverAdapter { - inner: &mut device, - cx: None, - }); + let iface = Interface::new( + iface_cfg, + &mut DriverAdapter { + inner: &mut device, + cx: None, + }, + ); let sockets = SocketSet::new(&mut resources.sockets[..]); @@ -226,7 +225,13 @@ impl Inner { debug!("Acquired IP configuration:"); debug!(" IP address: {}", config.address); - self.set_ipv4_addr(s, config.address); + s.iface.update_ip_addrs(|addrs| { + if addrs.is_empty() { + addrs.push(IpCidr::Ipv4(config.address)).unwrap(); + } else { + addrs[0] = IpCidr::Ipv4(config.address); + } + }); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { @@ -258,7 +263,7 @@ impl Inner { let medium = self.device.capabilities().medium; debug!("Lost IP configuration"); - self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear()); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { s.iface.routes_mut().remove_default_ipv4_route(); @@ -266,13 +271,6 @@ impl Inner { self.config = None } - fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { - s.iface.update_ip_addrs(|addrs| { - let dest = addrs.iter_mut().next().unwrap(); - *dest = IpCidr::Ipv4(cidr); - }); - } - fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { s.waker.register(cx.waker()); From f604153f05e02fb07903f45b756fc9ddb6667d8a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 20 Jan 2023 16:31:04 +0100 Subject: [PATCH 0522/1575] stm32/rcc: print actual freqs on boot. --- embassy-stm32/src/rcc/mod.rs | 4 +++- embassy-stm32/src/time.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 0a52089d1..d4bd3d6b8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -24,7 +24,8 @@ use crate::time::Hertz; mod _version; pub use _version::*; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Clocks { pub sys: Hertz, @@ -72,6 +73,7 @@ static mut CLOCK_FREQS: MaybeUninit = MaybeUninit::uninit(); /// /// Safety: Sets a mutable global. pub(crate) unsafe fn set_freqs(freqs: Clocks) { + debug!("rcc: {:?}", freqs); CLOCK_FREQS.as_mut_ptr().write(freqs); } diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 49140bbe0..975517a48 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs @@ -2,6 +2,7 @@ /// Hertz #[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Hertz(pub u32); impl Hertz { From 0412d1922c6cca1200da0af54db3a5dbf60c035c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 21 Jan 2023 00:24:07 +0100 Subject: [PATCH 0523/1575] fix embedded-sdmmc integration. - Rename feature to `embedded-sdmmc`. - Move embedded-sdmmc fork repo to the embassy-rs org. - Remove unused features in the fork - Fix impl in embassy-stm32 - Add to CI so it doesn't break again. --- ci.sh | 2 +- embassy-stm32/Cargo.toml | 3 +-- embassy-stm32/src/sdmmc/mod.rs | 22 ++++++---------------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/ci.sh b/ci.sh index 323e752d2..b325e3764 100755 --- a/ci.sh +++ b/ci.sh @@ -63,7 +63,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ - --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d9c1f6dcf..10e5e48c0 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ cortex-m = "0.7.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand_core = "0.6.3" sdio-host = "0.5.0" -embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } +embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } @@ -77,7 +77,6 @@ stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features [features] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] -sdmmc-rs = ["embedded-sdmmc"] memory-x = ["stm32-metapac/memory-x"] subghz = [] exti = [] diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index a52c65b92..3f07e0c07 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1532,7 +1532,7 @@ foreach_peripheral!( }; ); -#[cfg(feature = "sdmmc-rs")] +#[cfg(feature = "embedded-sdmmc")] mod sdmmc_rs { use core::future::Future; @@ -1540,7 +1540,7 @@ mod sdmmc_rs { use super::*; - impl<'d, T: Instance, P: Pins> BlockDevice for Sdmmc<'d, T, P> { + impl<'d, T: Instance, Dma: SdmmcDma> BlockDevice for Sdmmc<'d, T, Dma> { type Error = Error; type ReadFuture<'a> = impl Future> + 'a @@ -1558,19 +1558,14 @@ mod sdmmc_rs { _reason: &str, ) -> Self::ReadFuture<'a> { async move { - let card_capacity = self.card()?.card_type; - let inner = T::inner(); - let state = T::state(); let mut address = start_block_idx.0; for block in blocks.iter_mut() { let block: &mut [u8; 512] = &mut block.contents; // NOTE(unsafe) Block uses align(4) - let buf = unsafe { &mut *(block as *mut [u8; 512] as *mut [u32; 128]) }; - inner - .read_block(address, buf, card_capacity, state, self.config.data_transfer_timeout) - .await?; + let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) }; + self.read_block(address, block).await?; address += 1; } Ok(()) @@ -1579,19 +1574,14 @@ mod sdmmc_rs { fn write<'a>(&'a mut self, blocks: &'a [Block], start_block_idx: BlockIdx) -> Self::WriteFuture<'a> { async move { - let card = self.card.as_mut().ok_or(Error::NoCard)?; - let inner = T::inner(); - let state = T::state(); let mut address = start_block_idx.0; for block in blocks.iter() { let block: &[u8; 512] = &block.contents; // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &*(block as *const [u8; 512] as *const [u32; 128]) }; - inner - .write_block(address, buf, card, state, self.config.data_transfer_timeout) - .await?; + let block = unsafe { &*(block as *const _ as *const DataBlock) }; + self.write_block(address, block).await?; address += 1; } Ok(()) From 5e3c33b77766df94f6021457c723ad52d97684b7 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Sat, 21 Jan 2023 14:39:25 +0100 Subject: [PATCH 0524/1575] Fix rcc prescaler for wb55 HCLK1 - fix prescaler not divided which incorrectly set prescaler divided by 3 --- embassy-stm32/src/rcc/wb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index f6a5feea6..c9ada83e2 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -64,7 +64,7 @@ impl Into for APBPrescaler { impl Into for AHBPrescaler { fn into(self) -> u8 { match self { - AHBPrescaler::NotDivided => 1, + AHBPrescaler::NotDivided => 0x0, AHBPrescaler::Div2 => 0x08, AHBPrescaler::Div3 => 0x01, AHBPrescaler::Div4 => 0x09, From 64e610fef708b7078e9690049583e05e16c6a3bd Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Mon, 23 Jan 2023 13:35:24 +0100 Subject: [PATCH 0525/1575] Replace `Level: Into` with `From` This automatically implements Into for Level --- embassy-nrf/src/gpio.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index b7230302a..6ef5033eb 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -88,9 +88,9 @@ impl From for Level { } } -impl Into for Level { - fn into(self) -> bool { - match self { +impl From for bool { + fn from(level: Level) -> bool { + match level { Level::Low => false, Level::High => true, } From f38d54a6a6a7fcfb836ea4606f719b2fed877250 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 24 Jan 2023 08:15:22 +0000 Subject: [PATCH 0526/1575] IPv6 has no checksum --- embassy-net/src/device.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index d0c8a62db..650af23b8 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -57,10 +57,6 @@ where ), }; smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); - #[cfg(feature = "proto-ipv6")] - { - smolcaps.checksum.ipv6 = convert(caps.checksum.ipv6); - } smolcaps.checksum.tcp = convert(caps.checksum.tcp); smolcaps.checksum.udp = convert(caps.checksum.udp); smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); From 32bdc54ccba9291b6aafc3c2695a8368d8137bb4 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 24 Jan 2023 08:27:53 +0000 Subject: [PATCH 0527/1575] Changed crates' names for nrf examples since they were conflicting --- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 994823a9e..bbd8a5d2f 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-nrf-examples" +name = "embassy-nrf52840-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 03485711e..eed493012 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-nrf-examples" +name = "embassy-nrf5340-examples" version = "0.1.0" license = "MIT OR Apache-2.0" From 2a0ea52878ece15fa12c9d70da32b5809aa1e713 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 24 Jan 2023 09:57:26 +0100 Subject: [PATCH 0528/1575] add missing copy of icmpv6 checksum add proto-ipv6 feature to stm32h7 example to catch issues in CI --- embassy-net/src/device.rs | 4 ++++ examples/stm32h7/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 650af23b8..5daa00544 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -60,6 +60,10 @@ where smolcaps.checksum.tcp = convert(caps.checksum.tcp); smolcaps.checksum.udp = convert(caps.checksum.udp); smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + #[cfg(feature = "proto-ipv6")] + { + smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6); + } smolcaps } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index ff38440a7..bcf976416 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de 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", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } From 34b67fe1372c535a659590744242cd4ffd52dfb6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Thu, 26 Jan 2023 20:14:53 +0000 Subject: [PATCH 0529/1575] STD driver needs a reentrant mutex; logic fixed to be reentrancy-safe --- embassy-time/src/driver_std.rs | 67 +++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index fc7fd1979..da46a599d 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -1,10 +1,12 @@ -use std::cell::UnsafeCell; +use std::cell::{RefCell, UnsafeCell}; use std::mem::MaybeUninit; use std::sync::{Condvar, Mutex, Once}; use std::time::{Duration as StdDuration, Instant as StdInstant}; use std::{mem, ptr, thread}; use atomic_polyfill::{AtomicU8, Ordering}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex as EmbassyMutex; use crate::driver::{AlarmHandle, Driver}; @@ -35,7 +37,10 @@ struct TimeDriver { alarm_count: AtomicU8, once: Once, - alarms: UninitCell>, + // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't + // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections + // themselves are reentrant + alarms: UninitCell>>, zero_instant: UninitCell, signaler: UninitCell, } @@ -53,7 +58,8 @@ crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { impl TimeDriver { fn init(&self) { self.once.call_once(|| unsafe { - self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); + self.alarms + .write(EmbassyMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT]))); self.zero_instant.write(StdInstant::now()); self.signaler.write(Signaler::new()); @@ -66,25 +72,37 @@ impl TimeDriver { loop { let now = DRIVER.now(); - let mut next_alarm = u64::MAX; - { - let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); - for alarm in alarms { - if alarm.timestamp <= now { - alarm.timestamp = u64::MAX; + let next_alarm = unsafe { DRIVER.alarms.as_ref() }.lock(|alarms| { + loop { + let pending = alarms + .borrow_mut() + .iter_mut() + .find(|alarm| alarm.timestamp <= now) + .map(|alarm| { + alarm.timestamp = u64::MAX; - // Call after clearing alarm, so the callback can set another alarm. + (alarm.callback, alarm.ctx) + }); + if let Some((callback, ctx)) = pending { // safety: // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; - f(alarm.ctx); + let f: fn(*mut ()) = unsafe { mem::transmute(callback) }; + f(ctx); } else { - next_alarm = next_alarm.min(alarm.timestamp); + // No alarm due + break; } } - } + + alarms + .borrow() + .iter() + .map(|alarm| alarm.timestamp) + .min() + .unwrap_or(u64::MAX) + }); // Ensure we don't overflow let until = zero @@ -121,18 +139,23 @@ impl Driver for TimeDriver { fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.callback = callback as *const (); - alarm.ctx = ctx; + unsafe { self.alarms.as_ref() }.lock(|alarms| { + let mut alarms = alarms.borrow_mut(); + let alarm = &mut alarms[alarm.id() as usize]; + alarm.callback = callback as *const (); + alarm.ctx = ctx; + }); } fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.timestamp = timestamp; - unsafe { self.signaler.as_ref() }.signal(); + unsafe { self.alarms.as_ref() }.lock(|alarms| { + let mut alarms = alarms.borrow_mut(); + + let alarm = &mut alarms[alarm.id() as usize]; + alarm.timestamp = timestamp; + unsafe { self.signaler.as_ref() }.signal(); + }); true } From 1e60c60afdfc2a4677a3312d7af8d065af6c50ac Mon Sep 17 00:00:00 2001 From: nitroxis Date: Fri, 27 Jan 2023 07:19:34 +0100 Subject: [PATCH 0530/1575] rp: allow isochronous USB endpoints to be up to 1023 in size --- embassy-rp/src/usb.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 2710c4ad9..8eec55b49 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -219,14 +219,16 @@ impl<'d, T: Instance> Driver<'d, T> { let (index, ep) = index.ok_or(EndpointAllocError)?; assert!(!ep.used); - if max_packet_size > 64 { + // as per datasheet, the maximum buffer size is 64, except for isochronous + // endpoints, which are allowed to be up to 1023 bytes. + if (ep_type != EndpointType::Isochronous && max_packet_size > 64) || max_packet_size > 1023 { warn!("max_packet_size too high: {}", max_packet_size); return Err(EndpointAllocError); } // ep mem addrs must be 64-byte aligned, so there's no point in trying // to allocate smaller chunks to save memory. - let len = 64; + let len = (max_packet_size + 63) / 64 * 64; let addr = self.ep_mem_free; if addr + len > EP_MEMORY_SIZE as _ { From c9e2cd6dd41f9281f89ba40cc57dea07b9a224b8 Mon Sep 17 00:00:00 2001 From: nitroxis Date: Fri, 27 Jan 2023 15:49:19 +0100 Subject: [PATCH 0531/1575] usb: allow adding isochronous endpoints --- embassy-usb/src/builder.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 87a8333bb..fc16d2b41 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -401,4 +401,17 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval) } + + /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. + /// + /// Descriptors are written in the order builder functions are called. Note that some + /// classes care about the order. + pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { + self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval) + } + + /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. + pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { + self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval) + } } From e453334870575dd0d18f65dcd207bfbff6e88990 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 27 Jan 2023 15:36:26 +0100 Subject: [PATCH 0532/1575] LoRa/SX1276: adjust Rx window offset and duration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After a transmission, two receive windows Rx1 and Rx2 are opened for one second each, one right after the other, after a fixed delay (for example 5s). The Rx window offset is added to the starting date of each window and the Rx window duration represents the maximum delay we will wait for an incoming message before declaring that a timeout occurred. A value of -500ms for the offset and 800ms for the duration means that instead of having Rx1 = [5000, 6000[ and Rx2 = [6000, 7000[ we get Rx1 = [4500, 5300[ and Rx2 = [5500, 6300[. We only cover 30% of the expected windows. The maximum time a SX127x can take before the Rx side is ready is TS_HOP + TS_RE = 50µs + 2.33ms. Using 3ms for the offset and 1003ms for the duration will give much better time windows: Rx1 = [4997, 5997[ and Rx2 = [5997, 7000]. Note that the lorawan-device crate caps Rx1 end date to Rx2 start date. This change allows a previously failing Murata CMWX1ZZABZ-091 module (STM32L + SX1276) to connect to the TTN LoRa network. --- embassy-lora/src/sx127x/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs index f47a9eb55..8904c9a13 100644 --- a/embassy-lora/src/sx127x/mod.rs +++ b/embassy-lora/src/sx127x/mod.rs @@ -70,10 +70,10 @@ where RFS: RadioSwitch + 'static, { fn get_rx_window_offset_ms(&self) -> i32 { - -500 + -3 } fn get_rx_window_duration_ms(&self) -> u32 { - 800 + 1003 } } From 48e1aab762e902ee0a132602d3c2f9ec0551cd6b Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 29 Jan 2023 12:55:06 -0600 Subject: [PATCH 0533/1575] executor: Replace `NonNull` with `TaskRef` --- embassy-executor/src/raw/mod.rs | 63 ++++++++++++++++++------- embassy-executor/src/raw/run_queue.rs | 13 ++--- embassy-executor/src/raw/timer_queue.rs | 35 ++++++-------- embassy-executor/src/raw/waker.rs | 13 +++-- embassy-executor/src/spawner.rs | 9 ++-- 5 files changed, 76 insertions(+), 57 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 181dabe8e..10a154a9f 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -43,14 +43,11 @@ pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; /// Raw task header for use in task pointers. -/// -/// This is an opaque struct, used for raw pointers to tasks, for use -/// with funtions like [`wake_task`] and [`task_from_waker`]. -pub struct TaskHeader { +pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 - pub(crate) poll_fn: UninitCell)>, // Valid if STATE_SPAWNED + pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 + pub(crate) poll_fn: UninitCell, // Valid if STATE_SPAWNED #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Cell, @@ -59,7 +56,7 @@ pub struct TaskHeader { } impl TaskHeader { - pub(crate) const fn new() -> Self { + const fn new() -> Self { Self { state: AtomicU32::new(0), run_queue_item: RunQueueItem::new(), @@ -74,6 +71,36 @@ impl TaskHeader { } } +/// This is essentially a `&'static TaskStorage` where the type of the future has been erased. +#[derive(Clone, Copy)] +pub struct TaskRef { + ptr: NonNull, +} + +impl TaskRef { + fn new(task: &'static TaskStorage) -> Self { + Self { + ptr: NonNull::from(task).cast(), + } + } + + /// Safety: The pointer must have been obtained with `Task::as_ptr` + pub(crate) unsafe fn from_ptr(ptr: *const TaskHeader) -> Self { + Self { + ptr: NonNull::new_unchecked(ptr as *mut TaskHeader), + } + } + + pub(crate) fn header(self) -> &'static TaskHeader { + unsafe { self.ptr.as_ref() } + } + + /// The returned pointer is valid for the entire TaskStorage. + pub(crate) fn as_ptr(self) -> *const TaskHeader { + self.ptr.as_ptr() + } +} + /// Raw storage in which a task can be spawned. /// /// This struct holds the necessary memory to spawn one task whose future is `F`. @@ -135,14 +162,14 @@ impl TaskStorage { .is_ok() } - unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> NonNull { + unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> TaskRef { // Initialize the task self.raw.poll_fn.write(Self::poll); self.future.write(future()); - NonNull::new_unchecked(self as *const TaskStorage as *const TaskHeader as *mut TaskHeader) + TaskRef::new(self) } - unsafe fn poll(p: NonNull) { + unsafe fn poll(p: TaskRef) { let this = &*(p.as_ptr() as *const TaskStorage); let future = Pin::new_unchecked(this.future.as_mut()); @@ -307,7 +334,7 @@ impl Executor { /// - `task` must be set up to run in this executor. /// - `task` must NOT be already enqueued (in this executor or another one). #[inline(always)] - unsafe fn enqueue(&self, cs: CriticalSection, task: NonNull) { + unsafe fn enqueue(&self, cs: CriticalSection, task: TaskRef) { #[cfg(feature = "rtos-trace")] trace::task_ready_begin(task.as_ptr() as u32); @@ -325,8 +352,8 @@ impl Executor { /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. /// In this case, the task's Future must be Send. This is because this is effectively /// sending the task to the executor thread. - pub(super) unsafe fn spawn(&'static self, task: NonNull) { - task.as_ref().executor.set(self); + pub(super) unsafe fn spawn(&'static self, task: TaskRef) { + task.header().executor.set(self); #[cfg(feature = "rtos-trace")] trace::task_new(task.as_ptr() as u32); @@ -359,7 +386,7 @@ impl Executor { self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); self.run_queue.dequeue_all(|p| { - let task = p.as_ref(); + let task = p.header(); #[cfg(feature = "integrated-timers")] task.expires_at.set(Instant::MAX); @@ -378,7 +405,7 @@ impl Executor { trace::task_exec_begin(p.as_ptr() as u32); // Run the task - task.poll_fn.read()(p as _); + task.poll_fn.read()(p); #[cfg(feature = "rtos-trace")] trace::task_exec_end(); @@ -424,9 +451,9 @@ impl Executor { /// # Safety /// /// `task` must be a valid task pointer obtained from [`task_from_waker`]. -pub unsafe fn wake_task(task: NonNull) { +pub unsafe fn wake_task(task: TaskRef) { critical_section::with(|cs| { - let header = task.as_ref(); + let header = task.header(); let state = header.state.load(Ordering::Relaxed); // If already scheduled, or if not started, @@ -450,7 +477,7 @@ struct TimerQueue; impl embassy_time::queue::TimerQueue for TimerQueue { fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); - let task = unsafe { task.as_ref() }; + let task = task.header(); let expires_at = task.expires_at.get(); task.expires_at.set(expires_at.min(at)); } diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index ed8c82a5c..362157535 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -4,7 +4,7 @@ use core::ptr::NonNull; use atomic_polyfill::{AtomicPtr, Ordering}; use critical_section::CriticalSection; -use super::TaskHeader; +use super::{TaskHeader, TaskRef}; pub(crate) struct RunQueueItem { next: AtomicPtr, @@ -46,25 +46,26 @@ impl RunQueue { /// /// `item` must NOT be already enqueued in any queue. #[inline(always)] - pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: NonNull) -> bool { + pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: TaskRef) -> bool { let prev = self.head.load(Ordering::Relaxed); - task.as_ref().run_queue_item.next.store(prev, Ordering::Relaxed); - self.head.store(task.as_ptr(), Ordering::Relaxed); + task.header().run_queue_item.next.store(prev, Ordering::Relaxed); + self.head.store(task.as_ptr() as _, Ordering::Relaxed); prev.is_null() } /// Empty the queue, then call `on_task` for each task that was in the queue. /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. - pub(crate) fn dequeue_all(&self, on_task: impl Fn(NonNull)) { + pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { // Atomically empty the queue. let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); // Iterate the linked list of tasks that were previously in the queue. while let Some(task) = NonNull::new(ptr) { + let task = unsafe { TaskRef::from_ptr(task.as_ptr()) }; // If the task re-enqueues itself, the `next` pointer will get overwritten. // Therefore, first read the next pointer, and only then process the task. - let next = unsafe { task.as_ref() }.run_queue_item.next.load(Ordering::Relaxed); + let next = task.header().run_queue_item.next.load(Ordering::Relaxed); on_task(task); diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 24c31892a..57d6d3cda 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,45 +1,39 @@ use core::cell::Cell; use core::cmp::min; -use core::ptr; -use core::ptr::NonNull; use atomic_polyfill::Ordering; use embassy_time::Instant; -use super::{TaskHeader, STATE_TIMER_QUEUED}; +use super::{TaskRef, STATE_TIMER_QUEUED}; pub(crate) struct TimerQueueItem { - next: Cell<*mut TaskHeader>, + next: Cell>, } impl TimerQueueItem { pub const fn new() -> Self { - Self { - next: Cell::new(ptr::null_mut()), - } + Self { next: Cell::new(None) } } } pub(crate) struct TimerQueue { - head: Cell<*mut TaskHeader>, + head: Cell>, } impl TimerQueue { pub const fn new() -> Self { - Self { - head: Cell::new(ptr::null_mut()), - } + Self { head: Cell::new(None) } } - pub(crate) unsafe fn update(&self, p: NonNull) { - let task = p.as_ref(); + pub(crate) unsafe fn update(&self, p: TaskRef) { + let task = p.header(); if task.expires_at.get() != Instant::MAX { let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel); let is_new = old_state & STATE_TIMER_QUEUED == 0; if is_new { task.timer_queue_item.next.set(self.head.get()); - self.head.set(p.as_ptr()); + self.head.set(Some(p)); } } } @@ -47,7 +41,7 @@ impl TimerQueue { pub(crate) unsafe fn next_expiration(&self) -> Instant { let mut res = Instant::MAX; self.retain(|p| { - let task = p.as_ref(); + let task = p.header(); let expires = task.expires_at.get(); res = min(res, expires); expires != Instant::MAX @@ -55,9 +49,9 @@ impl TimerQueue { res } - pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(NonNull)) { + pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(TaskRef)) { self.retain(|p| { - let task = p.as_ref(); + let task = p.header(); if task.expires_at.get() <= now { on_task(p); false @@ -67,11 +61,10 @@ impl TimerQueue { }); } - pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull) -> bool) { + pub(crate) unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { let mut prev = &self.head; - while !prev.get().is_null() { - let p = NonNull::new_unchecked(prev.get()); - let task = &*p.as_ptr(); + while let Some(p) = prev.get() { + let task = p.header(); if f(p) { // Skip to next prev = &task.timer_queue_item.next; diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index 5765259f2..400b37fa9 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -1,8 +1,7 @@ use core::mem; -use core::ptr::NonNull; use core::task::{RawWaker, RawWakerVTable, Waker}; -use super::{wake_task, TaskHeader}; +use super::{wake_task, TaskHeader, TaskRef}; const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); @@ -11,14 +10,14 @@ unsafe fn clone(p: *const ()) -> RawWaker { } unsafe fn wake(p: *const ()) { - wake_task(NonNull::new_unchecked(p as *mut TaskHeader)) + wake_task(TaskRef::from_ptr(p as *const TaskHeader)) } unsafe fn drop(_: *const ()) { // nop } -pub(crate) unsafe fn from_task(p: NonNull) -> Waker { +pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE)) } @@ -33,7 +32,7 @@ pub(crate) unsafe fn from_task(p: NonNull) -> Waker { /// # Panics /// /// Panics if the waker is not created by the Embassy executor. -pub fn task_from_waker(waker: &Waker) -> NonNull { +pub fn task_from_waker(waker: &Waker) -> TaskRef { // safety: OK because WakerHack has the same layout as Waker. // This is not really guaranteed because the structs are `repr(Rust)`, it is // indeed the case in the current implementation. @@ -43,8 +42,8 @@ pub fn task_from_waker(waker: &Waker) -> NonNull { panic!("Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor.") } - // safety: we never create a waker with a null data pointer. - unsafe { NonNull::new_unchecked(hack.data as *mut TaskHeader) } + // safety: our wakers are always created with `TaskRef::as_ptr` + unsafe { TaskRef::from_ptr(hack.data as *const TaskHeader) } } struct WakerHack { diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 400d973ff..650ea06cb 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,7 +1,6 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::mem; -use core::ptr::NonNull; use core::task::Poll; use super::raw; @@ -22,12 +21,12 @@ use super::raw; /// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. #[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] pub struct SpawnToken { - raw_task: Option>, + raw_task: Option, phantom: PhantomData<*mut S>, } impl SpawnToken { - pub(crate) unsafe fn new(raw_task: NonNull) -> Self { + pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self { Self { raw_task: Some(raw_task), phantom: PhantomData, @@ -92,7 +91,7 @@ impl Spawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| unsafe { let task = raw::task_from_waker(cx.waker()); - let executor = (*task.as_ptr()).executor.get(); + let executor = task.header().executor.get(); Poll::Ready(Self::new(&*executor)) }) .await @@ -168,7 +167,7 @@ impl SendSpawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| unsafe { let task = raw::task_from_waker(cx.waker()); - let executor = (*task.as_ptr()).executor.get(); + let executor = task.header().executor.get(); Poll::Ready(Self::new(&*executor)) }) .await From b6ca6d699ad529f56901485fed86bee1ececad6d Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 29 Jan 2023 16:32:12 -0600 Subject: [PATCH 0534/1575] Make `wake_task` safe --- embassy-executor/src/raw/mod.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 10a154a9f..183c5e6a2 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -444,14 +444,10 @@ impl Executor { } } -/// Wake a task by raw pointer. +/// Wake a task by `TaskRef`. /// -/// You can obtain task pointers from `Waker`s using [`task_from_waker`]. -/// -/// # Safety -/// -/// `task` must be a valid task pointer obtained from [`task_from_waker`]. -pub unsafe fn wake_task(task: TaskRef) { +/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. +pub fn wake_task(task: TaskRef) { critical_section::with(|cs| { let header = task.header(); let state = header.state.load(Ordering::Relaxed); @@ -465,8 +461,10 @@ pub unsafe fn wake_task(task: TaskRef) { header.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); // We have just marked the task as scheduled, so enqueue it. - let executor = &*header.executor.get(); - executor.enqueue(cs, task); + unsafe { + let executor = &*header.executor.get(); + executor.enqueue(cs, task); + } }) } From 768fe699cfbe10016cd4bb93bb78f9d89e1a8e96 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 31 Jan 2023 19:36:37 +0100 Subject: [PATCH 0535/1575] Pass the correct buffer when creating TcpSocket --- embassy-net/src/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0fbf0c91b..d46bd4dbf 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -389,7 +389,7 @@ pub mod client { fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?; Ok(Self { - socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) }, + socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().1, &mut bufs.as_mut().0) }, state, bufs, }) From ca10fe7135d10084e38038f3cd433da39e505bea Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 31 Jan 2023 22:27:19 +0100 Subject: [PATCH 0536/1575] usb: docs --- embassy-nrf/src/usb.rs | 8 +- embassy-rp/src/usb.rs | 16 +-- embassy-stm32/src/usb/usb.rs | 16 +-- embassy-stm32/src/usb_otg/usb.rs | 16 +-- embassy-usb-driver/README.md | 32 +++++ embassy-usb-driver/src/lib.rs | 116 ++++++++++++------- embassy-usb/README.md | 22 ++++ embassy-usb/src/builder.rs | 24 ++-- embassy-usb/src/class/cdc_acm.rs | 9 ++ embassy-usb/src/class/cdc_ncm/embassy_net.rs | 11 ++ embassy-usb/src/class/cdc_ncm/mod.rs | 51 +++++--- embassy-usb/src/class/hid.rs | 21 ++++ embassy-usb/src/class/mod.rs | 1 + embassy-usb/src/control.rs | 7 ++ embassy-usb/src/descriptor.rs | 4 +- embassy-usb/src/lib.rs | 8 +- embassy-usb/src/types.rs | 2 + 17 files changed, 267 insertions(+), 97 deletions(-) create mode 100644 embassy-usb-driver/README.md create mode 100644 embassy-usb/README.md diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 6be4fec8c..f030b909f 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -235,7 +235,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> &mut self, ep_type: EndpointType, packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { let index = self.alloc_in.allocate(ep_type)?; let ep_addr = EndpointAddress::from_parts(index, Direction::In); @@ -243,7 +243,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> addr: ep_addr, ep_type, max_packet_size: packet_size, - interval, + interval_ms, })) } @@ -251,7 +251,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> &mut self, ep_type: EndpointType, packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { let index = self.alloc_out.allocate(ep_type)?; let ep_addr = EndpointAddress::from_parts(index, Direction::Out); @@ -259,7 +259,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> addr: ep_addr, ep_type, max_packet_size: packet_size, - interval, + interval_ms, })) } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 8eec55b49..2e3708eff 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -194,13 +194,13 @@ impl<'d, T: Instance> Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result, driver::EndpointAllocError> { trace!( - "allocating type={:?} mps={:?} interval={}, dir={:?}", + "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", ep_type, max_packet_size, - interval, + interval_ms, D::dir() ); @@ -281,7 +281,7 @@ impl<'d, T: Instance> Driver<'d, T> { addr: EndpointAddress::from_parts(index, D::dir()), ep_type, max_packet_size, - interval, + interval_ms, }, buf, }) @@ -298,18 +298,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 03e792a21..0355c5f14 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -268,13 +268,13 @@ impl<'d, T: Instance> Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result, driver::EndpointAllocError> { trace!( - "allocating type={:?} mps={:?} interval={}, dir={:?}", + "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", ep_type, max_packet_size, - interval, + interval_ms, D::dir() ); @@ -345,7 +345,7 @@ impl<'d, T: Instance> Driver<'d, T> { addr: EndpointAddress::from_parts(index, D::dir()), ep_type, max_packet_size, - interval, + interval_ms, }, buf, }) @@ -362,18 +362,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index fd2f67073..96c574ca8 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -217,13 +217,13 @@ impl<'d, T: Instance> Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result, EndpointAllocError> { trace!( - "allocating type={:?} mps={:?} interval={}, dir={:?}", + "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", ep_type, max_packet_size, - interval, + interval_ms, D::dir() ); @@ -292,7 +292,7 @@ impl<'d, T: Instance> Driver<'d, T> { addr: EndpointAddress::from_parts(index, D::dir()), ep_type, max_packet_size, - interval, + interval_ms, }, }) } @@ -308,18 +308,18 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval) + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) } fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { diff --git a/embassy-usb-driver/README.md b/embassy-usb-driver/README.md new file mode 100644 index 000000000..012663c3f --- /dev/null +++ b/embassy-usb-driver/README.md @@ -0,0 +1,32 @@ +# embassy-usb-driver + +This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these +traits to add support for using `embassy-usb` for a given chip/platform. + +The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`] +APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used +with the newer `embassy-usb` without needing updates. + +If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate +instead of this one. + +[`embassy-usb`]: https://crates.io/crates/embassy-usb + +## Interoperability + +This crate can run on any executor. + +## Minimum supported Rust version (MSRV) + +This crate requires nightly Rust, due to using "async fn in trait" support. + +## 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-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 64d647351..e8f71a2dc 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![feature(async_fn_in_trait)] #![allow(incomplete_features)] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] /// 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 @@ -95,46 +97,65 @@ impl EndpointAddress { } } +/// Infomation for an endpoint. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointInfo { + /// Endpoint's address. pub addr: EndpointAddress, + /// Endpoint's type. pub ep_type: EndpointType, + /// Max packet size, in bytes. pub max_packet_size: u16, - pub interval: u8, + /// Polling interval, in milliseconds. + pub interval_ms: u8, } -/// Driver for a specific USB peripheral. Implement this to add support for a new hardware -/// platform. +/// Main USB driver trait. +/// +/// Implement this to add support for a new hardware platform. pub trait Driver<'a> { + /// Type of the OUT endpoints for this driver. type EndpointOut: EndpointOut + 'a; + /// Type of the IN endpoints for this driver. type EndpointIn: EndpointIn + 'a; + /// Type of the control pipe for this driver. type ControlPipe: ControlPipe + 'a; + /// Type for bus control for this driver. type Bus: Bus + 'a; - /// Allocates an endpoint and specified endpoint parameters. This method is called by the device - /// and class implementations to allocate endpoints, and can only be called before - /// [`start`](Self::start) is called. + /// Allocates an OUT endpoint. + /// + /// This method is called by the USB stack to allocate endpoints. + /// It can only be called before [`start`](Self::start) is called. /// /// # Arguments /// - /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should - /// attempt to return an endpoint with the specified address. If None, the implementation - /// should return the next available one. + /// * `ep_type` - the endpoint's type. /// * `max_packet_size` - Maximum packet size in bytes. - /// * `interval` - Polling interval parameter for interrupt endpoints. + /// * `interval_ms` - Polling interval parameter for interrupt endpoints. fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result; + /// Allocates an IN endpoint. + /// + /// This method is called by the USB stack to allocate endpoints. + /// It can only be called before [`start`](Self::start) is called. + /// + /// # Arguments + /// + /// * `ep_type` - the endpoint's type. + /// * `max_packet_size` - Maximum packet size in bytes. + /// * `interval_ms` - Polling interval parameter for interrupt endpoints. fn alloc_endpoint_in( &mut self, ep_type: EndpointType, max_packet_size: u16, - interval: u8, + interval_ms: u8, ) -> Result; /// Start operation of the USB device. @@ -146,35 +167,37 @@ pub trait Driver<'a> { /// This consumes the `Driver` instance, so it's no longer possible to allocate more /// endpoints. fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe); - - /// Indicates that `set_device_address` must be called before accepting the corresponding - /// control transfer, not after. - /// - /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6 - const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false; } +/// USB bus trait. +/// +/// This trait provides methods that act on the whole bus. It is kept owned by +/// the main USB task, and used to manage the bus. pub trait Bus { - /// 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. + /// Enable the USB peripheral. async fn enable(&mut self); - /// Disables and powers down the USB peripheral. + /// Disable and powers down the USB peripheral. async fn disable(&mut self); + /// Wait for a bus-related event. + /// + /// This method should asynchronously wait for an event to happen, then + /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event; - /// Enables or disables an endpoint. + /// Enable or disable an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); - /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it - /// should be prepared to receive data again. Only used during control transfers. + /// Set or clear the STALL condition for an endpoint. + /// + /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); - /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers. + /// Get whether the STALL condition is set for an endpoint. fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; - /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the + /// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the /// device. /// /// The default implementation just returns `Unsupported`. @@ -187,7 +210,7 @@ pub trait Bus { Err(Unsupported) } - /// Initiates a remote wakeup of the host by the device. + /// Initiate a remote wakeup of the host by the device. /// /// # Errors /// @@ -196,25 +219,27 @@ pub trait Bus { async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } +/// Endpoint trait, common for OUT and IN. pub trait Endpoint { /// Get the endpoint address fn info(&self) -> &EndpointInfo; - /// Waits for the endpoint to be enabled. + /// Wait for the endpoint to be enabled. async fn wait_enabled(&mut self); } +/// OUT Endpoint trait. pub trait EndpointOut: Endpoint { - /// Reads a single packet of data from the endpoint, and returns the actual length of + /// Read a single packet of data from the endpoint, and return the actual length of /// the packet. /// /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. async fn read(&mut self, buf: &mut [u8]) -> Result; } -/// Trait for USB control pipe. +/// USB control pipe trait. /// -/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single +/// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single /// unit, and manages them together to implement the control pipe state machine. /// /// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that @@ -232,6 +257,14 @@ pub trait EndpointOut: Endpoint { /// accept() or reject() /// ``` /// +/// - control out for setting the device address: +/// +/// ```not_rust +/// setup() +/// (...processing...) +/// accept_set_address(addr) or reject() +/// ``` +/// /// - control out with len != 0: /// /// ```not_rust @@ -280,26 +313,26 @@ pub trait ControlPipe { /// Maximum packet size for the control pipe fn max_packet_size(&self) -> usize; - /// Reads a single setup packet from the endpoint. + /// Read a single setup packet from the endpoint. async fn setup(&mut self) -> [u8; 8]; - /// Reads a DATA OUT packet into `buf` in response to a control write request. + /// Read 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. 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. + /// Send 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`. async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>; - /// Accepts a control request. + /// Accept a control request. /// /// Causes the STATUS packet for the current request to be ACKed. async fn accept(&mut self); - /// Rejects a control request. + /// Reject a control request. /// /// Sets a STALL condition on the pipe to indicate an error. async fn reject(&mut self); @@ -311,8 +344,9 @@ pub trait ControlPipe { async fn accept_set_address(&mut self, addr: u8); } +/// IN Endpoint trait. pub trait EndpointIn: Endpoint { - /// Writes a single packet of data to the endpoint. + /// Write a single packet of data to the endpoint. async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; } @@ -338,18 +372,22 @@ pub enum Event { PowerRemoved, } +/// Allocating an endpoint failed. +/// +/// This can be due to running out of endpoints, or out of endpoint memory, +/// or because the hardware doesn't support the requested combination of features. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointAllocError; +/// Operation is unsupported by the driver. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Operation is unsupported by the driver. pub struct Unsupported; +/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] pub enum EndpointError { /// Either the packet to be written is too long to fit in the transmission /// buffer or the received packet is too long to fit in `buf`. diff --git a/embassy-usb/README.md b/embassy-usb/README.md new file mode 100644 index 000000000..581c3290f --- /dev/null +++ b/embassy-usb/README.md @@ -0,0 +1,22 @@ +# embassy-usb + +TODO crate description/ + +## Interoperability + +This crate can run on any executor. + +## Minimum supported Rust version (MSRV) + +This crate requires nightly Rust, due to using "async fn in trait" support. + +## 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-usb/src/builder.rs b/embassy-usb/src/builder.rs index fc16d2b41..484989949 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -349,11 +349,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { self.builder.config_descriptor.write(descriptor_type, descriptor) } - fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointIn { + fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { let ep = self .builder .driver - .alloc_endpoint_in(ep_type, max_packet_size, interval) + .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) .expect("alloc_endpoint_in failed"); self.builder.config_descriptor.endpoint(ep.info()); @@ -361,11 +361,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { ep } - fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointOut { + fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { let ep = self .builder .driver - .alloc_endpoint_out(ep_type, max_packet_size, interval) + .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) .expect("alloc_endpoint_out failed"); self.builder.config_descriptor.endpoint(ep.info()); @@ -393,25 +393,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. - pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { - self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval) + pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { + self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms) } /// Allocate a INTERRUPT OUT endpoint and write its descriptor. - pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { - self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval) + pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { + self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms) } /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. /// /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. - pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { - self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval) + pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { + self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms) } /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. - pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { - self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval) + pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { + self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms) } } diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 84db20621..09f17456c 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -1,3 +1,5 @@ +//! CDC-ACM class implementation, aka Serial over USB. + use core::cell::Cell; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; @@ -28,12 +30,14 @@ const REQ_SET_LINE_CODING: u8 = 0x20; const REQ_GET_LINE_CODING: u8 = 0x21; const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; +/// Internal state for CDC-ACM pub struct State<'a> { control: MaybeUninit>, shared: ControlShared, } impl<'a> State<'a> { + /// Create a new `State`. pub fn new() -> Self { Self { control: MaybeUninit::uninit(), @@ -284,10 +288,15 @@ impl From for StopBits { #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParityType { + /// No parity bit. None = 0, + /// Parity bit is 1 if the amount of `1` bits in the data byte is odd. Odd = 1, + /// Parity bit is 1 if the amount of `1` bits in the data byte is even. Even = 2, + /// Parity bit is always 1 Mark = 3, + /// Parity bit is always 0 Space = 4, } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 501df2d8c..bc79b3671 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -1,3 +1,4 @@ +//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class. use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; @@ -5,11 +6,13 @@ use embassy_usb_driver::Driver; use super::{CdcNcmClass, Receiver, Sender}; +/// Internal state for the embassy-net integration. pub struct State { ch_state: ch::State, } impl State { + /// Create a new `State`. pub const fn new() -> Self { Self { ch_state: ch::State::new(), @@ -17,6 +20,9 @@ impl State, const MTU: usize> { tx_usb: Sender<'d, D>, rx_usb: Receiver<'d, D>, @@ -24,6 +30,9 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { } impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { + /// Run the CDC-NCM class. + /// + /// You must call this in a background task for the class to operate. pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); let rx_fut = async move { @@ -66,9 +75,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { // would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? //pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; +/// Type alias for the embassy-net driver for CDC-NCM. pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { + /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net). pub fn into_embassy_net_device( self, state: &'d mut State, diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 4954a65bc..5e59b72fe 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -1,18 +1,19 @@ -/// CDC-NCM, aka Ethernet over USB. -/// -/// # Compatibility -/// -/// Windows: NOT supported in Windows 10. Supported in Windows 11. -/// -/// Linux: Well-supported since forever. -/// -/// Android: Support for CDC-NCM is spotty and varies across manufacturers. -/// -/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. -/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), -/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. -/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 -/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 +//! CDC-NCM class implementation, aka Ethernet over USB. +//! +//! # Compatibility +//! +//! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11. +//! +//! Linux: Well-supported since forever. +//! +//! Android: Support for CDC-NCM is spotty and varies across manufacturers. +//! +//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12. +//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), +//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. +//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 +//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 + use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; @@ -114,6 +115,7 @@ fn byteify(buf: &mut [u8], data: T) -> &[u8] { &buf[..len] } +/// Internal state for the CDC-NCM class. pub struct State<'a> { comm_control: MaybeUninit>, data_control: MaybeUninit, @@ -121,6 +123,7 @@ pub struct State<'a> { } impl<'a> State<'a> { + /// Create a new `State`. pub fn new() -> Self { Self { comm_control: MaybeUninit::uninit(), @@ -223,6 +226,7 @@ impl ControlHandler for DataControl { } } +/// CDC-NCM class pub struct CdcNcmClass<'d, D: Driver<'d>> { _comm_if: InterfaceNumber, comm_ep: D::EndpointIn, @@ -235,6 +239,7 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> { } impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { + /// Create a new CDC NCM class. pub fn new( builder: &mut Builder<'d, D>, state: &'d mut State<'d>, @@ -319,6 +324,9 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { } } + /// Split the class into a sender and receiver. + /// + /// This allows concurrently sending and receiving packets from separate tasks. pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) { ( Sender { @@ -334,12 +342,18 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { } } +/// CDC NCM class packet sender. +/// +/// You can obtain a `Sender` with [`CdcNcmClass::split`] pub struct Sender<'d, D: Driver<'d>> { write_ep: D::EndpointIn, seq: u16, } impl<'d, D: Driver<'d>> Sender<'d, D> { + /// Write a packet. + /// + /// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers. pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { let seq = self.seq; self.seq = self.seq.wrapping_add(1); @@ -393,6 +407,9 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { } } +/// CDC NCM class packet receiver. +/// +/// You can obtain a `Receiver` with [`CdcNcmClass::split`] pub struct Receiver<'d, D: Driver<'d>> { data_if: InterfaceNumber, comm_ep: D::EndpointIn, @@ -400,7 +417,9 @@ pub struct Receiver<'d, D: Driver<'d>> { } impl<'d, D: Driver<'d>> Receiver<'d, D> { - /// Reads a single packet from the OUT endpoint. + /// Write a network packet. + /// + /// This waits until a packet is succesfully received from the endpoint buffers. pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result { // Retry loop loop { diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index b967aba0e..2493061b0 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -1,3 +1,5 @@ +//! USB HID (Human Interface Device) class implementation. + use core::mem::MaybeUninit; use core::ops::Range; use core::sync::atomic::{AtomicUsize, Ordering}; @@ -28,6 +30,7 @@ const HID_REQ_SET_REPORT: u8 = 0x09; const HID_REQ_GET_PROTOCOL: u8 = 0x03; const HID_REQ_SET_PROTOCOL: u8 = 0x0b; +/// Configuration for the HID class. pub struct Config<'d> { /// HID report descriptor. pub report_descriptor: &'d [u8], @@ -46,11 +49,15 @@ pub struct Config<'d> { pub max_packet_size: u16, } +/// Report ID #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ReportId { + /// IN report In(u8), + /// OUT report Out(u8), + /// Feature report Feature(u8), } @@ -65,12 +72,14 @@ impl ReportId { } } +/// Internal state for USB HID. pub struct State<'d> { control: MaybeUninit>, out_report_offset: AtomicUsize, } impl<'d> State<'d> { + /// Create a new `State`. pub fn new() -> Self { State { control: MaybeUninit::uninit(), @@ -79,6 +88,7 @@ impl<'d> State<'d> { } } +/// USB HID reader/writer. pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> { reader: HidReader<'d, D, READ_N>, writer: HidWriter<'d, D, WRITE_N>, @@ -180,20 +190,30 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit } } +/// USB HID writer. +/// +/// You can obtain a `HidWriter` using [`HidReaderWriter::split`]. pub struct HidWriter<'d, D: Driver<'d>, const N: usize> { ep_in: D::EndpointIn, } +/// USB HID reader. +/// +/// You can obtain a `HidReader` using [`HidReaderWriter::split`]. pub struct HidReader<'d, D: Driver<'d>, const N: usize> { ep_out: D::EndpointOut, offset: &'d AtomicUsize, } +/// Error when reading a HID report. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ReadError { + /// The given buffer was too small to read the received report. BufferOverflow, + /// The endpoint is disabled. Disabled, + /// The report was only partially read. See [`HidReader::read`] for details. Sync(Range), } @@ -344,6 +364,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> { } } +/// Handler for HID-related control requests. pub trait RequestHandler { /// Reads the value of report `id` into `buf` returning the size. /// diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs index af27577a6..b23e03d40 100644 --- a/embassy-usb/src/class/mod.rs +++ b/embassy-usb/src/class/mod.rs @@ -1,3 +1,4 @@ +//! Implementations of well-known USB classes. pub mod cdc_acm; pub mod cdc_ncm; pub mod hid; diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index d6d0c6565..39b499f03 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -126,17 +126,23 @@ impl Request { } } +/// Response for a CONTROL OUT request. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OutResponse { + /// The request was accepted. Accepted, + /// The request was rejected. Rejected, } +/// Response for a CONTROL IN request. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum InResponse<'a> { + /// The request was accepted. The buffer contains the response data. Accepted(&'a [u8]), + /// The request was rejected. Rejected, } @@ -148,6 +154,7 @@ pub trait ControlHandler { /// Called after a USB reset after the bus reset sequence is complete. fn reset(&mut self) {} + /// Called when a "set alternate setting" control request is done on the interface. fn set_alternate_setting(&mut self, alternate_setting: u8) { let _ = alternate_setting; } diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index 497f03196..ae38e26ca 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -1,3 +1,5 @@ +//! Utilities for writing USB descriptors. + use crate::builder::Config; use crate::driver::EndpointInfo; use crate::types::*; @@ -236,7 +238,7 @@ impl<'a> DescriptorWriter<'a> { endpoint.ep_type as u8, // bmAttributes endpoint.max_packet_size as u8, (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize - endpoint.interval, // bInterval + endpoint.interval_ms, // bInterval ], ); } diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 096e8b07a..2656af29d 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] #![feature(type_alias_impl_trait)] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; @@ -46,10 +48,13 @@ pub enum UsbDeviceState { Configured, } +/// Error returned by [`UsbDevice::remote_wakeup`]. #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RemoteWakeupError { + /// The USB device is not suspended, or remote wakeup was not enabled. InvalidState, + /// The underlying driver doesn't support remote wakeup. Unsupported, } @@ -65,6 +70,7 @@ pub const CONFIGURATION_NONE: u8 = 0; /// The bConfiguration value for the single configuration supported by this device. pub const CONFIGURATION_VALUE: u8 = 1; +/// Maximum interface count, configured at compile time. pub const MAX_INTERFACE_COUNT: usize = 4; const STRING_INDEX_MANUFACTURER: u8 = 1; @@ -100,6 +106,7 @@ struct Interface<'d> { num_strings: u8, } +/// Main struct for the USB device stack. pub struct UsbDevice<'d, D: Driver<'d>> { control_buf: &'d mut [u8], control: D::ControlPipe, @@ -489,7 +496,6 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { .unwrap(); // TODO check it is valid (not out of range) - // TODO actually enable/disable endpoints. if let Some(handler) = &mut iface.handler { handler.set_alternate_setting(new_altsetting); diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index aeab063d1..1743e61ff 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -1,3 +1,5 @@ +//! USB types. + /// A handle for a USB interface that contains its number. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From b5cf332cc076a0de11ce6a0563a2235c9e57eb5c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Feb 2023 00:48:33 +0100 Subject: [PATCH 0537/1575] nrf: docs. --- embassy-hal-common/src/macros.rs | 6 + .../src/macros/cortex_m_interrupt_declare.rs | 3 + embassy-nrf/README.md | 58 ++++++++++ embassy-nrf/src/buffered_uarte.rs | 9 +- embassy-nrf/src/chips/nrf5340_app.rs | 1 + embassy-nrf/src/chips/nrf5340_net.rs | 1 + embassy-nrf/src/chips/nrf9160.rs | 1 + embassy-nrf/src/gpio.rs | 2 +- embassy-nrf/src/gpiote.rs | 53 +++++++-- embassy-nrf/src/i2s.rs | 109 +++++++++++++----- embassy-nrf/src/lib.rs | 46 +------- embassy-nrf/src/nvmc.rs | 2 +- embassy-nrf/src/pdm.rs | 14 ++- embassy-nrf/src/ppi/dppi.rs | 3 + embassy-nrf/src/ppi/mod.rs | 2 +- embassy-nrf/src/ppi/ppi.rs | 2 +- embassy-nrf/src/pwm.rs | 22 +++- embassy-nrf/src/qdec.rs | 37 +++++- embassy-nrf/src/qspi.rs | 42 +++++++ embassy-nrf/src/rng.rs | 6 +- embassy-nrf/src/saadc.rs | 56 +++++---- embassy-nrf/src/spim.rs | 33 ++++-- embassy-nrf/src/spis.rs | 36 ++++-- embassy-nrf/src/temp.rs | 5 +- embassy-nrf/src/timer.rs | 36 +++++- embassy-nrf/src/twim.rs | 73 +++++++++--- embassy-nrf/src/twis.rs | 57 +++++++-- embassy-nrf/src/uarte.rs | 76 ++++++++---- embassy-nrf/src/usb.rs | 90 ++++++++++----- embassy-nrf/src/wdt.rs | 8 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf52840/src/bin/i2s_effect.rs | 6 +- examples/nrf52840/src/bin/i2s_monitor.rs | 6 +- examples/nrf52840/src/bin/i2s_waveform.rs | 6 +- examples/nrf52840/src/bin/saadc_continuous.rs | 4 +- examples/nrf52840/src/bin/usb_ethernet.rs | 6 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 4 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 4 +- examples/nrf52840/src/bin/usb_serial.rs | 6 +- .../nrf52840/src/bin/usb_serial_multitask.rs | 6 +- 40 files changed, 694 insertions(+), 245 deletions(-) create mode 100644 embassy-nrf/README.md diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs index 7af85f782..da7913136 100644 --- a/embassy-hal-common/src/macros.rs +++ b/embassy-hal-common/src/macros.rs @@ -1,10 +1,12 @@ #[macro_export] macro_rules! peripherals { ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + /// Types for the peripheral singletons. pub mod peripherals { $( $(#[$cfg])? #[allow(non_camel_case_types)] + #[doc = concat!(stringify!($name), " peripheral")] pub struct $name { _private: () } $(#[$cfg])? @@ -25,9 +27,13 @@ macro_rules! peripherals { )* } + /// Struct containing all the peripheral singletons. + /// + /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]. #[allow(non_snake_case)] pub struct Peripherals { $( + #[doc = concat!(stringify!($name), " peripheral")] $(#[$cfg])? pub $name: peripherals::$name, )* diff --git a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs index ab61ad5da..ebbb47cd7 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs @@ -6,7 +6,10 @@ pub fn run(name: syn::Ident) -> Result { let name_interrupt = format_ident!("{}", name); let name_handler = format!("__EMBASSY_{}_HANDLER", name); + let doc = format!("{} interrupt singleton.", name); + let result = quote! { + #[doc = #doc] #[allow(non_camel_case_types)] pub struct #name_interrupt(()); unsafe impl ::embassy_cortex_m::interrupt::Interrupt for #name_interrupt { diff --git a/embassy-nrf/README.md b/embassy-nrf/README.md new file mode 100644 index 000000000..a31cfae68 --- /dev/null +++ b/embassy-nrf/README.md @@ -0,0 +1,58 @@ +# Embassy nRF HAL + +HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. + +The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs +for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to +complete operations in low power mod and handling interrupts, so that applications can focus on more important matters. + +## EasyDMA considerations + +On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting +with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI). +However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust +slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: + +```no_run +// As we pass a slice to the function whose contents will not ever change, +// the compiler writes it into the flash and thus the pointer to it will +// reference static memory. Since EasyDMA requires slices to reside in RAM, +// this function call will fail. +let result = spim.write_from_ram(&[1, 2, 3]); +assert_eq!(result, Err(Error::BufferNotInRAM)); + +// The data is still static and located in flash. However, since we are assigning +// it to a variable, the compiler will load it into memory. Passing a reference to the +// variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. +// This function call succeeds. +let data = [1, 2, 3]; +let result = spim.write_from_ram(&data); +assert!(result.is_ok()); +``` + +Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions: +- Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM. +- Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission. + +Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will +fail and notify you, or the more convenient versions without the suffix which are potentially a little bit +more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage +as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840). + +Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as +mutable slices always reside in RAM. + +## Minimum supported Rust version (MSRV) + +Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. + +## 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-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index ea25236f0..112f084c1 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -1,4 +1,4 @@ -//! Async buffered UART +//! Async buffered UART driver. //! //! WARNING!!! The functionality provided here is intended to be used only //! in situations where hardware flow control are available i.e. CTS and RTS. @@ -69,7 +69,7 @@ struct StateInner<'d, U: UarteInstance, T: TimerInstance> { tx_waker: WakerRegistration, } -/// Interface to a UARTE instance +/// Buffered UARTE driver. pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { inner: RefCell>>, } @@ -199,6 +199,9 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { }); } + /// Split the UART in reader and writer parts. + /// + /// This allows reading and writing concurrently from independent tasks. pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) { (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) } @@ -320,10 +323,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } } +/// Reader part of the buffered UARTE driver. pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } +/// Writer part of the buffered UARTE driver. pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 4575f09ff..c600fcbf6 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -1,3 +1,4 @@ +/// Peripheral Access Crate #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 54827238a..a46583eca 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -1,3 +1,4 @@ +/// Peripheral Access Crate #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 472ee6772..e1509ddde 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -1,3 +1,4 @@ +/// Peripheral Access Crate #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 6ef5033eb..895ab9340 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -1,4 +1,4 @@ -//! General purpose input/output for nRF. +//! General purpose input/output (GPIO) driver. #![macro_use] use core::convert::Infallible; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 7467dbc60..e1816eb9b 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,3 +1,5 @@ +//! GPIO task/event (GPIOTE) driver. + use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; @@ -11,29 +13,38 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::ppi::{Event, Task}; use crate::{interrupt, pac, peripherals}; -pub const CHANNEL_COUNT: usize = 8; +/// Amount of GPIOTE channels in the chip. +const CHANNEL_COUNT: usize = 8; #[cfg(any(feature = "nrf52833", feature = "nrf52840"))] -pub const PIN_COUNT: usize = 48; +const PIN_COUNT: usize = 48; #[cfg(not(any(feature = "nrf52833", feature = "nrf52840")))] -pub const PIN_COUNT: usize = 32; +const PIN_COUNT: usize = 32; #[allow(clippy::declare_interior_mutable_const)] const NEW_AW: AtomicWaker = AtomicWaker::new(); static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; +/// Polarity for listening to events for GPIOTE input channels. pub enum InputChannelPolarity { + /// Don't listen for any pin changes. None, + /// Listen for high to low changes. HiToLo, + /// Listen for low to high changes. LoToHi, + /// Listen for any change, either low to high or high to low. Toggle, } -/// Polarity of the `task out` operation. +/// Polarity of the OUT task operation for GPIOTE output channels. pub enum OutputChannelPolarity { + /// Set the pin high. Set, + /// Set the pin low. Clear, + /// Toggle the pin. Toggle, } @@ -162,6 +173,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for InputChannel<'d, C, T> { } impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { + /// Create a new GPIOTE input channel driver. pub fn new(ch: impl Peripheral

+ 'd, pin: Input<'d, T>, polarity: InputChannelPolarity) -> Self { into_ref!(ch); @@ -188,6 +200,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { InputChannel { ch, pin } } + /// Asynchronously wait for an event in this channel. pub async fn wait(&self) { let g = regs(); let num = self.ch.number(); @@ -231,6 +244,7 @@ impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { } impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { + /// Create a new GPIOTE output channel driver. pub fn new(ch: impl Peripheral

+ 'd, pin: Output<'d, T>, polarity: OutputChannelPolarity) -> Self { into_ref!(ch); let g = regs(); @@ -258,20 +272,20 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { OutputChannel { ch, _pin: pin } } - /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). + /// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { let g = regs(); g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); } - /// Triggers `task set` (set associated pin high). + /// Triggers the SET task (set associated pin high). #[cfg(not(feature = "nrf51"))] pub fn set(&self) { let g = regs(); g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); } - /// Triggers `task clear` (set associated pin low). + /// Triggers the CLEAR task (set associated pin low). #[cfg(not(feature = "nrf51"))] pub fn clear(&self) { let g = regs(); @@ -336,48 +350,58 @@ impl<'a> Future for PortInputFuture<'a> { } impl<'d, T: GpioPin> Input<'d, T> { + /// Wait until the pin is high. If it is already high, return immediately. pub async fn wait_for_high(&mut self) { self.pin.wait_for_high().await } + /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { self.pin.wait_for_low().await } + /// Wait for the pin to undergo a transition from low to high. pub async fn wait_for_rising_edge(&mut self) { self.pin.wait_for_rising_edge().await } + /// Wait for the pin to undergo a transition from high to low. pub async fn wait_for_falling_edge(&mut self) { self.pin.wait_for_falling_edge().await } + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await } } impl<'d, T: GpioPin> Flex<'d, T> { + /// Wait until the pin is high. If it is already high, return immediately. pub async fn wait_for_high(&mut self) { self.pin.conf().modify(|_, w| w.sense().high()); PortInputFuture::new(&mut self.pin).await } + /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { self.pin.conf().modify(|_, w| w.sense().low()); PortInputFuture::new(&mut self.pin).await } + /// Wait for the pin to undergo a transition from low to high. pub async fn wait_for_rising_edge(&mut self) { self.wait_for_low().await; self.wait_for_high().await; } + /// Wait for the pin to undergo a transition from high to low. pub async fn wait_for_falling_edge(&mut self) { self.wait_for_high().await; self.wait_for_low().await; } + /// Wait for the pin to undergo any transition, i.e low to high OR high to low. pub async fn wait_for_any_edge(&mut self) { if self.is_high() { self.pin.conf().modify(|_, w| w.sense().low()); @@ -394,8 +418,17 @@ mod sealed { pub trait Channel {} } +/// GPIOTE channel trait. +/// +/// Implemented by all GPIOTE channels. pub trait Channel: sealed::Channel + Sized { + /// Get the channel number. fn number(&self) -> usize; + + /// Convert this channel to a type-erased `AnyChannel`. + /// + /// This allows using several channels in situations that might require + /// them to be the same type, like putting them in an array. fn degrade(self) -> AnyChannel { AnyChannel { number: self.number() as u8, @@ -403,6 +436,12 @@ pub trait Channel: sealed::Channel + Sized { } } +/// Type-erased channel. +/// +/// Obtained by calling `Channel::degrade`. +/// +/// This allows using several channels in situations that might require +/// them to be the same type, like putting them in an array. pub struct AnyChannel { number: u8, } diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 7e9507751..770df7c89 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -1,6 +1,6 @@ -#![macro_use] +//! Inter-IC Sound (I2S) driver. -//! Support for I2S audio +#![macro_use] use core::future::poll_fn; use core::marker::PhantomData; @@ -19,16 +19,23 @@ use crate::pac::i2s::RegisterBlock; use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::{Peripheral, EASY_DMA_SIZE}; +/// Type alias for `MultiBuffering` with 2 buffers. pub type DoubleBuffering = MultiBuffering; +/// I2S transfer error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// The buffer is too long. BufferTooLong, + /// The buffer is empty. BufferZeroLength, - BufferNotInDataMemory, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, + /// The buffer address is not aligned. BufferMisaligned, + /// The buffer length is not a multiple of the alignment. BufferLengthMisaligned, } @@ -36,34 +43,16 @@ pub enum Error { #[derive(Clone)] #[non_exhaustive] pub struct Config { + /// Sample width pub sample_width: SampleWidth, + /// Alignment pub align: Align, + /// Sample format pub format: Format, + /// Channel configuration. 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 { @@ -75,7 +64,7 @@ impl Default for Config { } } -/// I2S Mode +/// I2S clock configuration. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub struct MasterClock { freq: MckFreq, @@ -83,12 +72,14 @@ pub struct MasterClock { } impl MasterClock { + /// Create a new `MasterClock`. pub fn new(freq: MckFreq, ratio: Ratio) -> Self { Self { freq, ratio } } } impl MasterClock { + /// Get the sample rate for this clock configuration. pub fn sample_rate(&self) -> u32 { self.freq.to_frequency() / self.ratio.to_divisor() } @@ -97,18 +88,31 @@ impl MasterClock { /// Master clock generator frequency. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum MckFreq { + /// 32 Mhz / 8 = 4000.00 kHz _32MDiv8, + /// 32 Mhz / 10 = 3200.00 kHz _32MDiv10, + /// 32 Mhz / 11 = 2909.09 kHz _32MDiv11, + /// 32 Mhz / 15 = 2133.33 kHz _32MDiv15, + /// 32 Mhz / 16 = 2000.00 kHz _32MDiv16, + /// 32 Mhz / 21 = 1523.81 kHz _32MDiv21, + /// 32 Mhz / 23 = 1391.30 kHz _32MDiv23, + /// 32 Mhz / 30 = 1066.67 kHz _32MDiv30, + /// 32 Mhz / 31 = 1032.26 kHz _32MDiv31, + /// 32 Mhz / 32 = 1000.00 kHz _32MDiv32, + /// 32 Mhz / 42 = 761.90 kHz _32MDiv42, + /// 32 Mhz / 63 = 507.94 kHz _32MDiv63, + /// 32 Mhz / 125 = 256.00 kHz _32MDiv125, } @@ -146,14 +150,23 @@ impl From for usize { /// #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Ratio { + /// Divide by 32 _32x, + /// Divide by 48 _48x, + /// Divide by 64 _64x, + /// Divide by 96 _96x, + /// Divide by 128 _128x, + /// Divide by 192 _192x, + /// Divide by 256 _256x, + /// Divide by 384 _384x, + /// Divide by 512 _512x, } @@ -165,6 +178,7 @@ impl Ratio { usize::from(*self) as u8 } + /// Return the divisor for this ratio pub fn to_divisor(&self) -> u32 { Self::RATIOS[usize::from(*self)] } @@ -183,11 +197,17 @@ impl From for usize { /// For custom master clock configuration, please refer to [MasterClock]. #[derive(Clone, Copy)] pub enum ApproxSampleRate { + /// 11025 Hz _11025, + /// 16000 Hz _16000, + /// 22050 Hz _22050, + /// 32000 Hz _32000, + /// 44100 Hz _44100, + /// 48000 Hz _48000, } @@ -211,6 +231,7 @@ impl From for MasterClock { } impl ApproxSampleRate { + /// Get the sample rate as an integer. pub fn sample_rate(&self) -> u32 { MasterClock::from(*self).sample_rate() } @@ -223,20 +244,32 @@ impl ApproxSampleRate { /// For custom master clock configuration, please refer to [Mode]. #[derive(Clone, Copy)] pub enum ExactSampleRate { + /// 8000 Hz _8000, + /// 10582 Hz _10582, + /// 12500 Hz _12500, + /// 15625 Hz _15625, + /// 15873 Hz _15873, + /// 25000 Hz _25000, + /// 31250 Hz _31250, + /// 50000 Hz _50000, + /// 62500 Hz _62500, + /// 100000 Hz _100000, + /// 125000 Hz _125000, } impl ExactSampleRate { + /// Get the sample rate as an integer. pub fn sample_rate(&self) -> u32 { MasterClock::from(*self).sample_rate() } @@ -263,8 +296,11 @@ impl From for MasterClock { /// Sample width. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SampleWidth { + /// 8 bit samples. _8bit, + /// 16 bit samples. _16bit, + /// 24 bit samples. _24bit, } @@ -277,7 +313,9 @@ impl From for u8 { /// Channel used for the most significant sample value in a frame. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Align { + /// Left-align samples. Left, + /// Right-align samples. Right, } @@ -293,7 +331,9 @@ impl From for bool { /// Frame format. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Format { + /// I2S frame format I2S, + /// Aligned frame format Aligned, } @@ -309,8 +349,11 @@ impl From for bool { /// Channels #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Channels { + /// Stereo (2 channels). Stereo, + /// Mono, left channel only. MonoLeft, + /// Mono, right channel only. MonoRight, } @@ -320,7 +363,7 @@ impl From for u8 { } } -/// Interface to the I2S peripheral using EasyDMA to offload the transmission and reception workload. +/// I2S driver. pub struct I2S<'d, T: Instance> { i2s: PeripheralRef<'d, T>, irq: PeripheralRef<'d, T::Interrupt>, @@ -566,7 +609,7 @@ impl<'d, T: Instance> I2S<'d, T> { { trace!("SEND: {}", buffer_ptr as *const S as u32); - slice_in_ram_or(buffer_ptr, Error::BufferNotInDataMemory)?; + slice_in_ram_or(buffer_ptr, Error::BufferNotInRAM)?; compiler_fence(Ordering::SeqCst); @@ -1003,7 +1046,10 @@ impl Device { /// Sample details pub trait Sample: Sized + Copy + Default { + /// Width of this sample type. const WIDTH: usize; + + /// Scale of this sample. const SCALE: Self; } @@ -1022,12 +1068,13 @@ impl Sample for i32 { const SCALE: Self = 1 << (Self::WIDTH - 1); } -/// A 4-bytes aligned buffer. +/// A 4-bytes aligned buffer. Needed for DMA access. #[derive(Clone, Copy)] #[repr(align(4))] pub struct AlignedBuffer([T; N]); impl AlignedBuffer { + /// Create a new `AlignedBuffer`. pub fn new(array: [T; N]) -> Self { Self(array) } @@ -1052,12 +1099,14 @@ impl DerefMut for AlignedBuffer { } } +/// Set of multiple buffers, for multi-buffering transfers. pub struct MultiBuffering { buffers: [AlignedBuffer; NB], index: usize, } impl MultiBuffering { + /// Create a new `MultiBuffering`. pub fn new() -> Self { assert!(NB > 1); Self { @@ -1119,7 +1168,9 @@ pub(crate) mod sealed { } } +/// I2S peripehral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 1dd0e7905..20e70a248 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,53 +1,11 @@ -//! # Embassy nRF HAL -//! -//! HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. -//! -//! The Embassy nRF HAL targets the Nordic Semiconductor nRF family of hardware. The HAL implements both blocking and async APIs -//! for many peripherals. The benefit of using the async APIs is that the HAL takes care of waiting for peripherals to -//! complete operations in low power mod and handling interrupts, so that applications can focus on more important matters. -//! -//! ## EasyDMA considerations -//! -//! On nRF chips, peripherals can use the so called EasyDMA feature to offload the task of interacting -//! with peripherals. It takes care of sending/receiving data over a variety of bus protocols (TWI/I2C, UART, SPI). -//! However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust -//! slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: -//! -//! ```no_run -//! // As we pass a slice to the function whose contents will not ever change, -//! // the compiler writes it into the flash and thus the pointer to it will -//! // reference static memory. Since EasyDMA requires slices to reside in RAM, -//! // this function call will fail. -//! let result = spim.write_from_ram(&[1, 2, 3]); -//! assert_eq!(result, Err(Error::DMABufferNotInDataMemory)); -//! -//! // The data is still static and located in flash. However, since we are assigning -//! // it to a variable, the compiler will load it into memory. Passing a reference to the -//! // variable will yield a pointer that references dynamic memory, thus making EasyDMA happy. -//! // This function call succeeds. -//! let data = [1, 2, 3]; -//! let result = spim.write_from_ram(&data); -//! assert!(result.is_ok()); -//! ``` -//! -//! Each peripheral struct which uses EasyDMA ([`Spim`](spim::Spim), [`Uarte`](uarte::Uarte), [`Twim`](twim::Twim)) has two variants of their mutating functions: -//! - Functions with the suffix (e.g. [`write_from_ram`](spim::Spim::write_from_ram), [`transfer_from_ram`](spim::Spim::transfer_from_ram)) will return an error if the passed slice does not reside in RAM. -//! - Functions without the suffix (e.g. [`write`](spim::Spim::write), [`transfer`](spim::Spim::transfer)) will check whether the data is in RAM and copy it into memory prior to transmission. -//! -//! Since copying incurs a overhead, you are given the option to choose from `_from_ram` variants which will -//! fail and notify you, or the more convenient versions without the suffix which are potentially a little bit -//! more inefficient. Be aware that this overhead is not only in terms of instruction count but also in terms of memory usage -//! as the methods without the suffix will be allocating a statically sized buffer (up to 512 bytes for the nRF52840). -//! -//! Note that the methods that read data like [`read`](spim::Spim::read) and [`transfer_in_place`](spim::Spim::transfer_in_place) do not have the corresponding `_from_ram` variants as -//! mutable slices always reside in RAM. - #![no_std] #![cfg_attr( feature = "nightly", feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] +#![doc = include_str!("../README.md")] +#![warn(missing_docs)] #[cfg(not(any( feature = "nrf51", diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 405ea3171..c1ffa31aa 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -1,4 +1,4 @@ -//! Non-Volatile Memory Controller (NVMC) module. +//! Non-Volatile Memory Controller (NVMC, AKA internal flash) driver. use core::{ptr, slice}; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index b7c7022cf..54feca4c1 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,4 +1,4 @@ -//! PDM mirophone interface +//! Pulse Density Modulation (PDM) mirophone driver. use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; @@ -22,12 +22,16 @@ pub struct Pdm<'d> { phantom: PhantomData<&'d PDM>, } +/// PDM error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// Buffer is too long. BufferTooLong, + /// Buffer is empty BufferZeroLength, + /// PDM is not running NotRunning, } @@ -119,6 +123,7 @@ impl<'d> Pdm<'d> { r.events_started.reset(); } + /// Sample data into the given buffer. pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { if buffer.len() == 0 { return Err(Error::BufferZeroLength); @@ -215,14 +220,21 @@ impl Default for Config { } } +/// PDM operation mode. #[derive(PartialEq)] pub enum OperationMode { + /// Mono (1 channel) Mono, + /// Stereo (2 channels) Stereo, } + +/// PDM edge polarity #[derive(PartialEq)] pub enum Edge { + /// Left edge is rising LeftRising, + /// Left edge is falling LeftFalling, } diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index de856c0ca..0908cd7be 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -11,12 +11,14 @@ fn regs() -> &'static pac::dppic::RegisterBlock { } impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { + /// Configure PPI channel to trigger `task` on `event`. pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event, task: Task) -> Self { Ppi::new_many_to_many(ch, [event], [task]) } } impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { + /// Configure PPI channel to trigger both `task1` and `task2` on `event`. pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { Ppi::new_many_to_many(ch, [event], [task1, task2]) } @@ -25,6 +27,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> { + /// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires. pub fn new_many_to_many( ch: impl Peripheral

+ 'd, events: [Event; EVENT_COUNT], diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 8f5ed14cd..b76eccf0b 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -1,6 +1,6 @@ #![macro_use] -//! HAL interface for the PPI and DPPI peripheral. +//! Programmable Peripheral Interconnect (PPI/DPPI) driver. //! //! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability //! between peripherals through their events and tasks. There are fixed PPI channels and fully diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 19abc4e18..a96ab50b7 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -48,7 +48,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { #[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { - /// Configure PPI channel to trigger `task1` and `task2` on `event`. + /// Configure PPI channel to trigger both `task1` and `task2` on `event`. pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { into_ref!(ch); diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 5f750a91e..708f23104 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -1,3 +1,5 @@ +//! Pulse Width Modulation (PWM) driver. + #![macro_use] use core::sync::atomic::{compiler_fence, Ordering}; @@ -32,6 +34,7 @@ pub struct SequencePwm<'d, T: Instance> { ch3: Option>, } +/// PWM error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -41,7 +44,7 @@ pub enum Error { /// Min Sequence count is 1 SequenceTimesAtLeastOne, /// EasyDMA can only read from data memory, read only buffers in flash will fail. - DMABufferNotInDataMemory, + BufferNotInRAM, } const MAX_SEQUENCE_LEN: usize = 32767; @@ -358,6 +361,7 @@ pub struct Sequence<'s> { } impl<'s> Sequence<'s> { + /// Create a new `Sequence` pub fn new(words: &'s [u16], config: SequenceConfig) -> Self { Self { words, config } } @@ -367,7 +371,7 @@ impl<'s> Sequence<'s> { /// Takes at one sequence along with its configuration. #[non_exhaustive] pub struct SingleSequencer<'d, 's, T: Instance> { - pub sequencer: Sequencer<'d, 's, T>, + sequencer: Sequencer<'d, 's, T>, } impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { @@ -428,8 +432,8 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { let sequence0 = &self.sequence0; let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0); - slice_in_ram_or(sequence0.words, Error::DMABufferNotInDataMemory)?; - slice_in_ram_or(alt_sequence.words, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(sequence0.words, Error::BufferNotInRAM)?; + slice_in_ram_or(alt_sequence.words, Error::BufferNotInRAM)?; if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN { return Err(Error::SequenceTooLong); @@ -536,13 +540,21 @@ pub enum SequenceMode { /// PWM Base clock is system clock (16MHz) divided by prescaler #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Prescaler { + /// Divide by 1 Div1, + /// Divide by 2 Div2, + /// Divide by 4 Div4, + /// Divide by 8 Div8, + /// Divide by 16 Div16, + /// Divide by 32 Div32, + /// Divide by 64 Div64, + /// Divide by 128 Div128, } @@ -828,7 +840,9 @@ pub(crate) mod sealed { } } +/// PWM peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 253c85c32..c01babca3 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,4 +1,4 @@ -//! Quadrature decoder interface +//! Quadrature decoder (QDEC) driver. use core::future::poll_fn; use core::task::Poll; @@ -12,17 +12,23 @@ use crate::interrupt::InterruptExt; use crate::peripherals::QDEC; use crate::{interrupt, pac, Peripheral}; -/// Quadrature decoder +/// Quadrature decoder driver. pub struct Qdec<'d> { _p: PeripheralRef<'d, QDEC>, } +/// QDEC config #[non_exhaustive] pub struct Config { + /// Number of samples pub num_samples: NumSamples, + /// Sample period pub period: SamplePeriod, + /// Set LED output pin polarity pub led_polarity: LedPolarity, + /// Enable/disable input debounce filters pub debounce: bool, + /// Time period the LED is switched ON prior to sampling (0..511 us). pub led_pre_usecs: u16, } @@ -41,6 +47,7 @@ impl Default for Config { static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Qdec<'d> { + /// Create a new QDEC. pub fn new( qdec: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -52,6 +59,7 @@ impl<'d> Qdec<'d> { Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) } + /// Create a new QDEC, with a pin for LED output. pub fn new_with_led( qdec: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -170,36 +178,61 @@ impl<'d> Qdec<'d> { } } +/// Sample period #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum SamplePeriod { + /// 128 us _128us, + /// 256 us _256us, + /// 512 us _512us, + /// 1024 us _1024us, + /// 2048 us _2048us, + /// 4096 us _4096us, + /// 8192 us _8192us, + /// 16384 us _16384us, + /// 32 ms _32ms, + /// 65 ms _65ms, + /// 131 ms _131ms, } +/// Number of samples taken. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum NumSamples { + /// 10 samples _10smpl, + /// 40 samples _40smpl, + /// 80 samples _80smpl, + /// 120 samples _120smpl, + /// 160 samples _160smpl, + /// 200 samples _200smpl, + /// 240 samples _240smpl, + /// 280 samples _280smpl, + /// 1 sample _1smpl, } +/// LED polarity #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum LedPolarity { + /// Active high (a high output turns on the LED). ActiveHigh, + /// Active low (a low output turns on the LED). ActiveLow, } diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index ea0a17031..07a970018 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -1,3 +1,5 @@ +//! Quad Serial Peripheral Interface (QSPI) flash driver. + #![macro_use] use core::future::poll_fn; @@ -15,6 +17,7 @@ pub use crate::pac::qspi::ifconfig0::{ pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; use crate::{pac, Peripheral}; +/// Deep power-down config. pub struct DeepPowerDownConfig { /// Time required for entering DPM, in units of 16us pub enter_time: u16, @@ -22,37 +25,62 @@ pub struct DeepPowerDownConfig { pub exit_time: u16, } +/// QSPI bus frequency. pub enum Frequency { + /// 32 Mhz M32 = 0, + /// 16 Mhz M16 = 1, + /// 10.7 Mhz M10_7 = 2, + /// 8 Mhz M8 = 3, + /// 6.4 Mhz M6_4 = 4, + /// 5.3 Mhz M5_3 = 5, + /// 4.6 Mhz M4_6 = 6, + /// 4 Mhz M4 = 7, + /// 3.6 Mhz M3_6 = 8, + /// 3.2 Mhz M3_2 = 9, + /// 2.9 Mhz M2_9 = 10, + /// 2.7 Mhz M2_7 = 11, + /// 2.5 Mhz M2_5 = 12, + /// 2.3 Mhz M2_3 = 13, + /// 2.1 Mhz M2_1 = 14, + /// 2 Mhz M2 = 15, } +/// QSPI config. #[non_exhaustive] pub struct Config { + /// XIP offset. pub xip_offset: u32, + /// Opcode used for read operations. pub read_opcode: ReadOpcode, + /// Opcode used for write operations. pub write_opcode: WriteOpcode, + /// Page size for write operations. pub write_page_size: WritePageSize, + /// Configuration for deep power down. If None, deep power down is disabled. pub deep_power_down: Option, + /// QSPI bus frequency. pub frequency: Frequency, /// Value is specified in number of 16 MHz periods (62.5 ns) pub sck_delay: u8, /// Whether data is captured on the clock rising edge and data is output on a falling edge (MODE0) or vice-versa (MODE3) pub spi_mode: SpiMode, + /// Addressing mode (24-bit or 32-bit) pub address_mode: AddressMode, } @@ -72,20 +100,24 @@ impl Default for Config { } } +/// Error #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// Operation address was out of bounds. OutOfBounds, // TODO add "not in data memory" error and check for it } +/// QSPI flash driver. pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { irq: PeripheralRef<'d, T::Interrupt>, dpm_enabled: bool, } impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { + /// Create a new QSPI driver. pub fn new( _qspi: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -183,6 +215,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } } + /// Do a custom QSPI instruction. pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -198,6 +231,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Do a custom QSPI instruction, blocking version. pub fn blocking_custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { let len = core::cmp::max(req.len(), resp.len()) as u8; self.custom_instruction_start(opcode, req, len)?; @@ -346,6 +380,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Read data from the flash memory. pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -357,6 +392,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Write data to the flash memory. pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -368,6 +404,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Erase a sector on the flash memory. pub async fn erase(&mut self, address: usize) -> Result<(), Error> { let bomb = DropBomb::new(); @@ -379,18 +416,21 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } + /// Read data from the flash memory, blocking version. pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { self.start_read(address, data)?; self.blocking_wait_ready(); Ok(()) } + /// Write data to the flash memory, blocking version. pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { self.start_write(address, data)?; self.blocking_wait_ready(); Ok(()) } + /// Erase a sector on the flash memory, blocking version. pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { self.start_erase(address)?; self.blocking_wait_ready(); @@ -547,7 +587,9 @@ pub(crate) mod sealed { } } +/// QSPI peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index e0caeaaee..b0b3a8eb8 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,3 +1,5 @@ +//! Random Number Generator (RNG) driver. + use core::future::poll_fn; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; @@ -128,10 +130,11 @@ impl<'d> Rng<'d> { /// However, this makes the generation of numbers slower. /// /// Defaults to disabled. - pub fn bias_correction(&self, enable: bool) { + pub fn set_bias_correction(&self, enable: bool) { RNG::regs().config.write(|w| w.dercen().bit(enable)) } + /// Fill the buffer with random bytes. pub async fn fill_bytes(&mut self, dest: &mut [u8]) { if dest.len() == 0 { return; // Nothing to fill @@ -175,6 +178,7 @@ impl<'d> Rng<'d> { drop(on_drop); } + /// Fill the buffer with random bytes, blocking version. pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) { self.start(); diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 4592d4687..2d01a3dda 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -1,3 +1,5 @@ +//! Successive Approximation Analog-to-Digital Converter (SAADC) driver. + #![macro_use] use core::future::poll_fn; @@ -20,6 +22,7 @@ use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::{interrupt, pac, peripherals, Peripheral}; +/// SAADC error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -102,17 +105,17 @@ impl<'d> ChannelConfig<'d> { } } -/// The state of a continuously running sampler. While it reflects -/// the progress of a sampler, it also signals what should be done -/// next. For example, if the sampler has stopped then the Saadc implementation -/// can then tear down its infrastructure. +/// Value returned by the SAADC callback, deciding what happens next. #[derive(PartialEq)] -pub enum SamplerState { - Sampled, - Stopped, +pub enum CallbackResult { + /// The SAADC should keep sampling and calling the callback. + Continue, + /// The SAADC should stop sampling, and return. + Stop, } impl<'d, const N: usize> Saadc<'d, N> { + /// Create a new SAADC driver. pub fn new( saadc: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -285,7 +288,7 @@ impl<'d, const N: usize> Saadc<'d, N> { /// free the buffers from being used by the peripheral. Cancellation will /// also cause the sampling to be stopped. - pub async fn run_task_sampler( + pub async fn run_task_sampler( &mut self, timer: &mut T, ppi_ch1: &mut impl ConfigurableChannel, @@ -293,9 +296,9 @@ impl<'d, const N: usize> Saadc<'d, N> { frequency: Frequency, sample_counter: u32, bufs: &mut [[[i16; N]; N0]; 2], - sampler: S, + callback: F, ) where - S: FnMut(&[[i16; N]]) -> SamplerState, + F: FnMut(&[[i16; N]]) -> CallbackResult, { let r = Self::regs(); @@ -321,20 +324,20 @@ impl<'d, const N: usize> Saadc<'d, N> { || { sample_ppi.enable(); }, - sampler, + callback, ) .await; } - async fn run_sampler( + async fn run_sampler( &mut self, bufs: &mut [[[i16; N]; N0]; 2], sample_rate_divisor: Option, mut init: I, - mut sampler: S, + mut callback: F, ) where I: FnMut(), - S: FnMut(&[[i16; N]]) -> SamplerState, + F: FnMut(&[[i16; N]]) -> CallbackResult, { // In case the future is dropped, stop the task and wait for it to end. let on_drop = OnDrop::new(Self::stop_sampling_immediately); @@ -395,12 +398,15 @@ impl<'d, const N: usize> Saadc<'d, N> { r.events_end.reset(); r.intenset.write(|w| w.end().set()); - if sampler(&bufs[current_buffer]) == SamplerState::Sampled { - let next_buffer = 1 - current_buffer; - current_buffer = next_buffer; - } else { - return Poll::Ready(()); - }; + match callback(&bufs[current_buffer]) { + CallbackResult::Continue => { + let next_buffer = 1 - current_buffer; + current_buffer = next_buffer; + } + CallbackResult::Stop => { + return Poll::Ready(()); + } + } } if r.events_started.read().bits() != 0 { @@ -458,7 +464,7 @@ impl<'d> Saadc<'d, 1> { sample_rate_divisor: u16, sampler: S, ) where - S: FnMut(&[[i16; 1]]) -> SamplerState, + S: FnMut(&[[i16; 1]]) -> CallbackResult, { self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler).await; } @@ -658,6 +664,10 @@ pub(crate) mod sealed { /// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal. pub trait Input: sealed::Input + Into + Peripheral

+ Sized + 'static { + /// Convert this SAADC input to a type-erased `AnyInput`. + /// + /// This allows using several inputs in situations that might require + /// them to be the same type, like putting them in an array. fn degrade_saadc(self) -> AnyInput { AnyInput { channel: self.channel(), @@ -665,6 +675,10 @@ pub trait Input: sealed::Input + Into + Peripheral

+ Sized + } } +/// A type-erased SAADC input. +/// +/// This allows using several inputs in situations that might require +/// them to be the same type, like putting them in an array. pub struct AnyInput { channel: InputChannel, } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 7bb4e39f7..17e435787 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,3 +1,5 @@ +//! Serial Peripheral Instance in master mode (SPIM) driver. + #![macro_use] use core::future::poll_fn; @@ -16,27 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; +/// SPIM error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// RX buffer was too long. RxBufferTooLong, /// EasyDMA can only read from data memory, read only buffers in flash will fail. - DMABufferNotInDataMemory, + BufferNotInRAM, } -/// Interface for the SPIM peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// SPIM driver. pub struct Spim<'d, T: Instance> { _p: PeripheralRef<'d, T>, } +/// SPIM configuration. #[non_exhaustive] pub struct Config { + /// Frequency pub frequency: Frequency, + + /// SPI mode pub mode: Mode, + + /// Overread character. + /// + /// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer, + /// this byte will be transmitted in the MOSI line for the left-over bytes. pub orc: u8, } @@ -51,6 +63,7 @@ impl Default for Config { } impl<'d, T: Instance> Spim<'d, T> { + /// Create a new SPIM driver. pub fn new( spim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -70,6 +83,7 @@ impl<'d, T: Instance> Spim<'d, T> { ) } + /// Create a new SPIM driver, capable of TX only (MOSI only). pub fn new_txonly( spim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -81,6 +95,7 @@ impl<'d, T: Instance> Spim<'d, T> { Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config) } + /// Create a new SPIM driver, capable of RX only (MISO only). pub fn new_rxonly( spim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -194,7 +209,7 @@ impl<'d, T: Instance> Spim<'d, T> { } fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { - slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable // slice can only be built from data located in RAM. @@ -236,7 +251,7 @@ impl<'d, T: Instance> Spim<'d, T> { fn blocking_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { match self.blocking_inner_from_ram(rx, tx) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying SPIM 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); @@ -268,7 +283,7 @@ impl<'d, T: Instance> Spim<'d, T> { async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { match self.async_inner_from_ram(rx, tx).await { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying SPIM 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); @@ -385,7 +400,9 @@ pub(crate) mod sealed { } } +/// SPIM peripheral instance pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -437,7 +454,7 @@ mod eh1 { match *self { Self::TxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, Self::RxBufferTooLong => embedded_hal_1::spi::ErrorKind::Other, - Self::DMABufferNotInDataMemory => embedded_hal_1::spi::ErrorKind::Other, + Self::BufferNotInRAM => embedded_hal_1::spi::ErrorKind::Other, } } } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 44af61a19..1b7436477 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -1,3 +1,5 @@ +//! Serial Peripheral Instance in slave mode (SPIS) driver. + #![macro_use] use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; @@ -14,28 +16,43 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; +/// SPIS error #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// RX buffer was too long. RxBufferTooLong, /// EasyDMA can only read from data memory, read only buffers in flash will fail. - DMABufferNotInDataMemory, + BufferNotInRAM, } -/// Interface for the SPIS peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// SPIS driver. pub struct Spis<'d, T: Instance> { _p: PeripheralRef<'d, T>, } +/// SPIS configuration. #[non_exhaustive] pub struct Config { + /// SPI mode pub mode: Mode, + + /// Overread character. + /// + /// If the master keeps clocking the bus after all the bytes in the TX buffer have + /// already been transmitted, this byte will be constantly transmitted in the MISO line. pub orc: u8, + + /// Default byte. + /// + /// This is the byte clocked out in the MISO line for ignored transactions (if the master + /// sets CSN low while the semaphore is owned by the firmware) pub def: u8, + + /// Automatically make the firmware side acquire the semaphore on transfer end. pub auto_acquire: bool, } @@ -51,6 +68,7 @@ impl Default for Config { } impl<'d, T: Instance> Spis<'d, T> { + /// Create a new SPIS driver. pub fn new( spis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -72,6 +90,7 @@ impl<'d, T: Instance> Spis<'d, T> { ) } + /// Create a new SPIS driver, capable of TX only (MISO only). pub fn new_txonly( spis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -92,6 +111,7 @@ impl<'d, T: Instance> Spis<'d, T> { ) } + /// Create a new SPIS driver, capable of RX only (MOSI only). pub fn new_rxonly( spis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -212,7 +232,7 @@ impl<'d, T: Instance> Spis<'d, T> { } fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { - slice_in_ram_or(tx, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable // slice can only be built from data located in RAM. @@ -267,7 +287,7 @@ impl<'d, T: Instance> Spis<'d, T> { 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) => { + Err(Error::BufferNotInRAM) => { 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); @@ -330,7 +350,7 @@ impl<'d, T: Instance> Spis<'d, T> { 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) => { + Err(Error::BufferNotInRAM) => { 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); @@ -468,7 +488,9 @@ pub(crate) mod sealed { } } +/// SPIS peripheral instance pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 7a7f61b51..5298faabc 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -1,4 +1,4 @@ -//! Temperature sensor interface. +//! Builtin temperature sensor driver. use core::future::poll_fn; use core::task::Poll; @@ -12,7 +12,7 @@ use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; use crate::{interrupt, pac, Peripheral}; -/// Integrated temperature sensor. +/// Builtin temperature sensor driver. pub struct Temp<'d> { _irq: PeripheralRef<'d, interrupt::TEMP>, } @@ -20,6 +20,7 @@ pub struct Temp<'d> { static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Temp<'d> { + /// Create a new temperature sensor driver. pub fn new(_t: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { into_ref!(_t, irq); diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index bc8710640..d1ae57237 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -1,3 +1,9 @@ +//! Timer driver. +//! +//! Important note! This driver is very low level. For most time-related use cases, like +//! "sleep for X seconds", "do something every X seconds", or measuring time, you should +//! use [`embassy-time`](https://crates.io/crates/embassy-time) instead! + #![macro_use] use core::future::poll_fn; @@ -28,9 +34,13 @@ pub(crate) mod sealed { pub trait TimerType {} } +/// Basic Timer instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } + +/// Extended timer instance. pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} macro_rules! impl_timer { @@ -61,18 +71,28 @@ macro_rules! impl_timer { }; } +/// Timer frequency #[repr(u8)] pub enum Frequency { - // I'd prefer not to prefix these with `F`, but Rust identifiers can't start with digits. + /// 16MHz F16MHz = 0, + /// 8MHz F8MHz = 1, + /// 4MHz F4MHz = 2, + /// 2MHz F2MHz = 3, + /// 1MHz F1MHz = 4, + /// 500kHz F500kHz = 5, + /// 250kHz F250kHz = 6, + /// 125kHz F125kHz = 7, + /// 62500Hz F62500Hz = 8, + /// 31250Hz F31250Hz = 9, } @@ -86,7 +106,10 @@ pub enum Frequency { pub trait TimerType: sealed::TimerType {} +/// Marker type indicating the timer driver can await expiration (it owns the timer interrupt). pub enum Awaitable {} + +/// Marker type indicating the timer driver cannot await expiration (it does not own the timer interrupt). pub enum NotAwaitable {} impl sealed::TimerType for Awaitable {} @@ -94,12 +117,14 @@ impl sealed::TimerType for NotAwaitable {} impl TimerType for Awaitable {} impl TimerType for NotAwaitable {} +/// Timer driver. pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { _p: PeripheralRef<'d, T>, _i: PhantomData, } impl<'d, T: Instance> Timer<'d, T, Awaitable> { + /// Create a new async-capable timer driver. pub fn new_awaitable(timer: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { into_ref!(irq); @@ -107,16 +132,17 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> { irq.unpend(); irq.enable(); - Self::new_irqless(timer) + Self::new_inner(timer) } } + impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { - /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. + /// Create a `Timer` driver without an interrupt, meaning `Cc::wait` won't work. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. pub fn new(timer: impl Peripheral

+ 'd) -> Self { - Self::new_irqless(timer) + Self::new_inner(timer) } } @@ -124,7 +150,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. /// /// This is used by the public constructors. - fn new_irqless(timer: impl Peripheral

+ 'd) -> Self { + fn new_inner(timer: impl Peripheral

+ 'd) -> Self { into_ref!(timer); let regs = T::regs(); diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 4eafd18c2..0dcb2b0da 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -1,11 +1,7 @@ +//! I2C-compatible Two Wire Interface in master mode (TWIM) driver. + #![macro_use] -//! HAL interface to the TWIM 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; @@ -23,22 +19,39 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{gpio, pac, Peripheral}; +/// TWI frequency #[derive(Clone, Copy)] pub enum Frequency { - #[doc = "26738688: 100 kbps"] + /// 100 kbps K100 = 26738688, - #[doc = "67108864: 250 kbps"] + /// 250 kbps K250 = 67108864, - #[doc = "104857600: 400 kbps"] + /// 400 kbps K400 = 104857600, } +/// TWIM config. #[non_exhaustive] pub struct Config { + /// Frequency pub frequency: Frequency, + + /// Enable high drive for the SDA line. pub sda_high_drive: bool, + + /// Enable internal pullup for the SDA line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub sda_pullup: bool, + + /// Enable high drive for the SCL line. pub scl_high_drive: bool, + + /// Enable internal pullup for the SCL line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub scl_pullup: bool, } @@ -54,29 +67,38 @@ impl Default for Config { } } +/// TWI error. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// RX buffer was too long. RxBufferTooLong, + /// Data transmit failed. Transmit, + /// Data reception failed. Receive, - DMABufferNotInDataMemory, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, + /// Didn't receive an ACK bit after the address byte. Address might be wrong, or the i2c device chip might not be connected properly. AddressNack, + /// Didn't receive an ACK bit after a data byte. DataNack, + /// Overrun error. Overrun, + /// Timeout error. Timeout, } -/// Interface to a TWIM instance using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// TWI driver. pub struct Twim<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> Twim<'d, T> { + /// Create a new TWI driver. pub fn new( twim: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -153,7 +175,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// 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)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); @@ -233,7 +255,7 @@ impl<'d, T: Instance> Twim<'d, T> { return Err(Error::DataNack); } if err.overrun().is_received() { - return Err(Error::DataNack); + return Err(Error::Overrun); } Ok(()) } @@ -435,7 +457,7 @@ impl<'d, T: Instance> Twim<'d, T> { ) -> Result<(), Error> { match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying TWIM 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); @@ -448,7 +470,7 @@ impl<'d, T: Instance> Twim<'d, T> { fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { match self.setup_write_from_ram(address, wr_buffer, inten) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying TWIM 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); @@ -612,6 +634,10 @@ impl<'d, T: Instance> Twim<'d, T> { // =========================================== + /// Read from an I2C slave. + /// + /// 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 read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { self.setup_read(address, buffer, true)?; self.async_wait().await; @@ -621,6 +647,10 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Write to an I2C slave. + /// + /// 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 write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { self.setup_write(address, buffer, true)?; self.async_wait().await; @@ -640,6 +670,11 @@ impl<'d, T: Instance> Twim<'d, T> { Ok(()) } + /// Write data to an I2C slave, then read data from the slave without + /// triggering a stop condition between the two. + /// + /// The buffers must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { self.setup_write_read(address, wr_buffer, rd_buffer, true)?; self.async_wait().await; @@ -705,7 +740,9 @@ pub(crate) mod sealed { } } +/// TWIM peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -776,7 +813,7 @@ mod eh1 { Self::RxBufferTooLong => embedded_hal_1::i2c::ErrorKind::Other, Self::Transmit => embedded_hal_1::i2c::ErrorKind::Other, Self::Receive => embedded_hal_1::i2c::ErrorKind::Other, - Self::DMABufferNotInDataMemory => embedded_hal_1::i2c::ErrorKind::Other, + Self::BufferNotInRAM => embedded_hal_1::i2c::ErrorKind::Other, Self::AddressNack => { embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address) } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 4091b017e..51a70c308 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -1,11 +1,7 @@ +//! I2C-compatible Two Wire Interface in slave mode (TWIM) driver. + #![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; @@ -22,14 +18,37 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::util::slice_in_ram_or; use crate::{gpio, pac, Peripheral}; +/// TWIS config. #[non_exhaustive] pub struct Config { + /// First address pub address0: u8, + + /// Second address, optional. pub address1: Option, + + /// Overread character. + /// + /// If the master keeps clocking the bus after all the bytes in the TX buffer have + /// already been transmitted, this byte will be constantly transmitted. pub orc: u8, + + /// Enable high drive for the SDA line. pub sda_high_drive: bool, + + /// Enable internal pullup for the SDA line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub sda_pullup: bool, + + /// Enable high drive for the SCL line. pub scl_high_drive: bool, + + /// Enable internal pullup for the SCL line. + /// + /// Note that using external pullups is recommended for I2C, and + /// most boards already have them. pub scl_pullup: bool, } @@ -54,36 +73,48 @@ enum Status { Write, } +/// TWIS error. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// TX buffer was too long. TxBufferTooLong, + /// TX buffer was too long. RxBufferTooLong, + /// Didn't receive an ACK bit after a data byte. DataNack, + /// Bus error. Bus, - DMABufferNotInDataMemory, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, + /// Overflow Overflow, + /// Overread OverRead, + /// Timeout Timeout, } +/// Received command #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Command { + /// Read Read, + /// Write+read WriteRead(usize), + /// Write 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. +/// TWIS driver. pub struct Twis<'d, T: Instance> { _p: PeripheralRef<'d, T>, } impl<'d, T: Instance> Twis<'d, T> { + /// Create a new TWIS driver. pub fn new( twis: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -174,7 +205,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// 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)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); @@ -535,7 +566,7 @@ impl<'d, T: Instance> Twis<'d, T> { 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) => { + Err(Error::BufferNotInRAM) => { 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); @@ -737,7 +768,9 @@ pub(crate) mod sealed { } } +/// TWIS peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 031cf82fd..48457744b 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1,8 +1,6 @@ -#![macro_use] - -//! Async UART +//! Universal Asynchronous Receiver Transmitter (UART) driver. //! -//! Async UART is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte]. +//! The UART driver is provided in two flavors - this one and also [crate::buffered_uarte::BufferedUarte]. //! The [Uarte] here is useful for those use-cases where reading the UARTE peripheral is //! exclusively awaited on. If the [Uarte] is required to be awaited on with some other future, //! for example when using `futures_util::future::select`, then you should consider @@ -13,6 +11,8 @@ //! memory may be used given that buffers are passed in directly to its read and write //! methods. +#![macro_use] + use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -32,10 +32,13 @@ use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; use crate::{pac, Peripheral}; +/// UARTE config. #[derive(Clone)] #[non_exhaustive] pub struct Config { + /// Parity bit. pub parity: Parity, + /// Baud rate. pub baudrate: Baudrate, } @@ -48,31 +51,33 @@ impl Default for Config { } } +/// UART error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { + /// Buffer was too long. BufferTooLong, - DMABufferNotInDataMemory, - // TODO: add other error variants. + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, } -/// Interface to the UARTE peripheral using EasyDMA to offload the transmission and reception workload. -/// -/// For more details about EasyDMA, consult the module documentation. +/// UARTE driver. pub struct Uarte<'d, T: Instance> { tx: UarteTx<'d, T>, rx: UarteRx<'d, T>, } -/// Transmitter interface to the UARTE peripheral obtained -/// via [Uarte]::split. +/// Transmitter part of the UARTE driver. +/// +/// This can be obtained via [`Uarte::split`], or created directly. pub struct UarteTx<'d, T: Instance> { _p: PeripheralRef<'d, T>, } -/// Receiver interface to the UARTE peripheral obtained -/// via [Uarte]::split. +/// Receiver part of the UARTE driver. +/// +/// This can be obtained via [`Uarte::split`], or created directly. pub struct UarteRx<'d, T: Instance> { _p: PeripheralRef<'d, T>, } @@ -165,16 +170,16 @@ impl<'d, T: Instance> Uarte<'d, T> { } } - /// Split the Uarte into a transmitter and receiver, which is - /// particularly useful when having two tasks correlating to - /// transmitting and receiving. + /// Split the Uarte into the transmitter and receiver parts. + /// + /// This is useful to concurrently transmit and receive from independent tasks. pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { (self.tx, self.rx) } - /// Split the Uarte into a transmitter and receiver that will - /// return on idle, which is determined as the time it takes - /// for two bytes to be received. + /// Split the Uarte into the transmitter and receiver with idle support parts. + /// + /// This is useful to concurrently transmit and receive from independent tasks. pub fn split_with_idle( self, timer: impl Peripheral

+ 'd, @@ -247,10 +252,12 @@ impl<'d, T: Instance> Uarte<'d, T> { } } + /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.read(buffer).await } + /// Write all bytes in the buffer. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.write(buffer).await } @@ -260,10 +267,12 @@ impl<'d, T: Instance> Uarte<'d, T> { self.tx.write_from_ram(buffer).await } + /// Read bytes until the buffer is filled. pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.blocking_read(buffer) } + /// Write all bytes in the buffer. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write(buffer) } @@ -355,10 +364,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { Self { _p: uarte } } + /// Write all bytes in the buffer. pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { match self.write_from_ram(buffer).await { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying UARTE tx buffer into RAM for DMA"); let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; ram_buf.copy_from_slice(buffer); @@ -368,12 +378,13 @@ impl<'d, T: Instance> UarteTx<'d, T> { } } + /// Same as [`write`](Self::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); } - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -423,10 +434,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { Ok(()) } + /// Write all bytes in the buffer. pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { match self.blocking_write_from_ram(buffer) { Ok(_) => Ok(()), - Err(Error::DMABufferNotInDataMemory) => { + Err(Error::BufferNotInRAM) => { trace!("Copying UARTE tx buffer into RAM for DMA"); let ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..buffer.len()]; ram_buf.copy_from_slice(buffer); @@ -436,12 +448,13 @@ impl<'d, T: Instance> UarteTx<'d, T> { } } + /// Same as [`write_from_ram`](Self::write_from_ram) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_from_ram(&mut self, buffer: &[u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); } - slice_in_ram_or(buffer, Error::DMABufferNotInDataMemory)?; + slice_in_ram_or(buffer, Error::BufferNotInRAM)?; if buffer.len() > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } @@ -549,6 +562,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { Self { _p: uarte } } + /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); @@ -602,6 +616,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { Ok(()) } + /// Read bytes until the buffer is filled. pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { return Ok(()); @@ -653,6 +668,9 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { } } +/// Receiver part of the UARTE driver, with `read_until_idle` support. +/// +/// This can be obtained via [`Uarte::split_with_idle`]. pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { rx: UarteRx<'d, T>, timer: Timer<'d, U>, @@ -661,16 +679,21 @@ pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { } impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { + /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.ppi_ch1.disable(); self.rx.read(buffer).await } + /// Read bytes until the buffer is filled. pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.ppi_ch1.disable(); self.rx.blocking_read(buffer) } + /// Read bytes until the buffer is filled, or the line becomes idle. + /// + /// Returns the amount of bytes read. pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { return Ok(0); @@ -727,6 +750,9 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { Ok(n) } + /// Read bytes until the buffer is filled, or the line becomes idle. + /// + /// Returns the amount of bytes read. pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result { if buffer.len() == 0 { return Ok(0); @@ -860,7 +886,9 @@ pub(crate) mod sealed { } } +/// UARTE peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -919,7 +947,7 @@ mod eh1 { fn kind(&self) -> embedded_hal_1::serial::ErrorKind { match *self { Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, - Self::DMABufferNotInDataMemory => embedded_hal_1::serial::ErrorKind::Other, + Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other, } } } diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index f030b909f..cd142f00f 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,3 +1,5 @@ +//! Universal Serial Bus (USB) driver. + #![macro_use] use core::future::poll_fn; @@ -24,38 +26,38 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); +/// Trait for detecting USB VBUS power. +/// /// There are multiple ways to detect USB power. The behavior /// here provides a hook into determining whether it is. -pub trait UsbSupply { +pub trait VbusDetect { + /// Report whether power is detected. + /// + /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the + /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. fn is_usb_detected(&self) -> bool; + + /// Wait until USB power is ready. + /// + /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the + /// `USBPWRRDY` event from the `POWER` peripheral. async fn wait_power_ready(&mut self) -> Result<(), ()>; } -pub struct Driver<'d, T: Instance, P: UsbSupply> { - _p: PeripheralRef<'d, T>, - alloc_in: Allocator, - alloc_out: Allocator, - usb_supply: P, -} - -/// Uses the POWER peripheral to detect when power is available -/// for USB. Unsuitable for usage with the nRF softdevice. +/// [`VbusDetect`] implementation using the native hardware POWER peripheral. +/// +/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces +/// to POWER. In that case, use [`VbusDetectSignal`]. #[cfg(not(feature = "_nrf5340-app"))] -pub struct PowerUsb { +pub struct HardwareVbusDetect { _private: (), } -/// Can be used to signal that power is available. Particularly suited for -/// use with the nRF softdevice. -pub struct SignalledSupply { - usb_detected: AtomicBool, - power_ready: AtomicBool, -} - static POWER_WAKER: AtomicWaker = NEW_AW; #[cfg(not(feature = "_nrf5340-app"))] -impl PowerUsb { +impl HardwareVbusDetect { + /// Create a new `VbusDetectNative`. pub fn new(power_irq: impl Interrupt) -> Self { let regs = unsafe { &*pac::POWER::ptr() }; @@ -92,7 +94,7 @@ impl PowerUsb { } #[cfg(not(feature = "_nrf5340-app"))] -impl UsbSupply for PowerUsb { +impl VbusDetect for HardwareVbusDetect { fn is_usb_detected(&self) -> bool { let regs = unsafe { &*pac::POWER::ptr() }; regs.usbregstatus.read().vbusdetect().is_vbus_present() @@ -115,7 +117,20 @@ impl UsbSupply for PowerUsb { } } -impl SignalledSupply { +/// Software-backed [`VbusDetect`] implementation. +/// +/// This implementation does not interact with the hardware, it allows user code +/// to notify the power events by calling functions instead. +/// +/// This is suitable for use with the nRF softdevice, by calling the functions +/// when the softdevice reports power-related events. +pub struct SoftwareVbusDetect { + usb_detected: AtomicBool, + power_ready: AtomicBool, +} + +impl SoftwareVbusDetect { + /// Create a new `SoftwareVbusDetect`. pub fn new(usb_detected: bool, power_ready: bool) -> Self { BUS_WAKER.wake(); @@ -125,6 +140,9 @@ impl SignalledSupply { } } + /// Report whether power was detected. + /// + /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. pub fn detected(&self, detected: bool) { self.usb_detected.store(detected, Ordering::Relaxed); self.power_ready.store(false, Ordering::Relaxed); @@ -132,13 +150,16 @@ impl SignalledSupply { POWER_WAKER.wake(); } + /// Report when USB power is ready. + /// + /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. pub fn ready(&self) { self.power_ready.store(true, Ordering::Relaxed); POWER_WAKER.wake(); } } -impl UsbSupply for &SignalledSupply { +impl VbusDetect for &SoftwareVbusDetect { fn is_usb_detected(&self) -> bool { self.usb_detected.load(Ordering::Relaxed) } @@ -159,7 +180,16 @@ impl UsbSupply for &SignalledSupply { } } -impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> { +/// USB driver. +pub struct Driver<'d, T: Instance, P: VbusDetect> { + _p: PeripheralRef<'d, T>, + alloc_in: Allocator, + alloc_out: Allocator, + usb_supply: P, +} + +impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { + /// Create a new USB driver. pub fn new(usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, usb_supply: P) -> Self { into_ref!(usb, irq); irq.set_handler(Self::on_interrupt); @@ -225,7 +255,7 @@ impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> { } } -impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> { +impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> { type EndpointOut = Endpoint<'d, T, Out>; type EndpointIn = Endpoint<'d, T, In>; type ControlPipe = ControlPipe<'d, T>; @@ -278,13 +308,14 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> } } -pub struct Bus<'d, T: Instance, P: UsbSupply> { +/// USB bus. +pub struct Bus<'d, T: Instance, P: VbusDetect> { _p: PeripheralRef<'d, T>, power_available: bool, usb_supply: P, } -impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { +impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { async fn enable(&mut self) { let regs = T::regs(); @@ -513,7 +544,10 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { } } +/// Type-level marker for OUT endpoints. pub enum Out {} + +/// Type-level marker for IN endpoints. pub enum In {} trait EndpointDir { @@ -556,6 +590,7 @@ impl EndpointDir for Out { } } +/// USB endpoint. pub struct Endpoint<'d, T: Instance, Dir> { _phantom: PhantomData<(&'d mut T, Dir)>, info: EndpointInfo, @@ -715,6 +750,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { } } +/// USB control pipe. pub struct ControlPipe<'d, T: Instance> { _p: PeripheralRef<'d, T>, max_packet_size: u16, @@ -905,7 +941,9 @@ pub(crate) mod sealed { } } +/// USB peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. type Interrupt: Interrupt; } diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index 330ca98bf..40a674424 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -1,4 +1,4 @@ -//! HAL interface to the WDT peripheral. +//! Watchdog Timer (WDT) driver. //! //! This HAL implements a basic watchdog timer with 1..=8 handles. //! Once the watchdog has been started, it cannot be stopped. @@ -8,6 +8,7 @@ use crate::peripherals; const MIN_TICKS: u32 = 15; +/// WDT configuration. #[non_exhaustive] pub struct Config { /// Number of 32768 Hz ticks in each watchdog period. @@ -57,13 +58,13 @@ impl Default for Config { } } -/// An interface to the Watchdog. +/// Watchdog driver. pub struct Watchdog { _private: (), } impl Watchdog { - /// Try to create a new watchdog instance from the peripheral. + /// Try to create a new watchdog driver. /// /// This function will return an error if the watchdog is already active /// with a `config` different to the requested one, or a different number of @@ -155,6 +156,7 @@ impl Watchdog { } } +/// Watchdog handle. pub struct WatchdogHandle { index: u8, } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index bbd8a5d2f..95d939873 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -14,7 +14,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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"] } -embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = "0.4.0" diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs index 3cca005b1..52d46e4f9 100644 --- a/examples/nrf52840/src/bin/i2s_effect.rs +++ b/examples/nrf52840/src/bin/i2s_effect.rs @@ -24,9 +24,9 @@ async fn main(_spawner: Spawner) { let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - let config = Config::default() - .sample_width(SampleWidth::_16bit) - .channels(Channels::MonoLeft); + let mut config = Config::default(); + config.sample_width = SampleWidth::_16bit; + config.channels = Channels::MonoLeft; let irq = interrupt::take!(I2S); let buffers_out = MultiBuffering::::new(); diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs index 48eb7d581..5ebfd9542 100644 --- a/examples/nrf52840/src/bin/i2s_monitor.rs +++ b/examples/nrf52840/src/bin/i2s_monitor.rs @@ -22,9 +22,9 @@ async fn main(_spawner: Spawner) { let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - let config = Config::default() - .sample_width(SampleWidth::_16bit) - .channels(Channels::MonoLeft); + let mut config = Config::default(); + config.sample_width = SampleWidth::_16bit; + config.channels = Channels::MonoLeft; let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs index 1b0e8ebc8..eda930677 100644 --- a/examples/nrf52840/src/bin/i2s_waveform.rs +++ b/examples/nrf52840/src/bin/i2s_waveform.rs @@ -23,9 +23,9 @@ async fn main(_spawner: Spawner) { let sample_rate = master_clock.sample_rate(); info!("Sample rate: {}", sample_rate); - let config = Config::default() - .sample_width(SampleWidth::_16bit) - .channels(Channels::MonoLeft); + let mut config = Config::default(); + config.sample_width = SampleWidth::_16bit; + config.channels = Channels::MonoLeft; let irq = interrupt::take!(I2S); let buffers = DoubleBuffering::::new(); diff --git a/examples/nrf52840/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index bb50ac65e..2551d15fd 100644 --- a/examples/nrf52840/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs @@ -5,7 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_nrf::interrupt; -use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; +use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc}; use embassy_nrf::timer::Frequency; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; @@ -61,7 +61,7 @@ async fn main(_p: Spawner) { c = 0; a = 0; } - SamplerState::Sampled + CallbackResult::Continue }, ) .await; diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index a8d53e460..699666cee 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -9,7 +9,7 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::{Driver, PowerUsb}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac, peripherals}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; @@ -18,7 +18,7 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; +type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; macro_rules! singleton { ($val:expr) => {{ @@ -58,7 +58,7 @@ async fn main(spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 76e198719..017cac197 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -10,7 +10,7 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::usb::{Driver, PowerUsb}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; @@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 4916a38d4..a5849129a 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, PowerUsb}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 7c9c4184b..18b6f25b9 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -97,7 +97,7 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( +async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 93efc2fe6..3532d3f82 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -6,7 +6,7 @@ use core::mem; use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; -use embassy_nrf::usb::{Driver, PowerUsb}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect}; use embassy_nrf::{interrupt, pac, peripherals}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -14,7 +14,7 @@ use embassy_usb::{Builder, Config, UsbDevice}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; +type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { @@ -42,7 +42,7 @@ async fn main(spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); From a697f1517a9c54ba042bbf70e0b2ed762d300471 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 17:29:34 -0600 Subject: [PATCH 0538/1575] Set `poll_fn` in `TaskStorage::new` --- embassy-executor/src/raw/mod.rs | 35 +++++++++++++------------------- embassy-executor/src/raw/util.rs | 6 ------ 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 183c5e6a2..8cdce92ec 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -46,8 +46,8 @@ pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 - pub(crate) poll_fn: UninitCell, // Valid if STATE_SPAWNED + pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 + poll_fn: unsafe fn(TaskRef), #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Cell, @@ -55,22 +55,6 @@ pub(crate) struct TaskHeader { pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } -impl TaskHeader { - const fn new() -> Self { - Self { - state: AtomicU32::new(0), - run_queue_item: RunQueueItem::new(), - executor: Cell::new(ptr::null()), - poll_fn: UninitCell::uninit(), - - #[cfg(feature = "integrated-timers")] - expires_at: Cell::new(Instant::from_ticks(0)), - #[cfg(feature = "integrated-timers")] - timer_queue_item: timer_queue::TimerQueueItem::new(), - } - } -} - /// This is essentially a `&'static TaskStorage` where the type of the future has been erased. #[derive(Clone, Copy)] pub struct TaskRef { @@ -128,7 +112,17 @@ impl TaskStorage { /// Create a new TaskStorage, in not-spawned state. pub const fn new() -> Self { Self { - raw: TaskHeader::new(), + raw: TaskHeader { + state: AtomicU32::new(0), + run_queue_item: RunQueueItem::new(), + executor: Cell::new(ptr::null()), + poll_fn: Self::poll, + + #[cfg(feature = "integrated-timers")] + expires_at: Cell::new(Instant::from_ticks(0)), + #[cfg(feature = "integrated-timers")] + timer_queue_item: timer_queue::TimerQueueItem::new(), + }, future: UninitCell::uninit(), } } @@ -164,7 +158,6 @@ impl TaskStorage { unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> TaskRef { // Initialize the task - self.raw.poll_fn.write(Self::poll); self.future.write(future()); TaskRef::new(self) } @@ -405,7 +398,7 @@ impl Executor { trace::task_exec_begin(p.as_ptr() as u32); // Run the task - task.poll_fn.read()(p); + (task.poll_fn)(p); #[cfg(feature = "rtos-trace")] trace::task_exec_end(); diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index ed5822188..2b1f6b6f3 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -25,9 +25,3 @@ impl UninitCell { ptr::drop_in_place(self.as_mut_ptr()) } } - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} From fb1946be7fa38eecb36711a1257f89dae3714b61 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 17:49:18 -0600 Subject: [PATCH 0539/1575] Replace the pointer in `TaskHeader` with an `Option<&Executor>` --- embassy-executor/src/raw/mod.rs | 10 +++++----- embassy-executor/src/spawner.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 8cdce92ec..6783c4853 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -15,10 +15,10 @@ mod waker; use core::cell::Cell; use core::future::Future; +use core::mem; use core::pin::Pin; use core::ptr::NonNull; use core::task::{Context, Poll}; -use core::{mem, ptr}; use atomic_polyfill::{AtomicU32, Ordering}; use critical_section::CriticalSection; @@ -46,7 +46,7 @@ pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell<*const Executor>, // Valid if state != 0 + pub(crate) executor: Cell>, poll_fn: unsafe fn(TaskRef), #[cfg(feature = "integrated-timers")] @@ -115,7 +115,7 @@ impl TaskStorage { raw: TaskHeader { state: AtomicU32::new(0), run_queue_item: RunQueueItem::new(), - executor: Cell::new(ptr::null()), + executor: Cell::new(None), poll_fn: Self::poll, #[cfg(feature = "integrated-timers")] @@ -346,7 +346,7 @@ impl Executor { /// In this case, the task's Future must be Send. This is because this is effectively /// sending the task to the executor thread. pub(super) unsafe fn spawn(&'static self, task: TaskRef) { - task.header().executor.set(self); + task.header().executor.set(Some(self)); #[cfg(feature = "rtos-trace")] trace::task_new(task.as_ptr() as u32); @@ -455,7 +455,7 @@ pub fn wake_task(task: TaskRef) { // We have just marked the task as scheduled, so enqueue it. unsafe { - let executor = &*header.executor.get(); + let executor = header.executor.get().unwrap_unchecked(); executor.enqueue(cs, task); } }) diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 650ea06cb..7c0a0183c 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -89,10 +89,10 @@ impl Spawner { /// /// Panics if the current executor is not an Embassy executor. pub async fn for_current_executor() -> Self { - poll_fn(|cx| unsafe { + poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = task.header().executor.get(); - Poll::Ready(Self::new(&*executor)) + let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; + Poll::Ready(Self::new(executor)) }) .await } @@ -165,10 +165,10 @@ impl SendSpawner { /// /// Panics if the current executor is not an Embassy executor. pub async fn for_current_executor() -> Self { - poll_fn(|cx| unsafe { + poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = task.header().executor.get(); - Poll::Ready(Self::new(&*executor)) + let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; + Poll::Ready(Self::new(executor)) }) .await } From 4a8e9cf4d9f682bfe4942559da7e76315216c377 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 18:49:32 -0600 Subject: [PATCH 0540/1575] Add internal `AvailableTask` type --- embassy-executor/src/raw/mod.rs | 68 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 6783c4853..e93e60362 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -141,25 +141,14 @@ impl TaskStorage { /// Once the task has finished running, you may spawn it again. It is allowed to spawn it /// on a different executor. pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - if self.spawn_mark_used() { - return unsafe { SpawnToken::::new(self.spawn_initialize(future)) }; + let task = AvailableTask::claim(self); + match task { + Some(task) => { + let task = task.initialize(future); + unsafe { SpawnToken::::new(task) } + } + None => SpawnToken::new_failed(), } - - SpawnToken::::new_failed() - } - - fn spawn_mark_used(&'static self) -> bool { - let state = STATE_SPAWNED | STATE_RUN_QUEUED; - self.raw - .state - .compare_exchange(0, state, Ordering::AcqRel, Ordering::Acquire) - .is_ok() - } - - unsafe fn spawn_initialize(&'static self, future: impl FnOnce() -> F) -> TaskRef { - // Initialize the task - self.future.write(future()); - TaskRef::new(self) } unsafe fn poll(p: TaskRef) { @@ -184,6 +173,27 @@ impl TaskStorage { unsafe impl Sync for TaskStorage {} +struct AvailableTask { + task: &'static TaskStorage, +} + +impl AvailableTask { + fn claim(task: &'static TaskStorage) -> Option { + task.raw + .state + .compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire) + .ok() + .map(|_| Self { task }) + } + + fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { + unsafe { + self.task.future.write(future()); + } + TaskRef::new(self.task) + } +} + /// Raw storage that can hold up to N tasks of the same type. /// /// This is essentially a `[TaskStorage; N]`. @@ -207,13 +217,14 @@ impl TaskPool { /// is currently free. If none is free, a "poisoned" SpawnToken is returned, /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - for task in &self.pool { - if task.spawn_mark_used() { - return unsafe { SpawnToken::::new(task.spawn_initialize(future)) }; + let task = self.pool.iter().find_map(AvailableTask::claim); + match task { + Some(task) => { + let task = task.initialize(future); + unsafe { SpawnToken::::new(task) } } + None => SpawnToken::new_failed(), } - - SpawnToken::::new_failed() } /// Like spawn(), but allows the task to be send-spawned if the args are Send even if @@ -255,13 +266,14 @@ impl TaskPool { // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken`. - for task in &self.pool { - if task.spawn_mark_used() { - return SpawnToken::::new(task.spawn_initialize(future)); + let task = self.pool.iter().find_map(AvailableTask::claim); + match task { + Some(task) => { + let task = task.initialize(future); + unsafe { SpawnToken::::new(task) } } + None => SpawnToken::new_failed(), } - - SpawnToken::::new_failed() } } From 791fbb3ca0caf81882f67caea9e71adf43496261 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Tue, 31 Jan 2023 21:42:45 -0600 Subject: [PATCH 0541/1575] Make `poll_fn` lazily initialized again --- embassy-executor/src/raw/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e93e60362..42bd82262 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -47,7 +47,7 @@ pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, pub(crate) executor: Cell>, - poll_fn: unsafe fn(TaskRef), + poll_fn: Cell>, #[cfg(feature = "integrated-timers")] pub(crate) expires_at: Cell, @@ -116,7 +116,8 @@ impl TaskStorage { state: AtomicU32::new(0), run_queue_item: RunQueueItem::new(), executor: Cell::new(None), - poll_fn: Self::poll, + // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` + poll_fn: Cell::new(None), #[cfg(feature = "integrated-timers")] expires_at: Cell::new(Instant::from_ticks(0)), @@ -188,6 +189,7 @@ impl AvailableTask { fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { unsafe { + self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write(future()); } TaskRef::new(self.task) @@ -410,7 +412,7 @@ impl Executor { trace::task_exec_begin(p.as_ptr() as u32); // Run the task - (task.poll_fn)(p); + task.poll_fn.get().unwrap_unchecked()(p); #[cfg(feature = "rtos-trace")] trace::task_exec_end(); From cb88dd285dd53a12545a0f01a6d43aaf1361b82a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Feb 2023 20:54:32 +0100 Subject: [PATCH 0542/1575] nrf/twis: FIx doc typo --- embassy-nrf/src/twis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 51a70c308..c514d9f2f 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -80,7 +80,7 @@ enum Status { pub enum Error { /// TX buffer was too long. TxBufferTooLong, - /// TX buffer was too long. + /// RX buffer was too long. RxBufferTooLong, /// Didn't receive an ACK bit after a data byte. DataNack, From c4cbb89fcd60375c4982a61fc3d9e5183f97d748 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 2 Feb 2023 12:51:47 +0100 Subject: [PATCH 0543/1575] LoRa/SX126x: adjust Rx window offset and duration Those timings open Rx time windows covering 99.7% of the one expected by the antenna while allowing 3ms for the Rx subsystem to start listening. --- embassy-lora/src/sx126x/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index b14d422a7..a9aeadfcb 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -55,10 +55,10 @@ where BUS: Error + Format + 'static, { fn get_rx_window_offset_ms(&self) -> i32 { - -500 + -3 } fn get_rx_window_duration_ms(&self) -> u32 { - 800 + 1003 } } From ef4a20f67b925ffcbf83642e33b13d3605b78d46 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 2 Feb 2023 12:52:00 +0100 Subject: [PATCH 0544/1575] LoRa/STM32WL: adjust Rx window offset and duration Those timings open Rx time windows covering 99.7% of the one expected by the antenna while allowing 3ms for the Rx subsystem to start listening. --- embassy-lora/src/stm32wl/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 6ed63bf7b..3d52c1cc7 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -260,10 +260,10 @@ impl From for RadioError { impl<'d, RS> Timings for SubGhzRadio<'d, RS> { fn get_rx_window_offset_ms(&self) -> i32 { - -500 + -3 } fn get_rx_window_duration_ms(&self) -> u32 { - 3000 + 1003 } } From a432d91d82438f4ce0f17c85b9453ae79db5390d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 3 Feb 2023 06:36:10 +0100 Subject: [PATCH 0545/1575] PeripheralRef docs improvements. --- embassy-hal-common/src/peripheral.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-common/src/peripheral.rs index f507468f8..4a6b6a600 100644 --- a/embassy-hal-common/src/peripheral.rs +++ b/embassy-hal-common/src/peripheral.rs @@ -3,16 +3,17 @@ use core::ops::{Deref, DerefMut}; /// An exclusive reference to a peripheral. /// -/// This is functionally the same as a `&'a mut T`. The reason for having a -/// dedicated struct is memory efficiency: +/// This is functionally the same as a `&'a mut T`. There's a few advantages in having +/// a dedicated struct instead: /// -/// Peripheral singletons are typically either zero-sized (for concrete peripherals -/// like `PA9` or `Spi4`) or very small (for example `AnyPin` which is 1 byte). -/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized. -/// PeripheralRef stores a copy of `T` instead, so it's the same size. -/// -/// but it is the size of `T` not the size -/// of a pointer. This is useful if T is a zero sized type. +/// - Memory efficiency: Peripheral singletons are typically either zero-sized (for concrete +/// peripherals like `PA9` or `SPI4`) or very small (for example `AnyPin`, which is 1 byte). +/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized. +/// PeripheralRef stores a copy of `T` instead, so it's the same size. +/// - Code size efficiency. If the user uses the same driver with both `SPI4` and `&mut SPI4`, +/// the driver code would be monomorphized two times. With PeripheralRef, the driver is generic +/// over a lifetime only. `SPI4` becomes `PeripheralRef<'static, SPI4>`, and `&mut SPI4` becomes +/// `PeripheralRef<'a, SPI4>`. Lifetimes don't cause monomorphization. pub struct PeripheralRef<'a, T> { inner: T, _lifetime: PhantomData<&'a mut T>, From 0bb6000e5cab0a2f2dbbb95a4a2d69a2536de88b Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Thu, 2 Feb 2023 21:42:42 -0800 Subject: [PATCH 0546/1575] stm32 gpio implement degrade to AnyPin --- embassy-stm32/src/gpio.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 5e3346754..3024f1ffa 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -28,6 +28,21 @@ impl<'d, T: Pin> Flex<'d, T> { Self { pin } } + #[inline] + pub fn degrade(mut self) -> Flex<'d, AnyPin> { + // Safety: We are about to drop the other copy of this pin, so + // this clone is safe. + let pin = unsafe { self.pin.clone_unchecked() }; + + // We don't want to run the destructor here, because that would + // deconfigure the pin. + core::mem::forget(self); + + Flex { + pin: pin.map_into::(), + } + } + /// Put the pin into input mode. #[inline] pub fn set_as_input(&mut self, pull: Pull) { @@ -286,6 +301,13 @@ impl<'d, T: Pin> Input<'d, T> { Self { pin } } + #[inline] + pub fn degrade(self) -> Input<'d, AnyPin> { + Input { + pin: self.pin.degrade(), + } + } + #[inline] pub fn is_high(&self) -> bool { self.pin.is_high() @@ -345,6 +367,13 @@ impl<'d, T: Pin> Output<'d, T> { Self { pin } } + #[inline] + pub fn degrade(self) -> Output<'d, AnyPin> { + Output { + pin: self.pin.degrade(), + } + } + /// Set the output as high. #[inline] pub fn set_high(&mut self) { @@ -407,6 +436,13 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { Self { pin } } + #[inline] + pub fn degrade(self) -> Output<'d, AnyPin> { + Output { + pin: self.pin.degrade(), + } + } + #[inline] pub fn is_high(&self) -> bool { !self.pin.is_low() From 64ebb9b7fee1fec62eacc1064a7458a6e6b07048 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Mon, 6 Feb 2023 09:44:13 +1100 Subject: [PATCH 0547/1575] stm32/usart: implement stop_bits configuration --- embassy-stm32/src/usart/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 20f4eedeb..b18b646ba 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -770,7 +770,14 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable unsafe { r.brr().write_value(regs::Brr(div)); - r.cr2().write(|_w| {}); + r.cr2().write(|w| { + w.set_stop(match config.stop_bits { + StopBits::STOP0P5 => vals::Stop::STOP0P5, + StopBits::STOP1 => vals::Stop::STOP1, + StopBits::STOP1P5 => vals::Stop::STOP1P5, + StopBits::STOP2 => vals::Stop::STOP2, + }); + }); r.cr1().write(|w| { // enable uart w.set_ue(true); From fda36fd81b25bac914e977654beecdb41aaabbb3 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Mon, 6 Feb 2023 10:12:10 +1100 Subject: [PATCH 0548/1575] stm32/usart: fix LPUART clock multiplier According to RM0351 Rev 9 (L4) and RM0399 Rev 3 (H7): baud = (256 * clock) / LPUARTDIV --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 20f4eedeb..121699c7b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1148,7 +1148,7 @@ macro_rules! impl_lpuart { foreach_interrupt!( ($inst:ident, lpuart, $block:ident, $signal_name:ident, $irq:ident) => { - impl_lpuart!($inst, $irq, 255); + impl_lpuart!($inst, $irq, 256); }; ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => { From e3174d7a998ead0fbe5ddadf59914cc16a561a44 Mon Sep 17 00:00:00 2001 From: Ralf Date: Sun, 5 Feb 2023 20:17:53 +0100 Subject: [PATCH 0549/1575] STM32 SPI: Set clk-pin pull-up/-down to match spi clock polarity RM0394: 40.4.6 Communication formats ... The idle state of SCK must correspond to the polarity selected in the SPIx_CR1 register (by pulling up SCK if CPOL=1 or pulling down SCK if CPOL=0). --- embassy-stm32/src/spi/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 5b81c791a..e5ba746e4 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -10,7 +10,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use self::sealed::WordSize; use crate::dma::{slice_ptr_parts, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, Pull}; use crate::pac::spi::{regs, vals, Spi as Regs}; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -93,8 +93,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { config: Config, ) -> Self { into_ref!(peri, sck, mosi, miso); + + let sck_pull_mode = match config.mode.polarity { + Polarity::IdleLow => Pull::Down, + Polarity::IdleHigh => Pull::Up, + }; + unsafe { - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode); sck.set_speed(crate::gpio::Speed::VeryHigh); mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); mosi.set_speed(crate::gpio::Speed::VeryHigh); From 218f8e049096341c459de8d39146d59117d20d69 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 12:17:37 +0100 Subject: [PATCH 0550/1575] fix(stm32): Align FMC with new versions from stm32-data --- embassy-stm32/src/fmc/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 856a4adca..0c5461044 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -27,9 +27,13 @@ where } fn memory_controller_enable(&mut self) { - // The FMCEN bit of the FMC_BCR2..4 registers is don’t - // care. It is only enabled through the FMC_BCR1 register. - unsafe { T::regs().bcr1().modify(|r| r.set_fmcen(true)) }; + // fmc v1 and v2 does not have the fmcen bit + // fsmc v1 and v2 does not have the fmcen bit + // This is a "not" because it is expected that all future versions have this bit + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x3, fsmc_v2x1)))] + unsafe { + T::regs().bcr1().modify(|r| r.set_fmcen(true)) + }; } fn source_clock_hz(&self) -> u32 { From 36ca18132dd525bc2a3cdac0246834a2aae07ca9 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 12:35:59 +0100 Subject: [PATCH 0551/1575] Update stm32-data --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index 96decdd61..fe4a068ea 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit 96decdd6114d78813c1f748fb878a45e1b03bf73 +Subproject commit fe4a068eaf1b39c60eddea7d74c90182b44c5d08 From 494a76a0f1be2fb35000c7a088f9bda21c73ad9e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 14:14:47 +0100 Subject: [PATCH 0552/1575] React to updated fsmc versions --- embassy-stm32/src/fmc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 0c5461044..13c16af52 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -28,9 +28,9 @@ where fn memory_controller_enable(&mut self) { // fmc v1 and v2 does not have the fmcen bit - // fsmc v1 and v2 does not have the fmcen bit + // fsmc v1, v2 and v3 does not have the fmcen bit // This is a "not" because it is expected that all future versions have this bit - #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x3, fsmc_v2x1)))] + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v2x3, fsmc_v3x1)))] unsafe { T::regs().bcr1().modify(|r| r.set_fmcen(true)) }; From 562432ad8b9380cff7a536105b9877cdd8044870 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 14:16:13 +0100 Subject: [PATCH 0553/1575] Update stm32-data --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index fe4a068ea..ae8c8ed9d 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit fe4a068eaf1b39c60eddea7d74c90182b44c5d08 +Subproject commit ae8c8ed9dbde5eca7c5c74bd926f7ce77526dae1 From e4dc473e04ba2981cffd16ce4348f0646cae341d Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 14:46:36 +0100 Subject: [PATCH 0554/1575] Update stm32-data --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index ae8c8ed9d..cc93f9d10 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit ae8c8ed9dbde5eca7c5c74bd926f7ce77526dae1 +Subproject commit cc93f9d10395077770bebefb6b9488e06b0e5811 From 1b6aae9ddea09bf961d9cbe4a976f3021749e4ac Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 15:06:16 +0100 Subject: [PATCH 0555/1575] Also exclude fsmc_v1x3 --- embassy-stm32/src/fmc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 13c16af52..4d48721d1 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -30,7 +30,7 @@ where // fmc v1 and v2 does not have the fmcen bit // fsmc v1, v2 and v3 does not have the fmcen bit // This is a "not" because it is expected that all future versions have this bit - #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v2x3, fsmc_v3x1)))] + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))] unsafe { T::regs().bcr1().modify(|r| r.set_fmcen(true)) }; From 7b11e339bd9ce69f114778e1401cbe8279a70337 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 7 Feb 2023 16:06:59 +0100 Subject: [PATCH 0556/1575] feat(fmc): Add 16 data bit ctor --- embassy-stm32/src/fmc/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc/mod.rs index 856a4adca..a781e437a 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc/mod.rs @@ -107,6 +107,24 @@ impl<'d, T: Instance> Fmc<'d, T> { ] )); + fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank2: ( + bank: stm32_fmc::SdramTargetBank::Bank2, + addr: [ + (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin) + ], + ba: [(ba0: BA0Pin), (ba1: BA1Pin)], + d: [ + (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), + (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) + ], + nbl: [ + (nbl0: NBL0Pin), (nbl1: NBL1Pin) + ], + ctrl: [ + (sdcke: SDCKE1Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE1Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) + ] + )); + fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank2: ( bank: stm32_fmc::SdramTargetBank::Bank2, addr: [ From 102b2e52cb0b98a533ec12a1c0b93d8dc9e4f826 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 18:15:26 +0100 Subject: [PATCH 0557/1575] net: use released smoltcp 0.9.0 --- embassy-net/Cargo.toml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 15cbb5954..4ec340b7a 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -33,6 +33,12 @@ medium-ip = ["smoltcp/medium-ip"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } +smoltcp = { version = "0.9.0", default-features = false, features = [ + "proto-ipv4", + "socket", + "async", +]} + embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } @@ -47,14 +53,3 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw atomic-pool = "1.0" embedded-nal-async = { version = "0.3.0", optional = true } atomic-polyfill = { version = "1.0" } - -[dependencies.smoltcp] -version = "0.8.0" -git = "https://github.com/smoltcp-rs/smoltcp" -rev = "5740b765749b95c18aace5de8dc21cab75ba33d4" -default-features = false -features = [ - "proto-ipv4", - "socket", - "async", -] From f5ff3c4ac31c79cedf077f559dbd5685886399cc Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Thu, 12 Jan 2023 14:59:25 -0600 Subject: [PATCH 0558/1575] usb: add support for MS OS Descriptors --- embassy-usb/Cargo.toml | 1 + embassy-usb/src/builder.rs | 15 + embassy-usb/src/lib.rs | 17 + embassy-usb/src/msos.rs | 746 +++++++++++++++++++++++++++++++++++++ 4 files changed, 779 insertions(+) create mode 100644 embassy-usb/src/msos.rs diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1e567bb94..31d1f4cae 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -24,6 +24,7 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } heapless = "0.7.10" +widestring = { version = "1.0.2", default-features = false } # for HID usbd-hid = { version = "0.6.0", optional = true } diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 41b24fecf..2c42fd64e 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -130,6 +130,7 @@ pub struct Builder<'d, D: Driver<'d>> { device_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>, bos_descriptor: BosWriter<'d>, + msos_descriptor: Option>, } impl<'d, D: Driver<'d>> Builder<'d, D> { @@ -182,6 +183,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { device_descriptor, config_descriptor, bos_descriptor, + msos_descriptor: None, } } @@ -199,6 +201,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.bos_descriptor.writer.into_buf(), self.interfaces, self.control_buf, + self.msos_descriptor, ) } @@ -234,6 +237,18 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { iface_count_index, } } + + /// Add an MS OS 2.0 Descriptor Set. + /// + /// Panics if called more than once. + pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) { + if self.msos_descriptor.is_some() { + panic!("msos_descriptor already set"); + } + self.msos_descriptor + .insert(msos_descriptor) + .write_bos_capability(&mut self.bos_descriptor); + } } /// Function builder. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 2656af29d..948b8d523 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -13,6 +13,7 @@ pub mod class; pub mod control; pub mod descriptor; mod descriptor_reader; +pub mod msos; pub mod types; use embassy_futures::select::{select, Either}; @@ -135,6 +136,8 @@ struct Inner<'d, D: Driver<'d>> { set_address_pending: bool, interfaces: Vec, MAX_INTERFACE_COUNT>, + + msos_descriptor: Option>, } impl<'d, D: Driver<'d>> UsbDevice<'d, D> { @@ -147,6 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { bos_descriptor: &'d [u8], interfaces: Vec, MAX_INTERFACE_COUNT>, control_buf: &'d mut [u8], + msos_descriptor: Option>, ) -> UsbDevice<'d, D> { // Start the USB bus. // This prevent further allocation by consuming the driver. @@ -170,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { address: 0, set_address_pending: false, interfaces, + msos_descriptor, }, } } @@ -603,6 +608,18 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { None => InResponse::Rejected, } } + (RequestType::Vendor, Recipient::Device) => { + if let Some(msos) = &self.msos_descriptor { + if req.request == msos.vendor_code() && req.index == 7 { + // Index 7 retrieves the MS OS Descriptor Set + InResponse::Accepted(msos.descriptor()) + } else { + InResponse::Rejected + } + } else { + InResponse::Rejected + } + } _ => InResponse::Rejected, } } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs new file mode 100644 index 000000000..08a5074bf --- /dev/null +++ b/embassy-usb/src/msos.rs @@ -0,0 +1,746 @@ +//! Microsoft OS Descriptors +//! +//! + +#![allow(dead_code)] + +use core::mem::size_of; +use core::ops::Range; + +pub use widestring::{u16cstr, U16CStr}; + +use crate::descriptor::{capability_type, BosWriter}; +use crate::types::InterfaceNumber; + +fn write_u16>(buf: &mut [u8], range: Range, data: T) { + (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) +} + +/// A serialized Microsoft OS 2.0 Descriptor set. +/// +/// Create with [`DeviceDescriptorSetBuilder`]. +pub struct MsOsDescriptorSet<'a> { + descriptor: &'a [u8], + windows_version: u32, + vendor_code: u8, +} + +impl<'a> MsOsDescriptorSet<'a> { + pub fn descriptor(&self) -> &[u8] { + self.descriptor + } + + pub fn vendor_code(&self) -> u8 { + self.vendor_code + } + + pub fn write_bos_capability(&self, bos: &mut BosWriter) { + let windows_version = self.windows_version.to_le_bytes(); + let len = self.descriptor.len().to_le_bytes(); + bos.capability( + capability_type::PLATFORM, + &[ + 0, // reserved + // platform capability UUID, Microsoft OS 2.0 platform compabitility + 0xdf, + 0x60, + 0xdd, + 0xd8, + 0x89, + 0x45, + 0xc7, + 0x4c, + 0x9c, + 0xd2, + 0x65, + 0x9d, + 0x9e, + 0x64, + 0x8a, + 0x9f, + // Minimum compatible Windows version + windows_version[0], + windows_version[1], + windows_version[2], + windows_version[3], + // Descriptor set length + len[0], + len[1], + self.vendor_code, + 0x0, // Device does not support alternate enumeration + ], + ) + } +} + +/// A helper struct to implement the different descriptor set builders. +struct DescriptorSetBuilder<'a> { + used: usize, + buf: &'a mut [u8], +} + +impl<'a> DescriptorSetBuilder<'a> { + pub fn descriptor(&mut self, desc: T) + where + T: Descriptor + 'a, + { + let size = desc.size(); + let start = self.used; + let end = start + size; + desc.write_to(&mut self.buf[start..end]); + self.used += size; + } + + pub fn subset(&mut self, build_subset: impl FnOnce(&mut DescriptorSetBuilder<'_>)) { + self.used += { + let mut subset = DescriptorSetBuilder { + used: 0, + buf: self.remaining(), + }; + build_subset(&mut subset); + subset.used + }; + } + + pub fn remaining(&mut self) -> &mut [u8] { + &mut self.buf[self.used..] + } +} + +pub mod windows_version { + pub const WIN2K: u32 = 0x05000000; + pub const WIN2KSP1: u32 = 0x05000100; + pub const WIN2KSP2: u32 = 0x05000200; + pub const WIN2KSP3: u32 = 0x05000300; + pub const WIN2KSP4: u32 = 0x05000400; + + pub const WINXP: u32 = 0x05010000; + pub const WINXPSP1: u32 = 0x05010100; + pub const WINXPSP2: u32 = 0x05010200; + pub const WINXPSP3: u32 = 0x05010300; + pub const WINXPSP4: u32 = 0x05010400; + + pub const VISTA: u32 = 0x06000000; + pub const VISTASP1: u32 = 0x06000100; + pub const VISTASP2: u32 = 0x06000200; + pub const VISTASP3: u32 = 0x06000300; + pub const VISTASP4: u32 = 0x06000400; + + pub const WIN7: u32 = 0x06010000; + pub const WIN8: u32 = 0x06020000; + /// AKA `NTDDI_WINBLUE` + pub const WIN8_1: u32 = 0x06030000; + pub const WIN10: u32 = 0x0A000000; +} + +/// Helps build a Microsoft OS 2.0 Descriptor set. +/// +/// # Example +/// ```rust +/// # use embassy_usb::types::InterfaceNumber; +/// # use embassy_usb::msos::*; +/// # let cdc_interface = unsafe { core::mem::transmute::(0) }; +/// # let dfu_interface = unsafe { core::mem::transmute::(1) }; +/// let mut buf = [0u8; 256]; +/// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1); +/// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10)); +/// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128)); +/// builder.configuration(1, |conf| { +/// conf.function(cdc_interface, |func| { +/// func.winusb_device(); +/// func.feature(VendorRevisionDescriptor::new(1)); +/// }); +/// conf.function(dfu_interface, |func| { +/// func.winusb_device(); +/// func.feature(VendorRevisionDescriptor::new(1)); +/// }); +/// }); +/// ``` +pub struct DeviceDescriptorSetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, + windows_version: u32, + vendor_code: u8, +} + +impl<'a> DeviceDescriptorSetBuilder<'a> { + /// Create a device descriptor set builder. + /// + /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] + /// module. + /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. + pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(DescriptorSetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(), + dwWindowsVersion: windows_version.to_le(), + wTotalLength: 0, + }); + Self { + builder, + windows_version, + vendor_code, + } + } + + /// Add a device-level feature descriptor. + /// + /// Note that some feature descriptors may only be used at the device level in non-composite devices. + pub fn feature(&mut self, desc: T) + where + T: Descriptor + DeviceLevelDescriptor + 'a, + { + self.builder.descriptor(desc) + } + + /// Add a configuration subset. + pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) { + let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration); + build_conf(&mut cb); + self.builder.used += cb.finalize(); + } + + /// Finishes writing the data. + pub fn finalize(self) -> MsOsDescriptorSet<'a> { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[4..6]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + + MsOsDescriptorSet { + descriptor: &buf[..used], + windows_version: self.windows_version, + vendor_code: self.vendor_code, + } + } +} + +pub struct ConfigurationSubsetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, +} + +impl<'a> ConfigurationSubsetBuilder<'a> { + pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(ConfigurationSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(), + bConfigurationValue: configuration, + bReserved: 0, + wTotalLength: 0, + }); + Self { builder } + } + + /// Add a function subset. + pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) { + let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface); + build_func(&mut fb); + self.builder.used += fb.finalize(); + } + + /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. + pub fn finalize(self) -> usize { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[6..8]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + used + } +} + +pub struct FunctionSubsetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, +} + +impl<'a> FunctionSubsetBuilder<'a> { + pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(FunctionSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(), + bFirstInterface: interface.0, + bReserved: 0, + wSubsetLength: 0, + }); + Self { builder } + } + + /// Add a function-level descriptor. + /// + /// Note that many descriptors can only be used at function-level in a composite device. + pub fn feature(&mut self, desc: T) + where + T: Descriptor + FunctionLevelDescriptor + 'a, + { + self.builder.descriptor(desc) + } + + /// Adds the feature descriptors to configure this function to use the WinUSB driver. + /// + /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the + /// USB_DEVICE GUID. + pub fn winusb_device(&mut self) { + self.feature(CompatibleIdFeatureDescriptor::new_winusb()); + self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid()); + } + + /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. + pub fn finalize(self) -> usize { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[6..8]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + used + } +} + +/// A trait for descriptors +pub trait Descriptor: Sized { + const TYPE: DescriptorType; + + /// The size of the descriptor's header. + fn size(&self) -> usize { + size_of::() + } + + fn write_to(&self, buf: &mut [u8]); +} + +/// Copies the data of `t` into `buf`. +/// +/// # Safety +/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) +unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { + let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); + assert!(buf.len() >= bytes.len()); + (&mut buf[..bytes.len()]).copy_from_slice(bytes); +} + +/// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum DescriptorType { + SetHeaderDescriptor = 0, + SubsetHeaderConfiguration = 1, + SubsetHeaderFunction = 2, + FeatureCompatibleId = 3, + FeatureRegProperty = 4, + FeatureMinResumeTime = 5, + FeatureModelId = 6, + FeatureCcgpDevice = 7, + FeatureVendorRevision = 8, +} + +/// Table 5. Descriptor set information structure. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct DescriptorSetInformation { + dwWindowsVersion: u32, + wMSOSDescriptorSetTotalLength: u16, + bMS_VendorCode: u8, + bAltEnumCode: u8, +} + +/// Table 4. Microsoft OS 2.0 platform capability descriptor header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct PlatformDescriptor { + bLength: u8, + bDescriptorType: u8, + bDevCapabilityType: u8, + bReserved: u8, + platformCapabilityUUID: [u8; 16], + descriptor_set_information: DescriptorSetInformation, +} + +/// Table 10. Microsoft OS 2.0 descriptor set header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct DescriptorSetHeader { + wLength: u16, + wDescriptorType: u16, + dwWindowsVersion: u32, + wTotalLength: u16, +} + +impl Descriptor for DescriptorSetHeader { + const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +/// Table 11. Configuration subset header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct ConfigurationSubsetHeader { + wLength: u16, + wDescriptorType: u16, + bConfigurationValue: u8, + bReserved: u8, + wTotalLength: u16, +} + +impl Descriptor for ConfigurationSubsetHeader { + const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +/// Table 12. Function subset header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct FunctionSubsetHeader { + wLength: u16, + wDescriptorType: u16, + bFirstInterface: u8, + bReserved: u8, + wSubsetLength: u16, +} + +impl Descriptor for FunctionSubsetHeader { + const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +// Feature Descriptors + +/// A marker trait for feature descriptors that are valid at the device level. +pub trait DeviceLevelDescriptor {} + +/// A marker trait for feature descriptors that are valid at the function level. +pub trait FunctionLevelDescriptor { + /// `true` when the feature descriptor may only be used at the function level in composite devices. + const COMPOSITE_ONLY: bool = false; +} + +/// Table 13. Microsoft OS 2.0 compatible ID descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct CompatibleIdFeatureDescriptor { + wLength: u16, + wDescriptorType: u16, + compatibleId: [u8; 8], + subCompatibleId: [u8; 8], +} + +impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} +impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor { + const COMPOSITE_ONLY: bool = true; +} + +impl Descriptor for CompatibleIdFeatureDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl CompatibleIdFeatureDescriptor { + /// Creates a compatible ID descriptor that signals WINUSB driver compatiblilty. + pub fn new_winusb() -> Self { + Self::new_raw([b'W', b'I', b'N', b'U', b'S', b'B', 0, 0], [0u8; 8]) + } + + /// The ids must be 8 ASCII bytes or fewer. + pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self { + assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8); + let mut cid = [0u8; 8]; + (&mut cid[..compatible_id.len()]).copy_from_slice(compatible_id.as_bytes()); + let mut scid = [0u8; 8]; + (&mut scid[..sub_compatible_id.len()]).copy_from_slice(sub_compatible_id.as_bytes()); + Self::new_raw(cid, scid) + } + + pub fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + compatibleId: compatible_id, + subCompatibleId: sub_compatible_id, + } + } +} + +/// Table 14. Microsoft OS 2.0 registry property descriptor +#[allow(non_snake_case)] +pub struct RegistryPropertyFeatureDescriptor<'a> { + wLength: u16, + wDescriptorType: u16, + wPropertyDataType: u16, + wPropertyNameLength: u16, + PropertyName: &'a [u8], + wPropertyDataLength: u16, + PropertyData: &'a [u8], +} + +impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} +impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> { + const COMPOSITE_ONLY: bool = true; +} + +impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { + const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; + fn size(&self) -> usize { + 10 + self.PropertyName.len() + self.PropertyData.len() + } + fn write_to(&self, buf: &mut [u8]) { + assert!(buf.len() >= self.size()); + assert!(self.wPropertyNameLength as usize == self.PropertyName.len()); + assert!(self.wPropertyDataLength as usize == self.PropertyData.len()); + write_u16(buf, 0..2, self.wLength); + write_u16(buf, 2..4, self.wDescriptorType); + write_u16(buf, 4..6, self.wPropertyDataType); + write_u16(buf, 6..8, self.wPropertyNameLength); + let pne = 8 + self.PropertyName.len(); + (&mut buf[8..pne]).copy_from_slice(self.PropertyName); + let pds = pne + 2; + let pde = pds + self.PropertyData.len(); + write_u16(buf, pne..pds, self.wPropertyDataLength); + (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); + } +} + +impl<'a> RegistryPropertyFeatureDescriptor<'a> { + /// A registry property. + /// + /// `name` should be a NUL-terminated 16-bit Unicode string. + pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { + Self { + wLength: ((10 + name.len() + data.len()) as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + wPropertyDataType: (data_type as u16).to_le(), + wPropertyNameLength: (name.len() as u16).to_le(), + PropertyName: name, + wPropertyDataLength: (data.len() as u16).to_le(), + PropertyData: data, + } + } + + fn u16str_bytes(s: &U16CStr) -> &[u8] { + unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } + } + + /// A registry property that sets the DeviceInterfaceGUID to the device interface class for USB devices which are + /// attached to a USB hub. + pub fn new_usb_deviceinterfaceguid() -> Self { + // Can't use defmt::panic in constant expressions (inside u16cstr!) + macro_rules! panic { + ($($x:tt)*) => { + { + ::core::panic!($($x)*); + } + }; + } + + Self::new_string( + u16cstr!("DeviceInterfaceGUID"), + u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"), + ) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string. + pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string that expands environment variables. + pub fn new_string_expand<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + Self::u16str_bytes(data), + PropertyDataType::ExpandSz, + ) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string that contains a symbolic link. + pub fn new_link<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + Self::u16str_bytes(data), + PropertyDataType::Link, + ) + } + + /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. + pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, + PropertyDataType::RegMultiSz, + ) + } + + /// A registry property containing binary data. + pub fn new_binary<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u8]) -> Self { + Self::new_raw(Self::u16str_bytes(name), data, PropertyDataType::Binary) + } + + /// A registry property containing a Little-Endian 32-bit integer. + /// + /// The function assumes that `data` is already little-endian, it does not convert it. + pub fn new_dword_le<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, + PropertyDataType::DwordLittleEndian, + ) + } + + /// A registry property containing a big-endian 32-bit integer. + /// + /// The function assumes that `data` is already big-endian, it does not convert it. + pub fn new_dword_be<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, + PropertyDataType::DwordBigEndian, + ) + } +} + +/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum PropertyDataType { + Sz = 1, + ExpandSz = 2, + Binary = 3, + DwordLittleEndian = 4, + DwordBigEndian = 5, + Link = 6, + RegMultiSz = 7, +} + +/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct MinimumRecoveryTimeDescriptor { + wLength: u16, + wDescriptorType: u16, + bResumeRecoveryTime: u8, + bResumeSignalingTime: u8, +} + +impl DeviceLevelDescriptor for MinimumRecoveryTimeDescriptor {} + +impl Descriptor for MinimumRecoveryTimeDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureMinResumeTime; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl MinimumRecoveryTimeDescriptor { + /// Times are in milliseconds. + /// + /// `resume_recovery_time` must be >= 0 and <= 10. + /// `resume_signaling_time` must be >= 1 and <= 20. + pub fn new(resume_recovery_time: u8, resume_signaling_time: u8) -> Self { + assert!(resume_recovery_time <= 10); + assert!(resume_signaling_time >= 1 && resume_signaling_time <= 20); + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + bResumeRecoveryTime: resume_recovery_time, + bResumeSignalingTime: resume_signaling_time, + } + } +} + +/// Table 17. Microsoft OS 2.0 model ID descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct ModelIdDescriptor { + wLength: u16, + wDescriptorType: u16, + modelId: [u8; 16], +} + +impl DeviceLevelDescriptor for ModelIdDescriptor {} + +impl Descriptor for ModelIdDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureModelId; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl ModelIdDescriptor { + pub fn new(model_id: u128) -> Self { + Self::new_bytes(model_id.to_le_bytes()) + } + + pub fn new_bytes(model_id: [u8; 16]) -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + modelId: model_id, + } + } +} + +/// Table 18. Microsoft OS 2.0 CCGP device descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct CcgpDeviceDescriptor { + wLength: u16, + wDescriptorType: u16, +} + +impl DeviceLevelDescriptor for CcgpDeviceDescriptor {} + +impl Descriptor for CcgpDeviceDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureCcgpDevice; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl CcgpDeviceDescriptor { + pub fn new() -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + } + } +} + +/// Table 19. Microsoft OS 2.0 vendor revision descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct VendorRevisionDescriptor { + wLength: u16, + wDescriptorType: u16, + /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or + /// other MSOS descriptor. Shell set to greater than or equal to 1. + VendorRevision: u16, +} + +impl DeviceLevelDescriptor for VendorRevisionDescriptor {} +impl FunctionLevelDescriptor for VendorRevisionDescriptor {} + +impl Descriptor for VendorRevisionDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureVendorRevision; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl VendorRevisionDescriptor { + pub fn new(revision: u16) -> Self { + assert!(revision >= 1); + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + VendorRevision: revision.to_le(), + } + } +} From 617b0a03b94d8efde3e89bd80b67caf9a0d4a35d Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 13 Jan 2023 10:18:33 -0600 Subject: [PATCH 0559/1575] usb: fix descriptor set length and DeviceInterfaceGUIDs --- embassy-usb/src/msos.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 08a5074bf..8a2889ce5 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -205,7 +205,7 @@ impl<'a> DeviceDescriptorSetBuilder<'a> { let used = self.builder.used; let buf = self.builder.buf; // Update length in header with final length - let total_len = &mut buf[4..6]; + let total_len = &mut buf[8..10]; total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); MsOsDescriptorSet { @@ -528,7 +528,7 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } } - /// A registry property that sets the DeviceInterfaceGUID to the device interface class for USB devices which are + /// A registry property that sets the DeviceInterfaceGUIDs to the device interface class for USB devices which are /// attached to a USB hub. pub fn new_usb_deviceinterfaceguid() -> Self { // Can't use defmt::panic in constant expressions (inside u16cstr!) @@ -540,9 +540,9 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { }; } - Self::new_string( - u16cstr!("DeviceInterfaceGUID"), - u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"), + Self::new_multi_string( + u16cstr!("DeviceInterfaceGUIDs"), + u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}").as_slice_with_nul(), ) } From b9ecdb72bb55792a8fa5a0bace8cdad498fee9b0 Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 13 Jan 2023 12:21:29 -0600 Subject: [PATCH 0560/1575] usb: remove msos dead code --- embassy-usb/src/msos.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 8a2889ce5..df8461258 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -2,8 +2,6 @@ //! //! -#![allow(dead_code)] - use core::mem::size_of; use core::ops::Range; @@ -91,17 +89,6 @@ impl<'a> DescriptorSetBuilder<'a> { self.used += size; } - pub fn subset(&mut self, build_subset: impl FnOnce(&mut DescriptorSetBuilder<'_>)) { - self.used += { - let mut subset = DescriptorSetBuilder { - used: 0, - buf: self.remaining(), - }; - build_subset(&mut subset); - subset.used - }; - } - pub fn remaining(&mut self) -> &mut [u8] { &mut self.buf[self.used..] } From 9f9230ae7abb545822e59c6f06cabb721b63e0a1 Mon Sep 17 00:00:00 2001 From: alexmoon Date: Thu, 2 Feb 2023 16:13:16 -0500 Subject: [PATCH 0561/1575] Convert MS OS descriptor builder to a writer API This brings it inline with the other embassy-usb descriptor APIs and allows it to integrate well with the Builder to allow class constructors to add MS OS descriptors. Also adds a `usb_serial_winusb` example to demonstrate how to use the API. --- embassy-usb/Cargo.toml | 3 +- embassy-usb/src/builder.rs | 78 ++- embassy-usb/src/lib.rs | 14 +- embassy-usb/src/msos.rs | 493 +++++++++--------- examples/nrf52840/Cargo.toml | 7 +- .../nrf52840/src/bin/usb_serial_winusb.rs | 139 +++++ 6 files changed, 461 insertions(+), 273 deletions(-) create mode 100644 examples/nrf52840/src/bin/usb_serial_winusb.rs diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 31d1f4cae..54a8f27c7 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -13,6 +13,7 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] +msos-descriptor = ["dep:widestring"] default = ["usbd-hid"] [dependencies] @@ -24,7 +25,7 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } heapless = "0.7.10" -widestring = { version = "1.0.2", default-features = false } +widestring = { version = "1.0.2", default-features = false, optional = true } # for HID usbd-hid = { version = "0.6.0", optional = true } diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 2c42fd64e..d1cbf674b 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -3,6 +3,8 @@ use heapless::Vec; use crate::control::ControlHandler; use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::driver::{Driver, Endpoint, EndpointType}; +#[cfg(feature = "msos-descriptor")] +use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::*; use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; @@ -130,7 +132,9 @@ pub struct Builder<'d, D: Driver<'d>> { device_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>, bos_descriptor: BosWriter<'d>, - msos_descriptor: Option>, + + #[cfg(feature = "msos-descriptor")] + msos_descriptor: MsOsDescriptorWriter<'d>, } impl<'d, D: Driver<'d>> Builder<'d, D> { @@ -145,6 +149,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { device_descriptor_buf: &'d mut [u8], config_descriptor_buf: &'d mut [u8], bos_descriptor_buf: &'d mut [u8], + #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], control_buf: &'d mut [u8], handler: Option<&'d dyn DeviceStateHandler>, ) -> Self { @@ -183,12 +188,17 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { device_descriptor, config_descriptor, bos_descriptor, - msos_descriptor: None, + + #[cfg(feature = "msos-descriptor")] + msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf), } } /// Creates the [`UsbDevice`] instance with the configuration in this builder. pub fn build(mut self) -> UsbDevice<'d, D> { + #[cfg(feature = "msos-descriptor")] + let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor); + self.config_descriptor.end_configuration(); self.bos_descriptor.end_bos(); @@ -201,7 +211,8 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.bos_descriptor.writer.into_buf(), self.interfaces, self.control_buf, - self.msos_descriptor, + #[cfg(feature = "msos-descriptor")] + msos_descriptor, ) } @@ -218,14 +229,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { /// /// If it's not set, no IAD descriptor is added. pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> { + let first_interface = InterfaceNumber::new(self.interfaces.len() as u8); let iface_count_index = if self.config.composite_with_iads { - self.config_descriptor.iad( - InterfaceNumber::new(self.interfaces.len() as _), - 0, - class, - subclass, - protocol, - ); + self.config_descriptor + .iad(first_interface, 0, class, subclass, protocol); Some(self.config_descriptor.position() - 5) } else { @@ -235,19 +242,31 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { FunctionBuilder { builder: self, iface_count_index, + + #[cfg(feature = "msos-descriptor")] + first_interface, } } + #[cfg(feature = "msos-descriptor")] /// Add an MS OS 2.0 Descriptor Set. /// /// Panics if called more than once. - pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) { - if self.msos_descriptor.is_some() { - panic!("msos_descriptor already set"); - } - self.msos_descriptor - .insert(msos_descriptor) - .write_bos_capability(&mut self.bos_descriptor); + pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) { + self.msos_descriptor.header(windows_version, vendor_code); + } + + #[cfg(feature = "msos-descriptor")] + /// Add an MS OS 2.0 Device Level Feature Descriptor. + pub fn msos_feature(&mut self, desc: T) { + self.msos_descriptor.device_feature(desc); + } + + #[cfg(feature = "msos-descriptor")] + /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that + /// do not add their own. + pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> { + &mut self.msos_descriptor } } @@ -259,6 +278,16 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> { builder: &'a mut Builder<'d, D>, iface_count_index: Option, + + #[cfg(feature = "msos-descriptor")] + first_interface: InterfaceNumber, +} + +impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> { + fn drop(&mut self) { + #[cfg(feature = "msos-descriptor")] + self.builder.msos_descriptor.end_function(); + } } impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { @@ -288,6 +317,21 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { next_alt_setting_number: 0, } } + + #[cfg(feature = "msos-descriptor")] + /// Add an MS OS 2.0 Function Level Feature Descriptor. + pub fn msos_feature(&mut self, desc: T) { + if !self.builder.msos_descriptor.is_in_config_subset() { + self.builder.msos_descriptor.configuration(0); + } + + if !self.builder.msos_descriptor.is_in_function_subset() { + self.builder.msos_descriptor.function(self.first_interface.0); + } + + #[cfg(feature = "msos-descriptor")] + self.builder.msos_descriptor.function_feature(desc); + } } /// Interface builder. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 948b8d523..aec18524b 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -136,8 +136,8 @@ struct Inner<'d, D: Driver<'d>> { set_address_pending: bool, interfaces: Vec, MAX_INTERFACE_COUNT>, - - msos_descriptor: Option>, + #[cfg(feature = "msos-descriptor")] + msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, } impl<'d, D: Driver<'d>> UsbDevice<'d, D> { @@ -150,7 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { bos_descriptor: &'d [u8], interfaces: Vec, MAX_INTERFACE_COUNT>, control_buf: &'d mut [u8], - msos_descriptor: Option>, + #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, ) -> UsbDevice<'d, D> { // Start the USB bus. // This prevent further allocation by consuming the driver. @@ -174,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { address: 0, set_address_pending: false, interfaces, + #[cfg(feature = "msos-descriptor")] msos_descriptor, }, } @@ -608,11 +609,12 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { None => InResponse::Rejected, } } + #[cfg(feature = "msos-descriptor")] (RequestType::Vendor, Recipient::Device) => { - if let Some(msos) = &self.msos_descriptor { - if req.request == msos.vendor_code() && req.index == 7 { + if !self.msos_descriptor.is_empty() { + if req.request == self.msos_descriptor.vendor_code() && req.index == 7 { // Index 7 retrieves the MS OS Descriptor Set - InResponse::Accepted(msos.descriptor()) + InResponse::Accepted(self.msos_descriptor.descriptor()) } else { InResponse::Rejected } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index df8461258..360f80d91 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "msos-descriptor")] + //! Microsoft OS Descriptors //! //! @@ -5,10 +7,9 @@ use core::mem::size_of; use core::ops::Range; -pub use widestring::{u16cstr, U16CStr}; +pub use widestring::{u16cstr, u16str, U16CStr, U16Str}; -use crate::descriptor::{capability_type, BosWriter}; -use crate::types::InterfaceNumber; +use super::{capability_type, BosWriter}; fn write_u16>(buf: &mut [u8], range: Range, data: T) { (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) @@ -17,13 +18,12 @@ fn write_u16>(buf: &mut [u8], range: Range, data: T) { /// A serialized Microsoft OS 2.0 Descriptor set. /// /// Create with [`DeviceDescriptorSetBuilder`]. -pub struct MsOsDescriptorSet<'a> { - descriptor: &'a [u8], - windows_version: u32, +pub struct MsOsDescriptorSet<'d> { + descriptor: &'d [u8], vendor_code: u8, } -impl<'a> MsOsDescriptorSet<'a> { +impl<'d> MsOsDescriptorSet<'d> { pub fn descriptor(&self) -> &[u8] { self.descriptor } @@ -32,9 +32,150 @@ impl<'a> MsOsDescriptorSet<'a> { self.vendor_code } - pub fn write_bos_capability(&self, bos: &mut BosWriter) { - let windows_version = self.windows_version.to_le_bytes(); - let len = self.descriptor.len().to_le_bytes(); + pub fn is_empty(&self) -> bool { + self.descriptor.is_empty() + } +} + +/// Writes a Microsoft OS 2.0 Descriptor set into a buffer. +pub struct MsOsDescriptorWriter<'d> { + pub buf: &'d mut [u8], + + position: usize, + config_mark: Option, + function_mark: Option, + vendor_code: u8, +} + +impl<'d> MsOsDescriptorWriter<'d> { + pub(crate) fn new(buf: &'d mut [u8]) -> Self { + MsOsDescriptorWriter { + buf, + position: 0, + config_mark: None, + function_mark: None, + vendor_code: 0, + } + } + + pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> { + self.end(); + + if self.is_empty() { + MsOsDescriptorSet { + descriptor: &[], + vendor_code: 0, + } + } else { + self.write_bos(bos); + MsOsDescriptorSet { + descriptor: &self.buf[..self.position], + vendor_code: self.vendor_code, + } + } + } + + pub fn is_empty(&self) -> bool { + self.position == 0 + } + + pub fn is_in_config_subset(&self) -> bool { + self.config_mark.is_some() + } + + pub fn is_in_function_subset(&self) -> bool { + self.function_mark.is_some() + } + + /// Write the MS OS descriptor set header. + /// + /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] + /// module. + /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. + pub fn header(&mut self, windows_version: u32, vendor_code: u8) { + assert!(self.is_empty(), "You can only call MsOsDescriptorWriter::header once"); + self.write(DescriptorSetHeader::new(windows_version)); + self.vendor_code = vendor_code; + } + + /// Add a device level feature descriptor. + /// + /// Note that some feature descriptors may only be used at the device level in non-composite devices. + /// Those features must be written before the first call to [`Self::configuration`]. + pub fn device_feature(&mut self, desc: T) { + assert!( + !self.is_empty(), + "device features may only be added after the header is written" + ); + assert!( + self.config_mark.is_none(), + "device features must be added before the first configuration subset" + ); + self.write(desc); + } + + /// Add a configuration subset. + pub fn configuration(&mut self, config: u8) { + assert!( + !self.is_empty(), + "MsOsDescriptorWriter: configuration must be called after header" + ); + Self::end_subset::(self.buf, self.position, &mut self.config_mark); + self.config_mark = Some(self.position); + self.write(ConfigurationSubsetHeader::new(config)); + } + + /// Add a function subset. + pub fn function(&mut self, first_interface: u8) { + assert!( + self.config_mark.is_some(), + "MsOsDescriptorWriter: function subset requires a configuration subset" + ); + self.end_function(); + self.function_mark = Some(self.position); + self.write(FunctionSubsetHeader::new(first_interface)); + } + + /// Add a function level feature descriptor. + /// + /// Note that some features may only be used at the function level. Those features must be written after a call + /// to [`Self::function`]. + pub fn function_feature(&mut self, desc: T) { + assert!( + self.function_mark.is_some(), + "function features may only be added to a function subset" + ); + self.write(desc); + } + + pub fn end_function(&mut self) { + Self::end_subset::(self.buf, self.position, &mut self.function_mark); + } + + fn write(&mut self, desc: T) { + desc.write_to(&mut self.buf[self.position..]); + self.position += desc.size(); + } + + fn end_subset(buf: &mut [u8], position: usize, mark: &mut Option) { + if let Some(mark) = mark.take() { + let len = position - mark; + let p = mark + T::LENGTH_OFFSET; + buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes()); + } + } + + fn end(&mut self) { + if self.position > 0 { + Self::end_subset::(self.buf, self.position, &mut self.function_mark); + Self::end_subset::(self.buf, self.position, &mut self.config_mark); + Self::end_subset::(self.buf, self.position, &mut Some(0)); + } + } + + fn write_bos(&mut self, bos: &mut BosWriter) { + let windows_version = &self.buf[4..8]; + let len = (self.position as u16).to_le_bytes(); bos.capability( capability_type::PLATFORM, &[ @@ -67,30 +208,7 @@ impl<'a> MsOsDescriptorSet<'a> { self.vendor_code, 0x0, // Device does not support alternate enumeration ], - ) - } -} - -/// A helper struct to implement the different descriptor set builders. -struct DescriptorSetBuilder<'a> { - used: usize, - buf: &'a mut [u8], -} - -impl<'a> DescriptorSetBuilder<'a> { - pub fn descriptor(&mut self, desc: T) - where - T: Descriptor + 'a, - { - let size = desc.size(); - let start = self.used; - let end = start + size; - desc.write_to(&mut self.buf[start..end]); - self.used += size; - } - - pub fn remaining(&mut self) -> &mut [u8] { - &mut self.buf[self.used..] + ); } } @@ -120,182 +238,27 @@ pub mod windows_version { pub const WIN10: u32 = 0x0A000000; } -/// Helps build a Microsoft OS 2.0 Descriptor set. -/// -/// # Example -/// ```rust -/// # use embassy_usb::types::InterfaceNumber; -/// # use embassy_usb::msos::*; -/// # let cdc_interface = unsafe { core::mem::transmute::(0) }; -/// # let dfu_interface = unsafe { core::mem::transmute::(1) }; -/// let mut buf = [0u8; 256]; -/// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1); -/// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10)); -/// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128)); -/// builder.configuration(1, |conf| { -/// conf.function(cdc_interface, |func| { -/// func.winusb_device(); -/// func.feature(VendorRevisionDescriptor::new(1)); -/// }); -/// conf.function(dfu_interface, |func| { -/// func.winusb_device(); -/// func.feature(VendorRevisionDescriptor::new(1)); -/// }); -/// }); -/// ``` -pub struct DeviceDescriptorSetBuilder<'a> { - builder: DescriptorSetBuilder<'a>, - windows_version: u32, - vendor_code: u8, -} +mod sealed { + use core::mem::size_of; -impl<'a> DeviceDescriptorSetBuilder<'a> { - /// Create a device descriptor set builder. - /// - /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] - /// module. - /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. - pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self { - let mut builder = DescriptorSetBuilder { used: 0, buf }; - builder.descriptor(DescriptorSetHeader { - wLength: (size_of::() as u16).to_le(), - wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(), - dwWindowsVersion: windows_version.to_le(), - wTotalLength: 0, - }); - Self { - builder, - windows_version, - vendor_code, + /// A trait for descriptors + pub trait Descriptor: Sized { + const TYPE: super::DescriptorType; + + /// The size of the descriptor's header. + fn size(&self) -> usize { + size_of::() } + + fn write_to(&self, buf: &mut [u8]); } - /// Add a device-level feature descriptor. - /// - /// Note that some feature descriptors may only be used at the device level in non-composite devices. - pub fn feature(&mut self, desc: T) - where - T: Descriptor + DeviceLevelDescriptor + 'a, - { - self.builder.descriptor(desc) - } - - /// Add a configuration subset. - pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) { - let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration); - build_conf(&mut cb); - self.builder.used += cb.finalize(); - } - - /// Finishes writing the data. - pub fn finalize(self) -> MsOsDescriptorSet<'a> { - let used = self.builder.used; - let buf = self.builder.buf; - // Update length in header with final length - let total_len = &mut buf[8..10]; - total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); - - MsOsDescriptorSet { - descriptor: &buf[..used], - windows_version: self.windows_version, - vendor_code: self.vendor_code, - } + pub trait DescriptorSet: Descriptor { + const LENGTH_OFFSET: usize; } } -pub struct ConfigurationSubsetBuilder<'a> { - builder: DescriptorSetBuilder<'a>, -} - -impl<'a> ConfigurationSubsetBuilder<'a> { - pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self { - let mut builder = DescriptorSetBuilder { used: 0, buf }; - builder.descriptor(ConfigurationSubsetHeader { - wLength: (size_of::() as u16).to_le(), - wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(), - bConfigurationValue: configuration, - bReserved: 0, - wTotalLength: 0, - }); - Self { builder } - } - - /// Add a function subset. - pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) { - let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface); - build_func(&mut fb); - self.builder.used += fb.finalize(); - } - - /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. - pub fn finalize(self) -> usize { - let used = self.builder.used; - let buf = self.builder.buf; - // Update length in header with final length - let total_len = &mut buf[6..8]; - total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); - used - } -} - -pub struct FunctionSubsetBuilder<'a> { - builder: DescriptorSetBuilder<'a>, -} - -impl<'a> FunctionSubsetBuilder<'a> { - pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self { - let mut builder = DescriptorSetBuilder { used: 0, buf }; - builder.descriptor(FunctionSubsetHeader { - wLength: (size_of::() as u16).to_le(), - wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(), - bFirstInterface: interface.0, - bReserved: 0, - wSubsetLength: 0, - }); - Self { builder } - } - - /// Add a function-level descriptor. - /// - /// Note that many descriptors can only be used at function-level in a composite device. - pub fn feature(&mut self, desc: T) - where - T: Descriptor + FunctionLevelDescriptor + 'a, - { - self.builder.descriptor(desc) - } - - /// Adds the feature descriptors to configure this function to use the WinUSB driver. - /// - /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the - /// USB_DEVICE GUID. - pub fn winusb_device(&mut self) { - self.feature(CompatibleIdFeatureDescriptor::new_winusb()); - self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid()); - } - - /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. - pub fn finalize(self) -> usize { - let used = self.builder.used; - let buf = self.builder.buf; - // Update length in header with final length - let total_len = &mut buf[6..8]; - total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); - used - } -} - -/// A trait for descriptors -pub trait Descriptor: Sized { - const TYPE: DescriptorType; - - /// The size of the descriptor's header. - fn size(&self) -> usize { - size_of::() - } - - fn write_to(&self, buf: &mut [u8]); -} +use sealed::*; /// Copies the data of `t` into `buf`. /// @@ -303,7 +266,7 @@ pub trait Descriptor: Sized { /// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); - assert!(buf.len() >= bytes.len()); + assert!(buf.len() >= bytes.len(), "MSOS descriptor buffer full"); (&mut buf[..bytes.len()]).copy_from_slice(bytes); } @@ -354,6 +317,17 @@ pub struct DescriptorSetHeader { wTotalLength: u16, } +impl DescriptorSetHeader { + pub fn new(windows_version: u32) -> Self { + DescriptorSetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + dwWindowsVersion: windows_version.to_le(), + wTotalLength: 0, + } + } +} + impl Descriptor for DescriptorSetHeader { const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; fn write_to(&self, buf: &mut [u8]) { @@ -361,6 +335,10 @@ impl Descriptor for DescriptorSetHeader { } } +impl DescriptorSet for DescriptorSetHeader { + const LENGTH_OFFSET: usize = 8; +} + /// Table 11. Configuration subset header. #[allow(non_snake_case)] #[repr(C, packed(1))] @@ -372,6 +350,18 @@ pub struct ConfigurationSubsetHeader { wTotalLength: u16, } +impl ConfigurationSubsetHeader { + pub fn new(config: u8) -> Self { + ConfigurationSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + bConfigurationValue: config, + bReserved: 0, + wTotalLength: 0, + } + } +} + impl Descriptor for ConfigurationSubsetHeader { const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; fn write_to(&self, buf: &mut [u8]) { @@ -379,6 +369,10 @@ impl Descriptor for ConfigurationSubsetHeader { } } +impl DescriptorSet for ConfigurationSubsetHeader { + const LENGTH_OFFSET: usize = 6; +} + /// Table 12. Function subset header. #[allow(non_snake_case)] #[repr(C, packed(1))] @@ -390,6 +384,18 @@ pub struct FunctionSubsetHeader { wSubsetLength: u16, } +impl FunctionSubsetHeader { + pub fn new(first_interface: u8) -> Self { + FunctionSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + bFirstInterface: first_interface, + bReserved: 0, + wSubsetLength: 0, + } + } +} + impl Descriptor for FunctionSubsetHeader { const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; fn write_to(&self, buf: &mut [u8]) { @@ -397,16 +403,17 @@ impl Descriptor for FunctionSubsetHeader { } } +impl DescriptorSet for FunctionSubsetHeader { + const LENGTH_OFFSET: usize = 6; +} + // Feature Descriptors /// A marker trait for feature descriptors that are valid at the device level. -pub trait DeviceLevelDescriptor {} +pub trait DeviceLevelDescriptor: Descriptor {} /// A marker trait for feature descriptors that are valid at the function level. -pub trait FunctionLevelDescriptor { - /// `true` when the feature descriptor may only be used at the function level in composite devices. - const COMPOSITE_ONLY: bool = false; -} +pub trait FunctionLevelDescriptor: Descriptor {} /// Table 13. Microsoft OS 2.0 compatible ID descriptor. #[allow(non_snake_case)] @@ -419,9 +426,7 @@ pub struct CompatibleIdFeatureDescriptor { } impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} -impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor { - const COMPOSITE_ONLY: bool = true; -} +impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {} impl Descriptor for CompatibleIdFeatureDescriptor { const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; @@ -462,16 +467,12 @@ pub struct RegistryPropertyFeatureDescriptor<'a> { wLength: u16, wDescriptorType: u16, wPropertyDataType: u16, - wPropertyNameLength: u16, PropertyName: &'a [u8], - wPropertyDataLength: u16, PropertyData: &'a [u8], } impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} -impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> { - const COMPOSITE_ONLY: bool = true; -} +impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; @@ -479,45 +480,22 @@ impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { 10 + self.PropertyName.len() + self.PropertyData.len() } fn write_to(&self, buf: &mut [u8]) { - assert!(buf.len() >= self.size()); - assert!(self.wPropertyNameLength as usize == self.PropertyName.len()); - assert!(self.wPropertyDataLength as usize == self.PropertyData.len()); + assert!(buf.len() >= self.size(), "MSOS descriptor buffer full"); write_u16(buf, 0..2, self.wLength); write_u16(buf, 2..4, self.wDescriptorType); write_u16(buf, 4..6, self.wPropertyDataType); - write_u16(buf, 6..8, self.wPropertyNameLength); + write_u16(buf, 6..8, (self.PropertyName.len() as u16).to_le()); let pne = 8 + self.PropertyName.len(); (&mut buf[8..pne]).copy_from_slice(self.PropertyName); let pds = pne + 2; let pde = pds + self.PropertyData.len(); - write_u16(buf, pne..pds, self.wPropertyDataLength); + write_u16(buf, pne..pds, (self.PropertyData.len() as u16).to_le()); (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); } } impl<'a> RegistryPropertyFeatureDescriptor<'a> { - /// A registry property. - /// - /// `name` should be a NUL-terminated 16-bit Unicode string. - pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { - Self { - wLength: ((10 + name.len() + data.len()) as u16).to_le(), - wDescriptorType: (Self::TYPE as u16).to_le(), - wPropertyDataType: (data_type as u16).to_le(), - wPropertyNameLength: (name.len() as u16).to_le(), - PropertyName: name, - wPropertyDataLength: (data.len() as u16).to_le(), - PropertyData: data, - } - } - - fn u16str_bytes(s: &U16CStr) -> &[u8] { - unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } - } - - /// A registry property that sets the DeviceInterfaceGUIDs to the device interface class for USB devices which are - /// attached to a USB hub. - pub fn new_usb_deviceinterfaceguid() -> Self { + pub const DEVICE_INTERFACE_GUIDS_NAME: &U16CStr = { // Can't use defmt::panic in constant expressions (inside u16cstr!) macro_rules! panic { ($($x:tt)*) => { @@ -527,10 +505,25 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { }; } - Self::new_multi_string( - u16cstr!("DeviceInterfaceGUIDs"), - u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}").as_slice_with_nul(), - ) + u16cstr!("DeviceInterfaceGUIDs") + }; + + /// A registry property. + /// + /// `name` should be a NUL-terminated 16-bit Unicode string. + pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { + assert!(name.len() < usize::from(u16::MAX) && data.len() < usize::from(u16::MAX)); + Self { + wLength: ((10 + name.len() + data.len()) as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + wPropertyDataType: (data_type as u16).to_le(), + PropertyName: name, + PropertyData: data, + } + } + + fn u16str_bytes(s: &U16CStr) -> &[u8] { + unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } } /// A registry property containing a NUL-terminated 16-bit Unicode string. @@ -558,6 +551,10 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { + assert!( + data.len() >= 2 && data[data.len() - 1] == 0 && data[data.len() - 2] == 0, + "multi-strings must end in double nul terminators" + ); Self::new_raw( Self::u16str_bytes(name), unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 95d939873..cfdda076e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] +msos-descriptor = ["embassy-usb/msos-descriptor"] nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] @@ -34,4 +35,8 @@ 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 } \ No newline at end of file +serde = { version = "1.0.136", default-features = false } + +[[bin]] +name = "usb_serial_winusb" +required-features = ["msos-descriptor"] diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs new file mode 100644 index 000000000..443379a07 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::mem; + +use defmt::{info, panic}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; +use embassy_nrf::{interrupt, pac}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::msos::{self, windows_version}; +use embassy_usb::{Builder, Config}; +use {defmt_rtt as _, panic_probe as _}; + +const DEVICE_INTERFACE_GUIDS: &[u16] = { + // Can't use defmt::panic in constant expressions (inside u16str!) + macro_rules! panic { + ($($x:tt)*) => { + { + ::core::panic!($($x)*); + } + }; + } + msos::u16str!("{EAA9A5DC-30BA-44BC-9232-606CDC875321}\0\0").as_slice() +}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let clock: pac::CLOCK = unsafe { mem::transmute(()) }; + + info!("Enabling ext hfosc..."); + clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); + while clock.events_hfclkstarted.read().bits() != 1 {} + + // Create the driver, from the HAL. + let irq = interrupt::take!(USBD); + let power_irq = interrupt::take!(POWER_CLOCK); + let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut msos_descriptor, + &mut control_buf, + None, + ); + + builder.msos_descriptor(windows_version::WIN8_1, 2); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Since we want to create MS OS feature descriptors that apply to a function that has already been added to the + // builder, need to get the MsOsDescriptorWriter from the builder and manually add those descriptors. + // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. + let msos_writer = builder.msos_writer(); + msos_writer.configuration(0); + msos_writer.function(0); + msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); + msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new_multi_string( + msos::RegistryPropertyFeatureDescriptor::DEVICE_INTERFACE_GUIDS_NAME, + DEVICE_INTERFACE_GUIDS, + )); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( + class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From aa21aebb0b321a2085571e5be5fffcea4703584d Mon Sep 17 00:00:00 2001 From: alexmoon Date: Tue, 7 Feb 2023 14:19:51 -0500 Subject: [PATCH 0562/1575] Lazily encode UTF16 values and add docs --- embassy-usb/Cargo.toml | 3 +- embassy-usb/src/msos.rs | 318 +++++++++--------- .../nrf52840/src/bin/usb_serial_winusb.rs | 25 +- 3 files changed, 166 insertions(+), 180 deletions(-) diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 54a8f27c7..eb9ba36f4 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] -msos-descriptor = ["dep:widestring"] +msos-descriptor = [] default = ["usbd-hid"] [dependencies] @@ -25,7 +25,6 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } heapless = "0.7.10" -widestring = { version = "1.0.2", default-features = false, optional = true } # for HID usbd-hid = { version = "0.6.0", optional = true } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 360f80d91..19ed34979 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -5,16 +5,9 @@ //! use core::mem::size_of; -use core::ops::Range; - -pub use widestring::{u16cstr, u16str, U16CStr, U16Str}; use super::{capability_type, BosWriter}; -fn write_u16>(buf: &mut [u8], range: Range, data: T) { - (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) -} - /// A serialized Microsoft OS 2.0 Descriptor set. /// /// Create with [`DeviceDescriptorSetBuilder`]. @@ -24,14 +17,17 @@ pub struct MsOsDescriptorSet<'d> { } impl<'d> MsOsDescriptorSet<'d> { + /// Gets the raw bytes of the MS OS descriptor pub fn descriptor(&self) -> &[u8] { self.descriptor } + /// Gets the vendor code used by the host to retrieve the MS OS descriptor pub fn vendor_code(&self) -> u8 { self.vendor_code } + /// Returns `true` if no MS OS descriptor data is available pub fn is_empty(&self) -> bool { self.descriptor.is_empty() } @@ -39,7 +35,7 @@ impl<'d> MsOsDescriptorSet<'d> { /// Writes a Microsoft OS 2.0 Descriptor set into a buffer. pub struct MsOsDescriptorWriter<'d> { - pub buf: &'d mut [u8], + buf: &'d mut [u8], position: usize, config_mark: Option, @@ -75,14 +71,17 @@ impl<'d> MsOsDescriptorWriter<'d> { } } + /// Returns `true` if the MS OS descriptor header has not yet been written pub fn is_empty(&self) -> bool { self.position == 0 } + /// Returns `true` if a configuration subset header has been started pub fn is_in_config_subset(&self) -> bool { self.config_mark.is_some() } + /// Returns `true` if a function subset header has been started and not yet ended pub fn is_in_function_subset(&self) -> bool { self.function_mark.is_some() } @@ -148,6 +147,7 @@ impl<'d> MsOsDescriptorWriter<'d> { self.write(desc); } + /// Ends the current function subset (if any) pub fn end_function(&mut self) { Self::end_subset::(self.buf, self.position, &mut self.function_mark); } @@ -212,29 +212,13 @@ impl<'d> MsOsDescriptorWriter<'d> { } } +/// Microsoft Windows version codes +/// +/// Windows 8.1 is the minimum version allowed for MS OS 2.0 descriptors. pub mod windows_version { - pub const WIN2K: u32 = 0x05000000; - pub const WIN2KSP1: u32 = 0x05000100; - pub const WIN2KSP2: u32 = 0x05000200; - pub const WIN2KSP3: u32 = 0x05000300; - pub const WIN2KSP4: u32 = 0x05000400; - - pub const WINXP: u32 = 0x05010000; - pub const WINXPSP1: u32 = 0x05010100; - pub const WINXPSP2: u32 = 0x05010200; - pub const WINXPSP3: u32 = 0x05010300; - pub const WINXPSP4: u32 = 0x05010400; - - pub const VISTA: u32 = 0x06000000; - pub const VISTASP1: u32 = 0x06000100; - pub const VISTASP2: u32 = 0x06000200; - pub const VISTASP3: u32 = 0x06000300; - pub const VISTASP4: u32 = 0x06000400; - - pub const WIN7: u32 = 0x06010000; - pub const WIN8: u32 = 0x06020000; - /// AKA `NTDDI_WINBLUE` + /// Windows 8.1 (aka `NTDDI_WINBLUE`) pub const WIN8_1: u32 = 0x06030000; + /// Windows 10 pub const WIN10: u32 = 0x0A000000; } @@ -266,7 +250,7 @@ use sealed::*; /// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); - assert!(buf.len() >= bytes.len(), "MSOS descriptor buffer full"); + assert!(buf.len() >= bytes.len(), "MS OS descriptor buffer full"); (&mut buf[..bytes.len()]).copy_from_slice(bytes); } @@ -274,14 +258,23 @@ unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u16)] pub enum DescriptorType { + /// MS OS descriptor set header SetHeaderDescriptor = 0, + /// Configuration subset header SubsetHeaderConfiguration = 1, + /// Function subset header SubsetHeaderFunction = 2, + /// Compatible device ID feature descriptor FeatureCompatibleId = 3, + /// Registry property feature descriptor FeatureRegProperty = 4, + /// Minimum USB resume time feature descriptor FeatureMinResumeTime = 5, + /// Vendor revision feature descriptor FeatureModelId = 6, + /// CCGP device descriptor feature descriptor FeatureCcgpDevice = 7, + /// Vendor revision feature descriptor FeatureVendorRevision = 8, } @@ -318,6 +311,9 @@ pub struct DescriptorSetHeader { } impl DescriptorSetHeader { + /// Creates a MS OS descriptor set header. + /// + /// `windows_version` is the minimum Windows version the descriptor set can apply to. pub fn new(windows_version: u32) -> Self { DescriptorSetHeader { wLength: (size_of::() as u16).to_le(), @@ -351,6 +347,7 @@ pub struct ConfigurationSubsetHeader { } impl ConfigurationSubsetHeader { + /// Creates a configuration subset header pub fn new(config: u8) -> Self { ConfigurationSubsetHeader { wLength: (size_of::() as u16).to_le(), @@ -385,6 +382,7 @@ pub struct FunctionSubsetHeader { } impl FunctionSubsetHeader { + /// Creates a function subset header pub fn new(first_interface: u8) -> Self { FunctionSubsetHeader { wLength: (size_of::() as u16).to_le(), @@ -436,11 +434,8 @@ impl Descriptor for CompatibleIdFeatureDescriptor { } impl CompatibleIdFeatureDescriptor { - /// Creates a compatible ID descriptor that signals WINUSB driver compatiblilty. - pub fn new_winusb() -> Self { - Self::new_raw([b'W', b'I', b'N', b'U', b'S', b'B', 0, 0], [0u8; 8]) - } - + /// Creates a compatible ID feature descriptor + /// /// The ids must be 8 ASCII bytes or fewer. pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self { assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8); @@ -451,7 +446,7 @@ impl CompatibleIdFeatureDescriptor { Self::new_raw(cid, scid) } - pub fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { + fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { Self { wLength: (size_of::() as u16).to_le(), wDescriptorType: (Self::TYPE as u16).to_le(), @@ -464,129 +459,87 @@ impl CompatibleIdFeatureDescriptor { /// Table 14. Microsoft OS 2.0 registry property descriptor #[allow(non_snake_case)] pub struct RegistryPropertyFeatureDescriptor<'a> { - wLength: u16, - wDescriptorType: u16, - wPropertyDataType: u16, - PropertyName: &'a [u8], - PropertyData: &'a [u8], + name: &'a str, + data: PropertyData<'a>, } -impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} -impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} - -impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { - const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; - fn size(&self) -> usize { - 10 + self.PropertyName.len() + self.PropertyData.len() - } - fn write_to(&self, buf: &mut [u8]) { - assert!(buf.len() >= self.size(), "MSOS descriptor buffer full"); - write_u16(buf, 0..2, self.wLength); - write_u16(buf, 2..4, self.wDescriptorType); - write_u16(buf, 4..6, self.wPropertyDataType); - write_u16(buf, 6..8, (self.PropertyName.len() as u16).to_le()); - let pne = 8 + self.PropertyName.len(); - (&mut buf[8..pne]).copy_from_slice(self.PropertyName); - let pds = pne + 2; - let pde = pds + self.PropertyData.len(); - write_u16(buf, pne..pds, (self.PropertyData.len() as u16).to_le()); - (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); - } -} - -impl<'a> RegistryPropertyFeatureDescriptor<'a> { - pub const DEVICE_INTERFACE_GUIDS_NAME: &U16CStr = { - // Can't use defmt::panic in constant expressions (inside u16cstr!) - macro_rules! panic { - ($($x:tt)*) => { - { - ::core::panic!($($x)*); - } - }; - } - - u16cstr!("DeviceInterfaceGUIDs") - }; - - /// A registry property. - /// - /// `name` should be a NUL-terminated 16-bit Unicode string. - pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { - assert!(name.len() < usize::from(u16::MAX) && data.len() < usize::from(u16::MAX)); - Self { - wLength: ((10 + name.len() + data.len()) as u16).to_le(), - wDescriptorType: (Self::TYPE as u16).to_le(), - wPropertyDataType: (data_type as u16).to_le(), - PropertyName: name, - PropertyData: data, - } - } - - fn u16str_bytes(s: &U16CStr) -> &[u8] { - unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } - } - - /// A registry property containing a NUL-terminated 16-bit Unicode string. - pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { - Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) - } - - /// A registry property containing a NUL-terminated 16-bit Unicode string that expands environment variables. - pub fn new_string_expand<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - Self::u16str_bytes(data), - PropertyDataType::ExpandSz, - ) - } - - /// A registry property containing a NUL-terminated 16-bit Unicode string that contains a symbolic link. - pub fn new_link<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - Self::u16str_bytes(data), - PropertyDataType::Link, - ) - } - - /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. - pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { - assert!( - data.len() >= 2 && data[data.len() - 1] == 0 && data[data.len() - 2] == 0, - "multi-strings must end in double nul terminators" - ); - Self::new_raw( - Self::u16str_bytes(name), - unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, - PropertyDataType::RegMultiSz, - ) - } - +/// Data values that can be encoded into a registry property descriptor +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PropertyData<'a> { + /// A registry property containing a string. + Sz(&'a str), + /// A registry property containing a string that expands environment variables. + ExpandSz(&'a str), /// A registry property containing binary data. - pub fn new_binary<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u8]) -> Self { - Self::new_raw(Self::u16str_bytes(name), data, PropertyDataType::Binary) - } - - /// A registry property containing a Little-Endian 32-bit integer. - /// - /// The function assumes that `data` is already little-endian, it does not convert it. - pub fn new_dword_le<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, - PropertyDataType::DwordLittleEndian, - ) - } - + Binary(&'a [u8]), + /// A registry property containing a little-endian 32-bit integer. + DwordLittleEndian(u32), /// A registry property containing a big-endian 32-bit integer. - /// - /// The function assumes that `data` is already big-endian, it does not convert it. - pub fn new_dword_be<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { - Self::new_raw( - Self::u16str_bytes(name), - unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, - PropertyDataType::DwordBigEndian, - ) + DwordBigEndian(u32), + /// A registry property containing a string that contains a symbolic link. + Link(&'a str), + /// A registry property containing multiple strings. + RegMultiSz(&'a [&'a str]), +} + +fn write_bytes(val: &[u8], buf: &mut [u8]) -> usize { + assert!(buf.len() >= val.len()); + buf[..val.len()].copy_from_slice(val); + val.len() +} + +fn write_utf16(val: &str, buf: &mut [u8]) -> usize { + let mut pos = 0; + for c in val.encode_utf16() { + pos += write_bytes(&c.to_le_bytes(), &mut buf[pos..]); + } + pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..]) +} + +impl<'a> PropertyData<'a> { + /// Gets the `PropertyDataType` for this property value + pub fn kind(&self) -> PropertyDataType { + match self { + PropertyData::Sz(_) => PropertyDataType::Sz, + PropertyData::ExpandSz(_) => PropertyDataType::ExpandSz, + PropertyData::Binary(_) => PropertyDataType::Binary, + PropertyData::DwordLittleEndian(_) => PropertyDataType::DwordLittleEndian, + PropertyData::DwordBigEndian(_) => PropertyDataType::DwordBigEndian, + PropertyData::Link(_) => PropertyDataType::Link, + PropertyData::RegMultiSz(_) => PropertyDataType::RegMultiSz, + } + } + + /// Gets the size (in bytes) of this property value when encoded. + pub fn size(&self) -> usize { + match self { + PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => { + core::mem::size_of::() * (val.encode_utf16().count() + 1) + } + PropertyData::Binary(val) => val.len(), + PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val), + PropertyData::RegMultiSz(val) => { + core::mem::size_of::() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1 + } + } + } + + /// Encodes the data for this property value and writes it to `buf`. + pub fn write(&self, buf: &mut [u8]) -> usize { + match self { + PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => write_utf16(val, buf), + PropertyData::Binary(val) => write_bytes(val, buf), + PropertyData::DwordLittleEndian(val) => write_bytes(&val.to_le_bytes(), buf), + PropertyData::DwordBigEndian(val) => write_bytes(&val.to_be_bytes(), buf), + PropertyData::RegMultiSz(val) => { + let mut pos = 0; + for s in *val { + pos += write_utf16(s, &mut buf[pos..]); + } + pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..]) + } + } } } @@ -594,15 +547,57 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { #[derive(Clone, Copy, PartialEq, Eq)] #[repr(u16)] pub enum PropertyDataType { + /// A registry property containing a string. Sz = 1, + /// A registry property containing a string that expands environment variables. ExpandSz = 2, + /// A registry property containing binary data. Binary = 3, + /// A registry property containing a little-endian 32-bit integer. DwordLittleEndian = 4, + /// A registry property containing a big-endian 32-bit integer. DwordBigEndian = 5, + /// A registry property containing a string that contains a symbolic link. Link = 6, + /// A registry property containing multiple strings. RegMultiSz = 7, } +impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} +impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} + +impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { + const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; + + fn size(&self) -> usize { + 10 + self.name_size() + self.data.size() + } + + fn write_to(&self, buf: &mut [u8]) { + assert!(buf.len() >= self.size(), "MS OS descriptor buffer full"); + + let mut pos = 0; + pos += write_bytes(&(self.size() as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_bytes(&(Self::TYPE as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_bytes(&(self.data.kind() as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_bytes(&(self.name_size() as u16).to_le_bytes(), &mut buf[pos..]); + pos += write_utf16(self.name, &mut buf[pos..]); + pos += write_bytes(&(self.data.size() as u16).to_le_bytes(), &mut buf[pos..]); + self.data.write(&mut buf[pos..]); + } +} + +impl<'a> RegistryPropertyFeatureDescriptor<'a> { + /// A registry property. + pub fn new(name: &'a str, data: PropertyData<'a>) -> Self { + Self { name, data } + } + + fn name_size(&self) -> usize { + core::mem::size_of::() * (self.name.encode_utf16().count() + 1) + } +} + /// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor. #[allow(non_snake_case)] #[repr(C, packed(1))] @@ -658,15 +653,14 @@ impl Descriptor for ModelIdDescriptor { } impl ModelIdDescriptor { + /// Creates a new model ID descriptor + /// + /// `model_id` should be a uuid that uniquely identifies a physical device. pub fn new(model_id: u128) -> Self { - Self::new_bytes(model_id.to_le_bytes()) - } - - pub fn new_bytes(model_id: [u8; 16]) -> Self { Self { wLength: (size_of::() as u16).to_le(), wDescriptorType: (Self::TYPE as u16).to_le(), - modelId: model_id, + modelId: model_id.to_le_bytes(), } } } @@ -689,6 +683,7 @@ impl Descriptor for CcgpDeviceDescriptor { } impl CcgpDeviceDescriptor { + /// Creates a new CCGP device descriptor pub fn new() -> Self { Self { wLength: (size_of::() as u16).to_le(), @@ -704,7 +699,7 @@ pub struct VendorRevisionDescriptor { wLength: u16, wDescriptorType: u16, /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or - /// other MSOS descriptor. Shell set to greater than or equal to 1. + /// other MS OS descriptor. Shell set to greater than or equal to 1. VendorRevision: u16, } @@ -719,6 +714,7 @@ impl Descriptor for VendorRevisionDescriptor { } impl VendorRevisionDescriptor { + /// Creates a new vendor revision descriptor pub fn new(revision: u16) -> Self { assert!(revision >= 1); Self { diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 443379a07..f4b828de6 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; +use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; use embassy_nrf::{interrupt, pac}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -15,17 +15,8 @@ use embassy_usb::msos::{self, windows_version}; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; -const DEVICE_INTERFACE_GUIDS: &[u16] = { - // Can't use defmt::panic in constant expressions (inside u16str!) - macro_rules! panic { - ($($x:tt)*) => { - { - ::core::panic!($($x)*); - } - }; - } - msos::u16str!("{EAA9A5DC-30BA-44BC-9232-606CDC875321}\0\0").as_slice() -}; +// This is a randomly generated GUID to allow clients on Windows to find our device +const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -39,7 +30,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let irq = interrupt::take!(USBD); let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); + let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -89,9 +80,9 @@ async fn main(_spawner: Spawner) { msos_writer.configuration(0); msos_writer.function(0); msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); - msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new_multi_string( - msos::RegistryPropertyFeatureDescriptor::DEVICE_INTERFACE_GUIDS_NAME, - DEVICE_INTERFACE_GUIDS, + msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( + "DeviceInterfaceGUIDs", + msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), )); // Build the builder. @@ -126,7 +117,7 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( +async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; From 1d841cc8ac74feacc4d231958ce2c46419ae3bda Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 20:49:10 +0100 Subject: [PATCH 0563/1575] usb: make max interface count configurable at compile time. --- embassy-usb/Cargo.toml | 13 ++++++ embassy-usb/README.md | 24 +++++++++- embassy-usb/build.rs | 93 ++++++++++++++++++++++++++++++++++++++ embassy-usb/gen_config.py | 73 ++++++++++++++++++++++++++++++ embassy-usb/src/builder.rs | 5 +- embassy-usb/src/lib.rs | 9 ++-- 6 files changed, 212 insertions(+), 5 deletions(-) create mode 100644 embassy-usb/build.rs create mode 100644 embassy-usb/gen_config.py diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index eb9ba36f4..463e1268c 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -16,6 +16,19 @@ usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] msos-descriptor = [] default = ["usbd-hid"] +# BEGIN AUTOGENERATED CONFIG FEATURES +# Generated by gen_config.py. DO NOT EDIT. +max-interface-count-1 = [] +max-interface-count-2 = [] +max-interface-count-3 = [] +max-interface-count-4 = [] # Default +max-interface-count-5 = [] +max-interface-count-6 = [] +max-interface-count-7 = [] +max-interface-count-8 = [] + +# END AUTOGENERATED CONFIG FEATURES + [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } diff --git a/embassy-usb/README.md b/embassy-usb/README.md index 581c3290f..a3d45b561 100644 --- a/embassy-usb/README.md +++ b/embassy-usb/README.md @@ -1,6 +1,28 @@ # embassy-usb -TODO crate description/ +TODO crate description + +## Configuration + +`embassy-usb` has some configuration settings that are set at compile time, affecting sizes +and counts of buffers. + +They can be set in two ways: + +- Via Cargo features: enable a feature like `-`. `name` must be in lowercase and +use dashes instead of underscores. For example. `max-interface-count-3`. Only a selection of values +is available, check `Cargo.toml` for the list. +- Via environment variables at build time: set the variable named `EMBASSY_USB_`. For example +`EMBASSY_USB_MAX_INTERFACE_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. +Any value can be set, unlike with Cargo features. + +Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting +with different values, compilation fails. + +### `MAX_INTERFACE_COUNT` + +Max amount of interfaces that can be created in one device. Default: 4. + ## Interoperability diff --git a/embassy-usb/build.rs b/embassy-usb/build.rs new file mode 100644 index 000000000..524bdc2f6 --- /dev/null +++ b/embassy-usb/build.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; +use std::fmt::Write; +use std::path::PathBuf; +use std::{env, fs}; + +static CONFIGS: &[(&str, usize)] = &[ + // BEGIN AUTOGENERATED CONFIG FEATURES + // Generated by gen_config.py. DO NOT EDIT. + ("MAX_INTERFACE_COUNT", 4), + // END AUTOGENERATED CONFIG FEATURES +]; + +struct ConfigState { + value: usize, + seen_feature: bool, + seen_env: bool, +} + +fn main() { + let crate_name = env::var("CARGO_PKG_NAME") + .unwrap() + .to_ascii_uppercase() + .replace('-', "_"); + + // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any + // other file changed. + println!("cargo:rerun-if-changed=build.rs"); + + // Rebuild if config envvar changed. + for (name, _) in CONFIGS { + println!("cargo:rerun-if-env-changed={crate_name}_{name}"); + } + + let mut configs = HashMap::new(); + for (name, default) in CONFIGS { + configs.insert( + *name, + ConfigState { + value: *default, + seen_env: false, + seen_feature: false, + }, + ); + } + + let prefix = format!("{crate_name}_"); + for (var, value) in env::vars() { + if let Some(name) = var.strip_prefix(&prefix) { + let Some(cfg) = configs.get_mut(name) else { + panic!("Unknown env var {name}") + }; + + let Ok(value) = value.parse::() else { + panic!("Invalid value for env var {name}: {value}") + }; + + cfg.value = value; + cfg.seen_env = true; + } + + if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") { + if let Some(i) = feature.rfind('_') { + let name = &feature[..i]; + let value = &feature[i + 1..]; + if let Some(cfg) = configs.get_mut(name) { + let Ok(value) = value.parse::() else { + panic!("Invalid value for feature {name}: {value}") + }; + + // envvars take priority. + if !cfg.seen_env { + if cfg.seen_feature { + panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value); + } + + cfg.value = value; + cfg.seen_feature = true; + } + } + } + } + } + + let mut data = String::new(); + + for (name, cfg) in &configs { + writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap(); + } + + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file = out_dir.join("config.rs").to_string_lossy().to_string(); + fs::write(out_file, data).unwrap(); +} diff --git a/embassy-usb/gen_config.py b/embassy-usb/gen_config.py new file mode 100644 index 000000000..55a7fa3c0 --- /dev/null +++ b/embassy-usb/gen_config.py @@ -0,0 +1,73 @@ +import os + +abspath = os.path.abspath(__file__) +dname = os.path.dirname(abspath) +os.chdir(dname) + +features = [] + + +def feature(name, default, min, max, pow2=None): + vals = set() + val = min + while val <= max: + vals.add(val) + if pow2 == True or (isinstance(pow2, int) and val >= pow2): + val *= 2 + else: + val += 1 + vals.add(default) + + features.append( + { + "name": name, + "default": default, + "vals": sorted(list(vals)), + } + ) + + +feature("max_interface_count", default=4, min=1, max=8) + +# ========= Update Cargo.toml + +things = "" +for f in features: + name = f["name"].replace("_", "-") + for val in f["vals"]: + things += f"{name}-{val} = []" + if val == f["default"]: + things += " # Default" + things += "\n" + things += "\n" + +SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n" +HELP = "# Generated by gen_config.py. DO NOT EDIT.\n" +with open("Cargo.toml", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after +with open("Cargo.toml", "w") as f: + f.write(data) + + +# ========= Update build.rs + +things = "" +for f in features: + name = f["name"].upper() + things += f' ("{name}", {f["default"]}),\n' + +SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n" +SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n" +HELP = " // Generated by gen_config.py. DO NOT EDIT.\n" +with open("build.rs", "r") as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + \ + things + " " + SEPARATOR_END + after +with open("build.rs", "w") as f: + f.write(data) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index d1cbf674b..d89fc4017 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -308,7 +308,10 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { }; if self.builder.interfaces.push(iface).is_err() { - panic!("max interface count reached") + panic!( + "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}", + MAX_INTERFACE_COUNT + ) } InterfaceBuilder { diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index aec18524b..f8983318e 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -16,10 +16,16 @@ mod descriptor_reader; pub mod msos; pub mod types; +mod config { + #![allow(unused)] + include!(concat!(env!("OUT_DIR"), "/config.rs")); +} + use embassy_futures::select::{select, Either}; use heapless::Vec; pub use crate::builder::{Builder, Config}; +use crate::config::*; use crate::control::*; use crate::descriptor::*; use crate::descriptor_reader::foreach_endpoint; @@ -71,9 +77,6 @@ pub const CONFIGURATION_NONE: u8 = 0; /// The bConfiguration value for the single configuration supported by this device. pub const CONFIGURATION_VALUE: u8 = 1; -/// Maximum interface count, configured at compile time. -pub const MAX_INTERFACE_COUNT: usize = 4; - const STRING_INDEX_MANUFACTURER: u8 = 1; const STRING_INDEX_PRODUCT: u8 = 2; const STRING_INDEX_SERIAL_NUMBER: u8 = 3; From 3af991ab63d14cfad6f50d28bfb944d1895d1c70 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 22:49:14 +0100 Subject: [PATCH 0564/1575] usb: unify ControlHandler+DeviceStateHandler, route all control requests to all handlers. - Allows classes to handle vendor requests. - Allows classes to use a single handler for multiple interfaces. - Allows classes to access the other events (previously only `reset` was available). --- embassy-usb-logger/src/lib.rs | 1 - embassy-usb/Cargo.toml | 9 + embassy-usb/build.rs | 1 + embassy-usb/gen_config.py | 1 + embassy-usb/src/builder.rs | 45 ++-- embassy-usb/src/class/cdc_acm.rs | 50 ++-- embassy-usb/src/class/cdc_ncm/mod.rs | 83 +++--- embassy-usb/src/class/hid.rs | 172 +++++++------ embassy-usb/src/control.rs | 58 ----- embassy-usb/src/descriptor_reader.rs | 7 +- embassy-usb/src/lib.rs | 243 +++++++++++------- embassy-usb/src/types.rs | 6 +- examples/nrf52840/src/bin/usb_ethernet.rs | 1 - examples/nrf52840/src/bin/usb_hid_keyboard.rs | 25 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 1 - examples/nrf52840/src/bin/usb_serial.rs | 1 - .../nrf52840/src/bin/usb_serial_multitask.rs | 1 - .../nrf52840/src/bin/usb_serial_winusb.rs | 1 - examples/rp/src/bin/usb_ethernet.rs | 1 - examples/rp/src/bin/usb_serial.rs | 1 - examples/stm32f1/src/bin/usb_serial.rs | 1 - examples/stm32f3/src/bin/usb_serial.rs | 1 - examples/stm32f4/src/bin/usb_ethernet.rs | 1 - examples/stm32f4/src/bin/usb_serial.rs | 1 - examples/stm32f7/src/bin/usb_serial.rs | 1 - examples/stm32h7/src/bin/usb_serial.rs | 1 - examples/stm32l4/src/bin/usb_serial.rs | 1 - examples/stm32l5/src/bin/usb_ethernet.rs | 1 - examples/stm32l5/src/bin/usb_hid_mouse.rs | 1 - examples/stm32l5/src/bin/usb_serial.rs | 1 - examples/stm32u5/src/bin/usb_serial.rs | 1 - 31 files changed, 381 insertions(+), 338 deletions(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 6386e2096..1d8dd13ce 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -74,7 +74,6 @@ impl UsbLogger { &mut state.config_descriptor, &mut state.bos_descriptor, &mut state.control_buf, - None, ); // Create classes on the builder. diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 463e1268c..ae3f3ac37 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -27,6 +27,15 @@ max-interface-count-6 = [] max-interface-count-7 = [] max-interface-count-8 = [] +max-handler-count-1 = [] +max-handler-count-2 = [] +max-handler-count-3 = [] +max-handler-count-4 = [] # Default +max-handler-count-5 = [] +max-handler-count-6 = [] +max-handler-count-7 = [] +max-handler-count-8 = [] + # END AUTOGENERATED CONFIG FEATURES [dependencies] diff --git a/embassy-usb/build.rs b/embassy-usb/build.rs index 524bdc2f6..33d32f7d3 100644 --- a/embassy-usb/build.rs +++ b/embassy-usb/build.rs @@ -7,6 +7,7 @@ static CONFIGS: &[(&str, usize)] = &[ // BEGIN AUTOGENERATED CONFIG FEATURES // Generated by gen_config.py. DO NOT EDIT. ("MAX_INTERFACE_COUNT", 4), + ("MAX_HANDLER_COUNT", 4), // END AUTOGENERATED CONFIG FEATURES ]; diff --git a/embassy-usb/gen_config.py b/embassy-usb/gen_config.py index 55a7fa3c0..67ce359cd 100644 --- a/embassy-usb/gen_config.py +++ b/embassy-usb/gen_config.py @@ -28,6 +28,7 @@ def feature(name, default, min, max, pow2=None): feature("max_interface_count", default=4, min=1, max=8) +feature("max_handler_count", default=4, min=1, max=8) # ========= Update Cargo.toml diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index d89fc4017..07b2a177c 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -1,12 +1,12 @@ use heapless::Vec; -use crate::control::ControlHandler; +use crate::config::*; use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::driver::{Driver, Endpoint, EndpointType}; #[cfg(feature = "msos-descriptor")] use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::*; -use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; +use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -122,8 +122,8 @@ impl<'a> Config<'a> { /// [`UsbDevice`] builder. pub struct Builder<'d, D: Driver<'d>> { config: Config<'d>, - handler: Option<&'d dyn DeviceStateHandler>, - interfaces: Vec, MAX_INTERFACE_COUNT>, + handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, + interfaces: Vec, control_buf: &'d mut [u8], driver: D, @@ -151,7 +151,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { bos_descriptor_buf: &'d mut [u8], #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], control_buf: &'d mut [u8], - handler: Option<&'d dyn DeviceStateHandler>, ) -> Self { // Magic values specified in USB-IF ECN on IADs. if config.composite_with_iads @@ -179,9 +178,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { Builder { driver, - handler, config, interfaces: Vec::new(), + handlers: Vec::new(), control_buf, next_string_index: STRING_INDEX_CUSTOM_START, @@ -205,7 +204,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { UsbDevice::build( self.driver, self.config, - self.handler, + self.handlers, self.device_descriptor.into_buf(), self.config_descriptor.into_buf(), self.bos_descriptor.writer.into_buf(), @@ -248,6 +247,26 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { } } + /// Add a Handler. + /// + /// The Handler is called on some USB bus events, and to handle all control requests not already + /// handled by the USB stack. + pub fn handler(&mut self, handler: &'d mut dyn Handler) { + if self.handlers.push(handler).is_err() { + panic!( + "embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}", + MAX_HANDLER_COUNT + ) + } + } + + /// Allocates a new string index. + pub fn string(&mut self) -> StringIndex { + let index = self.next_string_index; + self.next_string_index += 1; + StringIndex::new(index) + } + #[cfg(feature = "msos-descriptor")] /// Add an MS OS 2.0 Descriptor Set. /// @@ -301,10 +320,8 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { let number = self.builder.interfaces.len() as _; let iface = Interface { - handler: None, current_alt_setting: 0, num_alt_settings: 0, - num_strings: 0, }; if self.builder.interfaces.push(iface).is_err() { @@ -350,17 +367,9 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { self.interface_number } - pub fn handler(&mut self, handler: &'d mut dyn ControlHandler) { - self.builder.interfaces[self.interface_number.0 as usize].handler = Some(handler); - } - /// Allocates a new string index. pub fn string(&mut self) -> StringIndex { - let index = self.builder.next_string_index; - self.builder.next_string_index += 1; - self.builder.interfaces[self.interface_number.0 as usize].num_strings += 1; - - StringIndex::new(index) + self.builder.string() } /// Add an alternate setting to the interface and write its descriptor. diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index fb9eaeca7..ff82ad40d 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -6,10 +6,10 @@ use core::sync::atomic::{AtomicBool, Ordering}; use embassy_sync::blocking_mutex::CriticalSectionMutex; -use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; -use crate::Builder; +use crate::{Builder, Handler}; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; @@ -67,6 +67,7 @@ pub struct CdcAcmClass<'d, D: Driver<'d>> { } struct Control<'a> { + comm_if: InterfaceNumber, shared: &'a ControlShared, } @@ -98,7 +99,7 @@ impl<'a> Control<'a> { } } -impl<'d> ControlHandler for Control<'d> { +impl<'d> Handler for Control<'d> { fn reset(&mut self) { let shared = self.shared(); shared.line_coding.lock(|x| x.set(LineCoding::default())); @@ -106,12 +107,18 @@ impl<'d> ControlHandler for Control<'d> { shared.rts.store(false, Ordering::Relaxed); } - fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse { + fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { REQ_SEND_ENCAPSULATED_COMMAND => { // We don't actually support encapsulated commands but pretend we do for standards // compatibility. - OutResponse::Accepted + Some(OutResponse::Accepted) } REQ_SET_LINE_CODING if data.len() >= 7 => { let coding = LineCoding { @@ -123,7 +130,7 @@ impl<'d> ControlHandler for Control<'d> { self.shared().line_coding.lock(|x| x.set(coding)); debug!("Set line coding to: {:?}", coding); - OutResponse::Accepted + Some(OutResponse::Accepted) } REQ_SET_CONTROL_LINE_STATE => { let dtr = (req.value & 0x0001) != 0; @@ -134,13 +141,19 @@ impl<'d> ControlHandler for Control<'d> { shared.rts.store(rts, Ordering::Relaxed); debug!("Set dtr {}, rts {}", dtr, rts); - OutResponse::Accepted + Some(OutResponse::Accepted) } - _ => OutResponse::Rejected, + _ => Some(OutResponse::Rejected), } } - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. REQ_GET_LINE_CODING if req.length == 7 => { @@ -151,9 +164,9 @@ impl<'d> ControlHandler for Control<'d> { buf[4] = coding.stop_bits as u8; buf[5] = coding.parity_type as u8; buf[6] = coding.data_bits; - InResponse::Accepted(&buf[0..7]) + Some(InResponse::Accepted(&buf[0..7])) } - _ => InResponse::Rejected, + _ => Some(InResponse::Rejected), } } } @@ -162,17 +175,12 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, max_packet_size: u16) -> Self { - let control = state.control.write(Control { shared: &state.shared }); - - let control_shared = &state.shared; - assert!(builder.control_buf_len() >= 7); let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE); // Control interface let mut iface = func.interface(); - iface.handler(control); let comm_if = iface.interface_number(); let data_if = u8::from(comm_if) + 1; let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); @@ -213,6 +221,16 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); + drop(func); + + let control = state.control.write(Control { + shared: &state.shared, + comm_if, + }); + builder.handler(control); + + let control_shared = &state.shared; + CdcAcmClass { _comm_ep: comm_ep, _data_if: data_if, diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index d6c7d37e6..262499ccb 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -17,10 +17,10 @@ use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; -use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; -use crate::Builder; +use crate::{Builder, Handler}; pub mod embassy_net; @@ -117,8 +117,7 @@ fn byteify(buf: &mut [u8], data: T) -> &[u8] { /// Internal state for the CDC-NCM class. pub struct State<'a> { - comm_control: MaybeUninit>, - data_control: MaybeUninit, + control: MaybeUninit>, shared: ControlShared, } @@ -126,8 +125,7 @@ impl<'a> State<'a> { /// Create a new `State`. pub fn new() -> Self { Self { - comm_control: MaybeUninit::uninit(), - data_control: MaybeUninit::uninit(), + control: MaybeUninit::uninit(), shared: Default::default(), } } @@ -144,29 +142,55 @@ impl Default for ControlShared { } } -struct CommControl<'a> { +struct Control<'a> { mac_addr_string: StringIndex, shared: &'a ControlShared, mac_addr_str: [u8; 12], + comm_if: InterfaceNumber, + data_if: InterfaceNumber, } -impl<'d> ControlHandler for CommControl<'d> { - fn control_out(&mut self, req: control::Request, _data: &[u8]) -> OutResponse { +impl<'d> Handler for Control<'d> { + fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { + if iface != self.data_if { + return; + } + + match alternate_setting { + ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), + ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), + _ => unreachable!(), + } + } + + fn control_out(&mut self, req: control::Request, _data: &[u8]) -> Option { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { REQ_SEND_ENCAPSULATED_COMMAND => { // We don't actually support encapsulated commands but pretend we do for standards // compatibility. - OutResponse::Accepted + Some(OutResponse::Accepted) } REQ_SET_NTB_INPUT_SIZE => { // TODO - OutResponse::Accepted + Some(OutResponse::Accepted) } - _ => OutResponse::Rejected, + _ => Some(OutResponse::Rejected), } } - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.comm_if.0 as u16) + { + return None; + } + match req.request { REQ_GET_NTB_PARAMETERS => { let res = NtbParameters { @@ -187,9 +211,9 @@ impl<'d> ControlHandler for CommControl<'d> { max_datagram_count: 1, // We only decode 1 packet per NTB }, }; - InResponse::Accepted(byteify(buf, res)) + Some(InResponse::Accepted(byteify(buf, res))) } - _ => InResponse::Rejected, + _ => Some(InResponse::Rejected), } } @@ -214,18 +238,6 @@ impl<'d> ControlHandler for CommControl<'d> { } } -struct DataControl {} - -impl ControlHandler for DataControl { - fn set_alternate_setting(&mut self, alternate_setting: u8) { - match alternate_setting { - ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"), - ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"), - _ => unreachable!(), - } - } -} - /// CDC-NCM class pub struct CdcNcmClass<'d, D: Driver<'d>> { _comm_if: InterfaceNumber, @@ -253,11 +265,6 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { // Control interface let mut iface = func.interface(); let mac_addr_string = iface.string(); - iface.handler(state.comm_control.write(CommControl { - mac_addr_string, - shared: &state.shared, - mac_addr_str: [0; 12], - })); let comm_if = iface.interface_number(); let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE, None); @@ -307,13 +314,23 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { // Data interface let mut iface = func.interface(); - iface.handler(state.data_control.write(DataControl {})); let data_if = iface.interface_number(); let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB, None); let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); + drop(func); + + let control = state.control.write(Control { + mac_addr_string, + shared: &state.shared, + mac_addr_str: [0; 12], + comm_if, + data_if, + }); + builder.handler(control); + CdcNcmClass { _comm_if: comm_if, comm_ep, diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 0283c1124..974268c62 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -9,9 +9,10 @@ use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; -use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; +use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use crate::Builder; +use crate::types::InterfaceNumber; +use crate::{Builder, Handler}; const USB_CLASS_HID: u8 = 0x03; const USB_SUBCLASS_NONE: u8 = 0x00; @@ -100,17 +101,11 @@ fn build<'d, D: Driver<'d>>( config: Config<'d>, with_out_endpoint: bool, ) -> (Option, D::EndpointIn, &'d AtomicUsize) { - let control = state.control.write(Control::new( - config.report_descriptor, - config.request_handler, - &state.out_report_offset, - )); - let len = config.report_descriptor.len(); let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); let mut iface = func.interface(); - iface.handler(control); + let if_num = iface.interface_number(); let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); // HID descriptor @@ -139,6 +134,16 @@ fn build<'d, D: Driver<'d>>( None }; + drop(func); + + let control = state.control.write(Control::new( + if_num, + config.report_descriptor, + config.request_handler, + &state.out_report_offset, + )); + builder.handler(control); + (ep_out, ep_in, &state.out_report_offset) } @@ -400,6 +405,7 @@ pub trait RequestHandler { } struct Control<'d> { + if_num: InterfaceNumber, report_descriptor: &'d [u8], request_handler: Option<&'d dyn RequestHandler>, out_report_offset: &'d AtomicUsize, @@ -408,11 +414,13 @@ struct Control<'d> { impl<'d> Control<'d> { fn new( + if_num: InterfaceNumber, report_descriptor: &'d [u8], request_handler: Option<&'d dyn RequestHandler>, out_report_offset: &'d AtomicUsize, ) -> Self { Control { + if_num, report_descriptor, request_handler, out_report_offset, @@ -438,88 +446,100 @@ impl<'d> Control<'d> { } } -impl<'d> ControlHandler for Control<'d> { +impl<'d> Handler for Control<'d> { fn reset(&mut self) { self.out_report_offset.store(0, Ordering::Release); } - fn get_descriptor<'a>(&'a mut self, req: Request, _buf: &'a mut [u8]) -> InResponse<'a> { - match (req.value >> 8) as u8 { - HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor), - HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor), - _ => InResponse::Rejected, + fn control_out(&mut self, req: Request, data: &[u8]) -> Option { + if (req.request_type, req.recipient, req.index) + != (RequestType::Class, Recipient::Interface, self.if_num.0 as u16) + { + return None; } - } - fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { trace!("HID control_out {:?} {=[u8]:x}", req, data); - if let RequestType::Class = req.request_type { - match req.request { - HID_REQ_SET_IDLE => { - if let Some(handler) = self.request_handler { - let id = req.value as u8; - let id = (id != 0).then(|| ReportId::In(id)); - let dur = u32::from(req.value >> 8); - let dur = if dur == 0 { u32::MAX } else { 4 * dur }; - handler.set_idle_ms(id, dur); - } - OutResponse::Accepted - } - HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { - (Ok(id), Some(handler)) => handler.set_report(id, data), - _ => OutResponse::Rejected, - }, - HID_REQ_SET_PROTOCOL => { - if req.value == 1 { - OutResponse::Accepted - } else { - warn!("HID Boot Protocol is unsupported."); - OutResponse::Rejected // UNSUPPORTED: Boot Protocol - } - } - _ => OutResponse::Rejected, - } - } else { - OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR - } - } - - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { - trace!("HID control_in {:?}", req); match req.request { - HID_REQ_GET_REPORT => { - let size = match ReportId::try_from(req.value) { - Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), - Err(_) => None, - }; - - if let Some(size) = size { - InResponse::Accepted(&buf[0..size]) - } else { - InResponse::Rejected - } - } - HID_REQ_GET_IDLE => { + HID_REQ_SET_IDLE => { if let Some(handler) = self.request_handler { let id = req.value as u8; let id = (id != 0).then(|| ReportId::In(id)); - if let Some(dur) = handler.get_idle_ms(id) { - let dur = u8::try_from(dur / 4).unwrap_or(0); - buf[0] = dur; - InResponse::Accepted(&buf[0..1]) - } else { - InResponse::Rejected - } + let dur = u32::from(req.value >> 8); + let dur = if dur == 0 { u32::MAX } else { 4 * dur }; + handler.set_idle_ms(id, dur); + } + Some(OutResponse::Accepted) + } + HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { + (Ok(id), Some(handler)) => Some(handler.set_report(id, data)), + _ => Some(OutResponse::Rejected), + }, + HID_REQ_SET_PROTOCOL => { + if req.value == 1 { + Some(OutResponse::Accepted) } else { - InResponse::Rejected + warn!("HID Boot Protocol is unsupported."); + Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol } } - HID_REQ_GET_PROTOCOL => { - // UNSUPPORTED: Boot Protocol - buf[0] = 1; - InResponse::Accepted(&buf[0..1]) + _ => Some(OutResponse::Rejected), + } + } + + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + if req.index != self.if_num.0 as u16 { + return None; + } + + match (req.request_type, req.recipient) { + (RequestType::Standard, Recipient::Interface) => match req.request { + Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 { + HID_DESC_DESCTYPE_HID_REPORT => Some(InResponse::Accepted(self.report_descriptor)), + HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)), + _ => Some(InResponse::Rejected), + }, + + _ => Some(InResponse::Rejected), + }, + (RequestType::Class, Recipient::Interface) => { + trace!("HID control_in {:?}", req); + match req.request { + HID_REQ_GET_REPORT => { + let size = match ReportId::try_from(req.value) { + Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), + Err(_) => None, + }; + + if let Some(size) = size { + Some(InResponse::Accepted(&buf[0..size])) + } else { + Some(InResponse::Rejected) + } + } + HID_REQ_GET_IDLE => { + if let Some(handler) = self.request_handler { + let id = req.value as u8; + let id = (id != 0).then(|| ReportId::In(id)); + if let Some(dur) = handler.get_idle_ms(id) { + let dur = u8::try_from(dur / 4).unwrap_or(0); + buf[0] = dur; + Some(InResponse::Accepted(&buf[0..1])) + } else { + Some(InResponse::Rejected) + } + } else { + Some(InResponse::Rejected) + } + } + HID_REQ_GET_PROTOCOL => { + // UNSUPPORTED: Boot Protocol + buf[0] = 1; + Some(InResponse::Accepted(&buf[0..1])) + } + _ => Some(InResponse::Rejected), + } } - _ => InResponse::Rejected, + _ => None, } } } diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 39b499f03..ceccfd85b 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -2,7 +2,6 @@ use core::mem; use crate::driver::Direction; -use crate::types::StringIndex; /// Control request type. #[repr(u8)] @@ -145,60 +144,3 @@ pub enum InResponse<'a> { /// The request was rejected. Rejected, } - -/// Handler for control requests. -/// -/// All methods are optional callbacks that will be called by -/// [`UsbDevice::run()`](crate::UsbDevice::run) -pub trait ControlHandler { - /// Called after a USB reset after the bus reset sequence is complete. - fn reset(&mut self) {} - - /// Called when a "set alternate setting" control request is done on the interface. - fn set_alternate_setting(&mut self, alternate_setting: u8) { - let _ = alternate_setting; - } - - /// Called when a control request is received with direction HostToDevice. - /// - /// # Arguments - /// - /// * `req` - The request from the SETUP packet. - /// * `data` - The data from the request. - fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { - let _ = (req, data); - OutResponse::Rejected - } - - /// Called when a control request is received with direction DeviceToHost. - /// - /// You should write the response somewhere (usually to `buf`, but you may use another buffer - /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. - /// - /// # Arguments - /// - /// * `req` - The request from the SETUP packet. - fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { - let _ = (req, buf); - InResponse::Rejected - } - - /// Called when a GET DESCRIPTOR control request is received on the interface. - /// - /// You should write the response somewhere (usually to `buf`, but you may use another buffer - /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. - /// - /// # Arguments - /// - /// * `req` - The request from the SETUP packet. - fn get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { - let _ = (req, buf); - InResponse::Rejected - } - - /// Called when a GET_DESCRIPTOR STRING control request is received. - fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> { - let _ = (index, lang_id); - None - } -} diff --git a/embassy-usb/src/descriptor_reader.rs b/embassy-usb/src/descriptor_reader.rs index d64bcb73b..05adcce60 100644 --- a/embassy-usb/src/descriptor_reader.rs +++ b/embassy-usb/src/descriptor_reader.rs @@ -1,5 +1,6 @@ use crate::descriptor::descriptor_type; use crate::driver::EndpointAddress; +use crate::types::InterfaceNumber; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -75,7 +76,7 @@ impl<'a, 'b> Iterator for DescriptorIter<'a, 'b> { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointInfo { pub configuration: u8, - pub interface: u8, + pub interface: InterfaceNumber, pub interface_alt: u8, pub ep_address: EndpointAddress, } @@ -83,7 +84,7 @@ pub struct EndpointInfo { pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result<(), ReadError> { let mut ep = EndpointInfo { configuration: 0, - interface: 0, + interface: InterfaceNumber(0), interface_alt: 0, ep_address: EndpointAddress::from(0), }; @@ -96,7 +97,7 @@ pub fn foreach_endpoint(data: &[u8], mut f: impl FnMut(EndpointInfo)) -> Result< ep.configuration = r.read_u8()?; } descriptor_type::INTERFACE => { - ep.interface = r.read_u8()?; + ep.interface = InterfaceNumber(r.read_u8()?); ep.interface_alt = r.read_u8()?; } descriptor_type::ENDPOINT => { diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index f8983318e..bfeccd5fe 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -82,32 +82,87 @@ const STRING_INDEX_PRODUCT: u8 = 2; const STRING_INDEX_SERIAL_NUMBER: u8 = 3; const STRING_INDEX_CUSTOM_START: u8 = 4; -/// A handler trait for changes in the device state of the [UsbDevice]. -pub trait DeviceStateHandler { +/// Handler for device events and control requests. +/// +/// All methods are optional callbacks that will be called by +/// [`UsbDevice::run()`](crate::UsbDevice::run) +pub trait Handler { /// Called when the USB device has been enabled or disabled. - fn enabled(&self, _enabled: bool) {} + fn enabled(&mut self, _enabled: bool) {} - /// Called when the host resets the device. - fn reset(&self) {} + /// Called after a USB reset after the bus reset sequence is complete. + fn reset(&mut self) {} /// Called when the host has set the address of the device to `addr`. - fn addressed(&self, _addr: u8) {} + fn addressed(&mut self, _addr: u8) {} /// Called when the host has enabled or disabled the configuration of the device. - fn configured(&self, _configured: bool) {} + fn configured(&mut self, _configured: bool) {} /// Called when the bus has entered or exited the suspend state. - fn suspended(&self, _suspended: bool) {} + fn suspended(&mut self, _suspended: bool) {} /// Called when remote wakeup feature is enabled or disabled. - fn remote_wakeup_enabled(&self, _enabled: bool) {} + fn remote_wakeup_enabled(&mut self, _enabled: bool) {} + + /// Called when a "set alternate setting" control request is done on the interface. + fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { + let _ = iface; + let _ = alternate_setting; + } + + /// Called when a control request is received with direction HostToDevice. + /// + /// # Arguments + /// + /// * `req` - The request from the SETUP packet. + /// * `data` - The data from the request. + /// + /// # Returns + /// + /// If you didn't handle this request (for example if it's for the wrong interface), return + /// `None`. In this case, the the USB stack will continue calling the other handlers, to see + /// if another handles it. + /// + /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack + /// respond to the control request, and stop calling other handlers. + fn control_out(&mut self, req: Request, data: &[u8]) -> Option { + let _ = (req, data); + None + } + + /// Called when a control request is received with direction DeviceToHost. + /// + /// You should write the response somewhere (usually to `buf`, but you may use another buffer + /// owned by yourself, or a static buffer), then return `InResponse::Accepted(data)`. + /// + /// # Arguments + /// + /// * `req` - The request from the SETUP packet. + /// + /// # Returns + /// + /// If you didn't handle this request (for example if it's for the wrong interface), return + /// `None`. In this case, the the USB stack will continue calling the other handlers, to see + /// if another handles it. + /// + /// If you did, return `Some` with either `Accepted` or `Rejected`. This will make the USB stack + /// respond to the control request, and stop calling other handlers. + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + let _ = (req, buf); + None + } + + /// Called when a GET_DESCRIPTOR STRING control request is received. + fn get_string(&mut self, index: StringIndex, lang_id: u16) -> Option<&str> { + let _ = (index, lang_id); + None + } } -struct Interface<'d> { - handler: Option<&'d mut dyn ControlHandler>, +struct Interface { current_alt_setting: u8, num_alt_settings: u8, - num_strings: u8, } /// Main struct for the USB device stack. @@ -119,7 +174,6 @@ pub struct UsbDevice<'d, D: Driver<'d>> { struct Inner<'d, D: Driver<'d>> { bus: D::Bus, - handler: Option<&'d dyn DeviceStateHandler>, config: Config<'d>, device_descriptor: &'d [u8], @@ -138,7 +192,9 @@ struct Inner<'d, D: Driver<'d>> { /// instead of regular `accept()`. set_address_pending: bool, - interfaces: Vec, MAX_INTERFACE_COUNT>, + interfaces: Vec, + handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, + #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, } @@ -147,11 +203,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { pub(crate) fn build( driver: D, config: Config<'d>, - handler: Option<&'d dyn DeviceStateHandler>, + handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, device_descriptor: &'d [u8], config_descriptor: &'d [u8], bos_descriptor: &'d [u8], - interfaces: Vec, MAX_INTERFACE_COUNT>, + interfaces: Vec, control_buf: &'d mut [u8], #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, ) -> UsbDevice<'d, D> { @@ -165,7 +221,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { inner: Inner { bus, config, - handler, device_descriptor, config_descriptor, bos_descriptor, @@ -177,6 +232,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { address: 0, set_address_pending: false, interfaces, + handlers, #[cfg(feature = "msos-descriptor")] msos_descriptor, }, @@ -221,7 +277,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { self.inner.suspended = false; self.inner.remote_wakeup_enabled = false; - if let Some(h) = &self.inner.handler { + for h in &mut self.inner.handlers { h.enabled(false); } } @@ -250,7 +306,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { self.inner.bus.remote_wakeup().await?; self.inner.suspended = false; - if let Some(h) = &self.inner.handler { + for h in &mut self.inner.handlers { h.suspended(false); } @@ -361,29 +417,29 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.remote_wakeup_enabled = false; self.address = 0; - for iface in self.interfaces.iter_mut() { - iface.current_alt_setting = 0; - if let Some(h) = &mut iface.handler { - h.reset(); - h.set_alternate_setting(0); - } + for h in &mut self.handlers { + h.reset(); } - if let Some(h) = &self.handler { - h.reset(); + for (i, iface) in self.interfaces.iter_mut().enumerate() { + iface.current_alt_setting = 0; + + for h in &mut self.handlers { + h.set_alternate_setting(InterfaceNumber::new(i as _), 0); + } } } Event::Resume => { trace!("usb: resume"); self.suspended = false; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.suspended(false); } } Event::Suspend => { trace!("usb: suspend"); self.suspended = true; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.suspended(true); } } @@ -392,7 +448,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.bus.enable().await; self.device_state = UsbDeviceState::Default; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.enabled(true); } } @@ -401,7 +457,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.bus.disable().await; self.device_state = UsbDeviceState::Unpowered; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.enabled(false); } } @@ -416,14 +472,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { (RequestType::Standard, Recipient::Device) => match (req.request, req.value) { (Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { self.remote_wakeup_enabled = false; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.remote_wakeup_enabled(false); } OutResponse::Accepted } (Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { self.remote_wakeup_enabled = true; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.remote_wakeup_enabled(true); } OutResponse::Accepted @@ -432,7 +488,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { self.address = addr as u8; self.set_address_pending = true; self.device_state = UsbDeviceState::Addressed; - if let Some(h) = &self.handler { + for h in &mut self.handlers { h.addressed(self.address); } OutResponse::Accepted @@ -443,14 +499,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { // Enable all endpoints of selected alt settings. foreach_endpoint(self.config_descriptor, |ep| { - let iface = &self.interfaces[ep.interface as usize]; + let iface = &self.interfaces[ep.interface.0 as usize]; self.bus .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); }) .unwrap(); - // Notify handler. - if let Some(h) = &self.handler { + // Notify handlers. + for h in &mut self.handlers { h.configured(true); } @@ -468,8 +524,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { }) .unwrap(); - // Notify handler. - if let Some(h) = &self.handler { + // Notify handlers. + for h in &mut self.handlers { h.configured(false); } @@ -479,7 +535,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { _ => OutResponse::Rejected, }, (RequestType::Standard, Recipient::Interface) => { - let iface = match self.interfaces.get_mut(req.index as usize) { + let iface_num = InterfaceNumber::new(req.index as _); + let iface = match self.interfaces.get_mut(iface_num.0 as usize) { Some(iface) => iface, None => return OutResponse::Rejected, }; @@ -497,7 +554,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { // Enable/disable EPs of this interface as needed. foreach_endpoint(self.config_descriptor, |ep| { - if ep.interface == req.index as u8 { + if ep.interface == iface_num { self.bus .endpoint_set_enabled(ep.ep_address, iface.current_alt_setting == ep.interface_alt); } @@ -506,8 +563,8 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { // TODO check it is valid (not out of range) - if let Some(handler) = &mut iface.handler { - handler.set_alternate_setting(new_altsetting); + for h in &mut self.handlers { + h.set_alternate_setting(iface_num, new_altsetting); } OutResponse::Accepted } @@ -527,17 +584,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { } _ => OutResponse::Rejected, }, - (RequestType::Class, Recipient::Interface) => { - let iface = match self.interfaces.get_mut(req.index as usize) { - Some(iface) => iface, - None => return OutResponse::Rejected, - }; - match &mut iface.handler { - Some(handler) => handler.control_out(req, data), - None => OutResponse::Rejected, - } - } - _ => OutResponse::Rejected, + _ => self.handle_control_out_delegated(req, data), } } @@ -582,11 +629,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { buf[0] = iface.current_alt_setting; InResponse::Accepted(&buf[..1]) } - Request::GET_DESCRIPTOR => match &mut iface.handler { - Some(handler) => handler.get_descriptor(req, buf), - None => InResponse::Rejected, - }, - _ => InResponse::Rejected, + _ => self.handle_control_in_delegated(req, buf), } } (RequestType::Standard, Recipient::Endpoint) => match req.request { @@ -601,34 +644,48 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { } _ => InResponse::Rejected, }, - (RequestType::Class, Recipient::Interface) => { - let iface = match self.interfaces.get_mut(req.index as usize) { - Some(iface) => iface, - None => return InResponse::Rejected, - }; - - match &mut iface.handler { - Some(handler) => handler.control_in(req, buf), - None => InResponse::Rejected, - } - } #[cfg(feature = "msos-descriptor")] (RequestType::Vendor, Recipient::Device) => { - if !self.msos_descriptor.is_empty() { - if req.request == self.msos_descriptor.vendor_code() && req.index == 7 { - // Index 7 retrieves the MS OS Descriptor Set - InResponse::Accepted(self.msos_descriptor.descriptor()) - } else { - InResponse::Rejected - } + if !self.msos_descriptor.is_empty() + && req.request == self.msos_descriptor.vendor_code() + && req.index == 7 + { + // Index 7 retrieves the MS OS Descriptor Set + InResponse::Accepted(self.msos_descriptor.descriptor()) } else { - InResponse::Rejected + self.handle_control_in_delegated(req, buf) } } - _ => InResponse::Rejected, + _ => self.handle_control_in_delegated(req, buf), } } + fn handle_control_out_delegated(&mut self, req: Request, data: &[u8]) -> OutResponse { + for h in &mut self.handlers { + if let Some(res) = h.control_out(req, data) { + return res; + } + } + OutResponse::Rejected + } + + fn handle_control_in_delegated<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { + unsafe fn extend_lifetime<'x, 'y>(r: InResponse<'x>) -> InResponse<'y> { + core::mem::transmute(r) + } + + for h in &mut self.handlers { + if let Some(res) = h.control_in(req, buf) { + // safety: the borrow checker isn't smart enough to know this pattern (returning a + // borrowed value from inside the loop) is sound. Workaround by unsafely extending lifetime. + // Also, Polonius (the WIP new borrow checker) does accept it. + + return unsafe { extend_lifetime(res) }; + } + } + InResponse::Rejected + } + fn handle_get_descriptor<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { let (dtype, index) = req.descriptor_type_index(); @@ -649,30 +706,16 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { STRING_INDEX_PRODUCT => self.config.product, STRING_INDEX_SERIAL_NUMBER => self.config.serial_number, _ => { - // Find out which iface owns this string index. - let mut index_left = index - STRING_INDEX_CUSTOM_START; - let mut the_iface = None; - for iface in &mut self.interfaces { - if index_left < iface.num_strings { - the_iface = Some(iface); + let mut s = None; + for handler in &mut self.handlers { + let index = StringIndex::new(index); + let lang_id = req.index; + if let Some(res) = handler.get_string(index, lang_id) { + s = Some(res); break; } - index_left -= iface.num_strings; - } - - if let Some(iface) = the_iface { - if let Some(handler) = &mut iface.handler { - let index = StringIndex::new(index); - let lang_id = req.index; - handler.get_string(index, lang_id) - } else { - warn!("String requested to an interface with no handler."); - None - } - } else { - warn!("String requested but didn't match to an interface."); - None } + s } }; diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index 1743e61ff..15d195002 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -1,9 +1,9 @@ //! USB types. /// A handle for a USB interface that contains its number. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InterfaceNumber(pub(crate) u8); +pub struct InterfaceNumber(pub u8); impl InterfaceNumber { pub(crate) fn new(index: u8) -> InterfaceNumber { @@ -20,7 +20,7 @@ impl From for u8 { /// A handle for a USB string descriptor that contains its index. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct StringIndex(u8); +pub struct StringIndex(pub u8); impl StringIndex { pub(crate) fn new(index: u8) -> StringIndex { diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 699666cee..979780896 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -82,7 +82,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 017cac197..3d8a114cd 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -16,7 +16,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; -use embassy_usb::{Builder, Config, DeviceStateHandler}; +use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -52,7 +52,7 @@ async fn main(_spawner: Spawner) { let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; - let device_state_handler = MyDeviceStateHandler::new(); + let mut device_handler = MyDeviceHandler::new(); let mut state = State::new(); @@ -63,9 +63,10 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - Some(&device_state_handler), ); + builder.handler(&mut device_handler); + // Create classes on the builder. let config = embassy_usb::class::hid::Config { report_descriptor: KeyboardReport::desc(), @@ -164,20 +165,20 @@ impl RequestHandler for MyRequestHandler { } } -struct MyDeviceStateHandler { +struct MyDeviceHandler { configured: AtomicBool, } -impl MyDeviceStateHandler { +impl MyDeviceHandler { fn new() -> Self { - MyDeviceStateHandler { + MyDeviceHandler { configured: AtomicBool::new(false), } } } -impl DeviceStateHandler for MyDeviceStateHandler { - fn enabled(&self, enabled: bool) { +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { self.configured.store(false, Ordering::Relaxed); SUSPENDED.store(false, Ordering::Release); if enabled { @@ -187,17 +188,17 @@ impl DeviceStateHandler for MyDeviceStateHandler { } } - fn reset(&self) { + fn reset(&mut self) { self.configured.store(false, Ordering::Relaxed); info!("Bus reset, the Vbus current limit is 100mA"); } - fn addressed(&self, addr: u8) { + fn addressed(&mut self, addr: u8) { self.configured.store(false, Ordering::Relaxed); info!("USB address set to: {}", addr); } - fn configured(&self, configured: bool) { + fn configured(&mut self, configured: bool) { self.configured.store(configured, Ordering::Relaxed); if configured { info!("Device configured, it may now draw up to the configured current limit from Vbus.") @@ -206,7 +207,7 @@ impl DeviceStateHandler for MyDeviceStateHandler { } } - fn suspended(&self, suspended: bool) { + fn suspended(&mut self, suspended: bool) { if suspended { info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); SUSPENDED.store(true, Ordering::Release); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index a5849129a..d7c9d55b7 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 18b6f25b9..102d7ea60 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 3532d3f82..558d4ba60 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -83,7 +83,6 @@ async fn main(spawner: Spawner) { &mut res.config_descriptor, &mut res.bos_descriptor, &mut res.control_buf, - None, ); // Create classes on the builder. diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index f4b828de6..ade6af527 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -65,7 +65,6 @@ async fn main(_spawner: Spawner) { &mut bos_descriptor, &mut msos_descriptor, &mut control_buf, - None, ); builder.msos_descriptor(windows_version::WIN8_1, 2); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 104b25d39..66a6ed4d0 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -73,7 +73,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index b7d6493b4..a991082ee 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -53,7 +53,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index ad92cdeb2..07cad84ef 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index f6d27c860..5b4e0a91a 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -55,7 +55,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index cf2885ae5..4a16aac07 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -82,7 +82,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 014647762..baabc1a2d 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 688bd0dab..5fd9d2ec9 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -58,7 +58,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index b319d12c3..9ef520ae2 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -57,7 +57,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 3e38b10a3..663f60d52 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index e5a46b064..98ec0e836 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -79,7 +79,6 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], - None, ); // Our MAC addr. diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index d38ed7496..e3bbe9d09 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -51,7 +51,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 7562a4e96..66ccacb73 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index c846836b0..8cd3bf2f4 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -59,7 +59,6 @@ async fn main(_spawner: Spawner) { &mut config_descriptor, &mut bos_descriptor, &mut control_buf, - None, ); // Create classes on the builder. From 86487db5d1773d2a764ab340051d70cfa40e4714 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Feb 2023 23:45:01 +0100 Subject: [PATCH 0565/1575] usb: use InterfaceNumber in msos. --- embassy-usb/src/builder.rs | 2 +- embassy-usb/src/msos.rs | 7 ++++--- embassy-usb/src/types.rs | 2 ++ examples/nrf52840/src/bin/usb_serial_winusb.rs | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 07b2a177c..305dfa02e 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -346,7 +346,7 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { } if !self.builder.msos_descriptor.is_in_function_subset() { - self.builder.msos_descriptor.function(self.first_interface.0); + self.builder.msos_descriptor.function(self.first_interface); } #[cfg(feature = "msos-descriptor")] diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 19ed34979..b1e0335ee 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -7,6 +7,7 @@ use core::mem::size_of; use super::{capability_type, BosWriter}; +use crate::types::InterfaceNumber; /// A serialized Microsoft OS 2.0 Descriptor set. /// @@ -125,7 +126,7 @@ impl<'d> MsOsDescriptorWriter<'d> { } /// Add a function subset. - pub fn function(&mut self, first_interface: u8) { + pub fn function(&mut self, first_interface: InterfaceNumber) { assert!( self.config_mark.is_some(), "MsOsDescriptorWriter: function subset requires a configuration subset" @@ -376,14 +377,14 @@ impl DescriptorSet for ConfigurationSubsetHeader { pub struct FunctionSubsetHeader { wLength: u16, wDescriptorType: u16, - bFirstInterface: u8, + bFirstInterface: InterfaceNumber, bReserved: u8, wSubsetLength: u16, } impl FunctionSubsetHeader { /// Creates a function subset header - pub fn new(first_interface: u8) -> Self { + pub fn new(first_interface: InterfaceNumber) -> Self { FunctionSubsetHeader { wLength: (size_of::() as u16).to_le(), wDescriptorType: (Self::TYPE as u16).to_le(), diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index 15d195002..c7a47f7e4 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -3,6 +3,7 @@ /// A handle for a USB interface that contains its number. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(transparent)] pub struct InterfaceNumber(pub u8); impl InterfaceNumber { @@ -20,6 +21,7 @@ impl From for u8 { /// A handle for a USB string descriptor that contains its index. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(transparent)] pub struct StringIndex(pub u8); impl StringIndex { diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index ade6af527..6561fc3b4 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -12,6 +12,7 @@ use embassy_nrf::{interrupt, pac}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::msos::{self, windows_version}; +use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; @@ -77,7 +78,7 @@ async fn main(_spawner: Spawner) { // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. let msos_writer = builder.msos_writer(); msos_writer.configuration(0); - msos_writer.function(0); + msos_writer.function(InterfaceNumber(0)); msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new( "DeviceInterfaceGUIDs", From da6b1e8399cf977698e4a630f0862f698cba2b83 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Wed, 8 Feb 2023 17:52:49 +0200 Subject: [PATCH 0566/1575] Reset rng in case of clock or seed error --- embassy-stm32/src/rng.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 10fc4a75e..c487b759f 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -90,8 +90,10 @@ impl<'d, T: Instance> Rng<'d, T> { impl<'d, T: Instance> RngCore for Rng<'d, T> { fn next_u32(&mut self) -> u32 { loop { - let bits = unsafe { T::regs().sr().read() }; - if bits.drdy() { + let sr = unsafe { T::regs().sr().read() }; + if sr.seis() | sr.ceis() { + self.reset(); + } else if sr.drdy() { return unsafe { T::regs().dr().read() }; } } From bab4277a8620e9a5e2347ff919d677e9c87528c9 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Wed, 8 Feb 2023 17:57:37 +0200 Subject: [PATCH 0567/1575] hack for STM32WL, rcc reset in case of seed error The STM32WL series has a more complicated rng device that gets stuck when there is a seed error. --- embassy-stm32/src/rng.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index c487b759f..abc625835 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -32,6 +32,11 @@ impl<'d, T: Instance> Rng<'d, T> { } pub fn reset(&mut self) { + //stm32wl gets stuck if there is a seed error + #[cfg(stm32wl)] + if unsafe { T::regs().sr().read().seis()} { + T::reset(); + } unsafe { T::regs().cr().modify(|reg| { reg.set_rngen(true); From ac3e225988f1666eaa773a20561cbf40a3295613 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Feb 2023 17:12:22 +0100 Subject: [PATCH 0568/1575] Update Rust nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f2adc4357..da75fa53a 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-11-22" +channel = "nightly-2023-02-07" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From 5edb3052e6e7c26e8b936ca1633e32d18c4bd67c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Feb 2023 17:52:02 +0100 Subject: [PATCH 0569/1575] net: reexport driver crate. --- embassy-net/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 8d0119f67..0f694ee70 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -8,7 +8,9 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub mod device; +pub use embassy_net_driver as driver; + +mod device; #[cfg(feature = "tcp")] pub mod tcp; #[cfg(feature = "udp")] From ab4b3fa96dc320f034f5437c9540897f553519be Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 9 Feb 2023 12:42:57 +0200 Subject: [PATCH 0570/1575] update stm32-data to include rng_v2 --- stm32-data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stm32-data b/stm32-data index cc93f9d10..662529829 160000 --- a/stm32-data +++ b/stm32-data @@ -1 +1 @@ -Subproject commit cc93f9d10395077770bebefb6b9488e06b0e5811 +Subproject commit 66252982939014e94fc4a1b7423c30c3d108ae0b From 43d018b67ff98a68e7d1d8c122f803a3d90c9381 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 9 Feb 2023 12:44:20 +0200 Subject: [PATCH 0571/1575] Use rng_v2 cfg instead of chip specific for seed error recover hack --- embassy-stm32/src/rng.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index abc625835..cd3cc94ce 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -32,8 +32,7 @@ impl<'d, T: Instance> Rng<'d, T> { } pub fn reset(&mut self) { - //stm32wl gets stuck if there is a seed error - #[cfg(stm32wl)] + #[cfg(rng_v2)] if unsafe { T::regs().sr().read().seis()} { T::reset(); } From 2b6654541d2d195636cf7a9c5ef3fdced0583e21 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 9 Feb 2023 13:01:44 +0200 Subject: [PATCH 0572/1575] rustfmt --- embassy-stm32/src/rng.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index cd3cc94ce..1e16b8478 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -32,8 +32,9 @@ impl<'d, T: Instance> Rng<'d, T> { } pub fn reset(&mut self) { + // rng_v2 locks up on seed error, needs reset #[cfg(rng_v2)] - if unsafe { T::regs().sr().read().seis()} { + if unsafe { T::regs().sr().read().seis() } { T::reset(); } unsafe { From a4371e9544f284927aae3b0de0ff5318f81acc80 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 9 Feb 2023 19:22:06 -0500 Subject: [PATCH 0573/1575] Add from_hz function for Duration. --- embassy-time/src/duration.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index d3c6f42a9..846a9c3d5 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -81,6 +81,11 @@ impl Duration { } } + /// Creates a duration corresponding to the specified Hz. + pub const fn from_hz(hz: u64) -> Duration { + Duration { ticks: TICK_HZ / hz } + } + /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. pub fn checked_add(self, rhs: Duration) -> Option { self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks }) From bd7b3bd455fc5946a3944bd931acfdf255929cb6 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 9 Feb 2023 20:57:27 -0500 Subject: [PATCH 0574/1575] Clamp ticks to 1 and round to nearest. --- embassy-time/src/duration.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index 846a9c3d5..9d0bab2dd 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -82,8 +82,17 @@ impl Duration { } /// Creates a duration corresponding to the specified Hz. + /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1 + /// tick. Doing so will not deadlock, but will certainly not produce the desired output. pub const fn from_hz(hz: u64) -> Duration { - Duration { ticks: TICK_HZ / hz } + let ticks = { + if hz >= TICK_HZ { + 1 + } else { + (TICK_HZ + hz / 2) / hz + } + }; + Duration { ticks } } /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. From 9cfea693edec5af17ba698f64b3f0a168ad92944 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 31 Jan 2023 22:06:41 +0100 Subject: [PATCH 0575/1575] Add DNS socket to embassy-net --- embassy-net/Cargo.toml | 4 +- embassy-net/src/dns.rs | 114 ++++++++++++++++++++++++++++++++ embassy-net/src/lib.rs | 2 + examples/std/Cargo.toml | 2 +- examples/std/src/bin/net_dns.rs | 102 ++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 embassy-net/src/dns.rs create mode 100644 examples/std/src/bin/net_dns.rs diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 4ec340b7a..6b3468283 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] default = [] -std = [] +std = ["smoltcp/alloc", "managed/std"] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] @@ -22,7 +22,7 @@ unstable-traits = [] udp = ["smoltcp/socket-udp"] tcp = ["smoltcp/socket-tcp"] -dns = ["smoltcp/socket-dns"] +dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"] dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs new file mode 100644 index 000000000..f18750cc3 --- /dev/null +++ b/embassy-net/src/dns.rs @@ -0,0 +1,114 @@ +//! DNS socket with async support. +use core::cell::RefCell; +use core::future::poll_fn; +use core::mem; +use core::task::Poll; + +use embassy_net_driver::Driver; +use heapless::Vec; +use managed::ManagedSlice; +use smoltcp::iface::{Interface, SocketHandle}; +pub use smoltcp::socket::dns::DnsQuery; +use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; +pub use smoltcp::wire::{DnsQueryType, IpAddress}; + +use crate::{SocketStack, Stack}; + +/// Errors returned by DnsSocket. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// No available query slot + NoFreeSlot, + /// Invalid name + InvalidName, + /// Name too long + NameTooLong, + /// Name lookup failed + Failed, +} + +impl From for Error { + fn from(_: GetQueryResultError) -> Self { + Self::Failed + } +} + +impl From for Error { + fn from(e: StartQueryError) -> Self { + match e { + StartQueryError::NoFreeSlot => Self::NoFreeSlot, + StartQueryError::InvalidName => Self::InvalidName, + StartQueryError::NameTooLong => Self::NameTooLong, + } + } +} + +/// Async socket for making DNS queries. +pub struct DnsSocket<'a> { + stack: &'a RefCell, + handle: SocketHandle, +} + +impl<'a> DnsSocket<'a> { + /// Create a new DNS socket using the provided stack and query storage. + /// + /// DNS servers are derived from the stack configuration. + /// + /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. + pub fn new(stack: &'a Stack, queries: Q) -> Self + where + D: Driver + 'static, + Q: Into>>, + { + let servers = stack + .config() + .map(|c| { + let v: Vec = c.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + v + }) + .unwrap_or(Vec::new()); + let s = &mut *stack.socket.borrow_mut(); + let queries: ManagedSlice<'static, Option> = unsafe { mem::transmute(queries.into()) }; + + let handle = s.sockets.add(dns::Socket::new(&servers[..], queries)); + Self { + stack: &stack.socket, + handle, + } + } + + fn with_mut(&mut self, f: impl FnOnce(&mut dns::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(); + res + } + + /// Make a query for a given name and return the corresponding IP addresses. + pub async fn query(&mut self, name: &str, qtype: DnsQueryType) -> Result, Error> { + let query = match { self.with_mut(|s, i| s.start_query(i.context(), name, qtype)) } { + Ok(handle) => handle, + Err(e) => return Err(e.into()), + }; + + poll_fn(|cx| { + self.with_mut(|s, _| match s.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(GetQueryResultError::Pending) => { + s.register_query_waker(query, cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e.into())), + }) + }) + .await + } +} + +impl<'a> Drop for DnsSocket<'a> { + fn drop(&mut self) { + self.stack.borrow_mut().sockets.remove(self.handle); + } +} diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 0f694ee70..ae447d063 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -11,6 +11,8 @@ pub(crate) mod fmt; pub use embassy_net_driver as driver; mod device; +#[cfg(feature = "dns")] +pub mod dns; #[cfg(feature = "tcp")] pub mod tcp; #[cfg(feature = "udp")] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index af1481e08..8087df09a 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 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"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs new file mode 100644 index 000000000..6203f8370 --- /dev/null +++ b/examples/std/src/bin/net_dns.rs @@ -0,0 +1,102 @@ +#![feature(type_alias_impl_trait)] + +use std::default::Default; + +use clap::Parser; +use embassy_executor::{Executor, Spawner}; +use embassy_net::dns::{DnsQueryType, DnsSocket}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use heapless::Vec; +use log::*; +use rand_core::{OsRng, RngCore}; +use static_cell::StaticCell; + +#[path = "../tuntap.rs"] +mod tuntap; + +use crate::tuntap::TunTapDevice; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +#[derive(Parser)] +#[clap(version = "1.0")] +struct Opts { + /// TAP device name + #[clap(long, default_value = "tap0")] + tap: String, + /// use a static IP instead of DHCP + #[clap(long)] + static_ip: bool, +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::task] +async fn main_task(spawner: Spawner) { + let opts: Opts = Opts::parse(); + + // Init network device + let device = TunTapDevice::new(&opts.tap).unwrap(); + + // Choose between dhcp or static ip + let config = if opts.static_ip { + Config::Static(embassy_net::StaticConfig { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), + dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()]) + .unwrap(), + gateway: Some(Ipv4Address::new(192, 168, 69, 100)), + }) + } else { + Config::Dhcp(Default::default()) + }; + + // Generate random seed + let mut seed = [0; 8]; + OsRng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + + // Launch network task + spawner.spawn(net_task(stack)).unwrap(); + + // Then we can use it! + + let mut socket = DnsSocket::new(stack, vec![]); + + let host = "example.com"; + info!("querying host {:?}...", host); + match socket.query(host, DnsQueryType::A).await { + Ok(r) => { + info!("query response: {:?}", r); + } + Err(e) => { + warn!("query error: {:?}", e); + } + }; +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .filter_module("async_io", log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(main_task(spawner)).unwrap(); + }); +} From c203cefe0103ae150be514875b6b110b1f9a3a90 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 6 Feb 2023 20:18:12 +0100 Subject: [PATCH 0576/1575] Handle cancellation --- embassy-net/Cargo.toml | 1 + embassy-net/src/dns.rs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6b3468283..6eea8c307 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -40,6 +40,7 @@ smoltcp = { version = "0.9.0", default-features = false, features = [ ]} embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index f18750cc3..2d2e7b79b 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -11,6 +11,7 @@ use smoltcp::iface::{Interface, SocketHandle}; pub use smoltcp::socket::dns::DnsQuery; use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; +use embassy_hal_common::drop::OnDrop; use crate::{SocketStack, Stack}; @@ -93,7 +94,15 @@ impl<'a> DnsSocket<'a> { Err(e) => return Err(e.into()), }; - poll_fn(|cx| { + let handle = self.handle; + let drop = OnDrop::new(|| { + let s = &mut *self.stack.borrow_mut(); + let socket = s.sockets.get_mut::(handle); + socket.cancel_query(query); + s.waker.wake(); + }); + + let res = poll_fn(|cx| { self.with_mut(|s, _| match s.get_query_result(query) { Ok(addrs) => Poll::Ready(Ok(addrs)), Err(GetQueryResultError::Pending) => { @@ -103,7 +112,10 @@ impl<'a> DnsSocket<'a> { Err(e) => Poll::Ready(Err(e.into())), }) }) - .await + .await; + + drop.defuse(); + res } } From 614740a1b2ea1cada16e3391cb86d0394f64edfc Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 6 Feb 2023 20:36:08 +0100 Subject: [PATCH 0577/1575] cargo fmt --- embassy-net/src/dns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 2d2e7b79b..e98247bfd 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -4,6 +4,7 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_hal_common::drop::OnDrop; use embassy_net_driver::Driver; use heapless::Vec; use managed::ManagedSlice; @@ -11,7 +12,6 @@ use smoltcp::iface::{Interface, SocketHandle}; pub use smoltcp::socket::dns::DnsQuery; use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; -use embassy_hal_common::drop::OnDrop; use crate::{SocketStack, Stack}; From cd440a49d677f7dfc09e405d99b87a49fba9ba31 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 17:43:23 +0100 Subject: [PATCH 0578/1575] Rewrite to use a single socket --- embassy-net/Cargo.toml | 2 +- embassy-net/src/dns.rs | 124 +++++++++++++------------------- embassy-net/src/lib.rs | 83 ++++++++++++++++++++- examples/std/src/bin/net_dns.rs | 3 +- 4 files changed, 133 insertions(+), 79 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6eea8c307..53778899b 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -52,5 +52,5 @@ generic-array = { version = "0.14.4", default-features = false } 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" -embedded-nal-async = { version = "0.3.0", optional = true } +embedded-nal-async = { version = "0.4.0", optional = true } atomic-polyfill = { version = "1.0" } diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index e98247bfd..1815d258f 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,19 +1,10 @@ //! DNS socket with async support. -use core::cell::RefCell; -use core::future::poll_fn; -use core::mem; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; -use embassy_net_driver::Driver; use heapless::Vec; -use managed::ManagedSlice; -use smoltcp::iface::{Interface, SocketHandle}; -pub use smoltcp::socket::dns::DnsQuery; -use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; +pub use smoltcp::socket::dns::{DnsQuery, Socket, MAX_ADDRESS_COUNT}; +pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; -use crate::{SocketStack, Stack}; +use crate::{Driver, Stack}; /// Errors returned by DnsSocket. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -46,81 +37,64 @@ impl From for Error { } /// Async socket for making DNS queries. -pub struct DnsSocket<'a> { - stack: &'a RefCell, - handle: SocketHandle, +pub struct DnsSocket<'a, D> +where + D: Driver + 'static, +{ + stack: &'a Stack, } -impl<'a> DnsSocket<'a> { +impl<'a, D> DnsSocket<'a, D> +where + D: Driver + 'static, +{ /// Create a new DNS socket using the provided stack and query storage. /// /// DNS servers are derived from the stack configuration. /// /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. - pub fn new(stack: &'a Stack, queries: Q) -> Self - where - D: Driver + 'static, - Q: Into>>, - { - let servers = stack - .config() - .map(|c| { - let v: Vec = c.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - v - }) - .unwrap_or(Vec::new()); - let s = &mut *stack.socket.borrow_mut(); - let queries: ManagedSlice<'static, Option> = unsafe { mem::transmute(queries.into()) }; - - let handle = s.sockets.add(dns::Socket::new(&servers[..], queries)); - Self { - stack: &stack.socket, - handle, - } - } - - fn with_mut(&mut self, f: impl FnOnce(&mut dns::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(); - res + pub fn new(stack: &'a Stack) -> Self { + Self { stack } } /// Make a query for a given name and return the corresponding IP addresses. - pub async fn query(&mut self, name: &str, qtype: DnsQueryType) -> Result, Error> { - let query = match { self.with_mut(|s, i| s.start_query(i.context(), name, qtype)) } { - Ok(handle) => handle, - Err(e) => return Err(e.into()), + pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { + self.stack.dns_query(name, qtype).await + } +} + +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D> +where + D: Driver + 'static, +{ + type Error = Error; + + async fn get_host_by_name( + &self, + host: &str, + addr_type: embedded_nal_async::AddrType, + ) -> Result { + use embedded_nal_async::{AddrType, IpAddr}; + let qtype = match addr_type { + AddrType::IPv6 => DnsQueryType::Aaaa, + _ => DnsQueryType::A, }; - - let handle = self.handle; - let drop = OnDrop::new(|| { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(handle); - socket.cancel_query(query); - s.waker.wake(); - }); - - let res = poll_fn(|cx| { - self.with_mut(|s, _| match s.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), - Err(GetQueryResultError::Pending) => { - s.register_query_waker(query, cx.waker()); - Poll::Pending - } - Err(e) => Poll::Ready(Err(e.into())), + let addrs = self.query(host, qtype).await?; + if let Some(first) = addrs.get(0) { + Ok(match first { + IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), + IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), }) - }) - .await; + } else { + Err(Error::Failed) + } + } - drop.defuse(); - res - } -} - -impl<'a> Drop for DnsSocket<'a> { - fn drop(&mut self) { - self.stack.borrow_mut().sockets.remove(self.handle); + async fn get_host_by_address( + &self, + _addr: embedded_nal_async::IpAddr, + ) -> Result, Self::Error> { + todo!() } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ae447d063..b63aa83df 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -48,15 +48,22 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; +const MAX_QUERIES: usize = 2; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], + #[cfg(feature = "dns")] + queries: Option<[Option; MAX_QUERIES]>, } impl StackResources { pub fn new() -> Self { + #[cfg(feature = "dns")] + const INIT: Option = None; Self { sockets: [SocketStorage::EMPTY; SOCK], + #[cfg(feature = "dns")] + queries: Some([INIT; MAX_QUERIES]), } } } @@ -109,6 +116,8 @@ struct Inner { config: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, + #[cfg(feature = "dns")] + dns_socket: Option, } pub(crate) struct SocketStack { @@ -153,6 +162,8 @@ impl Stack { config: None, #[cfg(feature = "dhcpv4")] dhcp_socket: None, + #[cfg(feature = "dns")] + dns_socket: None, }; let mut socket = SocketStack { sockets, @@ -161,8 +172,17 @@ impl Stack { next_local_port, }; + #[cfg(feature = "dns")] + { + if let Some(queries) = resources.queries.take() { + inner.dns_socket = Some(socket.sockets.add(dns::Socket::new(&[], queries))); + } + } + match config { - Config::Static(config) => inner.apply_config(&mut socket, config), + Config::Static(config) => { + inner.apply_config(&mut socket, config); + } #[cfg(feature = "dhcpv4")] Config::Dhcp(config) => { let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); @@ -210,6 +230,59 @@ impl Stack { .await; unreachable!() } + + #[cfg(feature = "dns")] + async fn dns_query( + &self, + name: &str, + qtype: dns::DnsQueryType, + ) -> Result, dns::Error> { + let query = self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + match socket.start_query(s.iface.context(), name, qtype) { + Ok(handle) => Ok(handle), + Err(e) => Err(e.into()), + } + } else { + Err(dns::Error::Failed) + } + })?; + + use embassy_hal_common::drop::OnDrop; + let drop = OnDrop::new(|| { + self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + socket.cancel_query(query); + s.waker.wake(); + } + }) + }); + + let res = poll_fn(|cx| { + self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + match socket.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(dns::GetQueryResultError::Pending) => { + socket.register_query_waker(query, cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e.into())), + } + } else { + Poll::Ready(Err(dns::Error::Failed)) + } + }) + }) + .await; + + drop.defuse(); + + res + } } impl SocketStack { @@ -251,6 +324,13 @@ impl Inner { debug!(" DNS server {}: {}", i, s); } + #[cfg(feature = "dns")] + if let Some(dns_socket) = self.dns_socket { + let socket = s.sockets.get_mut::(dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); + } + self.config = Some(config) } @@ -326,6 +406,7 @@ impl Inner { //if old_link_up || self.link_up { // self.poll_configurator(timestamp) //} + // if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { let t = Timer::at(instant_from_smoltcp(poll_at)); diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 6203f8370..e787cb823 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -71,8 +71,7 @@ async fn main_task(spawner: Spawner) { spawner.spawn(net_task(stack)).unwrap(); // Then we can use it! - - let mut socket = DnsSocket::new(stack, vec![]); + let socket = DnsSocket::new(stack); let host = "example.com"; info!("querying host {:?}...", host); From 7ae47cb1d81d5112fdc42ceaec470350aab5c372 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:20:50 +0100 Subject: [PATCH 0579/1575] Expose api on Stack and add doc Make it work with smoltcp 0.9 --- embassy-net/src/dns.rs | 8 +++----- embassy-net/src/lib.rs | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 1815d258f..9b1b936c4 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,6 +1,6 @@ //! DNS socket with async support. use heapless::Vec; -pub use smoltcp::socket::dns::{DnsQuery, Socket, MAX_ADDRESS_COUNT}; +pub use smoltcp::socket::dns::{DnsQuery, Socket}; pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; @@ -48,9 +48,7 @@ impl<'a, D> DnsSocket<'a, D> where D: Driver + 'static, { - /// Create a new DNS socket using the provided stack and query storage. - /// - /// DNS servers are derived from the stack configuration. + /// Create a new DNS socket using the provided stack. /// /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. pub fn new(stack: &'a Stack) -> Self { @@ -58,7 +56,7 @@ where } /// Make a query for a given name and return the corresponding IP addresses. - pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { + pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { self.stack.dns_query(name, qtype).await } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b63aa83df..5b6ab0e3a 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -231,12 +231,9 @@ impl Stack { unreachable!() } + /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] - async fn dns_query( - &self, - name: &str, - qtype: dns::DnsQueryType, - ) -> Result, dns::Error> { + pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { let query = self.with_mut(|s, i| { if let Some(dns_handle) = i.dns_socket { let socket = s.sockets.get_mut::(dns_handle); From 6e68353a931cc268f61b4606c3a99bf8f5e96f6e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:30:17 +0100 Subject: [PATCH 0580/1575] attempt removing option --- embassy-net/src/lib.rs | 76 ++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 5b6ab0e3a..627c0a0f7 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -53,7 +53,7 @@ const MAX_QUERIES: usize = 2; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], #[cfg(feature = "dns")] - queries: Option<[Option; MAX_QUERIES]>, + queries: [Option; MAX_QUERIES], } impl StackResources { @@ -63,7 +63,7 @@ impl StackResources { Self { sockets: [SocketStorage::EMPTY; SOCK], #[cfg(feature = "dns")] - queries: Some([INIT; MAX_QUERIES]), + queries: [INIT; MAX_QUERIES], } } } @@ -117,7 +117,7 @@ struct Inner { #[cfg(feature = "dhcpv4")] dhcp_socket: Option, #[cfg(feature = "dns")] - dns_socket: Option, + dns_socket: SocketHandle, } pub(crate) struct SocketStack { @@ -156,15 +156,7 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - let mut inner = Inner { - device, - link_up: false, - config: None, - #[cfg(feature = "dhcpv4")] - dhcp_socket: None, - #[cfg(feature = "dns")] - dns_socket: None, - }; + let mut socket = SocketStack { sockets, iface, @@ -172,12 +164,15 @@ impl Stack { next_local_port, }; - #[cfg(feature = "dns")] - { - if let Some(queries) = resources.queries.take() { - inner.dns_socket = Some(socket.sockets.add(dns::Socket::new(&[], queries))); - } - } + let mut inner = Inner { + device, + link_up: false, + config: None, + #[cfg(feature = "dhcpv4")] + dhcp_socket: None, + #[cfg(feature = "dns")] + dns_socket: socket.sockets.add(dns::Socket::new(&[], &mut resources.queries)), + }; match config { Config::Static(config) => { @@ -235,42 +230,29 @@ impl Stack { #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { let query = self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - match socket.start_query(s.iface.context(), name, qtype) { - Ok(handle) => Ok(handle), - Err(e) => Err(e.into()), - } - } else { - Err(dns::Error::Failed) - } + let socket = s.sockets.get_mut::(i.dns_socket); + socket.start_query(s.iface.context(), name, qtype) })?; use embassy_hal_common::drop::OnDrop; let drop = OnDrop::new(|| { self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - socket.cancel_query(query); - s.waker.wake(); - } + let socket = s.sockets.get_mut::(i.dns_socket); + socket.cancel_query(query); + s.waker.wake(); }) }); let res = poll_fn(|cx| { self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - match socket.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), - Err(dns::GetQueryResultError::Pending) => { - socket.register_query_waker(query, cx.waker()); - Poll::Pending - } - Err(e) => Poll::Ready(Err(e.into())), + let socket = s.sockets.get_mut::(i.dns_socket); + match socket.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(dns::GetQueryResultError::Pending) => { + socket.register_query_waker(query, cx.waker()); + Poll::Pending } - } else { - Poll::Ready(Err(dns::Error::Failed)) + Err(e) => Poll::Ready(Err(e.into())), } }) }) @@ -322,11 +304,9 @@ impl Inner { } #[cfg(feature = "dns")] - if let Some(dns_socket) = self.dns_socket { - let socket = s.sockets.get_mut::(dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - socket.update_servers(&servers[..]); - } + let socket = s.sockets.get_mut::(self.dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); self.config = Some(config) } From 472473d8c1cfc5985ee81fa8dac88c46b8fa64a9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:32:35 +0100 Subject: [PATCH 0581/1575] Create slice using ::Owned --- embassy-net/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 627c0a0f7..25a7891cf 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -171,7 +171,7 @@ impl Stack { #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] - dns_socket: socket.sockets.add(dns::Socket::new(&[], &mut resources.queries)), + dns_socket: socket.sockets.add(dns::Socket::new(&[], managed::ManagedSlice::Borrowed(&mut resources.queries))), }; match config { From 48dff04d647a5303b9ed0a85ecce2f8a8f17ba0f Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:34:21 +0100 Subject: [PATCH 0582/1575] Bump max queries --- embassy-net/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 25a7891cf..e5f7479cf 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -48,7 +48,8 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; -const MAX_QUERIES: usize = 2; +#[cfg(feature = "dns")] +const MAX_QUERIES: usize = 4; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], From 32c3725631b72807b926187d29916468fed68c81 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:44:51 +0100 Subject: [PATCH 0583/1575] add waker for DNS slots --- embassy-net/src/dns.rs | 4 +--- embassy-net/src/lib.rs | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 9b1b936c4..2dd44a4e1 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -10,8 +10,6 @@ use crate::{Driver, Stack}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// No available query slot - NoFreeSlot, /// Invalid name InvalidName, /// Name too long @@ -29,7 +27,7 @@ impl From for Error { impl From for Error { fn from(e: StartQueryError) -> Self { match e { - StartQueryError::NoFreeSlot => Self::NoFreeSlot, + StartQueryError::NoFreeSlot => Self::Failed, StartQueryError::InvalidName => Self::InvalidName, StartQueryError::NameTooLong => Self::NameTooLong, } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index e5f7479cf..9d9de9134 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -119,6 +119,8 @@ struct Inner { dhcp_socket: Option, #[cfg(feature = "dns")] dns_socket: SocketHandle, + #[cfg(feature = "dns")] + dns_waker: WakerRegistration, } pub(crate) struct SocketStack { @@ -157,7 +159,6 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - let mut socket = SocketStack { sockets, iface, @@ -172,7 +173,12 @@ impl Stack { #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] - dns_socket: socket.sockets.add(dns::Socket::new(&[], managed::ManagedSlice::Borrowed(&mut resources.queries))), + dns_socket: socket.sockets.add(dns::Socket::new( + &[], + managed::ManagedSlice::Borrowed(&mut resources.queries), + )), + #[cfg(feature = "dns")] + dns_waker: WakerRegistration::new(), }; match config { @@ -230,10 +236,20 @@ impl Stack { /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { - let query = self.with_mut(|s, i| { - let socket = s.sockets.get_mut::(i.dns_socket); - socket.start_query(s.iface.context(), name, qtype) - })?; + let query = poll_fn(|cx| { + self.with_mut(|s, i| { + let socket = s.sockets.get_mut::(i.dns_socket); + match socket.start_query(s.iface.context(), name, qtype) { + Ok(handle) => Poll::Ready(Ok(handle)), + Err(dns::StartQueryError::NoFreeSlot) => { + i.dns_waker.register(cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e)), + } + }) + }) + .await?; use embassy_hal_common::drop::OnDrop; let drop = OnDrop::new(|| { @@ -241,6 +257,7 @@ impl Stack { let socket = s.sockets.get_mut::(i.dns_socket); socket.cancel_query(query); s.waker.wake(); + i.dns_waker.wake(); }) }); @@ -248,12 +265,18 @@ impl Stack { self.with_mut(|s, i| { let socket = s.sockets.get_mut::(i.dns_socket); match socket.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), + Ok(addrs) => { + i.dns_waker.wake(); + Poll::Ready(Ok(addrs)) + } Err(dns::GetQueryResultError::Pending) => { socket.register_query_waker(query, cx.waker()); Poll::Pending } - Err(e) => Poll::Ready(Err(e.into())), + Err(e) => { + i.dns_waker.wake(); + Poll::Ready(Err(e.into())) + } } }) }) From a7d3ef9122e45a808e1c401b33ee380a0c7c867d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:00:00 +0100 Subject: [PATCH 0584/1575] scope dns operations within a cfged block --- embassy-net/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 9d9de9134..bda5f9e14 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -328,9 +328,11 @@ impl Inner { } #[cfg(feature = "dns")] - let socket = s.sockets.get_mut::(self.dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - socket.update_servers(&servers[..]); + { + let socket = s.sockets.get_mut::(self.dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); + } self.config = Some(config) } From 128a453163acbdd2f3e3e320bbd06eb38f196601 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:04:54 +0100 Subject: [PATCH 0585/1575] remove unneeded features --- embassy-net/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 53778899b..2d8c5c7b7 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] default = [] -std = ["smoltcp/alloc", "managed/std"] +std = [] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] From a2b8921ff3e0b50da824c2242f0b90f9f8f9a0e2 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:38:17 +0100 Subject: [PATCH 0586/1575] fix: cfg guard for ipv6 --- embassy-net/src/dns.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 2dd44a4e1..97320e44b 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -80,6 +80,7 @@ where if let Some(first) = addrs.get(0) { Ok(match first { IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), + #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), }) } else { From a509af4bc00ae6945e568b268731e854e8ae3994 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 23:00:16 +0100 Subject: [PATCH 0587/1575] exmaples/dns: don't use the socket. --- examples/std/src/bin/net_dns.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index e787cb823..e1cc45a38 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -4,7 +4,7 @@ use std::default::Default; use clap::Parser; use embassy_executor::{Executor, Spawner}; -use embassy_net::dns::{DnsQueryType, DnsSocket}; +use embassy_net::dns::DnsQueryType; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use heapless::Vec; use log::*; @@ -65,17 +65,14 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); - // Then we can use it! - let socket = DnsSocket::new(stack); - let host = "example.com"; info!("querying host {:?}...", host); - match socket.query(host, DnsQueryType::A).await { + match stack.dns_query(host, DnsQueryType::A).await { Ok(r) => { info!("query response: {:?}", r); } From 4c4e923e057cc376ae3ebc2c2f7a01ed4723d308 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 22:47:17 +0100 Subject: [PATCH 0588/1575] nrf/qspi: do not panic when canceling futures. --- embassy-nrf/src/qspi.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 07a970018..d434327fc 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -6,7 +6,7 @@ use core::future::poll_fn; use core::ptr; use core::task::Poll; -use embassy_hal_common::drop::DropBomb; +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{self, Pin as GpioPin}; @@ -190,7 +190,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { // Enable it r.enable.write(|w| w.enable().enabled()); - let mut res = Self { + let res = Self { dpm_enabled: config.deep_power_down.is_some(), irq, }; @@ -200,7 +200,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { r.tasks_activate.write(|w| w.tasks_activate().bit(true)); - res.blocking_wait_ready(); + Self::blocking_wait_ready(); res } @@ -217,7 +217,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { /// Do a custom QSPI instruction. pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); let len = core::cmp::max(req.len(), resp.len()) as u8; self.custom_instruction_start(opcode, req, len)?; @@ -226,7 +226,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { self.custom_instruction_finish(resp)?; - bomb.defuse(); + ondrop.defuse(); Ok(()) } @@ -236,7 +236,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { let len = core::cmp::max(req.len(), resp.len()) as u8; self.custom_instruction_start(opcode, req, len)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); self.custom_instruction_finish(resp)?; @@ -312,7 +312,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { .await } - fn blocking_wait_ready(&mut self) { + fn blocking_wait_ready() { loop { let r = T::regs(); if r.events_ready.read().bits() != 0 { @@ -382,36 +382,36 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { /// Read data from the flash memory. pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_read(address, data)?; self.wait_ready().await; - bomb.defuse(); + ondrop.defuse(); Ok(()) } /// Write data to the flash memory. pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_write(address, data)?; self.wait_ready().await; - bomb.defuse(); + ondrop.defuse(); Ok(()) } /// Erase a sector on the flash memory. pub async fn erase(&mut self, address: usize) -> Result<(), Error> { - let bomb = DropBomb::new(); + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_erase(address)?; self.wait_ready().await; - bomb.defuse(); + ondrop.defuse(); Ok(()) } @@ -419,21 +419,21 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { /// Read data from the flash memory, blocking version. pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { self.start_read(address, data)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); Ok(()) } /// Write data to the flash memory, blocking version. pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { self.start_write(address, data)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); Ok(()) } /// Erase a sector on the flash memory, blocking version. pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { self.start_erase(address)?; - self.blocking_wait_ready(); + Self::blocking_wait_ready(); Ok(()) } } From 76642b3a3cddaa226dcd741d5f9f8e9d01c2f3ac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 23:35:44 +0100 Subject: [PATCH 0589/1575] fix h7 examples --- examples/nrf52840/src/bin/usb_ethernet.rs | 23 +++++++++++++++++++++++ examples/stm32h7/Cargo.toml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 979780896..430468adf 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -46,8 +46,31 @@ async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } +#[inline(never)] +pub fn test_function() -> (usize, u32, [u32; 2]) { + let mut array = [3; 2]; + + let mut index = 0; + let mut result = 0; + + for x in [1, 2] { + if x == 1 { + array[1] = 99; + } else { + index = if x == 2 { 1 } else { 0 }; + + // grabs value from array[0], not array[1] + result = array[index]; + } + } + + (index, result, array) +} + #[embassy_executor::main] async fn main(spawner: Spawner) { + info!("{:?}", test_function()); + let p = embassy_nrf::init(Default::default()); let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index bcf976416..a04134789 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -21,7 +21,7 @@ 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.2.0-alpha.0" } -embedded-nal-async = "0.3.0" +embedded-nal-async = "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 } From d21643c060376fff0f0f5b8c5bc4eed4df4bc560 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Sun, 12 Feb 2023 11:31:22 +0100 Subject: [PATCH 0590/1575] fix "prescaler none" which incorrectly set "prescaler divided by 3" --- embassy-stm32/src/rcc/wl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 347674918..82b0d04e2 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -158,7 +158,7 @@ impl Into for APBPrescaler { impl Into for AHBPrescaler { fn into(self) -> u8 { match self { - AHBPrescaler::NotDivided => 1, + AHBPrescaler::NotDivided => 0x0, AHBPrescaler::Div2 => 0x08, AHBPrescaler::Div3 => 0x01, AHBPrescaler::Div4 => 0x09, From 80b7c3cf69141c76ba2ae099225c2580cd430086 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 01:30:53 +0100 Subject: [PATCH 0591/1575] Fix doc build. --- .github/workflows/doc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 8a341b8f7..827f88178 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: crates: - - stm32 + #- stm32 # runs out of disk space... - rest # This will ensure at most one doc build job is running at a time @@ -46,7 +46,7 @@ jobs: - name: Install docserver run: | - wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder" + wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.4/builder" chmod +x /usr/local/bin/builder - name: build-stm32 From 1e36c91bf884ed16b898bd5a79e32ee0e62471ef Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 02:22:06 +0100 Subject: [PATCH 0592/1575] stm32: fix fmc-related build failures on some F4's --- embassy-stm32/src/{fmc/mod.rs => fmc.rs} | 130 +++++++++++++++++++++-- embassy-stm32/src/fmc/pins.rs | 118 -------------------- 2 files changed, 122 insertions(+), 126 deletions(-) rename embassy-stm32/src/{fmc/mod.rs => fmc.rs} (64%) delete mode 100644 embassy-stm32/src/fmc/pins.rs diff --git a/embassy-stm32/src/fmc/mod.rs b/embassy-stm32/src/fmc.rs similarity index 64% rename from embassy-stm32/src/fmc/mod.rs rename to embassy-stm32/src/fmc.rs index 5adfa0d48..b9129cb51 100644 --- a/embassy-stm32/src/fmc/mod.rs +++ b/embassy-stm32/src/fmc.rs @@ -6,9 +6,6 @@ use crate::gpio::sealed::AFType; use crate::gpio::{Pull, Speed}; use crate::Peripheral; -mod pins; -pub use pins::*; - pub struct Fmc<'d, T: Instance> { peri: PhantomData<&'d mut T>, } @@ -19,7 +16,7 @@ unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T> where T: Instance, { - const REGISTERS: *const () = crate::pac::FMC.0 as *const _; + const REGISTERS: *const () = T::REGS.0 as *const _; fn enable(&mut self) { ::enable(); @@ -32,7 +29,7 @@ where // This is a "not" because it is expected that all future versions have this bit #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))] unsafe { - T::regs().bcr1().modify(|r| r.set_fmcen(true)) + T::REGS.bcr1().modify(|r| r.set_fmcen(true)) }; } @@ -150,13 +147,130 @@ impl<'d, T: Instance> Fmc<'d, T> { )); } +pub(crate) mod sealed { + pub trait Instance: crate::rcc::sealed::RccPeripheral { + const REGS: crate::pac::fmc::Fmc; + } +} + +pub trait Instance: sealed::Instance + 'static {} + foreach_peripheral!( (fmc, $inst:ident) => { impl crate::fmc::sealed::Instance for crate::peripherals::$inst { - fn regs() -> stm32_metapac::fmc::Fmc { - crate::pac::$inst - } + const REGS: crate::pac::fmc::Fmc = crate::pac::$inst; } impl crate::fmc::Instance for crate::peripherals::$inst {} }; ); + +pin_trait!(SDNWEPin, Instance); +pin_trait!(SDNCASPin, Instance); +pin_trait!(SDNRASPin, Instance); + +pin_trait!(SDNE0Pin, Instance); +pin_trait!(SDNE1Pin, Instance); + +pin_trait!(SDCKE0Pin, Instance); +pin_trait!(SDCKE1Pin, Instance); + +pin_trait!(SDCLKPin, Instance); + +pin_trait!(NBL0Pin, Instance); +pin_trait!(NBL1Pin, Instance); +pin_trait!(NBL2Pin, Instance); +pin_trait!(NBL3Pin, Instance); + +pin_trait!(INTPin, Instance); +pin_trait!(NLPin, Instance); +pin_trait!(NWaitPin, Instance); + +pin_trait!(NE1Pin, Instance); +pin_trait!(NE2Pin, Instance); +pin_trait!(NE3Pin, Instance); +pin_trait!(NE4Pin, Instance); + +pin_trait!(NCEPin, Instance); +pin_trait!(NOEPin, Instance); +pin_trait!(NWEPin, Instance); +pin_trait!(ClkPin, Instance); + +pin_trait!(BA0Pin, Instance); +pin_trait!(BA1Pin, Instance); + +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(D4Pin, Instance); +pin_trait!(D5Pin, Instance); +pin_trait!(D6Pin, Instance); +pin_trait!(D7Pin, Instance); +pin_trait!(D8Pin, Instance); +pin_trait!(D9Pin, Instance); +pin_trait!(D10Pin, Instance); +pin_trait!(D11Pin, Instance); +pin_trait!(D12Pin, Instance); +pin_trait!(D13Pin, Instance); +pin_trait!(D14Pin, Instance); +pin_trait!(D15Pin, Instance); +pin_trait!(D16Pin, Instance); +pin_trait!(D17Pin, Instance); +pin_trait!(D18Pin, Instance); +pin_trait!(D19Pin, Instance); +pin_trait!(D20Pin, Instance); +pin_trait!(D21Pin, Instance); +pin_trait!(D22Pin, Instance); +pin_trait!(D23Pin, Instance); +pin_trait!(D24Pin, Instance); +pin_trait!(D25Pin, Instance); +pin_trait!(D26Pin, Instance); +pin_trait!(D27Pin, Instance); +pin_trait!(D28Pin, Instance); +pin_trait!(D29Pin, Instance); +pin_trait!(D30Pin, Instance); +pin_trait!(D31Pin, Instance); + +pin_trait!(DA0Pin, Instance); +pin_trait!(DA1Pin, Instance); +pin_trait!(DA2Pin, Instance); +pin_trait!(DA3Pin, Instance); +pin_trait!(DA4Pin, Instance); +pin_trait!(DA5Pin, Instance); +pin_trait!(DA6Pin, Instance); +pin_trait!(DA7Pin, Instance); +pin_trait!(DA8Pin, Instance); +pin_trait!(DA9Pin, Instance); +pin_trait!(DA10Pin, Instance); +pin_trait!(DA11Pin, Instance); +pin_trait!(DA12Pin, Instance); +pin_trait!(DA13Pin, Instance); +pin_trait!(DA14Pin, Instance); +pin_trait!(DA15Pin, Instance); + +pin_trait!(A0Pin, Instance); +pin_trait!(A1Pin, Instance); +pin_trait!(A2Pin, Instance); +pin_trait!(A3Pin, Instance); +pin_trait!(A4Pin, Instance); +pin_trait!(A5Pin, Instance); +pin_trait!(A6Pin, Instance); +pin_trait!(A7Pin, Instance); +pin_trait!(A8Pin, Instance); +pin_trait!(A9Pin, Instance); +pin_trait!(A10Pin, Instance); +pin_trait!(A11Pin, Instance); +pin_trait!(A12Pin, Instance); +pin_trait!(A13Pin, Instance); +pin_trait!(A14Pin, Instance); +pin_trait!(A15Pin, Instance); +pin_trait!(A16Pin, Instance); +pin_trait!(A17Pin, Instance); +pin_trait!(A18Pin, Instance); +pin_trait!(A19Pin, Instance); +pin_trait!(A20Pin, Instance); +pin_trait!(A21Pin, Instance); +pin_trait!(A22Pin, Instance); +pin_trait!(A23Pin, Instance); +pin_trait!(A24Pin, Instance); +pin_trait!(A25Pin, Instance); diff --git a/embassy-stm32/src/fmc/pins.rs b/embassy-stm32/src/fmc/pins.rs deleted file mode 100644 index 5062e52ae..000000000 --- a/embassy-stm32/src/fmc/pins.rs +++ /dev/null @@ -1,118 +0,0 @@ -pub(crate) mod sealed { - pub trait Instance: crate::rcc::sealed::RccPeripheral { - fn regs() -> crate::pac::fmc::Fmc; - } -} - -pub trait Instance: sealed::Instance + 'static {} - -pin_trait!(SDNWEPin, Instance); -pin_trait!(SDNCASPin, Instance); -pin_trait!(SDNRASPin, Instance); - -pin_trait!(SDNE0Pin, Instance); -pin_trait!(SDNE1Pin, Instance); - -pin_trait!(SDCKE0Pin, Instance); -pin_trait!(SDCKE1Pin, Instance); - -pin_trait!(SDCLKPin, Instance); - -pin_trait!(NBL0Pin, Instance); -pin_trait!(NBL1Pin, Instance); -pin_trait!(NBL2Pin, Instance); -pin_trait!(NBL3Pin, Instance); - -pin_trait!(INTPin, Instance); -pin_trait!(NLPin, Instance); -pin_trait!(NWaitPin, Instance); - -pin_trait!(NE1Pin, Instance); -pin_trait!(NE2Pin, Instance); -pin_trait!(NE3Pin, Instance); -pin_trait!(NE4Pin, Instance); - -pin_trait!(NCEPin, Instance); -pin_trait!(NOEPin, Instance); -pin_trait!(NWEPin, Instance); -pin_trait!(ClkPin, Instance); - -pin_trait!(BA0Pin, Instance); -pin_trait!(BA1Pin, Instance); - -pin_trait!(D0Pin, Instance); -pin_trait!(D1Pin, Instance); -pin_trait!(D2Pin, Instance); -pin_trait!(D3Pin, Instance); -pin_trait!(D4Pin, Instance); -pin_trait!(D5Pin, Instance); -pin_trait!(D6Pin, Instance); -pin_trait!(D7Pin, Instance); -pin_trait!(D8Pin, Instance); -pin_trait!(D9Pin, Instance); -pin_trait!(D10Pin, Instance); -pin_trait!(D11Pin, Instance); -pin_trait!(D12Pin, Instance); -pin_trait!(D13Pin, Instance); -pin_trait!(D14Pin, Instance); -pin_trait!(D15Pin, Instance); -pin_trait!(D16Pin, Instance); -pin_trait!(D17Pin, Instance); -pin_trait!(D18Pin, Instance); -pin_trait!(D19Pin, Instance); -pin_trait!(D20Pin, Instance); -pin_trait!(D21Pin, Instance); -pin_trait!(D22Pin, Instance); -pin_trait!(D23Pin, Instance); -pin_trait!(D24Pin, Instance); -pin_trait!(D25Pin, Instance); -pin_trait!(D26Pin, Instance); -pin_trait!(D27Pin, Instance); -pin_trait!(D28Pin, Instance); -pin_trait!(D29Pin, Instance); -pin_trait!(D30Pin, Instance); -pin_trait!(D31Pin, Instance); - -pin_trait!(DA0Pin, Instance); -pin_trait!(DA1Pin, Instance); -pin_trait!(DA2Pin, Instance); -pin_trait!(DA3Pin, Instance); -pin_trait!(DA4Pin, Instance); -pin_trait!(DA5Pin, Instance); -pin_trait!(DA6Pin, Instance); -pin_trait!(DA7Pin, Instance); -pin_trait!(DA8Pin, Instance); -pin_trait!(DA9Pin, Instance); -pin_trait!(DA10Pin, Instance); -pin_trait!(DA11Pin, Instance); -pin_trait!(DA12Pin, Instance); -pin_trait!(DA13Pin, Instance); -pin_trait!(DA14Pin, Instance); -pin_trait!(DA15Pin, Instance); - -pin_trait!(A0Pin, Instance); -pin_trait!(A1Pin, Instance); -pin_trait!(A2Pin, Instance); -pin_trait!(A3Pin, Instance); -pin_trait!(A4Pin, Instance); -pin_trait!(A5Pin, Instance); -pin_trait!(A6Pin, Instance); -pin_trait!(A7Pin, Instance); -pin_trait!(A8Pin, Instance); -pin_trait!(A9Pin, Instance); -pin_trait!(A10Pin, Instance); -pin_trait!(A11Pin, Instance); -pin_trait!(A12Pin, Instance); -pin_trait!(A13Pin, Instance); -pin_trait!(A14Pin, Instance); -pin_trait!(A15Pin, Instance); -pin_trait!(A16Pin, Instance); -pin_trait!(A17Pin, Instance); -pin_trait!(A18Pin, Instance); -pin_trait!(A19Pin, Instance); -pin_trait!(A20Pin, Instance); -pin_trait!(A21Pin, Instance); -pin_trait!(A22Pin, Instance); -pin_trait!(A23Pin, Instance); -pin_trait!(A24Pin, Instance); -pin_trait!(A25Pin, Instance); From 951f2089156288288b8402b2b4b1d3feacdfe975 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 02:37:53 +0100 Subject: [PATCH 0593/1575] Add more crates to docs. --- .github/workflows/doc.yml | 2 ++ embassy-net-driver-channel/Cargo.toml | 6 ++++++ embassy-net-driver/Cargo.toml | 2 +- embassy-usb-logger/Cargo.toml | 5 +++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 827f88178..4bc73fdcf 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -70,12 +70,14 @@ jobs: builder ./embassy-lora crates/embassy-lora/git.zup builder ./embassy-net crates/embassy-net/git.zup builder ./embassy-net-driver crates/embassy-net-driver/git.zup + builder ./embassy-net-driver-channel crates/embassy-net-driver-channel/git.zup builder ./embassy-nrf crates/embassy-nrf/git.zup builder ./embassy-rp crates/embassy-rp/git.zup builder ./embassy-sync crates/embassy-sync/git.zup builder ./embassy-time crates/embassy-time/git.zup builder ./embassy-usb crates/embassy-usb/git.zup builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup + builder ./embassy-usb-logger crates/embassy-usb-logger/git.zup - name: upload run: | diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index 700a4e8a0..6cb6c3568 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -3,6 +3,12 @@ name = "embassy-net-driver-channel" version = "0.1.0" edition = "2021" +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver-channel/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index 7ab9d1194..ff6f29355 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net/src/" +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/" features = ["defmt"] target = "thumbv7em-none-eabi" diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 2f4665922..18d70b0c5 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -3,6 +3,11 @@ name = "embassy-usb-logger" version = "0.1.0" edition = "2021" +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-logger-v$VERSION/embassy-usb/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-logger/src/" +target = "thumbv7em-none-eabi" + [dependencies] embassy-usb = { version = "0.1.0", path = "../embassy-usb" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } From 4e15043fc2047751c5580e500a6cd7a0b316f8f5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 02:40:29 +0100 Subject: [PATCH 0594/1575] add stm32f413vh to CI --- ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci.sh b/ci.sh index b325e3764..4199f91d3 100755 --- a/ci.sh +++ b/ci.sh @@ -63,6 +63,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ From 363054de983f15a9344ca58a05e41c425661d3db Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 03:02:12 +0100 Subject: [PATCH 0595/1575] stm32: doc all chips. --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 10e5e48c0..82b1ba3a0 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -17,7 +17,7 @@ flavors = [ { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32f42.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, From 41a563aae3e474955892b27487e185f5f486f525 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 13 Feb 2023 03:11:16 +0100 Subject: [PATCH 0596/1575] net: document all features --- embassy-net/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2d8c5c7b7..ca34262df 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] +features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] target = "thumbv7em-none-eabi" [features] From 5e74926907b48b7f778de2ff4dfd6f1fd6e24dcb Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 13 Feb 2023 15:12:33 +0100 Subject: [PATCH 0597/1575] feature-gate variants without vals defined --- embassy-stm32/src/rtc/v3.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index eba67c28f..6998c48c2 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -15,7 +15,12 @@ impl<'d, T: Instance> super::Rtc<'d, T> { while !crate::pac::PWR.cr1().read().dbp() {} } - #[cfg(not(feature = "stm32g0c1ve"))] + #[cfg(not(any( + feature = "stm32g0c1ve", + feature = "stm32g491re", + feature = "stm32u585zi", + feature = "stm32g473cc" + )))] { crate::pac::PWR .cr1() @@ -32,7 +37,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { feature = "stm32wle5ub", feature = "stm32g0c1ve", feature = "stm32wl55jc-cm4", - feature = "stm32wl55uc-cm4" + feature = "stm32wl55uc-cm4", + feature = "stm32g491re", + feature = "stm32g473cc", + feature = "stm32u585zi", + feature = "stm32wle5jb" )))] let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); #[cfg(feature = "stm32g0c1ve")] From 1626a4a74b50d60f2f8a53f477d49cb67a140f79 Mon Sep 17 00:00:00 2001 From: Slushee <55996847+Slushee-a@users.noreply.github.com> Date: Mon, 13 Feb 2023 17:12:50 +0000 Subject: [PATCH 0598/1575] Add clone to embassy_rp::gpio::Level --- embassy-rp/src/gpio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 4abb98394..e258733a6 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -16,7 +16,7 @@ const NEW_AW: AtomicWaker = AtomicWaker::new(); static INTERRUPT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; /// Represents a digital input or output level. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub enum Level { Low, High, From dfc58ad3a27d9313bebbd0ee49976afb54df772e Mon Sep 17 00:00:00 2001 From: Slushee <55996847+Slushee-a@users.noreply.github.com> Date: Mon, 13 Feb 2023 17:29:35 +0000 Subject: [PATCH 0599/1575] Add copy to Level enum in embassy-rp gpio module --- embassy-rp/src/gpio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index e258733a6..76d4281ff 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -16,7 +16,7 @@ const NEW_AW: AtomicWaker = AtomicWaker::new(); static INTERRUPT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; /// Represents a digital input or output level. -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Level { Low, High, From 0bcd1b1e10e0edefa520ba3f293d34367b416c99 Mon Sep 17 00:00:00 2001 From: Aaron Tsui Date: Wed, 15 Feb 2023 11:08:27 +0800 Subject: [PATCH 0600/1575] update embassy dependences --- examples/rpi-pico-w/Cargo.toml | 16 ++++++++-------- examples/rpi-pico-w/src/main.rs | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index fa1cad8c7..99b82ca31 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -28,14 +28,14 @@ heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "771806be790a2758f1314d6460defe7c2f0d3e99" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index d2f47fd6c..71459a122 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -9,7 +9,7 @@ use core::convert::Infallible; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; -use embassy_net::{Stack, StackResources}; +use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output}; use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; use embedded_hal_1::spi::ErrorType; @@ -78,8 +78,8 @@ async fn main(spawner: Spawner) { //control.join_open(env!("WIFI_NETWORK")).await; control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::Config { // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), @@ -92,7 +92,7 @@ async fn main(spawner: Spawner) { let stack = &*singleton!(Stack::new( net_device, config, - singleton!(StackResources::<1, 2, 8>::new()), + singleton!(StackResources::<2>::new()), seed )); From e641db1f7565c3eb46bae04b5a8c9aef01900f53 Mon Sep 17 00:00:00 2001 From: sekoia Date: Wed, 15 Feb 2023 14:10:07 +0100 Subject: [PATCH 0601/1575] Fix a typo in "PioPeripheral" --- embassy-rp/src/pio.rs | 4 ++-- examples/rp/src/bin/pio_async.rs | 2 +- examples/rp/src/bin/pio_dma.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 1a067e54f..2fb2783de 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -1174,7 +1174,7 @@ impl SmInstance for SmInstanceBase { const SM_NO: u8 = SM_NO; } -pub trait PioPeripherial: Sized { +pub trait PioPeripheral: Sized { type Pio: PioInstance; fn pio(&self) -> u8 { let _ = self; @@ -1249,7 +1249,7 @@ pub type Sm3 = SmInstanceBase<3>; macro_rules! impl_pio_sm { ($name:ident, $pio:expr) => { - impl PioPeripherial for peripherals::$name { + impl PioPeripheral for peripherals::$name { type Pio = PioInstanceBase<$pio>; } }; diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 45a8c73f7..e616d8c5a 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; -use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; +use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index b19ef4083..145e4a656 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{PioPeripheral, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; From 7783e0ebb1a3870f3b180c87c1acd2807439f976 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Fri, 17 Feb 2023 07:43:19 -0500 Subject: [PATCH 0602/1575] Change timing window to match values found experimentally. --- embassy-lora/src/sx126x/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index a9aeadfcb..8559574cb 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -55,10 +55,10 @@ where BUS: Error + Format + 'static, { fn get_rx_window_offset_ms(&self) -> i32 { - -3 + -50 } fn get_rx_window_duration_ms(&self) -> u32 { - 1003 + 1050 } } From a53f525f510de07e8c35d38ecc575cb8ea929dd9 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Sat, 18 Feb 2023 01:35:35 +0200 Subject: [PATCH 0603/1575] stm32/sdmmc: Fix SDIOv1 writes --- embassy-stm32/src/dma/bdma.rs | 1 + embassy-stm32/src/dma/dma.rs | 23 +++++++++++++++++- embassy-stm32/src/dma/gpdma.rs | 10 +++++++- embassy-stm32/src/dma/mod.rs | 16 +++++++++++++ embassy-stm32/src/sdmmc/mod.rs | 40 ++++++++++++++++++++++++++++++- examples/stm32f4/src/bin/sdmmc.rs | 39 ++++++++++++++++++++++++++++-- 6 files changed, 124 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 6160b9f40..5a7a408bb 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -192,6 +192,7 @@ mod low_level_api { options.flow_ctrl == crate::dma::FlowControl::Dma, "Peripheral flow control not supported" ); + assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); let ch = dma.ch(channel_number as _); diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index fec60f708..385a833f7 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -4,7 +4,7 @@ use core::task::Waker; use embassy_cortex_m::interrupt::Priority; use embassy_sync::waitqueue::AtomicWaker; -use super::{Burst, FlowControl, Request, TransferOptions, Word, WordSize}; +use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize}; use crate::_generated::DMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::dma::{regs, vals}; @@ -40,6 +40,17 @@ impl From for vals::Pfctrl { } } +impl From for vals::Fth { + fn from(value: FifoThreshold) -> Self { + match value { + FifoThreshold::Quarter => vals::Fth::QUARTER, + FifoThreshold::Half => vals::Fth::HALF, + FifoThreshold::ThreeQuarters => vals::Fth::THREEQUARTERS, + FifoThreshold::Full => vals::Fth::FULL, + } + } +} + struct ChannelState { waker: AtomicWaker, } @@ -236,6 +247,16 @@ mod low_level_api { ch.par().write_value(peri_addr as u32); ch.m0ar().write_value(mem_addr as u32); ch.ndtr().write_value(regs::Ndtr(mem_len as _)); + ch.fcr().write(|w| { + if let Some(fth) = options.fifo_threshold { + // FIFO mode + w.set_dmdis(vals::Dmdis::DISABLED); + w.set_fth(fth.into()); + } else { + // Direct mode + w.set_dmdis(vals::Dmdis::ENABLED); + } + }); ch.cr().write(|w| { w.set_dir(dir); w.set_msize(data_size); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index d252cef3e..442fee48e 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -176,8 +176,16 @@ mod low_level_api { mem_len: usize, incr_mem: bool, data_size: WordSize, - _options: TransferOptions, + options: TransferOptions, ) { + assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); + assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); + assert!( + options.flow_ctrl == crate::dma::FlowControl::Dma, + "Peripheral flow control not supported" + ); + assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); + // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index b51e0d40b..f5a82fb7a 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -186,6 +186,19 @@ pub enum FlowControl { Peripheral, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FifoThreshold { + /// 1/4 full FIFO + Quarter, + /// 1/2 full FIFO + Half, + /// 3/4 full FIFO + ThreeQuarters, + /// Full FIFO + Full, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TransferOptions { @@ -195,6 +208,8 @@ pub struct TransferOptions { pub mburst: Burst, /// Flow control configuration pub flow_ctrl: FlowControl, + /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. + pub fifo_threshold: Option, } impl Default for TransferOptions { @@ -203,6 +218,7 @@ impl Default for TransferOptions { pburst: Burst::Single, mburst: Burst::Single, flow_ctrl: FlowControl::Dma, + fifo_threshold: None, } } } diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 3f07e0c07..3c99a0b6c 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -2,6 +2,7 @@ use core::default::Default; use core::future::poll_fn; +use core::ops::{Deref, DerefMut}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; @@ -40,7 +41,23 @@ impl Default for Signalling { } #[repr(align(4))] -pub struct DataBlock([u8; 512]); +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DataBlock(pub [u8; 512]); + +impl Deref for DataBlock { + type Target = [u8; 512]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for DataBlock { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} /// Errors #[non_exhaustive] @@ -570,6 +587,12 @@ impl SdmmcInner { regs.clkcr().write(|w| { w.set_pwrsav(false); w.set_negedge(false); + + // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. + // See chip erratas for more details. + #[cfg(sdmmc_v1)] + w.set_hwfc_en(false); + #[cfg(sdmmc_v2)] w.set_hwfc_en(true); #[cfg(sdmmc_v1)] @@ -807,10 +830,16 @@ impl SdmmcInner { let regs = self.0; let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + // sdmmc_v1 uses different cmd/dma order than v2, but only for writes + #[cfg(sdmmc_v1)] + self.cmd(Cmd::write_single_block(address), true)?; + unsafe { self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); self.data_interrupts(true); } + + #[cfg(sdmmc_v2)] self.cmd(Cmd::write_single_block(address), true)?; let res = poll_fn(|cx| { @@ -922,7 +951,9 @@ impl SdmmcInner { let request = dma.request(); dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), ..Default::default() }); } else if #[cfg(sdmmc_v2)] { @@ -970,7 +1001,9 @@ impl SdmmcInner { let request = dma.request(); dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), ..Default::default() }); } else if #[cfg(sdmmc_v2)] { @@ -982,6 +1015,11 @@ impl SdmmcInner { regs.dctrl().modify(|w| { w.set_dblocksize(block_size); w.set_dtdir(false); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } }); } diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 0edd8a61a..b57e955f6 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -4,11 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::sdmmc::Sdmmc; +use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; use embassy_stm32::time::mhz; use embassy_stm32::{interrupt, Config}; use {defmt_rtt as _, panic_probe as _}; +/// This is a safeguard to not overwrite any data on the SD card. +/// If you don't care about SD card contents, set this to `true` to test writes. +const ALLOW_WRITES: bool = false; + #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let mut config = Config::default(); @@ -34,11 +38,42 @@ async fn main(_spawner: Spawner) -> ! { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_card(mhz(25)).await); + unwrap!(sdmmc.init_card(mhz(24)).await); let card = unwrap!(sdmmc.card()); info!("Card: {:#?}", Debug2Format(card)); + info!("Clock: {}", sdmmc.clock()); + + // Arbitrary block index + let block_idx = 16; + + // SDMMC uses `DataBlock` instead of `&[u8]` to ensure 4 byte alignment required by the hardware. + let mut block = DataBlock([0u8; 512]); + + sdmmc.read_block(block_idx, &mut block).await.unwrap(); + info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); + + if !ALLOW_WRITES { + info!("Writing is disabled."); + loop {} + } + + info!("Filling block with 0x55"); + block.fill(0x55); + sdmmc.write_block(block_idx, &block).await.unwrap(); + info!("Write done"); + + sdmmc.read_block(block_idx, &mut block).await.unwrap(); + info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); + + info!("Filling block with 0xAA"); + block.fill(0xAA); + sdmmc.write_block(block_idx, &block).await.unwrap(); + info!("Write done"); + + sdmmc.read_block(block_idx, &mut block).await.unwrap(); + info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); loop {} } From 272982ee5417ea6bdd5873e36717ba9ae9a59ee4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 19 Feb 2023 23:00:16 +0100 Subject: [PATCH 0604/1575] examples/stm32wb: fix linker script. cortex-m-rt 0.7.2 now enforces the stack is 8-byte aligned. Stack is placed at `ORIGIN(RAM) + LENGTH(RAM)` by default, which wasn't 8-byte-aligned. See https://github.com/rust-embedded/cortex-m/discussions/469 ST trims 8 bytes from start of RAM, and uses the whole 192kb, so let's just copy that: https://github.com/STMicroelectronics/STM32CubeWB/blob/bceb1dae09f912eae47fba059251d499ba32f4ca/Drivers/CMSIS/Device/ST/STM32WBxx/Source/Templates/gcc/linker/stm32wb55xx_flash_cm4.ld#L48 --- examples/stm32wb/memory.x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x index 2b4dcce34..ae14e5a12 100644 --- a/examples/stm32wb/memory.x +++ b/examples/stm32wb/memory.x @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K - RAM (xrw) : ORIGIN = 0x20000004, LENGTH = 191K + RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K } From 13328c58d385ef57db74d902b1056c175a2ab367 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 20 Feb 2023 01:01:01 +0100 Subject: [PATCH 0605/1575] examples/stm32wb: do not reserve words at start of RAM. They're used to communicate from the app to ST's OTA bootloader. See AN5247. This bootloader is optional, must be flashed by the user, and requires changing the FLASH start address as well, so the current memory regions still require modifications to use it. Therefore there's no point in reserving these words. Thanks @adamgreig for investigating the purpose. --- examples/stm32wb/memory.x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x index ae14e5a12..0e48c916d 100644 --- a/examples/stm32wb/memory.x +++ b/examples/stm32wb/memory.x @@ -6,7 +6,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K - RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K } From 3f88bf6f9b998e209c6bfe860930fc82516f3f9c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 23 Jan 2023 01:48:35 +0100 Subject: [PATCH 0606/1575] nrf: add support for UICR configuration. - APPROTECT enable/disable. Notably this fixes issues with nrf52-rev3 and nrf53 from locking itself at reset. - Use NFC pins as GPIO. - Use RESET pin as GPIO. NFC and RESET pins singletons are made available only when usable as GPIO, for compile-time checking. --- ci.sh | 4 +- embassy-nrf/Cargo.toml | 35 ++++-- embassy-nrf/src/chips/nrf52805.rs | 4 + embassy-nrf/src/chips/nrf52810.rs | 4 + embassy-nrf/src/chips/nrf52811.rs | 4 + embassy-nrf/src/chips/nrf52820.rs | 4 + embassy-nrf/src/chips/nrf52832.rs | 8 ++ embassy-nrf/src/chips/nrf52833.rs | 8 ++ embassy-nrf/src/chips/nrf52840.rs | 8 ++ embassy-nrf/src/chips/nrf5340_app.rs | 4 + embassy-nrf/src/lib.rs | 156 +++++++++++++++++++++++++++ embassy-nrf/src/nvmc.rs | 12 +-- 12 files changed, 232 insertions(+), 19 deletions(-) diff --git a/ci.sh b/ci.sh index 4199f91d3..417937d07 100755 --- a/ci.sh +++ b/ci.sh @@ -46,8 +46,8 @@ cargo batch \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52820,gpiote,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52832,gpiote,time-driver-rtc1 \ - --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52833,gpiote,time-driver-rtc1,unstable-traits \ + --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52832,gpiote,time-driver-rtc1,reset-pin-as-gpio \ + --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52833,gpiote,time-driver-rtc1,unstable-traits,nfc-pins-as-gpio \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf9160-s,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf9160-ns,gpiote,time-driver-rtc1,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,nrf5340-app-s,gpiote,time-driver-rtc1,unstable-traits \ diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 6b06d5d05..c31ce199b 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -34,22 +34,30 @@ unstable-pac = [] # Implement embedded-hal-async traits if `nightly` is set as well. unstable-traits = ["embedded-hal-1"] -nrf52805 = ["nrf52805-pac", "_ppi"] -nrf52810 = ["nrf52810-pac", "_ppi"] -nrf52811 = ["nrf52811-pac", "_ppi"] -nrf52820 = ["nrf52820-pac", "_ppi"] -nrf52832 = ["nrf52832-pac", "_ppi"] -nrf52833 = ["nrf52833-pac", "_ppi", "_gpio-p1"] -nrf52840 = ["nrf52840-pac", "_ppi", "_gpio-p1"] -nrf5340-app-s = ["_nrf5340-app"] -nrf5340-app-ns = ["_nrf5340-app"] +nrf52805 = ["nrf52805-pac", "_nrf52"] +nrf52810 = ["nrf52810-pac", "_nrf52"] +nrf52811 = ["nrf52811-pac", "_nrf52"] +nrf52820 = ["nrf52820-pac", "_nrf52"] +nrf52832 = ["nrf52832-pac", "_nrf52"] +nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"] +nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"] +nrf5340-app-s = ["_nrf5340-app", "_s"] +nrf5340-app-ns = ["_nrf5340-app", "_ns"] nrf5340-net = ["_nrf5340-net"] -nrf9160-s = ["_nrf9160"] -nrf9160-ns = ["_nrf9160"] +nrf9160-s = ["_nrf9160", "_s"] +nrf9160-ns = ["_nrf9160", "_ns"] gpiote = [] time-driver-rtc1 = ["_time-driver"] +# Allow using the NFC pins as regular GPIO pins (P0_09/P0_10 on nRF52, P0_02/P0_03 on nRF53) +nfc-pins-as-gpio = [] + +# Allow using the RST pin as a regular GPIO pin. +# nrf52805, nrf52810, nrf52811, nrf52832: P0_21 +# nrf52820, nrf52833, nrf52840: P0_18 +reset-pin-as-gpio = [] + # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. @@ -57,9 +65,14 @@ _nrf5340-app = ["_nrf5340", "nrf5340-app-pac"] _nrf5340-net = ["_nrf5340", "nrf5340-net-pac"] _nrf5340 = ["_gpio-p1", "_dppi"] _nrf9160 = ["nrf9160-pac", "_dppi"] +_nrf52 = ["_ppi"] _time-driver = ["dep:embassy-time", "embassy-time?/tick-hz-32_768"] +# trustzone state. +_s = [] +_ns = [] + _ppi = [] _dppi = [] _gpio-p1 = [] diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index bf4019c13..3c74a2a63 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 256; pub const FLASH_SIZE: usize = 192 * 1024; +pub const RESET_PIN: u32 = 21; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -108,6 +110,7 @@ embassy_hal_common::peripherals! { P0_18, P0_19, P0_20, + #[cfg(feature="reset-pin-as-gpio")] P0_21, P0_22, P0_23, @@ -162,6 +165,7 @@ impl_pin!(P0_17, 0, 17); impl_pin!(P0_18, 0, 18); impl_pin!(P0_19, 0, 19); impl_pin!(P0_20, 0, 20); +#[cfg(feature = "reset-pin-as-gpio")] impl_pin!(P0_21, 0, 21); impl_pin!(P0_22, 0, 22); impl_pin!(P0_23, 0, 23); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 6c28a3bea..6b5c134b8 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 256; pub const FLASH_SIZE: usize = 192 * 1024; +pub const RESET_PIN: u32 = 21; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -111,6 +113,7 @@ embassy_hal_common::peripherals! { P0_18, P0_19, P0_20, + #[cfg(feature="reset-pin-as-gpio")] P0_21, P0_22, P0_23, @@ -170,6 +173,7 @@ impl_pin!(P0_17, 0, 17); impl_pin!(P0_18, 0, 18); impl_pin!(P0_19, 0, 19); impl_pin!(P0_20, 0, 20); +#[cfg(feature = "reset-pin-as-gpio")] impl_pin!(P0_21, 0, 21); impl_pin!(P0_22, 0, 22); impl_pin!(P0_23, 0, 23); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index e7214cf5c..c5de9a447 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 256; pub const FLASH_SIZE: usize = 192 * 1024; +pub const RESET_PIN: u32 = 21; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -111,6 +113,7 @@ embassy_hal_common::peripherals! { P0_18, P0_19, P0_20, + #[cfg(feature="reset-pin-as-gpio")] P0_21, P0_22, P0_23, @@ -172,6 +175,7 @@ impl_pin!(P0_17, 0, 17); impl_pin!(P0_18, 0, 18); impl_pin!(P0_19, 0, 19); impl_pin!(P0_20, 0, 20); +#[cfg(feature = "reset-pin-as-gpio")] impl_pin!(P0_21, 0, 21); impl_pin!(P0_22, 0, 22); impl_pin!(P0_23, 0, 23); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 21d1d16cc..81b07f32c 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512; pub const FLASH_SIZE: usize = 256 * 1024; +pub const RESET_PIN: u32 = 18; + embassy_hal_common::peripherals! { // USB USBD, @@ -106,6 +108,7 @@ embassy_hal_common::peripherals! { P0_15, P0_16, P0_17, + #[cfg(feature="reset-pin-as-gpio")] P0_18, P0_19, P0_20, @@ -168,6 +171,7 @@ impl_pin!(P0_14, 0, 14); impl_pin!(P0_15, 0, 15); impl_pin!(P0_16, 0, 16); impl_pin!(P0_17, 0, 17); +#[cfg(feature = "reset-pin-as-gpio")] impl_pin!(P0_18, 0, 18); impl_pin!(P0_19, 0, 19); impl_pin!(P0_20, 0, 20); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 152dad4e3..c2b23fc5b 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -10,6 +10,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 255; // nrf52832xxAB = 256kb pub const FLASH_SIZE: usize = 512 * 1024; +pub const RESET_PIN: u32 = 21; + embassy_hal_common::peripherals! { // RTC RTC0, @@ -109,7 +111,9 @@ embassy_hal_common::peripherals! { P0_06, P0_07, P0_08, + #[cfg(feature = "nfc-pins-as-gpio")] P0_09, + #[cfg(feature = "nfc-pins-as-gpio")] P0_10, P0_11, P0_12, @@ -121,6 +125,7 @@ embassy_hal_common::peripherals! { P0_18, P0_19, P0_20, + #[cfg(feature="reset-pin-as-gpio")] P0_21, P0_22, P0_23, @@ -178,7 +183,9 @@ impl_pin!(P0_05, 0, 5); impl_pin!(P0_06, 0, 6); impl_pin!(P0_07, 0, 7); impl_pin!(P0_08, 0, 8); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_09, 0, 9); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_10, 0, 10); impl_pin!(P0_11, 0, 11); impl_pin!(P0_12, 0, 12); @@ -190,6 +197,7 @@ impl_pin!(P0_17, 0, 17); impl_pin!(P0_18, 0, 18); impl_pin!(P0_19, 0, 19); impl_pin!(P0_20, 0, 20); +#[cfg(feature = "reset-pin-as-gpio")] impl_pin!(P0_21, 0, 21); impl_pin!(P0_22, 0, 22); impl_pin!(P0_23, 0, 23); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index a99ca6343..95f71ade7 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512; pub const FLASH_SIZE: usize = 512 * 1024; +pub const RESET_PIN: u32 = 18; + embassy_hal_common::peripherals! { // USB USBD, @@ -111,7 +113,9 @@ embassy_hal_common::peripherals! { P0_06, P0_07, P0_08, + #[cfg(feature = "nfc-pins-as-gpio")] P0_09, + #[cfg(feature = "nfc-pins-as-gpio")] P0_10, P0_11, P0_12, @@ -120,6 +124,7 @@ embassy_hal_common::peripherals! { P0_15, P0_16, P0_17, + #[cfg(feature="reset-pin-as-gpio")] P0_18, P0_19, P0_20, @@ -207,7 +212,9 @@ impl_pin!(P0_05, 0, 5); impl_pin!(P0_06, 0, 6); impl_pin!(P0_07, 0, 7); impl_pin!(P0_08, 0, 8); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_09, 0, 9); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_10, 0, 10); impl_pin!(P0_11, 0, 11); impl_pin!(P0_12, 0, 12); @@ -216,6 +223,7 @@ impl_pin!(P0_14, 0, 14); impl_pin!(P0_15, 0, 15); impl_pin!(P0_16, 0, 16); impl_pin!(P0_17, 0, 17); +#[cfg(feature = "reset-pin-as-gpio")] impl_pin!(P0_18, 0, 18); impl_pin!(P0_19, 0, 19); impl_pin!(P0_20, 0, 20); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4f7463be2..5e7479e88 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -6,6 +6,8 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 512; pub const FLASH_SIZE: usize = 1024 * 1024; +pub const RESET_PIN: u32 = 18; + embassy_hal_common::peripherals! { // USB USBD, @@ -117,7 +119,9 @@ embassy_hal_common::peripherals! { P0_06, P0_07, P0_08, + #[cfg(feature = "nfc-pins-as-gpio")] P0_09, + #[cfg(feature = "nfc-pins-as-gpio")] P0_10, P0_11, P0_12, @@ -126,6 +130,7 @@ embassy_hal_common::peripherals! { P0_15, P0_16, P0_17, + #[cfg(feature="reset-pin-as-gpio")] P0_18, P0_19, P0_20, @@ -212,7 +217,9 @@ impl_pin!(P0_05, 0, 5); impl_pin!(P0_06, 0, 6); impl_pin!(P0_07, 0, 7); impl_pin!(P0_08, 0, 8); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_09, 0, 9); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_10, 0, 10); impl_pin!(P0_11, 0, 11); impl_pin!(P0_12, 0, 12); @@ -221,6 +228,7 @@ impl_pin!(P0_14, 0, 14); impl_pin!(P0_15, 0, 15); impl_pin!(P0_16, 0, 16); impl_pin!(P0_17, 0, 17); +#[cfg(feature = "reset-pin-as-gpio")] impl_pin!(P0_18, 0, 18); impl_pin!(P0_19, 0, 19); impl_pin!(P0_20, 0, 20); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index c600fcbf6..2b8b55e77 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -304,7 +304,9 @@ embassy_hal_common::peripherals! { // GPIO port 0 P0_00, P0_01, + #[cfg(feature = "nfc-pins-as-gpio")] P0_02, + #[cfg(feature = "nfc-pins-as-gpio")] P0_03, P0_04, P0_05, @@ -393,7 +395,9 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_02, 0, 2); +#[cfg(feature = "nfc-pins-as-gpio")] impl_pin!(P0_03, 0, 3); impl_pin!(P0_04, 0, 4); impl_pin!(P0_05, 0, 5); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 20e70a248..0fa1c0f66 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -24,6 +24,12 @@ )))] compile_error!("No chip feature activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840"); +#[cfg(all(feature = "reset-pin-as-gpio", not(feature = "_nrf52")))] +compile_error!("feature `reset-pin-as-gpio` is only valid for nRF52 series chips."); + +#[cfg(all(feature = "nfc-pins-as-gpio", not(any(feature = "_nrf52", feature = "_nrf5340-app"))))] +compile_error!("feature `nfc-pins-as-gpio` is only valid for nRF52, or nRF53's application core."); + // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; pub(crate) mod util; @@ -139,6 +145,19 @@ pub mod config { ExternalFullSwing, } + /// SWD access port protection setting. + #[non_exhaustive] + pub enum Debug { + /// Debugging is allowed (APPROTECT is disabled). Default. + Allowed, + /// Debugging is not allowed (APPROTECT is enabled). + Disallowed, + /// APPROTECT is not configured (neither to enable it or disable it). + /// This can be useful if you're already doing it by other means and + /// you don't want embassy-nrf to touch UICR. + NotConfigured, + } + /// Configuration for peripherals. Default configuration should work on any nRF chip. #[non_exhaustive] pub struct Config { @@ -152,6 +171,8 @@ pub mod config { /// Time driver interrupt priority. Should be lower priority than softdevice if used. #[cfg(feature = "_time-driver")] pub time_interrupt_priority: crate::interrupt::Priority, + /// Enable or disable the debug port. + pub debug: Debug, } impl Default for Config { @@ -166,17 +187,152 @@ pub mod config { gpiote_interrupt_priority: crate::interrupt::Priority::P0, #[cfg(feature = "_time-driver")] time_interrupt_priority: crate::interrupt::Priority::P0, + + // In NS mode, default to NotConfigured, assuming the S firmware will do it. + #[cfg(feature = "_ns")] + debug: Debug::NotConfigured, + #[cfg(not(feature = "_ns"))] + debug: Debug::Allowed, } } } } +#[cfg(feature = "_nrf9160")] +#[allow(unused)] +mod consts { + pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32; + pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF802C as *mut u32; + pub const APPROTECT_ENABLED: u32 = 0x0000_0000; +} + +#[cfg(feature = "_nrf5340-app")] +#[allow(unused)] +mod consts { + pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32; + pub const UICR_SECUREAPPROTECT: *mut u32 = 0x00FF801C as *mut u32; + pub const UICR_NFCPINS: *mut u32 = 0x00FF8028 as *mut u32; + pub const APPROTECT_ENABLED: u32 = 0x0000_0000; + pub const APPROTECT_DISABLED: u32 = 0x50FA50FA; +} + +#[cfg(feature = "_nrf5340-net")] +#[allow(unused)] +mod consts { + pub const UICR_APPROTECT: *mut u32 = 0x01FF8000 as *mut u32; + pub const APPROTECT_ENABLED: u32 = 0x0000_0000; + pub const APPROTECT_DISABLED: u32 = 0x50FA50FA; +} + +#[cfg(feature = "_nrf52")] +#[allow(unused)] +mod consts { + pub const UICR_PSELRESET1: *mut u32 = 0x10001200 as *mut u32; + pub const UICR_PSELRESET2: *mut u32 = 0x10001204 as *mut u32; + pub const UICR_NFCPINS: *mut u32 = 0x1000120C as *mut u32; + pub const UICR_APPROTECT: *mut u32 = 0x10001208 as *mut u32; + pub const APPROTECT_ENABLED: u32 = 0x0000_0000; + pub const APPROTECT_DISABLED: u32 = 0x0000_005a; +} + +unsafe fn uicr_write(address: *mut u32, value: u32) -> bool { + let curr_val = address.read_volatile(); + if curr_val == value { + return false; + } + + // Writing to UICR can only change `1` bits to `0` bits. + // If this write would change `0` bits to `1` bits, we can't do it. + // It is only possible to do when erasing UICR, which is forbidden if + // APPROTECT is enabled. + if (!curr_val) & value != 0 { + panic!("Cannot write UICR address={:08x} value={:08x}", address as u32, value) + } + + let nvmc = &*pac::NVMC::ptr(); + nvmc.config.write(|w| w.wen().wen()); + while nvmc.ready.read().ready().is_busy() {} + address.write_volatile(value); + while nvmc.ready.read().ready().is_busy() {} + nvmc.config.reset(); + while nvmc.ready.read().ready().is_busy() {} + + true +} + /// Initialize peripherals with the provided configuration. This should only be called once at startup. pub fn init(config: config::Config) -> Peripherals { // Do this first, so that it panics if user is calling `init` a second time // before doing anything important. let peripherals = Peripherals::take(); + let mut needs_reset = false; + + // Setup debug protection. + match config.debug { + config::Debug::Allowed => { + #[cfg(feature = "_nrf52")] + unsafe { + let variant = (0x1000_0104 as *mut u32).read_volatile(); + // Get the letter for the build code (b'A' .. b'F') + let build_code = (variant >> 8) as u8; + + if build_code >= b'F' { + // Chips with build code F and higher (revision 3 and higher) have an + // improved APPROTECT ("hardware and software controlled access port protection") + // which needs explicit action by the firmware to keep it unlocked + + // UICR.APPROTECT = SwDisabled + needs_reset |= uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); + // APPROTECT.DISABLE = SwDisabled + (0x4000_0558 as *mut u32).write_volatile(consts::APPROTECT_DISABLED); + } else { + // nothing to do on older chips, debug is allowed by default. + } + } + + #[cfg(feature = "_nrf5340")] + unsafe { + let p = &*pac::CTRLAP::ptr(); + + needs_reset |= uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); + p.approtect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED)); + + #[cfg(feature = "_nrf5340-app")] + { + needs_reset |= uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED); + p.secureapprotect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED)); + } + } + + // nothing to do on the nrf9160, debug is allowed by default. + } + config::Debug::Disallowed => unsafe { + // UICR.APPROTECT = Enabled + needs_reset |= uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_ENABLED); + #[cfg(any(feature = "_nrf5340-app", feature = "_nrf9160"))] + { + needs_reset |= uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED); + } + }, + config::Debug::NotConfigured => {} + } + + #[cfg(all(feature = "_nrf52", not(feature = "reset-pin-as-gpio")))] + unsafe { + needs_reset |= uicr_write(consts::UICR_PSELRESET1, chip::RESET_PIN); + needs_reset |= uicr_write(consts::UICR_PSELRESET2, chip::RESET_PIN); + } + + #[cfg(all(any(feature = "_nrf52", feature = "_nrf5340-app"), feature = "nfc-pins-as-gpio"))] + unsafe { + needs_reset |= uicr_write(consts::UICR_NFCPINS, 0); + } + + if needs_reset { + cortex_m::peripheral::SCB::sys_reset(); + } + let r = unsafe { &*pac::CLOCK::ptr() }; // Start HFCLK. diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index c1ffa31aa..6f48853d5 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -85,23 +85,23 @@ impl<'d> Nvmc<'d> { } fn enable_erase(&self) { - #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + #[cfg(not(feature = "_ns"))] Self::regs().config.write(|w| w.wen().een()); - #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + #[cfg(feature = "_ns")] Self::regs().configns.write(|w| w.wen().een()); } fn enable_read(&self) { - #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + #[cfg(not(feature = "_ns"))] Self::regs().config.write(|w| w.wen().ren()); - #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + #[cfg(feature = "_ns")] Self::regs().configns.write(|w| w.wen().ren()); } fn enable_write(&self) { - #[cfg(not(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns")))] + #[cfg(not(feature = "_ns"))] Self::regs().config.write(|w| w.wen().wen()); - #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + #[cfg(feature = "_ns")] Self::regs().configns.write(|w| w.wen().wen()); } } From 7fa478358ac117557af778d12d7812da5521fdfd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 20 Feb 2023 01:29:12 +0100 Subject: [PATCH 0607/1575] nrf: warn if uicr configuration could not be written. If the user requests some configuration, but UICR is already programmed to something else, detect this and warn the user. We don't do it for the debug port settings, because if they are wrong then the user will simply not be able to read debug logs. --- embassy-nrf/src/lib.rs | 78 +++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 0fa1c0f66..a9683df44 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -235,10 +235,26 @@ mod consts { pub const APPROTECT_DISABLED: u32 = 0x0000_005a; } -unsafe fn uicr_write(address: *mut u32, value: u32) -> bool { +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum WriteResult { + /// Word was written successfully, needs reset. + Written, + /// Word was already set to the value we wanted to write, nothing was done. + Noop, + /// Word is already set to something else, we couldn't write the desired value. + Failed, +} + +unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { let curr_val = address.read_volatile(); if curr_val == value { - return false; + return WriteResult::Noop; + } + + // We can only change `1` bits to `0` bits. + if curr_val & value != value { + return WriteResult::Failed; } // Writing to UICR can only change `1` bits to `0` bits. @@ -257,7 +273,7 @@ unsafe fn uicr_write(address: *mut u32, value: u32) -> bool { nvmc.config.reset(); while nvmc.ready.read().ready().is_busy() {} - true + WriteResult::Written } /// Initialize peripherals with the provided configuration. This should only be called once at startup. @@ -283,7 +299,8 @@ pub fn init(config: config::Config) -> Peripherals { // which needs explicit action by the firmware to keep it unlocked // UICR.APPROTECT = SwDisabled - needs_reset |= uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); + let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); + needs_reset |= res == WriteResult::Written; // APPROTECT.DISABLE = SwDisabled (0x4000_0558 as *mut u32).write_volatile(consts::APPROTECT_DISABLED); } else { @@ -295,12 +312,14 @@ pub fn init(config: config::Config) -> Peripherals { unsafe { let p = &*pac::CTRLAP::ptr(); - needs_reset |= uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); + let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); + needs_reset |= res == WriteResult::Written; p.approtect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED)); #[cfg(feature = "_nrf5340-app")] { - needs_reset |= uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED); + let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED); + needs_reset |= res == WriteResult::Written; p.secureapprotect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED)); } } @@ -309,24 +328,57 @@ pub fn init(config: config::Config) -> Peripherals { } config::Debug::Disallowed => unsafe { // UICR.APPROTECT = Enabled - needs_reset |= uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_ENABLED); + let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_ENABLED); + needs_reset |= res == WriteResult::Written; #[cfg(any(feature = "_nrf5340-app", feature = "_nrf9160"))] { - needs_reset |= uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED); + let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED); + needs_reset |= res == WriteResult::Written; } }, config::Debug::NotConfigured => {} } - #[cfg(all(feature = "_nrf52", not(feature = "reset-pin-as-gpio")))] + #[cfg(feature = "_nrf52")] unsafe { - needs_reset |= uicr_write(consts::UICR_PSELRESET1, chip::RESET_PIN); - needs_reset |= uicr_write(consts::UICR_PSELRESET2, chip::RESET_PIN); + let value = if cfg!(feature = "reset-pin-as-gpio") { + !0 + } else { + chip::RESET_PIN + }; + let res1 = uicr_write(consts::UICR_PSELRESET1, value); + let res2 = uicr_write(consts::UICR_PSELRESET2, value); + needs_reset |= res1 == WriteResult::Written || res2 == WriteResult::Written; + if res1 == WriteResult::Failed || res2 == WriteResult::Failed { + #[cfg(not(feature = "reset-pin-as-gpio"))] + warn!( + "You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\ + However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ + To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." + ); + #[cfg(feature = "reset-pin-as-gpio")] + warn!( + "You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\ + However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ + To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." + ); + } } - #[cfg(all(any(feature = "_nrf52", feature = "_nrf5340-app"), feature = "nfc-pins-as-gpio"))] + #[cfg(any(feature = "_nrf52", feature = "_nrf5340-app"))] unsafe { - needs_reset |= uicr_write(consts::UICR_NFCPINS, 0); + let value = if cfg!(feature = "nfc-pins-as-gpio") { 0 } else { !0 }; + let res = uicr_write(consts::UICR_NFCPINS, value); + needs_reset |= res == WriteResult::Written; + if res == WriteResult::Failed { + // with nfc-pins-as-gpio, this can never fail because we're writing all zero bits. + #[cfg(not(feature = "nfc-pins-as-gpio"))] + warn!( + "You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\ + However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ + To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." + ); + } } if needs_reset { From f34829f534297dfccb1c5b206bffcc7700ef86ae Mon Sep 17 00:00:00 2001 From: Pol Fernandez Date: Mon, 20 Feb 2023 21:03:39 +0100 Subject: [PATCH 0608/1575] Add stringify function --- examples/rpi-pico-w/src/main.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 71459a122..e71c22345 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -18,6 +18,9 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +use heapless::String; + + macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; @@ -129,7 +132,8 @@ async fn main(spawner: Spawner) { } }; - info!("rxd {:02x}", &buf[..n]); + info!("rxd {}", asciify(&buf[..n])); + match socket.write_all(&buf[..n]).await { Ok(()) => {} @@ -214,3 +218,7 @@ impl SpiBusWrite for MySpi { Ok(()) } } + +fn asciify(buf: &[u8],) -> String<4096> { + buf.into_iter().map(|c| *c as char).into_iter().collect() +} From f6f041b05d9702982e3cf56bb76f7904485677c8 Mon Sep 17 00:00:00 2001 From: Pol Fernandez Date: Tue, 21 Feb 2023 08:52:57 +0100 Subject: [PATCH 0609/1575] Add from_utf8 --- examples/rpi-pico-w/src/main.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index e71c22345..c706e121d 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -1,4 +1,4 @@ -#![no_std] +#![no_std] #![no_main] #![feature(type_alias_impl_trait)] #![feature(async_fn_in_trait)] @@ -18,8 +18,7 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -use heapless::String; - +use core::str::from_utf8; macro_rules! singleton { ($val:expr) => {{ @@ -132,7 +131,7 @@ async fn main(spawner: Spawner) { } }; - info!("rxd {}", asciify(&buf[..n])); + info!("rxd {}", from_utf8(&buf[..n]).unwrap()); match socket.write_all(&buf[..n]).await { @@ -218,7 +217,3 @@ impl SpiBusWrite for MySpi { Ok(()) } } - -fn asciify(buf: &[u8],) -> String<4096> { - buf.into_iter().map(|c| *c as char).into_iter().collect() -} From ada3d5be7c9746819c9c0c73d3031a68ac20e6fe Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 21 Feb 2023 22:41:23 +0100 Subject: [PATCH 0610/1575] nrf: rename UARTETWISPIn -> SERIALn The UARTETWISPIn naming is quite horrible. With the nRF53, Nordic realized this and renamed the interrupts to SERIALn. Let's copy that for our peripheral names, in nrf53 and nrf91. --- embassy-nrf/src/chips/nrf5340_app.rs | 48 ++++++++++++++-------------- embassy-nrf/src/chips/nrf5340_net.rs | 18 +++++------ embassy-nrf/src/chips/nrf9160.rs | 48 ++++++++++++++-------------- examples/nrf5340/src/bin/uart.rs | 2 +- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 2b8b55e77..2e1c7f384 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -231,10 +231,10 @@ embassy_hal_common::peripherals! { NVMC, // UARTE, TWI & SPI - UARTETWISPI0, - UARTETWISPI1, - UARTETWISPI2, - UARTETWISPI3, + SERIAL0, + SERIAL1, + SERIAL2, + SERIAL3, // SAADC SAADC, @@ -359,30 +359,30 @@ embassy_hal_common::peripherals! { #[cfg(feature = "nightly")] impl_usb!(USBD, USBD, USBD); -impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); -impl_uarte!(UARTETWISPI1, UARTE1, SERIAL1); -impl_uarte!(UARTETWISPI2, UARTE2, SERIAL2); -impl_uarte!(UARTETWISPI3, UARTE3, SERIAL3); +impl_uarte!(SERIAL0, UARTE0, SERIAL0); +impl_uarte!(SERIAL1, UARTE1, SERIAL1); +impl_uarte!(SERIAL2, UARTE2, SERIAL2); +impl_uarte!(SERIAL3, UARTE3, SERIAL3); -impl_spim!(UARTETWISPI0, SPIM0, SERIAL0); -impl_spim!(UARTETWISPI1, SPIM1, SERIAL1); -impl_spim!(UARTETWISPI2, SPIM2, SERIAL2); -impl_spim!(UARTETWISPI3, SPIM3, SERIAL3); +impl_spim!(SERIAL0, SPIM0, SERIAL0); +impl_spim!(SERIAL1, SPIM1, SERIAL1); +impl_spim!(SERIAL2, SPIM2, SERIAL2); +impl_spim!(SERIAL3, SPIM3, SERIAL3); -impl_spis!(UARTETWISPI0, SPIS0, SERIAL0); -impl_spis!(UARTETWISPI1, SPIS1, SERIAL1); -impl_spis!(UARTETWISPI2, SPIS2, SERIAL2); -impl_spis!(UARTETWISPI3, SPIS3, SERIAL3); +impl_spis!(SERIAL0, SPIS0, SERIAL0); +impl_spis!(SERIAL1, SPIS1, SERIAL1); +impl_spis!(SERIAL2, SPIS2, SERIAL2); +impl_spis!(SERIAL3, SPIS3, SERIAL3); -impl_twim!(UARTETWISPI0, TWIM0, SERIAL0); -impl_twim!(UARTETWISPI1, TWIM1, SERIAL1); -impl_twim!(UARTETWISPI2, TWIM2, SERIAL2); -impl_twim!(UARTETWISPI3, TWIM3, SERIAL3); +impl_twim!(SERIAL0, TWIM0, SERIAL0); +impl_twim!(SERIAL1, TWIM1, SERIAL1); +impl_twim!(SERIAL2, TWIM2, SERIAL2); +impl_twim!(SERIAL3, TWIM3, SERIAL3); -impl_twis!(UARTETWISPI0, TWIS0, SERIAL0); -impl_twis!(UARTETWISPI1, TWIS1, SERIAL1); -impl_twis!(UARTETWISPI2, TWIS2, SERIAL2); -impl_twis!(UARTETWISPI3, TWIS3, SERIAL3); +impl_twis!(SERIAL0, TWIS0, SERIAL0); +impl_twis!(SERIAL1, TWIS1, SERIAL1); +impl_twis!(SERIAL2, TWIS2, SERIAL2); +impl_twis!(SERIAL3, TWIS3, SERIAL3); impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index a46583eca..d7ba6c16c 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -119,10 +119,10 @@ embassy_hal_common::peripherals! { NVMC, // UARTE, TWI & SPI - UARTETWISPI0, - UARTETWISPI1, - UARTETWISPI2, - UARTETWISPI3, + SERIAL0, + SERIAL1, + SERIAL2, + SERIAL3, // SAADC SAADC, @@ -242,11 +242,11 @@ embassy_hal_common::peripherals! { P1_15, } -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_uarte!(SERIAL0, UARTE0, SERIAL0); +impl_spim!(SERIAL0, SPIM0, SERIAL0); +impl_spis!(SERIAL0, SPIS0, SERIAL0); +impl_twim!(SERIAL0, TWIM0, SERIAL0); +impl_twis!(SERIAL0, 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 e1509ddde..385bd133d 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -179,10 +179,10 @@ embassy_hal_common::peripherals! { NVMC, // UARTE, TWI & SPI - UARTETWISPI0, - UARTETWISPI1, - UARTETWISPI2, - UARTETWISPI3, + SERIAL0, + SERIAL1, + SERIAL2, + SERIAL3, // SAADC SAADC, @@ -271,30 +271,30 @@ embassy_hal_common::peripherals! { PDM, } -impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); -impl_uarte!(UARTETWISPI1, UARTE1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); -impl_uarte!(UARTETWISPI2, UARTE2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); -impl_uarte!(UARTETWISPI3, UARTE3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_uarte!(SERIAL0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_uarte!(SERIAL1, UARTE1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_uarte!(SERIAL2, UARTE2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_uarte!(SERIAL3, UARTE3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); -impl_spim!(UARTETWISPI0, SPIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); -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_spim!(SERIAL0, SPIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_spim!(SERIAL1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_spim!(SERIAL2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_spim!(SERIAL3, 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_spis!(SERIAL0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_spis!(SERIAL1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_spis!(SERIAL2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_spis!(SERIAL3, 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_twim!(SERIAL0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_twim!(SERIAL1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_twim!(SERIAL2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_twim!(SERIAL3, 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_twis!(SERIAL0, TWIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); +impl_twis!(SERIAL1, TWIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); +impl_twis!(SERIAL2, TWIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); +impl_twis!(SERIAL3, TWIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs index 0f2b7b1e3..5f448c2ba 100644 --- a/examples/nrf5340/src/bin/uart.rs +++ b/examples/nrf5340/src/bin/uart.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { config.baudrate = uarte::Baudrate::BAUD115200; let irq = interrupt::take!(SERIAL0); - let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P1_00, p.P1_01, config); + let mut uart = uarte::Uarte::new(p.SERIAL0, irq, p.P1_00, p.P1_01, config); info!("uarte initialized!"); From f1a4db44c4c3f591df378190a6604d4547a37c25 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Wed, 22 Feb 2023 13:57:40 +0100 Subject: [PATCH 0611/1575] Implement flush for TcpSocket --- embassy-net/src/tcp.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index d46bd4dbf..b9e494fc4 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -254,10 +254,19 @@ impl<'d> TcpIo<'d> { .await } - #[allow(unused)] async fn flush(&mut self) -> Result<(), Error> { - poll_fn(move |_| { - Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? + poll_fn(move |cx| { + self.with_mut(|s, _| { + // If there are outstanding send operations, register for wake up and wait + // smoltcp issues wake-ups when octets are dequeued from the send buffer + if s.send_queue() > 0 { + s.register_send_waker(cx.waker()); + Poll::Pending + // No outstanding sends, socket is flushed + } else { + Poll::Ready(Ok(())) + } + }) }) .await } From 035de6f3ff7bae31dce25b55e822f75b46ebe047 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Wed, 22 Feb 2023 14:45:17 +0100 Subject: [PATCH 0612/1575] embassy-net: add flush to TcpSocket and TcpWriter as an inherent method --- embassy-net/src/tcp.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index b9e494fc4..c3d8764b0 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -63,6 +63,10 @@ impl<'a> TcpWriter<'a> { pub async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } + + pub async fn flush(&mut self) -> Result<(), Error> { + self.io.flush().await + } } impl<'a> TcpSocket<'a> { @@ -146,6 +150,10 @@ impl<'a> TcpSocket<'a> { self.io.write(buf).await } + pub async fn flush(&mut self) -> Result<(), Error> { + self.io.flush().await + } + pub fn set_timeout(&mut self, duration: Option) { self.io.with_mut(|s, _| s.set_timeout(duration)) } From 4e884ee2d2f0c3f4a46f1bc539a12e9fdce173e2 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Thu, 23 Feb 2023 10:09:09 +1100 Subject: [PATCH 0613/1575] stm32/dma: fix spurious transfer complete interrupts DMA interrupts must be acknowledged by writing to the DMA_{L,H}IFCR register. Writing to the CR register is unnecessary as the channel (EN bit) is disabled by hardware on completion of the transfer. --- embassy-stm32/src/dma/dma.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index fec60f708..8966214ec 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -411,12 +411,8 @@ mod low_level_api { } if isr.tcif(channel_num % 4) && cr.read().tcie() { - if cr.read().dbm() == vals::Dbm::DISABLED { - cr.write(|_| ()); // Disable channel with the default value. - } else { - // for double buffered mode, clear TCIF flag but do not stop the transfer - dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); - } + /* acknowledge transfer complete interrupt */ + dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); STATE.channels[state_index].waker.wake(); } } From 42462681bd604750dfe8fa709453edf43c25b09d Mon Sep 17 00:00:00 2001 From: chemicstry Date: Thu, 23 Feb 2023 16:57:21 +0200 Subject: [PATCH 0614/1575] stm32/sdmmc: Implement proper clock configuration --- embassy-stm32/src/sdmmc/mod.rs | 87 +++++++++++++++++++++++++------ examples/stm32f4/src/bin/sdmmc.rs | 3 +- examples/stm32f7/src/bin/sdmmc.rs | 1 + 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 3c99a0b6c..0bcd42bc2 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -140,10 +140,21 @@ cfg_if::cfg_if! { /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to /// `sdmmc_ck` in Hertz. /// - /// Returns `(clk_div, clk_f)`, where `clk_div` is the divisor register - /// value and `clk_f` is the resulting new clock frequency. - fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(u8, Hertz), Error> { - let clk_div = match ker_ck.0 / sdmmc_ck { + /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), + /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. + fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { + // sdmmc_v1 maximum clock is 50 MHz + if sdmmc_ck > 50_000_000 { + return Err(Error::BadClock); + } + + // bypass divisor + if ker_ck.0 <= sdmmc_ck { + return Ok((true, 0, ker_ck)); + } + + // `ker_ck / sdmmc_ck` rounded up + let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { 0 | 1 => Ok(0), x @ 2..=258 => { Ok((x - 2) as u8) @@ -153,22 +164,24 @@ cfg_if::cfg_if! { // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); - Ok((clk_div, clk_f)) + Ok((false, clk_div, clk_f)) } } else if #[cfg(sdmmc_v2)] { /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to /// `sdmmc_ck` in Hertz. /// - /// Returns `(clk_div, clk_f)`, where `clk_div` is the divisor register - /// value and `clk_f` is the resulting new clock frequency. - fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(u16, Hertz), Error> { + /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), + /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. + fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { + // `ker_ck / sdmmc_ck` rounded up match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { - 0 | 1 => Ok((0, ker_ck)), + 0 | 1 => Ok((false, 0, ker_ck)), x @ 2..=2046 => { + // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] let clk_div = ((x + 1) / 2) as u16; let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); - Ok((clk_div, clk)) + Ok((false, clk_div, clk)) } _ => Err(Error::BadClock), } @@ -478,7 +491,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { bus_width, &mut self.card, &mut self.signalling, - T::frequency(), + Self::kernel_clock(), &mut self.clock, T::state(), self.config.data_transfer_timeout, @@ -550,6 +563,44 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { regs.data_interrupts(false); state.wake(); } + + /// Returns kernel clock (SDIOCLK) for the SD-card facing domain + fn kernel_clock() -> Hertz { + cfg_if::cfg_if! { + // TODO, these could not be implemented, because required clocks are not exposed in RCC: + // - H7 uses pll1_q_ck or pll2_r_ck depending on SDMMCSEL + // - L1 uses pll48 + // - L4 uses clk48(pll48) + // - L4+, L5, U5 uses clk48(pll48) or PLLSAI3CLK(PLLP) depending on SDMMCSEL + if #[cfg(stm32f1)] { + // F1 uses AHB1(HCLK), which is correct in PAC + T::frequency() + } else if #[cfg(any(stm32f2, stm32f4))] { + // F2, F4 always use pll48 + critical_section::with(|_| unsafe { + crate::rcc::get_freqs().pll48 + }).expect("PLL48 is required for SDIO") + } else if #[cfg(stm32f7)] { + critical_section::with(|_| unsafe { + use core::any::TypeId; + let sdmmcsel = if TypeId::of::() == TypeId::of::() { + crate::pac::RCC.dckcfgr2().read().sdmmc1sel() + } else { + crate::pac::RCC.dckcfgr2().read().sdmmc2sel() + }; + + if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK { + crate::rcc::get_freqs().sys + } else { + crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC") + } + }) + } else { + // Use default peripheral clock and hope it works + T::frequency() + } + } + } } impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { @@ -625,7 +676,7 @@ impl SdmmcInner { 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)); + let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); *clock = init_clock; // CPSMACT and DPSMACT must be 0 to set WIDBUS @@ -634,6 +685,8 @@ impl SdmmcInner { regs.clkcr().modify(|w| { w.set_widbus(0); w.set_clkdiv(clkdiv); + #[cfg(sdmmc_v1)] + w.set_bypass(_bypass); }); regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); @@ -1052,7 +1105,8 @@ impl SdmmcInner { _ => panic!("Invalid Bus Width"), }; - let (clkdiv, new_clock) = clk_div(ker_ck, freq)?; + let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; + // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 // Section 55.5.8 let sdmmc_bus_bandwidth = new_clock.0 * width_u32; @@ -1063,7 +1117,11 @@ impl SdmmcInner { unsafe { // CPSMACT and DPSMACT must be 0 to set CLKDIV self.wait_idle(); - regs.clkcr().modify(|w| w.set_clkdiv(clkdiv)); + regs.clkcr().modify(|w| { + w.set_clkdiv(clkdiv); + #[cfg(sdmmc_v1)] + w.set_bypass(_bypass); + }); } Ok(()) @@ -1152,7 +1210,6 @@ impl SdmmcInner { } /// Query the card status (CMD13, returns R1) - /// fn read_status(&self, card: &Card) -> Result { let regs = self.0; let rca = card.rca; diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index b57e955f6..1d0e60cb8 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -17,6 +17,7 @@ const ALLOW_WRITES: bool = false; async fn main(_spawner: Spawner) -> ! { let mut config = Config::default(); config.rcc.sys_ck = Some(mhz(48)); + config.rcc.pll48 = true; let p = embassy_stm32::init(config); info!("Hello World!"); @@ -38,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_card(mhz(24)).await); + unwrap!(sdmmc.init_card(mhz(48)).await); let card = unwrap!(sdmmc.card()); diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 3bf427eca..cf8128e27 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -13,6 +13,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) -> ! { let mut config = Config::default(); config.rcc.sys_ck = Some(mhz(200)); + config.rcc.pll48 = true; let p = embassy_stm32::init(config); info!("Hello World!"); From 896764bb8562b483ceecb39db2827061fc90d598 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Thu, 23 Feb 2023 17:38:52 +0200 Subject: [PATCH 0615/1575] stm32/sdmmc: Refactor TypeId into a macro --- embassy-stm32/src/sdmmc/mod.rs | 100 ++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 0bcd42bc2..2d91286fa 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -491,7 +491,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { bus_width, &mut self.card, &mut self.signalling, - Self::kernel_clock(), + T::kernel_clk(), &mut self.clock, T::state(), self.config.data_transfer_timeout, @@ -563,44 +563,6 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { regs.data_interrupts(false); state.wake(); } - - /// Returns kernel clock (SDIOCLK) for the SD-card facing domain - fn kernel_clock() -> Hertz { - cfg_if::cfg_if! { - // TODO, these could not be implemented, because required clocks are not exposed in RCC: - // - H7 uses pll1_q_ck or pll2_r_ck depending on SDMMCSEL - // - L1 uses pll48 - // - L4 uses clk48(pll48) - // - L4+, L5, U5 uses clk48(pll48) or PLLSAI3CLK(PLLP) depending on SDMMCSEL - if #[cfg(stm32f1)] { - // F1 uses AHB1(HCLK), which is correct in PAC - T::frequency() - } else if #[cfg(any(stm32f2, stm32f4))] { - // F2, F4 always use pll48 - critical_section::with(|_| unsafe { - crate::rcc::get_freqs().pll48 - }).expect("PLL48 is required for SDIO") - } else if #[cfg(stm32f7)] { - critical_section::with(|_| unsafe { - use core::any::TypeId; - let sdmmcsel = if TypeId::of::() == TypeId::of::() { - crate::pac::RCC.dckcfgr2().read().sdmmc1sel() - } else { - crate::pac::RCC.dckcfgr2().read().sdmmc2sel() - }; - - if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK { - crate::rcc::get_freqs().sys - } else { - crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC") - } - }) - } else { - // Use default peripheral clock and hope it works - T::frequency() - } - } - } } impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { @@ -1580,6 +1542,7 @@ pub(crate) mod sealed { fn inner() -> SdmmcInner; fn state() -> &'static AtomicWaker; + fn kernel_clk() -> Hertz; } pub trait Pins {} @@ -1607,6 +1570,61 @@ cfg_if::cfg_if! { } } +cfg_if::cfg_if! { + // TODO, these could not be implemented, because required clocks are not exposed in RCC: + // - H7 uses pll1_q_ck or pll2_r_ck depending on SDMMCSEL + // - L1 uses pll48 + // - L4 uses clk48(pll48) + // - L4+, L5, U5 uses clk48(pll48) or PLLSAI3CLK(PLLP) depending on SDMMCSEL + if #[cfg(stm32f1)] { + // F1 uses AHB1(HCLK), which is correct in PAC + macro_rules! kernel_clk { + ($inst:ident) => { + peripherals::$inst::frequency() + } + } + } else if #[cfg(any(stm32f2, stm32f4))] { + // F2, F4 always use pll48 + macro_rules! kernel_clk { + ($inst:ident) => { + critical_section::with(|_| unsafe { + crate::rcc::get_freqs().pll48 + }).expect("PLL48 is required for SDIO") + } + } + } else if #[cfg(stm32f7)] { + macro_rules! kernel_clk { + (SDMMC1) => { + critical_section::with(|_| unsafe { + let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc1sel(); + if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK { + crate::rcc::get_freqs().sys + } else { + crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC") + } + }) + }; + (SDMMC2) => { + critical_section::with(|_| unsafe { + let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc2sel(); + if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK { + crate::rcc::get_freqs().sys + } else { + crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC") + } + }) + }; + } + } else { + // Use default peripheral clock and hope it works + macro_rules! kernel_clk { + ($inst:ident) => { + peripherals::$inst::frequency() + } + } + } +} + foreach_peripheral!( (sdmmc, $inst:ident) => { impl sealed::Instance for peripherals::$inst { @@ -1621,6 +1639,10 @@ foreach_peripheral!( static WAKER: ::embassy_sync::waitqueue::AtomicWaker = ::embassy_sync::waitqueue::AtomicWaker::new(); &WAKER } + + fn kernel_clk() -> Hertz { + kernel_clk!($inst) + } } impl Instance for peripherals::$inst {} From 73ef85b7650eea65c2f52e570f26062dd8ec38d0 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Thu, 23 Feb 2023 18:00:55 +0200 Subject: [PATCH 0616/1575] stm32/sdmmc: Fix compile errors --- embassy-stm32/src/sdmmc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 2d91286fa..03d24dcb1 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1580,7 +1580,7 @@ cfg_if::cfg_if! { // F1 uses AHB1(HCLK), which is correct in PAC macro_rules! kernel_clk { ($inst:ident) => { - peripherals::$inst::frequency() + ::frequency() } } } else if #[cfg(any(stm32f2, stm32f4))] { @@ -1619,7 +1619,7 @@ cfg_if::cfg_if! { // Use default peripheral clock and hope it works macro_rules! kernel_clk { ($inst:ident) => { - peripherals::$inst::frequency() + ::frequency() } } } From 43a4409405e0df2f53452ca5d3a19a7d13e0f234 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 23 Feb 2023 19:25:22 +0100 Subject: [PATCH 0617/1575] embassy-time: Implement conversions to/from core::time::Duration for embassy-time::Duration --- embassy-time/src/duration.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index 9d0bab2dd..8366455be 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -192,3 +192,19 @@ impl<'a> fmt::Display for Duration { const fn div_ceil(num: u64, den: u64) -> u64 { (num + den - 1) / den } + +impl TryFrom for Duration { + type Error = >::Error; + + /// Converts using [`Duration::from_micros`]. Fails if value can not be represented as u64. + fn try_from(value: core::time::Duration) -> Result { + Ok(Self::from_micros(value.as_micros().try_into()?)) + } +} + +impl From for core::time::Duration { + /// Converts using [`Duration::as_micros`]. + fn from(value: Duration) -> Self { + core::time::Duration::from_micros(value.as_micros()) + } +} From 7be4337de96de9948632bdc2fc5067d0c4a76b33 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 24 Feb 2023 13:01:41 -0600 Subject: [PATCH 0618/1575] Add `#[must_use]` to all futures --- embassy-futures/src/yield_now.rs | 1 + embassy-nrf/src/gpiote.rs | 1 + embassy-rp/src/dma.rs | 1 + embassy-rp/src/gpio.rs | 1 + embassy-rp/src/pio.rs | 3 +++ embassy-stm32/src/dma/mod.rs | 1 + embassy-stm32/src/exti.rs | 1 + embassy-sync/src/channel.rs | 4 ++++ embassy-sync/src/pipe.rs | 2 ++ embassy-time/src/timer.rs | 1 + 10 files changed, 16 insertions(+) diff --git a/embassy-futures/src/yield_now.rs b/embassy-futures/src/yield_now.rs index 13b103778..bb3c67d17 100644 --- a/embassy-futures/src/yield_now.rs +++ b/embassy-futures/src/yield_now.rs @@ -24,6 +24,7 @@ pub fn yield_now() -> impl Future { YieldNowFuture { yielded: false } } +#[must_use = "futures do nothing unless you `.await` or poll them"] struct YieldNowFuture { yielded: bool, } diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index e1816eb9b..66c682b43 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -315,6 +315,7 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { // ======================= +#[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct PortInputFuture<'a> { pin: PeripheralRef<'a, AnyPin>, } diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index fd281fd5d..05adcecdd 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -151,6 +151,7 @@ fn copy_inner<'a, C: Channel>( Transfer::new(ch) } +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a, C: Channel> { channel: PeripheralRef<'a, C>, } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 76d4281ff..fd3b05567 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -193,6 +193,7 @@ unsafe fn IO_IRQ_BANK0() { } } +#[must_use = "futures do nothing unless you `.await` or poll them"] struct InputFuture<'a, T: Pin> { pin: PeripheralRef<'a, T>, level: InterruptTrigger, diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 2fb2783de..3c7abea25 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -120,6 +120,7 @@ unsafe fn PIO1_IRQ_0() { } /// Future that waits for TX-FIFO to become writable +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { sm: &'a mut SM, pio: PhantomData, @@ -182,6 +183,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<' } /// Future that waits for RX-FIFO to become readable +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachine> { sm: &'a mut SM, pio: PhantomData, @@ -241,6 +243,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, S } /// Future that waits for IRQ +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct IrqFuture { pio: PhantomData, irq_no: u8, diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index f5a82fb7a..0030bd575 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -273,6 +273,7 @@ mod transfers { Transfer::new(channel) } + #[must_use = "futures do nothing unless you `.await` or poll them"] pub(crate) struct Transfer<'a, C: Channel> { channel: PeripheralRef<'a, C>, } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index c9c3ef62a..e1ce09a49 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -198,6 +198,7 @@ mod eha { } } +#[must_use = "futures do nothing unless you `.await` or poll them"] struct ExtiInputFuture<'a> { pin: u8, phantom: PhantomData<&'a mut AnyPin>, diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 76f42d0e7..77352874d 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -181,6 +181,7 @@ where } /// Future returned by [`Channel::recv`] and [`Receiver::recv`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct RecvFuture<'ch, M, T, const N: usize> where M: RawMutex, @@ -203,6 +204,7 @@ where } /// Future returned by [`DynamicReceiver::recv`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DynamicRecvFuture<'ch, T> { channel: &'ch dyn DynamicChannel, } @@ -219,6 +221,7 @@ impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { } /// Future returned by [`Channel::send`] and [`Sender::send`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct SendFuture<'ch, M, T, const N: usize> where M: RawMutex, @@ -250,6 +253,7 @@ where impl<'ch, M, T, const N: usize> Unpin for SendFuture<'ch, M, T, N> where M: RawMutex {} /// Future returned by [`DynamicSender::send`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DynamicSendFuture<'ch, T> { channel: &'ch dyn DynamicChannel, message: Option, diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 905686acd..1977005fb 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -48,6 +48,7 @@ where } /// Future returned by [`Pipe::write`] and [`Writer::write`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct WriteFuture<'p, M, const N: usize> where M: RawMutex, @@ -110,6 +111,7 @@ where } /// Future returned by [`Pipe::read`] and [`Reader::read`]. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ReadFuture<'p, M, const N: usize> where M: RawMutex, diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index bd791b817..f74b5cb24 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -26,6 +26,7 @@ pub async fn with_timeout(timeout: Duration, fut: F) -> Result Date: Sat, 25 Feb 2023 20:58:28 +0100 Subject: [PATCH 0619/1575] embassy-net: DNS resolver detects when name is just an IP address and returns immediately --- embassy-net/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index bda5f9e14..4ec1b5a77 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -236,6 +236,22 @@ impl Stack { /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { + // For A and AAAA queries we try detect whether `name` is just an IP address + match qtype { + dns::DnsQueryType::A => { + if let Ok(ip) = name.parse().map(IpAddress::Ipv4) { + return Ok([ip].into_iter().collect()); + } + } + #[cfg(feature = "proto-ipv6")] + dns::DnsQueryType::Aaaa => { + if let Ok(ip) = name.parse().map(IpAddress::Ipv6) { + return Ok([ip].into_iter().collect()); + } + } + _ => {} + } + let query = poll_fn(|cx| { self.with_mut(|s, i| { let socket = s.sockets.get_mut::(i.dns_socket); From bc71230cd07296468f2e03c00f9ceddbab67c9d9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Feb 2023 21:50:12 +0100 Subject: [PATCH 0620/1575] examples/std: fix net running out of sockets. --- examples/std/src/bin/net.rs | 2 +- examples/std/src/bin/net_dns.rs | 2 +- examples/std/src/bin/net_udp.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 451850d99..e018e18c9 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -65,7 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index e1cc45a38..d1e1f8212 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -65,7 +65,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index f1923f180..328a0536c 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -62,7 +62,7 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); From 8fb380b18052c2393ae1dc3466bb87e9402181d8 Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 18:40:23 -0500 Subject: [PATCH 0621/1575] RP-PICO UART adding set_baudrate --- embassy-rp/src/uart/mod.rs | 50 +++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index bbbf97c01..7540052b8 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -296,7 +296,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { Some(rx_dma.map_into()), config, ) - } + } } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { @@ -324,6 +324,25 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { } } + fn baudrate_calculations(baudrate: u32) -> (u32, u32) { + + let clk_base = crate::clocks::clk_peri_freq(); + + let baud_rate_div = (8 * clk_base) / baudrate; + let mut baud_ibrd = baud_rate_div >> 7; + let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; + + if baud_ibrd == 0 { + baud_ibrd = 1; + baud_fbrd = 0; + } else if baud_ibrd >= 65535 { + baud_ibrd = 65535; + baud_fbrd = 0; + } + + (baud_ibrd, baud_fbrd) + } + fn init( tx: Option>, rx: Option>, @@ -350,19 +369,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - let clk_base = crate::clocks::clk_peri_freq(); - - let baud_rate_div = (8 * clk_base) / config.baudrate; - let mut baud_ibrd = baud_rate_div >> 7; - let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; - - if baud_ibrd == 0 { - baud_ibrd = 1; - baud_fbrd = 0; - } else if baud_ibrd >= 65535 { - baud_ibrd = 65535; - baud_fbrd = 0; - } + let (baud_ibrd, baud_fbrd) = Uart::::baudrate_calculations(config.baudrate); // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); @@ -400,6 +407,21 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { }); } } + + pub fn set_baudrate(&mut self, baudrate: u32) { + + let r = T::regs(); + + let (baud_ibrd, baud_fbrd) = Self::baudrate_calculations(baudrate); + + unsafe { + + // Load PL011's baud divisor registers + r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); + r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + } + + } } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { From 7172dfd08379a3732372ada61e25d1c5678fa59f Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 19:14:25 -0500 Subject: [PATCH 0622/1575] RP-PICO UART adding set_baudrate: refactoring of methods --- embassy-rp/src/uart/mod.rs | 51 +++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7540052b8..f9cce5c69 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -324,25 +324,6 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { } } - fn baudrate_calculations(baudrate: u32) -> (u32, u32) { - - let clk_base = crate::clocks::clk_peri_freq(); - - let baud_rate_div = (8 * clk_base) / baudrate; - let mut baud_ibrd = baud_rate_div >> 7; - let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; - - if baud_ibrd == 0 { - baud_ibrd = 1; - baud_fbrd = 0; - } else if baud_ibrd >= 65535 { - baud_ibrd = 65535; - baud_fbrd = 0; - } - - (baud_ibrd, baud_fbrd) - } - fn init( tx: Option>, rx: Option>, @@ -369,11 +350,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - let (baud_ibrd, baud_fbrd) = Uart::::baudrate_calculations(config.baudrate); - - // Load PL011's baud divisor registers - r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); - r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + Uart::::set_baudrate_inner(config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), @@ -408,20 +385,38 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { } } + + /// sets baudrate on runtime pub fn set_baudrate(&mut self, baudrate: u32) { - + Self::set_baudrate_inner(baudrate); + } + + + fn set_baudrate_inner(baudrate: u32) { let r = T::regs(); - let (baud_ibrd, baud_fbrd) = Self::baudrate_calculations(baudrate); - + let clk_base = crate::clocks::clk_peri_freq(); + + let baud_rate_div = (8 * clk_base) / baudrate; + let mut baud_ibrd = baud_rate_div >> 7; + let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; + + if baud_ibrd == 0 { + baud_ibrd = 1; + baud_fbrd = 0; + } else if baud_ibrd >= 65535 { + baud_ibrd = 65535; + baud_fbrd = 0; + } + unsafe { // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); } - } + } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { From 482ba835c4eec6d3bb006eea3a715360c170e418 Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 19:20:08 -0500 Subject: [PATCH 0623/1575] RP-PICO UART adding set_baudrate: Changing static call from specific type to a Self (requires adding lifetime specifier) --- embassy-rp/src/uart/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f9cce5c69..6b3e2406d 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -299,7 +299,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { } } -impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { +impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { fn new_inner( _uart: impl Peripheral

+ 'd, mut tx: PeripheralRef<'d, AnyPin>, @@ -350,7 +350,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - Uart::::set_baudrate_inner(config.baudrate); + Self::set_baudrate_inner(config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), From 2331d58aa667d31ce74a2e10582a93b710c2aef7 Mon Sep 17 00:00:00 2001 From: Andres Hurtado Lopez Date: Sun, 26 Feb 2023 21:23:51 -0500 Subject: [PATCH 0624/1575] RP-PICO UART adding set_baudrate: missing to run rust-fmt --- embassy-rp/src/uart/mod.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 6b3e2406d..42b3671a0 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -296,7 +296,7 @@ impl<'d, T: Instance> Uart<'d, T, Async> { Some(rx_dma.map_into()), config, ) - } + } } impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { @@ -350,7 +350,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } - Self::set_baudrate_inner(config.baudrate); + Self::set_baudrate_inner(config.baudrate); let (pen, eps) = match config.parity { Parity::ParityNone => (false, false), @@ -385,22 +385,20 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { } } - /// sets baudrate on runtime pub fn set_baudrate(&mut self, baudrate: u32) { - Self::set_baudrate_inner(baudrate); + Self::set_baudrate_inner(baudrate); } - fn set_baudrate_inner(baudrate: u32) { - let r = T::regs(); - + let r = T::regs(); + let clk_base = crate::clocks::clk_peri_freq(); - + let baud_rate_div = (8 * clk_base) / baudrate; let mut baud_ibrd = baud_rate_div >> 7; let mut baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; - + if baud_ibrd == 0 { baud_ibrd = 1; baud_fbrd = 0; @@ -408,15 +406,13 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { baud_ibrd = 65535; baud_fbrd = 0; } - + unsafe { - - // Load PL011's baud divisor registers + // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); - } + } } - } impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { From 5cb0c8cc01583137ccb8b6a64a452b52316b626f Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 Feb 2023 09:22:38 +0200 Subject: [PATCH 0625/1575] fix: rp - disable Pull-down/up resistors for ADC read Signed-off-by: Lachezar Lechev --- embassy-rp/src/adc.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 025c6f917..145ba9c59 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -7,6 +7,7 @@ use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal_02::adc::{Channel, OneShot}; +use crate::gpio::Pin; use crate::interrupt::{self, InterruptExt}; use crate::peripherals::ADC; use crate::{pac, peripherals, Peripheral}; @@ -90,9 +91,17 @@ impl<'d> Adc<'d> { } } - pub async fn read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { + pub async fn read, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { let r = Self::regs(); unsafe { + // disable pull-down and pull-up resistors + // pull-down resistors are enabled by default + pin.pad_ctrl().modify(|w| { + w.set_ie(true); + let (pu, pd) = (false, false); + w.set_pue(pu); + w.set_pde(pd); + }); r.cs().modify(|w| { w.set_ainsel(PIN::channel()); w.set_start_once(true) From 90f2939bf6ad02c68c3ada800d4ed7ddb66619a0 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 14:22:54 +0000 Subject: [PATCH 0626/1575] Added PacketQueue::init() --- embassy-stm32/src/eth/mod.rs | 18 ++++++++++++++++++ embassy-stm32/src/lib.rs | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 9f62b61ee..9b500bfd6 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -39,6 +39,24 @@ impl PacketQueue { rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], } } + + // Allow to initialize a Self without requiring it to go on the stack + #[cfg(feature = "nightly")] + pub const unsafe fn init(this: &mut core::mem::MaybeUninit) { + let this: &mut Self = unsafe { this.assume_init_mut() }; + let mut i = 0; + while i < TX { + this.tx_desc[i] = TDes::new(); + this.tx_buf[i] = Packet([0; TX_BUFFER_SIZE]); + i += 1; + } + i = 0; + while i < RX { + this.rx_desc[i] = RDes::new(); + this.rx_buf[i] = Packet([0; RX_BUFFER_SIZE]); + i += 1; + } + } } static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index eeaa04f67..7c0b2d516 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,7 +1,13 @@ #![no_std] #![cfg_attr( feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) + feature( + type_alias_impl_trait, + async_fn_in_trait, + impl_trait_projections, + const_mut_refs, + const_maybe_uninit_assume_init + ) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] From c1e93c0904706a7497046ba25d82fcfda6576734 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 14:34:26 +0000 Subject: [PATCH 0627/1575] PacketQueue::new() uses ::init() when in nightly --- embassy-stm32/src/eth/mod.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 9b500bfd6..04c74e605 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -30,13 +30,24 @@ pub struct PacketQueue { impl PacketQueue { pub const fn new() -> Self { - const NEW_TDES: TDes = TDes::new(); - const NEW_RDES: RDes = RDes::new(); - Self { - tx_desc: [NEW_TDES; TX], - rx_desc: [NEW_RDES; RX], - tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], - rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], + #[cfg(feature = "nightly")] + { + let mut this = core::mem::MaybeUninit::uninit(); + unsafe { + Self::init(&mut this); + this.assume_init() + } + } + #[cfg(not(feature = "nightly"))] + { + const NEW_TDES: TDes = TDes::new(); + const NEW_RDES: RDes = RDes::new(); + Self { + tx_desc: [NEW_TDES; TX], + rx_desc: [NEW_RDES; RX], + tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], + rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], + } } } From 4e212c7a0b171cae116d9a95353a0611f424c68d Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 28 Feb 2023 17:04:56 +0100 Subject: [PATCH 0628/1575] embassy-time: add async tick() method to Ticker --- embassy-time/src/timer.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index f74b5cb24..416830a7c 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -3,7 +3,7 @@ use core::pin::Pin; use core::task::{Context, Poll, Waker}; use futures_util::future::{select, Either}; -use futures_util::{pin_mut, Stream}; +use futures_util::{pin_mut, Stream, StreamExt}; use crate::{Duration, Instant}; @@ -132,6 +132,11 @@ impl Ticker { let expires_at = Instant::now() + duration; Self { expires_at, duration } } + + /// Waits for the next tick + pub async fn next(&mut self) { + ::next(self).await; + } } impl Unpin for Ticker {} From 485bb76e467aaa2522907bcb1bad538b4374d672 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 17:39:02 +0000 Subject: [PATCH 0629/1575] Implemented suggestions from @Dirbaio --- embassy-stm32/src/eth/mod.rs | 43 +++++++++--------------------------- embassy-stm32/src/lib.rs | 8 +------ 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 04c74e605..89d2c5a3d 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -5,6 +5,7 @@ mod _version; pub mod generic_smi; +use core::mem::MaybeUninit; use core::task::Context; use embassy_net_driver::{Capabilities, LinkState}; @@ -30,43 +31,19 @@ pub struct PacketQueue { impl PacketQueue { pub const fn new() -> Self { - #[cfg(feature = "nightly")] - { - let mut this = core::mem::MaybeUninit::uninit(); - unsafe { - Self::init(&mut this); - this.assume_init() - } - } - #[cfg(not(feature = "nightly"))] - { - const NEW_TDES: TDes = TDes::new(); - const NEW_RDES: RDes = RDes::new(); - Self { - tx_desc: [NEW_TDES; TX], - rx_desc: [NEW_RDES; RX], - tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], - rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], - } + const NEW_TDES: TDes = TDes::new(); + const NEW_RDES: RDes = RDes::new(); + Self { + tx_desc: [NEW_TDES; TX], + rx_desc: [NEW_RDES; RX], + tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], + rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], } } // Allow to initialize a Self without requiring it to go on the stack - #[cfg(feature = "nightly")] - pub const unsafe fn init(this: &mut core::mem::MaybeUninit) { - let this: &mut Self = unsafe { this.assume_init_mut() }; - let mut i = 0; - while i < TX { - this.tx_desc[i] = TDes::new(); - this.tx_buf[i] = Packet([0; TX_BUFFER_SIZE]); - i += 1; - } - i = 0; - while i < RX { - this.rx_desc[i] = RDes::new(); - this.rx_buf[i] = Packet([0; RX_BUFFER_SIZE]); - i += 1; - } + pub unsafe fn init(this: &mut MaybeUninit) { + this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); } } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 7c0b2d516..eeaa04f67 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,13 +1,7 @@ #![no_std] #![cfg_attr( feature = "nightly", - feature( - type_alias_impl_trait, - async_fn_in_trait, - impl_trait_projections, - const_mut_refs, - const_maybe_uninit_assume_init - ) + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] From 3c601bf8d2624d6fa8fd8e68dfee5cb9348ab4f9 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Tue, 28 Feb 2023 18:04:43 +0000 Subject: [PATCH 0630/1575] PacketQueue::init() does not need to be unsafe --- embassy-stm32/src/eth/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 89d2c5a3d..b632861bf 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -42,8 +42,10 @@ impl PacketQueue { } // Allow to initialize a Self without requiring it to go on the stack - pub unsafe fn init(this: &mut MaybeUninit) { - this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); + pub fn init(this: &mut MaybeUninit) { + unsafe { + this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); + } } } From 4dfa32b1e0572c03a5f97f0ed4a4a0acd6f12cca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Feb 2023 01:08:16 +0100 Subject: [PATCH 0631/1575] cortex-m/executor: don't use the owned interrupts system. Preparation for #1224. --- embassy-cortex-m/src/executor.rs | 99 ++++++++++++------- examples/nrf52840/src/bin/multiprio.rs | 32 +++--- examples/stm32f0/Cargo.toml | 2 +- .../src/bin/{priority.rs => multiprio.rs} | 32 +++--- examples/stm32f3/src/bin/multiprio.rs | 36 ++++--- examples/stm32f4/src/bin/multiprio.rs | 36 ++++--- 6 files changed, 152 insertions(+), 85 deletions(-) rename examples/stm32f0/src/bin/{priority.rs => multiprio.rs} (85%) diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs index 0d1745d8a..558539e73 100644 --- a/embassy-cortex-m/src/executor.rs +++ b/embassy-cortex-m/src/executor.rs @@ -1,18 +1,22 @@ //! Executor specific to cortex-m devices. -use core::marker::PhantomData; +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; + +use atomic_polyfill::{AtomicBool, Ordering}; +use cortex_m::interrupt::InterruptNumber; +use cortex_m::peripheral::NVIC; pub use embassy_executor::*; -use crate::interrupt::{Interrupt, InterruptExt}; +#[derive(Clone, Copy)] +struct N(u16); +unsafe impl cortex_m::interrupt::InterruptNumber for N { + fn number(self) -> u16 { + self.0 + } +} fn pend_by_number(n: u16) { - #[derive(Clone, Copy)] - struct N(u16); - unsafe impl cortex_m::interrupt::InterruptNumber for N { - fn number(self) -> u16 { - self.0 - } - } cortex_m::peripheral::NVIC::pend(N(n)) } @@ -37,26 +41,37 @@ fn pend_by_number(n: u16) { /// /// It is somewhat more complex to use, it's recommended to use the thread-mode /// [`Executor`] instead, if it works for your use case. -pub struct InterruptExecutor { - irq: I, - inner: raw::Executor, - not_send: PhantomData<*mut ()>, +pub struct InterruptExecutor { + started: AtomicBool, + executor: UnsafeCell>, } -impl InterruptExecutor { - /// Create a new Executor. - pub fn new(irq: I) -> Self { - let ctx = irq.number() as *mut (); +unsafe impl Send for InterruptExecutor {} +unsafe impl Sync for InterruptExecutor {} + +impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { Self { - irq, - inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), - not_send: PhantomData, + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), } } + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + /// Start the executor. /// - /// This initializes the executor, configures and enables the interrupt, and returns. + /// This initializes the executor, enables the interrupt, and returns. /// The executor keeps running in the background through the interrupt. /// /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] @@ -67,23 +82,35 @@ impl InterruptExecutor { /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from /// a task running in it. /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: + /// # Interrupt requirements /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - pub fn start(&'static mut self) -> SendSpawner { - self.irq.disable(); + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } - self.irq.set_handler(|ctx| unsafe { - let executor = &*(ctx as *const raw::Executor); - executor.poll(); - }); - self.irq.set_handler_context(&self.inner as *const _ as _); - self.irq.enable(); + unsafe { + (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new( + |ctx| pend_by_number(ctx as u16), + irq.number() as *mut (), + )) + } - self.inner.spawner().make_send() + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() } } diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 25806ae48..851e189ea 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::{info, unwrap}; use embassy_nrf::executor::{Executor, InterruptExecutor}; use embassy_nrf::interrupt; -use embassy_nrf::interrupt::InterruptExt; +use embassy_nrf::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn SWI1_EGU1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn SWI0_EGU0() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_nrf::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(SWI1_EGU1); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::SWI1_EGU1, 6 << 5) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::SWI1_EGU1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(SWI0_EGU0); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::SWI0_EGU0, 7 << 5) }; + let spawner = EXECUTOR_MED.start(Interrupt::SWI0_EGU0); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index d4afbb8f8..89d99b6d3 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,5 +15,5 @@ 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", "stm32f091rc", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = "1.0" diff --git a/examples/stm32f0/src/bin/priority.rs b/examples/stm32f0/src/bin/multiprio.rs similarity index 85% rename from examples/stm32f0/src/bin/priority.rs rename to examples/stm32f0/src/bin/multiprio.rs index 7fed6a773..e0dc8c989 100644 --- a/examples/stm32f0/src/bin/priority.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,27 +111,34 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn USART1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn USART2() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { // Initialize and create handle for devicer peripherals let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: USART1, priority level 6 - let irq = interrupt::take!(USART1); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::USART1, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::USART1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: USART2, priority level 7 - let irq = interrupt::take!(USART2); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + unsafe { nvic.set_priority(Interrupt::USART2, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::USART2); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 9e8228a4b..77df51ac7 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn UART4() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn UART5() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; - // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(UART4); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // High-priority executor: UART4, priority level 6 + unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); unwrap!(spawner.spawn(run_high())); - // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(UART5); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // Medium-priority executor: UART5, priority level 7 + unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 9e8228a4b..77df51ac7 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -57,11 +57,14 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::mem; + +use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_stm32::executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::InterruptExt; +use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -108,28 +111,35 @@ async fn run_low() { } } -static EXECUTOR_HIGH: StaticCell> = StaticCell::new(); -static EXECUTOR_MED: StaticCell> = StaticCell::new(); +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); +#[interrupt] +unsafe fn UART4() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn UART5() { + EXECUTOR_MED.on_interrupt() +} + #[entry] fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; - // High-priority executor: SWI1_EGU1, priority level 6 - let irq = interrupt::take!(UART4); - irq.set_priority(interrupt::Priority::P6); - let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // High-priority executor: UART4, priority level 6 + unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; + let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); unwrap!(spawner.spawn(run_high())); - // Medium-priority executor: SWI0_EGU0, priority level 7 - let irq = interrupt::take!(UART5); - irq.set_priority(interrupt::Priority::P7); - let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); - let spawner = executor.start(); + // Medium-priority executor: UART5, priority level 7 + unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; + let spawner = EXECUTOR_MED.start(Interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV From 6dbb631f1ecb75361ee70da91f50779c29f23482 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Mar 2023 01:32:42 +0100 Subject: [PATCH 0632/1575] Example fixes. --- examples/nrf52840/src/bin/usb_ethernet.rs | 23 ----------------------- examples/nrf5340/Cargo.toml | 23 +++++++---------------- examples/stm32f4/Cargo.toml | 6 +----- examples/stm32f4/src/bin/usb_ethernet.rs | 11 +++-------- examples/stm32l4/Cargo.toml | 2 -- examples/stm32l5/Cargo.toml | 2 -- 6 files changed, 11 insertions(+), 56 deletions(-) diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 430468adf..979780896 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -46,31 +46,8 @@ async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } -#[inline(never)] -pub fn test_function() -> (usize, u32, [u32; 2]) { - let mut array = [3; 2]; - - let mut index = 0; - let mut result = 0; - - for x in [1, 2] { - if x == 1 { - array[1] = 99; - } else { - index = if x == 2 { 1 } else { 0 }; - - // grabs value from array[0], not array[1] - result = array[index]; - } - } - - (index, result, array) -} - #[embassy_executor::main] async fn main(spawner: Spawner) { - info!("{:?}", test_function()); - let p = embassy_nrf::init(Default::default()); let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index eed493012..e88ddf2f7 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -4,24 +4,13 @@ name = "embassy-nrf5340-examples" version = "0.1.0" license = "MIT OR Apache-2.0" -[features] -default = ["nightly"] -nightly = [ - "embassy-executor/nightly", - "embassy-nrf/nightly", - "embassy-net/nightly", - "embassy-nrf/unstable-traits", - "embassy-usb", - "embedded-io/async", - "embassy-net", -] - [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ "defmt", ] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ + "nightly", "defmt", "integrated-timers", ] } @@ -30,6 +19,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = [ "defmt-timestamp-uptime", ] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ + "nightly", + "unstable-traits", "defmt", "nrf5340-app-s", "time-driver-rtc1", @@ -37,16 +28,16 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ "unstable-pac", ] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ + "nightly", "defmt", "tcp", "dhcpv4", "medium-ethernet", -], optional = true } +] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ "defmt", -], optional = true } -embedded-io = "0.4.0" - +] } +embedded-io = { version = "0.4.0", features = [ "async" ]} defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index e2b17bfcb..7a7bab5bb 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -10,7 +10,7 @@ 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"], optional = true } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } defmt = "0.3" defmt-rtt = "0.4" @@ -27,9 +27,5 @@ embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" -[[bin]] -name = "usb_ethernet" -required-features = ["embassy-net"] - [profile.release] debug = 2 diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 4a16aac07..db9e18393 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -100,8 +100,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::ConfigStrategy::Dhcp; - //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), @@ -114,12 +114,7 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new( - device, - config, - singleton!(StackResources::<1, 2, 8>::new()), - seed - )); + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 5627760ef..644c90b1a 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -4,8 +4,6 @@ name = "embassy-stm32l4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" -[features] - [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index c0accb0d6..f880328dc 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -4,8 +4,6 @@ name = "embassy-stm32l5-examples" version = "0.1.0" license = "MIT OR Apache-2.0" -[features] - [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } From aabc275186aff8a640ee6591c3572c00f508b3b7 Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Wed, 1 Mar 2023 10:10:40 +1100 Subject: [PATCH 0633/1575] stm32/spi: fix occasional data corruption Need to clear the rx fifo before enabling rx dma. --- embassy-stm32/src/spi/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index e5ba746e4..1f1708873 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -456,13 +456,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(false); }); - set_rxdmaen(T::REGS, true); } // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4)))] flush_rx_fifo(T::REGS); + set_rxdmaen(T::REGS, true); + let clock_byte_count = data.len(); let rx_request = self.rxdma.request(); @@ -510,13 +511,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(false); }); - set_rxdmaen(T::REGS, true); } // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4)))] flush_rx_fifo(T::REGS); + set_rxdmaen(T::REGS, true); + let rx_request = self.rxdma.request(); let rx_src = T::REGS.rx_ptr(); unsafe { self.rxdma.start_read(rx_request, rx_src, read, Default::default()) }; From c0e40b887bf4975512cc40e13ea17cb57f34f099 Mon Sep 17 00:00:00 2001 From: Davide Della Giustina Date: Wed, 1 Mar 2023 20:57:13 +0000 Subject: [PATCH 0634/1575] Apply fix --- embassy-stm32/src/eth/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index b632861bf..e1d7a09b4 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -44,7 +44,7 @@ impl PacketQueue { // Allow to initialize a Self without requiring it to go on the stack pub fn init(this: &mut MaybeUninit) { unsafe { - this.as_mut_ptr().write_bytes(0u8, core::mem::size_of::()); + this.as_mut_ptr().write_bytes(0u8, 1); } } } From f95aafc90e365bbdd243c0ed5069ff23abfa8be4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 2 Mar 2023 00:57:54 +0100 Subject: [PATCH 0635/1575] common: allow atomic ringbuf to fill up to N instead of just N-1. This allows the ringbuf to be filled up to `N` instead of just `N-1`, using some fun tricks on the indices. The advantage is better performance: Before, the first write would fill N-1 bytes, The second would write just the 1 byte left before wrapping, then N-2. Then 2, then N-3, and so on. This would result in more smaller chunks, so worse perf. This problem is gone now. --- embassy-hal-common/src/atomic_ring_buffer.rs | 98 ++++++++++++++------ 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index 4c944d763..ccbc37362 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -14,10 +14,18 @@ use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; /// 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 buf: AtomicPtr, + pub len: AtomicUsize, + + // start and end wrap at len*2, not at len. + // This allows distinguishing "full" and "empty". + // full is when start+len == end (modulo len*2) + // empty is when start == end + // + // This avoids having to consider the ringbuffer "full" at len-1 instead of len. + // The usual solution is adding a "full" flag, but that can't be made atomic + pub start: AtomicUsize, + pub end: AtomicUsize, } pub struct Reader<'a>(&'a RingBuffer); @@ -90,7 +98,7 @@ impl RingBuffer { let start = self.start.load(Ordering::Relaxed); let end = self.end.load(Ordering::Relaxed); - len == 0 || self.wrap(end + 1) == start + self.wrap(start + len) == end } pub fn is_empty(&self) -> bool { @@ -100,15 +108,13 @@ impl RingBuffer { start == end } - fn wrap(&self, n: usize) -> usize { + fn wrap(&self, mut n: usize) -> usize { let len = self.len.load(Ordering::Relaxed); - assert!(n <= len); - if n == len { - 0 - } else { - n + if n >= len * 2 { + n -= len * 2 } + n } } @@ -161,16 +167,25 @@ impl<'a> Writer<'a> { 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 mut 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 mut end = self.0.end.load(Ordering::Relaxed); - let n = if start <= end { - len - end - (start == 0 && len != 0) as usize - } else { - start - end - 1 - }; + let empty = start == end; + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + if start == end && !empty { + // full + return (buf, 0); + } + let n = if start > end { start - end } else { len - end }; trace!(" ringbuf: push_buf {:?}..{:?}", end, end + n); (unsafe { buf.add(end) }, n) @@ -239,12 +254,23 @@ impl<'a> Reader<'a> { // 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 mut 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 mut start = self.0.start.load(Ordering::Relaxed); - let n = if end < start { len - start } else { end - start }; + if start == end { + return (buf, 0); + } + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + let n = if end > start { end - start } else { len - start }; trace!(" ringbuf: pop_buf {:?}..{:?}", start, start + n); (unsafe { buf.add(start) }, n) @@ -280,12 +306,12 @@ mod tests { 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()); + assert_eq!(4, buf.len()); buf[0] = 1; buf[1] = 2; buf[2] = 3; - 3 + buf[3] = 4; + 4 }); assert_eq!(rb.is_empty(), false); @@ -301,7 +327,7 @@ mod tests { assert_eq!(rb.is_full(), true); rb.reader().pop(|buf| { - assert_eq!(3, buf.len()); + assert_eq!(4, buf.len()); assert_eq!(1, buf[0]); 1 }); @@ -310,7 +336,7 @@ mod tests { assert_eq!(rb.is_full(), false); rb.reader().pop(|buf| { - assert_eq!(2, buf.len()); + assert_eq!(3, buf.len()); 0 }); @@ -318,11 +344,16 @@ mod tests { assert_eq!(rb.is_full(), false); rb.reader().pop(|buf| { - assert_eq!(2, buf.len()); + assert_eq!(3, buf.len()); assert_eq!(2, buf[0]); assert_eq!(3, buf[1]); 2 }); + rb.reader().pop(|buf| { + assert_eq!(1, buf.len()); + assert_eq!(4, buf[0]); + 1 + }); assert_eq!(rb.is_empty(), true); assert_eq!(rb.is_full(), false); @@ -333,18 +364,27 @@ mod tests { }); rb.writer().push(|buf| { - assert_eq!(1, buf.len()); + assert_eq!(4, buf.len()); buf[0] = 10; 1 }); rb.writer().push(|buf| { - assert_eq!(2, buf.len()); + assert_eq!(3, buf.len()); buf[0] = 11; buf[1] = 12; 2 }); + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_full(), false); + + rb.writer().push(|buf| { + assert_eq!(1, buf.len()); + buf[0] = 13; + 1 + }); + assert_eq!(rb.is_empty(), false); assert_eq!(rb.is_full(), true); } From 78974dfeb4db4a36bf9478397fd65370eb1dcf0c Mon Sep 17 00:00:00 2001 From: Patrick Oppenlander Date: Tue, 7 Feb 2023 14:49:44 +1100 Subject: [PATCH 0636/1575] hal-common/atomic_ring_buffer: add push_bufs() push_slices() This allows a writer to access all of the free space in the buffer, even when it's wrapping around. --- embassy-hal-common/src/atomic_ring_buffer.rs | 147 +++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index ccbc37362..afd3ce1de 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -153,6 +153,14 @@ impl<'a> Writer<'a> { unsafe { slice::from_raw_parts_mut(data, len) } } + /// Get up to two buffers where data can be pushed to. + /// + /// Equivalent to [`Self::push_bufs`] but returns slices. + pub fn push_slices(&mut self) -> [&mut [u8]; 2] { + let [(d0, l0), (d1, l1)] = self.push_bufs(); + unsafe { [slice::from_raw_parts_mut(d0, l0), slice::from_raw_parts_mut(d1, l1)] } + } + /// Get a buffer where data can be pushed to. /// /// Write data to the start of the buffer, then call `push_done` with @@ -191,6 +199,45 @@ impl<'a> Writer<'a> { (unsafe { buf.add(end) }, n) } + /// Get up to two buffers where data can be pushed to. + /// + /// Write data starting at the beginning of the first buffer, then call + /// `push_done` with however many bytes you've pushed. + /// + /// The buffers are suitable to DMA to. + /// + /// If the ringbuf is full, both buffers will be zero length. + /// If there is only area available, the second buffer will be zero length. + /// + /// 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_bufs(&mut self) -> [(*mut u8, usize); 2] { + // Ordering: as per push_buf() + let mut start = self.0.start.load(Ordering::Acquire); + let buf = self.0.buf.load(Ordering::Relaxed); + let len = self.0.len.load(Ordering::Relaxed); + let mut end = self.0.end.load(Ordering::Relaxed); + + let empty = start == end; + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + if start == end && !empty { + // full + return [(buf, 0), (buf, 0)]; + } + let n0 = if start > end { start - end } else { len - end }; + let n1 = if start <= end { start } else { 0 }; + + trace!(" ringbuf: push_bufs [{:?}..{:?}, {:?}..{:?}]", end, end + n0, 0, n1); + [(unsafe { buf.add(end) }, n0), (buf, n1)] + } + pub fn push_done(&mut self, n: usize) { trace!(" ringbuf: push {:?}", n); let end = self.0.end.load(Ordering::Relaxed); @@ -408,4 +455,104 @@ mod tests { }); } } + + #[test] + fn push_slices() { + init(); + + let mut b = [0; 4]; + let rb = RingBuffer::new(); + unsafe { + rb.init(b.as_mut_ptr(), 4); + + /* push 3 -> [1 2 3 x] */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(4, ps[0].len()); + assert_eq!(0, ps[1].len()); + ps[0][0] = 1; + ps[0][1] = 2; + ps[0][2] = 3; + w.push_done(3); + drop(w); + + /* pop 2 -> [x x 3 x] */ + rb.reader().pop(|buf| { + assert_eq!(3, buf.len()); + assert_eq!(1, buf[0]); + assert_eq!(2, buf[1]); + assert_eq!(3, buf[2]); + 2 + }); + + /* push 3 -> [5 6 3 4] */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(1, ps[0].len()); + assert_eq!(2, ps[1].len()); + ps[0][0] = 4; + ps[1][0] = 5; + ps[1][1] = 6; + w.push_done(3); + drop(w); + + /* buf is now full */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(0, ps[0].len()); + assert_eq!(0, ps[1].len()); + + /* pop 2 -> [5 6 x x] */ + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(3, buf[0]); + assert_eq!(4, buf[1]); + 2 + }); + + /* should now have one push slice again */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(2, ps[0].len()); + assert_eq!(0, ps[1].len()); + drop(w); + + /* pop 2 -> [x x x x] */ + rb.reader().pop(|buf| { + assert_eq!(2, buf.len()); + assert_eq!(5, buf[0]); + assert_eq!(6, buf[1]); + 2 + }); + + /* should now have two push slices */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(2, ps[0].len()); + assert_eq!(2, ps[1].len()); + drop(w); + + /* make sure we exercise all wrap around cases properly */ + for _ in 0..10 { + /* should be empty, push 1 */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(4, ps[0].len() + ps[1].len()); + w.push_done(1); + drop(w); + + /* should have 1 element */ + let mut w = rb.writer(); + let ps = w.push_slices(); + assert_eq!(3, ps[0].len() + ps[1].len()); + drop(w); + + /* pop 1 */ + rb.reader().pop(|buf| { + assert_eq!(1, buf.len()); + 1 + }); + } + } + } } From 64003cdfd60439195d75b4d510d3027c463fdb0a Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Thu, 2 Mar 2023 11:36:41 +0000 Subject: [PATCH 0637/1575] Add embassy-esp README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 938f2f4a6..6d5e75ead 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ Rust's async/await allows - **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. - embassy-stm32, for all STM32 microcontroller families. - embassy-nrf, for the Nordic Semiconductor nRF52, nRF53, nRF91 series. + - esp-rs, for the Espressif Systems ESP32 series of chips. + - Embassy HAL support for Espressif chips is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. + - Async WiFi, Bluetooth and ESP-NOW is being developed in the [esp-rs/esp-wifi](https://github.com/esp-rs/esp-wifi) repository. - **Time that Just Works** - No more messing with hardware timers. embassy_time provides Instant, Duration and Timer types that are globally available and never overflow. From 7bdb3abad7bbf89a55c73cfb0b475b78c5a7488d Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 2 Mar 2023 13:59:52 -0500 Subject: [PATCH 0638/1575] Swap debug! for trace! in rp gpio When using gpio pin changes for things like peripheral interrupts these debug! calls flood defmt, making it difficult to find what you're actually looking for. --- embassy-rp/src/gpio.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index fd3b05567..fb45ef7cf 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -174,7 +174,7 @@ unsafe fn IO_IRQ_BANK0() { w.set_edge_low(pin_group, false); } InterruptTrigger::LevelHigh => { - debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); + trace!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); w.set_level_high(pin_group, false); } InterruptTrigger::LevelLow => { @@ -214,7 +214,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { - debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); + trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); } InterruptTrigger::LevelLow => { @@ -261,45 +261,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // the pin and if it has been disabled that means it was done by the // interrupt service routine, so we then know that the event/trigger // happened and Poll::Ready will be returned. - debug!("{:?} for pin {}", self.level, self.pin.pin()); + trace!("{:?} for pin {}", self.level, self.pin.pin()); match self.level { InterruptTrigger::AnyEdge => { if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelHigh => { if !inte.level_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelLow => { if !inte.level_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeHigh => { if !inte.edge_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeLow => { if !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); + trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } } - debug!("InputFuture::poll return Poll::Pending"); + trace!("InputFuture::poll return Poll::Pending"); Poll::Pending } } From 4314b823aaadc54387fbc1379f89300c70007d83 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:24:38 +0100 Subject: [PATCH 0639/1575] nrf: add PPI channel group driver. --- embassy-nrf/src/ppi/dppi.rs | 2 +- embassy-nrf/src/ppi/mod.rs | 104 +++++++++++++++++++++++++++++++++--- embassy-nrf/src/ppi/ppi.rs | 2 +- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 0908cd7be..3a1e7f170 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -6,7 +6,7 @@ use crate::{pac, Peripheral}; const DPPI_ENABLE_BIT: u32 = 0x8000_0000; const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF; -fn regs() -> &'static pac::dppic::RegisterBlock { +pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock { unsafe { &*pac::DPPIC::ptr() } } diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index b76eccf0b..7c18da6ee 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -17,16 +17,16 @@ use core::ptr::NonNull; -use embassy_hal_common::{impl_peripheral, PeripheralRef}; +use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use crate::{peripherals, Peripheral}; -#[cfg(feature = "_dppi")] -mod dppi; -#[cfg(feature = "_ppi")] -mod ppi; +#[cfg_attr(feature = "_dppi", path = "dppi.rs")] +#[cfg_attr(feature = "_ppi", path = "ppi.rs")] +mod _version; +pub(crate) use _version::*; -/// An instance of the Programmable peripheral interconnect on nRF devices. +/// PPI channel driver. pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> { ch: PeripheralRef<'d, C>, #[cfg(feature = "_dppi")] @@ -35,6 +35,88 @@ pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize tasks: [Task; TASK_COUNT], } +/// PPI channel group driver. +pub struct PpiGroup<'d, G: Group> { + g: PeripheralRef<'d, G>, +} + +impl<'d, G: Group> PpiGroup<'d, G> { + /// Create a new PPI group driver. + /// + /// The group is initialized as containing no channels. + pub fn new(g: impl Peripheral

+ 'd) -> Self { + into_ref!(g); + + let r = regs(); + let n = g.number(); + r.chg[n].write(|w| unsafe { w.bits(0) }); + + Self { g } + } + + /// Add a PPI channel to this group. + /// + /// If the channel is already in the group, this is a no-op. + pub fn add_channel( + &mut self, + ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>, + ) { + let r = regs(); + let ng = self.g.number(); + let nc = ch.ch.number(); + r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) }); + } + + /// Remove a PPI channel from this group. + /// + /// If the channel is already not in the group, this is a no-op. + pub fn remove_channel( + &mut self, + ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>, + ) { + let r = regs(); + let ng = self.g.number(); + let nc = ch.ch.number(); + r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) }); + } + + /// Enable all the channels in this group. + pub fn enable_all(&mut self) { + let n = self.g.number(); + regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) }); + } + + /// Disable all the channels in this group. + pub fn disable_all(&mut self) { + let n = self.g.number(); + regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) }); + } + + /// Get a reference to the "enable all" task. + /// + /// When triggered, it will enable all the channels in this group. + pub fn task_enable_all(&self) -> Task { + let n = self.g.number(); + Task::from_reg(®s().tasks_chg[n].en) + } + + /// Get a reference to the "disable all" task. + /// + /// When triggered, it will disable all the channels in this group. + pub fn task_disable_all(&self) -> Task { + let n = self.g.number(); + Task::from_reg(®s().tasks_chg[n].dis) + } +} + +impl<'d, G: Group> Drop for PpiGroup<'d, G> { + fn drop(&mut self) { + let r = regs(); + let n = self.g.number(); + r.chg[n].write(|w| unsafe { w.bits(0) }); + } +} + #[cfg(feature = "_dppi")] const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::(); @@ -112,7 +194,7 @@ pub(crate) mod sealed { } /// Interface for PPI channels. -pub trait Channel: sealed::Channel + Peripheral

+ Sized { +pub trait Channel: sealed::Channel + Peripheral

+ Sized + 'static { /// Returns the number of the channel fn number(&self) -> usize; } @@ -130,7 +212,7 @@ pub trait StaticChannel: Channel + Into { } /// Interface for a group of PPI channels. -pub trait Group: sealed::Group + Sized { +pub trait Group: sealed::Group + Peripheral

+ Into + Sized + 'static { /// Returns the number of the group. fn number(&self) -> usize; /// Convert into a type erased group. @@ -248,6 +330,12 @@ macro_rules! impl_group { $number } } + + impl From for crate::ppi::AnyGroup { + fn from(val: peripherals::$type) -> Self { + crate::ppi::Group::degrade(val) + } + } }; } diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index a96ab50b7..f1eeaee1e 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -14,7 +14,7 @@ impl Event { } } -fn regs() -> &'static pac::ppi::RegisterBlock { +pub(crate) fn regs() -> &'static pac::ppi::RegisterBlock { unsafe { &*pac::PPI::ptr() } } From 51478caad843e4b0be1d1baf83d1f0e8c0d96a30 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:25:57 +0100 Subject: [PATCH 0640/1575] nrf/timer: add support for counter mode. --- embassy-nrf/src/timer.rs | 43 +++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index d1ae57237..3b0d2f1ca 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -132,7 +132,21 @@ impl<'d, T: Instance> Timer<'d, T, Awaitable> { irq.unpend(); irq.enable(); - Self::new_inner(timer) + Self::new_inner(timer, false) + } + + /// Create a new async-capable timer driver in counter mode. + pub fn new_awaitable_counter( + timer: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + ) -> Self { + into_ref!(irq); + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self::new_inner(timer, true) } } @@ -142,7 +156,15 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. pub fn new(timer: impl Peripheral

+ 'd) -> Self { - Self::new_inner(timer) + Self::new_inner(timer, false) + } + + /// Create a `Timer` driver in counter mode without an interrupt, meaning `Cc::wait` won't work. + /// + /// This can be useful for triggering tasks via PPI + /// `Uarte` uses this internally. + pub fn new_counter(timer: impl Peripheral

+ 'd) -> Self { + Self::new_inner(timer, true) } } @@ -150,7 +172,7 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. /// /// This is used by the public constructors. - fn new_inner(timer: impl Peripheral

+ 'd) -> Self { + fn new_inner(timer: impl Peripheral

+ 'd, is_counter: bool) -> Self { into_ref!(timer); let regs = T::regs(); @@ -164,8 +186,11 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. this.stop(); - // Set the instance to timer mode. - regs.mode.write(|w| w.mode().timer()); + if is_counter { + regs.mode.write(|w| w.mode().counter()); + } else { + regs.mode.write(|w| w.mode().timer()); + } // Make the counter's max value as high as possible. // TODO: is there a reason someone would want to set this lower? @@ -225,6 +250,14 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { Task::from_reg(&T::regs().tasks_clear) } + /// Returns the COUNT task, for use with PPI. + /// + /// When triggered, this task increments the timer's counter by 1. + /// Only works in counter mode. + pub fn task_count(&self) -> Task { + Task::from_reg(&T::regs().tasks_count) + } + /// Change the timer's frequency. /// /// This will stop the timer if it isn't already stopped, From 7b9075130e72e0f6fab6b07c0171e42e1b9802a1 Mon Sep 17 00:00:00 2001 From: Lasse Dalegaard Date: Sat, 4 Mar 2023 10:36:10 +0100 Subject: [PATCH 0641/1575] embassy_usb: Add split() for cdc_acm --- embassy-usb/src/class/cdc_acm.rs | 100 +++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index ff82ad40d..a341e10da 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -276,6 +276,106 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { pub async fn wait_connection(&mut self) { self.read_ep.wait_enabled().await } + + /// Split the class into a sender and receiver. + /// + /// This allows concurrently sending and receiving packets from separate tasks. + pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) { + ( + Sender { + write_ep: self.write_ep, + control: self.control, + }, + Receiver { + read_ep: self.read_ep, + control: self.control, + }, + ) + } +} + +/// CDC ACM class packet sender. +/// +/// You can obtain a `Sender` with [`CdcAcmClass::split`] +pub struct Sender<'d, D: Driver<'d>> { + write_ep: D::EndpointIn, + control: &'d ControlShared, +} + +impl<'d, D: Driver<'d>> Sender<'d, D> { + /// Gets the maximum packet size in bytes. + pub fn max_packet_size(&self) -> u16 { + // The size is the same for both endpoints. + self.write_ep.info().max_packet_size + } + + /// Gets the current line coding. The line coding contains information that's mainly relevant + /// for USB to UART serial port emulators, and can be ignored if not relevant. + pub fn line_coding(&self) -> LineCoding { + self.control.line_coding.lock(|x| x.get()) + } + + /// Gets the DTR (data terminal ready) state + pub fn dtr(&self) -> bool { + self.control.dtr.load(Ordering::Relaxed) + } + + /// Gets the RTS (request to send) state + pub fn rts(&self) -> bool { + self.control.rts.load(Ordering::Relaxed) + } + + /// Writes a single packet into the IN endpoint. + pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { + self.write_ep.write(data).await + } + + /// Waits for the USB host to enable this interface + pub async fn wait_connection(&mut self) { + self.write_ep.wait_enabled().await + } +} + +/// CDC ACM class packet receiver. +/// +/// You can obtain a `Receiver` with [`CdcAcmClass::split`] +pub struct Receiver<'d, D: Driver<'d>> { + read_ep: D::EndpointOut, + control: &'d ControlShared, +} + +impl<'d, D: Driver<'d>> Receiver<'d, D> { + /// Gets the maximum packet size in bytes. + pub fn max_packet_size(&self) -> u16 { + // The size is the same for both endpoints. + self.read_ep.info().max_packet_size + } + + /// Gets the current line coding. The line coding contains information that's mainly relevant + /// for USB to UART serial port emulators, and can be ignored if not relevant. + pub fn line_coding(&self) -> LineCoding { + self.control.line_coding.lock(|x| x.get()) + } + + /// Gets the DTR (data terminal ready) state + pub fn dtr(&self) -> bool { + self.control.dtr.load(Ordering::Relaxed) + } + + /// Gets the RTS (request to send) state + pub fn rts(&self) -> bool { + self.control.rts.load(Ordering::Relaxed) + } + + /// Reads a single packet from the OUT endpoint. + pub async fn read_packet(&mut self, data: &mut [u8]) -> Result { + self.read_ep.read(data).await + } + + /// Waits for the USB host to enable this interface + pub async fn wait_connection(&mut self) { + self.read_ep.wait_enabled().await + } } /// Number of stop bits for LineCoding From ccc224c81ff1a56296576f4a249fe91a37c03fd8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:27:29 +0100 Subject: [PATCH 0642/1575] nrf/buffered_uarte: remove PeripheralMutex, make it work without rts/cts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > dirbaio: so I was checking how zephyr does UARTE RX on nRF > dirbaio: because currently we have the ugly "restart DMA on line idle to flush it" hack > dirbaio: because according to the docs "For each byte received over the RXD line, an RXDRDY event will be generated. This event is likely to occur before the corresponding data has been transferred to Data RAM." > dirbaio: so as I understood it, the only way to guarantee the data is actually transferred to RAM is to stop+restart DMA > dirbaio: well, guess what? > dirbaio: they just count RXDRDY's, and process that amount of data without restarting DMA > dirbaio: with a timer configured as counter https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/serial/uart_nrfx_uarte.c#L650-L692 > dirbaio: 🤔🤷⁉️ > dirbaio: someone saying you can do the "hook up rxdrdy to a counter" trick, someone else saying it's wrong 🤪 https://devzone.nordicsemi.com/f/nordic-q-a/28420/uarte-in-circular-mode So we're going to do just that! - BufferedUarte is lock-free now. No PeripheralMutex. - The "restart DMA on line idle to flush it" hack is GONE. This means - It'll work correctly without RTS/CTS now. - It'll have better throughput when using RTS/CTS. --- embassy-nrf/src/buffered_uarte.rs | 668 ++++++++++++--------- embassy-nrf/src/uarte.rs | 5 + examples/nrf52840/src/bin/buffered_uart.rs | 12 +- 3 files changed, 379 insertions(+), 306 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 112f084c1..79f9a1f78 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -1,10 +1,5 @@ //! Async buffered UART driver. //! -//! WARNING!!! The functionality provided here is intended to be used only -//! in situations where hardware flow control are available i.e. CTS and RTS. -//! This is a problem that should be addressed at a later stage and can be -//! fully explained at . -//! //! Note that discarding a future from a read or write operation may lead to losing //! data. For example, when using `futures_util::future::select` and completion occurs //! on the "other" future, you should capture the incomplete future and continue to use @@ -13,82 +8,120 @@ //! //! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. -use core::cell::RefCell; use core::cmp::min; use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::slice; +use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; -use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; -use embassy_hal_common::ring_buffer::RingBuffer; +use embassy_cortex_m::interrupt::Interrupt; +use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::WakerRegistration; +use embassy_sync::waitqueue::AtomicWaker; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; -use crate::gpio::{self, Pin as GpioPin}; +use crate::gpio::sealed::Pin; +use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; use crate::interrupt::InterruptExt; -use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; -use crate::timer::{Frequency, Instance as TimerInstance, Timer}; +use crate::ppi::{ + self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, +}; +use crate::timer::{Instance as TimerInstance, Timer}; use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance}; use crate::{pac, Peripheral}; -#[derive(Copy, Clone, Debug, PartialEq)] -enum RxState { - Idle, - Receiving, -} +mod sealed { + use super::*; -#[derive(Copy, Clone, Debug, PartialEq)] -enum TxState { - Idle, - Transmitting(usize), -} + pub struct State { + pub tx_waker: AtomicWaker, + pub tx_buf: RingBuffer, + pub tx_count: AtomicUsize, -/// A type for storing the state of the UARTE peripheral that can be stored in a static. -pub struct State<'d, U: UarteInstance, T: TimerInstance>(StateStorage>); -impl<'d, U: UarteInstance, T: TimerInstance> State<'d, U, T> { - /// Create an instance for storing UARTE peripheral state. - pub fn new() -> Self { - Self(StateStorage::new()) + pub rx_waker: AtomicWaker, + pub rx_buf: RingBuffer, + pub rx_bufs: AtomicU8, + pub rx_ppi_ch: AtomicU8, } } -struct StateInner<'d, U: UarteInstance, T: TimerInstance> { - _peri: PeripheralRef<'d, U>, - timer: Timer<'d, T>, - _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, +pub(crate) use sealed::State; - rx: RingBuffer<'d>, - rx_state: RxState, - rx_waker: WakerRegistration, +impl State { + pub(crate) const fn new() -> Self { + Self { + tx_waker: AtomicWaker::new(), + tx_buf: RingBuffer::new(), + tx_count: AtomicUsize::new(0), - tx: RingBuffer<'d>, - tx_state: TxState, - tx_waker: WakerRegistration, + rx_waker: AtomicWaker::new(), + rx_buf: RingBuffer::new(), + rx_bufs: AtomicU8::new(0), + rx_ppi_ch: AtomicU8::new(0), + } + } } /// Buffered UARTE driver. pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { - inner: RefCell>>, + _peri: PeripheralRef<'d, U>, + timer: Timer<'d, T>, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_group: PpiGroup<'d, AnyGroup>, } impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { - /// Create a new instance of a BufferedUarte. + /// Create a new BufferedUarte without hardware flow control. /// - /// See the [module documentation](crate::buffered_uarte) for more details about the intended use. + /// # Panics /// - /// The BufferedUarte uses the provided state to store the buffers and peripheral state. The timer and ppi channels are used to 'emulate' idle line detection so that read operations - /// can return early if there is no data to receive. + /// Panics if `rx_buffer.len()` is odd. pub fn new( - state: &'d mut State<'d, U, T>, - peri: impl Peripheral

+ 'd, + uarte: impl Peripheral

+ 'd, timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + irq, + rxd.map_into(), + txd.map_into(), + None, + None, + config, + rx_buffer, + tx_buffer, + ) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new_with_rtscts( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, @@ -98,12 +131,45 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { rx_buffer: &'d mut [u8], tx_buffer: &'d mut [u8], ) -> Self { - into_ref!(peri, ppi_ch1, ppi_ch2, irq, rxd, txd, cts, rts); + into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + irq, + rxd.map_into(), + txd.map_into(), + Some(cts.map_into()), + Some(rts.map_into()), + config, + rx_buffer, + tx_buffer, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_group: PeripheralRef<'d, AnyGroup>, + irq: impl Peripheral

+ 'd, + rxd: PeripheralRef<'d, AnyPin>, + txd: PeripheralRef<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(peri, timer, irq); + + assert!(rx_buffer.len() % 2 == 0); let r = U::regs(); - let mut timer = Timer::new(timer); - rxd.conf().write(|w| w.input().connect().drive().h0h1()); r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); @@ -111,92 +177,200 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { txd.conf().write(|w| w.dir().output().drive().h0h1()); r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - cts.conf().write(|w| w.input().connect().drive().h0h1()); + if let Some(pin) = &cts { + pin.conf().write(|w| w.input().connect().drive().h0h1()); + } r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); - rts.set_high(); - rts.conf().write(|w| w.dir().output().drive().h0h1()); + if let Some(pin) = &rts { + pin.set_high(); + pin.conf().write(|w| w.dir().output().drive().h0h1()); + } r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - r.config.write(|w| w.parity().variant(config.parity)); + // Initialize state + let s = U::buffered_state(); + s.tx_count.store(0, Ordering::Relaxed); + s.rx_bufs.store(0, Ordering::Relaxed); + let len = tx_buffer.len(); + unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; // Configure r.config.write(|w| { - w.hwfc().bit(true); + w.hwfc().bit(false); w.parity().variant(config.parity); w }); r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - // Enable interrupts - r.intenset.write(|w| w.endrx().set().endtx().set()); + // clear errors + let errors = r.errorsrc.read().bits(); + r.errorsrc.write(|w| unsafe { w.bits(errors) }); - // Disable the irq, let the Registration enable it when everything is set up. - irq.disable(); - irq.pend(); + r.events_rxstarted.reset(); + r.events_txstarted.reset(); + r.events_error.reset(); + r.events_endrx.reset(); + r.events_endtx.reset(); + + // Enable interrupts + r.intenclr.write(|w| unsafe { w.bits(!0) }); + r.intenset.write(|w| { + w.endtx().set(); + w.rxstarted().set(); + w.error().set(); + w + }); // Enable UARTE instance apply_workaround_for_enable_anomaly(&r); r.enable.write(|w| w.enable().enabled()); - // BAUDRATE register values are `baudrate * 2^32 / 16000000` - // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values - // - // We want to stop RX if line is idle for 2 bytes worth of time - // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) - // This gives us the amount of 16M ticks for 20 bits. - let timeout = 0x8000_0000 / (config.baudrate as u32 / 40); + // Configure byte counter. + let mut timer = Timer::new_counter(timer); + timer.cc(1).write(rx_buffer.len() as u32 * 2); + timer.cc(1).short_compare_clear(); + timer.clear(); + timer.start(); - timer.set_frequency(Frequency::F16MHz); - timer.cc(0).write(timeout); - timer.cc(0).short_compare_clear(); - timer.cc(0).short_compare_stop(); - - let mut ppi_ch1 = Ppi::new_one_to_two( - ppi_ch1.map_into(), - Event::from_reg(&r.events_rxdrdy), - timer.task_clear(), - timer.task_start(), - ); + let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); ppi_ch1.enable(); - let mut ppi_ch2 = Ppi::new_one_to_one( - ppi_ch2.map_into(), - timer.cc(0).event_compare(), - Task::from_reg(&r.tasks_stoprx), + s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); + let mut ppi_group = PpiGroup::new(ppi_group); + let mut ppi_ch2 = Ppi::new_one_to_two( + ppi_ch2, + Event::from_reg(&r.events_endrx), + Task::from_reg(&r.tasks_startrx), + ppi_group.task_disable_all(), ); - ppi_ch2.enable(); + ppi_ch2.disable(); + ppi_group.add_channel(&ppi_ch2); + + irq.disable(); + irq.set_handler(Self::on_interrupt); + irq.pend(); + irq.enable(); Self { - inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { - _peri: peri, - timer, - _ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - - rx: RingBuffer::new(rx_buffer), - rx_state: RxState::Idle, - rx_waker: WakerRegistration::new(), - - tx: RingBuffer::new(tx_buffer), - tx_state: TxState::Idle, - tx_waker: WakerRegistration::new(), - })), + _peri: peri, + timer, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + _ppi_group: ppi_group, } } + fn pend_irq() { + unsafe { ::steal() }.pend() + } + + fn on_interrupt(_: *mut ()) { + //trace!("irq: start"); + let r = U::regs(); + let s = U::buffered_state(); + + let buf_len = s.rx_buf.len(); + let half_len = buf_len / 2; + let mut tx = unsafe { s.tx_buf.reader() }; + let mut rx = unsafe { s.rx_buf.writer() }; + + if r.events_error.read().bits() != 0 { + r.events_error.reset(); + let errs = r.errorsrc.read(); + r.errorsrc.write(|w| unsafe { w.bits(errs.bits()) }); + + if errs.overrun().bit() { + panic!("BufferedUarte overrun"); + } + } + + // Received some bytes, wake task. + if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().events_rxdrdy().bit_is_set() { + r.intenclr.write(|w| w.rxdrdy().clear()); + r.events_rxdrdy.reset(); + s.rx_waker.wake(); + } + + // If not RXing, start. + if s.rx_bufs.load(Ordering::Relaxed) == 0 { + let (ptr, len) = rx.push_buf(); + if len >= half_len { + //trace!(" irq_rx: starting {:?}", half_len); + s.rx_bufs.store(1, Ordering::Relaxed); + + // Set up the DMA read + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) }); + + // Start UARTE Receive transaction + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + rx.push_done(half_len); + r.intenset.write(|w| w.rxstarted().set()); + } + } + + if r.events_rxstarted.read().bits() != 0 { + //trace!(" irq_rx: rxstarted"); + let (ptr, len) = rx.push_buf(); + if len >= half_len { + //trace!(" irq_rx: starting second {:?}", half_len); + + // Set up the DMA read + r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) }); + + let chn = s.rx_ppi_ch.load(Ordering::Relaxed); + + ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) }); + + rx.push_done(half_len); + + r.events_rxstarted.reset(); + } else { + //trace!(" irq_rx: rxstarted no buf"); + r.intenclr.write(|w| w.rxstarted().clear()); + } + } + + // ============================= + + // TX end + if r.events_endtx.read().bits() != 0 { + r.events_endtx.reset(); + + let n = s.tx_count.load(Ordering::Relaxed); + //trace!(" irq_tx: endtx {:?}", n); + tx.pop_done(n); + s.tx_waker.wake(); + s.tx_count.store(0, Ordering::Relaxed); + } + + // If not TXing, start. + if s.tx_count.load(Ordering::Relaxed) == 0 { + let (ptr, len) = tx.pop_buf(); + if len != 0 { + //trace!(" irq_tx: starting {:?}", len); + s.tx_count.store(len, Ordering::Relaxed); + + // Set up the DMA write + r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); + r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + + // Start UARTE Transmit transaction + r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + } + } + + //trace!("irq: end"); + } + /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { - self.inner.borrow_mut().with(|state| { - let r = U::regs(); - - let timeout = 0x8000_0000 / (baudrate as u32 / 40); - state.timer.cc(0).write(timeout); - state.timer.clear(); - - r.baudrate.write(|w| w.baudrate().variant(baudrate)); - }); + let r = U::regs(); + r.baudrate.write(|w| w.baudrate().variant(baudrate)); } /// Split the UART in reader and writer parts. @@ -206,120 +380,117 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) } - async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { - poll_fn(move |cx| { - let mut do_pend = false; - let mut inner = self.inner.borrow_mut(); - let res = inner.with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("poll_read"); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - trace!(" got {:?} {:?}", data.as_ptr() as u32, data.len()); - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - state.rx.pop(len); - do_pend = true; - return Poll::Ready(Ok(len)); - } - - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::Pending - }); - if do_pend { - inner.pend(); - } - - res - }) - .await + async fn inner_read(&self, buf: &mut [u8]) -> Result { + let data = self.inner_fill_buf().await?; + let n = data.len().min(buf.len()); + buf[..n].copy_from_slice(&data[..n]); + self.inner_consume(n); + Ok(n) } async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { poll_fn(move |cx| { - let mut inner = self.inner.borrow_mut(); - let res = inner.with(|state| { - trace!("poll_write: {:?}", buf.len()); + //trace!("poll_write: {:?}", buf.len()); + let s = U::buffered_state(); + let mut tx = unsafe { s.tx_buf.writer() }; - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - trace!("poll_write: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } + let tx_buf = tx.push_slice(); + if tx_buf.is_empty() { + //trace!("poll_write: pending"); + s.tx_waker.register(cx.waker()); + return Poll::Pending; + } - let n = min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); + let n = min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + tx.push_done(n); - trace!("poll_write: queued {:?}", n); + //trace!("poll_write: queued {:?}", n); - compiler_fence(Ordering::SeqCst); + compiler_fence(Ordering::SeqCst); + Self::pend_irq(); - Poll::Ready(Ok(n)) - }); - - inner.pend(); - - res + Poll::Ready(Ok(n)) }) .await } async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> { poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - trace!("poll_flush"); + //trace!("poll_flush"); + let s = U::buffered_state(); + if !s.tx_buf.is_empty() { + //trace!("poll_flush: pending"); + s.tx_waker.register(cx.waker()); + return Poll::Pending; + } - if !state.tx.is_empty() { - trace!("poll_flush: pending"); - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) + Poll::Ready(Ok(())) }) .await } async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> { poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - trace!("fill_buf"); + compiler_fence(Ordering::SeqCst); + //trace!("poll_read"); - // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); - if !buf.is_empty() { - trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } + let r = U::regs(); + let s = U::buffered_state(); - trace!(" empty"); - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) + // Read the RXDRDY counter. + T::regs().tasks_capture[0].write(|w| unsafe { w.bits(1) }); + let mut end = T::regs().cc[0].read().bits() as usize; + //trace!(" rxdrdy count = {:?}", end); + + // We've set a compare channel that resets the counter to 0 when it reaches `len*2`. + // However, it's unclear if that's instant, or there's a small window where you can + // still read `len()*2`. + // This could happen if in one clock cycle the counter is updated, and in the next the + // clear takes effect. The docs are very sparse, they just say "Task delays: After TIMER + // is started, the CLEAR, COUNT, and STOP tasks are guaranteed to take effect within one + // clock cycle of the PCLK16M." :shrug: + // So, we wrap the counter ourselves, just in case. + if end > s.rx_buf.len() * 2 { + end = 0 + } + + // This logic mirrors `atomic_ring_buffer::Reader::pop_buf()` + let mut start = s.rx_buf.start.load(Ordering::Relaxed); + let len = s.rx_buf.len(); + if start == end { + //trace!(" empty"); + s.rx_waker.register(cx.waker()); + r.intenset.write(|w| w.rxdrdy().set_bit()); + return Poll::Pending; + } + + if start >= len { + start -= len + } + if end >= len { + end -= len + } + + let n = if end > start { end - start } else { len - start }; + assert!(n != 0); + //trace!(" uarte ringbuf: pop_buf {:?}..{:?}", start, start + n); + + let buf = s.rx_buf.buf.load(Ordering::Relaxed); + Poll::Ready(Ok(unsafe { slice::from_raw_parts(buf.add(start), n) })) }) .await } fn inner_consume(&self, amt: usize) { - let mut inner = self.inner.borrow_mut(); - let signal = inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - inner.pend(); + if amt == 0 { + return; } + + let s = U::buffered_state(); + let mut rx = unsafe { s.rx_buf.reader() }; + rx.pop_done(amt); + U::regs().intenset.write(|w| w.rxstarted().set()); } } @@ -397,7 +568,7 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write } } -impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { +impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarte<'a, U, T> { fn drop(&mut self) { let r = U::regs(); @@ -418,108 +589,11 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { gpio::deconfigure_pin(r.psel.txd.read().bits()); gpio::deconfigure_pin(r.psel.rts.read().bits()); gpio::deconfigure_pin(r.psel.cts.read().bits()); - } -} - -impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, U, T> { - type Interrupt = U::Interrupt; - fn on_interrupt(&mut self) { - trace!("irq: start"); - let r = U::regs(); - - loop { - match self.rx_state { - RxState::Idle => { - trace!(" irq_rx: in state idle"); - - let buf = self.rx.push_buf(); - if !buf.is_empty() { - trace!(" irq_rx: starting {:?}", buf.len()); - self.rx_state = RxState::Receiving; - - // Set up the DMA read - r.rxd.ptr.write(|w| - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - unsafe { w.ptr().bits(buf.as_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 `u8` is also fine. - // - // The MAXCNT field is at least 8 bits wide and accepts the full - // range of values. - unsafe { w.maxcnt().bits(buf.len() as _) }); - trace!(" irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len()); - - // Start UARTE Receive transaction - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - } - break; - } - RxState::Receiving => { - trace!(" irq_rx: in state receiving"); - if r.events_endrx.read().bits() != 0 { - self.timer.stop(); - - let n: usize = r.rxd.amount.read().amount().bits() as usize; - trace!(" irq_rx: endrx {:?}", n); - self.rx.push(n); - - r.events_endrx.reset(); - - self.rx_waker.wake(); - self.rx_state = RxState::Idle; - } else { - break; - } - } - } - } - - loop { - match self.tx_state { - TxState::Idle => { - trace!(" irq_tx: in state Idle"); - let buf = self.tx.pop_buf(); - if !buf.is_empty() { - trace!(" irq_tx: starting {:?}", buf.len()); - self.tx_state = TxState::Transmitting(buf.len()); - - // Set up the DMA write - r.txd.ptr.write(|w| - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - unsafe { w.ptr().bits(buf.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. - unsafe { w.maxcnt().bits(buf.len() as _) }); - - // Start UARTE Transmit transaction - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); - } - break; - } - TxState::Transmitting(n) => { - trace!(" irq_tx: in state Transmitting"); - if r.events_endtx.read().bits() != 0 { - r.events_endtx.reset(); - - trace!(" irq_tx: endtx {:?}", n); - self.tx.pop(n); - self.tx_waker.wake(); - self.tx_state = TxState::Idle; - } else { - break; - } - } - } - } - trace!("irq: end"); + + let s = U::buffered_state(); + unsafe { + s.rx_buf.deinit(); + s.tx_buf.deinit(); + } } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 48457744b..00afbd059 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -883,6 +883,7 @@ pub(crate) mod sealed { pub trait Instance { fn regs() -> &'static pac::uarte0::RegisterBlock; fn state() -> &'static State; + fn buffered_state() -> &'static crate::buffered_uarte::State; } } @@ -902,6 +903,10 @@ macro_rules! impl_uarte { static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new(); &STATE } + fn buffered_state() -> &'static crate::buffered_uarte::State { + static STATE: crate::buffered_uarte::State = crate::buffered_uarte::State::new(); + &STATE + } } impl crate::uarte::Instance for peripherals::$type { type Interrupt = crate::interrupt::$irq; diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index ea566f4b2..584e6b2bc 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -4,10 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::{BufferedUarte, State}; +use embassy_nrf::buffered_uarte::BufferedUarte; use embassy_nrf::{interrupt, uarte}; use embedded_io::asynch::{BufRead, Write}; -use futures::pin_mut; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -21,24 +20,19 @@ async fn main(_spawner: Spawner) { let mut rx_buffer = [0u8; 4096]; let irq = interrupt::take!(UARTE0_UART0); - let mut state = State::new(); - // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) - let u = BufferedUarte::new( - &mut state, + let mut u = BufferedUarte::new( p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, + p.PPI_GROUP0, irq, p.P0_08, p.P0_06, - p.P0_07, - p.P0_05, config, &mut rx_buffer, &mut tx_buffer, ); - pin_mut!(u); info!("uarte initialized!"); From 916f94b36663cbb638654b34acd53e30beb5c7b6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:59:16 +0100 Subject: [PATCH 0643/1575] nrf/buffered_uarte: make available on stable. --- embassy-nrf/src/buffered_uarte.rs | 175 +++++++++++++++------ embassy-nrf/src/lib.rs | 1 - examples/nrf52840/src/bin/buffered_uart.rs | 2 +- 3 files changed, 125 insertions(+), 53 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 79f9a1f78..07f292c4a 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -46,6 +46,14 @@ mod sealed { } } +/// UART error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + // No errors for now +} + pub(crate) use sealed::State; impl State { @@ -380,7 +388,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { (BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self }) } - async fn inner_read(&self, buf: &mut [u8]) -> Result { + async fn inner_read(&self, buf: &mut [u8]) -> Result { let data = self.inner_fill_buf().await?; let n = data.len().min(buf.len()); buf[..n].copy_from_slice(&data[..n]); @@ -388,7 +396,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { Ok(n) } - async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { + async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { poll_fn(move |cx| { //trace!("poll_write: {:?}", buf.len()); let s = U::buffered_state(); @@ -415,7 +423,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { .await } - async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> { + async fn inner_flush<'a>(&'a self) -> Result<(), Error> { poll_fn(move |cx| { //trace!("poll_flush"); let s = U::buffered_state(); @@ -430,7 +438,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { .await } - async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> { + async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { poll_fn(move |cx| { compiler_fence(Ordering::SeqCst); //trace!("poll_read"); @@ -492,6 +500,31 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { rx.pop_done(amt); U::regs().intenset.write(|w| w.rxstarted().set()); } + + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + pub async fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).await + } + + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + pub async fn fill_buf(&mut self) -> Result<&[u8], Error> { + self.inner_fill_buf().await + } + + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + pub fn consume(&mut self, amt: usize) { + self.inner_consume(amt) + } + + /// Write a buffer into this writer, returning how many bytes were written. + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub async fn flush(&mut self) -> Result<(), Error> { + self.inner_flush().await + } } /// Reader part of the buffered UARTE driver. @@ -499,72 +532,112 @@ pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } +impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteTx<'u, 'd, U, T> { + /// Write a buffer into this writer, returning how many bytes were written. + pub async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await + } + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + pub async fn flush(&mut self) -> Result<(), Error> { + self.inner.inner_flush().await + } +} + /// Writer part of the buffered UARTE driver. pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> { inner: &'u BufferedUarte<'d, U, T>, } -impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { - type Error = core::convert::Infallible; -} - -impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { - type Error = core::convert::Infallible; -} - -impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { - type Error = core::convert::Infallible; -} - -impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { - 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> { - async fn read(&mut self, buf: &mut [u8]) -> Result { +impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> { + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + pub 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> { - async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner_fill_buf().await - } - - fn consume(&mut self, amt: usize) { - self.inner_consume(amt) - } -} - -impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { - async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + pub async fn fill_buf(&mut self) -> Result<&[u8], Error> { self.inner.inner_fill_buf().await } - fn consume(&mut self, amt: usize) { + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + pub fn consume(&mut self, amt: usize) { self.inner.inner_consume(amt) } } -impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.inner_write(buf).await +#[cfg(feature = "nightly")] +mod _embedded_io { + use super::*; + + impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + match *self {} + } } - 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> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.inner.inner_write(buf).await + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { + type Error = Error; } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.inner_flush().await + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { + type Error = Error; + } + + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { + type Error = Error; + } + + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { + 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> { + 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> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner_fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.inner_consume(amt) + } + } + + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.inner_fill_buf().await + } + + fn consume(&mut self, amt: usize) { + self.inner.inner_consume(amt) + } + } + + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner_write(buf).await + } + + 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> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_write(buf).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.inner_flush().await + } } } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index a9683df44..6b7dc7791 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -37,7 +37,6 @@ pub(crate) mod util; #[cfg(feature = "_time-driver")] mod time_driver; -#[cfg(feature = "nightly")] pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 584e6b2bc..5b934b7d6 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::buffered_uarte::BufferedUarte; use embassy_nrf::{interrupt, uarte}; -use embedded_io::asynch::{BufRead, Write}; +use embedded_io::asynch::Write; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 7650fea5f2bed1c39a0ff6c5934709d316547a23 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 4 Mar 2023 05:37:13 +0100 Subject: [PATCH 0644/1575] nrf/buffered_uarte: add HIL tests. --- ci.sh | 1 + embassy-nrf/src/buffered_uarte.rs | 2 +- tests/nrf/.cargo/config.toml | 9 + tests/nrf/Cargo.toml | 20 ++ tests/nrf/build.rs | 16 ++ tests/nrf/link_ram.x | 254 ++++++++++++++++++++++++ tests/nrf/memory.x | 5 + tests/nrf/src/bin/buffered_uart.rs | 74 +++++++ tests/nrf/src/bin/buffered_uart_spam.rs | 86 ++++++++ 9 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 tests/nrf/.cargo/config.toml create mode 100644 tests/nrf/Cargo.toml create mode 100644 tests/nrf/build.rs create mode 100644 tests/nrf/link_ram.x create mode 100644 tests/nrf/memory.x create mode 100644 tests/nrf/src/bin/buffered_uart.rs create mode 100644 tests/nrf/src/bin/buffered_uart_spam.rs diff --git a/ci.sh b/ci.sh index 417937d07..bbcb26bdb 100755 --- a/ci.sh +++ b/ci.sh @@ -133,6 +133,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ + --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ $BUILD_EXTRA diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 07f292c4a..ab639aeea 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -296,7 +296,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } // Received some bytes, wake task. - if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().events_rxdrdy().bit_is_set() { + if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().bits() != 0 { r.intenclr.write(|w| w.rxdrdy().clear()); r.events_rxdrdy.reset(); s.rx_waker.wake(); diff --git a/tests/nrf/.cargo/config.toml b/tests/nrf/.cargo/config.toml new file mode 100644 index 000000000..4eec189d4 --- /dev/null +++ b/tests/nrf/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +#runner = "teleprobe local run --chip nRF52840_xxAA --elf" +runner = "teleprobe client run --target nrf52840-dk --elf" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml new file mode 100644 index 000000000..2a4e8cf41 --- /dev/null +++ b/tests/nrf/Cargo.toml @@ -0,0 +1,20 @@ +[package] +edition = "2021" +name = "embassy-nrf-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embedded-io = { version = "0.4.0", features = ["async"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } \ No newline at end of file diff --git a/tests/nrf/build.rs b/tests/nrf/build.rs new file mode 100644 index 000000000..6f4872249 --- /dev/null +++ b/tests/nrf/build.rs @@ -0,0 +1,16 @@ +use std::error::Error; +use std::path::PathBuf; +use std::{env, fs}; + +fn main() -> Result<(), Box> { + let out = PathBuf::from(env::var("OUT_DIR").unwrap()); + fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=link_ram.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + + Ok(()) +} diff --git a/tests/nrf/link_ram.x b/tests/nrf/link_ram.x new file mode 100644 index 000000000..26da86baa --- /dev/null +++ b/tests/nrf/link_ram.x @@ -0,0 +1,254 @@ +/* ##### EMBASSY NOTE + Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in + Adjusted to put everything in RAM +*/ + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut __sbss }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization + routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see + "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +/* Provides information about the memory layout of the device */ +/* This will be provided by the user (see `memory.x`) or by a Board Support Crate */ +INCLUDE memory.x + +/* # Entry point = reset vector */ +EXTERN(__RESET_VECTOR); +EXTERN(Reset); +ENTRY(Reset); + +/* # Exception vectors */ +/* This is effectively weak aliasing at the linker level */ +/* The user can override any of these aliases by defining the corresponding symbol themselves (cf. + the `exception!` macro) */ +EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ + +EXTERN(DefaultHandler); + +PROVIDE(NonMaskableInt = DefaultHandler); +EXTERN(HardFaultTrampoline); +PROVIDE(MemoryManagement = DefaultHandler); +PROVIDE(BusFault = DefaultHandler); +PROVIDE(UsageFault = DefaultHandler); +PROVIDE(SecureFault = DefaultHandler); +PROVIDE(SVCall = DefaultHandler); +PROVIDE(DebugMonitor = DefaultHandler); +PROVIDE(PendSV = DefaultHandler); +PROVIDE(SysTick = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultHandler_); +PROVIDE(HardFault = HardFault_); + +/* # Interrupt vectors */ +EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ + +/* # Pre-initialization function */ +/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = DefaultPreInit); + +/* # Sections */ +SECTIONS +{ + PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); + + /* ## Sections in RAM */ + /* ### Vector table */ + .vector_table ORIGIN(RAM) : + { + /* Initial Stack Pointer (SP) value */ + LONG(_stack_start); + + /* Reset vector */ + KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */ + __reset_vector = .; + + /* Exceptions */ + KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */ + __eexceptions = .; + + /* Device specific interrupts */ + KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */ + } > RAM + + PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table)); + + /* ### .text */ + .text _stext : + { + __stext = .; + *(.Reset); + + *(.text .text.*); + + /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`, + so must be placed close to it. */ + *(.HardFaultTrampoline); + *(.HardFault.*); + + . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ + __etext = .; + } > RAM + + /* ### .rodata */ + .rodata : ALIGN(4) + { + . = ALIGN(4); + __srodata = .; + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + __erodata = .; + } > RAM + + /* ## Sections in RAM */ + /* ### .data */ + .data : ALIGN(4) + { + . = ALIGN(4); + __sdata = .; + __edata = .; + *(.data .data.*); + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM + /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to + * use the .data loading mechanism by pushing __edata. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + + /* LMA of .data */ + __sidata = LOADADDR(.data); + + /* ### .gnu.sgstubs + This section contains the TrustZone-M veneers put there by the Arm GNU linker. */ + /* Security Attribution Unit blocks must be 32 bytes aligned. */ + /* Note that this pads the RAM usage to 32 byte alignment. */ + .gnu.sgstubs : ALIGN(32) + { + . = ALIGN(32); + __veneer_base = .; + *(.gnu.sgstubs*) + . = ALIGN(32); + __veneer_limit = .; + } > RAM + + /* ### .bss */ + .bss (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __sbss = .; + *(.bss .bss.*); + *(COMMON); /* Uninitialized C statics */ + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM + /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to + * use the .bss zeroing mechanism by pushing __ebss. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + __ebss = .; + + /* ### .uninit */ + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); + __euninit = .; + } > RAM + + /* Place the heap right after `.uninit` in RAM */ + PROVIDE(__sheap = __euninit); + + /* ## .got */ + /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in + the input files and raise an error if relocatable code is found */ + .got (NOLOAD) : + { + KEEP(*(.got .got.*)); + } + + /* ## Discarded sections */ + /DISCARD/ : + { + /* Unused exception related info that only wastes space */ + *(.ARM.exidx); + *(.ARM.exidx.*); + *(.ARM.extab.*); + } +} + +/* Do not exceed this mark in the error messages below | */ +/* # Alignment checks */ +ASSERT(ORIGIN(RAM) % 4 == 0, " +ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); + +ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " +BUG(cortex-m-rt): .data is not 4-byte aligned"); + +ASSERT(__sidata % 4 == 0, " +BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " +BUG(cortex-m-rt): .bss is not 4-byte aligned"); + +ASSERT(__sheap % 4 == 0, " +BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); + +/* # Position checks */ + +/* ## .vector_table */ +ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, " +BUG(cortex-m-rt): the reset vector is missing"); + +ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, " +BUG(cortex-m-rt): the exception vectors are missing"); + +ASSERT(SIZEOF(.vector_table) > 0x40, " +ERROR(cortex-m-rt): The interrupt vectors are missing. +Possible solutions, from most likely to less likely: +- Link to a svd2rust generated device crate +- Check that you actually use the device/hal/bsp crate in your code +- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency +may be enabling it) +- Supply the interrupt handlers yourself. Check the documentation for details."); + +/* ## .text */ +ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, " +ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section +Set _stext to an address greater than the end of .vector_table (See output of `nm`)"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), " +ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory. +Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'"); + +/* # Other checks */ +ASSERT(SIZEOF(.got) == 0, " +ERROR(cortex-m-rt): .got section detected in the input object files +Dynamic relocations are not supported. If you are linking to C code compiled using +the 'cc' crate then modify your build script to compile the C code _without_ +the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); +/* Do not exceed this mark in the error messages above | */ + + +/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */ +/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */ +INCLUDE device.x \ No newline at end of file diff --git a/tests/nrf/memory.x b/tests/nrf/memory.x new file mode 100644 index 000000000..58900a7bd --- /dev/null +++ b/tests/nrf/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs new file mode 100644 index 000000000..0550b0bb7 --- /dev/null +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -0,0 +1,74 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_nrf::buffered_uarte::BufferedUarte; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD1M; + + let mut tx_buffer = [0u8; 1024]; + let mut rx_buffer = [0u8; 1024]; + + let mut u = BufferedUarte::new( + p.UARTE0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + p.PPI_GROUP0, + interrupt::take!(UARTE0_UART0), + p.P1_03, + p.P1_02, + config.clone(), + &mut rx_buffer, + &mut tx_buffer, + ); + + info!("uarte initialized!"); + + let (mut rx, mut tx) = u.split(); + + const COUNT: usize = 40_000; + + let tx_fut = async { + let mut tx_buf = [0; 215]; + let mut i = 0; + while i < COUNT { + let n = tx_buf.len().min(COUNT - i); + let tx_buf = &mut tx_buf[..n]; + for (j, b) in tx_buf.iter_mut().enumerate() { + *b = (i + j) as u8; + } + let n = unwrap!(tx.write(tx_buf).await); + i += n; + } + }; + let rx_fut = async { + let mut i = 0; + while i < COUNT { + let buf = unwrap!(rx.fill_buf().await); + + for &b in buf { + assert_eq!(b, i as u8); + i = i + 1; + } + + let n = buf.len(); + rx.consume(n); + } + }; + + join(rx_fut, tx_fut).await; + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs new file mode 100644 index 000000000..57aaeca45 --- /dev/null +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::mem; +use core::ptr::NonNull; + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_nrf::buffered_uarte::BufferedUarte; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::ppi::{Event, Ppi, Task}; +use embassy_nrf::uarte::Uarte; +use embassy_nrf::{interrupt, pac, uarte}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD1M; + + let mut tx_buffer = [0u8; 1024]; + let mut rx_buffer = [0u8; 1024]; + + mem::forget(Output::new(&mut p.P1_02, Level::High, OutputDrive::Standard)); + + let mut u = BufferedUarte::new( + p.UARTE0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + p.PPI_GROUP0, + interrupt::take!(UARTE0_UART0), + p.P1_03, + p.P1_04, + config.clone(), + &mut rx_buffer, + &mut tx_buffer, + ); + + info!("uarte initialized!"); + + // uarte needs some quiet time to start rxing properly. + Timer::after(Duration::from_millis(10)).await; + + // Tx spam in a loop. + const NSPAM: usize = 17; + static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let _spam = Uarte::new(p.UARTE1, interrupt::take!(UARTE1), p.P1_01, p.P1_02, config.clone()); + let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) }; + let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) }; + let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; + let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task); + spam_ppi.enable(); + let p = unsafe { TX_BUF.as_mut_ptr() }; + spam_peri.txd.ptr.write(|w| unsafe { w.ptr().bits(p as u32) }); + spam_peri.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(NSPAM as _) }); + spam_peri.tasks_starttx.write(|w| unsafe { w.bits(1) }); + + let mut i = 0; + let mut total = 0; + while total < 256 * 1024 { + let buf = unwrap!(u.fill_buf().await); + //info!("rx {}", buf); + + for &b in buf { + assert_eq!(b, unsafe { TX_BUF[i] }); + + i = i + 1; + if i == NSPAM { + i = 0; + } + } + + // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again + let n = buf.len(); + u.consume(n); + total += n; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 9eb65b11cb29ec85224c129660d8ae2e01e06119 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 23 Feb 2023 22:23:01 +0100 Subject: [PATCH 0645/1575] nrf/qspi: remove cfg_if hack --- embassy-nrf/src/qspi.rs | 59 +++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d434327fc..5b68aa4d4 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -525,42 +525,43 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SI } } -cfg_if::cfg_if! { - if #[cfg(feature = "nightly")] - { - use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - use core::future::Future; +#[cfg(feature = "nightly")] +mod _eh1 { + use core::future::Future; - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { - const WRITE_SIZE: usize = ::WRITE_SIZE; - const ERASE_SIZE: usize = ::ERASE_SIZE; + use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.write(offset as usize, data).await } - } + use super::*; - type EraseFuture<'a> = impl Future> + 'a where Self: 'a; - fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { - async move { - for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { - self.erase(address).await? - } - Ok(()) - } - } + impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { + const WRITE_SIZE: usize = ::WRITE_SIZE; + const ERASE_SIZE: usize = ::ERASE_SIZE; + + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { + async move { self.write(offset as usize, data).await } } - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { - const READ_SIZE: usize = 4; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.read(address as usize, data).await } + type EraseFuture<'a> = impl Future> + 'a where Self: 'a; + fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { + async move { + for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { + self.erase(address).await? + } + Ok(()) } + } + } - fn capacity(&self) -> usize { - FLASH_SIZE - } + impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { + const READ_SIZE: usize = 4; + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { + async move { self.read(address as usize, data).await } + } + + fn capacity(&self) -> usize { + FLASH_SIZE } } } From 1955a225e86d358d39f00620c770631c2186576f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 23 Feb 2023 22:36:10 +0100 Subject: [PATCH 0646/1575] nrf/qspi: add nrf53 support. --- embassy-nrf/src/chips/nrf5340_app.rs | 5 ++++ embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/qspi.rs | 37 ++++++++++++++-------------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 2e1c7f384..9c7b738e6 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -250,6 +250,9 @@ embassy_hal_common::peripherals! { TIMER1, TIMER2, + // QSPI + QSPI, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -393,6 +396,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_qspi!(QSPI, QSPI, QSPI); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index a9683df44..26f22b617 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -58,7 +58,7 @@ pub mod ppi; pub mod pwm; #[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))] pub mod qdec; -#[cfg(feature = "nrf52840")] +#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod rng; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 5b68aa4d4..9a4e614f5 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -133,25 +133,26 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { let r = T::regs(); - sck.set_high(); - csn.set_high(); - io0.set_high(); - io1.set_high(); - io2.set_high(); - io3.set_high(); - sck.conf().write(|w| w.dir().output().drive().h0h1()); - csn.conf().write(|w| w.dir().output().drive().h0h1()); - io0.conf().write(|w| w.dir().output().drive().h0h1()); - io1.conf().write(|w| w.dir().output().drive().h0h1()); - io2.conf().write(|w| w.dir().output().drive().h0h1()); - io3.conf().write(|w| w.dir().output().drive().h0h1()); + macro_rules! config_pin { + ($pin:ident) => { + $pin.set_high(); + $pin.conf().write(|w| { + w.dir().output(); + w.drive().h0h1(); + #[cfg(feature = "_nrf5340-s")] + w.mcusel().peripheral(); + w + }); + r.psel.$pin.write(|w| unsafe { w.bits($pin.psel_bits()) }); + }; + } - r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); - r.psel.csn.write(|w| unsafe { w.bits(csn.psel_bits()) }); - r.psel.io0.write(|w| unsafe { w.bits(io0.psel_bits()) }); - r.psel.io1.write(|w| unsafe { w.bits(io1.psel_bits()) }); - r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) }); - r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) }); + config_pin!(sck); + config_pin!(csn); + config_pin!(io0); + config_pin!(io1); + config_pin!(io2); + config_pin!(io3); r.ifconfig0.write(|w| { w.addrmode().variant(config.address_mode); From 75f69803af244329ba6dd9093599be357f12ae60 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 02:24:52 +0100 Subject: [PATCH 0647/1575] nrf/qspi: always use u32 for addresses. --- embassy-nrf/src/qspi.rs | 62 +++++++++++++++---------------- examples/nrf52840/src/bin/qspi.rs | 10 ++--- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 9a4e614f5..404438ea7 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -111,12 +111,12 @@ pub enum Error { } /// QSPI flash driver. -pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { +pub struct Qspi<'d, T: Instance, const FLASH_SIZE: u32> { irq: PeripheralRef<'d, T::Interrupt>, dpm_enabled: bool, } -impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { /// Create a new QSPI driver. pub fn new( _qspi: impl Peripheral

+ 'd, @@ -322,17 +322,17 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } } - fn start_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { + fn start_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); - assert_eq!(address as u32 % 4, 0); + assert_eq!(address % 4, 0); if address > FLASH_SIZE { return Err(Error::OutOfBounds); } let r = T::regs(); - r.read.src.write(|w| unsafe { w.src().bits(address as u32) }); + r.read.src.write(|w| unsafe { w.src().bits(address) }); r.read.dst.write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) }); r.read.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) }); @@ -343,10 +343,10 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } - fn start_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { + fn start_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); - assert_eq!(address as u32 % 4, 0); + assert_eq!(address % 4, 0); if address > FLASH_SIZE { return Err(Error::OutOfBounds); @@ -354,7 +354,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { let r = T::regs(); r.write.src.write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); - r.write.dst.write(|w| unsafe { w.dst().bits(address as u32) }); + r.write.dst.write(|w| unsafe { w.dst().bits(address) }); r.write.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) }); r.events_ready.reset(); @@ -364,14 +364,14 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { Ok(()) } - fn start_erase(&mut self, address: usize) -> Result<(), Error> { - assert_eq!(address as u32 % 4096, 0); + fn start_erase(&mut self, address: u32) -> Result<(), Error> { + assert_eq!(address % 4096, 0); if address > FLASH_SIZE { return Err(Error::OutOfBounds); } let r = T::regs(); - r.erase.ptr.write(|w| unsafe { w.ptr().bits(address as u32) }); + r.erase.ptr.write(|w| unsafe { w.ptr().bits(address) }); r.erase.len.write(|w| w.len()._4kb()); r.events_ready.reset(); @@ -382,7 +382,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Read data from the flash memory. - pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { + pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_read(address, data)?; @@ -394,7 +394,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Write data to the flash memory. - pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { + pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_write(address, data)?; @@ -406,7 +406,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Erase a sector on the flash memory. - pub async fn erase(&mut self, address: usize) -> Result<(), Error> { + pub async fn erase(&mut self, address: u32) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_erase(address)?; @@ -418,28 +418,28 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { } /// Read data from the flash memory, blocking version. - pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { self.start_read(address, data)?; Self::blocking_wait_ready(); Ok(()) } /// Write data to the flash memory, blocking version. - pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { self.start_write(address, data)?; Self::blocking_wait_ready(); Ok(()) } /// Erase a sector on the flash memory, blocking version. - pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { + pub fn blocking_erase(&mut self, address: u32) -> Result<(), Error> { self.start_erase(address)?; Self::blocking_wait_ready(); Ok(()) } } -impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> Drop for Qspi<'d, T, FLASH_SIZE> { fn drop(&mut self) { let r = T::regs(); @@ -486,7 +486,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE> use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> ErrorType for Qspi<'d, T, FLASH_SIZE> { type Error = Error; } @@ -496,32 +496,32 @@ impl NorFlashError for Error { } } -impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> { const READ_SIZE: usize = 4; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset as usize, bytes)?; + self.blocking_read(offset, bytes)?; Ok(()) } fn capacity(&self) -> usize { - FLASH_SIZE + FLASH_SIZE as usize } } -impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: u32> NorFlash for Qspi<'d, T, FLASH_SIZE> { const WRITE_SIZE: usize = 4; const ERASE_SIZE: usize = 4096; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { + for address in (from..to).step_by(::ERASE_SIZE) { self.blocking_erase(address)?; } Ok(()) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset as usize, bytes)?; + self.blocking_write(offset, bytes)?; Ok(()) } } @@ -534,19 +534,19 @@ mod _eh1 { use super::*; - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { const WRITE_SIZE: usize = ::WRITE_SIZE; const ERASE_SIZE: usize = ::ERASE_SIZE; type WriteFuture<'a> = impl Future> + 'a where Self: 'a; fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.write(offset as usize, data).await } + async move { self.write(offset, data).await } } type EraseFuture<'a> = impl Future> + 'a where Self: 'a; fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { async move { - for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { + for address in (from..to).step_by(::ERASE_SIZE) { self.erase(address).await? } Ok(()) @@ -554,15 +554,15 @@ mod _eh1 { } } - impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { const READ_SIZE: usize = 4; type ReadFuture<'a> = impl Future> + 'a where Self: 'a; fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.read(address as usize, data).await } + async move { self.read(address, data).await } } fn capacity(&self) -> usize { - FLASH_SIZE + FLASH_SIZE as usize } } } diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index bdcf710b8..bc55f8463 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -52,23 +52,23 @@ async fn main(_spawner: Spawner) { for i in 0..8 { info!("page {:?}: erasing... ", i); - unwrap!(q.erase(i * PAGE_SIZE).await); + unwrap!(q.erase(i * PAGE_SIZE as u32).await); for j in 0..PAGE_SIZE { - buf.0[j] = pattern((j + i * PAGE_SIZE) as u32); + buf.0[j] = pattern((j as u32 + i * PAGE_SIZE as u32) as u32); } info!("programming..."); - unwrap!(q.write(i * PAGE_SIZE, &buf.0).await); + unwrap!(q.write(i * PAGE_SIZE as u32, &buf.0).await); } for i in 0..8 { info!("page {:?}: reading... ", i); - unwrap!(q.read(i * PAGE_SIZE, &mut buf.0).await); + unwrap!(q.read(i * PAGE_SIZE as u32, &mut buf.0).await); info!("verifying..."); for j in 0..PAGE_SIZE { - assert_eq!(buf.0[j], pattern((j + i * PAGE_SIZE) as u32)); + assert_eq!(buf.0[j], pattern((j as u32 + i * PAGE_SIZE as u32) as u32)); } } From 8eb8ea617419726915834555266e37568b8504e0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 02:33:02 +0100 Subject: [PATCH 0648/1575] nrf/qspi: remove FLASH_SIZE const generic param. --- embassy-nrf/src/qspi.rs | 36 ++++++++++++---------- examples/nrf52840/src/bin/qspi.rs | 2 +- examples/nrf52840/src/bin/qspi_lowpower.rs | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 404438ea7..3f7f464d0 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -8,6 +8,7 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; @@ -82,6 +83,8 @@ pub struct Config { pub spi_mode: SpiMode, /// Addressing mode (24-bit or 32-bit) pub address_mode: AddressMode, + /// Flash memory capacity in bytes. This is the value reported by the `embedded-storage` traits. + pub capacity: u32, } impl Default for Config { @@ -96,6 +99,7 @@ impl Default for Config { sck_delay: 80, spi_mode: SpiMode::MODE0, address_mode: AddressMode::_24BIT, + capacity: 0, } } } @@ -111,12 +115,13 @@ pub enum Error { } /// QSPI flash driver. -pub struct Qspi<'d, T: Instance, const FLASH_SIZE: u32> { +pub struct Qspi<'d, T: Instance> { irq: PeripheralRef<'d, T::Interrupt>, dpm_enabled: bool, + capacity: u32, } -impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> Qspi<'d, T> { /// Create a new QSPI driver. pub fn new( _qspi: impl Peripheral

+ 'd, @@ -128,7 +133,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { io2: impl Peripheral

+ 'd, io3: impl Peripheral

+ 'd, config: Config, - ) -> Qspi<'d, T, FLASH_SIZE> { + ) -> Self { into_ref!(irq, sck, csn, io0, io1, io2, io3); let r = T::regs(); @@ -194,6 +199,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { let res = Self { dpm_enabled: config.deep_power_down.is_some(), irq, + capacity: config.capacity, }; r.events_ready.reset(); @@ -326,7 +332,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > FLASH_SIZE { + if address > self.capacity { return Err(Error::OutOfBounds); } @@ -348,7 +354,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > FLASH_SIZE { + if address > self.capacity { return Err(Error::OutOfBounds); } @@ -366,7 +372,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { fn start_erase(&mut self, address: u32) -> Result<(), Error> { assert_eq!(address % 4096, 0); - if address > FLASH_SIZE { + if address > self.capacity { return Err(Error::OutOfBounds); } @@ -439,7 +445,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Qspi<'d, T, FLASH_SIZE> { } } -impl<'d, T: Instance, const FLASH_SIZE: u32> Drop for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> Drop for Qspi<'d, T> { fn drop(&mut self) { let r = T::regs(); @@ -484,9 +490,7 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> Drop for Qspi<'d, T, FLASH_SIZE> { } } -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; - -impl<'d, T: Instance, const FLASH_SIZE: u32> ErrorType for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> ErrorType for Qspi<'d, T> { type Error = Error; } @@ -496,7 +500,7 @@ impl NorFlashError for Error { } } -impl<'d, T: Instance, const FLASH_SIZE: u32> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> ReadNorFlash for Qspi<'d, T> { const READ_SIZE: usize = 4; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -505,11 +509,11 @@ impl<'d, T: Instance, const FLASH_SIZE: u32> ReadNorFlash for Qspi<'d, T, FLASH_ } fn capacity(&self) -> usize { - FLASH_SIZE as usize + self.capacity as usize } } -impl<'d, T: Instance, const FLASH_SIZE: u32> NorFlash for Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> NorFlash for Qspi<'d, T> { const WRITE_SIZE: usize = 4; const ERASE_SIZE: usize = 4096; @@ -534,7 +538,7 @@ mod _eh1 { use super::*; - impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance> AsyncNorFlash for Qspi<'d, T> { const WRITE_SIZE: usize = ::WRITE_SIZE; const ERASE_SIZE: usize = ::ERASE_SIZE; @@ -554,7 +558,7 @@ mod _eh1 { } } - impl<'d, T: Instance, const FLASH_SIZE: u32> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { + impl<'d, T: Instance> AsyncReadNorFlash for Qspi<'d, T> { const READ_SIZE: usize = 4; type ReadFuture<'a> = impl Future> + 'a where Self: 'a; fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { @@ -562,7 +566,7 @@ mod _eh1 { } fn capacity(&self) -> usize { - FLASH_SIZE as usize + self.capacity as usize } } } diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index bc55f8463..be665149a 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { config.write_page_size = qspi::WritePageSize::_256BYTES; let irq = interrupt::take!(QSPI); - let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( + let mut q = qspi::Qspi::new( p.QSPI, irq, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, ); diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 9341a2376..5008481c1 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs @@ -31,7 +31,7 @@ async fn main(_p: Spawner) { exit_time: 3, // tRDP = 35uS }); - let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( + let mut q = qspi::Qspi::new( &mut p.QSPI, &mut irq, &mut p.P0_19, From f7dfc49c5c40d70852d6d3c7313973adf97e4716 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 02:55:00 +0100 Subject: [PATCH 0649/1575] nrf/qspi: add _raw variants of methods that don't do bounds checks. Useful for the nRF7002, which presents as a "fake" QSPI flash, and the "capacity" concept doesn't really apply to it. --- embassy-nrf/src/qspi.rs | 90 +++++++++++++++++----- examples/nrf52840/src/bin/qspi.rs | 3 + examples/nrf52840/src/bin/qspi_lowpower.rs | 3 + 3 files changed, 76 insertions(+), 20 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 3f7f464d0..d514e0274 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -329,12 +329,10 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn start_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + // TODO: Return these as errors instead. assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > self.capacity { - return Err(Error::OutOfBounds); - } let r = T::regs(); @@ -350,14 +348,11 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn start_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + // TODO: Return these as errors instead. assert_eq!(data.as_ptr() as u32 % 4, 0); assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - if address > self.capacity { - return Err(Error::OutOfBounds); - } - let r = T::regs(); r.write.src.write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); r.write.dst.write(|w| unsafe { w.dst().bits(address) }); @@ -371,10 +366,8 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn start_erase(&mut self, address: u32) -> Result<(), Error> { + // TODO: Return these as errors instead. assert_eq!(address % 4096, 0); - if address > self.capacity { - return Err(Error::OutOfBounds); - } let r = T::regs(); r.erase.ptr.write(|w| unsafe { w.ptr().bits(address) }); @@ -387,8 +380,12 @@ impl<'d, T: Instance> Qspi<'d, T> { Ok(()) } - /// Read data from the flash memory. - pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + /// Raw QSPI read. + /// + /// The difference with `read` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub async fn read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_read(address, data)?; @@ -399,8 +396,12 @@ impl<'d, T: Instance> Qspi<'d, T> { Ok(()) } - /// Write data to the flash memory. - pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + /// Raw QSPI write. + /// + /// The difference with `write` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub async fn write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_write(address, data)?; @@ -411,8 +412,46 @@ impl<'d, T: Instance> Qspi<'d, T> { Ok(()) } + /// Raw QSPI read, blocking version. + /// + /// The difference with `blocking_read` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub fn blocking_read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + self.start_read(address, data)?; + Self::blocking_wait_ready(); + Ok(()) + } + + /// Raw QSPI write, blocking version. + /// + /// The difference with `blocking_write` is that this does not do bounds checks + /// against the flash capacity. It is intended for use when QSPI is used as + /// a raw bus, not with flash memory. + pub fn blocking_write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + self.start_write(address, data)?; + Self::blocking_wait_ready(); + Ok(()) + } + + /// Read data from the flash memory. + pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { + self.bounds_check(address, data.len())?; + self.read_raw(address, data).await + } + + /// Write data to the flash memory. + pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { + self.bounds_check(address, data.len())?; + self.write_raw(address, data).await + } + /// Erase a sector on the flash memory. pub async fn erase(&mut self, address: u32) -> Result<(), Error> { + if address >= self.capacity { + return Err(Error::OutOfBounds); + } + let ondrop = OnDrop::new(Self::blocking_wait_ready); self.start_erase(address)?; @@ -425,24 +464,35 @@ impl<'d, T: Instance> Qspi<'d, T> { /// Read data from the flash memory, blocking version. pub fn blocking_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> { - self.start_read(address, data)?; - Self::blocking_wait_ready(); - Ok(()) + self.bounds_check(address, data.len())?; + self.blocking_read_raw(address, data) } /// Write data to the flash memory, blocking version. pub fn blocking_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { - self.start_write(address, data)?; - Self::blocking_wait_ready(); - Ok(()) + self.bounds_check(address, data.len())?; + self.blocking_write_raw(address, data) } /// Erase a sector on the flash memory, blocking version. pub fn blocking_erase(&mut self, address: u32) -> Result<(), Error> { + if address >= self.capacity { + return Err(Error::OutOfBounds); + } + self.start_erase(address)?; Self::blocking_wait_ready(); Ok(()) } + + fn bounds_check(&self, address: u32, len: usize) -> Result<(), Error> { + let len_u32: u32 = len.try_into().map_err(|_| Error::OutOfBounds)?; + let end_address = address.checked_add(len_u32).ok_or(Error::OutOfBounds)?; + if end_address > self.capacity { + return Err(Error::OutOfBounds); + } + Ok(()) + } } impl<'d, T: Instance> Drop for Qspi<'d, T> { diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index be665149a..21a10940d 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -4,6 +4,7 @@ use defmt::{assert_eq, info, unwrap}; use embassy_executor::Spawner; +use embassy_nrf::qspi::Frequency; use embassy_nrf::{interrupt, qspi}; use {defmt_rtt as _, panic_probe as _}; @@ -19,6 +20,8 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); // Config for the MX25R64 present in the nRF52840 DK let mut config = qspi::Config::default(); + config.capacity = 8 * 1024 * 1024; // 8 MB + config.frequency = Frequency::M32; config.read_opcode = qspi::ReadOpcode::READ4IO; config.write_opcode = qspi::WriteOpcode::PP4IO; config.write_page_size = qspi::WritePageSize::_256BYTES; diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 5008481c1..20c903914 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs @@ -6,6 +6,7 @@ use core::mem; use defmt::{info, unwrap}; use embassy_executor::Spawner; +use embassy_nrf::qspi::Frequency; use embassy_nrf::{interrupt, qspi}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -23,6 +24,8 @@ async fn main(_p: Spawner) { loop { // Config for the MX25R64 present in the nRF52840 DK let mut config = qspi::Config::default(); + config.capacity = 8 * 1024 * 1024; // 8 MB + config.frequency = Frequency::M32; config.read_opcode = qspi::ReadOpcode::READ4IO; config.write_opcode = qspi::WriteOpcode::PP4IO; config.write_page_size = qspi::WritePageSize::_256BYTES; From 6dfda69cc45dfc259ee03f25342c729a3f3315c8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:19:18 +0100 Subject: [PATCH 0650/1575] readme: add embassy-rp --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6d5e75ead..33f38dafe 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Rust's async/await allows - **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy. - embassy-stm32, for all STM32 microcontroller families. - embassy-nrf, for the Nordic Semiconductor nRF52, nRF53, nRF91 series. + - embassy-rp, for the Raspberry Pi RP2040 microcontroller. - esp-rs, for the Espressif Systems ESP32 series of chips. - Embassy HAL support for Espressif chips is being developed in the [esp-rs/esp-hal](https://github.com/esp-rs/esp-hal) repository. - Async WiFi, Bluetooth and ESP-NOW is being developed in the [esp-rs/esp-wifi](https://github.com/esp-rs/esp-wifi) repository. From c88bbaa5ecc340e14fd540e3025a8635456e5405 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 23:13:22 +0100 Subject: [PATCH 0651/1575] time/ticker: make sure the future for .next() is Unpin. --- embassy-time/src/timer.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 416830a7c..52620d233 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -1,9 +1,9 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::pin::Pin; use core::task::{Context, Poll, Waker}; use futures_util::future::{select, Either}; -use futures_util::{pin_mut, Stream, StreamExt}; +use futures_util::{pin_mut, Stream}; use crate::{Duration, Instant}; @@ -134,8 +134,17 @@ impl Ticker { } /// Waits for the next tick - pub async fn next(&mut self) { - ::next(self).await; + pub fn next(&mut self) -> impl Future + '_ { + poll_fn(|cx| { + if self.expires_at <= Instant::now() { + let dur = self.duration; + self.expires_at += dur; + Poll::Ready(()) + } else { + schedule_wake(self.expires_at, cx.waker()); + Poll::Pending + } + }) } } From a054891263cbd17315cdf85ecdcc6359313063bc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 21 Feb 2023 22:19:06 +0100 Subject: [PATCH 0652/1575] cortex-m: rename Handler to DynHandler. I want to use the name Handler for the new interrupt binding macro. --- embassy-cortex-m/src/interrupt.rs | 6 +++--- embassy-macros/src/macros/cortex_m_interrupt_declare.rs | 4 ++-- embassy-macros/src/macros/cortex_m_interrupt_take.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index 1df8671b9..ead9d52fe 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -15,12 +15,12 @@ pub mod _export { /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] -pub struct Handler { +pub struct DynHandler { pub func: AtomicPtr<()>, pub ctx: AtomicPtr<()>, } -impl Handler { +impl DynHandler { pub const fn new() -> Self { Self { func: AtomicPtr::new(ptr::null_mut()), @@ -51,7 +51,7 @@ pub unsafe trait Interrupt: Peripheral

{ /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] - unsafe fn __handler(&self) -> &'static Handler; + unsafe fn __handler(&self) -> &'static DynHandler; } /// Represents additional behavior for all interrupts. diff --git a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs index ebbb47cd7..699883efa 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs @@ -21,9 +21,9 @@ pub fn run(name: syn::Ident) -> Result { unsafe fn steal() -> Self { Self(()) } - unsafe fn __handler(&self) -> &'static ::embassy_cortex_m::interrupt::Handler { + unsafe fn __handler(&self) -> &'static ::embassy_cortex_m::interrupt::DynHandler { #[export_name = #name_handler] - static HANDLER: ::embassy_cortex_m::interrupt::Handler = ::embassy_cortex_m::interrupt::Handler::new(); + static HANDLER: ::embassy_cortex_m::interrupt::DynHandler = ::embassy_cortex_m::interrupt::DynHandler::new(); &HANDLER } } diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index d30189ce3..e2ebf98c7 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs @@ -30,7 +30,7 @@ pub fn run(name: syn::Ident) -> Result { pub unsafe extern "C" fn trampoline() { extern "C" { #[link_name = #name_handler] - static HANDLER: interrupt::Handler; + static HANDLER: interrupt::DynHandler; } let func = HANDLER.func.load(interrupt::_export::atomic::Ordering::Relaxed); From 42c13c8c3d9514866c2842009f76e88e8cb01b22 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Feb 2023 22:42:22 +0100 Subject: [PATCH 0653/1575] nrf: add new interrupt binding traits and macro. --- embassy-cortex-m/src/interrupt.rs | 30 ++++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 33 +++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index ead9d52fe..3a82726df 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -13,6 +13,36 @@ pub mod _export { pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare}; } +/// Interrupt handler trait. +/// +/// Drivers that need to handle interrupts implement this trait. +/// The user must ensure `on_interrupt()` is called every time the interrupt fires. +/// Drivers must use use [`Binding`] to assert at compile time that the user has done so. +pub trait Handler { + /// Interrupt handler function. + /// + /// Must be called every time the `I` interrupt fires, synchronously from + /// the interrupt handler context. + /// + /// # Safety + /// + /// This function must ONLY be called from the interrupt handler for `I`. + unsafe fn on_interrupt(); +} + +/// Compile-time assertion that an interrupt has been bound to a handler. +/// +/// For the vast majority of cases, you should use the `bind_interrupts!` +/// macro instead of writing `unsafe impl`s of this trait. +/// +/// # Safety +/// +/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` +/// to be called every time the `I` interrupt fires. +/// +/// This allows drivers to check bindings at compile-time. +pub unsafe trait Binding> {} + /// Implementation detail, do not use outside embassy crates. #[doc(hidden)] pub struct DynHandler { diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 3c5db5c7c..446b1f41a 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -95,14 +95,39 @@ pub mod wdt; #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] mod chip; -pub use chip::EASY_DMA_SIZE; - pub mod interrupt { - //! nRF interrupts for cortex-m devices. + //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; pub use embassy_cortex_m::interrupt::*; pub use crate::chip::irqs::*; + + /// Macro to bind interrupts to handlers. + /// + /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) + /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to + /// prove at compile-time that the right interrupts have been bound. + // developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. + #[macro_export] + macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); + )* + } + + $( + unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} + )* + )* + }; + } } // Reexports @@ -111,7 +136,7 @@ pub mod interrupt { pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; -pub use chip::{peripherals, Peripherals}; +pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; pub use embassy_cortex_m::executor; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; From 9cf000ef4edd8f230b348ede8d7ce015045a0035 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:17:52 +0100 Subject: [PATCH 0654/1575] nrf/uart: switch to new interrupt binding. --- embassy-nrf/src/buffered_uarte.rs | 411 +++++++++++---------- embassy-nrf/src/uarte.rs | 85 ++--- examples/nrf52840/src/bin/buffered_uart.rs | 11 +- examples/nrf52840/src/bin/uart.rs | 9 +- examples/nrf52840/src/bin/uart_idle.rs | 10 +- examples/nrf52840/src/bin/uart_split.rs | 9 +- examples/nrf5340/src/bin/uart.rs | 10 +- tests/nrf/src/bin/buffered_uart.rs | 10 +- tests/nrf/src/bin/buffered_uart_spam.rs | 13 +- 9 files changed, 299 insertions(+), 269 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index ab639aeea..75f93f904 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -10,6 +10,7 @@ use core::cmp::min; use core::future::poll_fn; +use core::marker::PhantomData; use core::slice; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; @@ -23,7 +24,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::gpio::sealed::Pin; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::InterruptExt; +use crate::interrupt::{self, InterruptExt}; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; @@ -71,211 +72,13 @@ impl State { } } -/// Buffered UARTE driver. -pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { - _peri: PeripheralRef<'d, U>, - timer: Timer<'d, T>, - _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, - _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, - _ppi_group: PpiGroup<'d, AnyGroup>, +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} - -impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { - /// Create a new BufferedUarte without hardware flow control. - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - pub fn new( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - ppi_group: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); - Self::new_inner( - uarte, - timer, - ppi_ch1.map_into(), - ppi_ch2.map_into(), - ppi_group.map_into(), - irq, - rxd.map_into(), - txd.map_into(), - None, - None, - config, - rx_buffer, - tx_buffer, - ) - } - - /// Create a new BufferedUarte with hardware flow control (RTS/CTS) - /// - /// # Panics - /// - /// Panics if `rx_buffer.len()` is odd. - pub fn new_with_rtscts( - uarte: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: impl Peripheral

+ 'd, - ppi_ch2: impl Peripheral

+ 'd, - ppi_group: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - rxd: impl Peripheral

+ 'd, - txd: impl Peripheral

+ 'd, - cts: impl Peripheral

+ 'd, - rts: impl Peripheral

+ 'd, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); - Self::new_inner( - uarte, - timer, - ppi_ch1.map_into(), - ppi_ch2.map_into(), - ppi_group.map_into(), - irq, - rxd.map_into(), - txd.map_into(), - Some(cts.map_into()), - Some(rts.map_into()), - config, - rx_buffer, - tx_buffer, - ) - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - timer: impl Peripheral

+ 'd, - ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, - ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, - ppi_group: PeripheralRef<'d, AnyGroup>, - irq: impl Peripheral

+ 'd, - rxd: PeripheralRef<'d, AnyPin>, - txd: PeripheralRef<'d, AnyPin>, - cts: Option>, - rts: Option>, - config: Config, - rx_buffer: &'d mut [u8], - tx_buffer: &'d mut [u8], - ) -> Self { - into_ref!(peri, timer, irq); - - assert!(rx_buffer.len() % 2 == 0); - - let r = U::regs(); - - rxd.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); - - txd.set_high(); - txd.conf().write(|w| w.dir().output().drive().h0h1()); - r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - - if let Some(pin) = &cts { - pin.conf().write(|w| w.input().connect().drive().h0h1()); - } - r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); - - if let Some(pin) = &rts { - pin.set_high(); - pin.conf().write(|w| w.dir().output().drive().h0h1()); - } - r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - - // Initialize state - let s = U::buffered_state(); - s.tx_count.store(0, Ordering::Relaxed); - s.rx_bufs.store(0, Ordering::Relaxed); - let len = tx_buffer.len(); - unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let len = rx_buffer.len(); - unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - - // Configure - r.config.write(|w| { - w.hwfc().bit(false); - w.parity().variant(config.parity); - w - }); - r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); - - // clear errors - let errors = r.errorsrc.read().bits(); - r.errorsrc.write(|w| unsafe { w.bits(errors) }); - - r.events_rxstarted.reset(); - r.events_txstarted.reset(); - r.events_error.reset(); - r.events_endrx.reset(); - r.events_endtx.reset(); - - // Enable interrupts - r.intenclr.write(|w| unsafe { w.bits(!0) }); - r.intenset.write(|w| { - w.endtx().set(); - w.rxstarted().set(); - w.error().set(); - w - }); - - // Enable UARTE instance - apply_workaround_for_enable_anomaly(&r); - r.enable.write(|w| w.enable().enabled()); - - // Configure byte counter. - let mut timer = Timer::new_counter(timer); - timer.cc(1).write(rx_buffer.len() as u32 * 2); - timer.cc(1).short_compare_clear(); - timer.clear(); - timer.start(); - - let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); - ppi_ch1.enable(); - - s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); - let mut ppi_group = PpiGroup::new(ppi_group); - let mut ppi_ch2 = Ppi::new_one_to_two( - ppi_ch2, - Event::from_reg(&r.events_endrx), - Task::from_reg(&r.tasks_startrx), - ppi_group.task_disable_all(), - ); - ppi_ch2.disable(); - ppi_group.add_channel(&ppi_ch2); - - irq.disable(); - irq.set_handler(Self::on_interrupt); - irq.pend(); - irq.enable(); - - Self { - _peri: peri, - timer, - _ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - _ppi_group: ppi_group, - } - } - - fn pend_irq() { - unsafe { ::steal() }.pend() - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { //trace!("irq: start"); let r = U::regs(); let s = U::buffered_state(); @@ -374,6 +177,206 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { //trace!("irq: end"); } +} + +/// Buffered UARTE driver. +pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { + _peri: PeripheralRef<'d, U>, + timer: Timer<'d, T>, + _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, + _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, + _ppi_group: PpiGroup<'d, AnyGroup>, +} + +impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} + +impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { + /// Create a new BufferedUarte without hardware flow control. + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + rxd.map_into(), + txd.map_into(), + None, + None, + config, + rx_buffer, + tx_buffer, + ) + } + + /// Create a new BufferedUarte with hardware flow control (RTS/CTS) + /// + /// # Panics + /// + /// Panics if `rx_buffer.len()` is odd. + pub fn new_with_rtscts( + uarte: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ppi_group: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + rxd: impl Peripheral

+ 'd, + txd: impl Peripheral

+ 'd, + cts: impl Peripheral

+ 'd, + rts: impl Peripheral

+ 'd, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group); + Self::new_inner( + uarte, + timer, + ppi_ch1.map_into(), + ppi_ch2.map_into(), + ppi_group.map_into(), + rxd.map_into(), + txd.map_into(), + Some(cts.map_into()), + Some(rts.map_into()), + config, + rx_buffer, + tx_buffer, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + timer: impl Peripheral

+ 'd, + ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>, + ppi_group: PeripheralRef<'d, AnyGroup>, + rxd: PeripheralRef<'d, AnyPin>, + txd: PeripheralRef<'d, AnyPin>, + cts: Option>, + rts: Option>, + config: Config, + rx_buffer: &'d mut [u8], + tx_buffer: &'d mut [u8], + ) -> Self { + into_ref!(peri, timer); + + assert!(rx_buffer.len() % 2 == 0); + + let r = U::regs(); + + rxd.conf().write(|w| w.input().connect().drive().h0h1()); + r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); + + txd.set_high(); + txd.conf().write(|w| w.dir().output().drive().h0h1()); + r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); + + if let Some(pin) = &cts { + pin.conf().write(|w| w.input().connect().drive().h0h1()); + } + r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); + + if let Some(pin) = &rts { + pin.set_high(); + pin.conf().write(|w| w.dir().output().drive().h0h1()); + } + r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + + // Initialize state + let s = U::buffered_state(); + s.tx_count.store(0, Ordering::Relaxed); + s.rx_bufs.store(0, Ordering::Relaxed); + let len = tx_buffer.len(); + unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + + // Configure + r.config.write(|w| { + w.hwfc().bit(false); + w.parity().variant(config.parity); + w + }); + r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); + + // clear errors + let errors = r.errorsrc.read().bits(); + r.errorsrc.write(|w| unsafe { w.bits(errors) }); + + r.events_rxstarted.reset(); + r.events_txstarted.reset(); + r.events_error.reset(); + r.events_endrx.reset(); + r.events_endtx.reset(); + + // Enable interrupts + r.intenclr.write(|w| unsafe { w.bits(!0) }); + r.intenset.write(|w| { + w.endtx().set(); + w.rxstarted().set(); + w.error().set(); + w + }); + + // Enable UARTE instance + apply_workaround_for_enable_anomaly(&r); + r.enable.write(|w| w.enable().enabled()); + + // Configure byte counter. + let mut timer = Timer::new_counter(timer); + timer.cc(1).write(rx_buffer.len() as u32 * 2); + timer.cc(1).short_compare_clear(); + timer.clear(); + timer.start(); + + let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); + ppi_ch1.enable(); + + s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); + let mut ppi_group = PpiGroup::new(ppi_group); + let mut ppi_ch2 = Ppi::new_one_to_two( + ppi_ch2, + Event::from_reg(&r.events_endrx), + Task::from_reg(&r.tasks_startrx), + ppi_group.task_disable_all(), + ); + ppi_ch2.disable(); + ppi_group.add_channel(&ppi_ch2); + + unsafe { U::Interrupt::steal() }.pend(); + unsafe { U::Interrupt::steal() }.enable(); + + Self { + _peri: peri, + timer, + _ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + _ppi_group: ppi_group, + } + } + + fn pend_irq() { + unsafe { ::steal() }.pend() + } /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 00afbd059..3934d1b55 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -14,6 +14,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -26,7 +27,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; @@ -62,6 +63,27 @@ pub enum Error { BufferNotInRAM, } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_endrx.read().bits() != 0 { + s.endrx_waker.wake(); + r.intenclr.write(|w| w.endrx().clear()); + } + if r.events_endtx.read().bits() != 0 { + s.endtx_waker.wake(); + r.intenclr.write(|w| w.endtx().clear()); + } + } +} + /// UARTE driver. pub struct Uarte<'d, T: Instance> { tx: UarteTx<'d, T>, @@ -86,19 +108,19 @@ impl<'d, T: Instance> Uarte<'d, T> { /// Create a new UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd, txd); - Self::new_inner(uarte, irq, rxd.map_into(), txd.map_into(), None, None, config) + Self::new_inner(uarte, rxd.map_into(), txd.map_into(), None, None, config) } /// Create a new UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, @@ -108,7 +130,6 @@ impl<'d, T: Instance> Uarte<'d, T> { into_ref!(rxd, txd, cts, rts); Self::new_inner( uarte, - irq, rxd.map_into(), txd.map_into(), Some(cts.map_into()), @@ -119,14 +140,13 @@ impl<'d, T: Instance> Uarte<'d, T> { fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rxd: PeripheralRef<'d, AnyPin>, txd: PeripheralRef<'d, AnyPin>, cts: Option>, rts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -148,9 +168,8 @@ impl<'d, T: Instance> Uarte<'d, T> { } r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let hardware_flow_control = match (rts.is_some(), cts.is_some()) { (false, false) => false, @@ -238,20 +257,6 @@ impl<'d, T: Instance> Uarte<'d, T> { Event::from_reg(&r.events_endtx) } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_endrx.read().bits() != 0 { - s.endrx_waker.wake(); - r.intenclr.write(|w| w.endrx().clear()); - } - if r.events_endtx.read().bits() != 0 { - s.endtx_waker.wake(); - r.intenclr.write(|w| w.endtx().clear()); - } - } - /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.read(buffer).await @@ -308,34 +313,33 @@ impl<'d, T: Instance> UarteTx<'d, T> { /// Create a new tx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, txd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(txd); - Self::new_inner(uarte, irq, txd.map_into(), None, config) + Self::new_inner(uarte, txd.map_into(), None, config) } /// Create a new tx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(txd, cts); - Self::new_inner(uarte, irq, txd.map_into(), Some(cts.map_into()), config) + Self::new_inner(uarte, txd.map_into(), Some(cts.map_into()), config) } fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, txd: PeripheralRef<'d, AnyPin>, cts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -354,9 +358,8 @@ impl<'d, T: Instance> UarteTx<'d, T> { let hardware_flow_control = cts.is_some(); configure(r, config, hardware_flow_control); - irq.set_handler(Uarte::::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); @@ -506,34 +509,33 @@ impl<'d, T: Instance> UarteRx<'d, T> { /// Create a new rx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd); - Self::new_inner(uarte, irq, rxd.map_into(), None, config) + Self::new_inner(uarte, rxd.map_into(), None, config) } /// Create a new rx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rxd: impl Peripheral

+ 'd, rts: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(rxd, rts); - Self::new_inner(uarte, irq, rxd.map_into(), Some(rts.map_into()), config) + Self::new_inner(uarte, rxd.map_into(), Some(rts.map_into()), config) } fn new_inner( uarte: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rxd: PeripheralRef<'d, AnyPin>, rts: Option>, config: Config, ) -> Self { - into_ref!(uarte, irq); + into_ref!(uarte); let r = T::regs(); @@ -549,9 +551,8 @@ impl<'d, T: Instance> UarteRx<'d, T> { r.psel.txd.write(|w| w.connect().disconnected()); r.psel.cts.write(|w| w.connect().disconnected()); - irq.set_handler(Uarte::::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let hardware_flow_control = rts.is_some(); configure(r, config, hardware_flow_control); diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 5b934b7d6..238695371 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -4,11 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::BufferedUarte; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use embedded_io::asynch::Write; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -19,14 +23,13 @@ async fn main(_spawner: Spawner) { let mut tx_buffer = [0u8; 4096]; let mut rx_buffer = [0u8; 4096]; - let irq = interrupt::take!(UARTE0_UART0); let mut u = BufferedUarte::new( p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - irq, + Irqs, p.P0_08, p.P0_06, config, diff --git a/examples/nrf52840/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs index 600f7a6ef..50d5cab8c 100644 --- a/examples/nrf52840/src/bin/uart.rs +++ b/examples/nrf52840/src/bin/uart.rs @@ -4,9 +4,13 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +18,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let mut uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let mut uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); info!("uarte initialized!"); diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs index 6af4f7097..e1f42fa6c 100644 --- a/examples/nrf52840/src/bin/uart_idle.rs +++ b/examples/nrf52840/src/bin/uart_idle.rs @@ -4,9 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::peripherals::UARTE0; +use embassy_nrf::{bind_interrupts, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +19,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); info!("uarte initialized!"); diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 1adaf53fd..9979a1d53 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -6,13 +6,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::peripherals::UARTE0; use embassy_nrf::uarte::UarteRx; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::{bind_interrupts, uarte}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use {defmt_rtt as _, panic_probe as _}; static CHANNEL: Channel = Channel::new(); +bind_interrupts!(struct Irqs { + UARTE0_UART0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -20,8 +24,7 @@ async fn main(spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(UARTE0_UART0); - let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); + let uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config); let (mut tx, rx) = uart.split(); info!("uarte initialized!"); diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs index 5f448c2ba..d68539702 100644 --- a/examples/nrf5340/src/bin/uart.rs +++ b/examples/nrf5340/src/bin/uart.rs @@ -4,9 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::peripherals::SERIAL0; +use embassy_nrf::{bind_interrupts, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SERIAL0 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -14,8 +19,7 @@ async fn main(_spawner: Spawner) { config.parity = uarte::Parity::EXCLUDED; config.baudrate = uarte::Baudrate::BAUD115200; - let irq = interrupt::take!(SERIAL0); - let mut uart = uarte::Uarte::new(p.SERIAL0, irq, p.P1_00, p.P1_01, config); + let mut uart = uarte::Uarte::new(p.SERIAL0, Irqs, p.P1_00, p.P1_01, config); info!("uarte initialized!"); diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs index 0550b0bb7..e73d4f0b0 100644 --- a/tests/nrf/src/bin/buffered_uart.rs +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -5,10 +5,14 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::buffered_uarte::BufferedUarte; -use embassy_nrf::{interrupt, uarte}; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; +use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -25,7 +29,7 @@ async fn main(_spawner: Spawner) { p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - interrupt::take!(UARTE0_UART0), + Irqs, p.P1_03, p.P1_02, config.clone(), diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 57aaeca45..74eda6d01 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -7,14 +7,19 @@ use core::ptr::NonNull; use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_nrf::buffered_uarte::BufferedUarte; +use embassy_nrf::buffered_uarte::{self, BufferedUarte}; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::ppi::{Event, Ppi, Task}; use embassy_nrf::uarte::Uarte; -use embassy_nrf::{interrupt, pac, uarte}; +use embassy_nrf::{bind_interrupts, pac, peripherals, uarte}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UARTE0_UART0 => buffered_uarte::InterruptHandler; + UARTE1 => uarte::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut p = embassy_nrf::init(Default::default()); @@ -33,7 +38,7 @@ async fn main(_spawner: Spawner) { p.PPI_CH0, p.PPI_CH1, p.PPI_GROUP0, - interrupt::take!(UARTE0_UART0), + Irqs, p.P1_03, p.P1_04, config.clone(), @@ -49,7 +54,7 @@ async fn main(_spawner: Spawner) { // Tx spam in a loop. const NSPAM: usize = 17; static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - let _spam = Uarte::new(p.UARTE1, interrupt::take!(UARTE1), p.P1_01, p.P1_02, config.clone()); + let _spam = Uarte::new(p.UARTE1, Irqs, p.P1_01, p.P1_02, config.clone()); let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) }; let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) }; let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; From 63b75eaf644eee2da2c16f72dbd46bb404f5fdbd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:27:33 +0100 Subject: [PATCH 0655/1575] nrf/timer: remove awaitable. --- embassy-nrf/src/timer.rs | 134 ++----------------- examples/nrf52840/src/bin/awaitable_timer.rs | 26 ---- 2 files changed, 10 insertions(+), 150 deletions(-) delete mode 100644 examples/nrf52840/src/bin/awaitable_timer.rs diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3b0d2f1ca..a9487a9fd 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -6,15 +6,9 @@ #![macro_use] -use core::future::poll_fn; -use core::marker::PhantomData; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; @@ -26,8 +20,6 @@ pub(crate) mod sealed { /// The number of CC registers this instance has. const CCS: usize; fn regs() -> &'static pac::timer0::RegisterBlock; - /// Storage for the waker for CC register `n`. - fn waker(n: usize) -> &'static AtomicWaker; } pub trait ExtendedInstance {} @@ -50,12 +42,6 @@ macro_rules! impl_timer { fn regs() -> &'static pac::timer0::RegisterBlock { unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } } - fn waker(n: usize) -> &'static ::embassy_sync::waitqueue::AtomicWaker { - use ::embassy_sync::waitqueue::AtomicWaker; - const NEW_AW: AtomicWaker = AtomicWaker::new(); - static WAKERS: [AtomicWaker; $ccs] = [NEW_AW; $ccs]; - &WAKERS[n] - } } impl crate::timer::Instance for peripherals::$type { type Interrupt = crate::interrupt::$irq; @@ -99,59 +85,18 @@ pub enum Frequency { /// nRF Timer driver. /// /// The timer has an internal counter, which is incremented for every tick of the timer. -/// The counter is 32-bit, so it wraps back to 0 at 4294967296. +/// The counter is 32-bit, so it wraps back to 0 when it reaches 2^32. /// /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter /// or trigger an event when the counter reaches a certain value. -pub trait TimerType: sealed::TimerType {} - -/// Marker type indicating the timer driver can await expiration (it owns the timer interrupt). -pub enum Awaitable {} - -/// Marker type indicating the timer driver cannot await expiration (it does not own the timer interrupt). -pub enum NotAwaitable {} - -impl sealed::TimerType for Awaitable {} -impl sealed::TimerType for NotAwaitable {} -impl TimerType for Awaitable {} -impl TimerType for NotAwaitable {} - /// Timer driver. -pub struct Timer<'d, T: Instance, I: TimerType = NotAwaitable> { +pub struct Timer<'d, T: Instance> { _p: PeripheralRef<'d, T>, - _i: PhantomData, } -impl<'d, T: Instance> Timer<'d, T, Awaitable> { - /// Create a new async-capable timer driver. - pub fn new_awaitable(timer: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner(timer, false) - } - - /// Create a new async-capable timer driver in counter mode. - pub fn new_awaitable_counter( - timer: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - ) -> Self { - into_ref!(irq); - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self::new_inner(timer, true) - } -} - -impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { - /// Create a `Timer` driver without an interrupt, meaning `Cc::wait` won't work. +impl<'d, T: Instance> Timer<'d, T> { + /// Create a new `Timer` driver. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. @@ -159,28 +104,20 @@ impl<'d, T: Instance> Timer<'d, T, NotAwaitable> { Self::new_inner(timer, false) } - /// Create a `Timer` driver in counter mode without an interrupt, meaning `Cc::wait` won't work. + /// Create a new `Timer` driver in counter mode. /// /// This can be useful for triggering tasks via PPI /// `Uarte` uses this internally. pub fn new_counter(timer: impl Peripheral

+ 'd) -> Self { Self::new_inner(timer, true) } -} -impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { - /// Create a `Timer` without an interrupt, meaning `Cc::wait` won't work. - /// - /// This is used by the public constructors. fn new_inner(timer: impl Peripheral

+ 'd, is_counter: bool) -> Self { into_ref!(timer); let regs = T::regs(); - let mut this = Self { - _p: timer, - _i: PhantomData, - }; + let mut this = Self { _p: timer }; // Stop the timer before doing anything else, // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. @@ -272,31 +209,17 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { .write(|w| unsafe { w.prescaler().bits(frequency as u8) }) } - fn on_interrupt(_: *mut ()) { - let regs = T::regs(); - for n in 0..T::CCS { - if regs.events_compare[n].read().bits() != 0 { - // Clear the interrupt, otherwise the interrupt will be repeatedly raised as soon as the interrupt handler exits. - // We can't clear the event, because it's used to poll whether the future is done or still pending. - regs.intenclr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + n))) }); - T::waker(n).wake(); - } - } - } - /// Returns this timer's `n`th CC register. /// /// # Panics /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer). - pub fn cc(&mut self, n: usize) -> Cc { + pub fn cc(&mut self, n: usize) -> Cc { if n >= T::CCS { panic!("Cannot get CC register {} of timer with {} CC registers.", n, T::CCS); } Cc { n, _p: self._p.reborrow(), - _i: PhantomData, } } } @@ -308,49 +231,12 @@ impl<'d, T: Instance, I: TimerType> Timer<'d, T, I> { /// /// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register. /// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register -pub struct Cc<'d, T: Instance, I: TimerType = NotAwaitable> { +pub struct Cc<'d, T: Instance> { n: usize, _p: PeripheralRef<'d, T>, - _i: PhantomData, } -impl<'d, T: Instance> Cc<'d, T, Awaitable> { - /// Wait until the timer's counter reaches the value stored in this register. - /// - /// This requires a mutable reference so that this task's waker cannot be overwritten by a second call to `wait`. - pub async fn wait(&mut self) { - let regs = T::regs(); - - // Enable the interrupt for this CC's COMPARE event. - regs.intenset - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); - - // Disable the interrupt if the future is dropped. - let on_drop = OnDrop::new(|| { - regs.intenclr - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (16 + self.n))) }); - }); - - poll_fn(|cx| { - T::waker(self.n).register(cx.waker()); - - if regs.events_compare[self.n].read().bits() != 0 { - // Reset the register for next time - regs.events_compare[self.n].reset(); - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - // The interrupt was already disabled in the interrupt handler, so there's no need to disable it again. - on_drop.defuse(); - } -} -impl<'d, T: Instance> Cc<'d, T, NotAwaitable> {} - -impl<'d, T: Instance, I: TimerType> Cc<'d, T, I> { +impl<'d, T: Instance> Cc<'d, T> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { T::regs().cc[self.n].read().cc().bits() diff --git a/examples/nrf52840/src/bin/awaitable_timer.rs b/examples/nrf52840/src/bin/awaitable_timer.rs deleted file mode 100644 index b32af236c..000000000 --- a/examples/nrf52840/src/bin/awaitable_timer.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::info; -use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::timer::Timer; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); - // default frequency is 1MHz, so this triggers every second - t.cc(0).write(1_000_000); - // clear the timer value on cc[0] compare match - t.cc(0).short_compare_clear(); - t.start(); - - loop { - // wait for compare match - t.cc(0).wait().await; - info!("hardware timer tick"); - } -} From 34563b74aa48c53622344541153266b0227fc9bf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:40:13 +0100 Subject: [PATCH 0656/1575] nrf/i2s: switch to new interrupt binding. --- embassy-nrf/src/i2s.rs | 77 ++++++++++++----------- examples/nrf52840/src/bin/i2s_effect.rs | 15 +++-- examples/nrf52840/src/bin/i2s_monitor.rs | 9 ++- examples/nrf52840/src/bin/i2s_waveform.rs | 9 ++- 4 files changed, 59 insertions(+), 51 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 770df7c89..8a1188ce4 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -14,7 +14,7 @@ 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::interrupt::{self, Interrupt}; use crate::pac::i2s::RegisterBlock; use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::{Peripheral, EASY_DMA_SIZE}; @@ -363,10 +363,39 @@ impl From for u8 { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + 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(); + } + } +} + /// I2S driver. 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>, @@ -378,19 +407,18 @@ pub struct I2S<'d, T: Instance> { impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S in master mode - pub fn master( + pub fn new_master( i2s: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + '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); + into_ref!(i2s, mck, sck, lrck); Self { i2s, - irq, mck: Some(mck.map_into()), sck: sck.map_into(), lrck: lrck.map_into(), @@ -402,17 +430,16 @@ impl<'d, T: Instance> I2S<'d, T> { } /// Create a new I2S in slave mode - pub fn slave( + pub fn new_slave( i2s: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(i2s, irq, sck, lrck); + into_ref!(i2s, sck, lrck); Self { i2s, - irq, mck: None, sck: sck.map_into(), lrck: lrck.map_into(), @@ -537,9 +564,8 @@ impl<'d, T: Instance> I2S<'d, T> { } fn setup_interrupt(&self) { - self.irq.set_handler(Self::on_interrupt); - self.irq.unpend(); - self.irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let device = Device::::new(); device.disable_tx_ptr_interrupt(); @@ -555,29 +581,6 @@ impl<'d, T: Instance> I2S<'d, T> { 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); @@ -1168,7 +1171,7 @@ pub(crate) mod sealed { } } -/// I2S peripehral instance. +/// I2S peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: Interrupt; diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs index 52d46e4f9..391514d93 100644 --- a/examples/nrf52840/src/bin/i2s_effect.rs +++ b/examples/nrf52840/src/bin/i2s_effect.rs @@ -7,7 +7,7 @@ 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 embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; @@ -15,6 +15,10 @@ type Sample = i16; const NUM_BUFFERS: usize = 2; const NUM_SAMPLES: usize = 4; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -28,15 +32,10 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.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 full_duplex_stream = I2S::new_master(p.I2S, Irqs, 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); diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs index 5ebfd9542..4ed597c0d 100644 --- a/examples/nrf52840/src/bin/i2s_monitor.rs +++ b/examples/nrf52840/src/bin/i2s_monitor.rs @@ -5,14 +5,18 @@ 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 embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; const NUM_SAMPLES: usize = 500; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,10 +30,9 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.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); + I2S::new_master(p.I2S, Irqs, 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); diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs index eda930677..f2c1166b1 100644 --- a/examples/nrf52840/src/bin/i2s_waveform.rs +++ b/examples/nrf52840/src/bin/i2s_waveform.rs @@ -7,13 +7,17 @@ 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 embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; type Sample = i16; const NUM_SAMPLES: usize = 50; +bind_interrupts!(struct Irqs { + I2S => i2s::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -27,10 +31,9 @@ async fn main(_spawner: Spawner) { config.sample_width = SampleWidth::_16bit; config.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); + I2S::new_master(p.I2S, Irqs, 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); From f8f1d3bcf09045616f5b63a08a9623cd14acd045 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 20:50:45 +0100 Subject: [PATCH 0657/1575] nrf/pdm: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 5 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_app.rs | 7 +- embassy-nrf/src/chips/nrf9160.rs | 2 + embassy-nrf/src/lib.rs | 2 + embassy-nrf/src/pdm.rs | 131 ++++++++++++++++++--------- examples/nrf52840/src/bin/pdm.rs | 10 +- 10 files changed, 116 insertions(+), 49 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 6b5c134b8..c6ccb6a0e 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -148,6 +148,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index c5de9a447..7d1bce1c0 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -150,6 +150,8 @@ impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + 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 c2b23fc5b..ce19a18e8 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -146,6 +146,9 @@ embassy_hal_common::peripherals! { // I2S I2S, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -168,6 +171,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 95f71ade7..08b82021d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -197,6 +197,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 5e7479e88..4e8b6d9ee 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -208,6 +208,8 @@ impl_timer!(TIMER4, TIMER4, TIMER4, extended); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM, PDM, PDM); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 9c7b738e6..050612b1c 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -34,7 +34,7 @@ pub mod pac { nvmc_ns as nvmc, oscillators_ns as oscillators, p0_ns as p0, - pdm0_ns as pdm0, + pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, qdec0_ns as qdec0, @@ -253,6 +253,9 @@ embassy_hal_common::peripherals! { // QSPI QSPI, + // PDM + PDM0, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -398,6 +401,8 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM0, PDM0, PDM0); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 385bd133d..d2b45114f 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -301,6 +301,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 446b1f41a..17aa5ad15 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,8 +47,10 @@ pub mod nvmc; #[cfg(any( feature = "nrf52810", feature = "nrf52811", + feature = "nrf52832", feature = "nrf52833", feature = "nrf52840", + feature = "_nrf5340-app", feature = "_nrf9160" ))] pub mod pdm; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 54feca4c1..8815bb316 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,25 +1,37 @@ //! Pulse Density Modulation (PDM) mirophone driver. +#![macro_use] + use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::chip::EASY_DMA_SIZE; use crate::gpio::sealed::Pin; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::{self, InterruptExt}; -use crate::peripherals::PDM; -use crate::{pac, Peripheral}; +use crate::Peripheral; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.end().clear()); + T::state().waker.wake(); + } +} /// PDM microphone interface -pub struct Pdm<'d> { - irq: PeripheralRef<'d, interrupt::PDM>, - phantom: PhantomData<&'d PDM>, +pub struct Pdm<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, } /// PDM error. @@ -35,32 +47,30 @@ pub enum Error { NotRunning, } -static WAKER: AtomicWaker = AtomicWaker::new(); static DUMMY_BUFFER: [i16; 1] = [0; 1]; -impl<'d> Pdm<'d> { +impl<'d, T: Instance> Pdm<'d, T> { /// Create PDM driver pub fn new( - pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, clk: impl Peripheral

+ 'd, din: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(clk, din); - Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config) + into_ref!(pdm, clk, din); + Self::new_inner(pdm, clk.map_into(), din.map_into(), config) } fn new_inner( - _pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: PeripheralRef<'d, T>, clk: PeripheralRef<'d, AnyPin>, din: PeripheralRef<'d, AnyPin>, config: Config, ) -> Self { - into_ref!(irq); + into_ref!(pdm); - let r = Self::regs(); + let r = T::regs(); // setup gpio pins din.conf().write(|w| w.input().set_bit()); @@ -84,26 +94,18 @@ impl<'d> Pdm<'d> { r.gainr.write(|w| w.gainr().default_gain()); // IRQ - irq.disable(); - irq.set_handler(|_| { - let r = Self::regs(); - r.intenclr.write(|w| w.end().clear()); - WAKER.wake(); - }); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); r.enable.write(|w| w.enable().set_bit()); - Self { - phantom: PhantomData, - irq, - } + Self { _peri: pdm } } /// Start sampling microphon data into a dummy buffer /// Usefull to start the microphon and keep it active between recording samples pub async fn start(&mut self) { - let r = Self::regs(); + let r = T::regs(); // start dummy sampling because microphon needs some setup time r.sample @@ -113,13 +115,13 @@ impl<'d> Pdm<'d> { .maxcnt .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - r.tasks_start.write(|w| w.tasks_start().set_bit()); + r.tasks_start.write(|w| unsafe { w.bits(1) }); } /// Stop sampling microphon data inta a dummy buffer pub async fn stop(&mut self) { - let r = Self::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + let r = T::regs(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.events_started.reset(); } @@ -132,9 +134,9 @@ impl<'d> Pdm<'d> { return Err(Error::BufferTooLong); } - let r = Self::regs(); + let r = T::regs(); - if r.events_started.read().events_started().bit_is_clear() { + if r.events_started.read().bits() == 0 { return Err(Error::NotRunning); } @@ -179,7 +181,7 @@ impl<'d> Pdm<'d> { } async fn wait_for_sample() { - let r = Self::regs(); + let r = T::regs(); r.events_end.reset(); r.intenset.write(|w| w.end().set()); @@ -187,8 +189,8 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); poll_fn(|cx| { - WAKER.register(cx.waker()); - if r.events_end.read().events_end().bit_is_set() { + T::state().waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { return Poll::Ready(()); } Poll::Pending @@ -197,10 +199,6 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); } - - fn regs() -> &'static pac::pdm::RegisterBlock { - unsafe { &*pac::PDM::ptr() } - } } /// PDM microphone driver Config @@ -238,13 +236,11 @@ pub enum Edge { LeftFalling, } -impl<'d> Drop for Pdm<'d> { +impl<'d, T: Instance> Drop for Pdm<'d, T> { fn drop(&mut self) { - let r = Self::regs(); + let r = T::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); - - self.irq.disable(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.enable.write(|w| w.enable().disabled()); @@ -252,3 +248,48 @@ impl<'d> Drop for Pdm<'d> { r.psel.clk.reset(); } } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn state() -> &'static State; + } +} + +/// PDM peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_pdm { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::pdm::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::pdm::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::pdm::sealed::State { + static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); + &STATE + } + } + impl crate::pdm::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 7388580fb..6b41320ca 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -4,16 +4,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::pdm::{Config, Pdm}; +use embassy_nrf::pdm::{self, Config, Pdm}; +use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PDM => pdm::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let p = embassy_nrf::init(Default::default()); let config = Config::default(); - let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); + let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); loop { pdm.start().await; From c66b28e759dc42c5f802336385a66eb8a82dab9a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:28:13 +0100 Subject: [PATCH 0658/1575] nrf/qdec: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 2 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_app.rs | 9 ++- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/qdec.rs | 112 +++++++++++++++++++-------- examples/nrf52840/src/bin/qdec.rs | 9 ++- 11 files changed, 108 insertions(+), 38 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 3c74a2a63..185cda430 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -140,6 +140,8 @@ impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); +impl_qdec!(QDEC, QDEC, QDEC); + 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 c6ccb6a0e..1e3c054a4 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -150,6 +150,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 7d1bce1c0..3bb44171e 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -152,6 +152,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 81b07f32c..b28778f33 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -153,6 +153,8 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index ce19a18e8..00dc9fd8f 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -173,6 +173,8 @@ impl_pwm!(PWM2, PWM2, PWM2); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 08b82021d..345608c9d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -199,6 +199,8 @@ impl_pwm!(PWM3, PWM3, PWM3); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 4e8b6d9ee..630f01aa9 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -210,6 +210,8 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM, PDM, PDM); +impl_qdec!(QDEC, QDEC, QDEC); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 050612b1c..34f96800f 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -37,7 +37,7 @@ pub mod pac { pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, - qdec0_ns as qdec0, + qdec0_ns as qdec, qspi_ns as qspi, regulators_ns as regulators, reset_ns as reset, @@ -256,6 +256,10 @@ embassy_hal_common::peripherals! { // PDM PDM0, + // QDEC + QDEC0, + QDEC1, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -403,6 +407,9 @@ impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM0, PDM0, PDM0); +impl_qdec!(QDEC0, QDEC0, QDEC0); +impl_qdec!(QDEC1, QDEC1, QDEC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 17aa5ad15..feefa2486 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -57,7 +57,7 @@ pub mod pdm; pub mod ppi; #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod pwm; -#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))] +#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))] pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index c01babca3..4d2a09198 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,20 +1,22 @@ //! Quadrature decoder (QDEC) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::InterruptExt; -use crate::peripherals::QDEC; -use crate::{interrupt, pac, Peripheral}; +use crate::{interrupt, Peripheral}; /// Quadrature decoder driver. -pub struct Qdec<'d> { - _p: PeripheralRef<'d, QDEC>, +pub struct Qdec<'d, T: Instance> { + _p: PeripheralRef<'d, T>, } /// QDEC config @@ -44,44 +46,52 @@ impl Default for Config { } } -static WAKER: AtomicWaker = AtomicWaker::new(); +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} -impl<'d> Qdec<'d> { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.reportrdy().clear()); + T::state().waker.wake(); + } +} + +impl<'d, T: Instance> Qdec<'d, T> { /// Create a new QDEC. pub fn new( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), None, config) + into_ref!(qdec, a, b); + Self::new_inner(qdec, a.map_into(), b.map_into(), None, config) } /// Create a new QDEC, with a pin for LED output. pub fn new_with_led( - qdec: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qdec: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, led: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(a, b, led); - Self::new_inner(qdec, irq, a.map_into(), b.map_into(), Some(led.map_into()), config) + into_ref!(qdec, a, b, led); + Self::new_inner(qdec, a.map_into(), b.map_into(), Some(led.map_into()), config) } fn new_inner( - p: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + p: PeripheralRef<'d, T>, a: PeripheralRef<'d, AnyPin>, b: PeripheralRef<'d, AnyPin>, led: Option>, config: Config, ) -> Self { - into_ref!(p, irq); - let r = Self::regs(); + let r = T::regs(); // Select pins. a.conf().write(|w| w.input().connect().pull().pullup()); @@ -124,20 +134,15 @@ impl<'d> Qdec<'d> { SamplePeriod::_131ms => w.sampleper()._131ms(), }); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + // Enable peripheral r.enable.write(|w| w.enable().set_bit()); // Start sampling unsafe { r.tasks_start.write(|w| w.bits(1)) }; - irq.disable(); - irq.set_handler(|_| { - let r = Self::regs(); - r.intenclr.write(|w| w.reportrdy().clear()); - WAKER.wake(); - }); - irq.enable(); - Self { _p: p } } @@ -155,12 +160,12 @@ impl<'d> Qdec<'d> { /// let delta = q.read().await; /// ``` pub async fn read(&mut self) -> i16 { - let t = Self::regs(); + let t = T::regs(); t.intenset.write(|w| w.reportrdy().set()); unsafe { t.tasks_readclracc.write(|w| w.bits(1)) }; let value = poll_fn(|cx| { - WAKER.register(cx.waker()); + T::state().waker.register(cx.waker()); if t.events_reportrdy.read().bits() == 0 { return Poll::Pending; } else { @@ -172,10 +177,6 @@ impl<'d> Qdec<'d> { .await; value } - - fn regs() -> &'static pac::qdec::RegisterBlock { - unsafe { &*pac::QDEC::ptr() } - } } /// Sample period @@ -236,3 +237,48 @@ pub enum LedPolarity { /// Active low (a low output turns on the LED). ActiveLow, } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::qdec::RegisterBlock; + fn state() -> &'static State; + } +} + +/// qdec peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_qdec { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::qdec::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::qdec::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::qdec::sealed::State { + static STATE: crate::qdec::sealed::State = crate::qdec::sealed::State::new(); + &STATE + } + } + impl crate::qdec::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs index 600bba07a..59783d312 100644 --- a/examples/nrf52840/src/bin/qdec.rs +++ b/examples/nrf52840/src/bin/qdec.rs @@ -4,16 +4,19 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::qdec::{self, Qdec}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + QDEC => qdec::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(QDEC); let config = qdec::Config::default(); - let mut rotary_enc = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); + let mut rotary_enc = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config); info!("Turn rotary encoder!"); let mut value = 0; From 96788ac93a1e98ef8d9d5e8d80d5102aef34d45d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:37:21 +0100 Subject: [PATCH 0659/1575] nrf/qspi: switch to new interrupt binding. --- embassy-nrf/src/qspi.rs | 65 ++++++++++++---------- examples/nrf52840/src/bin/qspi.rs | 9 ++- examples/nrf52840/src/bin/qspi_lowpower.rs | 9 ++- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d514e0274..7f004b9fc 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::ptr; use core::task::Poll; @@ -11,12 +12,12 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; pub use crate::pac::qspi::ifconfig0::{ ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode, }; pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; -use crate::{pac, Peripheral}; +use crate::Peripheral; /// Deep power-down config. pub struct DeepPowerDownConfig { @@ -114,9 +115,26 @@ pub enum Error { // TODO add "not in data memory" error and check for it } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_ready.read().bits() != 0 { + s.waker.wake(); + r.intenclr.write(|w| w.ready().clear()); + } + } +} + /// QSPI flash driver. pub struct Qspi<'d, T: Instance> { - irq: PeripheralRef<'d, T::Interrupt>, + _peri: PeripheralRef<'d, T>, dpm_enabled: bool, capacity: u32, } @@ -124,8 +142,8 @@ pub struct Qspi<'d, T: Instance> { impl<'d, T: Instance> Qspi<'d, T> { /// Create a new QSPI driver. pub fn new( - _qspi: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + qspi: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, csn: impl Peripheral

+ 'd, io0: impl Peripheral

+ 'd, @@ -134,7 +152,7 @@ impl<'d, T: Instance> Qspi<'d, T> { io3: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(irq, sck, csn, io0, io1, io2, io3); + into_ref!(qspi, sck, csn, io0, io1, io2, io3); let r = T::regs(); @@ -189,16 +207,15 @@ impl<'d, T: Instance> Qspi<'d, T> { w }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); // Enable it r.enable.write(|w| w.enable().enabled()); let res = Self { + _peri: qspi, dpm_enabled: config.deep_power_down.is_some(), - irq, capacity: config.capacity, }; @@ -212,16 +229,6 @@ impl<'d, T: Instance> Qspi<'d, T> { res } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_ready.read().bits() != 0 { - s.ready_waker.wake(); - r.intenclr.write(|w| w.ready().clear()); - } - } - /// Do a custom QSPI instruction. pub async fn custom_instruction(&mut self, opcode: u8, req: &[u8], resp: &mut [u8]) -> Result<(), Error> { let ondrop = OnDrop::new(Self::blocking_wait_ready); @@ -310,7 +317,7 @@ impl<'d, T: Instance> Qspi<'d, T> { poll_fn(move |cx| { let r = T::regs(); let s = T::state(); - s.ready_waker.register(cx.waker()); + s.waker.register(cx.waker()); if r.events_ready.read().bits() != 0 { return Poll::Ready(()); } @@ -525,8 +532,6 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { r.enable.write(|w| w.enable().disabled()); - self.irq.disable(); - // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, // leaving it floating, the flash chip might read it as zero which would cause it to // spuriously exit DPM. @@ -624,27 +629,27 @@ mod _eh1 { pub(crate) mod sealed { use embassy_sync::waitqueue::AtomicWaker; - use super::*; - + /// Peripheral static state pub struct State { - pub ready_waker: AtomicWaker, + pub waker: AtomicWaker, } + impl State { pub const fn new() -> Self { Self { - ready_waker: AtomicWaker::new(), + waker: AtomicWaker::new(), } } } pub trait Instance { - fn regs() -> &'static pac::qspi::RegisterBlock; + fn regs() -> &'static crate::pac::qspi::RegisterBlock; fn state() -> &'static State; } } /// QSPI peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: Interrupt; } @@ -652,7 +657,7 @@ pub trait Instance: Peripheral

+ sealed::Instance + 'static { macro_rules! impl_qspi { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::qspi::sealed::Instance for peripherals::$type { - fn regs() -> &'static pac::qspi::RegisterBlock { + fn regs() -> &'static crate::pac::qspi::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } fn state() -> &'static crate::qspi::sealed::State { diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs index 21a10940d..9e8a01f4e 100644 --- a/examples/nrf52840/src/bin/qspi.rs +++ b/examples/nrf52840/src/bin/qspi.rs @@ -5,7 +5,7 @@ use defmt::{assert_eq, info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::qspi::Frequency; -use embassy_nrf::{interrupt, qspi}; +use embassy_nrf::{bind_interrupts, peripherals, qspi}; use {defmt_rtt as _, panic_probe as _}; const PAGE_SIZE: usize = 4096; @@ -15,6 +15,10 @@ const PAGE_SIZE: usize = 4096; #[repr(C, align(4))] struct AlignedBuf([u8; 4096]); +bind_interrupts!(struct Irqs { + QSPI => qspi::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,9 +30,8 @@ async fn main(_spawner: Spawner) { config.write_opcode = qspi::WriteOpcode::PP4IO; config.write_page_size = qspi::WritePageSize::_256BYTES; - let irq = interrupt::take!(QSPI); let mut q = qspi::Qspi::new( - p.QSPI, irq, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, + p.QSPI, Irqs, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, ); let mut id = [1; 3]; diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs index 20c903914..22a5c0c6d 100644 --- a/examples/nrf52840/src/bin/qspi_lowpower.rs +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs @@ -7,7 +7,7 @@ use core::mem; use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::qspi::Frequency; -use embassy_nrf::{interrupt, qspi}; +use embassy_nrf::{bind_interrupts, peripherals, qspi}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -16,10 +16,13 @@ use {defmt_rtt as _, panic_probe as _}; #[repr(C, align(4))] struct AlignedBuf([u8; 64]); +bind_interrupts!(struct Irqs { + QSPI => qspi::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); - let mut irq = interrupt::take!(QSPI); loop { // Config for the MX25R64 present in the nRF52840 DK @@ -36,7 +39,7 @@ async fn main(_p: Spawner) { let mut q = qspi::Qspi::new( &mut p.QSPI, - &mut irq, + Irqs, &mut p.P0_19, &mut p.P0_17, &mut p.P0_20, From d113fcfe326bd338df2db7733fcf0ae9f230c594 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:50:15 +0100 Subject: [PATCH 0660/1575] nrf/rng: make available on all chips, use Instance trait, switch to new interrupt binding. --- embassy-nrf/src/chips/nrf52805.rs | 2 + embassy-nrf/src/chips/nrf52810.rs | 2 + embassy-nrf/src/chips/nrf52811.rs | 2 + embassy-nrf/src/chips/nrf52820.rs | 2 + embassy-nrf/src/chips/nrf52832.rs | 2 + embassy-nrf/src/chips/nrf52833.rs | 2 + embassy-nrf/src/chips/nrf52840.rs | 2 + embassy-nrf/src/chips/nrf5340_net.rs | 5 + embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/rng.rs | 193 ++++++++++++++-------- examples/nrf52840/src/bin/rng.rs | 8 +- examples/nrf52840/src/bin/usb_ethernet.rs | 8 +- 12 files changed, 154 insertions(+), 76 deletions(-) diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 185cda430..e406c081b 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -142,6 +142,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + 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 1e3c054a4..153795e54 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -152,6 +152,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 3bb44171e..a7a7cf58c 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -154,6 +154,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index b28778f33..14a1b8cc9 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -155,6 +155,8 @@ impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 00dc9fd8f..83ecd0deb 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -175,6 +175,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 345608c9d..5e5db04de 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -201,6 +201,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 630f01aa9..f6d33f85c 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -212,6 +212,8 @@ impl_pdm!(PDM, PDM, PDM); impl_qdec!(QDEC, QDEC, QDEC); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index d7ba6c16c..1e59528cb 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -127,6 +127,9 @@ embassy_hal_common::peripherals! { // SAADC SAADC, + // RNG + RNG, + // PWM PWM0, PWM1, @@ -252,6 +255,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rng!(RNG, RNG, RNG); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index feefa2486..ee29ce20b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -61,7 +61,7 @@ pub mod pwm; pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; -#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] +#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))] pub mod rng; #[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index b0b3a8eb8..a5602248d 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,83 +1,48 @@ //! Random Number Generator (RNG) driver. +#![macro_use] + use core::future::poll_fn; +use core::marker::PhantomData; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; -use crate::peripherals::RNG; -use crate::{interrupt, pac, Peripheral}; +use crate::{interrupt, Peripheral}; -impl RNG { - fn regs() -> &'static pac::rng::RegisterBlock { - unsafe { &*pac::RNG::ptr() } - } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -static STATE: State = State { - ptr: AtomicPtr::new(ptr::null_mut()), - end: AtomicPtr::new(ptr::null_mut()), - waker: AtomicWaker::new(), -}; +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let s = T::state(); + let r = T::regs(); -struct State { - ptr: AtomicPtr, - end: AtomicPtr, - waker: AtomicWaker, -} - -/// A wrapper around an nRF RNG peripheral. -/// -/// It has a non-blocking API, and a blocking api through `rand`. -pub struct Rng<'d> { - irq: PeripheralRef<'d, interrupt::RNG>, -} - -impl<'d> Rng<'d> { - /// Creates a new RNG driver from the `RNG` peripheral and interrupt. - /// - /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, - /// e.g. using `mem::forget`. - /// - /// The synchronous API is safe. - pub fn new(_rng: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - - let this = Self { irq }; - - this.stop(); - this.disable_irq(); - - this.irq.set_handler(Self::on_interrupt); - this.irq.unpend(); - this.irq.enable(); - - this - } - - fn on_interrupt(_: *mut ()) { // Clear the event. - RNG::regs().events_valrdy.reset(); + r.events_valrdy.reset(); // Mutate the slice within a critical section, // so that the future isn't dropped in between us loading the pointer and actually dereferencing it. let (ptr, end) = critical_section::with(|_| { - let ptr = STATE.ptr.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); // We need to make sure we haven't already filled the whole slice, // in case the interrupt fired again before the executor got back to the future. - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); if !ptr.is_null() && ptr != end { // If the future was dropped, the pointer would have been set to null, // so we're still good to mutate the slice. // The safety contract of `Rng::new` means that the future can't have been dropped // without calling its destructor. unsafe { - *ptr = RNG::regs().value.read().value().bits(); + *ptr = r.value.read().value().bits(); } } (ptr, end) @@ -90,15 +55,15 @@ impl<'d> Rng<'d> { } let new_ptr = unsafe { ptr.add(1) }; - match STATE + match s .ptr .compare_exchange(ptr, new_ptr, Ordering::Relaxed, Ordering::Relaxed) { Ok(_) => { - let end = STATE.end.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); // It doesn't matter if `end` was changed under our feet, because then this will just be false. if new_ptr == end { - STATE.waker.wake(); + s.waker.wake(); } } Err(_) => { @@ -107,21 +72,53 @@ impl<'d> Rng<'d> { } } } +} + +/// A wrapper around an nRF RNG peripheral. +/// +/// It has a non-blocking API, and a blocking api through `rand`. +pub struct Rng<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Rng<'d, T> { + /// Creates a new RNG driver from the `RNG` peripheral and interrupt. + /// + /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, + /// e.g. using `mem::forget`. + /// + /// The synchronous API is safe. + pub fn new( + rng: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + ) -> Self { + into_ref!(rng); + + let this = Self { _peri: rng }; + + this.stop(); + this.disable_irq(); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + this + } fn stop(&self) { - RNG::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) } fn start(&self) { - RNG::regs().tasks_start.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_start.write(|w| unsafe { w.bits(1) }) } fn enable_irq(&self) { - RNG::regs().intenset.write(|w| w.valrdy().set()); + T::regs().intenset.write(|w| w.valrdy().set()); } fn disable_irq(&self) { - RNG::regs().intenclr.write(|w| w.valrdy().clear()); + T::regs().intenclr.write(|w| w.valrdy().clear()); } /// Enable or disable the RNG's bias correction. @@ -131,7 +128,7 @@ impl<'d> Rng<'d> { /// /// Defaults to disabled. pub fn set_bias_correction(&self, enable: bool) { - RNG::regs().config.write(|w| w.dercen().bit(enable)) + T::regs().config.write(|w| w.dercen().bit(enable)) } /// Fill the buffer with random bytes. @@ -140,11 +137,13 @@ impl<'d> Rng<'d> { return; // Nothing to fill } + let s = T::state(); + let range = dest.as_mut_ptr_range(); // Even if we've preempted the interrupt, it can't preempt us again, // so we don't need to worry about the order we write these in. - STATE.ptr.store(range.start, Ordering::Relaxed); - STATE.end.store(range.end, Ordering::Relaxed); + s.ptr.store(range.start, Ordering::Relaxed); + s.end.store(range.end, Ordering::Relaxed); self.enable_irq(); self.start(); @@ -154,16 +153,16 @@ impl<'d> Rng<'d> { self.disable_irq(); // The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here. - STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed); - STATE.end.store(ptr::null_mut(), Ordering::Relaxed); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); }); poll_fn(|cx| { - STATE.waker.register(cx.waker()); + s.waker.register(cx.waker()); // The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`. - let end = STATE.end.load(Ordering::Relaxed); - let ptr = STATE.ptr.load(Ordering::Relaxed); + let end = s.end.load(Ordering::Relaxed); + let ptr = s.ptr.load(Ordering::Relaxed); if ptr == end { // We're done. @@ -183,7 +182,7 @@ impl<'d> Rng<'d> { self.start(); for byte in dest.iter_mut() { - let regs = RNG::regs(); + let regs = T::regs(); while regs.events_valrdy.read().bits() == 0 {} regs.events_valrdy.reset(); *byte = regs.value.read().value().bits(); @@ -193,13 +192,16 @@ impl<'d> Rng<'d> { } } -impl<'d> Drop for Rng<'d> { +impl<'d, T: Instance> Drop for Rng<'d, T> { fn drop(&mut self) { - self.irq.disable() + self.stop(); + let s = T::state(); + s.ptr.store(ptr::null_mut(), Ordering::Relaxed); + s.end.store(ptr::null_mut(), Ordering::Relaxed); } } -impl<'d> rand_core::RngCore for Rng<'d> { +impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> { fn fill_bytes(&mut self, dest: &mut [u8]) { self.blocking_fill_bytes(dest); } @@ -223,4 +225,53 @@ impl<'d> rand_core::RngCore for Rng<'d> { } } -impl<'d> rand_core::CryptoRng for Rng<'d> {} +impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {} + +pub(crate) mod sealed { + use super::*; + + /// Peripheral static state + pub struct State { + pub ptr: AtomicPtr, + pub end: AtomicPtr, + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + ptr: AtomicPtr::new(ptr::null_mut()), + end: AtomicPtr::new(ptr::null_mut()), + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::rng::RegisterBlock; + fn state() -> &'static State; + } +} + +/// RNG peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_rng { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::rng::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::rng::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::rng::sealed::State { + static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new(); + &STATE + } + } + impl crate::rng::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs index 647073949..855743f50 100644 --- a/examples/nrf52840/src/bin/rng.rs +++ b/examples/nrf52840/src/bin/rng.rs @@ -3,15 +3,19 @@ #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::rng::Rng; +use embassy_nrf::{bind_interrupts, peripherals, rng}; use rand::Rng as _; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); // Async API let mut bytes = [0; 4]; diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 979780896..083a1cbb0 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -10,7 +10,7 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -18,6 +18,10 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; macro_rules! singleton { @@ -108,7 +112,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.blocking_fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); From 2dc56082033f650083355464c3106ccb57302338 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 21:56:22 +0100 Subject: [PATCH 0661/1575] nrf/saadc: switch to new interrupt binding. --- embassy-nrf/src/saadc.rs | 62 ++++++++++--------- examples/nrf52840/src/bin/saadc.rs | 8 ++- examples/nrf52840/src/bin/saadc_continuous.rs | 8 ++- 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 2d01a3dda..af952f03d 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,6 +6,7 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -17,7 +18,6 @@ use saadc::oversample::OVERSAMPLE_A; use saadc::resolution::VAL_A; use self::sealed::Input as _; -use crate::interrupt::InterruptExt; use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::{interrupt, pac, peripherals, Peripheral}; @@ -28,9 +28,30 @@ use crate::{interrupt, pac, peripherals, Peripheral}; #[non_exhaustive] pub enum Error {} -/// One-shot and continuous SAADC. -pub struct Saadc<'d, const N: usize> { - _p: PeripheralRef<'d, peripherals::SAADC>, +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = unsafe { &*SAADC::ptr() }; + + if r.events_calibratedone.read().bits() != 0 { + r.intenclr.write(|w| w.calibratedone().clear()); + WAKER.wake(); + } + + if r.events_end.read().bits() != 0 { + r.intenclr.write(|w| w.end().clear()); + WAKER.wake(); + } + + if r.events_started.read().bits() != 0 { + r.intenclr.write(|w| w.started().clear()); + WAKER.wake(); + } + } } static WAKER: AtomicWaker = AtomicWaker::new(); @@ -114,15 +135,20 @@ pub enum CallbackResult { Stop, } +/// One-shot and continuous SAADC. +pub struct Saadc<'d, const N: usize> { + _p: PeripheralRef<'d, peripherals::SAADC>, +} + impl<'d, const N: usize> Saadc<'d, N> { /// Create a new SAADC driver. pub fn new( saadc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, config: Config, channel_configs: [ChannelConfig; N], ) -> Self { - into_ref!(saadc, irq); + into_ref!(saadc); let r = unsafe { &*SAADC::ptr() }; @@ -163,32 +189,12 @@ impl<'d, const N: usize> Saadc<'d, N> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { interrupt::SAADC::steal() }.unpend(); + unsafe { interrupt::SAADC::steal() }.enable(); Self { _p: saadc } } - fn on_interrupt(_ctx: *mut ()) { - let r = Self::regs(); - - if r.events_calibratedone.read().bits() != 0 { - r.intenclr.write(|w| w.calibratedone().clear()); - WAKER.wake(); - } - - if r.events_end.read().bits() != 0 { - r.intenclr.write(|w| w.end().clear()); - WAKER.wake(); - } - - if r.events_started.read().bits() != 0 { - r.intenclr.write(|w| w.started().clear()); - WAKER.wake(); - } - } - fn regs() -> &'static saadc::RegisterBlock { unsafe { &*SAADC::ptr() } } diff --git a/examples/nrf52840/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs index 7cf588090..ffd9a7f4b 100644 --- a/examples/nrf52840/src/bin/saadc.rs +++ b/examples/nrf52840/src/bin/saadc.rs @@ -4,17 +4,21 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::saadc::{ChannelConfig, Config, Saadc}; +use embassy_nrf::{bind_interrupts, saadc}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SAADC => saadc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); let config = Config::default(); let channel_config = ChannelConfig::single_ended(&mut p.P0_02); - let mut saadc = Saadc::new(p.SAADC, interrupt::take!(SAADC), config, [channel_config]); + let mut saadc = Saadc::new(p.SAADC, Irqs, config, [channel_config]); loop { let mut buf = [0; 1]; diff --git a/examples/nrf52840/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs index 2551d15fd..a25e17465 100644 --- a/examples/nrf52840/src/bin/saadc_continuous.rs +++ b/examples/nrf52840/src/bin/saadc_continuous.rs @@ -4,14 +4,18 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::saadc::{CallbackResult, ChannelConfig, Config, Saadc}; use embassy_nrf::timer::Frequency; +use embassy_nrf::{bind_interrupts, saadc}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer +bind_interrupts!(struct Irqs { + SAADC => saadc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); @@ -21,7 +25,7 @@ async fn main(_p: Spawner) { let channel_3_config = ChannelConfig::single_ended(&mut p.P0_04); let mut saadc = Saadc::new( p.SAADC, - interrupt::take!(SAADC), + Irqs, config, [channel_1_config, channel_2_config, channel_3_config], ); From a32e82029a8b6944ef3e9861b09095bae01b37a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:00:52 +0100 Subject: [PATCH 0662/1575] nrf/spim: switch to new interrupt binding. --- embassy-nrf/src/spim.rs | 59 +++++++++++--------- examples/nrf52840/src/bin/lora_p2p_report.rs | 9 ++- examples/nrf52840/src/bin/lora_p2p_sense.rs | 9 ++- examples/nrf52840/src/bin/spim.rs | 9 ++- 4 files changed, 50 insertions(+), 36 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 17e435787..89cbdfee9 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -14,7 +15,7 @@ pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -31,11 +32,6 @@ pub enum Error { BufferNotInRAM, } -/// SPIM driver. -pub struct Spim<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - /// SPIM configuration. #[non_exhaustive] pub struct Config { @@ -62,11 +58,33 @@ impl Default for Config { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_end.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.end().clear()); + } + } +} + +/// SPIM driver. +pub struct Spim<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver. pub fn new( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, @@ -75,7 +93,6 @@ impl<'d, T: Instance> Spim<'d, T> { into_ref!(sck, miso, mosi); Self::new_inner( spim, - irq, sck.map_into(), Some(miso.map_into()), Some(mosi.map_into()), @@ -86,36 +103,35 @@ impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver, capable of TX only (MOSI only). pub fn new_txonly( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, config: Config, ) -> Self { into_ref!(sck, mosi); - Self::new_inner(spim, irq, sck.map_into(), None, Some(mosi.map_into()), config) + Self::new_inner(spim, sck.map_into(), None, Some(mosi.map_into()), config) } /// Create a new SPIM driver, capable of RX only (MISO only). pub fn new_rxonly( spim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

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

+ 'd, - irq: impl Peripheral

+ 'd, sck: PeripheralRef<'d, AnyPin>, miso: Option>, mosi: Option>, config: Config, ) -> Self { - into_ref!(spim, irq); + into_ref!(spim); let r = T::regs(); @@ -191,23 +207,12 @@ impl<'d, T: Instance> Spim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: spim } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_end.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.end().clear()); - } - } - fn prepare(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(), Error> { slice_in_ram_or(tx, Error::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable diff --git a/examples/nrf52840/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs index d512b83f6..e24f0db03 100644 --- a/examples/nrf52840/src/bin/lora_p2p_report.rs +++ b/examples/nrf52840/src/bin/lora_p2p_report.rs @@ -11,11 +11,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -23,8 +27,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = spim::Frequency::M16; let mut radio = { - let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); diff --git a/examples/nrf52840/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs index b9768874b..b6f41ffcc 100644 --- a/examples/nrf52840/src/bin/lora_p2p_sense.rs +++ b/examples/nrf52840/src/bin/lora_p2p_sense.rs @@ -12,13 +12,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_lora::sx126x::*; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher}; use embassy_time::{Duration, Timer}; use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; use {defmt_rtt as _, panic_probe as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); @@ -58,8 +62,7 @@ async fn main(spawner: Spawner) { spi_config.frequency = spim::Frequency::M16; let mut radio = { - let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); diff --git a/examples/nrf52840/src/bin/spim.rs b/examples/nrf52840/src/bin/spim.rs index 132e01660..9d1843a8f 100644 --- a/examples/nrf52840/src/bin/spim.rs +++ b/examples/nrf52840/src/bin/spim.rs @@ -5,9 +5,13 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_nrf::gpio::{Level, Output, OutputDrive}; -use embassy_nrf::{interrupt, spim}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -16,8 +20,7 @@ async fn main(_spawner: Spawner) { let mut config = spim::Config::default(); config.frequency = spim::Frequency::M16; - let irq = interrupt::take!(SPIM3); - let mut spim = spim::Spim::new(p.SPI3, irq, p.P0_29, p.P0_28, p.P0_30, config); + let mut spim = spim::Spim::new(p.SPI3, Irqs, p.P0_29, p.P0_28, p.P0_30, config); let mut ncs = Output::new(p.P0_31, Level::High, OutputDrive::Standard); From 9f5762d3654464011b5ddda771d4866c547106a0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:05:02 +0100 Subject: [PATCH 0663/1575] nrf/spis: switch to new interrupt binding. --- embassy-nrf/src/spis.rs | 85 ++++++++++++++----------------- examples/nrf52840/src/bin/spis.rs | 9 ++-- 2 files changed, 43 insertions(+), 51 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 1b7436477..55b5e060e 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -2,6 +2,7 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -12,7 +13,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO 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::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -29,11 +30,6 @@ pub enum Error { BufferNotInRAM, } -/// SPIS driver. -pub struct Spis<'d, T: Instance> { - _p: PeripheralRef<'d, T>, -} - /// SPIS configuration. #[non_exhaustive] pub struct Config { @@ -67,11 +63,38 @@ impl Default for Config { } } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + 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()); + } + } +} + +/// SPIS driver. +pub struct Spis<'d, T: Instance> { + _p: PeripheralRef<'d, T>, +} + impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver. pub fn new( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, @@ -81,7 +104,6 @@ impl<'d, T: Instance> Spis<'d, T> { into_ref!(cs, sck, miso, mosi); Self::new_inner( spis, - irq, cs.map_into(), sck.map_into(), Some(miso.map_into()), @@ -93,48 +115,31 @@ impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver, capable of TX only (MISO only). pub fn new_txonly( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + '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, - ) + Self::new_inner(spis, cs.map_into(), sck.map_into(), Some(miso.map_into()), None, config) } /// Create a new SPIS driver, capable of RX only (MOSI only). pub fn new_rxonly( spis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + '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, - ) + Self::new_inner(spis, 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>, @@ -143,7 +148,7 @@ impl<'d, T: Instance> Spis<'d, T> { ) -> Self { compiler_fence(Ordering::SeqCst); - into_ref!(spis, irq, cs, sck); + into_ref!(spis, cs, sck); let r = T::regs(); @@ -209,28 +214,12 @@ impl<'d, T: Instance> Spis<'d, T> { // Disable all events interrupts. r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.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::BufferNotInRAM)?; // NOTE: RAM slice check for rx is not necessary, as a mutable diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs index fe3b0c53d..77b6e8b64 100644 --- a/examples/nrf52840/src/bin/spis.rs +++ b/examples/nrf52840/src/bin/spis.rs @@ -4,17 +4,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::spis::{Config, Spis}; +use embassy_nrf::{bind_interrupts, peripherals, spis}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM2_SPIS2_SPI2 => spis::InterruptHandler; +}); + #[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()); + let mut spis = Spis::new(p.SPI2, Irqs, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); loop { let mut rx_buf = [0_u8; 64]; From 9e58d9274c63ebe48217e94162e47bea3211ff1c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:12:34 +0100 Subject: [PATCH 0664/1575] nrf/twim: switch to new interrupt binding. --- embassy-nrf/src/twim.rs | 47 +++++++++++++--------- examples/nrf52840/src/bin/twim.rs | 9 +++-- examples/nrf52840/src/bin/twim_lowpower.rs | 9 +++-- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 0dcb2b0da..ef4c929a3 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::{poll_fn, Future}; +use core::marker::PhantomData; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -15,7 +16,7 @@ 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::interrupt::{self, Interrupt, InterruptExt}; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{gpio, pac, Peripheral}; @@ -92,6 +93,27 @@ pub enum Error { Timeout, } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + if r.events_stopped.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.stopped().clear()); + } + if r.events_error.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.error().clear()); + } + } +} + /// TWI driver. pub struct Twim<'d, T: Instance> { _p: PeripheralRef<'d, T>, @@ -101,12 +123,12 @@ impl<'d, T: Instance> Twim<'d, T> { /// Create a new TWI driver. pub fn new( twim: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(twim, irq, sda, scl); + into_ref!(twim, sda, scl); let r = T::regs(); @@ -152,27 +174,12 @@ impl<'d, T: Instance> Twim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _p: twim } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - if r.events_stopped.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|w| w.stopped().clear()); - } - if r.events_error.read().bits() != 0 { - s.end_waker.wake(); - r.intenclr.write(|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::BufferNotInRAM)?; diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index a027cc1e7..959e3a4be 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs @@ -8,19 +8,22 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twim::{self, Twim}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Initializing TWI..."); let config = twim::Config::default(); - let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); - let mut twi = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); + let mut twi = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); info!("Reading..."); diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index e30cc9688..0970d3c3c 100644 --- a/examples/nrf52840/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs @@ -12,25 +12,28 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twim::{self, Twim}; +use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); info!("Started!"); - let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); loop { info!("Initializing TWI..."); let config = twim::Config::default(); // Create the TWIM instance with borrowed singletons, so they're not consumed. - let mut twi = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config); + let mut twi = Twim::new(&mut p.TWISPI0, Irqs, &mut p.P0_03, &mut p.P0_04, config); info!("Reading..."); From 36319fc121f19f86dded45b6fb93aed7c3f4ae33 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:09:54 +0100 Subject: [PATCH 0665/1575] nrf/temp: switch to new interrupt binding. --- embassy-nrf/src/temp.rs | 35 +++++++++++++++++++++---------- examples/nrf52840/src/bin/temp.rs | 9 +++++--- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 5298faabc..3a75ec629 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -3,6 +3,7 @@ use core::future::poll_fn; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -12,27 +13,39 @@ use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; use crate::{interrupt, pac, Peripheral}; +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = unsafe { &*pac::TEMP::PTR }; + r.intenclr.write(|w| w.datardy().clear()); + WAKER.wake(); + } +} + /// Builtin temperature sensor driver. pub struct Temp<'d> { - _irq: PeripheralRef<'d, interrupt::TEMP>, + _peri: PeripheralRef<'d, TEMP>, } static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d> Temp<'d> { /// Create a new temperature sensor driver. - pub fn new(_t: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(_t, irq); + pub fn new( + _peri: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, + ) -> Self { + into_ref!(_peri); // Enable interrupt that signals temperature values - irq.disable(); - irq.set_handler(|_| { - let t = Self::regs(); - t.intenclr.write(|w| w.datardy().clear()); - WAKER.wake(); - }); - irq.enable(); - Self { _irq: irq } + unsafe { interrupt::TEMP::steal() }.unpend(); + unsafe { interrupt::TEMP::steal() }.enable(); + + Self { _peri } } /// Perform an asynchronous temperature measurement. The returned future diff --git a/examples/nrf52840/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs index b06ac709e..70957548f 100644 --- a/examples/nrf52840/src/bin/temp.rs +++ b/examples/nrf52840/src/bin/temp.rs @@ -4,16 +4,19 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::temp::Temp; +use embassy_nrf::{bind_interrupts, temp}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + TEMP => temp::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let irq = interrupt::take!(TEMP); - let mut temp = Temp::new(p.TEMP, irq); + let mut temp = Temp::new(p.TEMP, Irqs); loop { let value = temp.read().await; From 5913553cb1e95431665d3370dce8154a6869e434 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:14:59 +0100 Subject: [PATCH 0666/1575] nrf/twis: switch to new interrupt binding. --- embassy-nrf/src/twis.rs | 55 +++++++++++++++++-------------- examples/nrf52840/src/bin/twis.rs | 12 ++++--- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index c514d9f2f..bfa30b044 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -3,6 +3,7 @@ #![macro_use] use core::future::{poll_fn, Future}; +use core::marker::PhantomData; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -14,7 +15,7 @@ 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::interrupt::{self, Interrupt, InterruptExt}; use crate::util::slice_in_ram_or; use crate::{gpio, pac, Peripheral}; @@ -108,6 +109,31 @@ pub enum Command { Write(usize), } +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + 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()); + } + } +} + /// TWIS driver. pub struct Twis<'d, T: Instance> { _p: PeripheralRef<'d, T>, @@ -117,12 +143,12 @@ impl<'d, T: Instance> Twis<'d, T> { /// Create a new TWIS driver. pub fn new( twis: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(twis, irq, sda, scl); + into_ref!(twis, sda, scl); let r = T::regs(); @@ -178,31 +204,12 @@ impl<'d, T: Instance> Twis<'d, T> { // Generate suspend on read event r.shorts.write(|w| w.read_suspend().enabled()); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.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::BufferNotInRAM)?; diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs index 54cba9494..aa42b679e 100644 --- a/examples/nrf52840/src/bin/twis.rs +++ b/examples/nrf52840/src/bin/twis.rs @@ -6,19 +6,21 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_nrf::interrupt; use embassy_nrf::twis::{self, Command, Twis}; +use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twis::InterruptHandler; +}); + #[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); + config.address0 = 0x55; // Set i2c address + let mut i2c = Twis::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, config); info!("Listening..."); loop { From 5249996d282e1ae08cea1593193e2fe6ca20880a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:36:53 +0100 Subject: [PATCH 0667/1575] nrf/usb: switch to new interrupt binding, fix vbus detect on nrf53. --- embassy-nrf/src/{usb.rs => usb/mod.rs} | 234 ++++-------------- embassy-nrf/src/usb/vbus_detect.rs | 177 +++++++++++++ examples/nrf52840/Cargo.toml | 7 +- examples/nrf52840/src/bin/usb_ethernet.rs | 12 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 16 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 16 +- examples/nrf52840/src/bin/usb_serial.rs | 16 +- .../nrf52840/src/bin/usb_serial_multitask.rs | 51 ++-- .../nrf52840/src/bin/usb_serial_winusb.rs | 14 +- 9 files changed, 300 insertions(+), 243 deletions(-) rename embassy-nrf/src/{usb.rs => usb/mod.rs} (81%) create mode 100644 embassy-nrf/src/usb/vbus_detect.rs diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb/mod.rs similarity index 81% rename from embassy-nrf/src/usb.rs rename to embassy-nrf/src/usb/mod.rs index cd142f00f..56de511df 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -2,10 +2,12 @@ #![macro_use] +pub mod vbus_detect; + use core::future::poll_fn; use core::marker::PhantomData; use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; @@ -15,7 +17,8 @@ use embassy_usb_driver as driver; use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; use pac::usbd::RegisterBlock; -use crate::interrupt::{Interrupt, InterruptExt}; +use self::vbus_detect::VbusDetect; +use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::util::slice_in_ram; use crate::{pac, Peripheral}; @@ -26,185 +29,13 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); -/// Trait for detecting USB VBUS power. -/// -/// There are multiple ways to detect USB power. The behavior -/// here provides a hook into determining whether it is. -pub trait VbusDetect { - /// Report whether power is detected. - /// - /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the - /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - fn is_usb_detected(&self) -> bool; - - /// Wait until USB power is ready. - /// - /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the - /// `USBPWRRDY` event from the `POWER` peripheral. - async fn wait_power_ready(&mut self) -> Result<(), ()>; +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -/// [`VbusDetect`] implementation using the native hardware POWER peripheral. -/// -/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces -/// to POWER. In that case, use [`VbusDetectSignal`]. -#[cfg(not(feature = "_nrf5340-app"))] -pub struct HardwareVbusDetect { - _private: (), -} - -static POWER_WAKER: AtomicWaker = NEW_AW; - -#[cfg(not(feature = "_nrf5340-app"))] -impl HardwareVbusDetect { - /// Create a new `VbusDetectNative`. - pub fn new(power_irq: impl Interrupt) -> Self { - let regs = unsafe { &*pac::POWER::ptr() }; - - power_irq.set_handler(Self::on_interrupt); - power_irq.unpend(); - power_irq.enable(); - - regs.intenset - .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); - - Self { _private: () } - } - - #[cfg(not(feature = "_nrf5340-app"))] - fn on_interrupt(_: *mut ()) { - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); - BUS_WAKER.wake(); - } - - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - if regs.events_usbpwrrdy.read().bits() != 0 { - regs.events_usbpwrrdy.reset(); - POWER_WAKER.wake(); - } - } -} - -#[cfg(not(feature = "_nrf5340-app"))] -impl VbusDetect for HardwareVbusDetect { - fn is_usb_detected(&self) -> bool { - let regs = unsafe { &*pac::POWER::ptr() }; - regs.usbregstatus.read().vbusdetect().is_vbus_present() - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - let regs = unsafe { &*pac::POWER::ptr() }; - - if regs.usbregstatus.read().outputrdy().is_ready() { - Poll::Ready(Ok(())) - } else if !self.is_usb_detected() { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// Software-backed [`VbusDetect`] implementation. -/// -/// This implementation does not interact with the hardware, it allows user code -/// to notify the power events by calling functions instead. -/// -/// This is suitable for use with the nRF softdevice, by calling the functions -/// when the softdevice reports power-related events. -pub struct SoftwareVbusDetect { - usb_detected: AtomicBool, - power_ready: AtomicBool, -} - -impl SoftwareVbusDetect { - /// Create a new `SoftwareVbusDetect`. - pub fn new(usb_detected: bool, power_ready: bool) -> Self { - BUS_WAKER.wake(); - - Self { - usb_detected: AtomicBool::new(usb_detected), - power_ready: AtomicBool::new(power_ready), - } - } - - /// Report whether power was detected. - /// - /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. - pub fn detected(&self, detected: bool) { - self.usb_detected.store(detected, Ordering::Relaxed); - self.power_ready.store(false, Ordering::Relaxed); - BUS_WAKER.wake(); - POWER_WAKER.wake(); - } - - /// Report when USB power is ready. - /// - /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. - pub fn ready(&self) { - self.power_ready.store(true, Ordering::Relaxed); - POWER_WAKER.wake(); - } -} - -impl VbusDetect for &SoftwareVbusDetect { - fn is_usb_detected(&self) -> bool { - self.usb_detected.load(Ordering::Relaxed) - } - - async fn wait_power_ready(&mut self) -> Result<(), ()> { - poll_fn(move |cx| { - POWER_WAKER.register(cx.waker()); - - if self.power_ready.load(Ordering::Relaxed) { - Poll::Ready(Ok(())) - } else if !self.usb_detected.load(Ordering::Relaxed) { - Poll::Ready(Err(())) - } else { - Poll::Pending - } - }) - .await - } -} - -/// USB driver. -pub struct Driver<'d, T: Instance, P: VbusDetect> { - _p: PeripheralRef<'d, T>, - alloc_in: Allocator, - alloc_out: Allocator, - usb_supply: P, -} - -impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { - /// Create a new USB driver. - pub fn new(usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, usb_supply: P) -> Self { - into_ref!(usb, irq); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self { - _p: usb, - alloc_in: Allocator::new(), - alloc_out: Allocator::new(), - usb_supply, - } - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { let regs = T::regs(); if regs.events_usbreset.read().bits() != 0 { @@ -255,11 +86,40 @@ impl<'d, T: Instance, P: VbusDetect> Driver<'d, T, P> { } } -impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P> { +/// USB driver. +pub struct Driver<'d, T: Instance, V: VbusDetect> { + _p: PeripheralRef<'d, T>, + alloc_in: Allocator, + alloc_out: Allocator, + vbus_detect: V, +} + +impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { + /// Create a new USB driver. + pub fn new( + usb: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + vbus_detect: V, + ) -> Self { + into_ref!(usb); + + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); + + Self { + _p: usb, + alloc_in: Allocator::new(), + alloc_out: Allocator::new(), + vbus_detect, + } + } +} + +impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V> { type EndpointOut = Endpoint<'d, T, Out>; type EndpointIn = Endpoint<'d, T, In>; type ControlPipe = ControlPipe<'d, T>; - type Bus = Bus<'d, T, P>; + type Bus = Bus<'d, T, V>; fn alloc_endpoint_in( &mut self, @@ -298,7 +158,7 @@ impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P Bus { _p: unsafe { self._p.clone_unchecked() }, power_available: false, - usb_supply: self.usb_supply, + vbus_detect: self.vbus_detect, }, ControlPipe { _p: self._p, @@ -309,13 +169,13 @@ impl<'d, T: Instance, P: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, P } /// USB bus. -pub struct Bus<'d, T: Instance, P: VbusDetect> { +pub struct Bus<'d, T: Instance, V: VbusDetect> { _p: PeripheralRef<'d, T>, power_available: bool, - usb_supply: P, + vbus_detect: V, } -impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { +impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { async fn enable(&mut self) { let regs = T::regs(); @@ -347,7 +207,7 @@ impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { w }); - if self.usb_supply.wait_power_ready().await.is_ok() { + if self.vbus_detect.wait_power_ready().await.is_ok() { // Enable the USB pullup, allowing enumeration. regs.usbpullup.write(|w| w.connect().enabled()); trace!("enabled"); @@ -406,7 +266,7 @@ impl<'d, T: Instance, P: VbusDetect> driver::Bus for Bus<'d, T, P> { trace!("USB event: ready"); } - if self.usb_supply.is_usb_detected() != self.power_available { + if self.vbus_detect.is_usb_detected() != self.power_available { self.power_available = !self.power_available; if self.power_available { trace!("Power event: available"); diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs new file mode 100644 index 000000000..cecd4c595 --- /dev/null +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -0,0 +1,177 @@ +//! Trait and implementations for performing VBUS detection. + +use core::future::poll_fn; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; + +use embassy_sync::waitqueue::AtomicWaker; + +use super::BUS_WAKER; +use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::pac; + +/// Trait for detecting USB VBUS power. +/// +/// There are multiple ways to detect USB power. The behavior +/// here provides a hook into determining whether it is. +pub trait VbusDetect { + /// Report whether power is detected. + /// + /// This is indicated by the `USBREGSTATUS.VBUSDETECT` register, or the + /// `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + fn is_usb_detected(&self) -> bool; + + /// Wait until USB power is ready. + /// + /// USB power ready is indicated by the `USBREGSTATUS.OUTPUTRDY` register, or the + /// `USBPWRRDY` event from the `POWER` peripheral. + async fn wait_power_ready(&mut self) -> Result<(), ()>; +} + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegIrq = interrupt::POWER_CLOCK; +#[cfg(feature = "_nrf5340")] +type UsbRegIrq = interrupt::USBREGULATOR; + +#[cfg(not(feature = "_nrf5340"))] +type UsbRegPeri = pac::POWER; +#[cfg(feature = "_nrf5340")] +type UsbRegPeri = pac::USBREGULATOR; + +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.events_usbdetected.read().bits() != 0 { + regs.events_usbdetected.reset(); + BUS_WAKER.wake(); + } + + if regs.events_usbremoved.read().bits() != 0 { + regs.events_usbremoved.reset(); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + if regs.events_usbpwrrdy.read().bits() != 0 { + regs.events_usbpwrrdy.reset(); + POWER_WAKER.wake(); + } + } +} + +/// [`VbusDetect`] implementation using the native hardware POWER peripheral. +/// +/// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces +/// to POWER. In that case, use [`VbusDetectSignal`]. +pub struct HardwareVbusDetect { + _private: (), +} + +static POWER_WAKER: AtomicWaker = AtomicWaker::new(); + +impl HardwareVbusDetect { + /// Create a new `VbusDetectNative`. + pub fn new(_irq: impl interrupt::Binding + 'static) -> Self { + let regs = unsafe { &*UsbRegPeri::ptr() }; + + unsafe { UsbRegIrq::steal() }.unpend(); + unsafe { UsbRegIrq::steal() }.enable(); + + regs.intenset + .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); + + Self { _private: () } + } +} + +impl VbusDetect for HardwareVbusDetect { + fn is_usb_detected(&self) -> bool { + let regs = unsafe { &*UsbRegPeri::ptr() }; + regs.usbregstatus.read().vbusdetect().is_vbus_present() + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + let regs = unsafe { &*UsbRegPeri::ptr() }; + + if regs.usbregstatus.read().outputrdy().is_ready() { + Poll::Ready(Ok(())) + } else if !self.is_usb_detected() { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} + +/// Software-backed [`VbusDetect`] implementation. +/// +/// This implementation does not interact with the hardware, it allows user code +/// to notify the power events by calling functions instead. +/// +/// This is suitable for use with the nRF softdevice, by calling the functions +/// when the softdevice reports power-related events. +pub struct SoftwareVbusDetect { + usb_detected: AtomicBool, + power_ready: AtomicBool, +} + +impl SoftwareVbusDetect { + /// Create a new `SoftwareVbusDetect`. + pub fn new(usb_detected: bool, power_ready: bool) -> Self { + BUS_WAKER.wake(); + + Self { + usb_detected: AtomicBool::new(usb_detected), + power_ready: AtomicBool::new(power_ready), + } + } + + /// Report whether power was detected. + /// + /// Equivalent to the `USBDETECTED`, `USBREMOVED` events from the `POWER` peripheral. + pub fn detected(&self, detected: bool) { + self.usb_detected.store(detected, Ordering::Relaxed); + self.power_ready.store(false, Ordering::Relaxed); + BUS_WAKER.wake(); + POWER_WAKER.wake(); + } + + /// Report when USB power is ready. + /// + /// Equivalent to the `USBPWRRDY` event from the `POWER` peripheral. + pub fn ready(&self) { + self.power_ready.store(true, Ordering::Relaxed); + POWER_WAKER.wake(); + } +} + +impl VbusDetect for &SoftwareVbusDetect { + fn is_usb_detected(&self) -> bool { + self.usb_detected.load(Ordering::Relaxed) + } + + async fn wait_power_ready(&mut self) -> Result<(), ()> { + poll_fn(move |cx| { + POWER_WAKER.register(cx.waker()); + + if self.power_ready.load(Ordering::Relaxed) { + Poll::Ready(Ok(())) + } else if !self.usb_detected.load(Ordering::Relaxed) { + Poll::Ready(Err(())) + } else { + Poll::Pending + } + }) + .await + } +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index cfdda076e..cc88d92c7 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,7 +6,6 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -msos-descriptor = ["embassy-usb/msos-descriptor"] nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] @@ -17,7 +16,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } @@ -36,7 +35,3 @@ 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 } - -[[bin]] -name = "usb_serial_winusb" -required-features = ["msos-descriptor"] diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 083a1cbb0..b8a72313a 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -9,8 +9,9 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{bind_interrupts, interrupt, pac, peripherals, rng}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -19,6 +20,8 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; RNG => rng::InterruptHandler; }); @@ -60,9 +63,7 @@ async fn main(spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -86,6 +87,7 @@ async fn main(spawner: Spawner) { &mut singleton!([0; 256])[..], &mut singleton!([0; 256])[..], &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Our MAC addr. diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 3d8a114cd..7ccd2946a 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -10,8 +10,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; @@ -20,6 +21,11 @@ use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + static SUSPENDED: AtomicBool = AtomicBool::new(false); #[embassy_executor::main] @@ -32,9 +38,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -50,6 +54,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; let mut device_handler = MyDeviceHandler::new(); @@ -62,6 +67,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index d7c9d55b7..edf634a5e 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_time::{Duration, Timer}; use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; @@ -16,6 +17,11 @@ use embassy_usb::{Builder, Config}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -26,9 +32,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -43,6 +47,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let request_handler = MyRequestHandler {}; @@ -54,6 +59,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 102d7ea60..9727a4f57 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -7,13 +7,19 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -24,9 +30,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -48,6 +52,7 @@ async fn main(_spawner: Spawner) { let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let mut state = State::new(); @@ -58,6 +63,7 @@ async fn main(_spawner: Spawner) { &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, + &mut msos_descriptor, &mut control_buf, ); diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 558d4ba60..6da2c2a2f 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -6,14 +6,29 @@ use core::mem; use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; -use embassy_nrf::usb::{Driver, HardwareVbusDetect}; -use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; +use embassy_nrf::usb::Driver; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; #[embassy_executor::task] @@ -39,10 +54,9 @@ async fn main(spawner: Spawner) { info!("Enabling ext hfosc..."); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); while clock.events_hfclkstarted.read().bits() != 1 {} + // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -59,34 +73,21 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; config.composite_with_iads = true; - struct Resources { - device_descriptor: [u8; 256], - config_descriptor: [u8; 256], - bos_descriptor: [u8; 256], - control_buf: [u8; 64], - serial_state: State<'static>, - } - static RESOURCES: StaticCell = StaticCell::new(); - let res = RESOURCES.init(Resources { - device_descriptor: [0; 256], - config_descriptor: [0; 256], - bos_descriptor: [0; 256], - control_buf: [0; 64], - serial_state: State::new(), - }); + let state = singleton!(State::new()); // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut res.device_descriptor, - &mut res.config_descriptor, - &mut res.bos_descriptor, - &mut res.control_buf, + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 256])[..], + &mut singleton!([0; 128])[..], + &mut singleton!([0; 128])[..], ); // Create classes on the builder. - let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); + let class = CdcAcmClass::new(&mut builder, state, 64); // Build the builder. let usb = builder.build(); diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 6561fc3b4..6e4f71a48 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -7,8 +7,9 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::{Driver, HardwareVbusDetect, Instance, VbusDetect}; -use embassy_nrf::{interrupt, pac}; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; +use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::msos::{self, windows_version}; @@ -16,6 +17,11 @@ use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBD => usb::InterruptHandler; + POWER_CLOCK => usb::vbus_detect::InterruptHandler; +}); + // This is a randomly generated GUID to allow clients on Windows to find our device const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; @@ -29,9 +35,7 @@ async fn main(_spawner: Spawner) { while clock.events_hfclkstarted.read().bits() != 1 {} // Create the driver, from the HAL. - let irq = interrupt::take!(USBD); - let power_irq = interrupt::take!(POWER_CLOCK); - let driver = Driver::new(p.USBD, irq, HardwareVbusDetect::new(power_irq)); + let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); From f5e09a8f4a6d7b1bd6723c60915cb9f29cf352f7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 5 Mar 2023 22:40:53 +0100 Subject: [PATCH 0668/1575] nrf/interrupt: do not reexport `take!` macro. --- embassy-nrf/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index ee29ce20b..cb629902a 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -100,7 +100,7 @@ mod chip; pub mod interrupt { //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; - pub use embassy_cortex_m::interrupt::*; + pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; pub use crate::chip::irqs::*; From c22218c72e4940a0aef3fc180ba18b557713cf40 Mon Sep 17 00:00:00 2001 From: Leon Camus Date: Mon, 6 Mar 2023 17:50:57 +0100 Subject: [PATCH 0669/1575] feat: Add multicast to udp socket --- embassy-net/Cargo.toml | 3 ++- embassy-net/src/lib.rs | 37 +++++++++++++++++++++++++++++++++ embassy-net/src/udp.rs | 47 ++++++++++++++++++++++++++++++------------ 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index ca34262df..1854d2043 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] +features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] target = "thumbv7em-none-eabi" [features] @@ -27,6 +27,7 @@ dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] +igmp = ["smoltcp/proto-igmp"] [dependencies] diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 4ec1b5a77..57055bd77 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -304,6 +304,43 @@ impl Stack { } } +#[cfg(feature = "igmp")] +impl Stack { + pub(crate) fn join_multicast_group(&self, addr: T) -> Result + where + T: Into + { + let addr = addr.into(); + + self.with_mut(|s, i| { + s.iface.join_multicast_group( + &mut i.device, + addr, + instant_to_smoltcp(Instant::now()), + ) + }) + } + + pub(crate) fn leave_multicast_group(&self, addr: T) -> Result + where + T: Into + { + let addr = addr.into(); + + self.with_mut(|s, i| { + s.iface.leave_multicast_group( + &mut i.device, + addr, + instant_to_smoltcp(Instant::now()), + ) + }) + } + + pub(crate) fn has_multicast_group>(&self, addr: T) -> bool { + self.socket.borrow().iface.has_multicast_group(addr) + } +} + impl SocketStack { #[allow(clippy::absurd_extreme_comparisons, dead_code)] pub fn get_local_port(&mut self) -> u16 { diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 0ee8c6e19..c840eeaa2 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -6,9 +6,9 @@ use core::task::Poll; use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; -use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; +use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; -use crate::{SocketStack, Stack}; +use crate::Stack; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -26,13 +26,13 @@ pub enum Error { NoRoute, } -pub struct UdpSocket<'a> { - stack: &'a RefCell, +pub struct UdpSocket<'a, D: Driver> { + stack: &'a Stack, handle: SocketHandle, } -impl<'a> UdpSocket<'a> { - pub fn new( +impl<'a, D: Driver> UdpSocket<'a, D> { + pub fn new( stack: &'a Stack, rx_meta: &'a mut [PacketMetadata], rx_buffer: &'a mut [u8], @@ -51,7 +51,7 @@ impl<'a> UdpSocket<'a> { )); Self { - stack: &stack.socket, + stack, handle, } } @@ -64,7 +64,7 @@ impl<'a> UdpSocket<'a> { if endpoint.port == 0 { // If user didn't specify port allocate a dynamic port. - endpoint.port = self.stack.borrow_mut().get_local_port(); + endpoint.port = self.stack.socket.borrow_mut().get_local_port(); } match self.with_mut(|s, _| s.bind(endpoint)) { @@ -75,13 +75,13 @@ impl<'a> UdpSocket<'a> { } fn with(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.borrow(); + let s = &*self.stack.socket.borrow(); let socket = s.sockets.get::(self.handle); f(socket, &s.iface) } fn with_mut(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.borrow_mut(); + let s = &mut *self.stack.socket.borrow_mut(); let socket = s.sockets.get_mut::(self.handle); let res = f(socket, &mut s.iface); s.waker.wake(); @@ -143,8 +143,29 @@ impl<'a> UdpSocket<'a> { } } -impl Drop for UdpSocket<'_> { - fn drop(&mut self) { - self.stack.borrow_mut().sockets.remove(self.handle); +#[cfg(feature = "igmp")] +impl<'a, D: Driver + smoltcp::phy::Device + 'static> UdpSocket<'a, D> { + pub fn join_multicast_group(&self, addr: T) -> Result + where + T: Into + { + self.stack.join_multicast_group(addr) + } + + pub fn leave_multicast_group(&self, addr: T) -> Result + where + T: Into + { + self.stack.leave_multicast_group(addr) + } + + pub fn has_multicast_group>(&self, addr: T) -> bool { + self.stack.has_multicast_group(addr) + } +} + +impl Drop for UdpSocket<'_, D> { + fn drop(&mut self) { + self.stack.socket.borrow_mut().sockets.remove(self.handle); } } From bc0cb43307c2a46330ce253505203dbc607bcc6c Mon Sep 17 00:00:00 2001 From: Mehmet Ali Anil Date: Mon, 6 Mar 2023 22:08:47 +0100 Subject: [PATCH 0670/1575] Bump embedded-storage-async to 0.4 --- embassy-boot/boot/Cargo.toml | 2 +- embassy-boot/boot/src/lib.rs | 4 ++-- embassy-boot/nrf/Cargo.toml | 2 +- embassy-boot/rp/Cargo.toml | 2 +- embassy-boot/stm32/Cargo.toml | 2 +- embassy-embedded-hal/Cargo.toml | 2 +- embassy-embedded-hal/src/adapter.rs | 19 +++++++----------- embassy-nrf/Cargo.toml | 2 +- embassy-nrf/src/qspi.rs | 24 +++++++++-------------- examples/boot/bootloader/rp/Cargo.toml | 2 +- examples/boot/bootloader/stm32/Cargo.toml | 2 +- 11 files changed, 26 insertions(+), 37 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 0b0c77b1e..3312c2f9f 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -28,7 +28,7 @@ log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" -embedded-storage-async = "0.3.0" +embedded-storage-async = "0.4.0" salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } signature = { version = "1.6.4", default-features = false } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index be254e9d7..b5dad5046 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -5,7 +5,7 @@ mod fmt; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use embedded_storage_async::nor_flash::AsyncNorFlash; +use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; const BOOT_MAGIC: u8 = 0xD0; const SWAP_MAGIC: u8 = 0xF0; @@ -1199,7 +1199,7 @@ mod tests { use core::future::Future; use embedded_storage::nor_flash::ErrorType; - use embedded_storage_async::nor_flash::AsyncReadNorFlash; + use embedded_storage_async::nor_flash::ReadNorFlash as AsyncReadNorFlash; use futures::executor::block_on; use super::*; diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index c6af70144..c1a127518 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -22,7 +22,7 @@ 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" +embedded-storage-async = "0.4.0" cfg-if = "1.0.0" nrf-softdevice-mbr = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-softdevice.git", branch = "master", optional = true } diff --git a/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml index ffc36a4e0..96024cdda 100644 --- a/embassy-boot/rp/Cargo.toml +++ b/embassy-boot/rp/Cargo.toml @@ -25,7 +25,7 @@ embassy-time = { path = "../../embassy-time", features = ["nightly"] } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" -embedded-storage-async = "0.3.0" +embedded-storage-async = "0.4.0" cfg-if = "1.0.0" [features] diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index 2fc169b32..7061063bb 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml @@ -24,7 +24,7 @@ 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" +embedded-storage-async = "0.4.0" cfg-if = "1.0.0" [features] diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index fa74be8c4..45eb0d43d 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -22,7 +22,7 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 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 } +embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" defmt = { version = "0.3", optional = true } diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index 3680984f1..a49f8df4b 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -1,7 +1,5 @@ //! Adapters between embedded-hal traits. -use core::future::Future; - use embedded_hal_02::{blocking, serial}; /// Wrapper that implements async traits using blocking implementations. @@ -182,7 +180,7 @@ where /// NOR flash wrapper use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; -use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; impl ErrorType for BlockingAsync where @@ -198,14 +196,12 @@ where const WRITE_SIZE: usize = ::WRITE_SIZE; const ERASE_SIZE: usize = ::ERASE_SIZE; - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.wrapped.write(offset, data) } + async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(offset, data) } - type EraseFuture<'a> = impl Future> + 'a where Self: 'a; - fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { - async move { self.wrapped.erase(from, to) } + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.wrapped.erase(from, to) } } @@ -214,9 +210,8 @@ where T: ReadNorFlash, { const READ_SIZE: usize = ::READ_SIZE; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.wrapped.read(address, data) } + async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, data) } fn capacity(&self) -> usize { diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index c31ce199b..4e62ca89e 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -100,7 +100,7 @@ critical-section = "1.1" rand_core = "0.6.3" fixed = "1.10.0" embedded-storage = "0.3.0" -embedded-storage-async = { version = "0.3.0", optional = true } +embedded-storage-async = { version = "0.4.0", optional = true } cfg-if = "1.0.0" nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d434327fc..1b379002b 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -528,34 +528,28 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SI cfg_if::cfg_if! { if #[cfg(feature = "nightly")] { - use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - use core::future::Future; + use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { const WRITE_SIZE: usize = ::WRITE_SIZE; const ERASE_SIZE: usize = ::ERASE_SIZE; - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.write(offset as usize, data).await } + async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { + self.write(offset as usize, data).await } - type EraseFuture<'a> = impl Future> + 'a where Self: 'a; - fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { - async move { - for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { - self.erase(address).await? - } - Ok(()) + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + for address in (from as usize..to as usize).step_by(::ERASE_SIZE) { + self.erase(address).await? } + Ok(()) } } impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { const READ_SIZE: usize = 4; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { self.read(address as usize, data).await } + async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> { + self.read(address as usize, data).await } fn capacity(&self) -> usize { diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index c0b576cff..a16cebe31 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -16,7 +16,7 @@ embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } 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" +embedded-storage-async = "0.4.0" cfg-if = "1.0.0" [features] diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index be659e02a..b1791620f 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -14,7 +14,7 @@ embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features 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" +embedded-storage-async = "0.4.0" cfg-if = "1.0.0" [features] From 27e989afa95458aec4a48411dbaf58da565eaed0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Mar 2023 15:28:27 +0100 Subject: [PATCH 0671/1575] nrf/uicr: only check lowest bit. This mirrors what nrfx does. Also it won't reboot/warn if NFCPINS is set to either 0xFFFF_FFFE or 0x0000_0000, which are all valid. --- embassy-nrf/src/lib.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index cb629902a..8defc551b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -273,28 +273,24 @@ enum WriteResult { } unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { + uicr_write_masked(address, value, 0xFFFF_FFFF) +} + +unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult { let curr_val = address.read_volatile(); - if curr_val == value { + if curr_val & mask == value & mask { return WriteResult::Noop; } // We can only change `1` bits to `0` bits. - if curr_val & value != value { + if curr_val & value & mask != value & mask { return WriteResult::Failed; } - // Writing to UICR can only change `1` bits to `0` bits. - // If this write would change `0` bits to `1` bits, we can't do it. - // It is only possible to do when erasing UICR, which is forbidden if - // APPROTECT is enabled. - if (!curr_val) & value != 0 { - panic!("Cannot write UICR address={:08x} value={:08x}", address as u32, value) - } - let nvmc = &*pac::NVMC::ptr(); nvmc.config.write(|w| w.wen().wen()); while nvmc.ready.read().ready().is_busy() {} - address.write_volatile(value); + address.write_volatile(value | !mask); while nvmc.ready.read().ready().is_busy() {} nvmc.config.reset(); while nvmc.ready.read().ready().is_busy() {} @@ -393,8 +389,8 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(any(feature = "_nrf52", feature = "_nrf5340-app"))] unsafe { - let value = if cfg!(feature = "nfc-pins-as-gpio") { 0 } else { !0 }; - let res = uicr_write(consts::UICR_NFCPINS, value); + let value = if cfg!(feature = "nfc-pins-as-gpio") { 0 } else { 1 }; + let res = uicr_write_masked(consts::UICR_NFCPINS, value, 1); needs_reset |= res == WriteResult::Written; if res == WriteResult::Failed { // with nfc-pins-as-gpio, this can never fail because we're writing all zero bits. From b62e3e1d47ef6d326e4b214bd7ac1e52142f2e3d Mon Sep 17 00:00:00 2001 From: Leon Camus Date: Tue, 7 Mar 2023 23:40:20 +0100 Subject: [PATCH 0672/1575] lint: Cargo fmt --- embassy-net/src/lib.rs | 22 ++++++++-------------- embassy-net/src/udp.rs | 13 +++++-------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 57055bd77..05bbd07f2 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -307,32 +307,26 @@ impl Stack { #[cfg(feature = "igmp")] impl Stack { pub(crate) fn join_multicast_group(&self, addr: T) -> Result - where - T: Into + where + T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface.join_multicast_group( - &mut i.device, - addr, - instant_to_smoltcp(Instant::now()), - ) + s.iface + .join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) }) } pub(crate) fn leave_multicast_group(&self, addr: T) -> Result - where - T: Into + where + T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface.leave_multicast_group( - &mut i.device, - addr, - instant_to_smoltcp(Instant::now()), - ) + s.iface + .leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) }) } diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index c840eeaa2..a8a426048 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -50,10 +50,7 @@ impl<'a, D: Driver> UdpSocket<'a, D> { udp::PacketBuffer::new(tx_meta, tx_buffer), )); - Self { - stack, - handle, - } + Self { stack, handle } } pub fn bind(&mut self, endpoint: T) -> Result<(), BindError> @@ -146,15 +143,15 @@ impl<'a, D: Driver> UdpSocket<'a, D> { #[cfg(feature = "igmp")] impl<'a, D: Driver + smoltcp::phy::Device + 'static> UdpSocket<'a, D> { pub fn join_multicast_group(&self, addr: T) -> Result - where - T: Into + where + T: Into, { self.stack.join_multicast_group(addr) } pub fn leave_multicast_group(&self, addr: T) -> Result - where - T: Into + where + T: Into, { self.stack.leave_multicast_group(addr) } From 208756100304647d2ba12507b494663ddd37e90c Mon Sep 17 00:00:00 2001 From: Leon Camus Date: Tue, 7 Mar 2023 23:51:10 +0100 Subject: [PATCH 0673/1575] lint: Remove unused imports --- embassy-net/src/udp.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index a8a426048..78db9c8a0 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,4 +1,3 @@ -use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; @@ -6,7 +5,7 @@ use core::task::Poll; use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; -use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; +use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::Stack; From 993875e11fc58a476b1e08d4aba752b83bb2b885 Mon Sep 17 00:00:00 2001 From: Leon Camus Date: Tue, 7 Mar 2023 23:52:25 +0100 Subject: [PATCH 0674/1575] fix: Add qualified imports --- embassy-net/src/udp.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 78db9c8a0..12bdbf402 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -143,19 +143,19 @@ impl<'a, D: Driver> UdpSocket<'a, D> { impl<'a, D: Driver + smoltcp::phy::Device + 'static> UdpSocket<'a, D> { pub fn join_multicast_group(&self, addr: T) -> Result where - T: Into, + T: Into, { self.stack.join_multicast_group(addr) } pub fn leave_multicast_group(&self, addr: T) -> Result where - T: Into, + T: Into, { self.stack.leave_multicast_group(addr) } - pub fn has_multicast_group>(&self, addr: T) -> bool { + pub fn has_multicast_group>(&self, addr: T) -> bool { self.stack.has_multicast_group(addr) } } From a614e697d0ab8f45c59e136ad5015cfdedac50c3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 01:59:06 +0100 Subject: [PATCH 0675/1575] macros: better validation of function signatures. Fixes #1266 --- embassy-macros/src/macros/main.rs | 21 +++++++++++++++++++++ embassy-macros/src/macros/task.rs | 23 ++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index 18f7c36c4..7af4ef836 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -1,6 +1,7 @@ use darling::FromMeta; use proc_macro2::TokenStream; use quote::quote; +use syn::{ReturnType, Type}; use crate::util::ctxt::Ctxt; @@ -76,6 +77,26 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Resul if !f.sig.generics.params.is_empty() { ctxt.error_spanned_by(&f.sig, "main function must not be generic"); } + if !f.sig.generics.where_clause.is_none() { + ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses"); + } + if !f.sig.abi.is_none() { + ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier"); + } + if !f.sig.variadic.is_none() { + ctxt.error_spanned_by(&f.sig, "main function must not be variadic"); + } + match &f.sig.output { + ReturnType::Default => {} + ReturnType::Type(_, ty) => match &**ty { + Type::Tuple(tuple) if tuple.elems.is_empty() => {} + Type::Never(_) => {} + _ => ctxt.error_spanned_by( + &f.sig, + "main function must either not return a value, return `()` or return `!`", + ), + }, + } if fargs.len() != 1 { ctxt.error_spanned_by(&f.sig, "main function must have 1 argument: the spawner."); diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 90d2cd893..9f30cf43e 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs @@ -1,7 +1,7 @@ use darling::FromMeta; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{parse_quote, ItemFn}; +use syn::{parse_quote, ItemFn, ReturnType, Type}; use crate::util::ctxt::Ctxt; @@ -24,6 +24,27 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result {} + ReturnType::Type(_, ty) => match &**ty { + Type::Tuple(tuple) if tuple.elems.is_empty() => {} + Type::Never(_) => {} + _ => ctxt.error_spanned_by( + &f.sig, + "task functions must either not return a value, return `()` or return `!`", + ), + }, + } + if pool_size < 1 { ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater"); } From e7a19a97259a5d3055db7164bc162ad7c7035420 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 02:03:39 +0100 Subject: [PATCH 0676/1575] macros/main: copy fn return to task. This prevents this bad code from compiling. ```rust async fn main(_spawner: Spawner) -> ! { // not really noreturn! } ``` --- embassy-macros/src/macros/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index 7af4ef836..6ae77398d 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -105,10 +105,11 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Resul ctxt.check()?; let f_body = f.block; + let out = &f.sig.output; let result = quote! { #[::embassy_executor::task()] - async fn __embassy_main(#fargs) { + async fn __embassy_main(#fargs) #out { #f_body } From b2c6dc45e3d87c7254ac20440998aa9f559ae94e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 03:08:59 +0100 Subject: [PATCH 0677/1575] Fix examples broken by the macro fix. --- examples/stm32f4/src/bin/i2c.rs | 2 +- examples/stm32f4/src/bin/sdmmc.rs | 4 +--- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32f7/src/bin/sdmmc.rs | 4 +--- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- examples/stm32h7/src/bin/i2c.rs | 2 +- examples/stm32l4/src/bin/i2c.rs | 2 +- examples/stm32l4/src/bin/i2c_blocking_async.rs | 2 +- examples/stm32l4/src/bin/i2c_dma.rs | 2 +- 10 files changed, 10 insertions(+), 14 deletions(-) diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 6e51c211d..f8ae0890c 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -15,7 +15,7 @@ const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index 1d0e60cb8..ebdfdb22d 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; const ALLOW_WRITES: bool = false; #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { let mut config = Config::default(); config.rcc.sys_ck = Some(mhz(48)); config.rcc.pll48 = true; @@ -75,6 +75,4 @@ async fn main(_spawner: Spawner) -> ! { sdmmc.read_block(block_idx, &mut block).await.unwrap(); info!("Read: {=[u8]:X}...{=[u8]:X}", block[..8], block[512 - 8..]); - - loop {} } diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 571a6c1b9..9febb14e6 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -106,7 +106,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(&buf).await; if let Err(e) = r { info!("write error: {:?}", e); - return; + continue; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index cf8128e27..c050a4002 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -10,7 +10,7 @@ use embassy_stm32::{interrupt, Config}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { let mut config = Config::default(); config.rcc.sys_ck = Some(mhz(200)); config.rcc.pll48 = true; @@ -41,6 +41,4 @@ async fn main(_spawner: Spawner) -> ! { let card = unwrap!(sdmmc.card()); info!("Card: {:#?}", Debug2Format(card)); - - loop {} } diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index cb245c325..541e49762 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -106,7 +106,7 @@ async fn main(spawner: Spawner) -> ! { let r = socket.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - return; + continue; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index cce85a083..b609fa5df 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -108,7 +108,7 @@ async fn main(spawner: Spawner) -> ! { let r = connection.write_all(b"Hello\n").await; if let Err(e) = r { info!("write error: {:?}", e); - return; + continue; } Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs index d44319ae6..78e03f014 100644 --- a/examples/stm32h7/src/bin/i2c.rs +++ b/examples/stm32h7/src/bin/i2c.rs @@ -14,7 +14,7 @@ const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); diff --git a/examples/stm32l4/src/bin/i2c.rs b/examples/stm32l4/src/bin/i2c.rs index d54c080c7..d40d6803d 100644 --- a/examples/stm32l4/src/bin/i2c.rs +++ b/examples/stm32l4/src/bin/i2c.rs @@ -14,7 +14,7 @@ const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new( diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs index 35a86660d..d868cac01 100644 --- a/examples/stm32l4/src/bin/i2c_blocking_async.rs +++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs @@ -16,7 +16,7 @@ const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let irq = interrupt::take!(I2C2_EV); let i2c = I2c::new( diff --git a/examples/stm32l4/src/bin/i2c_dma.rs b/examples/stm32l4/src/bin/i2c_dma.rs index 3ce9398a4..7e62ee637 100644 --- a/examples/stm32l4/src/bin/i2c_dma.rs +++ b/examples/stm32l4/src/bin/i2c_dma.rs @@ -13,7 +13,7 @@ const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; #[embassy_executor::main] -async fn main(_spawner: Spawner) -> ! { +async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new( From e484cb1b875adc7a0865299df5167f94302fcf49 Mon Sep 17 00:00:00 2001 From: Leon Camus Date: Wed, 8 Mar 2023 12:37:00 +0100 Subject: [PATCH 0678/1575] refactor: Multicast method modifiers on stack to public revert: udp.rs --- embassy-net/src/lib.rs | 6 +++--- embassy-net/src/udp.rs | 47 ++++++++++++++---------------------------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 05bbd07f2..7b9d0e773 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -306,7 +306,7 @@ impl Stack { #[cfg(feature = "igmp")] impl Stack { - pub(crate) fn join_multicast_group(&self, addr: T) -> Result + pub fn join_multicast_group(&self, addr: T) -> Result where T: Into, { @@ -318,7 +318,7 @@ impl Stack { }) } - pub(crate) fn leave_multicast_group(&self, addr: T) -> Result + pub fn leave_multicast_group(&self, addr: T) -> Result where T: Into, { @@ -330,7 +330,7 @@ impl Stack { }) } - pub(crate) fn has_multicast_group>(&self, addr: T) -> bool { + pub fn has_multicast_group>(&self, addr: T) -> bool { self.socket.borrow().iface.has_multicast_group(addr) } } diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 12bdbf402..0ee8c6e19 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,3 +1,4 @@ +use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; @@ -7,7 +8,7 @@ use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::Stack; +use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -25,13 +26,13 @@ pub enum Error { NoRoute, } -pub struct UdpSocket<'a, D: Driver> { - stack: &'a Stack, +pub struct UdpSocket<'a> { + stack: &'a RefCell, handle: SocketHandle, } -impl<'a, D: Driver> UdpSocket<'a, D> { - pub fn new( +impl<'a> UdpSocket<'a> { + pub fn new( stack: &'a Stack, rx_meta: &'a mut [PacketMetadata], rx_buffer: &'a mut [u8], @@ -49,7 +50,10 @@ impl<'a, D: Driver> UdpSocket<'a, D> { udp::PacketBuffer::new(tx_meta, tx_buffer), )); - Self { stack, handle } + Self { + stack: &stack.socket, + handle, + } } pub fn bind(&mut self, endpoint: T) -> Result<(), BindError> @@ -60,7 +64,7 @@ impl<'a, D: Driver> UdpSocket<'a, D> { if endpoint.port == 0 { // If user didn't specify port allocate a dynamic port. - endpoint.port = self.stack.socket.borrow_mut().get_local_port(); + endpoint.port = self.stack.borrow_mut().get_local_port(); } match self.with_mut(|s, _| s.bind(endpoint)) { @@ -71,13 +75,13 @@ impl<'a, D: Driver> UdpSocket<'a, D> { } fn with(&self, f: impl FnOnce(&udp::Socket, &Interface) -> R) -> R { - let s = &*self.stack.socket.borrow(); + let s = &*self.stack.borrow(); let socket = s.sockets.get::(self.handle); f(socket, &s.iface) } fn with_mut(&self, f: impl FnOnce(&mut udp::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.socket.borrow_mut(); + 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(); @@ -139,29 +143,8 @@ impl<'a, D: Driver> UdpSocket<'a, D> { } } -#[cfg(feature = "igmp")] -impl<'a, D: Driver + smoltcp::phy::Device + 'static> UdpSocket<'a, D> { - pub fn join_multicast_group(&self, addr: T) -> Result - where - T: Into, - { - self.stack.join_multicast_group(addr) - } - - pub fn leave_multicast_group(&self, addr: T) -> Result - where - T: Into, - { - self.stack.leave_multicast_group(addr) - } - - pub fn has_multicast_group>(&self, addr: T) -> bool { - self.stack.has_multicast_group(addr) - } -} - -impl Drop for UdpSocket<'_, D> { +impl Drop for UdpSocket<'_> { fn drop(&mut self) { - self.stack.socket.borrow_mut().sockets.remove(self.handle); + self.stack.borrow_mut().sockets.remove(self.handle); } } From 79061021f98ee01611e565a9edf89aa0dbf91bf6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 9 Mar 2023 23:32:00 +0100 Subject: [PATCH 0679/1575] time: add power-of-2 kHz tick rates. Fixes #1269 --- embassy-time/Cargo.toml | 19 ++++++++++++++ embassy-time/gen_tick.py | 12 ++++++--- embassy-time/src/tick.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 5701ab351..5b14814a1 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -101,6 +101,25 @@ tick-hz-2_097_152 = [] tick-hz-4_194_304 = [] tick-hz-8_388_608 = [] tick-hz-16_777_216 = [] +tick-hz-2_000 = [] +tick-hz-4_000 = [] +tick-hz-8_000 = [] +tick-hz-16_000 = [] +tick-hz-32_000 = [] +tick-hz-64_000 = [] +tick-hz-128_000 = [] +tick-hz-256_000 = [] +tick-hz-512_000 = [] +tick-hz-1_024_000 = [] +tick-hz-2_048_000 = [] +tick-hz-4_096_000 = [] +tick-hz-8_192_000 = [] +tick-hz-16_384_000 = [] +tick-hz-32_768_000 = [] +tick-hz-65_536_000 = [] +tick-hz-131_072_000 = [] +tick-hz-262_144_000 = [] +tick-hz-524_288_000 = [] tick-hz-2_000_000 = [] tick-hz-3_000_000 = [] tick-hz-4_000_000 = [] diff --git a/embassy-time/gen_tick.py b/embassy-time/gen_tick.py index 651ebbd72..15e65187b 100644 --- a/embassy-time/gen_tick.py +++ b/embassy-time/gen_tick.py @@ -11,6 +11,8 @@ for i in range(10): ticks.append(10**i) for i in range(1, 25): ticks.append(2**i) +for i in range(1, 20): + ticks.append(2**i * 1000) for i in range(1, 10): ticks.append(2**i * 1000000) ticks.append(2**i * 9 // 8 * 1000000) @@ -30,21 +32,23 @@ with open('Cargo.toml', 'r') as f: data = f.read() before, data = data.split(SEPARATOR_START, maxsplit=1) _, after = data.split(SEPARATOR_END, maxsplit=1) -data = before + SEPARATOR_START + HELP + toml.dumps(things) + SEPARATOR_END + after +data = before + SEPARATOR_START + HELP + \ + toml.dumps(things) + SEPARATOR_END + after with open('Cargo.toml', 'w') as f: f.write(data) # ========= Update src/tick.rs with open('src/tick.rs', 'w') as f: - + f.write('// Generated by gen_tick.py. DO NOT EDIT.\n\n') for hz in ticks: - f.write(f'#[cfg(feature = "tick-hz-{hz:_}")] pub const TICK_HZ: u64 = {hz:_};\n') + f.write( + f'#[cfg(feature = "tick-hz-{hz:_}")] pub const TICK_HZ: u64 = {hz:_};\n') f.write('#[cfg(not(any(\n') for hz in ticks: f.write(f'feature = "tick-hz-{hz:_}",\n') f.write(')))] pub const TICK_HZ: u64 = 1_000_000;') -os.system('rustfmt src/tick.rs') \ No newline at end of file +os.system('rustfmt src/tick.rs') diff --git a/embassy-time/src/tick.rs b/embassy-time/src/tick.rs index a79006506..608bc44f1 100644 --- a/embassy-time/src/tick.rs +++ b/embassy-time/src/tick.rs @@ -68,6 +68,44 @@ pub const TICK_HZ: u64 = 4_194_304; pub const TICK_HZ: u64 = 8_388_608; #[cfg(feature = "tick-hz-16_777_216")] pub const TICK_HZ: u64 = 16_777_216; +#[cfg(feature = "tick-hz-2_000")] +pub const TICK_HZ: u64 = 2_000; +#[cfg(feature = "tick-hz-4_000")] +pub const TICK_HZ: u64 = 4_000; +#[cfg(feature = "tick-hz-8_000")] +pub const TICK_HZ: u64 = 8_000; +#[cfg(feature = "tick-hz-16_000")] +pub const TICK_HZ: u64 = 16_000; +#[cfg(feature = "tick-hz-32_000")] +pub const TICK_HZ: u64 = 32_000; +#[cfg(feature = "tick-hz-64_000")] +pub const TICK_HZ: u64 = 64_000; +#[cfg(feature = "tick-hz-128_000")] +pub const TICK_HZ: u64 = 128_000; +#[cfg(feature = "tick-hz-256_000")] +pub const TICK_HZ: u64 = 256_000; +#[cfg(feature = "tick-hz-512_000")] +pub const TICK_HZ: u64 = 512_000; +#[cfg(feature = "tick-hz-1_024_000")] +pub const TICK_HZ: u64 = 1_024_000; +#[cfg(feature = "tick-hz-2_048_000")] +pub const TICK_HZ: u64 = 2_048_000; +#[cfg(feature = "tick-hz-4_096_000")] +pub const TICK_HZ: u64 = 4_096_000; +#[cfg(feature = "tick-hz-8_192_000")] +pub const TICK_HZ: u64 = 8_192_000; +#[cfg(feature = "tick-hz-16_384_000")] +pub const TICK_HZ: u64 = 16_384_000; +#[cfg(feature = "tick-hz-32_768_000")] +pub const TICK_HZ: u64 = 32_768_000; +#[cfg(feature = "tick-hz-65_536_000")] +pub const TICK_HZ: u64 = 65_536_000; +#[cfg(feature = "tick-hz-131_072_000")] +pub const TICK_HZ: u64 = 131_072_000; +#[cfg(feature = "tick-hz-262_144_000")] +pub const TICK_HZ: u64 = 262_144_000; +#[cfg(feature = "tick-hz-524_288_000")] +pub const TICK_HZ: u64 = 524_288_000; #[cfg(feature = "tick-hz-2_000_000")] pub const TICK_HZ: u64 = 2_000_000; #[cfg(feature = "tick-hz-3_000_000")] @@ -153,6 +191,25 @@ pub const TICK_HZ: u64 = 768_000_000; feature = "tick-hz-4_194_304", feature = "tick-hz-8_388_608", feature = "tick-hz-16_777_216", + feature = "tick-hz-2_000", + feature = "tick-hz-4_000", + feature = "tick-hz-8_000", + feature = "tick-hz-16_000", + feature = "tick-hz-32_000", + feature = "tick-hz-64_000", + feature = "tick-hz-128_000", + feature = "tick-hz-256_000", + feature = "tick-hz-512_000", + feature = "tick-hz-1_024_000", + feature = "tick-hz-2_048_000", + feature = "tick-hz-4_096_000", + feature = "tick-hz-8_192_000", + feature = "tick-hz-16_384_000", + feature = "tick-hz-32_768_000", + feature = "tick-hz-65_536_000", + feature = "tick-hz-131_072_000", + feature = "tick-hz-262_144_000", + feature = "tick-hz-524_288_000", feature = "tick-hz-2_000_000", feature = "tick-hz-3_000_000", feature = "tick-hz-4_000_000", From 12d6e37b3f471186f96275b585f5512704154a17 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sat, 11 Mar 2023 02:58:28 -0500 Subject: [PATCH 0680/1575] Example using the PIO to drive WS2812 aka Neopixel RGB leds This example also uses a pio program compiled at runtime, rather than one built at compile time. There's no reason to do that, but it's probably useful to have an example that does this as well. --- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/ws2812-pio.rs | 142 ++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 examples/rp/src/bin/ws2812-pio.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index f07684f29..1e8870ed7 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -28,6 +28,7 @@ embedded-graphics = "0.7.1" st7789 = "0.6.1" display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } +smart-leds = "0.3.0" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } embedded-hal-async = "0.2.0-alpha.0" diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs new file mode 100644 index 000000000..5f8a3baee --- /dev/null +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -0,0 +1,142 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::gpio::{self, Pin}; +use embassy_rp::pio::{ + FifoJoin, PioInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, +}; +use embassy_rp::pio_instr_util; +use embassy_rp::relocate::RelocatedProgram; +use embassy_time::{Duration, Timer}; +use smart_leds::RGB8; +use {defmt_rtt as _, panic_probe as _}; +pub struct Ws2812 { + sm: PioStateMachineInstance, +} + +impl Ws2812 { + pub fn new(mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { + // Setup sm0 + + // prepare the PIO program + let side_set = pio::SideSet::new(false, 1, false); + let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); + + const T1: u8 = 2; // start bit + const T2: u8 = 5; // data bit + const T3: u8 = 3; // stop bit + const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; + + let mut wrap_target = a.label(); + let mut wrap_source = a.label(); + let mut do_zero = a.label(); + a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); + a.bind(&mut wrap_target); + // Do stop bit + a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); + // Do start bit + a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); + // Do data bit = 1 + a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); + a.bind(&mut do_zero); + // Do data bit = 0 + a.nop_with_delay_and_side_set(T2 - 1, 0); + a.bind(&mut wrap_source); + + let prg = a.assemble_with_wrap(wrap_source, wrap_target); + + let relocated = RelocatedProgram::new(&prg); + sm.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(&mut sm, relocated.origin()); + + // Pin config + let out_pin = sm.make_pio_pin(pin); + sm.set_set_pins(&[&out_pin]); + sm.set_sideset_base_pin(&out_pin); + sm.set_sideset_count(1); + + // Clock config + // TODO CLOCK_FREQ should come from embassy_rp + const CLOCK_FREQ: u32 = 125_000_000; + const WS2812_FREQ: u32 = 800_000; + + let bit_freq = WS2812_FREQ * CYCLES_PER_BIT; + let mut int = CLOCK_FREQ / bit_freq; + let rem = CLOCK_FREQ - (int * bit_freq); + let frac = (rem * 256) / bit_freq; + // 65536.0 is represented as 0 in the pio's clock divider + if int == 65536 { + int = 0; + } + + sm.set_clkdiv((int << 8) | frac); + let pio::Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + + // FIFO config + sm.set_autopull(true); + sm.set_fifo_join(FifoJoin::TxOnly); + sm.set_pull_threshold(24); + sm.set_out_shift_dir(ShiftDirection::Left); + + sm.set_enable(true); + + Self { sm } + } + + pub async fn write(&mut self, colors: &[RGB8]) { + for color in colors { + let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); + self.sm.wait_push(word).await; + } + } +} + +/// Input a value 0 to 255 to get a color value +/// The colours are a transition r - g - b - back to r. +fn wheel(mut wheel_pos: u8) -> RGB8 { + wheel_pos = 255 - wheel_pos; + if wheel_pos < 85 { + return (255 - wheel_pos * 3, 0, wheel_pos * 3).into(); + } + if wheel_pos < 170 { + wheel_pos -= 85; + return (0, wheel_pos * 3, 255 - wheel_pos * 3).into(); + } + wheel_pos -= 170; + (wheel_pos * 3, 255 - wheel_pos * 3, 0).into() +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Start"); + let p = embassy_rp::init(Default::default()); + + let (_pio0, sm0, _sm1, _sm2, _sm3) = p.PIO0.split(); + + // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit + // feather boards for the 2040 both have one built in. + const NUM_LEDS: usize = 1; + let mut data = [RGB8::default(); NUM_LEDS]; + + // For the thing plus, use pin 8 + // For the feather, use pin 16 + let mut ws2812 = Ws2812::new(sm0, p.PIN_8.degrade()); + + // Loop forever making RGB values and pushing them out to the WS2812. + loop { + for j in 0..(256 * 5) { + debug!("New Colors:"); + for i in 0..NUM_LEDS { + data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8); + debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b); + } + ws2812.write(&data).await; + + Timer::after(Duration::from_micros(5)).await; + } + } +} From bce1ce7dcb7fff272a2658a8dda08b70af679e7b Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 16 Feb 2023 08:47:22 +0100 Subject: [PATCH 0681/1575] Allow upgrading a blocking uart to a BufferedUart, and implement blocking serial traits for BufferedUart --- embassy-rp/src/uart/buffered.rs | 340 +++++++++++++++++++++++++++----- embassy-rp/src/uart/mod.rs | 50 ++++- 2 files changed, 340 insertions(+), 50 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 0da5bfca1..32e5ddf14 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -28,30 +28,23 @@ impl State { } pub struct BufferedUart<'d, T: Instance> { - rx: BufferedUartRx<'d, T>, - tx: BufferedUartTx<'d, T>, + pub(crate) rx: BufferedUartRx<'d, T>, + pub(crate) tx: BufferedUartTx<'d, T>, } pub struct BufferedUartRx<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, + pub(crate) phantom: PhantomData<&'d mut T>, } pub struct BufferedUartTx<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, + pub(crate) phantom: PhantomData<&'d mut T>, } -fn init<'d, T: Instance + 'd>( +pub(crate) fn init_buffers<'d, T: Instance + 'd>( irq: PeripheralRef<'d, T::Interrupt>, - tx: Option>, - rx: Option>, - rts: Option>, - cts: Option>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], - config: Config, ) { - super::Uart::<'d, T, Async>::init(tx, rx, rts, cts, config); - let state = T::state(); let len = tx_buffer.len(); unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; @@ -69,7 +62,13 @@ fn init<'d, T: Instance + 'd>( // we clear it after it happens. The downside is that the we manually have // to pend the ISR when we want data transmission to start. let regs = T::regs(); - unsafe { regs.uartimsc().write_set(|w| w.set_txim(true)) }; + unsafe { + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + w.set_txim(true); + }); + }; irq.set_handler(on_interrupt::); irq.unpend(); @@ -87,16 +86,10 @@ impl<'d, T: Instance> BufferedUart<'d, T> { config: Config, ) -> Self { into_ref!(irq, tx, rx); - init::( - irq, - Some(tx.map_into()), - Some(rx.map_into()), - None, - None, - tx_buffer, - rx_buffer, - config, - ); + + super::Uart::<'d, T, Async>::init(Some(tx.map_into()), Some(rx.map_into()), None, None, config); + init_buffers::(irq, tx_buffer, rx_buffer); + Self { rx: BufferedUartRx { phantom: PhantomData }, tx: BufferedUartTx { phantom: PhantomData }, @@ -115,22 +108,34 @@ impl<'d, T: Instance> BufferedUart<'d, T> { config: Config, ) -> Self { into_ref!(irq, tx, rx, cts, rts); - init::( - irq, + + super::Uart::<'d, T, Async>::init( Some(tx.map_into()), Some(rx.map_into()), Some(rts.map_into()), Some(cts.map_into()), - tx_buffer, - rx_buffer, config, ); + init_buffers::(irq, tx_buffer, rx_buffer); + Self { rx: BufferedUartRx { phantom: PhantomData }, tx: BufferedUartTx { phantom: PhantomData }, } } + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.blocking_write(buffer) + } + + pub fn blocking_flush(&mut self) -> Result<(), Error> { + self.tx.blocking_flush() + } + + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.rx.blocking_read(buffer) + } + pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { (self.rx, self.tx) } @@ -145,7 +150,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { config: Config, ) -> Self { into_ref!(irq, rx); - init::(irq, None, Some(rx.map_into()), None, None, &mut [], rx_buffer, config); + + super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), None, None, config); + init_buffers::(irq, &mut [], rx_buffer); + Self { phantom: PhantomData } } @@ -158,16 +166,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { config: Config, ) -> Self { into_ref!(irq, rx, rts); - init::( - irq, - None, - Some(rx.map_into()), - Some(rts.map_into()), - None, - &mut [], - rx_buffer, - config, - ); + + super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), Some(rts.map_into()), None, config); + init_buffers::(irq, &mut [], rx_buffer); + Self { phantom: PhantomData } } @@ -199,6 +201,32 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }) } + pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), Error> { + loop { + 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 { + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + let regs = T::regs(); + unsafe { + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } + + return Ok(()); + } + } + } + fn fill_buf<'a>() -> impl Future> { poll_fn(move |cx| { let state = T::state(); @@ -240,7 +268,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { config: Config, ) -> Self { into_ref!(irq, tx); - init::(irq, Some(tx.map_into()), None, None, None, tx_buffer, &mut [], config); + + super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, None, config); + init_buffers::(irq, tx_buffer, &mut []); + Self { phantom: PhantomData } } @@ -253,16 +284,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { config: Config, ) -> Self { into_ref!(irq, tx, cts); - init::( - irq, - Some(tx.map_into()), - None, - None, - Some(cts.map_into()), - tx_buffer, - &mut [], - config, - ); + + super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, Some(cts.map_into()), config); + init_buffers::(irq, tx_buffer, &mut []); + Self { phantom: PhantomData } } @@ -300,6 +325,36 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { Poll::Ready(Ok(())) }) } + + pub fn blocking_write(&mut self, buf: &[u8]) -> Result<(), Error> { + loop { + 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 n != 0 { + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. + unsafe { T::Interrupt::steal() }.pend(); + return Ok(()); + } + } + } + + pub fn blocking_flush(&mut self) -> Result<(), Error> { + loop { + let state = T::state(); + if state.tx_buf.is_empty() { + return Ok(()); + } + } + } } impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { @@ -477,3 +532,190 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> Self::flush().await } } + +mod eh02 { + use super::*; + + impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUartRx<'d, T> { + type Error = Error; + + fn read(&mut self) -> Result> { + let r = T::regs(); + unsafe { + if r.uartfr().read().rxfe() { + return Err(nb::Error::WouldBlock); + } + + let dr = r.uartdr().read(); + + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else { + Ok(dr.data()) + } + } + } + } + + impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUartTx<'d, T> { + type Error = Error; + + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance> embedded_hal_02::serial::Read for BufferedUart<'d, T> { + type Error = Error; + + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUart<'d, T> { + type Error = Error; + + fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } +} + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + type Error = Error; + } + + impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> { + fn read(&mut self) -> nb::Result { + embedded_hal_02::serial::Read::read(self) + } + } + + impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: Instance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } +} + +#[cfg(all( + feature = "unstable-traits", + feature = "nightly", + feature = "_todo_embedded_hal_serial" +))] +mod eha { + use core::future::Future; + + use super::*; + + impl<'d, T: Instance> embedded_hal_async::serial::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> { + Self::write(buf) + } + + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + Self::flush() + } + } + + impl<'d, T: Instance> embedded_hal_async::serial::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> { + Self::read(buf) + } + } + + impl<'d, T: Instance> embedded_hal_async::serial::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> { + BufferedUartTx::<'d, T>::write(buf) + } + + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + BufferedUartTx::<'d, T>::flush() + } + } + + impl<'d, T: Instance> embedded_hal_async::serial::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> { + BufferedUartRx::<'d, T>::read(buf) + } + } +} diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 42b3671a0..97f4463e5 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1,11 +1,12 @@ use core::marker::PhantomData; +use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; -use crate::{pac, peripherals, Peripheral}; +use crate::{pac, peripherals, Peripheral, RegExt}; #[cfg(feature = "nightly")] mod buffered; @@ -135,6 +136,21 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { } } +impl<'d, T: Instance> UartTx<'d, T, Blocking> { + #[cfg(feature = "nightly")] + pub fn into_buffered( + self, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + ) -> BufferedUartTx<'d, T> { + into_ref!(irq); + + buffered::init_buffers::(irq, tx_buffer, &mut []); + + BufferedUartTx { phantom: PhantomData } + } +} + impl<'d, T: Instance> UartTx<'d, T, Async> { pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { let ch = self.tx_dma.as_mut().unwrap(); @@ -200,6 +216,21 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { } } +impl<'d, T: Instance> UartRx<'d, T, Blocking> { + #[cfg(feature = "nightly")] + pub fn into_buffered( + self, + irq: impl Peripheral

+ 'd, + rx_buffer: &'d mut [u8], + ) -> BufferedUartRx<'d, T> { + into_ref!(irq); + + buffered::init_buffers::(irq, &mut [], rx_buffer); + + BufferedUartRx { phantom: PhantomData } + } +} + impl<'d, T: Instance> UartRx<'d, T, Async> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { let ch = self.rx_dma.as_mut().unwrap(); @@ -249,6 +280,23 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { config, ) } + + #[cfg(feature = "nightly")] + pub fn into_buffered( + self, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + ) -> BufferedUart<'d, T> { + into_ref!(irq); + + buffered::init_buffers::(irq, tx_buffer, rx_buffer); + + BufferedUart { + rx: BufferedUartRx { phantom: PhantomData }, + tx: BufferedUartTx { phantom: PhantomData }, + } + } } impl<'d, T: Instance> Uart<'d, T, Async> { From 89a371d10c14768e45846d09d98682ec4ae7a229 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 14 Mar 2023 10:54:15 +0100 Subject: [PATCH 0682/1575] Add HIL test for into_buffered uart on embassy-rp --- embassy-rp/src/uart/mod.rs | 3 +- tests/rp/src/bin/uart_upgrade.rs | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/rp/src/bin/uart_upgrade.rs diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 97f4463e5..682243a27 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1,12 +1,11 @@ use core::marker::PhantomData; -use embassy_cortex_m::interrupt::InterruptExt; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; -use crate::{pac, peripherals, Peripheral, RegExt}; +use crate::{pac, peripherals, Peripheral}; #[cfg(feature = "nightly")] mod buffered; diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs new file mode 100644 index 000000000..d8c9aecf6 --- /dev/null +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -0,0 +1,54 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::uart::{Config, Uart}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let config = Config::default(); + let mut uart = Uart::new_blocking(uart, tx, rx, config); + + // We can't send too many bytes, they have to fit in the FIFO. + // This is because we aren't sending+receiving at the same time. + + let data = [0xC0, 0xDE]; + uart.blocking_write(&data).unwrap(); + + let mut buf = [0; 2]; + uart.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, data); + + let irq = interrupt::take!(UART0_IRQ); + let tx_buf = &mut [0u8; 16]; + let rx_buf = &mut [0u8; 16]; + + let mut uart = uart.into_buffered(irq, tx_buf, rx_buf); + + // Make sure we send more bytes than fits in the FIFO, to test the actual + // bufferedUart. + + let data = [ + 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; 31]; + uart.read_exact(&mut buf).await.unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 43462947edd69b54da775c690d7e1e28adb9580c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 14 Mar 2023 17:27:40 +0100 Subject: [PATCH 0683/1575] stm32: remove unused embedded-storage-async. --- embassy-stm32/Cargo.toml | 3 +-- embassy-stm32/src/flash/mod.rs | 43 ---------------------------------- 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 82b1ba3a0..1f430c7c6 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -49,7 +49,6 @@ 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" -embedded-storage-async = { version = "0.3.0", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -96,7 +95,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 988cf9fae..b7166a437 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -135,46 +135,3 @@ impl<'d> NorFlash for Flash<'d> { self.blocking_write(offset, bytes) } } - -/* -cfg_if::cfg_if! { - if #[cfg(feature = "nightly")] - { - use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; - use core::future::Future; - - impl<'d> AsyncNorFlash for Flash<'d> { - const WRITE_SIZE: usize = ::WRITE_SIZE; - const ERASE_SIZE: usize = ::ERASE_SIZE; - - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { - async move { - todo!() - } - } - - type EraseFuture<'a> = impl Future> + 'a where Self: 'a; - fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { - async move { - todo!() - } - } - } - - impl<'d> AsyncReadNorFlash for Flash<'d> { - const READ_SIZE: usize = ::READ_SIZE; - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - todo!() - } - } - - fn capacity(&self) -> usize { - FLASH_SIZE - } - } - } -} -*/ From 46b437dea020bb76eedc30124e83618b55751f1d Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Wed, 15 Mar 2023 13:56:40 +0100 Subject: [PATCH 0684/1575] Wait for waker when no data is available on UDP socket. --- embassy-net/src/udp.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 0ee8c6e19..476aef12f 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -94,8 +94,7 @@ impl<'a> UdpSocket<'a> { Ok(x) => Poll::Ready(Ok(x)), // No data ready Err(udp::RecvError::Exhausted) => { - //s.register_recv_waker(cx.waker()); - cx.waker().wake_by_ref(); + s.register_recv_waker(cx.waker()); Poll::Pending } }) From 472df3fad6dde82b15e8f4716291add8b1cae1ae Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 15 Mar 2023 16:45:18 +0100 Subject: [PATCH 0685/1575] fix(pubsub): Pop messages which count is 0 after unsubscribe --- embassy-sync/src/pubsub/mod.rs | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index faaf99dc6..5989e86ec 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -371,6 +371,20 @@ impl PubSubSta .iter_mut() .skip(current_message_index) .for_each(|(_, counter)| *counter -= 1); + + let mut wake_publishers = false; + while let Some((_, count)) = self.queue.front() { + if *count == 0 { + self.queue.pop_front().unwrap(); + wake_publishers = true; + } else { + break; + } + } + + if wake_publishers { + self.publisher_wakers.wake(); + } } } @@ -612,4 +626,37 @@ mod tests { sub1.next_message().await; assert_eq!(pub0.space(), 4); } + + #[futures_test::test] + async fn empty_channel_when_last_subscriber_is_dropped() { + let channel = PubSubChannel::::new(); + + let pub0 = channel.publisher().unwrap(); + let mut sub0 = channel.subscriber().unwrap(); + let mut sub1 = channel.subscriber().unwrap(); + + assert_eq!(4, pub0.space()); + + pub0.publish(1).await; + pub0.publish(2).await; + + assert_eq!(2, channel.space()); + + assert_eq!(1, sub0.try_next_message_pure().unwrap()); + assert_eq!(2, sub0.try_next_message_pure().unwrap()); + + assert_eq!(2, channel.space()); + + drop(sub0); + + assert_eq!(2, channel.space()); + + assert_eq!(1, sub1.try_next_message_pure().unwrap()); + + assert_eq!(3, channel.space()); + + drop(sub1); + + assert_eq!(4, channel.space()); + } } From 13f0c64a8c3cd7d9bc743bd0d69d4ce09c9ea11c Mon Sep 17 00:00:00 2001 From: Eric Yanush Date: Thu, 16 Mar 2023 21:21:39 -0600 Subject: [PATCH 0686/1575] Fix APB clock calculation for several STM32 families --- embassy-stm32/src/rcc/l0.rs | 2 +- embassy-stm32/src/rcc/l1.rs | 2 +- embassy-stm32/src/rcc/l4.rs | 2 +- embassy-stm32/src/rcc/l5.rs | 2 +- embassy-stm32/src/rcc/u5.rs | 4 ++-- embassy-stm32/src/rcc/wb.rs | 2 +- embassy-stm32/src/rcc/wl.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs index c3d29f164..42a481a74 100644 --- a/embassy-stm32/src/rcc/l0.rs +++ b/embassy-stm32/src/rcc/l0.rs @@ -313,7 +313,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: Ppre = pre.into(); let pre: u8 = 1 << (pre.0 - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs index e0180b24f..c907fa88a 100644 --- a/embassy-stm32/src/rcc/l1.rs +++ b/embassy-stm32/src/rcc/l1.rs @@ -314,7 +314,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: Ppre = pre.into(); let pre: u8 = 1 << (pre.0 - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index c820018ac..e650490fe 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -483,7 +483,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: Ppre = pre.into(); let pre: u8 = 1 << (pre.0 - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs index 81bf36be0..f56fce365 100644 --- a/embassy-stm32/src/rcc/l5.rs +++ b/embassy-stm32/src/rcc/l5.rs @@ -481,7 +481,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: Ppre = pre.into(); let pre: u8 = 1 << (pre.0 - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 2ba339ecf..81507a4d6 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -481,7 +481,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; @@ -491,7 +491,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index c9ada83e2..e6123821a 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -151,7 +151,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 82b0d04e2..7072db984 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -240,7 +240,7 @@ pub(crate) unsafe fn init(config: Config) { pre => { let pre: u8 = pre.into(); let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / (1 << (pre as u8 - 3)); + let freq = ahb_freq / pre as u32; (freq, freq * 2) } }; From 7be385dbb182a2bdafa286e4eb214bfea0190342 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 17 Mar 2023 11:40:19 +0100 Subject: [PATCH 0687/1575] Add must_use to OnDrop --- embassy-hal-common/src/drop.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-hal-common/src/drop.rs b/embassy-hal-common/src/drop.rs index 6ef716f99..7cd16aaec 100644 --- a/embassy-hal-common/src/drop.rs +++ b/embassy-hal-common/src/drop.rs @@ -1,6 +1,7 @@ use core::mem; use core::mem::MaybeUninit; +#[must_use = "to delay the drop handler invokation to the end of the scope"] pub struct OnDrop { f: MaybeUninit, } @@ -27,6 +28,7 @@ impl Drop for OnDrop { /// /// To correctly dispose of this device, call the [defuse](struct.DropBomb.html#method.defuse) /// method before this object is dropped. +#[must_use = "to delay the drop bomb invokation to the end of the scope"] pub struct DropBomb { _private: (), } From d57fe0de867cfc6510f0192fab488355d9ae8586 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Sun, 19 Feb 2023 16:31:33 +0100 Subject: [PATCH 0688/1575] Custom Bus Trait to support PIO --- examples/rpi-pico-w/build.rs | 34 +++++------ examples/rpi-pico-w/src/main.rs | 11 ++++ src/bus.rs | 100 +++++++++++++++++++++++--------- src/lib.rs | 8 ++- 4 files changed, 104 insertions(+), 49 deletions(-) diff --git a/examples/rpi-pico-w/build.rs b/examples/rpi-pico-w/build.rs index 3f915f931..d4c3ec89d 100644 --- a/examples/rpi-pico-w/build.rs +++ b/examples/rpi-pico-w/build.rs @@ -14,23 +14,23 @@ 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()); + // // 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"); + // // 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"); - println!("cargo:rustc-link-arg-bins=-Tdefmt.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"); + // println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); } diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index c706e121d..f768af193 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -161,6 +161,17 @@ impl ErrorType for MySpi { type Error = Infallible; } +impl cyw43::SpiBusCyw43 for MySpi { + async fn cmd_write<'a>(&'a mut self, write: &'a [u32]) -> Result<(), Self::Error> { + self.write(write).await + } + + async fn cmd_read<'a>(&'a mut self, write: &'a [u32], read: &'a mut [u32]) -> Result<(), Self::Error> { + self.write(write).await?; + self.read(read).await + } +} + impl SpiBusFlush for MySpi { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) diff --git a/src/bus.rs b/src/bus.rs index f64c0abba..1c8bb9893 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -2,10 +2,23 @@ use core::slice; use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; +use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice}; use crate::consts::*; +/// Custom Spi Trait that _only_ supports the bus operation of the cyw43 +pub trait SpiBusCyw43: ErrorType { + /// Issues a write command on the bus + /// Frist 32 bits of `word` are expected to be a cmd word + async fn cmd_write<'a>(&'a mut self, write: &'a [Word]) -> Result<(), Self::Error>; + + /// Issues a read command on the bus + /// `write` is expected to be a 32 bit cmd word + /// `read` will contain the response of the device + async fn cmd_read<'a>(&'a mut self, write: &'a [Word], read: &'a mut [Word]) -> Result<(), Self::Error>; +} + pub(crate) struct Bus { backplane_window: u32, pwr: PWR, @@ -16,7 +29,7 @@ impl Bus where PWR: OutputPin, SPI: SpiDevice, - SPI::Bus: SpiBusRead + SpiBusWrite, + SPI::Bus: SpiBusCyw43, { pub(crate) fn new(pwr: PWR, spi: SPI) -> Self { Self { @@ -52,8 +65,9 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len_in_u8); let len_in_u32 = (len_in_u8 as usize + 3) / 4; transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - bus.read(&mut buf[..len_in_u32]).await?; + // bus.write(&[cmd]).await?; + // bus.read(&mut buf[..len_in_u32]).await?; + bus.cmd_read(slice::from_ref(&cmd), &mut buf[..len_in_u32]).await?; Ok(()) }) .await @@ -62,9 +76,16 @@ where pub async fn wlan_write(&mut self, buf: &[u32]) { let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4); + //TODO try to remove copy? + let mut cmd_buf = [0_u32; 513]; + cmd_buf[0] = cmd; + cmd_buf[1..][..buf.len()].copy_from_slice(buf); + transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - bus.write(buf).await?; + // bus.write(&[cmd]).await?; + // bus.write(buf).await?; + + bus.cmd_write(&cmd_buf).await?; Ok(()) }) .await @@ -79,7 +100,7 @@ where // To simplify, enforce 4-align for now. assert!(addr % 4 == 0); - let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; + let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4 + 1]; while !data.is_empty() { // Ensure transfer doesn't cross a window boundary. @@ -93,20 +114,23 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; + // bus.write(&[cmd]).await?; - // 4-byte response delay. - let mut junk = [0; 1]; - bus.read(&mut junk).await?; + // // 4-byte response delay. + // let mut junk = [0; 1]; + // bus.read(&mut junk).await?; - // Read data - bus.read(&mut buf[..(len + 3) / 4]).await?; + // // Read data + // bus.read(&mut buf[..(len + 3) / 4]).await?; + + bus.cmd_read(slice::from_ref(&cmd), &mut buf[..(len + 3) / 4 + 1]) + .await?; Ok(()) }) .await .unwrap(); - data[..len].copy_from_slice(&slice8_mut(&mut buf)[..len]); + data[..len].copy_from_slice(&slice8_mut(&mut buf[1..])[..len]); // Advance ptr. addr += len as u32; @@ -121,7 +145,7 @@ where // To simplify, enforce 4-align for now. assert!(addr % 4 == 0); - let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4]; + let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4 + 1]; while !data.is_empty() { // Ensure transfer doesn't cross a window boundary. @@ -129,15 +153,19 @@ where let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); - slice8_mut(&mut buf)[..len].copy_from_slice(&data[..len]); + slice8_mut(&mut buf[1..])[..len].copy_from_slice(&data[..len]); self.backplane_set_window(addr).await; let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); + buf[0] = cmd; transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - bus.write(&buf[..(len + 3) / 4]).await?; + // bus.write(&[cmd]).await?; + // bus.write(&buf[..(len + 3) / 4]).await?; + + bus.cmd_write(&buf[..(len + 3) / 4 + 1]).await?; + Ok(()) }) .await @@ -253,28 +281,36 @@ where async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { let cmd = cmd_word(READ, INC_ADDR, func, addr, len); - let mut buf = [0; 1]; + let mut buf = [0; 2]; + let len = if func == FUNC_BACKPLANE { 2 } else { 1 }; transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd]).await?; - if func == FUNC_BACKPLANE { - // 4-byte response delay. - bus.read(&mut buf).await?; - } - bus.read(&mut buf).await?; + // bus.write(&[cmd]).await?; + // if func == FUNC_BACKPLANE { + // // 4-byte response delay. + // bus.read(&mut buf).await?; + // } + // bus.read(&mut buf).await?; + + bus.cmd_read(slice::from_ref(&cmd), &mut buf[..len]).await?; Ok(()) }) .await .unwrap(); - buf[0] + if func == FUNC_BACKPLANE { + buf[1] + } else { + buf[0] + } } async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); transaction!(&mut self.spi, |bus| async { - bus.write(&[cmd, val]).await?; + // bus.write(&[cmd, val]).await?; + bus.cmd_write(&[cmd, val]).await?; Ok(()) }) .await @@ -283,11 +319,14 @@ where async fn read32_swapped(&mut self, addr: u32) -> u32 { let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4); + let cmd = swap16(cmd); let mut buf = [0; 1]; transaction!(&mut self.spi, |bus| async { - bus.write(&[swap16(cmd)]).await?; - bus.read(&mut buf).await?; + // bus.write(&[swap16(cmd)]).await?; + // bus.read(&mut buf).await?; + + bus.cmd_read(slice::from_ref(&cmd), &mut buf).await?; Ok(()) }) .await @@ -298,9 +337,12 @@ where async fn write32_swapped(&mut self, addr: u32, val: u32) { let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); + let buf = [swap16(cmd), swap16(val)]; transaction!(&mut self.spi, |bus| async { - bus.write(&[swap16(cmd), swap16(val)]).await?; + // bus.write(&[swap16(cmd), swap16(val)]).await?; + + bus.cmd_write(&buf).await?; Ok(()) }) .await diff --git a/src/lib.rs b/src/lib.rs index 5733506ac..7bf3992cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] -#![feature(type_alias_impl_trait, concat_bytes)] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)] #![deny(unused_must_use)] // This mod MUST go first, so that the others see its macros. @@ -24,6 +25,7 @@ use embedded_hal_1::digital::OutputPin; use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; use crate::bus::Bus; +pub use crate::bus::SpiBusCyw43; use crate::consts::*; use crate::events::Event; use crate::structs::*; @@ -512,7 +514,7 @@ pub async fn new<'a, PWR, SPI>( where PWR: OutputPin, SPI: SpiDevice, - SPI::Bus: SpiBusRead + SpiBusWrite, + SPI::Bus: SpiBusCyw43, { let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); let state_ch = ch_runner.state_runner(); @@ -551,7 +553,7 @@ impl<'a, PWR, SPI> Runner<'a, PWR, SPI> where PWR: OutputPin, SPI: SpiDevice, - SPI::Bus: SpiBusRead + SpiBusWrite, + SPI::Bus: SpiBusCyw43, { async fn init(&mut self, firmware: &[u8]) { self.bus.init().await; From 0ff606dfc151b1b3812087b7508fdf4bee3b240b Mon Sep 17 00:00:00 2001 From: kbleeke Date: Sun, 19 Feb 2023 16:31:35 +0100 Subject: [PATCH 0689/1575] Add pio transport to pico w example --- examples/rpi-pico-w/Cargo.toml | 40 +++++-- examples/rpi-pico-w/build.rs | 34 +++--- examples/rpi-pico-w/src/main.rs | 29 +++-- examples/rpi-pico-w/src/pio.rs | 190 ++++++++++++++++++++++++++++++++ src/bus.rs | 2 +- src/lib.rs | 2 +- 6 files changed, 263 insertions(+), 34 deletions(-) create mode 100644 examples/rpi-pico-w/src/pio.rs diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 99b82ca31..0d789a932 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -5,11 +5,31 @@ edition = "2021" [dependencies] -cyw43 = { path = "../../", features = ["defmt", "firmware-logs"]} -embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } -embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } +cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } +embassy-executor = { version = "0.1.0", features = [ + "defmt", + "integrated-timers", +] } +embassy-time = { version = "0.1.0", features = [ + "defmt", + "defmt-timestamp-uptime", +] } +embassy-rp = { version = "0.1.0", features = [ + "defmt", + "unstable-traits", + "nightly", + "unstable-pac", + "pio", + "time-driver", +] } +embassy-net = { version = "0.1.0", features = [ + "defmt", + "tcp", + "dhcpv4", + "medium-ethernet", + "unstable-traits", + "nightly", +] } atomic-polyfill = "0.1.5" static_cell = "1.0" @@ -17,9 +37,15 @@ defmt = "0.3" defmt-rtt = "0.3" panic-probe = { version = "0.3", features = ["print-defmt"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]} +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", + "cfg-target-has-atomic", + "unstable", +] } +pio-proc = "0.2" +pio = "0.2.1" embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } embedded-hal-async = { version = "0.2.0-alpha.0" } diff --git a/examples/rpi-pico-w/build.rs b/examples/rpi-pico-w/build.rs index d4c3ec89d..3f915f931 100644 --- a/examples/rpi-pico-w/build.rs +++ b/examples/rpi-pico-w/build.rs @@ -14,23 +14,23 @@ 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()); + // 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"); + // 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"); - // println!("cargo:rustc-link-arg-bins=-Tdefmt.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"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); } diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index f768af193..3563d165a 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -4,21 +4,25 @@ #![feature(async_fn_in_trait)] #![allow(incomplete_features)] +mod pio; + use core::convert::Infallible; +use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output}; -use embassy_rp::peripherals::{PIN_23, PIN_24, PIN_25, PIN_29}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_24, PIN_25, PIN_29}; +use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachineInstance, Sm0}; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -use core::str::from_utf8; +use crate::pio::PioSpi; macro_rules! singleton { ($val:expr) => {{ @@ -30,7 +34,11 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner<'static, Output<'static, PIN_23>, ExclusiveDevice>>, + runner: cyw43::Runner< + 'static, + Output<'static, PIN_23>, + ExclusiveDevice, DMA_CH0>, Output<'static, PIN_25>>, + >, ) -> ! { runner.run().await } @@ -59,12 +67,15 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let clk = Output::new(p.PIN_29, Level::Low); - let mut dio = Flex::new(p.PIN_24); - dio.set_low(); - dio.set_as_output(); + // let clk = Output::new(p.PIN_29, Level::Low); + // let mut dio = Flex::new(p.PIN_24); + // dio.set_low(); + // dio.set_as_output(); + // // let bus = MySpi { clk, dio }; - let bus = MySpi { clk, dio }; + let (_, sm, _, _, _) = p.PIO0.split(); + let dma = p.DMA_CH0; + let bus = PioSpi::new(sm, p.PIN_24, p.PIN_29, dma); let spi = ExclusiveDevice::new(bus, cs); let state = singleton!(cyw43::State::new()); @@ -110,6 +121,7 @@ async fn main(spawner: Spawner) { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + control.gpio_set(0, false).await; info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { warn!("accept error: {:?}", e); @@ -117,6 +129,7 @@ async fn main(spawner: Spawner) { } info!("Received connection from {:?}", socket.remote_endpoint()); + control.gpio_set(0, true).await; loop { let n = match socket.read(&mut buf).await { diff --git a/examples/rpi-pico-w/src/pio.rs b/examples/rpi-pico-w/src/pio.rs new file mode 100644 index 000000000..abb71b5de --- /dev/null +++ b/examples/rpi-pico-w/src/pio.rs @@ -0,0 +1,190 @@ +use core::slice; + +use cyw43::SpiBusCyw43; +use embassy_rp::dma::Channel; +use embassy_rp::gpio::{Pin, Pull}; +use embassy_rp::pio::{PioStateMachine, ShiftDirection}; +use embassy_rp::relocate::RelocatedProgram; +use embassy_rp::{pio_instr_util, Peripheral}; +use embedded_hal_1::spi::ErrorType; +use embedded_hal_async::spi::SpiBusFlush; +use pio::Wrap; +use pio_proc::pio_asm; + +pub struct PioSpi { + // cs: Output<'static, AnyPin>, + sm: SM, + dma: DMA, + wrap_target: u8, +} + +impl PioSpi +where + SM: PioStateMachine, + DMA: Channel, +{ + pub fn new( + mut sm: SM, + // cs: AnyPin, + dio: DIO, + clk: CLK, + dma: DMA, + ) -> Self + where + DIO: Pin, + CLK: Pin, + { + let program = pio_asm!( + ".side_set 1" + // "set pindirs, 1 side 0" + // "set pins, 0 side 0" + ".wrap_target" + "lp:", + "out pins, 1 side 0" + "jmp x-- lp side 1" + "set pindirs, 0 side 0" + // "nop side 1" + "lp2:" + "in pins, 1 side 0" + "jmp y-- lp2 side 1" + ".wrap" + ); + + let relocated = RelocatedProgram::new(&program.program); + + let mut pin_io = sm.make_pio_pin(dio); + pin_io.set_pull(Pull::Down); + pin_io.set_schmitt(true); + let pin_clk = sm.make_pio_pin(clk); + + sm.write_instr(relocated.origin() as usize, relocated.code()); + + // 16 Mhz + sm.set_clkdiv(0x07d0); + + // 8Mhz + sm.set_clkdiv(0x0a_00); + + // 1Mhz + // sm.set_clkdiv(0x7d_00); + + // slowest possible + // sm.set_clkdiv(0xffff_00); + + sm.set_autopull(true); + // sm.set_pull_threshold(32); + sm.set_autopush(true); + // sm.set_push_threshold(32); + + sm.set_out_pins(&[&pin_io]); + sm.set_in_base_pin(&pin_io); + + sm.set_set_pins(&[&pin_clk]); + pio_instr_util::set_pindir(&mut sm, 0b1); + sm.set_set_pins(&[&pin_io]); + pio_instr_util::set_pindir(&mut sm, 0b1); + + sm.set_sideset_base_pin(&pin_clk); + sm.set_sideset_count(1); + + sm.set_out_shift_dir(ShiftDirection::Left); + sm.set_in_shift_dir(ShiftDirection::Left); + + let Wrap { source, target } = relocated.wrap(); + sm.set_wrap(source, target); + + // pull low for startup + pio_instr_util::set_pin(&mut sm, 0); + + Self { + // cs: Output::new(cs, Level::High), + sm, + dma, + wrap_target: target, + } + } + + pub async fn write(&mut self, write: &[u32]) { + let write_bits = write.len() * 32 - 1; + let read_bits = 31; + + defmt::trace!("write={} read={}", write_bits, read_bits); + + let mut dma = Peripheral::into_ref(&mut self.dma); + pio_instr_util::set_x(&mut self.sm, write_bits as u32); + pio_instr_util::set_y(&mut self.sm, read_bits as u32); + pio_instr_util::set_pindir(&mut self.sm, 0b1); + pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); + + self.sm.set_enable(true); + + self.sm.dma_push(dma.reborrow(), write).await; + + let mut status = 0; + self.sm.dma_pull(dma, slice::from_mut(&mut status)).await; + defmt::trace!("{:#08x}", status); + + self.sm.set_enable(false); + } + + pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) { + let write_bits = 31; + let read_bits = read.len() * 32 - 1; + + defmt::trace!("write={} read={}", write_bits, read_bits); + + let mut dma = Peripheral::into_ref(&mut self.dma); + pio_instr_util::set_y(&mut self.sm, read_bits as u32); + pio_instr_util::set_x(&mut self.sm, write_bits as u32); + pio_instr_util::set_pindir(&mut self.sm, 0b1); + pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); + // self.cs.set_low(); + self.sm.set_enable(true); + + self.sm.dma_push(dma.reborrow(), slice::from_ref(&cmd)).await; + self.sm.dma_pull(dma, read).await; + + self.sm.set_enable(false); + } +} + +#[derive(Debug)] +pub enum PioError {} + +impl embedded_hal_async::spi::Error for PioError { + fn kind(&self) -> embedded_hal_1::spi::ErrorKind { + embedded_hal_1::spi::ErrorKind::Other + } +} + +impl ErrorType for PioSpi +where + SM: PioStateMachine, +{ + type Error = PioError; +} + +impl SpiBusFlush for PioSpi +where + SM: PioStateMachine, +{ + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl SpiBusCyw43 for PioSpi +where + SM: PioStateMachine, + DMA: Channel, +{ + async fn cmd_write<'a>(&'a mut self, write: &'a [u32]) -> Result<(), Self::Error> { + self.write(write).await; + Ok(()) + } + + async fn cmd_read<'a>(&'a mut self, write: &'a [u32], read: &'a mut [u32]) -> Result<(), Self::Error> { + self.cmd_read(write[0], read).await; + Ok(()) + } +} diff --git a/src/bus.rs b/src/bus.rs index 1c8bb9893..aaa79b198 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -3,7 +3,7 @@ use core::slice; use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi::ErrorType; -use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice}; +use embedded_hal_async::spi::{transaction, SpiDevice}; use crate::consts::*; diff --git a/src/lib.rs b/src/lib.rs index 7bf3992cd..bcc3c59bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ use embassy_futures::yield_now; use embassy_net_driver_channel as ch; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; -use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; +use embedded_hal_async::spi::SpiDevice; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; From a6a2a035d57ced9a7a9bb2ef325885063ea83295 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Sun, 19 Mar 2023 16:43:46 +0100 Subject: [PATCH 0690/1575] even faster pio speed are possible --- examples/rpi-pico-w/src/pio.rs | 22 +++++++++++++++------- rust-toolchain.toml | 2 +- src/bus.rs | 31 +++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/examples/rpi-pico-w/src/pio.rs b/examples/rpi-pico-w/src/pio.rs index abb71b5de..1bf304d5d 100644 --- a/examples/rpi-pico-w/src/pio.rs +++ b/examples/rpi-pico-w/src/pio.rs @@ -2,7 +2,7 @@ use core::slice; use cyw43::SpiBusCyw43; use embassy_rp::dma::Channel; -use embassy_rp::gpio::{Pin, Pull}; +use embassy_rp::gpio::{Drive, Pin, Pull, SlewRate}; use embassy_rp::pio::{PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; @@ -43,10 +43,11 @@ where "out pins, 1 side 0" "jmp x-- lp side 1" "set pindirs, 0 side 0" - // "nop side 1" + "nop side 1" "lp2:" - "in pins, 1 side 0" - "jmp y-- lp2 side 1" + "in pins, 1 side 1" + "jmp y-- lp2 side 0" + ".wrap" ); @@ -55,15 +56,22 @@ where let mut pin_io = sm.make_pio_pin(dio); pin_io.set_pull(Pull::Down); pin_io.set_schmitt(true); - let pin_clk = sm.make_pio_pin(clk); + pin_io.set_input_sync_bypass(true); + + let mut pin_clk = sm.make_pio_pin(clk); + pin_clk.set_drive_strength(Drive::_12mA); + pin_clk.set_slew_rate(SlewRate::Fast); sm.write_instr(relocated.origin() as usize, relocated.code()); + // 32 Mhz + sm.set_clkdiv(0x03E8); + // 16 Mhz - sm.set_clkdiv(0x07d0); + // sm.set_clkdiv(0x07d0); // 8Mhz - sm.set_clkdiv(0x0a_00); + // sm.set_clkdiv(0x0a_00); // 1Mhz // sm.set_clkdiv(0x7d_00); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ffbcbd6f9..20c10c3f1 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-11-22" +channel = "nightly-2023-03-19" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", diff --git a/src/bus.rs b/src/bus.rs index aaa79b198..e4f9a69bb 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -4,6 +4,7 @@ use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi::ErrorType; use embedded_hal_async::spi::{transaction, SpiDevice}; +use futures::FutureExt; use crate::consts::*; @@ -46,18 +47,40 @@ where self.pwr.set_high().unwrap(); Timer::after(Duration::from_millis(250)).await; - while self.read32_swapped(REG_BUS_TEST_RO).await != FEEDBEAD {} + while self + .read32_swapped(REG_BUS_TEST_RO) + .inspect(|v| defmt::trace!("{:#x}", v)) + .await + != FEEDBEAD + {} self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await; - let val = self.read32_swapped(REG_BUS_TEST_RW).await; + let val = self + .read32_swapped(REG_BUS_TEST_RW) + .inspect(|v| defmt::trace!("{:#x}", v)) + .await; assert_eq!(val, TEST_PATTERN); + self.read32_swapped(REG_BUS_CTRL) + .inspect(|v| defmt::trace!("{:#010b}", (v & 0xff))) + .await; + // 32-bit word length, little endian (which is the default endianess). self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await; - let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await; + self.read8(FUNC_BUS, REG_BUS_CTRL) + .inspect(|v| defmt::trace!("{:#b}", v)) + .await; + + let val = self + .read32(FUNC_BUS, REG_BUS_TEST_RO) + .inspect(|v| defmt::trace!("{:#x}", v)) + .await; assert_eq!(val, FEEDBEAD); - let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await; + let val = self + .read32(FUNC_BUS, REG_BUS_TEST_RW) + .inspect(|v| defmt::trace!("{:#x}", v)) + .await; assert_eq!(val, TEST_PATTERN); } From 1b410d6f3f08f12f2bd250a8b76f217291f4df26 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Wed, 1 Mar 2023 19:03:46 +0100 Subject: [PATCH 0691/1575] add event handling to join --- examples/rpi-pico-w/src/main.rs | 9 ++++--- src/events.rs | 14 +++++++++++ src/lib.rs | 42 +++++++++++++++++++++++++++------ 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index c706e121d..91caa5e3a 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -70,16 +70,11 @@ async fn main(spawner: Spawner) { let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; - spawner.spawn(wifi_task(runner)).unwrap(); - control.init(clm).await; control .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; - //control.join_open(env!("WIFI_NETWORK")).await; - control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; - let config = Config::Dhcp(Default::default()); //let config = embassy_net::Config::Static(embassy_net::Config { // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), @@ -98,8 +93,12 @@ async fn main(spawner: Spawner) { seed )); + unwrap!(spawner.spawn(wifi_task(runner))); unwrap!(spawner.spawn(net_task(stack))); + //control.join_open(env!("WIFI_NETWORK")).await; + control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; + // And now we can use it! let mut rx_buffer = [0; 4096]; diff --git a/src/events.rs b/src/events.rs index a828eec98..9e6bb9625 100644 --- a/src/events.rs +++ b/src/events.rs @@ -3,6 +3,9 @@ use core::num; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; + #[derive(Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -280,3 +283,14 @@ pub enum Event { /// highest val + 1 for range checking LAST = 190, } + +pub type EventQueue = PubSubChannel; +pub type EventPublisher<'a> = Publisher<'a, CriticalSectionRawMutex, EventStatus, 2, 1, 1>; +pub type EventSubscriber<'a> = Subscriber<'a, CriticalSectionRawMutex, EventStatus, 2, 1, 1>; + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EventStatus { + pub event_type: Event, + pub status: u32, +} diff --git a/src/lib.rs b/src/lib.rs index 5733506ac..c58ac8e7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,13 +19,15 @@ use core::slice; use ch::driver::LinkState; use embassy_futures::yield_now; use embassy_net_driver_channel as ch; +use embassy_sync::pubsub::PubSubBehavior; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice}; +use events::EventQueue; use crate::bus::Bus; use crate::consts::*; -use crate::events::Event; +use crate::events::{Event, EventStatus}; use crate::structs::*; const MTU: usize = 1514; @@ -127,6 +129,7 @@ enum IoctlState { pub struct State { ioctl_state: Cell, ch: ch::State, + events: EventQueue, } impl State { @@ -134,12 +137,14 @@ impl State { Self { ioctl_state: Cell::new(IoctlState::Idle), ch: ch::State::new(), + events: EventQueue::new(), } } } pub struct Control<'a> { state_ch: ch::StateRunner<'a>, + event_sub: &'a EventQueue, ioctl_state: &'a Cell, } @@ -313,6 +318,7 @@ impl<'a> Control<'a> { evts.unset(Event::PROBREQ_MSG_RX); evts.unset(Event::PROBRESP_MSG); evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::ROAM); self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; @@ -393,8 +399,22 @@ impl<'a> Control<'a> { ssid: [0; 32], }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + + let mut subscriber = self.event_sub.subscriber().unwrap(); self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; // set_ssid + loop { + let msg = subscriber.next_message_pure().await; + if msg.event_type == Event::AUTH && msg.status != 0 { + // retry + defmt::warn!("JOIN failed with status={}", msg.status); + self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; + } else if msg.event_type == Event::JOIN && msg.status == 0 { + // successful join + break; + } + } + info!("JOINED"); } @@ -489,6 +509,8 @@ pub struct Runner<'a, PWR, SPI> { sdpcm_seq: u8, sdpcm_seq_max: u8, + events: &'a EventQueue, + #[cfg(feature = "firmware-logs")] log: LogState, } @@ -526,6 +548,8 @@ where sdpcm_seq: 0, sdpcm_seq_max: 1, + events: &state.events, + #[cfg(feature = "firmware-logs")] log: LogState { addr: 0, @@ -541,6 +565,7 @@ where device, Control { state_ch, + event_sub: &&state.events, ioctl_state: &state.ioctl_state, }, runner, @@ -883,13 +908,16 @@ where return; } + let evt_type = events::Event::from(event_packet.msg.event_type as u8); let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; - debug!( - "=== EVENT {}: {} {:02x}", - events::Event::from(event_packet.msg.event_type as u8), - event_packet.msg, - evt_data - ); + debug!("=== EVENT {}: {} {:02x}", evt_type, event_packet.msg, evt_data); + + if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { + self.events.publish_immediate(EventStatus { + status: event_packet.msg.status, + event_type: evt_type, + }); + } } CHANNEL_TYPE_DATA => { let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); From 67743bb1221fefc677bd2f207d0c382a85a46b7f Mon Sep 17 00:00:00 2001 From: Jacob Davis-Hansson Date: Sun, 19 Mar 2023 19:16:26 +0100 Subject: [PATCH 0692/1575] Update pre-flashed command to match file name Super minor, just to help the next person avoid the little stumble. --- examples/rpi-pico-w/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index c706e121d..ad4e98954 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -53,7 +53,7 @@ async fn main(spawner: Spawner) { // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 - // probe-rs-cli download 43439A0.clm_blob --format bin --chip RP2040 --base-address 0x10140000 + // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; From b411b7ce637e3e561f43b0c4f020f02b5607467b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 19 Mar 2023 22:36:18 +0100 Subject: [PATCH 0693/1575] vscode: recommend extensions, disable toml formatting, update. --- .vscode/extensions.json | 11 +++++++++++ .vscode/settings.json | 14 ++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..a8bb78adb --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 082b286da..dd479929e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,14 +1,12 @@ { "editor.formatOnSave": true, - "rust-analyzer.cargo.buildScripts.enable": true, - "rust-analyzer.cargo.noDefaultFeatures": true, + "[toml]": { + "editor.formatOnSave": false + }, "rust-analyzer.cargo.target": "thumbv6m-none-eabi", - "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.checkOnSave.noDefaultFeatures": true, - "rust-analyzer.imports.granularity.enforce": true, - "rust-analyzer.imports.granularity.group": "module", - "rust-analyzer.procMacro.attributes.enable": false, - "rust-analyzer.procMacro.enable": false, + "rust-analyzer.cargo.noDefaultFeatures": true, + "rust-analyzer.check.allTargets": false, + "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.linkedProjects": [ "examples/rpi-pico-w/Cargo.toml", ], From fcd24adba9cca483115a7ac207854d2a0ea94823 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 19 Mar 2023 22:38:34 +0100 Subject: [PATCH 0694/1575] vscode: recommend extensions, disable toml formatting, update. --- .vscode/extensions.json | 11 +++++++++++ .vscode/settings.json | 8 +++----- 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..a8bb78adb --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index db37b64ce..700804dca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,11 @@ { "editor.formatOnSave": true, + "[toml]": { + "editor.formatOnSave": false + }, "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.procMacro.enable": true, "rust-analyzer.cargo.target": "thumbv7em-none-eabi", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ @@ -38,8 +40,4 @@ // "examples/stm32wl55/Cargo.toml", // "examples/wasm/Cargo.toml", ], - "rust-analyzer.imports.granularity.enforce": true, - "rust-analyzer.imports.granularity.group": "module", - "rust-analyzer.cargo.buildScripts.enable": true, - "rust-analyzer.procMacro.attributes.enable": false, } \ No newline at end of file From 0b49b588a2890096f95bef2f8c6d8644acdc5401 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 20 Mar 2023 02:11:22 +0100 Subject: [PATCH 0695/1575] stm32: use stm32-metapac from crates.io, remove stm32-data submodule. --- .github/workflows/doc.yml | 1 - .gitignore | 4 - .gitmodules | 3 - ci.sh | 20 +- .../ROOT/examples/layer-by-layer/Cargo.toml | 1 - .../layer-by-layer/blinky-pac/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 64 +- stm32-data | 1 - stm32-gen-features/.cargo/config.toml | 3 - stm32-gen-features/.gitignore | 1 - stm32-gen-features/Cargo.toml | 11 - stm32-gen-features/src/lib.rs | 169 --- stm32-gen-features/src/main.rs | 25 - stm32-metapac-gen/Cargo.toml | 15 - stm32-metapac-gen/src/data.rs | 124 -- stm32-metapac-gen/src/lib.rs | 380 ----- stm32-metapac-gen/src/main.rs | 36 - stm32-metapac/Cargo.toml | 1332 ----------------- stm32-metapac/build.rs | 77 - stm32-metapac/build_pregenerated.rs | 39 - stm32-metapac/src/lib.rs | 18 - stm32-metapac/src/metadata.rs | 106 -- xtask/.cargo/config.toml | 6 - xtask/Cargo.toml | 11 - xtask/src/main.rs | 221 --- 25 files changed, 34 insertions(+), 2636 deletions(-) delete mode 100644 .gitmodules delete mode 160000 stm32-data delete mode 100644 stm32-gen-features/.cargo/config.toml delete mode 100644 stm32-gen-features/.gitignore delete mode 100644 stm32-gen-features/Cargo.toml delete mode 100644 stm32-gen-features/src/lib.rs delete mode 100644 stm32-gen-features/src/main.rs delete mode 100644 stm32-metapac-gen/Cargo.toml delete mode 100644 stm32-metapac-gen/src/data.rs delete mode 100644 stm32-metapac-gen/src/lib.rs delete mode 100644 stm32-metapac-gen/src/main.rs delete mode 100644 stm32-metapac/Cargo.toml delete mode 100644 stm32-metapac/build.rs delete mode 100644 stm32-metapac/build_pregenerated.rs delete mode 100644 stm32-metapac/src/lib.rs delete mode 100644 stm32-metapac/src/metadata.rs delete mode 100644 xtask/.cargo/config.toml delete mode 100644 xtask/Cargo.toml delete mode 100644 xtask/src/main.rs diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 4bc73fdcf..cb222803b 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -54,7 +54,6 @@ jobs: run: | mkdir crates builder ./embassy-stm32 crates/embassy-stm32/git.zup - builder ./stm32-metapac crates/stm32-metapac/git.zup - name: build-rest if: ${{ matrix.crates=='rest' }} diff --git a/.gitignore b/.gitignore index 144dd703f..1c221e876 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,4 @@ target_ci_stable Cargo.lock third_party /Cargo.toml -stm32-metapac-gen/out/ -stm32-metapac-backup -stm32-metapac/src/chips -stm32-metapac/src/peripherals out/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8157e36ef..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "stm32-data"] - path = stm32-data - url = https://github.com/embassy-rs/stm32-data.git diff --git a/ci.sh b/ci.sh index bbcb26bdb..d86c93520 100755 --- a/ci.sh +++ b/ci.sh @@ -8,28 +8,12 @@ export DEFMT_LOG=trace TARGET=$(rustc -vV | sed -n 's|host: ||p') +BUILD_EXTRA="" if [ $TARGET = "x86_64-unknown-linux-gnu" ]; then BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --out-dir out/examples/std" -else - BUILD_EXTRA="" fi -find . -name '*.rs' -not -path '*target*' -not -path '*stm32-metapac-gen/out/*' -not -path '*stm32-metapac/src/*' | xargs rustfmt --check --skip-children --unstable-features --edition 2018 - -# Generate stm32-metapac -if [ ! -d "stm32-metapac-backup" ] -then - cp -r stm32-metapac stm32-metapac-backup -fi - -rm -rf stm32-metapac -cp -r stm32-metapac-backup stm32-metapac - -# for some reason Cargo stomps the cache if we don't specify --target. -# This happens with vanilla Cargo, not just cargo-batch. Bug? -(cd stm32-metapac-gen; cargo run --release --target $TARGET) -rm -rf stm32-metapac -mv stm32-metapac-gen/out stm32-metapac +find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2018 cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \ diff --git a/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml index 9048d9302..943249a17 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/Cargo.toml @@ -10,7 +10,6 @@ members = [ [patch.crates-io] embassy-executor = { path = "../../../../../embassy-executor" } embassy-stm32 = { path = "../../../../../embassy-stm32" } -stm32-metapac = { path = "../../../../../stm32-metapac" } [profile.release] codegen-units = 1 diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml index a077f1828..f872b94cb 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" cortex-m-rt = "0.7" -stm32-metapac = { version = "0.1.0", features = ["stm32l475vg", "memory-x"] } +stm32-metapac = { version = "1", features = ["stm32l475vg", "memory-x"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1f430c7c6..b66d724d5 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } +stm32-metapac = { version = "1", features = ["rt"] } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -72,7 +72,7 @@ embedded-io = { version = "0.4.0", features = ["async"], optional = true } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "1", default-features = false, features = ["metadata"]} [features] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] @@ -107,8 +107,7 @@ unstable-pac = [] # Implement embedded-hal-async traits if `nightly` is set as well. unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] -# BEGIN GENERATED FEATURES -# Generated by stm32-gen-features. DO NOT EDIT. +# Chip-selection features stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] @@ -1070,89 +1069,89 @@ stm32l083rz = [ "stm32-metapac/stm32l083rz" ] stm32l083v8 = [ "stm32-metapac/stm32l083v8" ] stm32l083vb = [ "stm32-metapac/stm32l083vb" ] stm32l083vz = [ "stm32-metapac/stm32l083vz" ] -stm32l100c6-a = [ "stm32-metapac/stm32l100c6-a" ] stm32l100c6 = [ "stm32-metapac/stm32l100c6" ] -stm32l100r8-a = [ "stm32-metapac/stm32l100r8-a" ] +stm32l100c6-a = [ "stm32-metapac/stm32l100c6-a" ] stm32l100r8 = [ "stm32-metapac/stm32l100r8" ] -stm32l100rb-a = [ "stm32-metapac/stm32l100rb-a" ] +stm32l100r8-a = [ "stm32-metapac/stm32l100r8-a" ] stm32l100rb = [ "stm32-metapac/stm32l100rb" ] +stm32l100rb-a = [ "stm32-metapac/stm32l100rb-a" ] stm32l100rc = [ "stm32-metapac/stm32l100rc" ] -stm32l151c6-a = [ "stm32-metapac/stm32l151c6-a" ] stm32l151c6 = [ "stm32-metapac/stm32l151c6" ] -stm32l151c8-a = [ "stm32-metapac/stm32l151c8-a" ] +stm32l151c6-a = [ "stm32-metapac/stm32l151c6-a" ] stm32l151c8 = [ "stm32-metapac/stm32l151c8" ] -stm32l151cb-a = [ "stm32-metapac/stm32l151cb-a" ] +stm32l151c8-a = [ "stm32-metapac/stm32l151c8-a" ] stm32l151cb = [ "stm32-metapac/stm32l151cb" ] +stm32l151cb-a = [ "stm32-metapac/stm32l151cb-a" ] stm32l151cc = [ "stm32-metapac/stm32l151cc" ] stm32l151qc = [ "stm32-metapac/stm32l151qc" ] stm32l151qd = [ "stm32-metapac/stm32l151qd" ] stm32l151qe = [ "stm32-metapac/stm32l151qe" ] -stm32l151r6-a = [ "stm32-metapac/stm32l151r6-a" ] stm32l151r6 = [ "stm32-metapac/stm32l151r6" ] -stm32l151r8-a = [ "stm32-metapac/stm32l151r8-a" ] +stm32l151r6-a = [ "stm32-metapac/stm32l151r6-a" ] stm32l151r8 = [ "stm32-metapac/stm32l151r8" ] -stm32l151rb-a = [ "stm32-metapac/stm32l151rb-a" ] +stm32l151r8-a = [ "stm32-metapac/stm32l151r8-a" ] stm32l151rb = [ "stm32-metapac/stm32l151rb" ] -stm32l151rc-a = [ "stm32-metapac/stm32l151rc-a" ] +stm32l151rb-a = [ "stm32-metapac/stm32l151rb-a" ] stm32l151rc = [ "stm32-metapac/stm32l151rc" ] +stm32l151rc-a = [ "stm32-metapac/stm32l151rc-a" ] stm32l151rd = [ "stm32-metapac/stm32l151rd" ] stm32l151re = [ "stm32-metapac/stm32l151re" ] stm32l151uc = [ "stm32-metapac/stm32l151uc" ] -stm32l151v8-a = [ "stm32-metapac/stm32l151v8-a" ] stm32l151v8 = [ "stm32-metapac/stm32l151v8" ] -stm32l151vb-a = [ "stm32-metapac/stm32l151vb-a" ] +stm32l151v8-a = [ "stm32-metapac/stm32l151v8-a" ] stm32l151vb = [ "stm32-metapac/stm32l151vb" ] -stm32l151vc-a = [ "stm32-metapac/stm32l151vc-a" ] +stm32l151vb-a = [ "stm32-metapac/stm32l151vb-a" ] stm32l151vc = [ "stm32-metapac/stm32l151vc" ] -stm32l151vd-x = [ "stm32-metapac/stm32l151vd-x" ] +stm32l151vc-a = [ "stm32-metapac/stm32l151vc-a" ] stm32l151vd = [ "stm32-metapac/stm32l151vd" ] +stm32l151vd-x = [ "stm32-metapac/stm32l151vd-x" ] stm32l151ve = [ "stm32-metapac/stm32l151ve" ] stm32l151zc = [ "stm32-metapac/stm32l151zc" ] stm32l151zd = [ "stm32-metapac/stm32l151zd" ] stm32l151ze = [ "stm32-metapac/stm32l151ze" ] -stm32l152c6-a = [ "stm32-metapac/stm32l152c6-a" ] stm32l152c6 = [ "stm32-metapac/stm32l152c6" ] -stm32l152c8-a = [ "stm32-metapac/stm32l152c8-a" ] +stm32l152c6-a = [ "stm32-metapac/stm32l152c6-a" ] stm32l152c8 = [ "stm32-metapac/stm32l152c8" ] -stm32l152cb-a = [ "stm32-metapac/stm32l152cb-a" ] +stm32l152c8-a = [ "stm32-metapac/stm32l152c8-a" ] stm32l152cb = [ "stm32-metapac/stm32l152cb" ] +stm32l152cb-a = [ "stm32-metapac/stm32l152cb-a" ] stm32l152cc = [ "stm32-metapac/stm32l152cc" ] stm32l152qc = [ "stm32-metapac/stm32l152qc" ] stm32l152qd = [ "stm32-metapac/stm32l152qd" ] stm32l152qe = [ "stm32-metapac/stm32l152qe" ] -stm32l152r6-a = [ "stm32-metapac/stm32l152r6-a" ] stm32l152r6 = [ "stm32-metapac/stm32l152r6" ] -stm32l152r8-a = [ "stm32-metapac/stm32l152r8-a" ] +stm32l152r6-a = [ "stm32-metapac/stm32l152r6-a" ] stm32l152r8 = [ "stm32-metapac/stm32l152r8" ] -stm32l152rb-a = [ "stm32-metapac/stm32l152rb-a" ] +stm32l152r8-a = [ "stm32-metapac/stm32l152r8-a" ] stm32l152rb = [ "stm32-metapac/stm32l152rb" ] -stm32l152rc-a = [ "stm32-metapac/stm32l152rc-a" ] +stm32l152rb-a = [ "stm32-metapac/stm32l152rb-a" ] stm32l152rc = [ "stm32-metapac/stm32l152rc" ] +stm32l152rc-a = [ "stm32-metapac/stm32l152rc-a" ] stm32l152rd = [ "stm32-metapac/stm32l152rd" ] stm32l152re = [ "stm32-metapac/stm32l152re" ] stm32l152uc = [ "stm32-metapac/stm32l152uc" ] -stm32l152v8-a = [ "stm32-metapac/stm32l152v8-a" ] stm32l152v8 = [ "stm32-metapac/stm32l152v8" ] -stm32l152vb-a = [ "stm32-metapac/stm32l152vb-a" ] +stm32l152v8-a = [ "stm32-metapac/stm32l152v8-a" ] stm32l152vb = [ "stm32-metapac/stm32l152vb" ] -stm32l152vc-a = [ "stm32-metapac/stm32l152vc-a" ] +stm32l152vb-a = [ "stm32-metapac/stm32l152vb-a" ] stm32l152vc = [ "stm32-metapac/stm32l152vc" ] -stm32l152vd-x = [ "stm32-metapac/stm32l152vd-x" ] +stm32l152vc-a = [ "stm32-metapac/stm32l152vc-a" ] stm32l152vd = [ "stm32-metapac/stm32l152vd" ] +stm32l152vd-x = [ "stm32-metapac/stm32l152vd-x" ] stm32l152ve = [ "stm32-metapac/stm32l152ve" ] stm32l152zc = [ "stm32-metapac/stm32l152zc" ] stm32l152zd = [ "stm32-metapac/stm32l152zd" ] stm32l152ze = [ "stm32-metapac/stm32l152ze" ] stm32l162qc = [ "stm32-metapac/stm32l162qc" ] stm32l162qd = [ "stm32-metapac/stm32l162qd" ] -stm32l162rc-a = [ "stm32-metapac/stm32l162rc-a" ] stm32l162rc = [ "stm32-metapac/stm32l162rc" ] +stm32l162rc-a = [ "stm32-metapac/stm32l162rc-a" ] stm32l162rd = [ "stm32-metapac/stm32l162rd" ] stm32l162re = [ "stm32-metapac/stm32l162re" ] -stm32l162vc-a = [ "stm32-metapac/stm32l162vc-a" ] stm32l162vc = [ "stm32-metapac/stm32l162vc" ] -stm32l162vd-x = [ "stm32-metapac/stm32l162vd-x" ] +stm32l162vc-a = [ "stm32-metapac/stm32l162vc-a" ] stm32l162vd = [ "stm32-metapac/stm32l162vd" ] +stm32l162vd-x = [ "stm32-metapac/stm32l162vd-x" ] stm32l162ve = [ "stm32-metapac/stm32l162ve" ] stm32l162zc = [ "stm32-metapac/stm32l162zc" ] stm32l162zd = [ "stm32-metapac/stm32l162zd" ] @@ -1370,4 +1369,3 @@ stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] -# END GENERATED FEATURES diff --git a/stm32-data b/stm32-data deleted file mode 160000 index 662529829..000000000 --- a/stm32-data +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 66252982939014e94fc4a1b7423c30c3d108ae0b diff --git a/stm32-gen-features/.cargo/config.toml b/stm32-gen-features/.cargo/config.toml deleted file mode 100644 index 17d81c14d..000000000 --- a/stm32-gen-features/.cargo/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[profile.dev] -opt-level = 3 -lto = false diff --git a/stm32-gen-features/.gitignore b/stm32-gen-features/.gitignore deleted file mode 100644 index ea8c4bf7f..000000000 --- a/stm32-gen-features/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/stm32-gen-features/Cargo.toml b/stm32-gen-features/Cargo.toml deleted file mode 100644 index f4aa08ebe..000000000 --- a/stm32-gen-features/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "gen_features" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -glob = "0.3.0" -yaml-rust = "0.4.5" diff --git a/stm32-gen-features/src/lib.rs b/stm32-gen-features/src/lib.rs deleted file mode 100644 index 7aaad9da3..000000000 --- a/stm32-gen-features/src/lib.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! FIXME discuss about which errors to print and when to panic - -use std::path::Path; - -const SEPARATOR_START: &str = "# BEGIN GENERATED FEATURES\n"; -const SEPARATOR_END: &str = "# END GENERATED FEATURES\n"; -const HELP: &str = "# Generated by stm32-gen-features. DO NOT EDIT.\n"; - -/// Get the list of all the chips and their supported cores -/// -/// Print errors to `stderr` when something is returned by the glob but is not in the returned -/// [`Vec`] -/// -/// This function is slow because all the yaml files are parsed. -pub fn chip_names_and_cores() -> Vec<(String, Vec)> { - glob::glob("../stm32-data/data/chips/*.json") - .unwrap() - .filter_map(|entry| entry.map_err(|e| eprintln!("{:?}", e)).ok()) - .filter_map(|entry| { - if let Some(name) = entry.file_stem().and_then(|stem| stem.to_str()) { - Some((name.to_lowercase(), chip_cores(&entry))) - } else { - eprintln!("{:?} is not a regular file", entry); - None - } - }) - .collect() -} - -/// Get the list of the cores of a chip by its associated file -/// -/// # Panic -/// Panics if the file does not exist or if it contains yaml syntax errors. -/// Panics if "cores" is not an array. -fn chip_cores(path: &Path) -> Vec { - let file_contents = std::fs::read_to_string(path).unwrap(); - let doc = &yaml_rust::YamlLoader::load_from_str(&file_contents).unwrap()[0]; - doc["cores"] - .as_vec() - .unwrap_or_else(|| panic!("{:?}:[cores] is not an array", path)) - .iter() - .enumerate() - .map(|(i, core)| { - core["name"] - .as_str() - .unwrap_or_else(|| panic!("{:?}:[cores][{}][name] is not a string", path, i)) - .to_owned() - }) - .collect() -} - -/// Generate data needed in `../embassy-stm32/Cargo.toml` -/// -/// Print errors to `stderr` when something is returned by the glob but is not in the returned -/// [`Vec`] -/// -/// # Panic -/// Panics if a file contains yaml syntax errors or if a value does not have a consistent type -pub fn embassy_stm32_needed_data(names_and_cores: &[(String, Vec)]) -> String { - let mut result = String::new(); - for (chip_name, cores) in names_and_cores { - if cores.len() > 1 { - for core_name in cores.iter() { - result += &format!( - "{chip}-{core} = [ \"stm32-metapac/{chip}-{core}\" ]\n", - chip = chip_name, - core = core_name - ); - } - } else { - result += &format!("{chip} = [ \"stm32-metapac/{chip}\" ]\n", chip = chip_name); - } - } - result -} - -/// Generate data needed in `../stm32-metapac/Cargo.toml` -/// -/// Print errors to `stderr` when something is returned by the glob but is not in the returned -/// [`Vec`] -/// -/// # Panic -/// Panics if a file contains yaml syntax errors or if a value does not have a consistent type -pub fn stm32_metapac_needed_data(names_and_cores: &[(String, Vec)]) -> String { - let mut result = String::new(); - for (chip_name, cores) in names_and_cores { - if cores.len() > 1 { - for core_name in cores { - result += &format!("{}-{} = []\n", chip_name, core_name); - } - } else { - result += &format!("{} = []\n", chip_name); - } - } - result -} - -/// Get contents before and after generated contents -/// -/// # Panic -/// Panics when a separator cound not be not found -fn split_cargo_toml_contents(contents: &str) -> (&str, &str) { - let (before, remainder) = contents - .split_once(SEPARATOR_START) - .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_START)); - let (_, after) = remainder - .split_once(SEPARATOR_END) - .unwrap_or_else(|| panic!("missing \"{}\" tag", SEPARATOR_END)); - - (before, after) -} - -/// Generates new contents for Cargo.toml -/// -/// # Panic -/// Panics when a separator cound not be not found -pub fn generate_cargo_toml_file(previous_text: &str, new_contents: &str) -> String { - let (before, after) = split_cargo_toml_contents(previous_text); - before.to_owned() + SEPARATOR_START + HELP + new_contents + SEPARATOR_END + after -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[ignore] - fn stm32f407vg_yaml_file_exists() { - assert!(chip_names_and_cores() - .as_slice() - .into_iter() - .any(|(name, _)| { name == "stm32f407vg" })) - } - - #[test] - fn keeps_text_around_separators() { - let initial = "\ -before -# BEGIN GENERATED FEATURES -# END GENERATED FEATURES -after -"; - - let expected = "\ -before -# BEGIN GENERATED FEATURES -# Generated by stm32-gen-features. DO NOT EDIT. -a = [\"b\"] -# END GENERATED FEATURES -after -"; - - let new_contents = String::from("a = [\"b\"]\n"); - assert_eq!(generate_cargo_toml_file(initial, &new_contents), expected); - } - - #[test] - #[should_panic] - fn does_not_generate_if_separators_are_missing() { - let initial = "\ -before -# END GENERATED FEATURES -after -"; - - let new_contents = String::from("a = [\"b\"]\n"); - generate_cargo_toml_file(initial, &new_contents); - } -} diff --git a/stm32-gen-features/src/main.rs b/stm32-gen-features/src/main.rs deleted file mode 100644 index f40925169..000000000 --- a/stm32-gen-features/src/main.rs +++ /dev/null @@ -1,25 +0,0 @@ -use gen_features::{ - chip_names_and_cores, embassy_stm32_needed_data, generate_cargo_toml_file, stm32_metapac_needed_data, -}; - -fn main() { - let names_and_cores = chip_names_and_cores(); - update_cargo_file( - "../embassy-stm32/Cargo.toml", - &embassy_stm32_needed_data(&names_and_cores), - ); - update_cargo_file( - "../stm32-metapac/Cargo.toml", - &stm32_metapac_needed_data(&names_and_cores), - ); -} - -/// Update a Cargo.toml file -/// -/// Update the content between "# BEGIN GENERATED FEATURES" and "# END GENERATED FEATURES" -/// with the given content -fn update_cargo_file(path: &str, new_contents: &str) { - let previous_text = std::fs::read_to_string(path).unwrap(); - let new_text = generate_cargo_toml_file(&previous_text, new_contents); - std::fs::write(path, new_text).unwrap(); -} diff --git a/stm32-metapac-gen/Cargo.toml b/stm32-metapac-gen/Cargo.toml deleted file mode 100644 index 6d136ba6b..000000000 --- a/stm32-metapac-gen/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "stm32-metapac-gen" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" - - -[dependencies] -regex = "1.5.4" -chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "1d9e0a39a6acc291e50cabc4ed617a87f06d5e89" } -serde = { version = "1.0.130", features = [ "derive" ] } -serde_json = "1.0.87" -serde_yaml = "0.8.21" -proc-macro2 = "1.0.29" - diff --git a/stm32-metapac-gen/src/data.rs b/stm32-metapac-gen/src/data.rs deleted file mode 100644 index 17eccfe9a..000000000 --- a/stm32-metapac-gen/src/data.rs +++ /dev/null @@ -1,124 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct Chip { - pub name: String, - pub family: String, - pub line: String, - pub cores: Vec, - pub memory: Vec, - pub packages: Vec, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct MemoryRegion { - pub name: String, - pub kind: MemoryRegionKind, - pub address: u32, - pub size: u32, - pub settings: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct FlashSettings { - pub erase_size: u32, - pub write_size: u32, - pub erase_value: u8, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub enum MemoryRegionKind { - #[serde(rename = "flash")] - Flash, - #[serde(rename = "ram")] - Ram, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct Core { - pub name: String, - pub peripherals: Vec, - pub interrupts: Vec, - pub dma_channels: Vec, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct Interrupt { - pub name: String, - pub number: u32, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct Package { - pub name: String, - pub package: String, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct Peripheral { - pub name: String, - pub address: u64, - #[serde(default)] - pub registers: Option, - #[serde(default)] - pub rcc: Option, - #[serde(default)] - pub pins: Vec, - #[serde(default)] - pub dma_channels: Vec, - #[serde(default)] - pub interrupts: Vec, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct PeripheralInterrupt { - pub signal: String, - pub interrupt: String, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct PeripheralRcc { - pub clock: String, - #[serde(default)] - pub enable: Option, - #[serde(default)] - pub reset: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct PeripheralRccRegister { - pub register: String, - pub field: String, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct PeripheralPin { - pub pin: String, - pub signal: String, - pub af: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] -pub struct DmaChannel { - pub name: String, - pub dma: String, - pub channel: u32, - pub dmamux: Option, - pub dmamux_channel: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Hash)] -pub struct PeripheralDmaChannel { - pub signal: String, - pub channel: Option, - pub dmamux: Option, - pub dma: Option, - pub request: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Hash)] -pub struct PeripheralRegisters { - pub kind: String, - pub version: String, - pub block: String, -} diff --git a/stm32-metapac-gen/src/lib.rs b/stm32-metapac-gen/src/lib.rs deleted file mode 100644 index 64045986e..000000000 --- a/stm32-metapac-gen/src/lib.rs +++ /dev/null @@ -1,380 +0,0 @@ -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::fmt::{Debug, Write as _}; -use std::fs; -use std::fs::File; -use std::io::Write; -use std::path::{Path, PathBuf}; -use std::str::FromStr; - -use chiptool::generate::CommonModule; -use chiptool::{generate, ir, transform}; -use proc_macro2::TokenStream; -use regex::Regex; - -mod data; -use data::*; - -#[derive(Debug, Eq, PartialEq, Clone)] -struct Metadata<'a> { - name: &'a str, - family: &'a str, - line: &'a str, - memory: &'a [MemoryRegion], - peripherals: &'a [Peripheral], - interrupts: &'a [Interrupt], - dma_channels: &'a [DmaChannel], -} - -pub struct Options { - pub chips: Vec, - pub out_dir: PathBuf, - pub data_dir: PathBuf, -} - -pub struct Gen { - opts: Options, - all_peripheral_versions: HashSet<(String, String)>, - metadata_dedup: HashMap, -} - -impl Gen { - pub fn new(opts: Options) -> Self { - Self { - opts, - all_peripheral_versions: HashSet::new(), - metadata_dedup: HashMap::new(), - } - } - - fn gen_chip(&mut self, chip_core_name: &str, chip: &Chip, core: &Core, core_index: usize) { - let mut ir = ir::IR::new(); - - let mut dev = ir::Device { - interrupts: Vec::new(), - peripherals: Vec::new(), - }; - - let mut peripheral_versions: BTreeMap = BTreeMap::new(); - - let gpio_base = core.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; - let gpio_stride = 0x400; - - for p in &core.peripherals { - let mut ir_peri = ir::Peripheral { - name: p.name.clone(), - array: None, - base_address: p.address, - block: None, - description: None, - interrupts: HashMap::new(), - }; - - if let Some(bi) = &p.registers { - if let Some(old_version) = peripheral_versions.insert(bi.kind.clone(), bi.version.clone()) { - if old_version != bi.version { - panic!( - "Peripheral {} has multiple versions: {} and {}", - bi.kind, old_version, bi.version - ); - } - } - ir_peri.block = Some(format!("{}::{}", bi.kind, bi.block)); - - if bi.kind == "gpio" { - assert_eq!(0, (p.address as u32 - gpio_base) % gpio_stride); - } - } - - dev.peripherals.push(ir_peri); - } - - for irq in &core.interrupts { - dev.interrupts.push(ir::Interrupt { - name: irq.name.clone(), - description: None, - value: irq.number, - }); - } - - ir.devices.insert("".to_string(), dev); - - let mut extra = format!( - "pub fn GPIO(n: usize) -> gpio::Gpio {{ - gpio::Gpio(({} + {}*n) as _) - }}", - gpio_base, gpio_stride, - ); - - for (module, version) in &peripheral_versions { - self.all_peripheral_versions.insert((module.clone(), version.clone())); - write!( - &mut extra, - "#[path=\"../../peripherals/{}_{}.rs\"] pub mod {};\n", - module, version, module - ) - .unwrap(); - } - write!(&mut extra, "pub const CORE_INDEX: usize = {};\n", core_index).unwrap(); - - let flash = chip.memory.iter().find(|r| r.name == "BANK_1").unwrap(); - let settings = flash.settings.as_ref().unwrap(); - write!(&mut extra, "pub const FLASH_BASE: usize = {};\n", flash.address).unwrap(); - write!(&mut extra, "pub const FLASH_SIZE: usize = {};\n", flash.size).unwrap(); - write!(&mut extra, "pub const ERASE_SIZE: usize = {};\n", settings.erase_size).unwrap(); - write!(&mut extra, "pub const WRITE_SIZE: usize = {};\n", settings.write_size).unwrap(); - write!(&mut extra, "pub const ERASE_VALUE: u8 = {};\n", settings.erase_value).unwrap(); - - // Cleanups! - transform::sort::Sort {}.run(&mut ir).unwrap(); - transform::Sanitize {}.run(&mut ir).unwrap(); - - // ============================== - // Setup chip dir - - let chip_dir = self - .opts - .out_dir - .join("src/chips") - .join(chip_core_name.to_ascii_lowercase()); - fs::create_dir_all(&chip_dir).unwrap(); - - // ============================== - // generate pac.rs - - let data = generate::render(&ir, &gen_opts()).unwrap().to_string(); - let data = data.replace("] ", "]\n"); - - // Remove inner attributes like #![no_std] - let data = Regex::new("# *! *\\[.*\\]").unwrap().replace_all(&data, ""); - - let mut file = File::create(chip_dir.join("pac.rs")).unwrap(); - file.write_all(data.as_bytes()).unwrap(); - file.write_all(extra.as_bytes()).unwrap(); - - let mut device_x = String::new(); - - for irq in &core.interrupts { - write!(&mut device_x, "PROVIDE({} = DefaultHandler);\n", irq.name).unwrap(); - } - - // ============================== - // generate metadata.rs - - // (peripherals, interrupts, dma_channels) are often equal across multiple chips. - // To reduce bloat, deduplicate them. - let mut data = String::new(); - write!( - &mut data, - " - const PERIPHERALS: &'static [Peripheral] = {}; - const INTERRUPTS: &'static [Interrupt] = {}; - const DMA_CHANNELS: &'static [DmaChannel] = {}; - ", - stringify(&core.peripherals), - stringify(&core.interrupts), - stringify(&core.dma_channels), - ) - .unwrap(); - - let out_dir = self.opts.out_dir.clone(); - let n = self.metadata_dedup.len(); - let deduped_file = self.metadata_dedup.entry(data.clone()).or_insert_with(|| { - let file = format!("metadata_{:04}.rs", n); - let path = out_dir.join("src/chips").join(&file); - fs::write(path, data).unwrap(); - - file - }); - - let data = format!( - "include!(\"../{}\"); - pub const METADATA: Metadata = Metadata {{ - name: {:?}, - family: {:?}, - line: {:?}, - memory: {}, - peripherals: PERIPHERALS, - interrupts: INTERRUPTS, - dma_channels: DMA_CHANNELS, - }};", - deduped_file, - &chip.name, - &chip.family, - &chip.line, - stringify(&chip.memory), - ); - - let mut file = File::create(chip_dir.join("metadata.rs")).unwrap(); - file.write_all(data.as_bytes()).unwrap(); - - // ============================== - // generate device.x - - File::create(chip_dir.join("device.x")) - .unwrap() - .write_all(device_x.as_bytes()) - .unwrap(); - - // ============================== - // generate default memory.x - gen_memory_x(&chip_dir, &chip); - } - - fn load_chip(&mut self, name: &str) -> Chip { - let chip_path = self.opts.data_dir.join("chips").join(&format!("{}.json", name)); - let chip = fs::read(chip_path).expect(&format!("Could not load chip {}", name)); - serde_json::from_slice(&chip).unwrap() - } - - pub fn gen(&mut self) { - fs::create_dir_all(self.opts.out_dir.join("src/peripherals")).unwrap(); - fs::create_dir_all(self.opts.out_dir.join("src/chips")).unwrap(); - - let mut chip_core_names: Vec = Vec::new(); - - for chip_name in &self.opts.chips.clone() { - println!("Generating {}...", chip_name); - - let mut chip = self.load_chip(chip_name); - - // Cleanup - for core in &mut chip.cores { - for irq in &mut core.interrupts { - irq.name = irq.name.to_ascii_uppercase(); - } - for p in &mut core.peripherals { - for irq in &mut p.interrupts { - irq.interrupt = irq.interrupt.to_ascii_uppercase(); - } - } - } - - // Generate - for (core_index, core) in chip.cores.iter().enumerate() { - let chip_core_name = match chip.cores.len() { - 1 => chip_name.clone(), - _ => format!("{}-{}", chip_name, core.name), - }; - - chip_core_names.push(chip_core_name.clone()); - self.gen_chip(&chip_core_name, &chip, core, core_index) - } - } - - for (module, version) in &self.all_peripheral_versions { - println!("loading {} {}", module, version); - - let regs_path = Path::new(&self.opts.data_dir) - .join("registers") - .join(&format!("{}_{}.yaml", module, version)); - - let mut ir: ir::IR = serde_yaml::from_reader(File::open(regs_path).unwrap()).unwrap(); - - transform::expand_extends::ExpandExtends {}.run(&mut ir).unwrap(); - - transform::map_names(&mut ir, |k, s| match k { - transform::NameKind::Block => *s = format!("{}", s), - transform::NameKind::Fieldset => *s = format!("regs::{}", s), - transform::NameKind::Enum => *s = format!("vals::{}", s), - _ => {} - }); - - transform::sort::Sort {}.run(&mut ir).unwrap(); - transform::Sanitize {}.run(&mut ir).unwrap(); - - let items = generate::render(&ir, &gen_opts()).unwrap(); - let mut file = File::create( - self.opts - .out_dir - .join("src/peripherals") - .join(format!("{}_{}.rs", module, version)), - ) - .unwrap(); - let data = items.to_string().replace("] ", "]\n"); - - // Remove inner attributes like #![no_std] - let re = Regex::new("# *! *\\[.*\\]").unwrap(); - let data = re.replace_all(&data, ""); - file.write_all(data.as_bytes()).unwrap(); - } - - // Generate Cargo.toml - const BUILDDEP_BEGIN: &[u8] = b"# BEGIN BUILD DEPENDENCIES"; - const BUILDDEP_END: &[u8] = b"# END BUILD DEPENDENCIES"; - - let mut contents = include_bytes!("../../stm32-metapac/Cargo.toml").to_vec(); - let begin = bytes_find(&contents, BUILDDEP_BEGIN).unwrap(); - let end = bytes_find(&contents, BUILDDEP_END).unwrap() + BUILDDEP_END.len(); - contents.drain(begin..end); - fs::write(self.opts.out_dir.join("Cargo.toml"), contents).unwrap(); - - // copy misc files - fs::write( - self.opts.out_dir.join("build.rs"), - include_bytes!("../../stm32-metapac/build_pregenerated.rs"), - ) - .unwrap(); - fs::write( - self.opts.out_dir.join("src/lib.rs"), - include_bytes!("../../stm32-metapac/src/lib.rs"), - ) - .unwrap(); - fs::write( - self.opts.out_dir.join("src/common.rs"), - chiptool::generate::COMMON_MODULE, - ) - .unwrap(); - fs::write( - self.opts.out_dir.join("src/metadata.rs"), - include_bytes!("../../stm32-metapac/src/metadata.rs"), - ) - .unwrap(); - } -} - -fn bytes_find(haystack: &[u8], needle: &[u8]) -> Option { - haystack.windows(needle.len()).position(|window| window == needle) -} - -fn stringify(metadata: T) -> String { - let mut metadata = format!("{:?}", metadata); - if metadata.starts_with('[') { - metadata = format!("&{}", metadata); - } - metadata = metadata.replace(": [", ": &["); - metadata = metadata.replace("kind: Ram", "kind: MemoryRegionKind::Ram"); - metadata = metadata.replace("kind: Flash", "kind: MemoryRegionKind::Flash"); - metadata -} - -fn gen_opts() -> generate::Options { - generate::Options { - common_module: CommonModule::External(TokenStream::from_str("crate::common").unwrap()), - } -} - -fn gen_memory_x(out_dir: &PathBuf, chip: &Chip) { - let mut memory_x = String::new(); - - let flash = chip.memory.iter().find(|r| r.name == "BANK_1").unwrap(); - let ram = chip.memory.iter().find(|r| r.name == "SRAM").unwrap(); - - write!(memory_x, "MEMORY\n{{\n").unwrap(); - write!( - memory_x, - " FLASH : ORIGIN = 0x{:x}, LENGTH = {}\n", - flash.address, flash.size, - ) - .unwrap(); - write!( - memory_x, - " RAM : ORIGIN = 0x{:x}, LENGTH = {}\n", - ram.address, ram.size, - ) - .unwrap(); - write!(memory_x, "}}").unwrap(); - - fs::create_dir_all(out_dir.join("memory_x")).unwrap(); - let mut file = File::create(out_dir.join("memory_x").join("memory.x")).unwrap(); - file.write_all(memory_x.as_bytes()).unwrap(); -} diff --git a/stm32-metapac-gen/src/main.rs b/stm32-metapac-gen/src/main.rs deleted file mode 100644 index 40a73adf8..000000000 --- a/stm32-metapac-gen/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::env::args; -use std::path::PathBuf; - -use stm32_metapac_gen::*; - -fn main() { - let out_dir = PathBuf::from("out"); - let data_dir = PathBuf::from("../stm32-data/data"); - - let args: Vec = args().collect(); - - let mut chips = match &args[..] { - [_, chip] => { - vec![chip.clone()] - } - [_] => { - std::fs::read_dir(data_dir.join("chips")) - .unwrap() - .filter_map(|res| res.unwrap().file_name().to_str().map(|s| s.to_string())) - .filter(|s| s.ends_with(".json")) - .filter(|s| !s.starts_with("STM32GBK")) // cursed weird STM32G4 - .map(|s| s.strip_suffix(".json").unwrap().to_string()) - .collect() - } - _ => panic!("usage: stm32-metapac-gen [chip?]"), - }; - - chips.sort(); - - let opts = Options { - out_dir, - data_dir, - chips, - }; - Gen::new(opts).gen(); -} diff --git a/stm32-metapac/Cargo.toml b/stm32-metapac/Cargo.toml deleted file mode 100644 index 2605cf3d3..000000000 --- a/stm32-metapac/Cargo.toml +++ /dev/null @@ -1,1332 +0,0 @@ -[package] -name = "stm32-metapac" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/embassy-rs/embassy" -description = "Peripheral Access Crate (PAC) for all STM32 chips, including metadata." - -# `cargo publish` is unable to figure out which .rs files are needed due to the include! magic. -include = [ - "**/*.rs", - "**/*.x", - "Cargo.toml", -] - -[package.metadata.docs.rs] -features = ["stm32h755zi-cm7", "pac", "metadata"] -default-target = "thumbv7em-none-eabihf" -targets = [] - -[package.metadata.embassy_docs] -features = ["pac", "metadata"] -flavors = [ - { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, - { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, - { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, - { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, - { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, - { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi" }, - { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, - { regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf" }, - { regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" }, - { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, - { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, -] - -[dependencies] -cortex-m = "0.7.6" -cortex-m-rt = { version = ">=0.6.15,<0.8", optional = true } - -# BEGIN BUILD DEPENDENCIES -# These are removed when generating the pre-generated crate using the tool at gen/. -[build-dependencies] -stm32-metapac-gen = { path = "../stm32-metapac-gen" } -regex = "1.5.4" -# END BUILD DEPENDENCIES - -[features] -default = ["pac"] - -# Build the actual PAC. Set by default. -# If you just want the metadata, unset it with `default-features = false`. -pac = [] - -# Build the chip metadata. -# If set, a const `stm32_metapac::METADATA` will be exported, containing all the -# metadata for the currently selected chip. -metadata = [] - -rt = ["cortex-m-rt/device"] -memory-x = [] - -# BEGIN GENERATED FEATURES -# Generated by stm32-gen-features. DO NOT EDIT. -stm32c011d6 = [] -stm32c011f4 = [] -stm32c011f6 = [] -stm32c011j4 = [] -stm32c011j6 = [] -stm32c031c4 = [] -stm32c031c6 = [] -stm32c031f4 = [] -stm32c031f6 = [] -stm32c031g4 = [] -stm32c031g6 = [] -stm32c031k4 = [] -stm32c031k6 = [] -stm32f030c6 = [] -stm32f030c8 = [] -stm32f030cc = [] -stm32f030f4 = [] -stm32f030k6 = [] -stm32f030r8 = [] -stm32f030rc = [] -stm32f031c4 = [] -stm32f031c6 = [] -stm32f031e6 = [] -stm32f031f4 = [] -stm32f031f6 = [] -stm32f031g4 = [] -stm32f031g6 = [] -stm32f031k4 = [] -stm32f031k6 = [] -stm32f038c6 = [] -stm32f038e6 = [] -stm32f038f6 = [] -stm32f038g6 = [] -stm32f038k6 = [] -stm32f042c4 = [] -stm32f042c6 = [] -stm32f042f4 = [] -stm32f042f6 = [] -stm32f042g4 = [] -stm32f042g6 = [] -stm32f042k4 = [] -stm32f042k6 = [] -stm32f042t6 = [] -stm32f048c6 = [] -stm32f048g6 = [] -stm32f048t6 = [] -stm32f051c4 = [] -stm32f051c6 = [] -stm32f051c8 = [] -stm32f051k4 = [] -stm32f051k6 = [] -stm32f051k8 = [] -stm32f051r4 = [] -stm32f051r6 = [] -stm32f051r8 = [] -stm32f051t8 = [] -stm32f058c8 = [] -stm32f058r8 = [] -stm32f058t8 = [] -stm32f070c6 = [] -stm32f070cb = [] -stm32f070f6 = [] -stm32f070rb = [] -stm32f071c8 = [] -stm32f071cb = [] -stm32f071rb = [] -stm32f071v8 = [] -stm32f071vb = [] -stm32f072c8 = [] -stm32f072cb = [] -stm32f072r8 = [] -stm32f072rb = [] -stm32f072v8 = [] -stm32f072vb = [] -stm32f078cb = [] -stm32f078rb = [] -stm32f078vb = [] -stm32f091cb = [] -stm32f091cc = [] -stm32f091rb = [] -stm32f091rc = [] -stm32f091vb = [] -stm32f091vc = [] -stm32f098cc = [] -stm32f098rc = [] -stm32f098vc = [] -stm32f100c4 = [] -stm32f100c6 = [] -stm32f100c8 = [] -stm32f100cb = [] -stm32f100r4 = [] -stm32f100r6 = [] -stm32f100r8 = [] -stm32f100rb = [] -stm32f100rc = [] -stm32f100rd = [] -stm32f100re = [] -stm32f100v8 = [] -stm32f100vb = [] -stm32f100vc = [] -stm32f100vd = [] -stm32f100ve = [] -stm32f100zc = [] -stm32f100zd = [] -stm32f100ze = [] -stm32f101c4 = [] -stm32f101c6 = [] -stm32f101c8 = [] -stm32f101cb = [] -stm32f101r4 = [] -stm32f101r6 = [] -stm32f101r8 = [] -stm32f101rb = [] -stm32f101rc = [] -stm32f101rd = [] -stm32f101re = [] -stm32f101rf = [] -stm32f101rg = [] -stm32f101t4 = [] -stm32f101t6 = [] -stm32f101t8 = [] -stm32f101tb = [] -stm32f101v8 = [] -stm32f101vb = [] -stm32f101vc = [] -stm32f101vd = [] -stm32f101ve = [] -stm32f101vf = [] -stm32f101vg = [] -stm32f101zc = [] -stm32f101zd = [] -stm32f101ze = [] -stm32f101zf = [] -stm32f101zg = [] -stm32f102c4 = [] -stm32f102c6 = [] -stm32f102c8 = [] -stm32f102cb = [] -stm32f102r4 = [] -stm32f102r6 = [] -stm32f102r8 = [] -stm32f102rb = [] -stm32f103c4 = [] -stm32f103c6 = [] -stm32f103c8 = [] -stm32f103cb = [] -stm32f103r4 = [] -stm32f103r6 = [] -stm32f103r8 = [] -stm32f103rb = [] -stm32f103rc = [] -stm32f103rd = [] -stm32f103re = [] -stm32f103rf = [] -stm32f103rg = [] -stm32f103t4 = [] -stm32f103t6 = [] -stm32f103t8 = [] -stm32f103tb = [] -stm32f103v8 = [] -stm32f103vb = [] -stm32f103vc = [] -stm32f103vd = [] -stm32f103ve = [] -stm32f103vf = [] -stm32f103vg = [] -stm32f103zc = [] -stm32f103zd = [] -stm32f103ze = [] -stm32f103zf = [] -stm32f103zg = [] -stm32f105r8 = [] -stm32f105rb = [] -stm32f105rc = [] -stm32f105v8 = [] -stm32f105vb = [] -stm32f105vc = [] -stm32f107rb = [] -stm32f107rc = [] -stm32f107vb = [] -stm32f107vc = [] -stm32f205rb = [] -stm32f205rc = [] -stm32f205re = [] -stm32f205rf = [] -stm32f205rg = [] -stm32f205vb = [] -stm32f205vc = [] -stm32f205ve = [] -stm32f205vf = [] -stm32f205vg = [] -stm32f205zc = [] -stm32f205ze = [] -stm32f205zf = [] -stm32f205zg = [] -stm32f207ic = [] -stm32f207ie = [] -stm32f207if = [] -stm32f207ig = [] -stm32f207vc = [] -stm32f207ve = [] -stm32f207vf = [] -stm32f207vg = [] -stm32f207zc = [] -stm32f207ze = [] -stm32f207zf = [] -stm32f207zg = [] -stm32f215re = [] -stm32f215rg = [] -stm32f215ve = [] -stm32f215vg = [] -stm32f215ze = [] -stm32f215zg = [] -stm32f217ie = [] -stm32f217ig = [] -stm32f217ve = [] -stm32f217vg = [] -stm32f217ze = [] -stm32f217zg = [] -stm32f301c6 = [] -stm32f301c8 = [] -stm32f301k6 = [] -stm32f301k8 = [] -stm32f301r6 = [] -stm32f301r8 = [] -stm32f302c6 = [] -stm32f302c8 = [] -stm32f302cb = [] -stm32f302cc = [] -stm32f302k6 = [] -stm32f302k8 = [] -stm32f302r6 = [] -stm32f302r8 = [] -stm32f302rb = [] -stm32f302rc = [] -stm32f302rd = [] -stm32f302re = [] -stm32f302vb = [] -stm32f302vc = [] -stm32f302vd = [] -stm32f302ve = [] -stm32f302zd = [] -stm32f302ze = [] -stm32f303c6 = [] -stm32f303c8 = [] -stm32f303cb = [] -stm32f303cc = [] -stm32f303k6 = [] -stm32f303k8 = [] -stm32f303r6 = [] -stm32f303r8 = [] -stm32f303rb = [] -stm32f303rc = [] -stm32f303rd = [] -stm32f303re = [] -stm32f303vb = [] -stm32f303vc = [] -stm32f303vd = [] -stm32f303ve = [] -stm32f303zd = [] -stm32f303ze = [] -stm32f318c8 = [] -stm32f318k8 = [] -stm32f328c8 = [] -stm32f334c4 = [] -stm32f334c6 = [] -stm32f334c8 = [] -stm32f334k4 = [] -stm32f334k6 = [] -stm32f334k8 = [] -stm32f334r6 = [] -stm32f334r8 = [] -stm32f358cc = [] -stm32f358rc = [] -stm32f358vc = [] -stm32f373c8 = [] -stm32f373cb = [] -stm32f373cc = [] -stm32f373r8 = [] -stm32f373rb = [] -stm32f373rc = [] -stm32f373v8 = [] -stm32f373vb = [] -stm32f373vc = [] -stm32f378cc = [] -stm32f378rc = [] -stm32f378vc = [] -stm32f398ve = [] -stm32f401cb = [] -stm32f401cc = [] -stm32f401cd = [] -stm32f401ce = [] -stm32f401rb = [] -stm32f401rc = [] -stm32f401rd = [] -stm32f401re = [] -stm32f401vb = [] -stm32f401vc = [] -stm32f401vd = [] -stm32f401ve = [] -stm32f405oe = [] -stm32f405og = [] -stm32f405rg = [] -stm32f405vg = [] -stm32f405zg = [] -stm32f407ie = [] -stm32f407ig = [] -stm32f407ve = [] -stm32f407vg = [] -stm32f407ze = [] -stm32f407zg = [] -stm32f410c8 = [] -stm32f410cb = [] -stm32f410r8 = [] -stm32f410rb = [] -stm32f410t8 = [] -stm32f410tb = [] -stm32f411cc = [] -stm32f411ce = [] -stm32f411rc = [] -stm32f411re = [] -stm32f411vc = [] -stm32f411ve = [] -stm32f412ce = [] -stm32f412cg = [] -stm32f412re = [] -stm32f412rg = [] -stm32f412ve = [] -stm32f412vg = [] -stm32f412ze = [] -stm32f412zg = [] -stm32f413cg = [] -stm32f413ch = [] -stm32f413mg = [] -stm32f413mh = [] -stm32f413rg = [] -stm32f413rh = [] -stm32f413vg = [] -stm32f413vh = [] -stm32f413zg = [] -stm32f413zh = [] -stm32f415og = [] -stm32f415rg = [] -stm32f415vg = [] -stm32f415zg = [] -stm32f417ie = [] -stm32f417ig = [] -stm32f417ve = [] -stm32f417vg = [] -stm32f417ze = [] -stm32f417zg = [] -stm32f423ch = [] -stm32f423mh = [] -stm32f423rh = [] -stm32f423vh = [] -stm32f423zh = [] -stm32f427ag = [] -stm32f427ai = [] -stm32f427ig = [] -stm32f427ii = [] -stm32f427vg = [] -stm32f427vi = [] -stm32f427zg = [] -stm32f427zi = [] -stm32f429ag = [] -stm32f429ai = [] -stm32f429be = [] -stm32f429bg = [] -stm32f429bi = [] -stm32f429ie = [] -stm32f429ig = [] -stm32f429ii = [] -stm32f429ne = [] -stm32f429ng = [] -stm32f429ni = [] -stm32f429ve = [] -stm32f429vg = [] -stm32f429vi = [] -stm32f429ze = [] -stm32f429zg = [] -stm32f429zi = [] -stm32f437ai = [] -stm32f437ig = [] -stm32f437ii = [] -stm32f437vg = [] -stm32f437vi = [] -stm32f437zg = [] -stm32f437zi = [] -stm32f439ai = [] -stm32f439bg = [] -stm32f439bi = [] -stm32f439ig = [] -stm32f439ii = [] -stm32f439ng = [] -stm32f439ni = [] -stm32f439vg = [] -stm32f439vi = [] -stm32f439zg = [] -stm32f439zi = [] -stm32f446mc = [] -stm32f446me = [] -stm32f446rc = [] -stm32f446re = [] -stm32f446vc = [] -stm32f446ve = [] -stm32f446zc = [] -stm32f446ze = [] -stm32f469ae = [] -stm32f469ag = [] -stm32f469ai = [] -stm32f469be = [] -stm32f469bg = [] -stm32f469bi = [] -stm32f469ie = [] -stm32f469ig = [] -stm32f469ii = [] -stm32f469ne = [] -stm32f469ng = [] -stm32f469ni = [] -stm32f469ve = [] -stm32f469vg = [] -stm32f469vi = [] -stm32f469ze = [] -stm32f469zg = [] -stm32f469zi = [] -stm32f479ag = [] -stm32f479ai = [] -stm32f479bg = [] -stm32f479bi = [] -stm32f479ig = [] -stm32f479ii = [] -stm32f479ng = [] -stm32f479ni = [] -stm32f479vg = [] -stm32f479vi = [] -stm32f479zg = [] -stm32f479zi = [] -stm32f722ic = [] -stm32f722ie = [] -stm32f722rc = [] -stm32f722re = [] -stm32f722vc = [] -stm32f722ve = [] -stm32f722zc = [] -stm32f722ze = [] -stm32f723ic = [] -stm32f723ie = [] -stm32f723vc = [] -stm32f723ve = [] -stm32f723zc = [] -stm32f723ze = [] -stm32f730i8 = [] -stm32f730r8 = [] -stm32f730v8 = [] -stm32f730z8 = [] -stm32f732ie = [] -stm32f732re = [] -stm32f732ve = [] -stm32f732ze = [] -stm32f733ie = [] -stm32f733ve = [] -stm32f733ze = [] -stm32f745ie = [] -stm32f745ig = [] -stm32f745ve = [] -stm32f745vg = [] -stm32f745ze = [] -stm32f745zg = [] -stm32f746be = [] -stm32f746bg = [] -stm32f746ie = [] -stm32f746ig = [] -stm32f746ne = [] -stm32f746ng = [] -stm32f746ve = [] -stm32f746vg = [] -stm32f746ze = [] -stm32f746zg = [] -stm32f750n8 = [] -stm32f750v8 = [] -stm32f750z8 = [] -stm32f756bg = [] -stm32f756ig = [] -stm32f756ng = [] -stm32f756vg = [] -stm32f756zg = [] -stm32f765bg = [] -stm32f765bi = [] -stm32f765ig = [] -stm32f765ii = [] -stm32f765ng = [] -stm32f765ni = [] -stm32f765vg = [] -stm32f765vi = [] -stm32f765zg = [] -stm32f765zi = [] -stm32f767bg = [] -stm32f767bi = [] -stm32f767ig = [] -stm32f767ii = [] -stm32f767ng = [] -stm32f767ni = [] -stm32f767vg = [] -stm32f767vi = [] -stm32f767zg = [] -stm32f767zi = [] -stm32f768ai = [] -stm32f769ag = [] -stm32f769ai = [] -stm32f769bg = [] -stm32f769bi = [] -stm32f769ig = [] -stm32f769ii = [] -stm32f769ng = [] -stm32f769ni = [] -stm32f777bi = [] -stm32f777ii = [] -stm32f777ni = [] -stm32f777vi = [] -stm32f777zi = [] -stm32f778ai = [] -stm32f779ai = [] -stm32f779bi = [] -stm32f779ii = [] -stm32f779ni = [] -stm32g030c6 = [] -stm32g030c8 = [] -stm32g030f6 = [] -stm32g030j6 = [] -stm32g030k6 = [] -stm32g030k8 = [] -stm32g031c4 = [] -stm32g031c6 = [] -stm32g031c8 = [] -stm32g031f4 = [] -stm32g031f6 = [] -stm32g031f8 = [] -stm32g031g4 = [] -stm32g031g6 = [] -stm32g031g8 = [] -stm32g031j4 = [] -stm32g031j6 = [] -stm32g031k4 = [] -stm32g031k6 = [] -stm32g031k8 = [] -stm32g031y8 = [] -stm32g041c6 = [] -stm32g041c8 = [] -stm32g041f6 = [] -stm32g041f8 = [] -stm32g041g6 = [] -stm32g041g8 = [] -stm32g041j6 = [] -stm32g041k6 = [] -stm32g041k8 = [] -stm32g041y8 = [] -stm32g050c6 = [] -stm32g050c8 = [] -stm32g050f6 = [] -stm32g050k6 = [] -stm32g050k8 = [] -stm32g051c6 = [] -stm32g051c8 = [] -stm32g051f6 = [] -stm32g051f8 = [] -stm32g051g6 = [] -stm32g051g8 = [] -stm32g051k6 = [] -stm32g051k8 = [] -stm32g061c6 = [] -stm32g061c8 = [] -stm32g061f6 = [] -stm32g061f8 = [] -stm32g061g6 = [] -stm32g061g8 = [] -stm32g061k6 = [] -stm32g061k8 = [] -stm32g070cb = [] -stm32g070kb = [] -stm32g070rb = [] -stm32g071c6 = [] -stm32g071c8 = [] -stm32g071cb = [] -stm32g071eb = [] -stm32g071g6 = [] -stm32g071g8 = [] -stm32g071gb = [] -stm32g071k6 = [] -stm32g071k8 = [] -stm32g071kb = [] -stm32g071r6 = [] -stm32g071r8 = [] -stm32g071rb = [] -stm32g081cb = [] -stm32g081eb = [] -stm32g081gb = [] -stm32g081kb = [] -stm32g081rb = [] -stm32g0b0ce = [] -stm32g0b0ke = [] -stm32g0b0re = [] -stm32g0b0ve = [] -stm32g0b1cb = [] -stm32g0b1cc = [] -stm32g0b1ce = [] -stm32g0b1kb = [] -stm32g0b1kc = [] -stm32g0b1ke = [] -stm32g0b1mb = [] -stm32g0b1mc = [] -stm32g0b1me = [] -stm32g0b1ne = [] -stm32g0b1rb = [] -stm32g0b1rc = [] -stm32g0b1re = [] -stm32g0b1vb = [] -stm32g0b1vc = [] -stm32g0b1ve = [] -stm32g0c1cc = [] -stm32g0c1ce = [] -stm32g0c1kc = [] -stm32g0c1ke = [] -stm32g0c1mc = [] -stm32g0c1me = [] -stm32g0c1ne = [] -stm32g0c1rc = [] -stm32g0c1re = [] -stm32g0c1vc = [] -stm32g0c1ve = [] -stm32g431c6 = [] -stm32g431c8 = [] -stm32g431cb = [] -stm32g431k6 = [] -stm32g431k8 = [] -stm32g431kb = [] -stm32g431m6 = [] -stm32g431m8 = [] -stm32g431mb = [] -stm32g431r6 = [] -stm32g431r8 = [] -stm32g431rb = [] -stm32g431v6 = [] -stm32g431v8 = [] -stm32g431vb = [] -stm32g441cb = [] -stm32g441kb = [] -stm32g441mb = [] -stm32g441rb = [] -stm32g441vb = [] -stm32g471cc = [] -stm32g471ce = [] -stm32g471mc = [] -stm32g471me = [] -stm32g471qc = [] -stm32g471qe = [] -stm32g471rc = [] -stm32g471re = [] -stm32g471vc = [] -stm32g471ve = [] -stm32g473cb = [] -stm32g473cc = [] -stm32g473ce = [] -stm32g473mb = [] -stm32g473mc = [] -stm32g473me = [] -stm32g473pb = [] -stm32g473pc = [] -stm32g473pe = [] -stm32g473qb = [] -stm32g473qc = [] -stm32g473qe = [] -stm32g473rb = [] -stm32g473rc = [] -stm32g473re = [] -stm32g473vb = [] -stm32g473vc = [] -stm32g473ve = [] -stm32g474cb = [] -stm32g474cc = [] -stm32g474ce = [] -stm32g474mb = [] -stm32g474mc = [] -stm32g474me = [] -stm32g474pb = [] -stm32g474pc = [] -stm32g474pe = [] -stm32g474qb = [] -stm32g474qc = [] -stm32g474qe = [] -stm32g474rb = [] -stm32g474rc = [] -stm32g474re = [] -stm32g474vb = [] -stm32g474vc = [] -stm32g474ve = [] -stm32g483ce = [] -stm32g483me = [] -stm32g483pe = [] -stm32g483qe = [] -stm32g483re = [] -stm32g483ve = [] -stm32g484ce = [] -stm32g484me = [] -stm32g484pe = [] -stm32g484qe = [] -stm32g484re = [] -stm32g484ve = [] -stm32g491cc = [] -stm32g491ce = [] -stm32g491kc = [] -stm32g491ke = [] -stm32g491mc = [] -stm32g491me = [] -stm32g491rc = [] -stm32g491re = [] -stm32g491vc = [] -stm32g491ve = [] -stm32g4a1ce = [] -stm32g4a1ke = [] -stm32g4a1me = [] -stm32g4a1re = [] -stm32g4a1ve = [] -stm32h723ve = [] -stm32h723vg = [] -stm32h723ze = [] -stm32h723zg = [] -stm32h725ae = [] -stm32h725ag = [] -stm32h725ie = [] -stm32h725ig = [] -stm32h725re = [] -stm32h725rg = [] -stm32h725ve = [] -stm32h725vg = [] -stm32h725ze = [] -stm32h725zg = [] -stm32h730ab = [] -stm32h730ib = [] -stm32h730vb = [] -stm32h730zb = [] -stm32h733vg = [] -stm32h733zg = [] -stm32h735ag = [] -stm32h735ig = [] -stm32h735rg = [] -stm32h735vg = [] -stm32h735zg = [] -stm32h742ag = [] -stm32h742ai = [] -stm32h742bg = [] -stm32h742bi = [] -stm32h742ig = [] -stm32h742ii = [] -stm32h742vg = [] -stm32h742vi = [] -stm32h742xg = [] -stm32h742xi = [] -stm32h742zg = [] -stm32h742zi = [] -stm32h743ag = [] -stm32h743ai = [] -stm32h743bg = [] -stm32h743bi = [] -stm32h743ig = [] -stm32h743ii = [] -stm32h743vg = [] -stm32h743vi = [] -stm32h743xg = [] -stm32h743xi = [] -stm32h743zg = [] -stm32h743zi = [] -stm32h745bg-cm7 = [] -stm32h745bg-cm4 = [] -stm32h745bi-cm7 = [] -stm32h745bi-cm4 = [] -stm32h745ig-cm7 = [] -stm32h745ig-cm4 = [] -stm32h745ii-cm7 = [] -stm32h745ii-cm4 = [] -stm32h745xg-cm7 = [] -stm32h745xg-cm4 = [] -stm32h745xi-cm7 = [] -stm32h745xi-cm4 = [] -stm32h745zg-cm7 = [] -stm32h745zg-cm4 = [] -stm32h745zi-cm7 = [] -stm32h745zi-cm4 = [] -stm32h747ag-cm7 = [] -stm32h747ag-cm4 = [] -stm32h747ai-cm7 = [] -stm32h747ai-cm4 = [] -stm32h747bg-cm7 = [] -stm32h747bg-cm4 = [] -stm32h747bi-cm7 = [] -stm32h747bi-cm4 = [] -stm32h747ig-cm7 = [] -stm32h747ig-cm4 = [] -stm32h747ii-cm7 = [] -stm32h747ii-cm4 = [] -stm32h747xg-cm7 = [] -stm32h747xg-cm4 = [] -stm32h747xi-cm7 = [] -stm32h747xi-cm4 = [] -stm32h747zi-cm7 = [] -stm32h747zi-cm4 = [] -stm32h750ib = [] -stm32h750vb = [] -stm32h750xb = [] -stm32h750zb = [] -stm32h753ai = [] -stm32h753bi = [] -stm32h753ii = [] -stm32h753vi = [] -stm32h753xi = [] -stm32h753zi = [] -stm32h755bi-cm7 = [] -stm32h755bi-cm4 = [] -stm32h755ii-cm7 = [] -stm32h755ii-cm4 = [] -stm32h755xi-cm7 = [] -stm32h755xi-cm4 = [] -stm32h755zi-cm7 = [] -stm32h755zi-cm4 = [] -stm32h757ai-cm7 = [] -stm32h757ai-cm4 = [] -stm32h757bi-cm7 = [] -stm32h757bi-cm4 = [] -stm32h757ii-cm7 = [] -stm32h757ii-cm4 = [] -stm32h757xi-cm7 = [] -stm32h757xi-cm4 = [] -stm32h757zi-cm7 = [] -stm32h757zi-cm4 = [] -stm32h7a3ag = [] -stm32h7a3ai = [] -stm32h7a3ig = [] -stm32h7a3ii = [] -stm32h7a3lg = [] -stm32h7a3li = [] -stm32h7a3ng = [] -stm32h7a3ni = [] -stm32h7a3qi = [] -stm32h7a3rg = [] -stm32h7a3ri = [] -stm32h7a3vg = [] -stm32h7a3vi = [] -stm32h7a3zg = [] -stm32h7a3zi = [] -stm32h7b0ab = [] -stm32h7b0ib = [] -stm32h7b0rb = [] -stm32h7b0vb = [] -stm32h7b0zb = [] -stm32h7b3ai = [] -stm32h7b3ii = [] -stm32h7b3li = [] -stm32h7b3ni = [] -stm32h7b3qi = [] -stm32h7b3ri = [] -stm32h7b3vi = [] -stm32h7b3zi = [] -stm32l010c6 = [] -stm32l010f4 = [] -stm32l010k4 = [] -stm32l010k8 = [] -stm32l010r8 = [] -stm32l010rb = [] -stm32l011d3 = [] -stm32l011d4 = [] -stm32l011e3 = [] -stm32l011e4 = [] -stm32l011f3 = [] -stm32l011f4 = [] -stm32l011g3 = [] -stm32l011g4 = [] -stm32l011k3 = [] -stm32l011k4 = [] -stm32l021d4 = [] -stm32l021f4 = [] -stm32l021g4 = [] -stm32l021k4 = [] -stm32l031c4 = [] -stm32l031c6 = [] -stm32l031e4 = [] -stm32l031e6 = [] -stm32l031f4 = [] -stm32l031f6 = [] -stm32l031g4 = [] -stm32l031g6 = [] -stm32l031k4 = [] -stm32l031k6 = [] -stm32l041c4 = [] -stm32l041c6 = [] -stm32l041e6 = [] -stm32l041f6 = [] -stm32l041g6 = [] -stm32l041k6 = [] -stm32l051c6 = [] -stm32l051c8 = [] -stm32l051k6 = [] -stm32l051k8 = [] -stm32l051r6 = [] -stm32l051r8 = [] -stm32l051t6 = [] -stm32l051t8 = [] -stm32l052c6 = [] -stm32l052c8 = [] -stm32l052k6 = [] -stm32l052k8 = [] -stm32l052r6 = [] -stm32l052r8 = [] -stm32l052t6 = [] -stm32l052t8 = [] -stm32l053c6 = [] -stm32l053c8 = [] -stm32l053r6 = [] -stm32l053r8 = [] -stm32l062c8 = [] -stm32l062k8 = [] -stm32l063c8 = [] -stm32l063r8 = [] -stm32l071c8 = [] -stm32l071cb = [] -stm32l071cz = [] -stm32l071k8 = [] -stm32l071kb = [] -stm32l071kz = [] -stm32l071rb = [] -stm32l071rz = [] -stm32l071v8 = [] -stm32l071vb = [] -stm32l071vz = [] -stm32l072cb = [] -stm32l072cz = [] -stm32l072kb = [] -stm32l072kz = [] -stm32l072rb = [] -stm32l072rz = [] -stm32l072v8 = [] -stm32l072vb = [] -stm32l072vz = [] -stm32l073cb = [] -stm32l073cz = [] -stm32l073rb = [] -stm32l073rz = [] -stm32l073v8 = [] -stm32l073vb = [] -stm32l073vz = [] -stm32l081cb = [] -stm32l081cz = [] -stm32l081kz = [] -stm32l082cz = [] -stm32l082kb = [] -stm32l082kz = [] -stm32l083cb = [] -stm32l083cz = [] -stm32l083rb = [] -stm32l083rz = [] -stm32l083v8 = [] -stm32l083vb = [] -stm32l083vz = [] -stm32l100c6-a = [] -stm32l100c6 = [] -stm32l100r8-a = [] -stm32l100r8 = [] -stm32l100rb-a = [] -stm32l100rb = [] -stm32l100rc = [] -stm32l151c6-a = [] -stm32l151c6 = [] -stm32l151c8-a = [] -stm32l151c8 = [] -stm32l151cb-a = [] -stm32l151cb = [] -stm32l151cc = [] -stm32l151qc = [] -stm32l151qd = [] -stm32l151qe = [] -stm32l151r6-a = [] -stm32l151r6 = [] -stm32l151r8-a = [] -stm32l151r8 = [] -stm32l151rb-a = [] -stm32l151rb = [] -stm32l151rc-a = [] -stm32l151rc = [] -stm32l151rd = [] -stm32l151re = [] -stm32l151uc = [] -stm32l151v8-a = [] -stm32l151v8 = [] -stm32l151vb-a = [] -stm32l151vb = [] -stm32l151vc-a = [] -stm32l151vc = [] -stm32l151vd-x = [] -stm32l151vd = [] -stm32l151ve = [] -stm32l151zc = [] -stm32l151zd = [] -stm32l151ze = [] -stm32l152c6-a = [] -stm32l152c6 = [] -stm32l152c8-a = [] -stm32l152c8 = [] -stm32l152cb-a = [] -stm32l152cb = [] -stm32l152cc = [] -stm32l152qc = [] -stm32l152qd = [] -stm32l152qe = [] -stm32l152r6-a = [] -stm32l152r6 = [] -stm32l152r8-a = [] -stm32l152r8 = [] -stm32l152rb-a = [] -stm32l152rb = [] -stm32l152rc-a = [] -stm32l152rc = [] -stm32l152rd = [] -stm32l152re = [] -stm32l152uc = [] -stm32l152v8-a = [] -stm32l152v8 = [] -stm32l152vb-a = [] -stm32l152vb = [] -stm32l152vc-a = [] -stm32l152vc = [] -stm32l152vd-x = [] -stm32l152vd = [] -stm32l152ve = [] -stm32l152zc = [] -stm32l152zd = [] -stm32l152ze = [] -stm32l162qc = [] -stm32l162qd = [] -stm32l162rc-a = [] -stm32l162rc = [] -stm32l162rd = [] -stm32l162re = [] -stm32l162vc-a = [] -stm32l162vc = [] -stm32l162vd-x = [] -stm32l162vd = [] -stm32l162ve = [] -stm32l162zc = [] -stm32l162zd = [] -stm32l162ze = [] -stm32l412c8 = [] -stm32l412cb = [] -stm32l412k8 = [] -stm32l412kb = [] -stm32l412r8 = [] -stm32l412rb = [] -stm32l412t8 = [] -stm32l412tb = [] -stm32l422cb = [] -stm32l422kb = [] -stm32l422rb = [] -stm32l422tb = [] -stm32l431cb = [] -stm32l431cc = [] -stm32l431kb = [] -stm32l431kc = [] -stm32l431rb = [] -stm32l431rc = [] -stm32l431vc = [] -stm32l432kb = [] -stm32l432kc = [] -stm32l433cb = [] -stm32l433cc = [] -stm32l433rb = [] -stm32l433rc = [] -stm32l433vc = [] -stm32l442kc = [] -stm32l443cc = [] -stm32l443rc = [] -stm32l443vc = [] -stm32l451cc = [] -stm32l451ce = [] -stm32l451rc = [] -stm32l451re = [] -stm32l451vc = [] -stm32l451ve = [] -stm32l452cc = [] -stm32l452ce = [] -stm32l452rc = [] -stm32l452re = [] -stm32l452vc = [] -stm32l452ve = [] -stm32l462ce = [] -stm32l462re = [] -stm32l462ve = [] -stm32l471qe = [] -stm32l471qg = [] -stm32l471re = [] -stm32l471rg = [] -stm32l471ve = [] -stm32l471vg = [] -stm32l471ze = [] -stm32l471zg = [] -stm32l475rc = [] -stm32l475re = [] -stm32l475rg = [] -stm32l475vc = [] -stm32l475ve = [] -stm32l475vg = [] -stm32l476je = [] -stm32l476jg = [] -stm32l476me = [] -stm32l476mg = [] -stm32l476qe = [] -stm32l476qg = [] -stm32l476rc = [] -stm32l476re = [] -stm32l476rg = [] -stm32l476vc = [] -stm32l476ve = [] -stm32l476vg = [] -stm32l476ze = [] -stm32l476zg = [] -stm32l486jg = [] -stm32l486qg = [] -stm32l486rg = [] -stm32l486vg = [] -stm32l486zg = [] -stm32l496ae = [] -stm32l496ag = [] -stm32l496qe = [] -stm32l496qg = [] -stm32l496re = [] -stm32l496rg = [] -stm32l496ve = [] -stm32l496vg = [] -stm32l496wg = [] -stm32l496ze = [] -stm32l496zg = [] -stm32l4a6ag = [] -stm32l4a6qg = [] -stm32l4a6rg = [] -stm32l4a6vg = [] -stm32l4a6zg = [] -stm32l4p5ae = [] -stm32l4p5ag = [] -stm32l4p5ce = [] -stm32l4p5cg = [] -stm32l4p5qe = [] -stm32l4p5qg = [] -stm32l4p5re = [] -stm32l4p5rg = [] -stm32l4p5ve = [] -stm32l4p5vg = [] -stm32l4p5ze = [] -stm32l4p5zg = [] -stm32l4q5ag = [] -stm32l4q5cg = [] -stm32l4q5qg = [] -stm32l4q5rg = [] -stm32l4q5vg = [] -stm32l4q5zg = [] -stm32l4r5ag = [] -stm32l4r5ai = [] -stm32l4r5qg = [] -stm32l4r5qi = [] -stm32l4r5vg = [] -stm32l4r5vi = [] -stm32l4r5zg = [] -stm32l4r5zi = [] -stm32l4r7ai = [] -stm32l4r7vi = [] -stm32l4r7zi = [] -stm32l4r9ag = [] -stm32l4r9ai = [] -stm32l4r9vg = [] -stm32l4r9vi = [] -stm32l4r9zg = [] -stm32l4r9zi = [] -stm32l4s5ai = [] -stm32l4s5qi = [] -stm32l4s5vi = [] -stm32l4s5zi = [] -stm32l4s7ai = [] -stm32l4s7vi = [] -stm32l4s7zi = [] -stm32l4s9ai = [] -stm32l4s9vi = [] -stm32l4s9zi = [] -stm32l552cc = [] -stm32l552ce = [] -stm32l552me = [] -stm32l552qc = [] -stm32l552qe = [] -stm32l552rc = [] -stm32l552re = [] -stm32l552vc = [] -stm32l552ve = [] -stm32l552zc = [] -stm32l552ze = [] -stm32l562ce = [] -stm32l562me = [] -stm32l562qe = [] -stm32l562re = [] -stm32l562ve = [] -stm32l562ze = [] -stm32u575ag = [] -stm32u575ai = [] -stm32u575cg = [] -stm32u575ci = [] -stm32u575og = [] -stm32u575oi = [] -stm32u575qg = [] -stm32u575qi = [] -stm32u575rg = [] -stm32u575ri = [] -stm32u575vg = [] -stm32u575vi = [] -stm32u575zg = [] -stm32u575zi = [] -stm32u585ai = [] -stm32u585ci = [] -stm32u585oi = [] -stm32u585qi = [] -stm32u585ri = [] -stm32u585vi = [] -stm32u585zi = [] -stm32wb10cc = [] -stm32wb15cc = [] -stm32wb30ce = [] -stm32wb35cc = [] -stm32wb35ce = [] -stm32wb50cg = [] -stm32wb55cc = [] -stm32wb55ce = [] -stm32wb55cg = [] -stm32wb55rc = [] -stm32wb55re = [] -stm32wb55rg = [] -stm32wb55vc = [] -stm32wb55ve = [] -stm32wb55vg = [] -stm32wb55vy = [] -stm32wl54cc-cm4 = [] -stm32wl54cc-cm0p = [] -stm32wl54jc-cm4 = [] -stm32wl54jc-cm0p = [] -stm32wl55cc-cm4 = [] -stm32wl55cc-cm0p = [] -stm32wl55jc-cm4 = [] -stm32wl55jc-cm0p = [] -stm32wle4c8 = [] -stm32wle4cb = [] -stm32wle4cc = [] -stm32wle4j8 = [] -stm32wle4jb = [] -stm32wle4jc = [] -stm32wle5c8 = [] -stm32wle5cb = [] -stm32wle5cc = [] -stm32wle5j8 = [] -stm32wle5jb = [] -stm32wle5jc = [] -# END GENERATED FEATURES diff --git a/stm32-metapac/build.rs b/stm32-metapac/build.rs deleted file mode 100644 index 0c183fa21..000000000 --- a/stm32-metapac/build.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::env; -use std::path::PathBuf; - -use stm32_metapac_gen::*; - -fn parse_chip_core(chip_and_core: &str) -> (String, Option) { - let mut s = chip_and_core.split('-'); - let chip_name: String = s.next().unwrap().to_string(); - if let Some(c) = s.next() { - if c.starts_with("cm") { - return (chip_name, Some(c.to_ascii_lowercase())); - } - } - - (chip_and_core.to_string(), None) -} - -fn main() { - let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - let data_dir = PathBuf::from("../stm32-data/data"); - - let chip_core_name = env::vars_os() - .map(|(a, _)| a.to_string_lossy().to_string()) - .find(|x| x.starts_with("CARGO_FEATURE_STM32")) - .expect("No stm32xx Cargo feature enabled") - .strip_prefix("CARGO_FEATURE_") - .unwrap() - .to_ascii_lowercase() - .replace('_', "-"); - - let (chip_name, _) = parse_chip_core(&chip_core_name); - - let opts = Options { - out_dir: out_dir.clone(), - data_dir: data_dir.clone(), - chips: vec![chip_name.to_ascii_uppercase()], - }; - Gen::new(opts).gen(); - - println!( - "cargo:rustc-link-search={}/src/chips/{}", - out_dir.display(), - chip_core_name, - ); - - #[cfg(feature = "memory-x")] - println!( - "cargo:rustc-link-search={}/src/chips/{}/memory_x/", - out_dir.display(), - chip_core_name - ); - println!( - "cargo:rustc-env=STM32_METAPAC_PAC_PATH={}/src/chips/{}/pac.rs", - out_dir.display(), - chip_core_name - ); - println!( - "cargo:rustc-env=STM32_METAPAC_METADATA_PATH={}/src/chips/{}/metadata.rs", - out_dir.display(), - chip_core_name - ); - println!( - "cargo:rustc-env=STM32_METAPAC_COMMON_PATH={}/src/common.rs", - out_dir.display(), - ); - - println!("cargo:rerun-if-changed=build.rs"); - - // When the stm32-data chip's JSON changes, we must rebuild - println!( - "cargo:rerun-if-changed={}/chips/{}.json", - data_dir.display(), - chip_name.to_uppercase() - ); - - println!("cargo:rerun-if-changed={}/registers", data_dir.display()); -} diff --git a/stm32-metapac/build_pregenerated.rs b/stm32-metapac/build_pregenerated.rs deleted file mode 100644 index 0f0358071..000000000 --- a/stm32-metapac/build_pregenerated.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::env; -use std::path::PathBuf; - -fn main() { - let crate_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); - - let chip_core_name = env::vars_os() - .map(|(a, _)| a.to_string_lossy().to_string()) - .find(|x| x.starts_with("CARGO_FEATURE_STM32")) - .expect("No stm32xx Cargo feature enabled") - .strip_prefix("CARGO_FEATURE_") - .unwrap() - .to_ascii_lowercase() - .replace('_', "-"); - - println!( - "cargo:rustc-link-search={}/src/chips/{}", - crate_dir.display(), - chip_core_name, - ); - - #[cfg(feature = "memory-x")] - println!( - "cargo:rustc-link-search={}/src/chips/{}/memory_x/", - crate_dir.display(), - chip_core_name - ); - println!("cargo:rustc-env=STM32_METAPAC_PAC_PATH=chips/{}/pac.rs", chip_core_name); - println!( - "cargo:rustc-env=STM32_METAPAC_METADATA_PATH=chips/{}/metadata.rs", - chip_core_name - ); - println!( - "cargo:rustc-env=STM32_METAPAC_COMMON_PATH={}/src/common.rs", - crate_dir.display(), - ); - - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/stm32-metapac/src/lib.rs b/stm32-metapac/src/lib.rs deleted file mode 100644 index 58a1c5e45..000000000 --- a/stm32-metapac/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![no_std] -#![allow(non_snake_case)] -#![allow(unused)] -#![allow(non_camel_case_types)] -#![doc(html_no_source)] - -pub mod common { - include!(env!("STM32_METAPAC_COMMON_PATH")); -} - -#[cfg(feature = "pac")] -include!(env!("STM32_METAPAC_PAC_PATH")); - -#[cfg(feature = "metadata")] -pub mod metadata { - include!("metadata.rs"); - include!(env!("STM32_METAPAC_METADATA_PATH")); -} diff --git a/stm32-metapac/src/metadata.rs b/stm32-metapac/src/metadata.rs deleted file mode 100644 index d05830e94..000000000 --- a/stm32-metapac/src/metadata.rs +++ /dev/null @@ -1,106 +0,0 @@ -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Metadata { - pub name: &'static str, - pub family: &'static str, - pub line: &'static str, - pub memory: &'static [MemoryRegion], - pub peripherals: &'static [Peripheral], - pub interrupts: &'static [Interrupt], - pub dma_channels: &'static [DmaChannel], -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct MemoryRegion { - pub name: &'static str, - pub kind: MemoryRegionKind, - pub address: u32, - pub size: u32, - pub settings: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct FlashSettings { - pub erase_size: u32, - pub write_size: u32, - pub erase_value: u8, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum MemoryRegionKind { - Flash, - Ram, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Interrupt { - pub name: &'static str, - pub number: u32, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Package { - pub name: &'static str, - pub package: &'static str, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Peripheral { - pub name: &'static str, - pub address: u64, - pub registers: Option, - pub rcc: Option, - pub pins: &'static [PeripheralPin], - pub dma_channels: &'static [PeripheralDmaChannel], - pub interrupts: &'static [PeripheralInterrupt], -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PeripheralRegisters { - pub kind: &'static str, - pub version: &'static str, - pub block: &'static str, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PeripheralInterrupt { - pub signal: &'static str, - pub interrupt: &'static str, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PeripheralRcc { - pub clock: &'static str, - pub enable: Option, - pub reset: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PeripheralRccRegister { - pub register: &'static str, - pub field: &'static str, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PeripheralPin { - pub pin: &'static str, - pub signal: &'static str, - pub af: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct DmaChannel { - pub name: &'static str, - pub dma: &'static str, - pub channel: u32, - pub dmamux: Option<&'static str>, - pub dmamux_channel: Option, -} - -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct PeripheralDmaChannel { - pub signal: &'static str, - pub channel: Option<&'static str>, - pub dmamux: Option<&'static str>, - pub dma: Option<&'static str>, - pub request: Option, -} diff --git a/xtask/.cargo/config.toml b/xtask/.cargo/config.toml deleted file mode 100644 index 919ec05a4..000000000 --- a/xtask/.cargo/config.toml +++ /dev/null @@ -1,6 +0,0 @@ -[alias] -xtask = "run --package xtask --" -ci = "run --package xtask -- ci" -core = "run --package xtask -- core" -examples = "run --package xtask -- examples" -fmt = "run --package xtask -- fmt" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml deleted file mode 100644 index 696cfdafe..000000000 --- a/xtask/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -edition = "2021" -name = "xtask" -version = "0.1.0" -license = "MIT OR Apache-2.0" - -[dependencies] -anyhow = "1.0.43" -xshell = "0.1.17" -yaml-rust = "0.4.5" -walkdir = "2.3.2" diff --git a/xtask/src/main.rs b/xtask/src/main.rs deleted file mode 100644 index b8b453fd0..000000000 --- a/xtask/src/main.rs +++ /dev/null @@ -1,221 +0,0 @@ -#![allow(dead_code)] -#![deny(unused_must_use)] - -use std::path::{Path, PathBuf}; -use std::{env, format, fs}; - -use walkdir::WalkDir; -use xshell::{cmd, Cmd}; -use yaml_rust::YamlLoader; - -extern crate yaml_rust; - -fn main() -> Result<(), anyhow::Error> { - let args = env::args().skip(1).collect::>(); - let args = args.iter().map(|s| &**s).collect::>(); - - match &args[..] { - ["ci"] => task_ci()?, - ["core"] => task_check(Realm::Core)?, - ["metapac"] => task_metapac_gen()?, - ["examples"] => task_check(Realm::Examples)?, - ["fmt-check"] => task_cargo_fmt_check()?, - ["fmt"] => task_cargo_fmt()?, - _ => { - println!(""); - println!("USAGE: cargo xtask [command]"); - println!(""); - println!("Commands:"); - println!(" ci :: Runs entire CI"); - println!(" core :: Builds the core"); - println!(" metapac :: Builds the metapac"); - println!(" examples :: Builds the examples"); - println!(" fmt-check :: Checks rustfmt"); - println!(" fmt :: Performs rustfmt"); - println!(""); - } - } - Ok(()) -} - -fn task_ci() -> Result<(), anyhow::Error> { - task_check(Realm::Core)?; - task_check(Realm::Examples)?; - task_metapac_gen()?; - task_cargo_fmt_check()?; - Ok(()) -} - -#[derive(Copy, Clone)] -enum Realm { - All, - Core, - Examples, -} - -impl Realm { - fn accepts(&self, package: &str) -> bool { - match self { - Realm::All => true, - Realm::Core => !package.contains("examples"), - Realm::Examples => package.contains("examples"), - } - } -} - -fn task_check(realm: Realm) -> Result<(), anyhow::Error> { - let _e = xshell::pushenv("CI", "true"); - - let matrix_yaml = root_dir().join(".github").join("workflows").join("rust.yml"); - - let matrix = YamlLoader::load_from_str(&*fs::read_to_string(matrix_yaml).unwrap()).unwrap(); - - let matrix = &matrix.get(0).unwrap()["jobs"]["ci"]["strategy"]["matrix"]["include"]; - - let entries = matrix.as_vec().unwrap(); - - for entry in entries { - let package = entry["package"].as_str().unwrap(); - if !realm.accepts(package) { - continue; - } - let target = entry["target"].as_str().unwrap(); - let features = entry["features"].as_str(); - let package_dir = root_dir().join(entry["package"].as_str().unwrap()); - let _p = xshell::pushd(package_dir)?; - banner(&*format!( - "Building {} [target={}] [features={}]", - package, - target, - features.unwrap_or("default-features") - )); - - let root_cargo_dir = root_dir().join(".cargo"); - fs::create_dir_all(root_cargo_dir.clone()).unwrap(); - fs::write( - root_cargo_dir.join("config"), - "[target.\"cfg(all())\"]\nrustflags = [\"-D\", \"warnings\"]", - ) - .unwrap(); - - let mut args = Vec::new(); - args.push("check"); - args.push("--target"); - args.push(target); - - if let Some(features) = features { - args.push("--features"); - args.push(features); - } - - let command = Cmd::new(PathBuf::from("cargo")); - let command = command.args(args); - let result = command.run(); - - fs::remove_file(root_cargo_dir.join("config")).unwrap(); - - result?; - } - - Ok(()) -} - -fn task_metapac_gen() -> Result<(), anyhow::Error> { - banner("Building metapac"); - let _p = xshell::pushd(root_dir().join("stm32-metapac-gen")); - cmd!("cargo run").run()?; - Ok(()) -} - -fn task_cargo_fmt() -> Result<(), anyhow::Error> { - for entry in WalkDir::new(root_dir()) - .follow_links(false) - .into_iter() - .filter_map(|e| e.ok()) - { - let f_name = entry.file_name().to_string_lossy(); - - if f_name.ends_with(".rs") { - if !is_primary_source(entry.path()) { - continue; - } - let mut args = Vec::new(); - args.push("--skip-children"); - args.push("--unstable-features"); - args.push("--edition=2018"); - args.push(&*entry.path().to_str().unwrap()); - let command = Cmd::new("rustfmt"); - command.args(args).run()?; - } - } - - Ok(()) -} - -fn task_cargo_fmt_check() -> Result<(), anyhow::Error> { - let mut actual_result = Ok(()); - for entry in WalkDir::new(root_dir()) - .follow_links(false) - .into_iter() - .filter_map(|e| e.ok()) - { - let f_name = entry.file_name().to_string_lossy(); - - if f_name.ends_with(".rs") { - if !is_primary_source(entry.path()) { - continue; - } - let mut args = Vec::new(); - args.push("--check"); - args.push("--skip-children"); - args.push("--unstable-features"); - args.push("--edition=2018"); - args.push(&*entry.path().to_str().unwrap()); - let command = Cmd::new("rustfmt"); - if let Err(result) = command.args(args).run() { - actual_result = Err(result.into()); - } - } - } - - actual_result -} - -fn root_dir() -> PathBuf { - let mut xtask_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - xtask_dir.pop(); - xtask_dir -} - -fn examples_dir() -> PathBuf { - root_dir().join("examples") -} - -fn is_primary_source(path: &Path) -> bool { - let mut current = path; - - loop { - let current_file_name = current.file_name().unwrap().to_str().unwrap(); - if current_file_name == "target" - || current_file_name == "stm32-metapac-gen" - || current_file_name == "stm32-data" - { - return false; - } - - if let Some(path) = current.parent() { - current = path.into(); - if current == root_dir() { - return true; - } - } else { - return false; - } - } -} - -fn banner(text: &str) { - println!("------------------------------------------------------------------------------------------------------------------------------------------------"); - println!("== {}", text); - println!("------------------------------------------------------------------------------------------------------------------------------------------------"); -} From 7a4db1da2641c785f5fd9d2365df2a213f3aaade Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Mon, 20 Mar 2023 16:34:30 +0200 Subject: [PATCH 0696/1575] fix(rp): spi transfer Signed-off-by: Lachezar Lechev --- embassy-rp/src/dma.rs | 1 + embassy-rp/src/spi.rs | 46 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 05adcecdd..ba07a88df 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,3 +1,4 @@ +//! Direct Memory Access (DMA) use core::future::Future; use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 584370d56..c48e33fce 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -1,3 +1,4 @@ +//! Serial Peripheral Interface use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; @@ -385,19 +386,36 @@ impl<'d, T: Instance> Spi<'d, T, Async> { async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> { let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); - assert_eq!(from_len, to_len); + unsafe { self.inner.regs().dmacr().write(|reg| { reg.set_rxdmae(true); reg.set_txdmae(true); }) }; - let tx_ch = self.tx_dma.as_mut().unwrap(); - let tx_transfer = unsafe { - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + + let mut tx_ch = self.tx_dma.as_mut().unwrap(); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + let tx_transfer = async { + let p = self.inner.regs(); + unsafe { + crate::dma::write(&mut tx_ch, tx_ptr, p.dr().ptr() as *mut _, T::TX_DREQ).await; + + if from_len > to_len { + let write_bytes_len = from_len - to_len; + // disable incrementation of buffer + tx_ch.regs().ctrl_trig().modify(|ctrl_trig| { + ctrl_trig.set_incr_write(false); + ctrl_trig.set_incr_read(false); + }); + + // write dummy data + crate::dma::write_repeated(tx_ch, p.dr().ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await + } + } }; + let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer @@ -405,6 +423,22 @@ impl<'d, T: Instance> Spi<'d, T, Async> { crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) }; join(tx_transfer, rx_transfer).await; + + // if tx > rx we should clear any overflow of the FIFO SPI buffer + if from_len > to_len { + let p = self.inner.regs(); + unsafe { + while p.sr().read().bsy() {} + + // clear RX FIFO contents to prevent stale reads + while p.sr().read().rne() { + let _: u16 = p.dr().read().data(); + } + // clear RX overrun interrupt + p.icr().write(|w| w.set_roric(true)); + } + } + Ok(()) } } From 41d558a5f40bbea865f2ba0899b34baed9c1c0d1 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Mon, 20 Mar 2023 16:20:51 -0500 Subject: [PATCH 0697/1575] executor: Allow TaskStorage to auto-implement `Sync` --- embassy-executor/src/raw/mod.rs | 156 +++++++++++++++++------- embassy-executor/src/raw/timer_queue.rs | 14 ++- embassy-executor/src/raw/util.rs | 29 +++++ embassy-executor/src/spawner.rs | 12 +- 4 files changed, 154 insertions(+), 57 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 42bd82262..938492c21 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -13,8 +13,8 @@ mod timer_queue; pub(crate) mod util; mod waker; -use core::cell::Cell; use core::future::Future; +use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; @@ -30,7 +30,7 @@ use embassy_time::Instant; use rtos_trace::trace; use self::run_queue::{RunQueue, RunQueueItem}; -use self::util::UninitCell; +use self::util::{SyncUnsafeCell, UninitCell}; pub use self::waker::task_from_waker; use super::SpawnToken; @@ -46,11 +46,11 @@ pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct TaskHeader { pub(crate) state: AtomicU32, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: Cell>, - poll_fn: Cell>, + pub(crate) executor: SyncUnsafeCell>, + poll_fn: SyncUnsafeCell>, #[cfg(feature = "integrated-timers")] - pub(crate) expires_at: Cell, + pub(crate) expires_at: SyncUnsafeCell, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -61,6 +61,9 @@ pub struct TaskRef { ptr: NonNull, } +unsafe impl Send for TaskRef where &'static TaskHeader: Send {} +unsafe impl Sync for TaskRef where &'static TaskHeader: Sync {} + impl TaskRef { fn new(task: &'static TaskStorage) -> Self { Self { @@ -115,12 +118,12 @@ impl TaskStorage { raw: TaskHeader { state: AtomicU32::new(0), run_queue_item: RunQueueItem::new(), - executor: Cell::new(None), + executor: SyncUnsafeCell::new(None), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` - poll_fn: Cell::new(None), + poll_fn: SyncUnsafeCell::new(None), #[cfg(feature = "integrated-timers")] - expires_at: Cell::new(Instant::from_ticks(0)), + expires_at: SyncUnsafeCell::new(Instant::from_ticks(0)), #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, @@ -170,9 +173,15 @@ impl TaskStorage { // it's a noop for our waker. mem::forget(waker); } -} -unsafe impl Sync for TaskStorage {} + #[doc(hidden)] + #[allow(dead_code)] + fn _assert_sync(self) { + fn assert_sync(_: T) {} + + assert_sync(self) + } +} struct AvailableTask { task: &'static TaskStorage, @@ -279,29 +288,13 @@ impl TaskPool { } } -/// Raw executor. -/// -/// This is the core of the Embassy executor. It is low-level, requiring manual -/// handling of wakeups and task polling. If you can, prefer using one of the -/// [higher level executors](crate::Executor). -/// -/// The raw executor leaves it up to you to handle wakeups and scheduling: -/// -/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks -/// that "want to run"). -/// - You must supply a `signal_fn`. The executor will call it to notify you it has work -/// to do. You must arrange for `poll()` to be called as soon as possible. -/// -/// `signal_fn` can be called from *any* context: any thread, any interrupt priority -/// level, etc. It may be called synchronously from any `Executor` method call as well. -/// You must deal with this correctly. -/// -/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates -/// the requirement for `poll` to not be called reentrantly. -pub struct Executor { +struct SignalCtx(*mut ()); +unsafe impl Sync for SignalCtx {} + +pub(crate) struct SyncExecutor { run_queue: RunQueue, signal_fn: fn(*mut ()), - signal_ctx: *mut (), + signal_ctx: SignalCtx, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue: timer_queue::TimerQueue, @@ -309,14 +302,8 @@ pub struct Executor { alarm: AlarmHandle, } -impl Executor { - /// Create a new executor. - /// - /// When the executor has work to do, it will call `signal_fn` with - /// `signal_ctx` as argument. - /// - /// See [`Executor`] docs for details on `signal_fn`. - pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { +impl SyncExecutor { + pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { #[cfg(feature = "integrated-timers")] let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; #[cfg(feature = "integrated-timers")] @@ -325,7 +312,7 @@ impl Executor { Self { run_queue: RunQueue::new(), signal_fn, - signal_ctx, + signal_ctx: SignalCtx(signal_ctx), #[cfg(feature = "integrated-timers")] timer_queue: timer_queue::TimerQueue::new(), @@ -346,7 +333,7 @@ impl Executor { trace::task_ready_begin(task.as_ptr() as u32); if self.run_queue.enqueue(cs, task) { - (self.signal_fn)(self.signal_ctx) + (self.signal_fn)(self.signal_ctx.0) } } @@ -387,7 +374,8 @@ impl Executor { /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. - pub unsafe fn poll(&'static self) { + pub(crate) unsafe fn poll(&'static self) { + #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); @@ -441,6 +429,84 @@ impl Executor { #[cfg(feature = "rtos-trace")] trace::system_idle(); } +} + +/// Raw executor. +/// +/// This is the core of the Embassy executor. It is low-level, requiring manual +/// handling of wakeups and task polling. If you can, prefer using one of the +/// [higher level executors](crate::Executor). +/// +/// The raw executor leaves it up to you to handle wakeups and scheduling: +/// +/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks +/// that "want to run"). +/// - You must supply a `signal_fn`. The executor will call it to notify you it has work +/// to do. You must arrange for `poll()` to be called as soon as possible. +/// +/// `signal_fn` can be called from *any* context: any thread, any interrupt priority +/// level, etc. It may be called synchronously from any `Executor` method call as well. +/// You must deal with this correctly. +/// +/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates +/// the requirement for `poll` to not be called reentrantly. +#[repr(transparent)] +pub struct Executor { + pub(crate) inner: SyncExecutor, + + _not_sync: PhantomData<*mut ()>, +} + +impl Executor { + pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { + mem::transmute(inner) + } + /// Create a new executor. + /// + /// When the executor has work to do, it will call `signal_fn` with + /// `signal_ctx` as argument. + /// + /// See [`Executor`] docs for details on `signal_fn`. + pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { + Self { + inner: SyncExecutor::new(signal_fn, signal_ctx), + _not_sync: PhantomData, + } + } + + /// Spawn a task in this executor. + /// + /// # Safety + /// + /// `task` must be a valid pointer to an initialized but not-already-spawned task. + /// + /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. + /// In this case, the task's Future must be Send. This is because this is effectively + /// sending the task to the executor thread. + pub(super) unsafe fn spawn(&'static self, task: TaskRef) { + self.inner.spawn(task) + } + + /// Poll all queued tasks in this executor. + /// + /// This loops over all tasks that are queued to be polled (i.e. they're + /// freshly spawned or they've been woken). Other tasks are not polled. + /// + /// You must call `poll` after receiving a call to `signal_fn`. It is OK + /// to call `poll` even when not requested by `signal_fn`, but it wastes + /// energy. + /// + /// # Safety + /// + /// You must NOT call `poll` reentrantly on the same executor. + /// + /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you + /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to + /// somehow schedule for `poll()` to be called later, at a time you know for sure there's + /// no `poll()` already running. + pub unsafe fn poll(&'static self) { + self.inner.poll() + } /// Get a spawner that spawns tasks in this executor. /// @@ -483,8 +549,10 @@ impl embassy_time::queue::TimerQueue for TimerQueue { fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) { let task = waker::task_from_waker(waker); let task = task.header(); - let expires_at = task.expires_at.get(); - task.expires_at.set(expires_at.min(at)); + unsafe { + let expires_at = task.expires_at.get(); + task.expires_at.set(expires_at.min(at)); + } } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 57d6d3cda..dc71c95b1 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,28 +1,32 @@ -use core::cell::Cell; use core::cmp::min; use atomic_polyfill::Ordering; use embassy_time::Instant; use super::{TaskRef, STATE_TIMER_QUEUED}; +use crate::raw::util::SyncUnsafeCell; pub(crate) struct TimerQueueItem { - next: Cell>, + next: SyncUnsafeCell>, } impl TimerQueueItem { pub const fn new() -> Self { - Self { next: Cell::new(None) } + Self { + next: SyncUnsafeCell::new(None), + } } } pub(crate) struct TimerQueue { - head: Cell>, + head: SyncUnsafeCell>, } impl TimerQueue { pub const fn new() -> Self { - Self { head: Cell::new(None) } + Self { + head: SyncUnsafeCell::new(None), + } } pub(crate) unsafe fn update(&self, p: TaskRef) { diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index 2b1f6b6f3..e2e8f4df8 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -25,3 +25,32 @@ impl UninitCell { ptr::drop_in_place(self.as_mut_ptr()) } } + +unsafe impl Sync for UninitCell {} + +#[repr(transparent)] +pub struct SyncUnsafeCell { + value: UnsafeCell, +} + +unsafe impl Sync for SyncUnsafeCell {} + +impl SyncUnsafeCell { + #[inline] + pub const fn new(value: T) -> Self { + Self { + value: UnsafeCell::new(value), + } + } + + pub unsafe fn set(&self, value: T) { + *self.value.get() = value; + } + + pub unsafe fn get(&self) -> T + where + T: Copy, + { + *self.value.get() + } +} diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 7c0a0183c..2b6224045 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -92,6 +92,7 @@ impl Spawner { poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; + let executor = unsafe { raw::Executor::wrap(executor) }; Poll::Ready(Self::new(executor)) }) .await @@ -130,9 +131,7 @@ impl Spawner { /// spawner to other threads, but the spawner loses the ability to spawn /// non-Send tasks. pub fn make_send(&self) -> SendSpawner { - SendSpawner { - executor: self.executor, - } + SendSpawner::new(&self.executor.inner) } } @@ -145,14 +144,11 @@ impl Spawner { /// If you want to spawn non-Send tasks, use [Spawner]. #[derive(Copy, Clone)] pub struct SendSpawner { - executor: &'static raw::Executor, + executor: &'static raw::SyncExecutor, } -unsafe impl Send for SendSpawner {} -unsafe impl Sync for SendSpawner {} - impl SendSpawner { - pub(crate) fn new(executor: &'static raw::Executor) -> Self { + pub(crate) fn new(executor: &'static raw::SyncExecutor) -> Self { Self { executor } } From ce7bd6955f85baf53b3e1091974be70c6cf88b49 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 21 Mar 2023 13:25:49 +0100 Subject: [PATCH 0698/1575] perf(pubsub): Skip clone on last message --- embassy-sync/src/pubsub/mod.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 5989e86ec..59e701c58 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -322,12 +322,15 @@ impl PubSubSta // We're reading this item, so decrement the counter queue_item.1 -= 1; - let message = queue_item.0.clone(); - if current_message_index == 0 && queue_item.1 == 0 { - self.queue.pop_front(); + let message = if current_message_index == 0 && queue_item.1 == 0 { + let (message, _) = self.queue.pop_front().unwrap(); self.publisher_wakers.wake(); - } + // Return pop'd message without clone + message + } else { + queue_item.0.clone() + }; Some(WaitResult::Message(message)) } @@ -659,4 +662,25 @@ mod tests { assert_eq!(4, channel.space()); } + + struct CloneCallCounter(usize); + + impl Clone for CloneCallCounter { + fn clone(&self) -> Self { + Self(self.0 + 1) + } + } + + #[futures_test::test] + async fn skip_clone_for_last_message() { + let channel = PubSubChannel::::new(); + let pub0 = channel.publisher().unwrap(); + let mut sub0 = channel.subscriber().unwrap(); + let mut sub1 = channel.subscriber().unwrap(); + + pub0.publish(CloneCallCounter(0)).await; + + assert_eq!(1, sub0.try_next_message_pure().unwrap().0); + assert_eq!(0, sub1.try_next_message_pure().unwrap().0); + } } From b4b8d829801e149c90f9f0fc85736be3549dff87 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 21 Mar 2023 19:15:54 +0100 Subject: [PATCH 0699/1575] remove use of embedded-hal SPI traits. Instead just call our bus trait directly and push responsibility for implementing CS on the trait implementor --- Cargo.toml | 1 - examples/rpi-pico-w/Cargo.toml | 2 - examples/rpi-pico-w/src/main.rs | 62 ++++++++----------- examples/rpi-pico-w/src/pio.rs | 61 +++++-------------- src/bus.rs | 104 +++++--------------------------- src/lib.rs | 7 +-- 6 files changed, 59 insertions(+), 178 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6e3237448..3bdeb0cfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,5 +25,4 @@ cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } -embedded-hal-async = { version = "0.2.0-alpha.0" } num_enum = { version = "0.5.7", default-features = false } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 0d789a932..17b4214d8 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -47,8 +47,6 @@ futures = { version = "0.3.17", default-features = false, features = [ pio-proc = "0.2" pio = "0.2.1" -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } -embedded-hal-async = { version = "0.2.0-alpha.0" } embedded-io = { version = "0.4.0", features = ["async", "defmt"] } heapless = "0.7.15" diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 3563d165a..f30a20bac 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -1,4 +1,4 @@ -#![no_std] +#![no_std] #![no_main] #![feature(type_alias_impl_trait)] #![feature(async_fn_in_trait)] @@ -6,7 +6,7 @@ mod pio; -use core::convert::Infallible; +use core::slice; use core::str::from_utf8; use defmt::*; @@ -16,8 +16,6 @@ use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_24, PIN_25, PIN_29}; use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachineInstance, Sm0}; -use embedded_hal_1::spi::ErrorType; -use embedded_hal_async::spi::{ExclusiveDevice, SpiBusFlush, SpiBusRead, SpiBusWrite}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -37,7 +35,7 @@ async fn wifi_task( runner: cyw43::Runner< 'static, Output<'static, PIN_23>, - ExclusiveDevice, DMA_CH0>, Output<'static, PIN_25>>, + PioSpi, DMA_CH0>, >, ) -> ! { runner.run().await @@ -75,8 +73,7 @@ async fn main(spawner: Spawner) { let (_, sm, _, _, _) = p.PIO0.split(); let dma = p.DMA_CH0; - let bus = PioSpi::new(sm, p.PIN_24, p.PIN_29, dma); - let spi = ExclusiveDevice::new(bus, cs); + let spi = PioSpi::new(sm, cs, p.PIN_24, p.PIN_29, dma); let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; @@ -146,7 +143,6 @@ async fn main(spawner: Spawner) { info!("rxd {}", from_utf8(&buf[..n]).unwrap()); - match socket.write_all(&buf[..n]).await { Ok(()) => {} Err(e) => { @@ -168,31 +164,13 @@ struct MySpi { /// - IRQ /// - strap to set to gSPI mode on boot. dio: Flex<'static, PIN_24>, + + /// Chip select + cs: Output<'static, PIN_25>, } -impl ErrorType for MySpi { - type Error = Infallible; -} - -impl cyw43::SpiBusCyw43 for MySpi { - async fn cmd_write<'a>(&'a mut self, write: &'a [u32]) -> Result<(), Self::Error> { - self.write(write).await - } - - async fn cmd_read<'a>(&'a mut self, write: &'a [u32], read: &'a mut [u32]) -> Result<(), Self::Error> { - self.write(write).await?; - self.read(read).await - } -} - -impl SpiBusFlush for MySpi { - async fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl SpiBusRead for MySpi { - async fn read(&mut self, words: &mut [u32]) -> Result<(), Self::Error> { +impl MySpi { + async fn read(&mut self, words: &mut [u32]) { self.dio.set_as_input(); for word in words { let mut w = 0; @@ -210,13 +188,9 @@ impl SpiBusRead for MySpi { } *word = w } - - Ok(()) } -} -impl SpiBusWrite for MySpi { - async fn write(&mut self, words: &[u32]) -> Result<(), Self::Error> { + async fn write(&mut self, words: &[u32]) { self.dio.set_as_output(); for word in words { let mut word = *word; @@ -238,6 +212,20 @@ impl SpiBusWrite for MySpi { self.clk.set_low(); self.dio.set_as_input(); - Ok(()) + } +} + +impl cyw43::SpiBusCyw43 for MySpi { + async fn cmd_write(&mut self, write: &[u32]) { + self.cs.set_low(); + self.write(write).await; + self.cs.set_high(); + } + + async fn cmd_read(&mut self, write: u32, read: &mut [u32]) { + self.cs.set_low(); + self.write(slice::from_ref(&write)).await; + self.read(read).await; + self.cs.set_high(); } } diff --git a/examples/rpi-pico-w/src/pio.rs b/examples/rpi-pico-w/src/pio.rs index 1bf304d5d..896fd0457 100644 --- a/examples/rpi-pico-w/src/pio.rs +++ b/examples/rpi-pico-w/src/pio.rs @@ -2,34 +2,27 @@ use core::slice; use cyw43::SpiBusCyw43; use embassy_rp::dma::Channel; -use embassy_rp::gpio::{Drive, Pin, Pull, SlewRate}; +use embassy_rp::gpio::{Drive, Output, Pin, Pull, SlewRate}; use embassy_rp::pio::{PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; -use embedded_hal_1::spi::ErrorType; -use embedded_hal_async::spi::SpiBusFlush; use pio::Wrap; use pio_proc::pio_asm; -pub struct PioSpi { - // cs: Output<'static, AnyPin>, +pub struct PioSpi { + cs: Output<'static, CS>, sm: SM, dma: DMA, wrap_target: u8, } -impl PioSpi +impl PioSpi where SM: PioStateMachine, DMA: Channel, + CS: Pin, { - pub fn new( - mut sm: SM, - // cs: AnyPin, - dio: DIO, - clk: CLK, - dma: DMA, - ) -> Self + pub fn new(mut sm: SM, cs: Output<'static, CS>, dio: DIO, clk: CLK, dma: DMA) -> Self where DIO: Pin, CLK: Pin, @@ -105,7 +98,7 @@ where pio_instr_util::set_pin(&mut sm, 0); Self { - // cs: Output::new(cs, Level::High), + cs, sm, dma, wrap_target: target, @@ -156,43 +149,21 @@ where } } -#[derive(Debug)] -pub enum PioError {} - -impl embedded_hal_async::spi::Error for PioError { - fn kind(&self) -> embedded_hal_1::spi::ErrorKind { - embedded_hal_1::spi::ErrorKind::Other - } -} - -impl ErrorType for PioSpi -where - SM: PioStateMachine, -{ - type Error = PioError; -} - -impl SpiBusFlush for PioSpi -where - SM: PioStateMachine, -{ - async fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl SpiBusCyw43 for PioSpi +impl SpiBusCyw43 for PioSpi where + CS: Pin, SM: PioStateMachine, DMA: Channel, { - async fn cmd_write<'a>(&'a mut self, write: &'a [u32]) -> Result<(), Self::Error> { + async fn cmd_write(&mut self, write: & [u32]) { + self.cs.set_low(); self.write(write).await; - Ok(()) + self.cs.set_high(); } - async fn cmd_read<'a>(&'a mut self, write: &'a [u32], read: &'a mut [u32]) -> Result<(), Self::Error> { - self.cmd_read(write[0], read).await; - Ok(()) + async fn cmd_read(&mut self, write: u32, read: & mut [u32]) { + self.cs.set_low(); + self.cmd_read(write, read).await; + self.cs.set_high(); } } diff --git a/src/bus.rs b/src/bus.rs index e4f9a69bb..90990f35a 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -2,22 +2,22 @@ use core::slice; use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; -use embedded_hal_1::spi::ErrorType; -use embedded_hal_async::spi::{transaction, SpiDevice}; use futures::FutureExt; use crate::consts::*; /// Custom Spi Trait that _only_ supports the bus operation of the cyw43 -pub trait SpiBusCyw43: ErrorType { +/// Implementors are expected to hold the CS pin low during an operation. +pub trait SpiBusCyw43 { /// Issues a write command on the bus - /// Frist 32 bits of `word` are expected to be a cmd word - async fn cmd_write<'a>(&'a mut self, write: &'a [Word]) -> Result<(), Self::Error>; + /// First 32 bits of `word` are expected to be a cmd word + async fn cmd_write(&mut self, write: &[u32]); /// Issues a read command on the bus /// `write` is expected to be a 32 bit cmd word /// `read` will contain the response of the device - async fn cmd_read<'a>(&'a mut self, write: &'a [Word], read: &'a mut [Word]) -> Result<(), Self::Error>; + /// + async fn cmd_read(&mut self, write: u32, read: &mut [u32]); } pub(crate) struct Bus { @@ -29,8 +29,7 @@ pub(crate) struct Bus { impl Bus where PWR: OutputPin, - SPI: SpiDevice, - SPI::Bus: SpiBusCyw43, + SPI: SpiBusCyw43, { pub(crate) fn new(pwr: PWR, spi: SPI) -> Self { Self { @@ -87,14 +86,8 @@ where pub async fn wlan_read(&mut self, buf: &mut [u32], len_in_u8: u32) { let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len_in_u8); let len_in_u32 = (len_in_u8 as usize + 3) / 4; - transaction!(&mut self.spi, |bus| async { - // bus.write(&[cmd]).await?; - // bus.read(&mut buf[..len_in_u32]).await?; - bus.cmd_read(slice::from_ref(&cmd), &mut buf[..len_in_u32]).await?; - Ok(()) - }) - .await - .unwrap(); + + self.spi.cmd_read(cmd, &mut buf[..len_in_u32]).await; } pub async fn wlan_write(&mut self, buf: &[u32]) { @@ -104,15 +97,7 @@ where cmd_buf[0] = cmd; cmd_buf[1..][..buf.len()].copy_from_slice(buf); - transaction!(&mut self.spi, |bus| async { - // bus.write(&[cmd]).await?; - // bus.write(buf).await?; - - bus.cmd_write(&cmd_buf).await?; - Ok(()) - }) - .await - .unwrap(); + self.spi.cmd_write(&cmd_buf).await; } #[allow(unused)] @@ -136,22 +121,7 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); - transaction!(&mut self.spi, |bus| async { - // bus.write(&[cmd]).await?; - - // // 4-byte response delay. - // let mut junk = [0; 1]; - // bus.read(&mut junk).await?; - - // // Read data - // bus.read(&mut buf[..(len + 3) / 4]).await?; - - bus.cmd_read(slice::from_ref(&cmd), &mut buf[..(len + 3) / 4 + 1]) - .await?; - Ok(()) - }) - .await - .unwrap(); + self.spi.cmd_read(cmd, &mut buf[..(len + 3) / 4 + 1]).await; data[..len].copy_from_slice(&slice8_mut(&mut buf[1..])[..len]); @@ -183,16 +153,7 @@ where let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); buf[0] = cmd; - transaction!(&mut self.spi, |bus| async { - // bus.write(&[cmd]).await?; - // bus.write(&buf[..(len + 3) / 4]).await?; - - bus.cmd_write(&buf[..(len + 3) / 4 + 1]).await?; - - Ok(()) - }) - .await - .unwrap(); + self.spi.cmd_write(&buf[..(len + 3) / 4 + 1]).await; // Advance ptr. addr += len as u32; @@ -307,19 +268,7 @@ where let mut buf = [0; 2]; let len = if func == FUNC_BACKPLANE { 2 } else { 1 }; - transaction!(&mut self.spi, |bus| async { - // bus.write(&[cmd]).await?; - // if func == FUNC_BACKPLANE { - // // 4-byte response delay. - // bus.read(&mut buf).await?; - // } - // bus.read(&mut buf).await?; - - bus.cmd_read(slice::from_ref(&cmd), &mut buf[..len]).await?; - Ok(()) - }) - .await - .unwrap(); + self.spi.cmd_read(cmd, &mut buf[..len]).await; if func == FUNC_BACKPLANE { buf[1] @@ -331,13 +280,7 @@ where async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); - transaction!(&mut self.spi, |bus| async { - // bus.write(&[cmd, val]).await?; - bus.cmd_write(&[cmd, val]).await?; - Ok(()) - }) - .await - .unwrap(); + self.spi.cmd_write(&[cmd, val]).await; } async fn read32_swapped(&mut self, addr: u32) -> u32 { @@ -345,15 +288,7 @@ where let cmd = swap16(cmd); let mut buf = [0; 1]; - transaction!(&mut self.spi, |bus| async { - // bus.write(&[swap16(cmd)]).await?; - // bus.read(&mut buf).await?; - - bus.cmd_read(slice::from_ref(&cmd), &mut buf).await?; - Ok(()) - }) - .await - .unwrap(); + self.spi.cmd_read(cmd, &mut buf).await; swap16(buf[0]) } @@ -362,14 +297,7 @@ where let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); let buf = [swap16(cmd), swap16(val)]; - transaction!(&mut self.spi, |bus| async { - // bus.write(&[swap16(cmd), swap16(val)]).await?; - - bus.cmd_write(&buf).await?; - Ok(()) - }) - .await - .unwrap(); + self.spi.cmd_write(&buf).await; } } diff --git a/src/lib.rs b/src/lib.rs index bcc3c59bd..f0a7aaa0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,6 @@ use embassy_futures::yield_now; use embassy_net_driver_channel as ch; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; -use embedded_hal_async::spi::SpiDevice; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; @@ -513,8 +512,7 @@ pub async fn new<'a, PWR, SPI>( ) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) where PWR: OutputPin, - SPI: SpiDevice, - SPI::Bus: SpiBusCyw43, + SPI: SpiBusCyw43, { let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); let state_ch = ch_runner.state_runner(); @@ -552,8 +550,7 @@ where impl<'a, PWR, SPI> Runner<'a, PWR, SPI> where PWR: OutputPin, - SPI: SpiDevice, - SPI::Bus: SpiBusCyw43, + SPI: SpiBusCyw43, { async fn init(&mut self, firmware: &[u8]) { self.bus.init().await; From 3034e8fb458cae0ff84d1ca07b4a64bced815f0c Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 21 Mar 2023 19:26:01 +0100 Subject: [PATCH 0700/1575] document response delay quirks in bus code --- src/bus.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/bus.rs b/src/bus.rs index 90990f35a..262b9e0d4 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -16,7 +16,8 @@ pub trait SpiBusCyw43 { /// Issues a read command on the bus /// `write` is expected to be a 32 bit cmd word /// `read` will contain the response of the device - /// + /// Backplane reads have a response delay that produces one extra unspecified word at the beginning of `read`. + /// Callers that want to read `n` word from the backplane, have to provide a slice that is `n+1` words long. async fn cmd_read(&mut self, write: u32, read: &mut [u32]); } @@ -108,6 +109,7 @@ where // To simplify, enforce 4-align for now. assert!(addr % 4 == 0); + // Backplane read buffer has one extra word for the response delay. let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4 + 1]; while !data.is_empty() { @@ -121,8 +123,10 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); + // round `buf` to word boundary, add one extra word for the response delay self.spi.cmd_read(cmd, &mut buf[..(len + 3) / 4 + 1]).await; + // when writing out the data, we skip the response-delay byte data[..len].copy_from_slice(&slice8_mut(&mut buf[1..])[..len]); // Advance ptr. @@ -266,10 +270,12 @@ where async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { let cmd = cmd_word(READ, INC_ADDR, func, addr, len); let mut buf = [0; 2]; + // if we are reading from the backplane, we need an extra word for the response delay let len = if func == FUNC_BACKPLANE { 2 } else { 1 }; self.spi.cmd_read(cmd, &mut buf[..len]).await; + // if we read from the backplane, the result is in the second word, after the response delay if func == FUNC_BACKPLANE { buf[1] } else { From f82f931dc2b8df2338fb8331ad27d667811e5c09 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 21 Mar 2023 19:30:45 +0100 Subject: [PATCH 0701/1575] revert formatting changes in Cargo.toml --- examples/rpi-pico-w/Cargo.toml | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 17b4214d8..4a531c88c 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -6,30 +6,10 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } -embassy-executor = { version = "0.1.0", features = [ - "defmt", - "integrated-timers", -] } -embassy-time = { version = "0.1.0", features = [ - "defmt", - "defmt-timestamp-uptime", -] } -embassy-rp = { version = "0.1.0", features = [ - "defmt", - "unstable-traits", - "nightly", - "unstable-pac", - "pio", - "time-driver", -] } -embassy-net = { version = "0.1.0", features = [ - "defmt", - "tcp", - "dhcpv4", - "medium-ethernet", - "unstable-traits", - "nightly", -] } +embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } +embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } +embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } atomic-polyfill = "0.1.5" static_cell = "1.0" @@ -39,11 +19,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -futures = { version = "0.3.17", default-features = false, features = [ - "async-await", - "cfg-target-has-atomic", - "unstable", -] } +futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } pio-proc = "0.2" pio = "0.2.1" From 359b1c7fdb246c125e0b835eb58283a8a9a6a946 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 21 Mar 2023 19:39:41 +0100 Subject: [PATCH 0702/1575] replace inspect() with direct calls to trace!() after awaiting --- examples/rpi-pico-w/src/pio.rs | 4 ++-- src/bus.rs | 28 ++++++++++------------------ 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/examples/rpi-pico-w/src/pio.rs b/examples/rpi-pico-w/src/pio.rs index 896fd0457..8017f4f44 100644 --- a/examples/rpi-pico-w/src/pio.rs +++ b/examples/rpi-pico-w/src/pio.rs @@ -155,13 +155,13 @@ where SM: PioStateMachine, DMA: Channel, { - async fn cmd_write(&mut self, write: & [u32]) { + async fn cmd_write(&mut self, write: &[u32]) { self.cs.set_low(); self.write(write).await; self.cs.set_high(); } - async fn cmd_read(&mut self, write: u32, read: & mut [u32]) { + async fn cmd_read(&mut self, write: u32, read: &mut [u32]) { self.cs.set_low(); self.cmd_read(write, read).await; self.cs.set_high(); diff --git a/src/bus.rs b/src/bus.rs index 262b9e0d4..f77b890df 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -55,32 +55,24 @@ where {} self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await; - let val = self - .read32_swapped(REG_BUS_TEST_RW) - .inspect(|v| defmt::trace!("{:#x}", v)) - .await; + let val = self.read32_swapped(REG_BUS_TEST_RW).await; + defmt::trace!("{:#x}", val); assert_eq!(val, TEST_PATTERN); - self.read32_swapped(REG_BUS_CTRL) - .inspect(|v| defmt::trace!("{:#010b}", (v & 0xff))) - .await; + let val = self.read32_swapped(REG_BUS_CTRL).await; + defmt::trace!("{:#010b}", (val & 0xff)); // 32-bit word length, little endian (which is the default endianess). self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await; - self.read8(FUNC_BUS, REG_BUS_CTRL) - .inspect(|v| defmt::trace!("{:#b}", v)) - .await; + let val = self.read8(FUNC_BUS, REG_BUS_CTRL).await; + defmt::trace!("{:#b}", val); - let val = self - .read32(FUNC_BUS, REG_BUS_TEST_RO) - .inspect(|v| defmt::trace!("{:#x}", v)) - .await; + let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await; + defmt::trace!("{:#x}", val); assert_eq!(val, FEEDBEAD); - let val = self - .read32(FUNC_BUS, REG_BUS_TEST_RW) - .inspect(|v| defmt::trace!("{:#x}", v)) - .await; + let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await; + defmt::trace!("{:#x}", val); assert_eq!(val, TEST_PATTERN); } From 369f2059627c579c344b1f4d8d34002b466e057d Mon Sep 17 00:00:00 2001 From: kbleeke Date: Wed, 22 Mar 2023 11:33:55 +0100 Subject: [PATCH 0703/1575] wifi task needs to be spawned immediately, otherwise ioctls are just stuck (duh). fix #44 --- examples/rpi-pico-w/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 67348e454..434851378 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -77,6 +77,7 @@ async fn main(spawner: Spawner) { let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; + unwrap!(spawner.spawn(wifi_task(runner))); control.init(clm).await; control @@ -101,7 +102,6 @@ async fn main(spawner: Spawner) { seed )); - unwrap!(spawner.spawn(wifi_task(runner))); unwrap!(spawner.spawn(net_task(stack))); //control.join_open(env!("WIFI_NETWORK")).await; From 20923080e6ea313278b5f3aa8ec21055c6208527 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 2 Mar 2023 12:10:13 +0100 Subject: [PATCH 0704/1575] split lib.rs into multiple files --- src/control.rs | 299 +++++++++++++++++ src/lib.rs | 890 +------------------------------------------------ src/nvram.rs | 54 +++ src/runner.rs | 564 +++++++++++++++++++++++++++++++ 4 files changed, 926 insertions(+), 881 deletions(-) create mode 100644 src/control.rs create mode 100644 src/nvram.rs create mode 100644 src/runner.rs diff --git a/src/control.rs b/src/control.rs new file mode 100644 index 000000000..7f1c9fe86 --- /dev/null +++ b/src/control.rs @@ -0,0 +1,299 @@ +use core::cell::Cell; +use core::cmp::{max, min}; + +use ch::driver::LinkState; +use embassy_futures::yield_now; +use embassy_net_driver_channel as ch; +use embassy_time::{Duration, Timer}; + +pub use crate::bus::SpiBusCyw43; +use crate::consts::*; +use crate::events::{Event, EventQueue}; +use crate::structs::*; +use crate::{countries, IoctlState, IoctlType, PowerManagementMode}; + +pub struct Control<'a> { + state_ch: ch::StateRunner<'a>, + event_sub: &'a EventQueue, + ioctl_state: &'a Cell, +} + +impl<'a> Control<'a> { + pub(crate) fn new( + state_ch: ch::StateRunner<'a>, + event_sub: &'a EventQueue, + ioctl_state: &'a Cell, + ) -> Self { + Self { + state_ch, + event_sub, + ioctl_state, + } + } + + pub async fn init(&mut self, clm: &[u8]) { + const CHUNK_SIZE: usize = 1024; + + info!("Downloading CLM..."); + + let mut offs = 0; + for chunk in clm.chunks(CHUNK_SIZE) { + let mut flag = DOWNLOAD_FLAG_HANDLER_VER; + if offs == 0 { + flag |= DOWNLOAD_FLAG_BEGIN; + } + offs += chunk.len(); + if offs == clm.len() { + flag |= DOWNLOAD_FLAG_END; + } + + let header = DownloadHeader { + flag, + dload_type: DOWNLOAD_TYPE_CLM, + len: chunk.len() as _, + crc: 0, + }; + let mut buf = [0; 8 + 12 + CHUNK_SIZE]; + buf[0..8].copy_from_slice(b"clmload\x00"); + buf[8..20].copy_from_slice(&header.to_bytes()); + buf[20..][..chunk.len()].copy_from_slice(&chunk); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) + .await; + } + + // check clmload ok + assert_eq!(self.get_iovar_u32("clmload_status").await, 0); + + info!("Configuring misc stuff..."); + + // Disable tx gloming which transfers multiple packets in one request. + // 'glom' is short for "conglomerate" which means "gather together into + // a compact mass". + self.set_iovar_u32("bus:txglom", 0).await; + self.set_iovar_u32("apsta", 1).await; + + // read MAC addr. + let mut mac_addr = [0; 6]; + assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + info!("mac addr: {:02x}", mac_addr); + + let country = countries::WORLD_WIDE_XX; + let country_info = CountryInfo { + country_abbrev: [country.code[0], country.code[1], 0, 0], + country_code: [country.code[0], country.code[1], 0, 0], + rev: if country.rev == 0 { -1 } else { country.rev as _ }, + }; + self.set_iovar("country", &country_info.to_bytes()).await; + + // set country takes some time, next ioctls fail if we don't wait. + Timer::after(Duration::from_millis(100)).await; + + // Set antenna to chip antenna + self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; + + self.set_iovar_u32("bus:txglom", 0).await; + Timer::after(Duration::from_millis(100)).await; + //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? + //Timer::after(Duration::from_millis(100)).await; + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + Timer::after(Duration::from_millis(100)).await; + self.set_iovar_u32("ampdu_mpdu", 4).await; + Timer::after(Duration::from_millis(100)).await; + //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes + + //Timer::after(Duration::from_millis(100)).await; + + // evts + let mut evts = EventMask { + iface: 0, + events: [0xFF; 24], + }; + + // Disable spammy uninteresting events. + evts.unset(Event::RADIO); + evts.unset(Event::IF); + evts.unset(Event::PROBREQ_MSG); + evts.unset(Event::PROBREQ_MSG_RX); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::ROAM); + + self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; + + Timer::after(Duration::from_millis(100)).await; + + // set wifi up + self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + + Timer::after(Duration::from_millis(100)).await; + + self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto + self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any + + Timer::after(Duration::from_millis(100)).await; + + self.state_ch.set_ethernet_address(mac_addr); + self.state_ch.set_link_state(LinkState::Up); // TODO do on join/leave + + info!("INIT DONE"); + } + + pub async fn set_power_management(&mut self, mode: PowerManagementMode) { + // power save mode + let mode_num = mode.mode(); + if mode_num == 2 { + self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; + self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; + self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; + self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; + } + self.ioctl_set_u32(86, 0, mode_num).await; + } + + pub async fn join_open(&mut self, ssid: &str) { + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + + self.ioctl_set_u32(134, 0, 0).await; // wsec = open + self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; + self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 + self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) + + let mut i = SsidInfo { + len: ssid.len() as _, + ssid: [0; 32], + }; + i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) + .await; // set_ssid + + info!("JOINED"); + } + + pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) { + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + + self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 + self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; + self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; + self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; + + Timer::after(Duration::from_millis(100)).await; + + let mut pfi = PassphraseInfo { + len: passphrase.len() as _, + flags: 1, + passphrase: [0; 64], + }; + pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) + .await; // WLC_SET_WSEC_PMK + + self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 + self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) + self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth + + let mut i = SsidInfo { + len: ssid.len() as _, + ssid: [0; 32], + }; + i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + + let mut subscriber = self.event_sub.subscriber().unwrap(); + self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; // set_ssid + + loop { + let msg = subscriber.next_message_pure().await; + if msg.event_type == Event::AUTH && msg.status != 0 { + // retry + defmt::warn!("JOIN failed with status={}", msg.status); + self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; + } else if msg.event_type == Event::JOIN && msg.status == 0 { + // successful join + break; + } + } + + info!("JOINED"); + } + + pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { + assert!(gpio_n < 3); + self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) + .await + } + + async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { + let mut buf = [0; 8]; + buf[0..4].copy_from_slice(&val1.to_le_bytes()); + buf[4..8].copy_from_slice(&val2.to_le_bytes()); + self.set_iovar(name, &buf).await + } + + async fn set_iovar_u32(&mut self, name: &str, val: u32) { + self.set_iovar(name, &val.to_le_bytes()).await + } + + async fn get_iovar_u32(&mut self, name: &str) -> u32 { + let mut buf = [0; 4]; + let len = self.get_iovar(name, &mut buf).await; + assert_eq!(len, 4); + u32::from_le_bytes(buf) + } + + async fn set_iovar(&mut self, name: &str, val: &[u8]) { + info!("set {} = {:02x}", name, val); + + let mut buf = [0; 64]; + buf[..name.len()].copy_from_slice(name.as_bytes()); + buf[name.len()] = 0; + buf[name.len() + 1..][..val.len()].copy_from_slice(val); + + let total_len = name.len() + 1 + val.len(); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) + .await; + } + + // TODO this is not really working, it always returns all zeros. + async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { + info!("get {}", name); + + let mut buf = [0; 64]; + buf[..name.len()].copy_from_slice(name.as_bytes()); + buf[name.len()] = 0; + + let total_len = max(name.len() + 1, res.len()); + let res_len = self + .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) + .await; + + let out_len = min(res.len(), res_len); + res[..out_len].copy_from_slice(&buf[..out_len]); + out_len + } + + async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { + let mut buf = val.to_le_bytes(); + self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; + } + + async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { + // TODO cancel ioctl on future drop. + + while !matches!(self.ioctl_state.get(), IoctlState::Idle) { + yield_now().await; + } + + self.ioctl_state.set(IoctlState::Pending { kind, cmd, iface, buf }); + + let resp_len = loop { + if let IoctlState::Done { resp_len } = self.ioctl_state.get() { + break resp_len; + } + yield_now().await; + }; + + self.ioctl_state.set(IoctlState::Idle); + + resp_len + } +} diff --git a/src/lib.rs b/src/lib.rs index 1b7d603d7..af8f74a6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,23 +13,20 @@ mod countries; mod events; mod structs; -use core::cell::Cell; -use core::cmp::{max, min}; -use core::slice; +mod control; +mod nvram; +mod runner; + +use core::cell::Cell; -use ch::driver::LinkState; -use embassy_futures::yield_now; use embassy_net_driver_channel as ch; -use embassy_sync::pubsub::PubSubBehavior; -use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; use events::EventQueue; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; -use crate::consts::*; -use crate::events::{Event, EventStatus}; -use crate::structs::*; +pub use crate::control::Control; +pub use crate::runner::Runner; const MTU: usize = 1514; @@ -143,12 +140,6 @@ impl State { } } -pub struct Control<'a> { - state_ch: ch::StateRunner<'a>, - event_sub: &'a EventQueue, - ioctl_state: &'a Cell, -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PowerManagementMode { /// Custom, officially unsupported mode. Use at your own risk. @@ -233,297 +224,6 @@ impl PowerManagementMode { } } -impl<'a> Control<'a> { - pub async fn init(&mut self, clm: &[u8]) { - const CHUNK_SIZE: usize = 1024; - - info!("Downloading CLM..."); - - let mut offs = 0; - for chunk in clm.chunks(CHUNK_SIZE) { - let mut flag = DOWNLOAD_FLAG_HANDLER_VER; - if offs == 0 { - flag |= DOWNLOAD_FLAG_BEGIN; - } - offs += chunk.len(); - if offs == clm.len() { - flag |= DOWNLOAD_FLAG_END; - } - - let header = DownloadHeader { - flag, - dload_type: DOWNLOAD_TYPE_CLM, - len: chunk.len() as _, - crc: 0, - }; - let mut buf = [0; 8 + 12 + CHUNK_SIZE]; - buf[0..8].copy_from_slice(b"clmload\x00"); - buf[8..20].copy_from_slice(&header.to_bytes()); - buf[20..][..chunk.len()].copy_from_slice(&chunk); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) - .await; - } - - // check clmload ok - assert_eq!(self.get_iovar_u32("clmload_status").await, 0); - - info!("Configuring misc stuff..."); - - // Disable tx gloming which transfers multiple packets in one request. - // 'glom' is short for "conglomerate" which means "gather together into - // a compact mass". - self.set_iovar_u32("bus:txglom", 0).await; - self.set_iovar_u32("apsta", 1).await; - - // read MAC addr. - let mut mac_addr = [0; 6]; - assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); - info!("mac addr: {:02x}", mac_addr); - - let country = countries::WORLD_WIDE_XX; - let country_info = CountryInfo { - country_abbrev: [country.code[0], country.code[1], 0, 0], - country_code: [country.code[0], country.code[1], 0, 0], - rev: if country.rev == 0 { -1 } else { country.rev as _ }, - }; - self.set_iovar("country", &country_info.to_bytes()).await; - - // set country takes some time, next ioctls fail if we don't wait. - Timer::after(Duration::from_millis(100)).await; - - // Set antenna to chip antenna - self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; - - self.set_iovar_u32("bus:txglom", 0).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? - //Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_mpdu", 4).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes - - //Timer::after(Duration::from_millis(100)).await; - - // evts - let mut evts = EventMask { - iface: 0, - events: [0xFF; 24], - }; - - // Disable spammy uninteresting events. - evts.unset(Event::RADIO); - evts.unset(Event::IF); - evts.unset(Event::PROBREQ_MSG); - evts.unset(Event::PROBREQ_MSG_RX); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::ROAM); - - self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; - - Timer::after(Duration::from_millis(100)).await; - - // set wifi up - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; - - Timer::after(Duration::from_millis(100)).await; - - self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto - self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any - - Timer::after(Duration::from_millis(100)).await; - - self.state_ch.set_ethernet_address(mac_addr); - self.state_ch.set_link_state(LinkState::Up); // TODO do on join/leave - - info!("INIT DONE"); - } - - pub async fn set_power_management(&mut self, mode: PowerManagementMode) { - // power save mode - let mode_num = mode.mode(); - if mode_num == 2 { - self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; - self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; - self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; - self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; - } - self.ioctl_set_u32(86, 0, mode_num).await; - } - - pub async fn join_open(&mut self, ssid: &str) { - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - - self.ioctl_set_u32(134, 0, 0).await; // wsec = open - self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; - self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 - self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) - - let mut i = SsidInfo { - len: ssid.len() as _, - ssid: [0; 32], - }; - i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) - .await; // set_ssid - - info!("JOINED"); - } - - pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) { - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - - self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 - self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; - self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; - self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; - - Timer::after(Duration::from_millis(100)).await; - - let mut pfi = PassphraseInfo { - len: passphrase.len() as _, - flags: 1, - passphrase: [0; 64], - }; - pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) - .await; // WLC_SET_WSEC_PMK - - self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 - self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) - self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth - - let mut i = SsidInfo { - len: ssid.len() as _, - ssid: [0; 32], - }; - i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - - let mut subscriber = self.event_sub.subscriber().unwrap(); - self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; // set_ssid - - loop { - let msg = subscriber.next_message_pure().await; - if msg.event_type == Event::AUTH && msg.status != 0 { - // retry - defmt::warn!("JOIN failed with status={}", msg.status); - self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; - } else if msg.event_type == Event::JOIN && msg.status == 0 { - // successful join - break; - } - } - - info!("JOINED"); - } - - pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { - assert!(gpio_n < 3); - self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) - .await - } - - async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { - let mut buf = [0; 8]; - buf[0..4].copy_from_slice(&val1.to_le_bytes()); - buf[4..8].copy_from_slice(&val2.to_le_bytes()); - self.set_iovar(name, &buf).await - } - - async fn set_iovar_u32(&mut self, name: &str, val: u32) { - self.set_iovar(name, &val.to_le_bytes()).await - } - - async fn get_iovar_u32(&mut self, name: &str) -> u32 { - let mut buf = [0; 4]; - let len = self.get_iovar(name, &mut buf).await; - assert_eq!(len, 4); - u32::from_le_bytes(buf) - } - - async fn set_iovar(&mut self, name: &str, val: &[u8]) { - info!("set {} = {:02x}", name, val); - - let mut buf = [0; 64]; - buf[..name.len()].copy_from_slice(name.as_bytes()); - buf[name.len()] = 0; - buf[name.len() + 1..][..val.len()].copy_from_slice(val); - - let total_len = name.len() + 1 + val.len(); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) - .await; - } - - // TODO this is not really working, it always returns all zeros. - async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { - info!("get {}", name); - - let mut buf = [0; 64]; - buf[..name.len()].copy_from_slice(name.as_bytes()); - buf[name.len()] = 0; - - let total_len = max(name.len() + 1, res.len()); - let res_len = self - .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) - .await; - - let out_len = min(res.len(), res_len); - res[..out_len].copy_from_slice(&buf[..out_len]); - out_len - } - - async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { - let mut buf = val.to_le_bytes(); - self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; - } - - async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - // TODO cancel ioctl on future drop. - - while !matches!(self.ioctl_state.get(), IoctlState::Idle) { - yield_now().await; - } - - self.ioctl_state.set(IoctlState::Pending { kind, cmd, iface, buf }); - - let resp_len = loop { - if let IoctlState::Done { resp_len } = self.ioctl_state.get() { - break resp_len; - } - yield_now().await; - }; - - self.ioctl_state.set(IoctlState::Idle); - - resp_len - } -} - -pub struct Runner<'a, PWR, SPI> { - ch: ch::Runner<'a, MTU>, - bus: Bus, - - ioctl_state: &'a Cell, - ioctl_id: u16, - sdpcm_seq: u8, - sdpcm_seq_max: u8, - - events: &'a EventQueue, - - #[cfg(feature = "firmware-logs")] - log: LogState, -} - -#[cfg(feature = "firmware-logs")] -struct LogState { - addr: u32, - last_idx: usize, - buf: [u8; 256], - buf_count: usize, -} - pub type NetDriver<'a> = ch::Device<'a, MTU>; pub async fn new<'a, PWR, SPI>( @@ -539,585 +239,13 @@ where let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); let state_ch = ch_runner.state_runner(); - let mut runner = Runner { - ch: ch_runner, - bus: Bus::new(pwr, spi), - - ioctl_state: &state.ioctl_state, - ioctl_id: 0, - sdpcm_seq: 0, - sdpcm_seq_max: 1, - - events: &state.events, - - #[cfg(feature = "firmware-logs")] - log: LogState { - addr: 0, - last_idx: 0, - buf: [0; 256], - buf_count: 0, - }, - }; + let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); runner.init(firmware).await; ( device, - Control { - state_ch, - event_sub: &&state.events, - ioctl_state: &state.ioctl_state, - }, + Control::new(state_ch, &state.events, &state.ioctl_state), runner, ) } - -impl<'a, PWR, SPI> Runner<'a, PWR, SPI> -where - PWR: OutputPin, - SPI: SpiBusCyw43, -{ - async fn init(&mut self, firmware: &[u8]) { - self.bus.init().await; - - // Init ALP (Active Low Power) clock - self.bus - .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) - .await; - info!("waiting for clock..."); - while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} - info!("clock ok"); - - let chip_id = self.bus.bp_read16(0x1800_0000).await; - info!("chip ID: {}", chip_id); - - // Upload firmware. - self.core_disable(Core::WLAN).await; - self.core_reset(Core::SOCSRAM).await; - self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; - self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; - - let ram_addr = CHIP.atcm_ram_base_address; - - info!("loading fw"); - self.bus.bp_write(ram_addr, firmware).await; - - info!("loading nvram"); - // Round up to 4 bytes. - let nvram_len = (NVRAM.len() + 3) / 4 * 4; - self.bus - .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) - .await; - - let nvram_len_words = nvram_len as u32 / 4; - let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; - self.bus - .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) - .await; - - // Start core! - info!("starting up core..."); - self.core_reset(Core::WLAN).await; - assert!(self.core_is_up(Core::WLAN).await); - - while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} - - // "Set up the interrupt mask and enable interrupts" - self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; - - // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." - // Sounds scary... - self.bus - .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32) - .await; - - // wait for wifi startup - info!("waiting for wifi init..."); - while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} - - // Some random configs related to sleep. - // These aren't needed if we don't want to sleep the bus. - // TODO do we need to sleep the bus to read the irq line, due to - // being on the same pin as MOSI/MISO? - - /* - let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; - val |= 0x02; // WAKE_TILL_HT_AVAIL - self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; - self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 - self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT - - let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; - val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON - self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; - */ - - // clear pulls - self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; - let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; - - // start HT clock - //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; - //info!("waiting for HT clock..."); - //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} - //info!("clock ok"); - - #[cfg(feature = "firmware-logs")] - self.log_init().await; - - info!("init done "); - } - - #[cfg(feature = "firmware-logs")] - async fn log_init(&mut self) { - // Initialize shared memory for logging. - - let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; - let shared_addr = self.bus.bp_read32(addr).await; - info!("shared_addr {:08x}", shared_addr); - - let mut shared = [0; SharedMemData::SIZE]; - self.bus.bp_read(shared_addr, &mut shared).await; - let shared = SharedMemData::from_bytes(&shared); - info!("shared: {:08x}", shared); - - self.log.addr = shared.console_addr + 8; - } - - #[cfg(feature = "firmware-logs")] - async fn log_read(&mut self) { - // Read log struct - let mut log = [0; SharedMemLog::SIZE]; - self.bus.bp_read(self.log.addr, &mut log).await; - let log = SharedMemLog::from_bytes(&log); - - let idx = log.idx as usize; - - // If pointer hasn't moved, no need to do anything. - if idx == self.log.last_idx { - return; - } - - // Read entire buf for now. We could read only what we need, but then we - // run into annoying alignment issues in `bp_read`. - let mut buf = [0; 0x400]; - self.bus.bp_read(log.buf, &mut buf).await; - - while self.log.last_idx != idx as usize { - let b = buf[self.log.last_idx]; - if b == b'\r' || b == b'\n' { - if self.log.buf_count != 0 { - let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; - debug!("LOGS: {}", s); - self.log.buf_count = 0; - } - } else if self.log.buf_count < self.log.buf.len() { - self.log.buf[self.log.buf_count] = b; - self.log.buf_count += 1; - } - - self.log.last_idx += 1; - if self.log.last_idx == 0x400 { - self.log.last_idx = 0; - } - } - } - - pub async fn run(mut self) -> ! { - let mut buf = [0; 512]; - loop { - #[cfg(feature = "firmware-logs")] - self.log_read().await; - - // Send stuff - // TODO flow control not yet complete - if !self.has_credit() { - warn!("TX stalled"); - } else { - if let IoctlState::Pending { kind, cmd, iface, buf } = self.ioctl_state.get() { - self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; - self.ioctl_state.set(IoctlState::Sent { buf }); - } - if !self.has_credit() { - warn!("TX stalled"); - } else { - if let Some(packet) = self.ch.try_tx_buf() { - trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); - - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); - - let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); - - let seq = self.sdpcm_seq; - self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); - - let sdpcm_header = SdpcmHeader { - len: total_len as u16, // TODO does this len need to be rounded up to u32? - len_inv: !total_len as u16, - sequence: seq, - channel_and_flags: CHANNEL_TYPE_DATA, - next_length: 0, - header_length: SdpcmHeader::SIZE as _, - wireless_flow_control: 0, - bus_data_credit: 0, - reserved: [0, 0], - }; - - let bcd_header = BcdHeader { - flags: BDC_VERSION << BDC_VERSION_SHIFT, - priority: 0, - flags2: 0, - data_offset: 0, - }; - trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", bcd_header); - - buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); - buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); - - let total_len = (total_len + 3) & !3; // round up to 4byte - - trace!(" {:02x}", &buf8[..total_len.min(48)]); - - self.bus.wlan_write(&buf[..(total_len / 4)]).await; - self.ch.tx_done(); - } - } - } - - // Receive stuff - let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; - - if irq & IRQ_F2_PACKET_AVAILABLE != 0 { - let mut status = 0xFFFF_FFFF; - while status == 0xFFFF_FFFF { - status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; - } - - if status & STATUS_F2_PKT_AVAILABLE != 0 { - let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(&mut buf, len).await; - trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); - self.rx(&slice8_mut(&mut buf)[..len as usize]); - } - } - - // TODO use IRQs - yield_now().await; - } - } - - fn rx(&mut self, packet: &[u8]) { - if packet.len() < SdpcmHeader::SIZE { - warn!("packet too short, len={}", packet.len()); - return; - } - - let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); - trace!("rx {:?}", sdpcm_header); - if sdpcm_header.len != !sdpcm_header.len_inv { - warn!("len inv mismatch"); - return; - } - if sdpcm_header.len as usize != packet.len() { - // TODO: is this guaranteed?? - warn!("len from header doesn't match len from spi"); - return; - } - - self.update_credit(&sdpcm_header); - - let channel = sdpcm_header.channel_and_flags & 0x0f; - - let payload = &packet[sdpcm_header.header_length as _..]; - - match channel { - CHANNEL_TYPE_CONTROL => { - if payload.len() < CdcHeader::SIZE { - warn!("payload too short, len={}", payload.len()); - return; - } - - let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); - trace!(" {:?}", cdc_header); - - if let IoctlState::Sent { buf } = self.ioctl_state.get() { - if cdc_header.id == self.ioctl_id { - if cdc_header.status != 0 { - // TODO: propagate error instead - panic!("IOCTL error {=i32}", cdc_header.status as i32); - } - - let resp_len = cdc_header.len as usize; - info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); - - (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); - self.ioctl_state.set(IoctlState::Done { resp_len }); - } - } - } - CHANNEL_TYPE_EVENT => { - let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); - trace!(" {:?}", bcd_header); - - let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; - - if packet_start + EventPacket::SIZE > payload.len() { - warn!("BCD event, incomplete header"); - return; - } - let bcd_packet = &payload[packet_start..]; - trace!(" {:02x}", &bcd_packet[..(bcd_packet.len() as usize).min(36)]); - - let mut event_packet = EventPacket::from_bytes(&bcd_packet[..EventPacket::SIZE].try_into().unwrap()); - event_packet.byteswap(); - - const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h - if event_packet.eth.ether_type != ETH_P_LINK_CTL { - warn!( - "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", - event_packet.eth.ether_type, ETH_P_LINK_CTL - ); - return; - } - const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; - if event_packet.hdr.oui != BROADCOM_OUI { - warn!( - "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", - event_packet.hdr.oui, BROADCOM_OUI - ); - return; - } - const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; - if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { - warn!("unexpected subtype {}", event_packet.hdr.subtype); - return; - } - - const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; - if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { - warn!("unexpected user_subtype {}", event_packet.hdr.subtype); - return; - } - - if event_packet.msg.datalen as usize >= (bcd_packet.len() - EventMessage::SIZE) { - warn!("BCD event, incomplete data"); - return; - } - - let evt_type = events::Event::from(event_packet.msg.event_type as u8); - let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; - debug!("=== EVENT {}: {} {:02x}", evt_type, event_packet.msg, evt_data); - - if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { - self.events.publish_immediate(EventStatus { - status: event_packet.msg.status, - event_type: evt_type, - }); - } - } - CHANNEL_TYPE_DATA => { - let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); - trace!(" {:?}", bcd_header); - - let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; - if packet_start > payload.len() { - warn!("packet start out of range."); - return; - } - let packet = &payload[packet_start..]; - trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); - - match self.ch.try_rx_buf() { - Some(buf) => { - buf[..packet.len()].copy_from_slice(packet); - self.ch.rx_done(packet.len()) - } - None => warn!("failed to push rxd packet to the channel."), - } - } - _ => {} - } - } - - fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { - if sdpcm_header.channel_and_flags & 0xf < 3 { - let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; - if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { - sdpcm_seq_max = self.sdpcm_seq + 2; - } - self.sdpcm_seq_max = sdpcm_seq_max; - } - } - - fn has_credit(&self) -> bool { - self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 - } - - async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); - - let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); - - let sdpcm_seq = self.sdpcm_seq; - self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); - self.ioctl_id = self.ioctl_id.wrapping_add(1); - - let sdpcm_header = SdpcmHeader { - len: total_len as u16, // TODO does this len need to be rounded up to u32? - len_inv: !total_len as u16, - sequence: sdpcm_seq, - channel_and_flags: CHANNEL_TYPE_CONTROL, - next_length: 0, - header_length: SdpcmHeader::SIZE as _, - wireless_flow_control: 0, - bus_data_credit: 0, - reserved: [0, 0], - }; - - let cdc_header = CdcHeader { - cmd: cmd, - len: data.len() as _, - flags: kind as u16 | (iface as u16) << 12, - id: self.ioctl_id, - status: 0, - }; - trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", cdc_header); - - buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); - buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); - - let total_len = (total_len + 3) & !3; // round up to 4byte - - trace!(" {:02x}", &buf8[..total_len.min(48)]); - - self.bus.wlan_write(&buf[..total_len / 4]).await; - } - - async fn core_disable(&mut self, core: Core) { - let base = core.base_addr(); - - // Dummy read? - let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - - // Check it isn't already reset - let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - if r & AI_RESETCTRL_BIT_RESET != 0 { - return; - } - - self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; - let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - - block_for(Duration::from_millis(1)); - - self.bus - .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) - .await; - let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - } - - async fn core_reset(&mut self, core: Core) { - self.core_disable(core).await; - - let base = core.base_addr(); - self.bus - .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) - .await; - let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - - self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; - - Timer::after(Duration::from_millis(1)).await; - - self.bus - .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) - .await; - let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - - Timer::after(Duration::from_millis(1)).await; - } - - async fn core_is_up(&mut self, core: Core) -> bool { - let base = core.base_addr(); - - let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { - debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); - return false; - } - - let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - if r & (AI_RESETCTRL_BIT_RESET) != 0 { - debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); - return false; - } - - true - } -} - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } -} - -macro_rules! nvram { - ($($s:literal,)*) => { - concat_bytes!($($s, b"\x00",)* b"\x00\x00") - }; -} - -static NVRAM: &'static [u8] = &*nvram!( - b"NVRAMRev=$Rev$", - b"manfid=0x2d0", - b"prodid=0x0727", - b"vendid=0x14e4", - b"devid=0x43e2", - b"boardtype=0x0887", - b"boardrev=0x1100", - b"boardnum=22", - b"macaddr=00:A0:50:b5:59:5e", - b"sromrev=11", - b"boardflags=0x00404001", - b"boardflags3=0x04000000", - b"xtalfreq=37400", - b"nocrc=1", - b"ag0=255", - b"aa2g=1", - b"ccode=ALL", - b"pa0itssit=0x20", - b"extpagain2g=0", - b"pa2ga0=-168,6649,-778", - b"AvVmid_c0=0x0,0xc8", - b"cckpwroffset0=5", - b"maxp2ga0=84", - b"txpwrbckof=6", - b"cckbw202gpo=0", - b"legofdmbw202gpo=0x66111111", - b"mcsbw202gpo=0x77711111", - b"propbw202gpo=0xdd", - b"ofdmdigfilttype=18", - b"ofdmdigfilttypebe=18", - b"papdmode=1", - b"papdvalidtest=1", - b"pacalidx2g=45", - b"papdepsoffset=-30", - b"papdendidx=58", - b"ltecxmux=0", - b"ltecxpadnum=0x0102", - b"ltecxfnsel=0x44", - b"ltecxgcigpio=0x01", - b"il0macaddr=00:90:4c:c5:12:38", - b"wl0id=0x431b", - b"deadman_to=0xffffffff", - b"muxenab=0x100", - b"spurconfig=0x3", - b"glitch_based_crsmin=1", - b"btc_mode=1", -); diff --git a/src/nvram.rs b/src/nvram.rs new file mode 100644 index 000000000..964a3128d --- /dev/null +++ b/src/nvram.rs @@ -0,0 +1,54 @@ +macro_rules! nvram { + ($($s:literal,)*) => { + concat_bytes!($($s, b"\x00",)* b"\x00\x00") + }; +} + +pub static NVRAM: &'static [u8] = &*nvram!( + b"NVRAMRev=$Rev$", + b"manfid=0x2d0", + b"prodid=0x0727", + b"vendid=0x14e4", + b"devid=0x43e2", + b"boardtype=0x0887", + b"boardrev=0x1100", + b"boardnum=22", + b"macaddr=00:A0:50:b5:59:5e", + b"sromrev=11", + b"boardflags=0x00404001", + b"boardflags3=0x04000000", + b"xtalfreq=37400", + b"nocrc=1", + b"ag0=255", + b"aa2g=1", + b"ccode=ALL", + b"pa0itssit=0x20", + b"extpagain2g=0", + b"pa2ga0=-168,6649,-778", + b"AvVmid_c0=0x0,0xc8", + b"cckpwroffset0=5", + b"maxp2ga0=84", + b"txpwrbckof=6", + b"cckbw202gpo=0", + b"legofdmbw202gpo=0x66111111", + b"mcsbw202gpo=0x77711111", + b"propbw202gpo=0xdd", + b"ofdmdigfilttype=18", + b"ofdmdigfilttypebe=18", + b"papdmode=1", + b"papdvalidtest=1", + b"pacalidx2g=45", + b"papdepsoffset=-30", + b"papdendidx=58", + b"ltecxmux=0", + b"ltecxpadnum=0x0102", + b"ltecxfnsel=0x44", + b"ltecxgcigpio=0x01", + b"il0macaddr=00:90:4c:c5:12:38", + b"wl0id=0x431b", + b"deadman_to=0xffffffff", + b"muxenab=0x100", + b"spurconfig=0x3", + b"glitch_based_crsmin=1", + b"btc_mode=1", +); diff --git a/src/runner.rs b/src/runner.rs new file mode 100644 index 000000000..5d840bc59 --- /dev/null +++ b/src/runner.rs @@ -0,0 +1,564 @@ +use core::cell::Cell; +use core::slice; + +use embassy_futures::yield_now; +use embassy_net_driver_channel as ch; +use embassy_sync::pubsub::PubSubBehavior; +use embassy_time::{block_for, Duration, Timer}; +use embedded_hal_1::digital::OutputPin; + +use crate::bus::Bus; +pub use crate::bus::SpiBusCyw43; +use crate::consts::*; +use crate::events::{EventQueue, EventStatus}; +use crate::nvram::NVRAM; +use crate::structs::*; +use crate::{events, Core, IoctlState, IoctlType, CHIP, MTU}; + +#[cfg(feature = "firmware-logs")] +struct LogState { + addr: u32, + last_idx: usize, + buf: [u8; 256], + buf_count: usize, +} + +impl Default for LogState { + fn default() -> Self { + Self { + addr: Default::default(), + last_idx: Default::default(), + buf: [0; 256], + buf_count: Default::default(), + } + } +} + +pub struct Runner<'a, PWR, SPI> { + ch: ch::Runner<'a, MTU>, + bus: Bus, + + ioctl_state: &'a Cell, + ioctl_id: u16, + sdpcm_seq: u8, + sdpcm_seq_max: u8, + + events: &'a EventQueue, + + #[cfg(feature = "firmware-logs")] + log: LogState, +} + +impl<'a, PWR, SPI> Runner<'a, PWR, SPI> +where + PWR: OutputPin, + SPI: SpiBusCyw43, +{ + pub(crate) fn new( + ch: ch::Runner<'a, MTU>, + bus: Bus, + ioctl_state: &'a Cell, + events: &'a EventQueue, + ) -> Self { + Self { + ch, + bus, + ioctl_state, + ioctl_id: 0, + sdpcm_seq: 0, + sdpcm_seq_max: 1, + events, + #[cfg(feature = "firmware-logs")] + log: LogState::default(), + } + } + + pub(crate) async fn init(&mut self, firmware: &[u8]) { + self.bus.init().await; + + // Init ALP (Active Low Power) clock + self.bus + .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) + .await; + info!("waiting for clock..."); + while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} + info!("clock ok"); + + let chip_id = self.bus.bp_read16(0x1800_0000).await; + info!("chip ID: {}", chip_id); + + // Upload firmware. + self.core_disable(Core::WLAN).await; + self.core_reset(Core::SOCSRAM).await; + self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; + self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; + + let ram_addr = CHIP.atcm_ram_base_address; + + info!("loading fw"); + self.bus.bp_write(ram_addr, firmware).await; + + info!("loading nvram"); + // Round up to 4 bytes. + let nvram_len = (NVRAM.len() + 3) / 4 * 4; + self.bus + .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) + .await; + + let nvram_len_words = nvram_len as u32 / 4; + let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; + self.bus + .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) + .await; + + // Start core! + info!("starting up core..."); + self.core_reset(Core::WLAN).await; + assert!(self.core_is_up(Core::WLAN).await); + + while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} + + // "Set up the interrupt mask and enable interrupts" + self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; + + // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." + // Sounds scary... + self.bus + .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32) + .await; + + // wait for wifi startup + info!("waiting for wifi init..."); + while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} + + // Some random configs related to sleep. + // These aren't needed if we don't want to sleep the bus. + // TODO do we need to sleep the bus to read the irq line, due to + // being on the same pin as MOSI/MISO? + + /* + let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; + val |= 0x02; // WAKE_TILL_HT_AVAIL + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; + self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT + + let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; + val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; + */ + + // clear pulls + self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; + let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; + + // start HT clock + //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; + //info!("waiting for HT clock..."); + //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} + //info!("clock ok"); + + #[cfg(feature = "firmware-logs")] + self.log_init().await; + + info!("init done "); + } + + #[cfg(feature = "firmware-logs")] + async fn log_init(&mut self) { + // Initialize shared memory for logging. + + let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; + let shared_addr = self.bus.bp_read32(addr).await; + info!("shared_addr {:08x}", shared_addr); + + let mut shared = [0; SharedMemData::SIZE]; + self.bus.bp_read(shared_addr, &mut shared).await; + let shared = SharedMemData::from_bytes(&shared); + info!("shared: {:08x}", shared); + + self.log.addr = shared.console_addr + 8; + } + + #[cfg(feature = "firmware-logs")] + async fn log_read(&mut self) { + // Read log struct + let mut log = [0; SharedMemLog::SIZE]; + self.bus.bp_read(self.log.addr, &mut log).await; + let log = SharedMemLog::from_bytes(&log); + + let idx = log.idx as usize; + + // If pointer hasn't moved, no need to do anything. + if idx == self.log.last_idx { + return; + } + + // Read entire buf for now. We could read only what we need, but then we + // run into annoying alignment issues in `bp_read`. + let mut buf = [0; 0x400]; + self.bus.bp_read(log.buf, &mut buf).await; + + while self.log.last_idx != idx as usize { + let b = buf[self.log.last_idx]; + if b == b'\r' || b == b'\n' { + if self.log.buf_count != 0 { + let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; + debug!("LOGS: {}", s); + self.log.buf_count = 0; + } + } else if self.log.buf_count < self.log.buf.len() { + self.log.buf[self.log.buf_count] = b; + self.log.buf_count += 1; + } + + self.log.last_idx += 1; + if self.log.last_idx == 0x400 { + self.log.last_idx = 0; + } + } + } + + pub async fn run(mut self) -> ! { + let mut buf = [0; 512]; + loop { + #[cfg(feature = "firmware-logs")] + self.log_read().await; + + // Send stuff + // TODO flow control not yet complete + if !self.has_credit() { + warn!("TX stalled"); + } else { + if let IoctlState::Pending { kind, cmd, iface, buf } = self.ioctl_state.get() { + self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; + self.ioctl_state.set(IoctlState::Sent { buf }); + } + if !self.has_credit() { + warn!("TX stalled"); + } else { + if let Some(packet) = self.ch.try_tx_buf() { + trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); + + let mut buf = [0; 512]; + let buf8 = slice8_mut(&mut buf); + + let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); + + let seq = self.sdpcm_seq; + self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, // TODO does this len need to be rounded up to u32? + len_inv: !total_len as u16, + sequence: seq, + channel_and_flags: CHANNEL_TYPE_DATA, + next_length: 0, + header_length: SdpcmHeader::SIZE as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let bcd_header = BcdHeader { + flags: BDC_VERSION << BDC_VERSION_SHIFT, + priority: 0, + flags2: 0, + data_offset: 0, + }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", bcd_header); + + buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); + buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); + + let total_len = (total_len + 3) & !3; // round up to 4byte + + trace!(" {:02x}", &buf8[..total_len.min(48)]); + + self.bus.wlan_write(&buf[..(total_len / 4)]).await; + self.ch.tx_done(); + } + } + } + + // Receive stuff + let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; + + if irq & IRQ_F2_PACKET_AVAILABLE != 0 { + let mut status = 0xFFFF_FFFF; + while status == 0xFFFF_FFFF { + status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; + } + + if status & STATUS_F2_PKT_AVAILABLE != 0 { + let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; + self.bus.wlan_read(&mut buf, len).await; + trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); + self.rx(&slice8_mut(&mut buf)[..len as usize]); + } + } + + // TODO use IRQs + yield_now().await; + } + } + + fn rx(&mut self, packet: &[u8]) { + if packet.len() < SdpcmHeader::SIZE { + warn!("packet too short, len={}", packet.len()); + return; + } + + let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); + trace!("rx {:?}", sdpcm_header); + if sdpcm_header.len != !sdpcm_header.len_inv { + warn!("len inv mismatch"); + return; + } + if sdpcm_header.len as usize != packet.len() { + // TODO: is this guaranteed?? + warn!("len from header doesn't match len from spi"); + return; + } + + self.update_credit(&sdpcm_header); + + let channel = sdpcm_header.channel_and_flags & 0x0f; + + let payload = &packet[sdpcm_header.header_length as _..]; + + match channel { + CHANNEL_TYPE_CONTROL => { + if payload.len() < CdcHeader::SIZE { + warn!("payload too short, len={}", payload.len()); + return; + } + + let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); + trace!(" {:?}", cdc_header); + + if let IoctlState::Sent { buf } = self.ioctl_state.get() { + if cdc_header.id == self.ioctl_id { + if cdc_header.status != 0 { + // TODO: propagate error instead + panic!("IOCTL error {=i32}", cdc_header.status as i32); + } + + let resp_len = cdc_header.len as usize; + info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); + + (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); + self.ioctl_state.set(IoctlState::Done { resp_len }); + } + } + } + CHANNEL_TYPE_EVENT => { + let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); + trace!(" {:?}", bcd_header); + + let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; + + if packet_start + EventPacket::SIZE > payload.len() { + warn!("BCD event, incomplete header"); + return; + } + let bcd_packet = &payload[packet_start..]; + trace!(" {:02x}", &bcd_packet[..(bcd_packet.len() as usize).min(36)]); + + let mut event_packet = EventPacket::from_bytes(&bcd_packet[..EventPacket::SIZE].try_into().unwrap()); + event_packet.byteswap(); + + const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h + if event_packet.eth.ether_type != ETH_P_LINK_CTL { + warn!( + "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", + event_packet.eth.ether_type, ETH_P_LINK_CTL + ); + return; + } + const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; + if event_packet.hdr.oui != BROADCOM_OUI { + warn!( + "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", + event_packet.hdr.oui, BROADCOM_OUI + ); + return; + } + const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; + if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { + warn!("unexpected subtype {}", event_packet.hdr.subtype); + return; + } + + const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; + if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { + warn!("unexpected user_subtype {}", event_packet.hdr.subtype); + return; + } + + if event_packet.msg.datalen as usize >= (bcd_packet.len() - EventMessage::SIZE) { + warn!("BCD event, incomplete data"); + return; + } + + let evt_type = events::Event::from(event_packet.msg.event_type as u8); + let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; + debug!("=== EVENT {}: {} {:02x}", evt_type, event_packet.msg, evt_data); + + if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { + self.events.publish_immediate(EventStatus { + status: event_packet.msg.status, + event_type: evt_type, + }); + } + } + CHANNEL_TYPE_DATA => { + let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); + trace!(" {:?}", bcd_header); + + let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; + if packet_start > payload.len() { + warn!("packet start out of range."); + return; + } + let packet = &payload[packet_start..]; + trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); + + match self.ch.try_rx_buf() { + Some(buf) => { + buf[..packet.len()].copy_from_slice(packet); + self.ch.rx_done(packet.len()) + } + None => warn!("failed to push rxd packet to the channel."), + } + } + _ => {} + } + } + + fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { + if sdpcm_header.channel_and_flags & 0xf < 3 { + let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; + if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { + sdpcm_seq_max = self.sdpcm_seq + 2; + } + self.sdpcm_seq_max = sdpcm_seq_max; + } + } + + fn has_credit(&self) -> bool { + self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 + } + + async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { + let mut buf = [0; 512]; + let buf8 = slice8_mut(&mut buf); + + let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); + + let sdpcm_seq = self.sdpcm_seq; + self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); + self.ioctl_id = self.ioctl_id.wrapping_add(1); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, // TODO does this len need to be rounded up to u32? + len_inv: !total_len as u16, + sequence: sdpcm_seq, + channel_and_flags: CHANNEL_TYPE_CONTROL, + next_length: 0, + header_length: SdpcmHeader::SIZE as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let cdc_header = CdcHeader { + cmd: cmd, + len: data.len() as _, + flags: kind as u16 | (iface as u16) << 12, + id: self.ioctl_id, + status: 0, + }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", cdc_header); + + buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); + buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); + + let total_len = (total_len + 3) & !3; // round up to 4byte + + trace!(" {:02x}", &buf8[..total_len.min(48)]); + + self.bus.wlan_write(&buf[..total_len / 4]).await; + } + + async fn core_disable(&mut self, core: Core) { + let base = core.base_addr(); + + // Dummy read? + let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + + // Check it isn't already reset + let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + if r & AI_RESETCTRL_BIT_RESET != 0 { + return; + } + + self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + + block_for(Duration::from_millis(1)); + + self.bus + .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) + .await; + let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + } + + async fn core_reset(&mut self, core: Core) { + self.core_disable(core).await; + + let base = core.base_addr(); + self.bus + .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) + .await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + + self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; + + Timer::after(Duration::from_millis(1)).await; + + self.bus + .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) + .await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + + Timer::after(Duration::from_millis(1)).await; + } + + async fn core_is_up(&mut self, core: Core) -> bool { + let base = core.base_addr(); + + let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { + debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); + return false; + } + + let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + if r & (AI_RESETCTRL_BIT_RESET) != 0 { + debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); + return false; + } + + true + } +} + +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +} From ba9afbc26d06ab38065cbff5b17a7f76db297ad4 Mon Sep 17 00:00:00 2001 From: sander Date: Wed, 22 Mar 2023 16:49:49 +0100 Subject: [PATCH 0705/1575] embassy-boot: add default nightly feature, makes it possible to compile with the stable compiler --- embassy-boot/boot/Cargo.toml | 5 ++++- embassy-boot/boot/src/lib.rs | 11 ++++++++++- embassy-boot/nrf/Cargo.toml | 6 ++++-- embassy-boot/nrf/src/lib.rs | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 3312c2f9f..04409cdc7 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -28,7 +28,7 @@ log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" -embedded-storage-async = "0.4.0" +embedded-storage-async = { version = "0.4.0", optional = true} salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } signature = { version = "1.6.4", default-features = false } @@ -43,8 +43,11 @@ default_features = false features = ["rand", "std", "u32_backend"] [features] +default = ["nightly"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] +nightly = ["dep:embedded-storage-async"] + #Internal features _verify = [] \ No newline at end of file diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 0df44f36e..7ce0c664a 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(async_fn_in_trait)] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))] #![allow(incomplete_features)] #![no_std] #![warn(missing_docs)] @@ -6,6 +6,8 @@ mod fmt; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; + +#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; const BOOT_MAGIC: u8 = 0xD0; @@ -647,6 +649,7 @@ impl FirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. + #[cfg(feature = "nightly")] pub async fn get_state( &mut self, flash: &mut F, @@ -776,6 +779,7 @@ impl FirmwareUpdater { /// /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. #[cfg(not(feature = "_verify"))] + #[cfg(feature = "nightly")] pub async fn mark_updated( &mut self, flash: &mut F, @@ -790,6 +794,7 @@ impl FirmwareUpdater { /// # Safety /// /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(feature = "nightly")] pub async fn mark_booted( &mut self, flash: &mut F, @@ -799,6 +804,7 @@ impl FirmwareUpdater { self.set_magic(aligned, BOOT_MAGIC, flash).await } + #[cfg(feature = "nightly")] async fn set_magic( &mut self, aligned: &mut [u8], @@ -826,6 +832,7 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. + #[cfg(feature = "nightly")] pub async fn write_firmware( &mut self, offset: usize, @@ -860,6 +867,7 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. + #[cfg(feature = "nightly")] pub async fn prepare_update( &mut self, flash: &mut F, @@ -1112,6 +1120,7 @@ impl FirmwareWriter { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. + #[cfg(feature = "nightly")] pub async fn write_block( &mut self, offset: usize, diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index c1a127518..90a36d014 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -17,17 +17,18 @@ target = "thumbv7em-none-eabi" defmt = { version = "0.3", optional = true } embassy-sync = { path = "../../embassy-sync" } -embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] } +embassy-nrf = { path = "../../embassy-nrf", default-features = false } 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.4.0" +embedded-storage-async = { version = "0.4.0", optional = true } cfg-if = "1.0.0" nrf-softdevice-mbr = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-softdevice.git", branch = "master", optional = true } [features] +default = ["nightly"] defmt = [ "dep:defmt", "embassy-boot/defmt", @@ -36,3 +37,4 @@ defmt = [ softdevice = [ "nrf-softdevice-mbr", ] +nightly = ["dep:embedded-storage-async", "embassy-boot/nightly", "embassy-nrf/nightly"] diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index f40ae62d6..5cc6ba448 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(type_alias_impl_trait)] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; From 04f90e3a9d11d539dbe3554ee89e3aee793ee5ce Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 23 Mar 2023 14:18:19 +0100 Subject: [PATCH 0706/1575] Add embedded-io blocking Read + Write for BufferedUart --- embassy-rp/src/uart/buffered.rs | 66 ++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 32e5ddf14..88628d97b 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -124,7 +124,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } } - pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, buffer: &[u8]) -> Result { self.tx.blocking_write(buffer) } @@ -132,7 +132,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { self.tx.blocking_flush() } - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result { self.rx.blocking_read(buffer) } @@ -201,7 +201,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }) } - pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result { loop { let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; @@ -222,7 +222,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }); } - return Ok(()); + return Ok(n); } } } @@ -326,7 +326,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { }) } - pub fn blocking_write(&mut self, buf: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, buf: &[u8]) -> Result { loop { let state = T::state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; @@ -342,7 +342,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { // FIFO was empty we have to manually pend the interrupt to shovel // TX data from the buffer into the FIFO. unsafe { T::Interrupt::steal() }.pend(); - return Ok(()); + return Ok(n); } } } @@ -533,6 +533,38 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> } } +impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUart<'d, T> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.rx.blocking_read(buf) + } +} + +impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUartRx<'d, T> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.blocking_read(buf) + } +} + +impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUart<'d, T> { + fn write(&mut self, buf: &[u8]) -> Result { + self.tx.blocking_write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.blocking_flush() + } +} + +impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUartTx<'d, T> { + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + mod eh02 { use super::*; @@ -566,8 +598,15 @@ mod eh02 { impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUartTx<'d, T> { type Error = Error; - fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) } fn bflush(&mut self) -> Result<(), Self::Error> { @@ -586,8 +625,15 @@ mod eh02 { impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for BufferedUart<'d, T> { type Error = Error; - fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) } fn bflush(&mut self) -> Result<(), Self::Error> { From 88483b5abebc8b0e52197aade18ceec7fef2a0a6 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 23 Mar 2023 14:26:37 +0100 Subject: [PATCH 0707/1575] Fix return type for EH-nb traits --- embassy-rp/src/uart/buffered.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 88628d97b..1a573b311 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -666,7 +666,7 @@ mod eh1 { impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) + self.blocking_write(buffer).map(drop) } fn flush(&mut self) -> Result<(), Self::Error> { @@ -676,7 +676,7 @@ mod eh1 { impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { - self.blocking_write(&[char]).map_err(nb::Error::Other) + self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) } fn flush(&mut self) -> nb::Result<(), Self::Error> { @@ -692,7 +692,7 @@ mod eh1 { impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) + self.blocking_write(buffer).map(drop) } fn flush(&mut self) -> Result<(), Self::Error> { @@ -702,7 +702,7 @@ mod eh1 { impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { - self.blocking_write(&[char]).map_err(nb::Error::Other) + self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) } fn flush(&mut self) -> nb::Result<(), Self::Error> { From 9939d438007f33bc57697be97c9a73ee001fe737 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 24 Mar 2023 12:14:23 +0200 Subject: [PATCH 0708/1575] fix: PR comment Signed-off-by: Lachezar Lechev --- embassy-rp/src/spi.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index c48e33fce..e6682ad6a 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -404,13 +404,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { if from_len > to_len { let write_bytes_len = from_len - to_len; - // disable incrementation of buffer - tx_ch.regs().ctrl_trig().modify(|ctrl_trig| { - ctrl_trig.set_incr_write(false); - ctrl_trig.set_incr_read(false); - }); - // write dummy data + // this will disable incrementation of the buffers crate::dma::write_repeated(tx_ch, p.dr().ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await } } From cd2f28d2abb5b66981b7fdbb32566e6b942c7a54 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Fri, 24 Mar 2023 12:14:38 +0200 Subject: [PATCH 0709/1575] chore: add spi_async tests for uneven buffers Signed-off-by: Lachezar Lechev --- tests/rp/src/bin/spi_async.rs | 44 +++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index 6c85ef60a..e3fe6e84c 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs @@ -1,3 +1,6 @@ +//! Make sure to connect GPIO pins 3 (`PIN_3`) and 4 (`PIN_4`) together +//! to run this test. +//! #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -18,10 +21,43 @@ async fn main(_spawner: Spawner) { let mut spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); - let tx_buf = [1_u8, 2, 3, 4, 5, 6]; - let mut rx_buf = [0_u8; 6]; - spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); - assert_eq!(rx_buf, tx_buf); + // equal rx & tx buffers + { + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + assert_eq!(rx_buf, tx_buf); + } + + // tx > rx buffer + { + let tx_buf = [7_u8, 8, 9, 10, 11, 12]; + + let mut rx_buf = [0_u8, 3]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + assert_eq!(rx_buf, tx_buf[..3]); + } + + // we make sure to that clearing FIFO works after the uneven buffers + + // equal rx & tx buffers + { + let tx_buf = [13_u8, 14, 15, 16, 17, 18]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + + assert_eq!(rx_buf, tx_buf); + } + + // rx > tx buffer + { + let tx_buf = [19_u8, 20, 21]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + + assert_eq!(rx_buf[..3], tx_buf, "only the first 3 TX bytes should have been received in the RX buffer"); + assert_eq!(rx_buf[3..], [0, 0, 0], "the rest of the RX bytes should be empty"); + } info!("Test OK"); cortex_m::asm::bkpt(); From d8b265856f73ae0e7985a6c2fe1a8f0fb811328d Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:52:48 +0100 Subject: [PATCH 0710/1575] Add f4 flash sector computation to hal-common to allow for tests --- embassy-hal-common/src/lib.rs | 1 + embassy-hal-common/src/stm32/flash.rs | 113 ++++++++++++++++++++++++++ embassy-hal-common/src/stm32/mod.rs | 1 + 3 files changed, 115 insertions(+) create mode 100644 embassy-hal-common/src/stm32/flash.rs create mode 100644 embassy-hal-common/src/stm32/mod.rs diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index b2a35cd35..1b253719e 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -10,4 +10,5 @@ mod macros; mod peripheral; pub mod ratio; pub mod ring_buffer; +pub mod stm32; pub use peripheral::{Peripheral, PeripheralRef}; diff --git a/embassy-hal-common/src/stm32/flash.rs b/embassy-hal-common/src/stm32/flash.rs new file mode 100644 index 000000000..46c42aff9 --- /dev/null +++ b/embassy-hal-common/src/stm32/flash.rs @@ -0,0 +1,113 @@ +pub mod f4 { + const FLASH_BASE: u32 = 0x08_00_00_00; + pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; + + #[derive(Debug, PartialEq)] + pub struct FlashSector { + pub index: u8, + pub size: u32, + } + + pub fn get_sector(addr: u32, dual_bank: bool, flash_size: u32) -> FlashSector { + let offset = addr - FLASH_BASE; + if !dual_bank { + get_single_bank_sector(offset) + } else { + let bank_size = flash_size / 2; + if offset < bank_size { + get_single_bank_sector(offset) + } else { + let sector = get_single_bank_sector(offset - bank_size); + FlashSector { + index: SECOND_BANK_SECTOR_OFFSET + sector.index, + ..sector + } + } + } + } + + fn get_single_bank_sector(offset: u32) -> FlashSector { + // First 4 sectors are 16KB, then one 64KB, and rest are 128KB + + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + FlashSector { + index: (offset / SMALL_SECTOR_SIZE) as u8, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => FlashSector { + index: 4 + i as u8, + size: LARGE_SECTOR_SIZE, + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::f4::*; + + #[test] + fn can_get_sector_single_bank() { + let assert_sector = |index: u8, size: u32, addr: u32| { + assert_eq!(FlashSector { index, size }, get_sector(addr, false, 1024 * 1024)) + }; + + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(11, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(11, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } + + #[test] + fn can_get_sector_dual_bank() { + let assert_sector = |index: u8, size: u32, addr: u32| { + assert_eq!(FlashSector { index, size }, get_sector(addr, true, 1024 * 1024)) + }; + + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, LARGE_SECTOR_SIZE, 0x0807_FFFF); + + assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_FFFF); + + assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + + assert_sector(17, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(17, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(19, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(19, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-hal-common/src/stm32/mod.rs b/embassy-hal-common/src/stm32/mod.rs new file mode 100644 index 000000000..2e50f82b3 --- /dev/null +++ b/embassy-hal-common/src/stm32/mod.rs @@ -0,0 +1 @@ +pub mod flash; From cccceb88f2c91adcbdc6c1d07d785a97742f996b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:57:15 +0100 Subject: [PATCH 0711/1575] Generate flash regions during build --- embassy-stm32/build.rs | 76 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index dbfc1370d..892819120 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -5,7 +5,7 @@ use std::{env, fs}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use stm32_metapac::metadata::METADATA; +use stm32_metapac::metadata::{MemoryRegionKind, METADATA}; fn main() { let chip_name = match env::vars() @@ -103,6 +103,68 @@ fn main() { } }); + // ======== + // Generate FLASH regions + let mut flash_regions = TokenStream::new(); + let flash_memory_regions = METADATA + .memory + .iter() + .filter(|x| x.kind == MemoryRegionKind::Flash || x.kind == MemoryRegionKind::Otp); + for region in flash_memory_regions.clone() { + let region_name = format_ident!("{}", region.name); + let base = region.address as usize; + let size = region.size as usize; + let settings = region.settings.as_ref().unwrap(); + let erase_size = settings.erase_size as usize; + let write_size = settings.write_size as usize; + let erase_value = settings.erase_value; + + flash_regions.extend(quote! { + pub struct #region_name(()); + }); + + flash_regions.extend(quote! { + impl crate::flash::FlashRegion for #region_name { + const BASE: usize = #base; + const SIZE: usize = #size; + const ERASE_SIZE: usize = #erase_size; + const WRITE_SIZE: usize = #write_size; + const ERASE_VALUE: u8 = #erase_value; + } + }); + } + + let (fields, inits): (Vec, Vec) = flash_memory_regions + .map(|f| { + let field_name = format_ident!("{}", f.name.to_lowercase()); + let field_type = format_ident!("{}", f.name); + let field = quote! { + pub #field_name: #field_type + }; + let init = quote! { + #field_name: #field_type(()) + }; + + (field, init) + }) + .unzip(); + + flash_regions.extend(quote! { + pub struct FlashRegions { + #(#fields),* + } + + impl FlashRegions { + pub(crate) const fn take() -> Self { + Self { + #(#inits),* + } + } + } + }); + + g.extend(quote! { pub mod flash_regions { #flash_regions } }); + // ======== // Generate DMA IRQs. @@ -558,11 +620,22 @@ fn main() { // ======== // Write foreach_foo! macrotables + let mut flash_regions_table: Vec> = Vec::new(); let mut interrupts_table: Vec> = Vec::new(); let mut peripherals_table: Vec> = Vec::new(); let mut pins_table: Vec> = Vec::new(); let mut dma_channels_table: Vec> = Vec::new(); + for m in METADATA + .memory + .iter() + .filter(|m| m.kind == MemoryRegionKind::Flash || m.kind == MemoryRegionKind::Otp) + { + let mut row = Vec::new(); + row.push(m.name.to_string()); + flash_regions_table.push(row); + } + let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; let gpio_stride = 0x400; @@ -659,6 +732,7 @@ fn main() { let mut m = String::new(); + make_table(&mut m, "foreach_flash_region", &flash_regions_table); make_table(&mut m, "foreach_interrupt", &interrupts_table); make_table(&mut m, "foreach_peripheral", &peripherals_table); make_table(&mut m, "foreach_pin", &pins_table); From 6b44027eab273d1589eb289044d1bd4d172477f6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:58:40 +0100 Subject: [PATCH 0712/1575] Add FlashRegion trait and implement embedded_storage traits for each region --- embassy-stm32/src/flash/mod.rs | 162 +++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b7166a437..b6cecfdb3 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,10 +1,10 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -pub use crate::pac::{ERASE_SIZE, ERASE_VALUE, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +pub use crate::_generated::flash_regions::*; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE}; use crate::peripherals::FLASH; use crate::Peripheral; -const FLASH_END: usize = FLASH_BASE + FLASH_SIZE; #[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] @@ -23,60 +23,63 @@ impl<'d> Flash<'d> { Self { _inner: p } } + pub fn into_regions(self) -> FlashRegions { + FlashRegions::take() + } + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let offset = FLASH_BASE as u32 + offset; - if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END { + if offset as usize + bytes.len() > FLASH_SIZE { return Err(Error::Size); } - let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) }; + let first_address = FLASH_BASE as u32 + offset; + + let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let offset = FLASH_BASE as u32 + offset; - if offset as usize + buf.len() > FLASH_END { + if offset as usize + buf.len() > FLASH_SIZE { return Err(Error::Size); } - if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + if offset as usize % family::MAX_WRITE_SIZE != 0 || buf.len() as usize % family::MAX_WRITE_SIZE != 0 { return Err(Error::Unaligned); } - trace!("Writing {} bytes at 0x{:x}", buf.len(), offset); - self.clear_all_err(); + let first_address = FLASH_BASE as u32 + offset; + trace!("Writing {} bytes at 0x{:x}", buf.len(), first_address); unsafe { + family::clear_all_err(); + family::unlock(); - let res = family::blocking_write(offset, buf); + let res = family::blocking_write(first_address, buf); family::lock(); res } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let from = FLASH_BASE as u32 + from; - let to = FLASH_BASE as u32 + to; - if to < from || to as usize > FLASH_END { + if to < from || to as usize > FLASH_SIZE { return Err(Error::Size); } - if (from as usize % ERASE_SIZE) != 0 || (to as usize % ERASE_SIZE) != 0 { + if (from as usize % family::MAX_ERASE_SIZE) != 0 || (to as usize % family::MAX_ERASE_SIZE) != 0 { return Err(Error::Unaligned); } - self.clear_all_err(); + let from_address = FLASH_BASE as u32 + from; + let to_address = FLASH_BASE as u32 + to; unsafe { + family::clear_all_err(); + family::unlock(); - let res = family::blocking_erase(from, to); + let res = family::blocking_erase(from_address, to_address); family::lock(); res } } - - fn clear_all_err(&mut self) { - unsafe { family::clear_all_err() }; - } } impl Drop for Flash<'_> { @@ -85,6 +88,69 @@ impl Drop for Flash<'_> { } } +pub trait FlashRegion { + const BASE: usize; + const SIZE: usize; + const ERASE_SIZE: usize; + const WRITE_SIZE: usize; + const ERASE_VALUE: u8; + + fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + if offset as usize + bytes.len() > Self::SIZE { + return Err(Error::Size); + } + + let first_address = Self::BASE as u32 + offset; + + let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { + if offset as usize + buf.len() > Self::SIZE { + return Err(Error::Size); + } + if offset as usize % Self::WRITE_SIZE != 0 || buf.len() as usize % Self::WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let first_address = Self::BASE as u32 + offset; + trace!("Writing {} bytes from 0x{:x}", buf.len(), first_address); + + critical_section::with(|_| unsafe { + family::clear_all_err(); + + family::unlock(); + let res = family::blocking_write(first_address, buf); + family::lock(); + res + }) + } + + fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + if to < from || to as usize > Self::SIZE { + return Err(Error::Size); + } + if (from as usize % Self::ERASE_SIZE) != 0 || (to as usize % Self::ERASE_SIZE) != 0 { + return Err(Error::Unaligned); + } + + let from_address = Self::BASE as u32 + from; + let to_address = Self::BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", from_address, to_address); + + critical_section::with(|_| unsafe { + family::clear_all_err(); + + family::unlock(); + let res = family::blocking_erase(from_address, to_address); + family::lock(); + res + }) + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -97,10 +163,6 @@ pub enum Error { Parallelism, } -impl<'d> ErrorType for Flash<'d> { - type Error = Error; -} - impl NorFlashError for Error { fn kind(&self) -> NorFlashErrorKind { match self { @@ -111,27 +173,35 @@ impl NorFlashError for Error { } } -impl<'d> ReadNorFlash for Flash<'d> { - const READ_SIZE: usize = WRITE_SIZE; +foreach_flash_region! { + ($name:ident) => { + impl ErrorType for crate::_generated::flash_regions::$name { + type Error = Error; + } - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) - } + impl ReadNorFlash for crate::_generated::flash_regions::$name { + const READ_SIZE: usize = ::WRITE_SIZE; - fn capacity(&self) -> usize { - FLASH_SIZE - } -} - -impl<'d> NorFlash for Flash<'d> { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + ::SIZE + } + } + + impl NorFlash for crate::_generated::flash_regions::$name { + const WRITE_SIZE: usize = ::WRITE_SIZE; + const ERASE_SIZE: usize = ::ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } + } + }; } From 6c73b23f384b4814ad9d13e8d108ef71464f72af Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 05:59:40 +0100 Subject: [PATCH 0713/1575] Align F4 family --- embassy-stm32/src/flash/f4.rs | 70 ++++++++++------------------------- 1 file changed, 19 insertions(+), 51 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 9e23a8adf..d739c46b6 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,12 +1,16 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{ERASE_SIZE, FLASH_BASE, FLASH_SIZE}; +use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; + +use super::{FlashRegion, FLASH_SIZE, MAINC}; use crate::flash::Error; use crate::pac; -const SECOND_BANK_SECTOR_START: u32 = 12; +pub(crate) const MAX_WRITE_SIZE: usize = MAINC::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = MAINC::ERASE_SIZE; unsafe fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { @@ -34,7 +38,7 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); @@ -42,10 +46,12 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); + let mut offset = first_address; + for chunk in buf.chunks(MAX_WRITE_SIZE) { + let vals = chunk.chunks_exact(size_of::()); + assert!(vals.remainder().is_empty()); + for val in vals { + write_volatile(offset as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); offset += val.len() as u32; // prevents parallelism errors @@ -65,50 +71,12 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -struct FlashSector { - index: u8, - size: u32, -} - -fn get_sector(addr: u32, dual_bank: bool) -> FlashSector { - let offset = addr - FLASH_BASE as u32; - - let bank_size = match dual_bank { - true => FLASH_SIZE / 2, - false => FLASH_SIZE, - } as u32; - - let bank = offset / bank_size; - let offset_in_bank = offset % bank_size; - - let index_in_bank = if offset_in_bank >= ERASE_SIZE as u32 / 2 { - 4 + offset_in_bank / ERASE_SIZE as u32 - } else { - offset_in_bank / (ERASE_SIZE as u32 / 8) - }; - - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - let size = match index_in_bank { - 0..=3 => 16 * 1024, - 4 => 64 * 1024, - _ => 128 * 1024, - }; - - let index = if bank == 1 { - SECOND_BANK_SECTOR_START + index_in_bank - } else { - index_in_bank - } as u8; - - FlashSector { index, size } -} - -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let mut addr = from; +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + let mut addr = from_address; let dual_bank = is_dual_bank(); - while addr < to { - let sector = get_sector(addr, dual_bank); + while addr < to_address { + let sector = get_sector(addr, dual_bank, FLASH_SIZE as u32); erase_sector(sector.index)?; addr += sector.size; } @@ -117,8 +85,8 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { } unsafe fn erase_sector(sector: u8) -> Result<(), Error> { - let bank = sector / SECOND_BANK_SECTOR_START as u8; - let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_START as u8); + let bank = sector / SECOND_BANK_SECTOR_OFFSET as u8; + let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_OFFSET as u8); trace!("Erasing sector: {}", sector); From 7edd72f8f542e81143d2375f1783418404d1dc58 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 06:05:37 +0100 Subject: [PATCH 0714/1575] Align F3 family --- embassy-stm32/src/flash/f3.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 1cb08ee1a..294fcffc2 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -1,9 +1,14 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; +use super::FlashRegion; use crate::flash::Error; use crate::pac; +pub(crate) const MAX_WRITE_SIZE: usize = super::MAINA::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::MAINA::ERASE_SIZE; + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -13,15 +18,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { pac::FLASH.cr().write(|w| w.set_pg(true)); let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(2) { - write_volatile(offset as *mut u16, u16::from_le_bytes(chunk[0..2].try_into().unwrap())); - offset += chunk.len() as u32; + let mut address = first_address; + let chunks = buf.chunks_exact(size_of::()); + assert!(chunks.remainder().is_empty()); + for chunk in chunks { + write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); + address += chunk.len() as u32; ret = blocking_wait_ready(); if ret.is_err() { @@ -36,8 +43,8 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - for page in (from..to).step_by(super::ERASE_SIZE) { +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + for page in (from_address..to_address).step_by(MAX_ERASE_SIZE) { pac::FLASH.cr().modify(|w| { w.set_per(true); }); From 99c4346579cd000128d6b14aca98968bc4bbad22 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 06:25:12 +0100 Subject: [PATCH 0715/1575] Add f7 computation to hal common and add tests --- .../src/stm32/{flash.rs => flash/f4.rs} | 82 +++++++++---------- embassy-hal-common/src/stm32/flash/f7.rs | 57 +++++++++++++ embassy-hal-common/src/stm32/flash/mod.rs | 2 + 3 files changed, 99 insertions(+), 42 deletions(-) rename embassy-hal-common/src/stm32/{flash.rs => flash/f4.rs} (61%) create mode 100644 embassy-hal-common/src/stm32/flash/f7.rs create mode 100644 embassy-hal-common/src/stm32/flash/mod.rs diff --git a/embassy-hal-common/src/stm32/flash.rs b/embassy-hal-common/src/stm32/flash/f4.rs similarity index 61% rename from embassy-hal-common/src/stm32/flash.rs rename to embassy-hal-common/src/stm32/flash/f4.rs index 46c42aff9..44d6c08c4 100644 --- a/embassy-hal-common/src/stm32/flash.rs +++ b/embassy-hal-common/src/stm32/flash/f4.rs @@ -1,62 +1,60 @@ -pub mod f4 { - const FLASH_BASE: u32 = 0x08_00_00_00; - pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; - pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; - pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; - pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; +const FLASH_BASE: u32 = 0x0800_0000; +pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; +pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; +pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; +pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; - #[derive(Debug, PartialEq)] - pub struct FlashSector { - pub index: u8, - pub size: u32, - } +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub size: u32, +} - pub fn get_sector(addr: u32, dual_bank: bool, flash_size: u32) -> FlashSector { - let offset = addr - FLASH_BASE; - if !dual_bank { +pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { + let offset = address - FLASH_BASE; + if !dual_bank { + get_single_bank_sector(offset) + } else { + let bank_size = flash_size / 2; + if offset < bank_size { get_single_bank_sector(offset) } else { - let bank_size = flash_size / 2; - if offset < bank_size { - get_single_bank_sector(offset) - } else { - let sector = get_single_bank_sector(offset - bank_size); - FlashSector { - index: SECOND_BANK_SECTOR_OFFSET + sector.index, - ..sector - } + let sector = get_single_bank_sector(offset - bank_size); + FlashSector { + index: SECOND_BANK_SECTOR_OFFSET + sector.index, + ..sector } } } +} - fn get_single_bank_sector(offset: u32) -> FlashSector { - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB +fn get_single_bank_sector(offset: u32) -> FlashSector { + // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - FlashSector { - index: (offset / SMALL_SECTOR_SIZE) as u8, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - size: MEDIUM_SECTOR_SIZE, - } + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + FlashSector { + index: (offset / SMALL_SECTOR_SIZE) as u8, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + size: MEDIUM_SECTOR_SIZE, } } - i => FlashSector { - index: 4 + i as u8, - size: LARGE_SECTOR_SIZE, - }, } + i => FlashSector { + index: 4 + i as u8, + size: LARGE_SECTOR_SIZE, + }, } } #[cfg(test)] mod tests { - use super::f4::*; + use super::*; #[test] fn can_get_sector_single_bank() { diff --git a/embassy-hal-common/src/stm32/flash/f7.rs b/embassy-hal-common/src/stm32/flash/f7.rs new file mode 100644 index 000000000..90938e6ab --- /dev/null +++ b/embassy-hal-common/src/stm32/flash/f7.rs @@ -0,0 +1,57 @@ +const FLASH_BASE: u32 = 0x0800_0000; +pub(crate) const SMALL_SECTOR_SIZE: u32 = 32 * 1024; +pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; +pub(crate) const LARGE_SECTOR_SIZE: u32 = 256 * 1024; + +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub size: u32, +} + +pub fn get_sector(address: u32) -> FlashSector { + // First 4 sectors are 32KB, then one 128KB, and rest are 256KB + let offset = address - FLASH_BASE; + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + FlashSector { + index: (offset / SMALL_SECTOR_SIZE) as u8, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => FlashSector { + index: 4 + i as u8, + size: LARGE_SECTOR_SIZE, + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_get_sector() { + let assert_sector = |index: u8, size: u32, addr: u32| assert_eq!(FlashSector { index, size }, get_sector(addr)); + + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_7FFF); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_8000); + assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0802_0000); + assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); + + assert_sector(5, LARGE_SECTOR_SIZE, 0x0804_0000); + assert_sector(5, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(7, LARGE_SECTOR_SIZE, 0x080C_0000); + assert_sector(7, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-hal-common/src/stm32/flash/mod.rs b/embassy-hal-common/src/stm32/flash/mod.rs new file mode 100644 index 000000000..1452b491d --- /dev/null +++ b/embassy-hal-common/src/stm32/flash/mod.rs @@ -0,0 +1,2 @@ +pub mod f4; +pub mod f7; From a8567f06174c7a3fc2c708a2acb3763d9a59c8b7 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 06:26:00 +0100 Subject: [PATCH 0716/1575] Align F7 family --- embassy-stm32/src/flash/f7.rs | 39 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index dd0d8439d..ee0513fa4 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,10 +1,17 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +use embassy_hal_common::stm32::flash::f7::get_sector; + +use super::FlashRegion; use crate::flash::Error; use crate::pac; +pub(crate) const MAX_WRITE_SIZE: usize = super::MAINC::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::MAINC::ERASE_SIZE; + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -14,7 +21,7 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); @@ -22,11 +29,13 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += val.len() as u32; + let mut address = first_address; + for chunk in buf.chunks(MAX_WRITE_SIZE) { + let vals = chunk.chunks_exact(size_of::()); + assert!(vals.remainder().is_empty()); + for val in vals { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; // prevents parallelism errors fence(Ordering::SeqCst); @@ -45,20 +54,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let start_sector = if from >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { - 4 + (from - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32 - } else { - (from - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8) - }; - - let end_sector = if to >= (super::FLASH_BASE + super::ERASE_SIZE / 2) as u32 { - 4 + (to - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32 - } else { - (to - super::FLASH_BASE as u32) / (super::ERASE_SIZE as u32 / 8) - }; - - for sector in start_sector..end_sector { +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + let start_sector = get_sector(from_address); + let end_sector = get_sector(to_address); + for sector in start_sector.index..end_sector.index { let ret = erase_sector(sector as u8); if ret.is_err() { return ret; From c848bd9c9c0589e987918fb72647b9002f0eb4e4 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:02:42 +0100 Subject: [PATCH 0717/1575] Align with removal of MemoryRegionKind::Otp --- embassy-stm32/build.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 892819120..ca55681fe 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -109,9 +109,9 @@ fn main() { let flash_memory_regions = METADATA .memory .iter() - .filter(|x| x.kind == MemoryRegionKind::Flash || x.kind == MemoryRegionKind::Otp); + .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()); for region in flash_memory_regions.clone() { - let region_name = format_ident!("{}", region.name); + let region_name = format_ident!("{}", region.name.replace("_", "")); let base = region.address as usize; let size = region.size as usize; let settings = region.settings.as_ref().unwrap(); @@ -136,8 +136,9 @@ fn main() { let (fields, inits): (Vec, Vec) = flash_memory_regions .map(|f| { - let field_name = format_ident!("{}", f.name.to_lowercase()); - let field_type = format_ident!("{}", f.name); + let trimmed_name = f.name.replace("_", ""); + let field_name = format_ident!("{}", trimmed_name.to_lowercase()); + let field_type = format_ident!("{}", trimmed_name); let field = quote! { pub #field_name: #field_type }; @@ -629,10 +630,10 @@ fn main() { for m in METADATA .memory .iter() - .filter(|m| m.kind == MemoryRegionKind::Flash || m.kind == MemoryRegionKind::Otp) + .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { let mut row = Vec::new(); - row.push(m.name.to_string()); + row.push(m.name.replace("_", "")); flash_regions_table.push(row); } From 47e07584cacf9b632736185d95be2f703df9a8f8 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:03:00 +0100 Subject: [PATCH 0718/1575] Align H7 family --- embassy-stm32/src/flash/h7.rs | 41 ++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 7de95ac11..6ab8e7b74 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,13 +1,19 @@ use core::convert::TryInto; +use core::mem::size_of; use core::ptr::write_volatile; +use super::{FlashRegion, FLASH_SIZE}; use crate::flash::Error; use crate::pac; +const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; +const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; const SECOND_BANK_OFFSET: usize = 0x0010_0000; const fn is_dual_bank() -> bool { - super::FLASH_SIZE / 2 > super::ERASE_SIZE + FLASH_SIZE / 2 > ERASE_SIZE } pub(crate) unsafe fn lock() { @@ -27,8 +33,8 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { - let bank = if !is_dual_bank() || (offset - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { + let bank = if !is_dual_bank() || (first_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { pac::FLASH.bank(0) } else { pac::FLASH.bank(1) @@ -45,12 +51,13 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - 'outer: for chunk in buf.chunks(super::WRITE_SIZE) { - for val in chunk.chunks(4) { - trace!("Writing at {:x}", offset); - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += val.len() as u32; + let mut address = first_address; + 'outer: for chunk in buf.chunks(WRITE_SIZE) { + let vals = chunk.chunks_exact(size_of::()); + assert!(vals.remainder().is_empty()); + for val in vals { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; ret = blocking_wait_ready(bank); bank.sr().modify(|w| { @@ -76,20 +83,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error } 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 start_sector = (from - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + let end_sector = (to - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - 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; - (start_sector, end_sector) - } else { - error!("Attempting to write outside of defined sectors {:x} {:x}", from, to); - return Err(Error::Unaligned); - }; - - trace!("Erasing sectors from {} to {}", start, end); - for sector in start..end { + for sector in start_sector..end_sector { let bank = if sector >= 8 { 1 } else { 0 }; let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); if ret.is_err() { From 47d5f127bb307f441bc7765d6d053ce41168ce53 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:30:24 +0100 Subject: [PATCH 0719/1575] Align L family --- embassy-stm32/src/flash/l.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 5048a3314..9ab732b8b 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,9 +1,15 @@ use core::convert::TryInto; use core::ptr::write_volatile; +use super::FlashRegion; use crate::flash::Error; use crate::pac; +const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; +const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -33,17 +39,17 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = offset; - for chunk in buf.chunks(super::WRITE_SIZE) { + let mut address = first_address; + for chunk in buf.chunks(WRITE_SIZE) { for val in chunk.chunks(4) { - write_volatile(offset as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - offset += val.len() as u32; + write_volatile(address as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); + address += val.len() as u32; } ret = blocking_wait_ready(); @@ -60,8 +66,8 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error ret } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - for page in (from..to).step_by(super::ERASE_SIZE) { +pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { + for page in (from_address..to_address).step_by(ERASE_SIZE) { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -74,7 +80,7 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (page - super::FLASH_BASE as u32) / super::ERASE_SIZE as u32; + let idx = (page - super::FLASH_BASE as u32) / ERASE_SIZE as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; From 73ccc04231adb4c4e2f00c3ecaea8481afb218d4 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 13:39:10 +0100 Subject: [PATCH 0720/1575] Change region type name --- embassy-stm32/build.rs | 15 ++++++++++----- embassy-stm32/src/flash/f3.rs | 4 ++-- embassy-stm32/src/flash/f4.rs | 12 ++++++------ embassy-stm32/src/flash/f7.rs | 4 ++-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ca55681fe..393efc426 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -111,7 +111,7 @@ fn main() { .iter() .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()); for region in flash_memory_regions.clone() { - let region_name = format_ident!("{}", region.name.replace("_", "")); + let region_name = format_ident!("{}", get_flash_region_name(region.name)); let base = region.address as usize; let size = region.size as usize; let settings = region.settings.as_ref().unwrap(); @@ -120,6 +120,7 @@ fn main() { let erase_value = settings.erase_value; flash_regions.extend(quote! { + #[allow(non_camel_case_types)] pub struct #region_name(()); }); @@ -136,9 +137,9 @@ fn main() { let (fields, inits): (Vec, Vec) = flash_memory_regions .map(|f| { - let trimmed_name = f.name.replace("_", ""); - let field_name = format_ident!("{}", trimmed_name.to_lowercase()); - let field_type = format_ident!("{}", trimmed_name); + let region_name = get_flash_region_name(f.name); + let field_name = format_ident!("{}", region_name.to_lowercase()); + let field_type = format_ident!("{}", region_name); let field = quote! { pub #field_name: #field_type }; @@ -633,7 +634,7 @@ fn main() { .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { let mut row = Vec::new(); - row.push(m.name.replace("_", "")); + row.push(get_flash_region_name(m.name)); flash_regions_table.push(row); } @@ -886,3 +887,7 @@ macro_rules! {} {{ ) .unwrap(); } + +fn get_flash_region_name(name: &str) -> String { + name.replace("BANK_", "BANK").replace("REGION_", "REGION") +} diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 294fcffc2..b24dfb4a7 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -6,8 +6,8 @@ use super::FlashRegion; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::MAINA::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::MAINA::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index d739c46b6..0d9d405ba 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -5,12 +5,12 @@ use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; -use super::{FlashRegion, FLASH_SIZE, MAINC}; +use super::{FlashRegion, FLASH_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = MAINC::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = MAINC::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; unsafe fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { @@ -46,13 +46,13 @@ pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<() let ret = { let mut ret: Result<(), Error> = Ok(()); - let mut offset = first_address; + let mut address = first_address; for chunk in buf.chunks(MAX_WRITE_SIZE) { let vals = chunk.chunks_exact(size_of::()); assert!(vals.remainder().is_empty()); for val in vals { - write_volatile(offset as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - offset += val.len() as u32; + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; // prevents parallelism errors fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index ee0513fa4..8b8076e0c 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -9,8 +9,8 @@ use super::FlashRegion; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::MAINC::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::MAINC::ERASE_SIZE; +pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; +pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); From 245147634bfbdcd325eea20be19286708bb29c9f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 16:03:06 +0100 Subject: [PATCH 0721/1575] Add region start to flash sectors --- embassy-hal-common/src/stm32/flash/f4.rs | 90 +++++++++++++----------- embassy-hal-common/src/stm32/flash/f7.rs | 42 ++++++----- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/embassy-hal-common/src/stm32/flash/f4.rs b/embassy-hal-common/src/stm32/flash/f4.rs index 44d6c08c4..a8069dddf 100644 --- a/embassy-hal-common/src/stm32/flash/f4.rs +++ b/embassy-hal-common/src/stm32/flash/f4.rs @@ -7,6 +7,7 @@ pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; #[derive(Debug, PartialEq)] pub struct FlashSector { pub index: u8, + pub start: u32, pub size: u32, } @@ -22,7 +23,8 @@ pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector let sector = get_single_bank_sector(offset - bank_size); FlashSector { index: SECOND_BANK_SECTOR_OFFSET + sector.index, - ..sector + start: sector.start + bank_size, + size: sector.size, } } } @@ -30,25 +32,31 @@ pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector fn get_single_bank_sector(offset: u32) -> FlashSector { // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { 0 => { if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; FlashSector { - index: (offset / SMALL_SECTOR_SIZE) as u8, + index: small_sector_index as u8, + start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, size: SMALL_SECTOR_SIZE, } } else { FlashSector { index: 4, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, size: MEDIUM_SECTOR_SIZE, } } } - i => FlashSector { - index: 4 + i as u8, - size: LARGE_SECTOR_SIZE, - }, + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } } } @@ -58,54 +66,54 @@ mod tests { #[test] fn can_get_sector_single_bank() { - let assert_sector = |index: u8, size: u32, addr: u32| { - assert_eq!(FlashSector { index, size }, get_sector(addr, false, 1024 * 1024)) + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) }; - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(11, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(11, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } #[test] fn can_get_sector_dual_bank() { - let assert_sector = |index: u8, size: u32, addr: u32| { - assert_eq!(FlashSector { index, size }, get_sector(addr, true, 1024 * 1024)) + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) }; - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(7, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(7, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(12, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(15, SMALL_SECTOR_SIZE, 0x0808_FFFF); + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(16, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - assert_sector(17, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(17, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(19, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(19, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } diff --git a/embassy-hal-common/src/stm32/flash/f7.rs b/embassy-hal-common/src/stm32/flash/f7.rs index 90938e6ab..2f586adee 100644 --- a/embassy-hal-common/src/stm32/flash/f7.rs +++ b/embassy-hal-common/src/stm32/flash/f7.rs @@ -6,6 +6,7 @@ pub(crate) const LARGE_SECTOR_SIZE: u32 = 256 * 1024; #[derive(Debug, PartialEq)] pub struct FlashSector { pub index: u8, + pub start: u32, pub size: u32, } @@ -15,21 +16,28 @@ pub fn get_sector(address: u32) -> FlashSector { match offset / LARGE_SECTOR_SIZE { 0 => { if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; FlashSector { - index: (offset / SMALL_SECTOR_SIZE) as u8, + index: small_sector_index as u8, + start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, size: SMALL_SECTOR_SIZE, } } else { FlashSector { index: 4, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, size: MEDIUM_SECTOR_SIZE, } } } - i => FlashSector { - index: 4 + i as u8, - size: LARGE_SECTOR_SIZE, - }, + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } } } @@ -39,19 +47,21 @@ mod tests { #[test] fn can_get_sector() { - let assert_sector = |index: u8, size: u32, addr: u32| assert_eq!(FlashSector { index, size }, get_sector(addr)); + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr)) + }; - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, SMALL_SECTOR_SIZE, 0x0800_7FFF); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_8000); - assert_sector(3, SMALL_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0802_0000); - assert_sector(4, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000); + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0804_0000); - assert_sector(5, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(7, LARGE_SECTOR_SIZE, 0x080C_0000); - assert_sector(7, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000); + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } From bc69eb596e2496a5eb0cf1252ada12f2710aaff2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 16:04:45 +0100 Subject: [PATCH 0722/1575] Add is_eraseable_range and split write into consecutive parts --- embassy-stm32/src/flash/f3.rs | 61 ++++++++++++------------ embassy-stm32/src/flash/f4.rs | 84 ++++++++++++++++----------------- embassy-stm32/src/flash/f7.rs | 80 ++++++++++++++++---------------- embassy-stm32/src/flash/h7.rs | 72 ++++++++++++++-------------- embassy-stm32/src/flash/l.rs | 62 ++++++++++++------------- embassy-stm32/src/flash/mod.rs | 85 +++++++++++++++++++++++----------- 6 files changed, 236 insertions(+), 208 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index b24dfb4a7..99ac1a153 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -1,13 +1,13 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; -use super::FlashRegion; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::ERASE_SIZE; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -18,33 +18,35 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 2); + pac::FLASH.cr().write(|w| w.set_pg(true)); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - let chunks = buf.chunks_exact(size_of::()); - assert!(chunks.remainder().is_empty()); - for chunk in chunks { - write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); - address += chunk.len() as u32; - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - for page in (from_address..to_address).step_by(MAX_ERASE_SIZE) { +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for chunk in buf.chunks(2) { + write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); + address += chunk.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + for page in (start_address..end_address).step_by(ERASE_SIZE) { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -71,7 +73,6 @@ pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Resul return ret; } } - Ok(()) } @@ -89,7 +90,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 0d9d405ba..7428fd572 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,23 +1,19 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; -use super::{FlashRegion, FLASH_SIZE}; +use super::{FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; - -unsafe fn is_dual_bank() -> bool { +fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration 1024 => { if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { - pac::FLASH.optcr().read().db1m() + unsafe { pac::FLASH.optcr().read().db1m() } } else { false } @@ -38,49 +34,53 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); }); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(MAX_WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; - - // prevents parallelism errors - fence(Ordering::SeqCst); - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - let mut addr = from_address; - let dual_bank = is_dual_bank(); +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} - while addr < to_address { - let sector = get_sector(addr, dual_bank, FLASH_SIZE as u32); - erase_sector(sector.index)?; - addr += sector.size; +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); } + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let dual_bank = is_dual_bank(); + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let dual_bank = is_dual_bank(); + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + erase_sector(sector.index)?; + address += sector.size; + } Ok(()) } @@ -116,7 +116,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 8b8076e0c..16b684580 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,17 +1,13 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::stm32::flash::f7::get_sector; -use super::FlashRegion; +use super::WRITE_SIZE; use crate::flash::Error; use crate::pac; -pub(crate) const MAX_WRITE_SIZE: usize = super::BANK1_REGION3::WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = super::BANK1_REGION3::ERASE_SIZE; - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -21,49 +17,51 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| { w.set_pg(true); w.set_psize(pac::flash::vals::Psize::PSIZE32); }); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(MAX_WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; - - // prevents parallelism errors - fence(Ordering::SeqCst); - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - let start_sector = get_sector(from_address); - let end_sector = get_sector(to_address); - for sector in start_sector.index..end_sector.index { - let ret = erase_sector(sector as u8); - if ret.is_err() { - return ret; - } +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); } + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address); + erase_sector(sector.index)?; + address += sector.size; + } Ok(()) } @@ -106,7 +104,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 6ab8e7b74..21a9e45df 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,15 +1,13 @@ use core::convert::TryInto; -use core::mem::size_of; use core::ptr::write_volatile; -use super::{FlashRegion, FLASH_SIZE}; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; -pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; const SECOND_BANK_OFFSET: usize = 0x0010_0000; const fn is_dual_bank() -> bool { @@ -33,59 +31,60 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { - let bank = if !is_dual_bank() || (first_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); +} + +pub(crate) unsafe fn end_write() {} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + // We cannot have the write setup sequence in begin_write as it depends on the address + let bank = if !is_dual_bank() || (start_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { pac::FLASH.bank(0) } else { pac::FLASH.bank(1) }; - bank.cr().write(|w| { w.set_pg(true); w.set_psize(2); // 32 bits at once }); - cortex_m::asm::isb(); cortex_m::asm::dsb(); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + fence(Ordering::SeqCst); - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - 'outer: for chunk in buf.chunks(WRITE_SIZE) { - let vals = chunk.chunks_exact(size_of::()); - assert!(vals.remainder().is_empty()); - for val in vals { - write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); - address += val.len() as u32; + let mut res = None; + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; - ret = blocking_wait_ready(bank); - bank.sr().modify(|w| { - if w.eop() { - w.set_eop(true); - } - }); - if ret.is_err() { - break 'outer; - } + res = Some(blocking_wait_ready(bank)); + bank.sr().modify(|w| { + if w.eop() { + w.set_eop(true); } + }); + if res.unwrap().is_err() { + break; } - ret - }; + } bank.cr().write(|w| w.set_pg(false)); cortex_m::asm::isb(); cortex_m::asm::dsb(); - core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst); + fence(Ordering::SeqCst); - ret + res.unwrap() } -pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { - let start_sector = (from - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - let end_sector = (to - super::FLASH_BASE as u32) / ERASE_SIZE as u32; +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; for sector in start_sector..end_sector { let bank = if sector >= 8 { 1 } else { 0 }; let ret = erase_sector(pac::FLASH.bank(bank), (sector % 8) as u8); @@ -93,7 +92,6 @@ pub(crate) unsafe fn blocking_erase(from: u32, to: u32) -> Result<(), Error> { return ret; } } - Ok(()) } @@ -157,7 +155,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { }); } -pub(crate) unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { +unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 9ab732b8b..57989625c 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,14 +1,12 @@ -use core::convert::TryInto; use core::ptr::write_volatile; -use super::FlashRegion; +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const WRITE_SIZE: usize = super::BANK1::WRITE_SIZE; const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; -pub(crate) const MAX_WRITE_SIZE: usize = WRITE_SIZE; -pub(crate) const MAX_ERASE_SIZE: usize = ERASE_SIZE; pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] @@ -39,35 +37,37 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn blocking_write(first_address: u32, buf: &[u8]) -> Result<(), Error> { +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 4); + #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); - - let ret = { - let mut ret: Result<(), Error> = Ok(()); - let mut address = first_address; - for chunk in buf.chunks(WRITE_SIZE) { - for val in chunk.chunks(4) { - write_volatile(address as *mut u32, u32::from_le_bytes(val[0..4].try_into().unwrap())); - address += val.len() as u32; - } - - ret = blocking_wait_ready(); - if ret.is_err() { - break; - } - } - ret - }; - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - pac::FLASH.cr().write(|w| w.set_pg(false)); - - ret } -pub(crate) unsafe fn blocking_erase(from_address: u32, to_address: u32) -> Result<(), Error> { - for page in (from_address..to_address).step_by(ERASE_SIZE) { +pub(crate) unsafe fn end_write() { + #[cfg(any(flash_wl, flash_wb, flash_l4))] + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + blocking_wait_ready() +} + +pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 +} + +pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + for page in (start_address..end_address).step_by(ERASE_SIZE) { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -155,7 +155,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b6cecfdb3..c704909ac 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,8 +1,10 @@ use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::{Mutex, MutexGuard}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; pub use crate::_generated::flash_regions::*; -pub use crate::pac::{FLASH_BASE, FLASH_SIZE}; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::peripherals::FLASH; use crate::Peripheral; @@ -17,6 +19,8 @@ pub struct Flash<'d> { _inner: PeripheralRef<'d, FLASH>, } +static REGION_LOCK: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { pub fn new(p: impl Peripheral

+ 'd) -> Self { into_ref!(p); @@ -33,7 +37,6 @@ impl<'d> Flash<'d> { } let first_address = FLASH_BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) @@ -43,39 +46,56 @@ impl<'d> Flash<'d> { if offset as usize + buf.len() > FLASH_SIZE { return Err(Error::Size); } - if offset as usize % family::MAX_WRITE_SIZE != 0 || buf.len() as usize % family::MAX_WRITE_SIZE != 0 { + if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { return Err(Error::Unaligned); } - let first_address = FLASH_BASE as u32 + offset; - trace!("Writing {} bytes at 0x{:x}", buf.len(), first_address); + let start_address = FLASH_BASE as u32 + offset; + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + // No need to take lock here as we only have one mut flash reference. unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_write(first_address, buf); + let res = Flash::blocking_write_all(start_address, buf); family::lock(); res } } + unsafe fn blocking_write_all(start_address: u32, buf: &[u8]) -> Result<(), Error> { + family::begin_write(); + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + let res = unsafe { family::blocking_write(address, chunk.try_into().unwrap()) }; + if res.is_err() { + family::end_write(); + return res; + } + address += WRITE_SIZE as u32; + } + + family::end_write(); + Ok(()) + } + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { if to < from || to as usize > FLASH_SIZE { return Err(Error::Size); } - if (from as usize % family::MAX_ERASE_SIZE) != 0 || (to as usize % family::MAX_ERASE_SIZE) != 0 { + + let start_address = FLASH_BASE as u32 + from; + let end_address = FLASH_BASE as u32 + to; + if !family::is_eraseable_range(start_address, end_address) { return Err(Error::Unaligned); } - - let from_address = FLASH_BASE as u32 + from; - let to_address = FLASH_BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(from_address, to_address); + let res = family::blocking_erase(start_address, end_address); family::lock(); res } @@ -101,7 +121,6 @@ pub trait FlashRegion { } let first_address = Self::BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) @@ -115,17 +134,19 @@ pub trait FlashRegion { return Err(Error::Unaligned); } - let first_address = Self::BASE as u32 + offset; - trace!("Writing {} bytes from 0x{:x}", buf.len(), first_address); + let start_address = Self::BASE as u32 + offset; + trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); - critical_section::with(|_| unsafe { + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_write(first_address, buf); + let res = Flash::blocking_write_all(start_address, buf); family::lock(); res - }) + } } fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { @@ -136,18 +157,28 @@ pub trait FlashRegion { return Err(Error::Unaligned); } - let from_address = Self::BASE as u32 + from; - let to_address = Self::BASE as u32 + to; - trace!("Erasing from 0x{:x} to 0x{:x}", from_address, to_address); + let start_address = Self::BASE as u32 + from; + let end_address = Self::BASE as u32 + to; + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - critical_section::with(|_| unsafe { + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(from_address, to_address); + let res = family::blocking_erase(start_address, end_address); family::lock(); res - }) + } + } +} + +fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { + loop { + if let Ok(guard) = REGION_LOCK.try_lock() { + return guard; + } } } From e8fc7a66a344776463026fba8c91231951fcf2b4 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 16:32:32 +0100 Subject: [PATCH 0723/1575] Ensure flash module and FlashRegion trait is always defined --- embassy-stm32/src/flash/other.rs | 7 +++++++ embassy-stm32/src/lib.rs | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 embassy-stm32/src/flash/other.rs diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs new file mode 100644 index 000000000..c9836d095 --- /dev/null +++ b/embassy-stm32/src/flash/other.rs @@ -0,0 +1,7 @@ +pub trait FlashRegion { + const BASE: usize; + const SIZE: usize; + const ERASE_SIZE: usize; + const WRITE_SIZE: usize; + const ERASE_VALUE: u8; +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index eeaa04f67..ddd5c0fdd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -47,6 +47,13 @@ pub mod crc; flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 ))] pub mod flash; +#[cfg(not(any( + flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 +)))] +pub mod flash { + mod other; + pub use other::FlashRegion; +} pub mod pwm; #[cfg(rng)] pub mod rng; From e9a5b31fa83f7a261a79a810666039381553037f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 25 Mar 2023 17:00:52 +0100 Subject: [PATCH 0724/1575] Implement drop for FlashRegions --- embassy-stm32/src/flash/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index c704909ac..1294ace4d 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -108,6 +108,12 @@ impl Drop for Flash<'_> { } } +impl Drop for FlashRegions { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + pub trait FlashRegion { const BASE: usize; const SIZE: usize; From 7be63b3468f72fc684267c90093a00e77cff1bdc Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Sun, 26 Mar 2023 18:14:17 +0300 Subject: [PATCH 0725/1575] fix: spi transfer bug and additions to test Signed-off-by: Lachezar Lechev --- embassy-rp/src/spi.rs | 10 +++++----- tests/rp/src/bin/spi_async.rs | 26 +++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index e6682ad6a..ebd621ecf 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -384,8 +384,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> { - let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); - let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); + let (_, tx_len) = crate::dma::slice_ptr_parts(tx_ptr); + let (_, rx_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); unsafe { self.inner.regs().dmacr().write(|reg| { @@ -402,8 +402,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { unsafe { crate::dma::write(&mut tx_ch, tx_ptr, p.dr().ptr() as *mut _, T::TX_DREQ).await; - if from_len > to_len { - let write_bytes_len = from_len - to_len; + if rx_len > tx_len { + let write_bytes_len = rx_len - tx_len; // write dummy data // this will disable incrementation of the buffers crate::dma::write_repeated(tx_ch, p.dr().ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await @@ -420,7 +420,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { join(tx_transfer, rx_transfer).await; // if tx > rx we should clear any overflow of the FIFO SPI buffer - if from_len > to_len { + if tx_len > rx_len { let p = self.inner.regs(); unsafe { while p.sr().read().bsy() {} diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index e3fe6e84c..2e22c9de7 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs @@ -33,9 +33,11 @@ async fn main(_spawner: Spawner) { { let tx_buf = [7_u8, 8, 9, 10, 11, 12]; - let mut rx_buf = [0_u8, 3]; + let mut rx_buf = [0_u8; 3]; spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); assert_eq!(rx_buf, tx_buf[..3]); + + defmt::info!("tx > rx buffer - OK"); } // we make sure to that clearing FIFO works after the uneven buffers @@ -45,18 +47,36 @@ async fn main(_spawner: Spawner) { let tx_buf = [13_u8, 14, 15, 16, 17, 18]; let mut rx_buf = [0_u8; 6]; spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); - assert_eq!(rx_buf, tx_buf); + + defmt::info!("buffer rx length == tx length - OK"); } // rx > tx buffer { let tx_buf = [19_u8, 20, 21]; let mut rx_buf = [0_u8; 6]; + + // we should have written dummy data to tx buffer to sync clock. spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); - assert_eq!(rx_buf[..3], tx_buf, "only the first 3 TX bytes should have been received in the RX buffer"); + assert_eq!( + rx_buf[..3], + tx_buf, + "only the first 3 TX bytes should have been received in the RX buffer" + ); assert_eq!(rx_buf[3..], [0, 0, 0], "the rest of the RX bytes should be empty"); + defmt::info!("buffer rx length > tx length - OK"); + } + + // equal rx & tx buffers + { + let tx_buf = [22_u8, 23, 24, 25, 26, 27]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + + assert_eq!(rx_buf, tx_buf); + defmt::info!("buffer rx length = tx length - OK"); } info!("Test OK"); From 2c45b5c5193adc27d865cab27e1ac000aaae7899 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 26 Mar 2023 23:32:12 +0200 Subject: [PATCH 0726/1575] sync/pipe: update to clarify docs that it is byte-oriented. There was some language copypasted from Channel talking about "messages" or "values", that is not really accurate with Pipe. --- embassy-sync/src/pipe.rs | 71 ++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 1977005fb..ee27cdec8 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -32,16 +32,16 @@ impl<'p, M, const N: usize> Writer<'p, M, N> where M: RawMutex, { - /// Writes a value. + /// Write some bytes to the pipe. /// /// See [`Pipe::write()`] pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> { self.pipe.write(buf) } - /// Attempt to immediately write a message. + /// Attempt to immediately write some bytes to the pipe. /// - /// See [`Pipe::write()`] + /// See [`Pipe::try_write()`] pub fn try_write(&self, buf: &[u8]) -> Result { self.pipe.try_write(buf) } @@ -95,16 +95,16 @@ impl<'p, M, const N: usize> Reader<'p, M, N> where M: RawMutex, { - /// Reads a value. + /// Read some bytes from the pipe. /// /// See [`Pipe::read()`] pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> { self.pipe.read(buf) } - /// Attempt to immediately read a message. + /// Attempt to immediately read some bytes from the pipe. /// - /// See [`Pipe::read()`] + /// See [`Pipe::try_read()`] pub fn try_read(&self, buf: &mut [u8]) -> Result { self.pipe.try_read(buf) } @@ -221,12 +221,11 @@ impl PipeState { } } -/// A bounded pipe for communicating between asynchronous tasks +/// A bounded byte-oriented pipe for communicating between asynchronous tasks /// with backpressure. /// -/// The pipe will buffer up to the provided number of messages. Once the -/// buffer is full, attempts to `write` new messages will wait until a message is -/// read from the pipe. +/// The pipe will buffer up to the provided number of bytes. Once the +/// buffer is full, attempts to `write` new bytes will wait until buffer space is freed up. /// /// All data written will become available in the same order as it was written. pub struct Pipe @@ -277,40 +276,56 @@ where Reader { pipe: self } } - /// Write a value, waiting until there is capacity. + /// Write some bytes to the pipe. /// - /// Writeing completes when the value has been pushed to the pipe's queue. - /// This doesn't mean the value has been read yet. + /// This method writes a nonzero amount of bytes from `buf` into the pipe, and + /// returns the amount of bytes written. + /// + /// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full, + /// this method will wait until it is. See [`try_write`](Self::try_write) for a variant that + /// returns an error instead of waiting. + /// + /// It is not guaranteed that all bytes in the buffer are written, even if there's enough + /// free space in the pipe buffer for all. In other words, it is possible for `write` to return + /// without writing all of `buf` (returning a number less than `buf.len()`) and still leave + /// free space in the pipe buffer. You should always `write` in a loop, or use helpers like + /// `write_all` from the `embedded-io` crate. pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> { WriteFuture { pipe: self, buf } } - /// Attempt to immediately write a message. + /// Attempt to immediately write some bytes to the pipe. /// - /// This method differs from [`write`](Pipe::write) by returning immediately if the pipe's - /// buffer is full, instead of waiting. - /// - /// # Errors - /// - /// If the pipe capacity has been reached, i.e., the pipe has `n` - /// buffered values where `n` is the argument passed to [`Pipe`], then an - /// error is returned. + /// This method will either write a nonzero amount of bytes to the pipe immediately, + /// or return an error if the pipe is empty. See [`write`](Self::write) for a variant + /// that waits instead of returning an error. pub fn try_write(&self, buf: &[u8]) -> Result { self.lock(|c| c.try_write(buf)) } - /// Receive the next value. + /// Read some bytes from the pipe. /// - /// If there are no messages in the pipe's buffer, this method will - /// wait until a message is written. + /// This method reads a nonzero amount of bytes from the pipe into `buf` and + /// returns the amount of bytes read. + /// + /// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty, + /// this method will wait until it is. See [`try_read`](Self::try_read) for a variant that + /// returns an error instead of waiting. + /// + /// It is not guaranteed that all bytes in the buffer are read, even if there's enough + /// space in `buf` for all. In other words, it is possible for `read` to return + /// without filling `buf` (returning a number less than `buf.len()`) and still leave bytes + /// in the pipe buffer. You should always `read` in a loop, or use helpers like + /// `read_exact` from the `embedded-io` crate. pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> { ReadFuture { pipe: self, buf } } - /// Attempt to immediately read a message. + /// Attempt to immediately read some bytes from the pipe. /// - /// This method will either read a message from the pipe immediately or return an error - /// if the pipe is empty. + /// This method will either read a nonzero amount of bytes from the pipe immediately, + /// or return an error if the pipe is empty. See [`read`](Self::read) for a variant + /// that waits instead of returning an error. pub fn try_read(&self, buf: &mut [u8]) -> Result { self.lock(|c| c.try_read(buf)) } From 805bca1f5aab8f95bf37007eb9be9016bc0dd8c1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Mar 2023 00:20:24 +0200 Subject: [PATCH 0727/1575] executor: deduplicate doc comments. --- embassy-executor/src/raw/mod.rs | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 938492c21..0120334b6 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -337,15 +337,6 @@ impl SyncExecutor { } } - /// Spawn a task in this executor. - /// - /// # Safety - /// - /// `task` must be a valid pointer to an initialized but not-already-spawned task. - /// - /// It is OK to use `unsafe` to call this from a thread that's not the executor thread. - /// In this case, the task's Future must be Send. This is because this is effectively - /// sending the task to the executor thread. pub(super) unsafe fn spawn(&'static self, task: TaskRef) { task.header().executor.set(Some(self)); @@ -357,23 +348,9 @@ impl SyncExecutor { }) } - /// Poll all queued tasks in this executor. - /// - /// This loops over all tasks that are queued to be polled (i.e. they're - /// freshly spawned or they've been woken). Other tasks are not polled. - /// - /// You must call `poll` after receiving a call to `signal_fn`. It is OK - /// to call `poll` even when not requested by `signal_fn`, but it wastes - /// energy. - /// /// # Safety /// - /// You must NOT call `poll` reentrantly on the same executor. - /// - /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you - /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to - /// somehow schedule for `poll()` to be called later, at a time you know for sure there's - /// no `poll()` already running. + /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { #[allow(clippy::never_loop)] loop { From 21400da073d7173e4c2445cbbcd2cd430f120ad1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Mar 2023 00:22:00 +0200 Subject: [PATCH 0728/1575] executor: Use AtomicPtr for signal_ctx, removes 1 unsafe. --- embassy-executor/src/raw/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 0120334b6..15ff18fc8 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -18,6 +18,7 @@ use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; +use core::sync::atomic::AtomicPtr; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, Ordering}; @@ -288,13 +289,10 @@ impl TaskPool { } } -struct SignalCtx(*mut ()); -unsafe impl Sync for SignalCtx {} - pub(crate) struct SyncExecutor { run_queue: RunQueue, signal_fn: fn(*mut ()), - signal_ctx: SignalCtx, + signal_ctx: AtomicPtr<()>, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue: timer_queue::TimerQueue, @@ -312,7 +310,7 @@ impl SyncExecutor { Self { run_queue: RunQueue::new(), signal_fn, - signal_ctx: SignalCtx(signal_ctx), + signal_ctx: AtomicPtr::new(signal_ctx), #[cfg(feature = "integrated-timers")] timer_queue: timer_queue::TimerQueue::new(), @@ -333,7 +331,7 @@ impl SyncExecutor { trace::task_ready_begin(task.as_ptr() as u32); if self.run_queue.enqueue(cs, task) { - (self.signal_fn)(self.signal_ctx.0) + (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed)) } } From cffc3fc7956570c66bf1bd259a4f68a8ca02fe58 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Mar 2023 03:33:06 +0200 Subject: [PATCH 0729/1575] Fix build with log. --- src/bus.rs | 12 ++++++------ src/control.rs | 7 ++++--- src/events.rs | 2 +- src/fmt.rs | 29 +++++++++++++++++++++++++++++ src/runner.rs | 29 ++++++++++++++++++----------- src/structs.rs | 8 ++++---- 6 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index f77b890df..7700a832a 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -49,30 +49,30 @@ where while self .read32_swapped(REG_BUS_TEST_RO) - .inspect(|v| defmt::trace!("{:#x}", v)) + .inspect(|v| trace!("{:#x}", v)) .await != FEEDBEAD {} self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await; let val = self.read32_swapped(REG_BUS_TEST_RW).await; - defmt::trace!("{:#x}", val); + trace!("{:#x}", val); assert_eq!(val, TEST_PATTERN); let val = self.read32_swapped(REG_BUS_CTRL).await; - defmt::trace!("{:#010b}", (val & 0xff)); + trace!("{:#010b}", (val & 0xff)); // 32-bit word length, little endian (which is the default endianess). self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await; let val = self.read8(FUNC_BUS, REG_BUS_CTRL).await; - defmt::trace!("{:#b}", val); + trace!("{:#b}", val); let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await; - defmt::trace!("{:#x}", val); + trace!("{:#x}", val); assert_eq!(val, FEEDBEAD); let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await; - defmt::trace!("{:#x}", val); + trace!("{:#x}", val); assert_eq!(val, TEST_PATTERN); } diff --git a/src/control.rs b/src/control.rs index 7f1c9fe86..8bfa033ba 100644 --- a/src/control.rs +++ b/src/control.rs @@ -9,6 +9,7 @@ use embassy_time::{Duration, Timer}; pub use crate::bus::SpiBusCyw43; use crate::consts::*; use crate::events::{Event, EventQueue}; +use crate::fmt::Bytes; use crate::structs::*; use crate::{countries, IoctlState, IoctlType, PowerManagementMode}; @@ -75,7 +76,7 @@ impl<'a> Control<'a> { // read MAC addr. let mut mac_addr = [0; 6]; assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); - info!("mac addr: {:02x}", mac_addr); + info!("mac addr: {:02x}", Bytes(&mac_addr)); let country = countries::WORLD_WIDE_XX; let country_info = CountryInfo { @@ -205,7 +206,7 @@ impl<'a> Control<'a> { let msg = subscriber.next_message_pure().await; if msg.event_type == Event::AUTH && msg.status != 0 { // retry - defmt::warn!("JOIN failed with status={}", msg.status); + warn!("JOIN failed with status={}", msg.status); self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; } else if msg.event_type == Event::JOIN && msg.status == 0 { // successful join @@ -241,7 +242,7 @@ impl<'a> Control<'a> { } async fn set_iovar(&mut self, name: &str, val: &[u8]) { - info!("set {} = {:02x}", name, val); + info!("set {} = {:02x}", name, Bytes(val)); let mut buf = [0; 64]; buf[..name.len()].copy_from_slice(name.as_bytes()); diff --git a/src/events.rs b/src/events.rs index 9e6bb9625..b9c8cca6b 100644 --- a/src/events.rs +++ b/src/events.rs @@ -6,7 +6,7 @@ use core::num; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; -#[derive(Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum Event { diff --git a/src/fmt.rs b/src/fmt.rs index f8bb0a035..5730447b3 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,6 +1,8 @@ #![macro_use] #![allow(unused_macros)] +use core::fmt::{Debug, Display, LowerHex}; + #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); @@ -226,3 +228,30 @@ impl Try for Result { self } } + +pub struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/src/runner.rs b/src/runner.rs index 5d840bc59..9945af3fc 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -11,6 +11,7 @@ use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; use crate::consts::*; use crate::events::{EventQueue, EventStatus}; +use crate::fmt::Bytes; use crate::nvram::NVRAM; use crate::structs::*; use crate::{events, Core, IoctlState, IoctlType, CHIP, MTU}; @@ -23,6 +24,7 @@ struct LogState { buf_count: usize, } +#[cfg(feature = "firmware-logs")] impl Default for LogState { fn default() -> Self { Self { @@ -175,7 +177,6 @@ where let mut shared = [0; SharedMemData::SIZE]; self.bus.bp_read(shared_addr, &mut shared).await; let shared = SharedMemData::from_bytes(&shared); - info!("shared: {:08x}", shared); self.log.addr = shared.console_addr + 8; } @@ -238,7 +239,7 @@ where warn!("TX stalled"); } else { if let Some(packet) = self.ch.try_tx_buf() { - trace!("tx pkt {:02x}", &packet[..packet.len().min(48)]); + trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); @@ -275,7 +276,7 @@ where let total_len = (total_len + 3) & !3; // round up to 4byte - trace!(" {:02x}", &buf8[..total_len.min(48)]); + trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); self.bus.wlan_write(&buf[..(total_len / 4)]).await; self.ch.tx_done(); @@ -295,7 +296,7 @@ where if status & STATUS_F2_PKT_AVAILABLE != 0 { let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; self.bus.wlan_read(&mut buf, len).await; - trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]); + trace!("rx {:02x}", Bytes(&slice8_mut(&mut buf)[..(len as usize).min(48)])); self.rx(&slice8_mut(&mut buf)[..len as usize]); } } @@ -343,11 +344,11 @@ where if cdc_header.id == self.ioctl_id { if cdc_header.status != 0 { // TODO: propagate error instead - panic!("IOCTL error {=i32}", cdc_header.status as i32); + panic!("IOCTL error {}", cdc_header.status as i32); } let resp_len = cdc_header.len as usize; - info!("IOCTL Response: {:02x}", &payload[CdcHeader::SIZE..][..resp_len]); + info!("IOCTL Response: {:02x}", Bytes(&payload[CdcHeader::SIZE..][..resp_len])); (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); self.ioctl_state.set(IoctlState::Done { resp_len }); @@ -365,7 +366,7 @@ where return; } let bcd_packet = &payload[packet_start..]; - trace!(" {:02x}", &bcd_packet[..(bcd_packet.len() as usize).min(36)]); + trace!(" {:02x}", Bytes(&bcd_packet[..(bcd_packet.len() as usize).min(36)])); let mut event_packet = EventPacket::from_bytes(&bcd_packet[..EventPacket::SIZE].try_into().unwrap()); event_packet.byteswap(); @@ -382,7 +383,8 @@ where if event_packet.hdr.oui != BROADCOM_OUI { warn!( "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", - event_packet.hdr.oui, BROADCOM_OUI + Bytes(&event_packet.hdr.oui), + Bytes(BROADCOM_OUI) ); return; } @@ -405,7 +407,12 @@ where let evt_type = events::Event::from(event_packet.msg.event_type as u8); let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; - debug!("=== EVENT {}: {} {:02x}", evt_type, event_packet.msg, evt_data); + debug!( + "=== EVENT {:?}: {:?} {:02x}", + evt_type, + event_packet.msg, + Bytes(evt_data) + ); if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { self.events.publish_immediate(EventStatus { @@ -424,7 +431,7 @@ where return; } let packet = &payload[packet_start..]; - trace!("rx pkt {:02x}", &packet[..(packet.len() as usize).min(48)]); + trace!("rx pkt {:02x}", Bytes(&packet[..(packet.len() as usize).min(48)])); match self.ch.try_rx_buf() { Some(buf) => { @@ -490,7 +497,7 @@ where let total_len = (total_len + 3) & !3; // round up to 4byte - trace!(" {:02x}", &buf8[..total_len.min(48)]); + trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); self.bus.wlan_write(&buf[..total_len / 4]).await; } diff --git a/src/structs.rs b/src/structs.rs index 41a340661..e16808f30 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -44,7 +44,7 @@ pub struct SharedMemLog { } impl_bytes!(SharedMemLog); -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct SdpcmHeader { @@ -67,7 +67,7 @@ pub struct SdpcmHeader { } impl_bytes!(SdpcmHeader); -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct CdcHeader { @@ -82,7 +82,7 @@ impl_bytes!(CdcHeader); pub const BDC_VERSION: u8 = 2; pub const BDC_VERSION_SHIFT: u8 = 4; -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct BcdHeader { @@ -129,7 +129,7 @@ impl EventHeader { } } -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] pub struct EventMessage { From ed601d439a222d057f7f19d08ac2cc7d519e831a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Mar 2023 03:33:20 +0200 Subject: [PATCH 0730/1575] Add CI. --- .github/workflows/rust.yml | 29 +++++++++++++++++++++++++++++ Cargo.toml | 6 ++++++ ci.sh | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 .github/workflows/rust.yml create mode 100755 ci.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 000000000..2cd3ba5d7 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,29 @@ +name: Rust + +on: + push: + branches: [master] + pull_request: + branches: [master] + merge_group: + +env: + CARGO_TERM_COLOR: always + +jobs: + build-nightly: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Check fmt + run: cargo fmt -- --check + - name: Build + run: ./ci.sh diff --git a/Cargo.toml b/Cargo.toml index 3bdeb0cfb..a307a6cc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,9 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } num_enum = { version = "0.5.7", default-features = false } + +[patch.crates-io] +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } diff --git a/ci.sh b/ci.sh new file mode 100755 index 000000000..1b33564fb --- /dev/null +++ b/ci.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euxo pipefail + +# build examples +#================== + +(cd examples/rpi-pico-w; WIFI_NETWORK=foo WIFI_PASSWORD=bar cargo build --release) + + +# build with log/defmt combinations +#===================================== + +cargo build --target thumbv6m-none-eabi --features '' +cargo build --target thumbv6m-none-eabi --features 'log' +cargo build --target thumbv6m-none-eabi --features 'defmt' +cargo build --target thumbv6m-none-eabi --features 'log,firmware-logs' +cargo build --target thumbv6m-none-eabi --features 'defmt,firmware-logs' From 472138122542d6c47e666c8e6bb16cc6635ede0a Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 2 Mar 2023 19:22:43 +0100 Subject: [PATCH 0731/1575] also wait for join event in join_open --- src/control.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/control.rs b/src/control.rs index 8bfa033ba..79677b554 100644 --- a/src/control.rs +++ b/src/control.rs @@ -134,7 +134,6 @@ impl<'a> Control<'a> { Timer::after(Duration::from_millis(100)).await; self.state_ch.set_ethernet_address(mac_addr); - self.state_ch.set_link_state(LinkState::Up); // TODO do on join/leave info!("INIT DONE"); } @@ -164,10 +163,8 @@ impl<'a> Control<'a> { ssid: [0; 32], }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) - .await; // set_ssid - info!("JOINED"); + self.wait_for_join(i).await; } pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) { @@ -199,21 +196,29 @@ impl<'a> Control<'a> { }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + self.wait_for_join(i).await; + } + + async fn wait_for_join(&mut self, i: SsidInfo) { let mut subscriber = self.event_sub.subscriber().unwrap(); - self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; // set_ssid + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) + .await; + // set_ssid loop { let msg = subscriber.next_message_pure().await; if msg.event_type == Event::AUTH && msg.status != 0 { // retry warn!("JOIN failed with status={}", msg.status); - self.ioctl(IoctlType::Set, 26, 0, &mut i.to_bytes()).await; + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) + .await; } else if msg.event_type == Event::JOIN && msg.status == 0 { // successful join break; } } + self.state_ch.set_link_state(LinkState::Up); info!("JOINED"); } From 6f547cf05ddd1a27c8ec4e107ac227f7f9520ba6 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 2 Mar 2023 15:34:08 +0100 Subject: [PATCH 0732/1575] asyncify outgoing events --- src/control.rs | 32 +++----------- src/ioctl.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 32 ++------------ src/runner.rs | 118 +++++++++++++++++++++++++++++-------------------- 4 files changed, 190 insertions(+), 103 deletions(-) create mode 100644 src/ioctl.rs diff --git a/src/control.rs b/src/control.rs index 8bfa033ba..98f678fbc 100644 --- a/src/control.rs +++ b/src/control.rs @@ -1,8 +1,6 @@ -use core::cell::Cell; use core::cmp::{max, min}; use ch::driver::LinkState; -use embassy_futures::yield_now; use embassy_net_driver_channel as ch; use embassy_time::{Duration, Timer}; @@ -10,21 +8,18 @@ pub use crate::bus::SpiBusCyw43; use crate::consts::*; use crate::events::{Event, EventQueue}; use crate::fmt::Bytes; +use crate::ioctl::{IoctlState, IoctlType}; use crate::structs::*; -use crate::{countries, IoctlState, IoctlType, PowerManagementMode}; +use crate::{countries, PowerManagementMode}; pub struct Control<'a> { state_ch: ch::StateRunner<'a>, event_sub: &'a EventQueue, - ioctl_state: &'a Cell, + ioctl_state: &'a IoctlState, } impl<'a> Control<'a> { - pub(crate) fn new( - state_ch: ch::StateRunner<'a>, - event_sub: &'a EventQueue, - ioctl_state: &'a Cell, - ) -> Self { + pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a EventQueue, ioctl_state: &'a IoctlState) -> Self { Self { state_ch, event_sub, @@ -278,23 +273,8 @@ impl<'a> Control<'a> { } async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - // TODO cancel ioctl on future drop. - - while !matches!(self.ioctl_state.get(), IoctlState::Idle) { - yield_now().await; - } - - self.ioctl_state.set(IoctlState::Pending { kind, cmd, iface, buf }); - - let resp_len = loop { - if let IoctlState::Done { resp_len } = self.ioctl_state.get() { - break resp_len; - } - yield_now().await; - }; - - self.ioctl_state.set(IoctlState::Idle); - + self.ioctl_state.do_ioctl(kind, cmd, iface, buf).await; + let resp_len = self.ioctl_state.wait_complete().await; resp_len } } diff --git a/src/ioctl.rs b/src/ioctl.rs new file mode 100644 index 000000000..2d4cdb871 --- /dev/null +++ b/src/ioctl.rs @@ -0,0 +1,111 @@ +use core::cell::{Cell, RefCell}; +use core::future::poll_fn; +use core::task::{Poll, Waker}; + +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::waitqueue::WakerRegistration; + +#[derive(Clone, Copy)] +pub enum IoctlType { + Get = 0, + Set = 2, +} + +#[derive(Clone, Copy)] +pub struct PendingIoctl { + pub buf: *mut [u8], + pub kind: IoctlType, + pub cmd: u32, + pub iface: u32, +} + +#[derive(Clone, Copy)] +enum IoctlStateInner { + Pending(PendingIoctl), + Sent { buf: *mut [u8] }, + Done { resp_len: usize }, +} + +pub struct IoctlState { + state: Cell, + wakers: Mutex>, +} + +impl IoctlState { + pub fn new() -> Self { + Self { + state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), + wakers: Mutex::new(RefCell::default()), + } + } + + fn wake_control(&self) { + self.wakers.lock(|f| { + f.borrow_mut().0.wake(); + }) + } + + fn register_control(&self, waker: &Waker) { + self.wakers.lock(|f| f.borrow_mut().0.register(waker)); + } + + fn wake_runner(&self) { + self.wakers.lock(|f| { + f.borrow_mut().1.wake(); + }) + } + + fn register_runner(&self, waker: &Waker) { + self.wakers.lock(|f| f.borrow_mut().1.register(waker)); + } + + pub async fn wait_complete(&self) -> usize { + poll_fn(|cx| { + if let IoctlStateInner::Done { resp_len } = self.state.get() { + Poll::Ready(resp_len) + } else { + self.register_control(cx.waker()); + Poll::Pending + } + }) + .await + } + + pub async fn wait_pending(&self) -> PendingIoctl { + let pending = poll_fn(|cx| { + if let IoctlStateInner::Pending(pending) = self.state.get() { + warn!("found pending ioctl"); + Poll::Ready(pending) + } else { + self.register_runner(cx.waker()); + Poll::Pending + } + }) + .await; + + self.state.set(IoctlStateInner::Sent { buf: pending.buf }); + pending + } + + pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { + warn!("doing ioctl"); + self.state + .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); + self.wake_runner(); + self.wait_complete().await + } + + pub fn ioctl_done(&self, response: &[u8]) { + if let IoctlStateInner::Sent { buf } = self.state.get() { + warn!("ioctl complete"); + // TODO fix this + (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); + + self.state.set(IoctlStateInner::Done { + resp_len: response.len(), + }); + self.wake_control(); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index af8f74a6d..069ca40f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,17 +11,17 @@ mod bus; mod consts; mod countries; mod events; +mod ioctl; mod structs; mod control; mod nvram; mod runner; -use core::cell::Cell; - use embassy_net_driver_channel as ch; use embedded_hal_1::digital::OutputPin; use events::EventQueue; +use ioctl::IoctlState; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; @@ -30,12 +30,6 @@ pub use crate::runner::Runner; const MTU: usize = 1514; -#[derive(Clone, Copy)] -pub enum IoctlType { - Get = 0, - Set = 2, -} - #[allow(unused)] #[derive(Clone, Copy, PartialEq, Eq)] enum Core { @@ -106,26 +100,8 @@ const CHIP: Chip = Chip { chanspec_ctl_sb_mask: 0x0700, }; -#[derive(Clone, Copy)] -enum IoctlState { - Idle, - - Pending { - kind: IoctlType, - cmd: u32, - iface: u32, - buf: *mut [u8], - }, - Sent { - buf: *mut [u8], - }, - Done { - resp_len: usize, - }, -} - pub struct State { - ioctl_state: Cell, + ioctl_state: IoctlState, ch: ch::State, events: EventQueue, } @@ -133,7 +109,7 @@ pub struct State { impl State { pub fn new() -> Self { Self { - ioctl_state: Cell::new(IoctlState::Idle), + ioctl_state: IoctlState::new(), ch: ch::State::new(), events: EventQueue::new(), } diff --git a/src/runner.rs b/src/runner.rs index 9945af3fc..4abccf48b 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,6 +1,6 @@ -use core::cell::Cell; use core::slice; +use embassy_futures::select::{select3, Either3}; use embassy_futures::yield_now; use embassy_net_driver_channel as ch; use embassy_sync::pubsub::PubSubBehavior; @@ -12,9 +12,10 @@ pub use crate::bus::SpiBusCyw43; use crate::consts::*; use crate::events::{EventQueue, EventStatus}; use crate::fmt::Bytes; +use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; use crate::nvram::NVRAM; use crate::structs::*; -use crate::{events, Core, IoctlState, IoctlType, CHIP, MTU}; +use crate::{events, Core, CHIP, MTU}; #[cfg(feature = "firmware-logs")] struct LogState { @@ -40,7 +41,7 @@ pub struct Runner<'a, PWR, SPI> { ch: ch::Runner<'a, MTU>, bus: Bus, - ioctl_state: &'a Cell, + ioctl_state: &'a IoctlState, ioctl_id: u16, sdpcm_seq: u8, sdpcm_seq_max: u8, @@ -59,7 +60,7 @@ where pub(crate) fn new( ch: ch::Runner<'a, MTU>, bus: Bus, - ioctl_state: &'a Cell, + ioctl_state: &'a IoctlState, events: &'a EventQueue, ) -> Self { Self { @@ -226,19 +227,22 @@ where #[cfg(feature = "firmware-logs")] self.log_read().await; - // Send stuff - // TODO flow control not yet complete - if !self.has_credit() { - warn!("TX stalled"); - } else { - if let IoctlState::Pending { kind, cmd, iface, buf } = self.ioctl_state.get() { - self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; - self.ioctl_state.set(IoctlState::Sent { buf }); - } - if !self.has_credit() { - warn!("TX stalled"); - } else { - if let Some(packet) = self.ch.try_tx_buf() { + let ev = || async { + // TODO use IRQs + yield_now().await; + }; + + if self.has_credit() { + let ioctl = self.ioctl_state.wait_pending(); + let tx = self.ch.tx_buf(); + + match select3(ioctl, tx, ev()).await { + Either3::First(PendingIoctl { buf, kind, cmd, iface }) => { + warn!("ioctl"); + self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; + } + Either3::Second(packet) => { + warn!("packet"); trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); let mut buf = [0; 512]; @@ -281,28 +285,46 @@ where self.bus.wlan_write(&buf[..(total_len / 4)]).await; self.ch.tx_done(); } + Either3::Third(()) => { + // Receive stuff + let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; + + if irq & IRQ_F2_PACKET_AVAILABLE != 0 { + let mut status = 0xFFFF_FFFF; + while status == 0xFFFF_FFFF { + status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; + } + + if status & STATUS_F2_PKT_AVAILABLE != 0 { + let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; + self.bus.wlan_read(&mut buf, len).await; + trace!("rx {:02x}", Bytes(&slice8_mut(&mut buf)[..(len as usize).min(48)])); + self.rx(&slice8_mut(&mut buf)[..len as usize]); + } + } + } + } + } else { + warn!("TX stalled"); + ev().await; + + // Receive stuff + let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; + + if irq & IRQ_F2_PACKET_AVAILABLE != 0 { + let mut status = 0xFFFF_FFFF; + while status == 0xFFFF_FFFF { + status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; + } + + if status & STATUS_F2_PKT_AVAILABLE != 0 { + let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; + self.bus.wlan_read(&mut buf, len).await; + trace!("rx {:02x}", Bytes(&slice8_mut(&mut buf)[..(len as usize).min(48)])); + self.rx(&slice8_mut(&mut buf)[..len as usize]); + } } } - - // Receive stuff - let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; - - if irq & IRQ_F2_PACKET_AVAILABLE != 0 { - let mut status = 0xFFFF_FFFF; - while status == 0xFFFF_FFFF { - status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; - } - - if status & STATUS_F2_PKT_AVAILABLE != 0 { - let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(&mut buf, len).await; - trace!("rx {:02x}", Bytes(&slice8_mut(&mut buf)[..(len as usize).min(48)])); - self.rx(&slice8_mut(&mut buf)[..len as usize]); - } - } - - // TODO use IRQs - yield_now().await; } } @@ -340,19 +362,17 @@ where let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); trace!(" {:?}", cdc_header); - if let IoctlState::Sent { buf } = self.ioctl_state.get() { - if cdc_header.id == self.ioctl_id { - if cdc_header.status != 0 { - // TODO: propagate error instead - panic!("IOCTL error {}", cdc_header.status as i32); - } - - let resp_len = cdc_header.len as usize; - info!("IOCTL Response: {:02x}", Bytes(&payload[CdcHeader::SIZE..][..resp_len])); - - (unsafe { &mut *buf }[..resp_len]).copy_from_slice(&payload[CdcHeader::SIZE..][..resp_len]); - self.ioctl_state.set(IoctlState::Done { resp_len }); + if cdc_header.id == self.ioctl_id { + if cdc_header.status != 0 { + // TODO: propagate error instead + panic!("IOCTL error {}", cdc_header.status as i32); } + + let resp_len = cdc_header.len as usize; + let response = &payload[CdcHeader::SIZE..][..resp_len]; + info!("IOCTL Response: {:02x}", Bytes(response)); + + self.ioctl_state.ioctl_done(response); } } CHANNEL_TYPE_EVENT => { From a33774ec5174d469639a63fe112907cd4e596fff Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 27 Mar 2023 12:36:31 +0200 Subject: [PATCH 0733/1575] Update stm32-metapac --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/adc/sample_time.rs | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b66d724d5..14ec3d70a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { version = "1", features = ["rt"] } +stm32-metapac = { version = "2", features = ["rt"] } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -72,7 +72,7 @@ embedded-io = { version = "0.4.0", features = ["async"], optional = true } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "1", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} [features] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 60ba80048..bc5fb1d6f 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,5 +1,5 @@ macro_rules! impl_sample_time { - ($default_doc:expr, $default:ident, $pac:ty, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { + ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum SampleTime { @@ -9,10 +9,10 @@ macro_rules! impl_sample_time { )* } - impl From for $pac { - fn from(sample_time: SampleTime) -> $pac { + impl From for crate::pac::adc::vals::SampleTime { + fn from(sample_time: SampleTime) -> crate::pac::adc::vals::SampleTime { match sample_time { - $(SampleTime::$variant => <$pac>::$pac_variant),* + $(SampleTime::$variant => crate::pac::adc::vals::SampleTime::$pac_variant),* } } } @@ -29,7 +29,6 @@ macro_rules! impl_sample_time { impl_sample_time!( "1.5", Cycles1_5, - crate::pac::adc::vals::SampleTime, ( ("1.5", Cycles1_5, CYCLES1_5), ("7.5", Cycles7_5, CYCLES7_5), @@ -46,7 +45,6 @@ impl_sample_time!( impl_sample_time!( "3", Cycles3, - crate::pac::adc::vals::Smp, ( ("3", Cycles3, CYCLES3), ("15", Cycles15, CYCLES15), @@ -63,7 +61,6 @@ impl_sample_time!( impl_sample_time!( "2.5", Cycles2_5, - crate::pac::adc::vals::SampleTime, ( ("2.5", Cycles2_5, CYCLES2_5), ("6.5", Cycles6_5, CYCLES6_5), @@ -80,7 +77,6 @@ impl_sample_time!( impl_sample_time!( "1.5", Cycles1_5, - crate::pac::adc::vals::SampleTime, ( ("1.5", Cycles1_5, CYCLES1_5), ("3.5", Cycles3_5, CYCLES3_5), @@ -97,7 +93,6 @@ impl_sample_time!( impl_sample_time!( "1.5", Cycles1_5, - crate::pac::adc::vals::Smp, ( ("1.5", Cycles1_5, CYCLES1_5), ("2.5", Cycles2_5, CYCLES2_5), From 4c521044131279aa36f7e21dbc6ec566703a57c6 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Mon, 27 Mar 2023 12:40:27 +0200 Subject: [PATCH 0734/1575] simplify ioctl waker code --- src/ioctl.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ioctl.rs b/src/ioctl.rs index 2d4cdb871..6a7465593 100644 --- a/src/ioctl.rs +++ b/src/ioctl.rs @@ -2,8 +2,6 @@ use core::cell::{Cell, RefCell}; use core::future::poll_fn; use core::task::{Poll, Waker}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::blocking_mutex::Mutex; use embassy_sync::waitqueue::WakerRegistration; #[derive(Clone, Copy)] @@ -27,37 +25,39 @@ enum IoctlStateInner { Done { resp_len: usize }, } +#[derive(Default)] +struct Wakers { + control: WakerRegistration, + runner: WakerRegistration, +} + pub struct IoctlState { state: Cell, - wakers: Mutex>, + wakers: RefCell, } impl IoctlState { pub fn new() -> Self { Self { state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), - wakers: Mutex::new(RefCell::default()), + wakers: Default::default(), } } fn wake_control(&self) { - self.wakers.lock(|f| { - f.borrow_mut().0.wake(); - }) + self.wakers.borrow_mut().control.wake(); } fn register_control(&self, waker: &Waker) { - self.wakers.lock(|f| f.borrow_mut().0.register(waker)); + self.wakers.borrow_mut().control.register(waker); } fn wake_runner(&self) { - self.wakers.lock(|f| { - f.borrow_mut().1.wake(); - }) + self.wakers.borrow_mut().runner.wake(); } fn register_runner(&self, waker: &Waker) { - self.wakers.lock(|f| f.borrow_mut().1.register(waker)); + self.wakers.borrow_mut().runner.register(waker); } pub async fn wait_complete(&self) -> usize { From c7646eb699e194a7c692b95b49adc76d6d3295ea Mon Sep 17 00:00:00 2001 From: kbleeke Date: Mon, 27 Mar 2023 12:40:40 +0200 Subject: [PATCH 0735/1575] bring back TODO note about dropping ioctls --- src/control.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/control.rs b/src/control.rs index 98f678fbc..f15a3d3f2 100644 --- a/src/control.rs +++ b/src/control.rs @@ -273,6 +273,8 @@ impl<'a> Control<'a> { } async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { + // TODO cancel ioctl on future drop. + self.ioctl_state.do_ioctl(kind, cmd, iface, buf).await; let resp_len = self.ioctl_state.wait_complete().await; resp_len From a2272dda08a2d1625eef0b79fcd80afc8a1e174a Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 2 Mar 2023 19:02:00 +0100 Subject: [PATCH 0736/1575] status and irq flags formatting with defmt --- src/consts.rs | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/consts.rs b/src/consts.rs index bee706600..140cb4b6d 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,4 +1,5 @@ #![allow(unused)] + pub(crate) const FUNC_BUS: u32 = 0; pub(crate) const FUNC_BACKPLANE: u32 = 1; pub(crate) const FUNC_WLAN: u32 = 2; @@ -103,3 +104,149 @@ pub(crate) const WRITE: bool = true; pub(crate) const READ: bool = false; pub(crate) const INC_ADDR: bool = true; pub(crate) const FIXED_ADDR: bool = false; + +#[allow(dead_code)] +pub(crate) struct FormatStatus(pub u32); + +#[cfg(feature = "defmt")] +impl defmt::Format for FormatStatus { + fn format(&self, fmt: defmt::Formatter) { + macro_rules! implm { + ($($name:ident),*) => { + $( + if self.0 & $name > 0 { + defmt::write!(fmt, " | {}", &stringify!($name)[7..]); + } + )* + }; + } + + implm!( + STATUS_DATA_NOT_AVAILABLE, + STATUS_UNDERFLOW, + STATUS_OVERFLOW, + STATUS_F2_INTR, + STATUS_F3_INTR, + STATUS_F2_RX_READY, + STATUS_F3_RX_READY, + STATUS_HOST_CMD_DATA_ERR, + STATUS_F2_PKT_AVAILABLE, + STATUS_F3_PKT_AVAILABLE + ); + } +} + +#[cfg(feature = "log")] +impl core::fmt::Debug for FormatStatus { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + macro_rules! implm { + ($($name:ident),*) => { + $( + if self.0 & $name > 0 { + core::write!(fmt, " | {}", &stringify!($name)[7..])?; + } + )* + }; + } + + implm!( + STATUS_DATA_NOT_AVAILABLE, + STATUS_UNDERFLOW, + STATUS_OVERFLOW, + STATUS_F2_INTR, + STATUS_F3_INTR, + STATUS_F2_RX_READY, + STATUS_F3_RX_READY, + STATUS_HOST_CMD_DATA_ERR, + STATUS_F2_PKT_AVAILABLE, + STATUS_F3_PKT_AVAILABLE + ); + Ok(()) + } +} + +#[cfg(feature = "log")] +impl core::fmt::Display for FormatStatus { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(self, f) + } +} + +#[allow(dead_code)] +pub(crate) struct FormatInterrupt(pub u16); + +#[cfg(feature = "defmt")] +impl defmt::Format for FormatInterrupt { + fn format(&self, fmt: defmt::Formatter) { + macro_rules! implm { + ($($name:ident),*) => { + $( + if self.0 & $name > 0 { + defmt::write!(fmt, " | {}", &stringify!($name)[4..]); + } + )* + }; + } + + implm!( + IRQ_DATA_UNAVAILABLE, + IRQ_F2_F3_FIFO_RD_UNDERFLOW, + IRQ_F2_F3_FIFO_WR_OVERFLOW, + IRQ_COMMAND_ERROR, + IRQ_DATA_ERROR, + IRQ_F2_PACKET_AVAILABLE, + IRQ_F3_PACKET_AVAILABLE, + IRQ_F1_OVERFLOW, + IRQ_MISC_INTR0, + IRQ_MISC_INTR1, + IRQ_MISC_INTR2, + IRQ_MISC_INTR3, + IRQ_MISC_INTR4, + IRQ_F1_INTR, + IRQ_F2_INTR, + IRQ_F3_INTR + ); + } +} + +#[cfg(feature = "log")] +impl core::fmt::Debug for FormatInterrupt { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + macro_rules! implm { + ($($name:ident),*) => { + $( + if self.0 & $name > 0 { + core::write!(fmt, " | {}", &stringify!($name)[7..])?; + } + )* + }; + } + + implm!( + IRQ_DATA_UNAVAILABLE, + IRQ_F2_F3_FIFO_RD_UNDERFLOW, + IRQ_F2_F3_FIFO_WR_OVERFLOW, + IRQ_COMMAND_ERROR, + IRQ_DATA_ERROR, + IRQ_F2_PACKET_AVAILABLE, + IRQ_F3_PACKET_AVAILABLE, + IRQ_F1_OVERFLOW, + IRQ_MISC_INTR0, + IRQ_MISC_INTR1, + IRQ_MISC_INTR2, + IRQ_MISC_INTR3, + IRQ_MISC_INTR4, + IRQ_F1_INTR, + IRQ_F2_INTR, + IRQ_F3_INTR + ); + Ok(()) + } +} + +#[cfg(feature = "log")] +impl core::fmt::Display for FormatInterrupt { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(self, f) + } +} From b58cc2aa239e4adba2c32462cc89133bb7d9f698 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 2 Mar 2023 19:02:32 +0100 Subject: [PATCH 0737/1575] use irqs to wait for events --- examples/rpi-pico-w/src/main.rs | 6 ++ examples/rpi-pico-w/src/pio.rs | 17 ++++-- src/bus.rs | 14 ++++- src/consts.rs | 2 + src/ioctl.rs | 3 - src/runner.rs | 98 ++++++++++++++++++--------------- 6 files changed, 87 insertions(+), 53 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 434851378..97e2d6a60 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -227,4 +227,10 @@ impl cyw43::SpiBusCyw43 for MySpi { self.read(read).await; self.cs.set_high(); } + + async fn wait_for_event(&mut self) {} + + fn clear_event(&mut self) {} + + } diff --git a/examples/rpi-pico-w/src/pio.rs b/examples/rpi-pico-w/src/pio.rs index 8017f4f44..6df227468 100644 --- a/examples/rpi-pico-w/src/pio.rs +++ b/examples/rpi-pico-w/src/pio.rs @@ -41,6 +41,9 @@ where "in pins, 1 side 1" "jmp y-- lp2 side 0" + "wait 1 pin 0 side 0" + "irq 0 side 0" + ".wrap" ); @@ -106,6 +109,7 @@ where } pub async fn write(&mut self, write: &[u32]) { + self.sm.set_enable(false); let write_bits = write.len() * 32 - 1; let read_bits = 31; @@ -124,11 +128,10 @@ where let mut status = 0; self.sm.dma_pull(dma, slice::from_mut(&mut status)).await; defmt::trace!("{:#08x}", status); - - self.sm.set_enable(false); } pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) { + self.sm.set_enable(false); let write_bits = 31; let read_bits = read.len() * 32 - 1; @@ -144,8 +147,6 @@ where self.sm.dma_push(dma.reborrow(), slice::from_ref(&cmd)).await; self.sm.dma_pull(dma, read).await; - - self.sm.set_enable(false); } } @@ -166,4 +167,12 @@ where self.cmd_read(write, read).await; self.cs.set_high(); } + + async fn wait_for_event(&mut self) { + self.sm.wait_irq(0).await; + } + + fn clear_event(&mut self) { + self.sm.clear_irq(0); + } } diff --git a/src/bus.rs b/src/bus.rs index 7700a832a..6ec5d0bd6 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -19,6 +19,9 @@ pub trait SpiBusCyw43 { /// Backplane reads have a response delay that produces one extra unspecified word at the beginning of `read`. /// Callers that want to read `n` word from the backplane, have to provide a slice that is `n+1` words long. async fn cmd_read(&mut self, write: u32, read: &mut [u32]); + + async fn wait_for_event(&mut self); + fn clear_event(&mut self); } pub(crate) struct Bus { @@ -63,7 +66,8 @@ where trace!("{:#010b}", (val & 0xff)); // 32-bit word length, little endian (which is the default endianess). - self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await; + self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED | INTERRUPT_HIGH | WAKE_UP) + .await; let val = self.read8(FUNC_BUS, REG_BUS_CTRL).await; trace!("{:#b}", val); @@ -297,6 +301,14 @@ where self.spi.cmd_write(&buf).await; } + + pub async fn wait_for_event(&mut self) { + self.spi.wait_for_event().await; + } + + pub fn clear_event(&mut self) { + self.spi.clear_event(); + } } fn swap16(x: u32) -> u32 { diff --git a/src/consts.rs b/src/consts.rs index 140cb4b6d..70d6660e0 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -14,6 +14,8 @@ pub(crate) const REG_BUS_TEST_RW: u32 = 0x18; pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c; pub(crate) const WORD_LENGTH_32: u32 = 0x1; pub(crate) const HIGH_SPEED: u32 = 0x10; +pub(crate) const INTERRUPT_HIGH: u32 = 1 << 5; +pub(crate) const WAKE_UP: u32 = 1 << 7; // SPI_STATUS_REGISTER bits pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; diff --git a/src/ioctl.rs b/src/ioctl.rs index 6a7465593..4a2eb252f 100644 --- a/src/ioctl.rs +++ b/src/ioctl.rs @@ -75,7 +75,6 @@ impl IoctlState { pub async fn wait_pending(&self) -> PendingIoctl { let pending = poll_fn(|cx| { if let IoctlStateInner::Pending(pending) = self.state.get() { - warn!("found pending ioctl"); Poll::Ready(pending) } else { self.register_runner(cx.waker()); @@ -89,7 +88,6 @@ impl IoctlState { } pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - warn!("doing ioctl"); self.state .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); self.wake_runner(); @@ -98,7 +96,6 @@ impl IoctlState { pub fn ioctl_done(&self, response: &[u8]) { if let IoctlStateInner::Sent { buf } = self.state.get() { - warn!("ioctl complete"); // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); diff --git a/src/runner.rs b/src/runner.rs index 4abccf48b..a1de0770e 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -122,7 +122,11 @@ where while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} // "Set up the interrupt mask and enable interrupts" - self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; + // self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; + + self.bus + .write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, IRQ_F2_PACKET_AVAILABLE) + .await; // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." // Sounds scary... @@ -227,22 +231,22 @@ where #[cfg(feature = "firmware-logs")] self.log_read().await; - let ev = || async { - // TODO use IRQs - yield_now().await; - }; - if self.has_credit() { let ioctl = self.ioctl_state.wait_pending(); let tx = self.ch.tx_buf(); + let ev = self.bus.wait_for_event(); - match select3(ioctl, tx, ev()).await { - Either3::First(PendingIoctl { buf, kind, cmd, iface }) => { - warn!("ioctl"); - self.send_ioctl(kind, cmd, iface, unsafe { &*buf }).await; + match select3(ioctl, tx, ev).await { + Either3::First(PendingIoctl { + buf: iobuf, + kind, + cmd, + iface, + }) => { + self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; + self.check_status(&mut buf).await; } Either3::Second(packet) => { - warn!("packet"); trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); let mut buf = [0; 512]; @@ -284,50 +288,54 @@ where self.bus.wlan_write(&buf[..(total_len / 4)]).await; self.ch.tx_done(); + self.check_status(&mut buf).await; } Either3::Third(()) => { - // Receive stuff - let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; - - if irq & IRQ_F2_PACKET_AVAILABLE != 0 { - let mut status = 0xFFFF_FFFF; - while status == 0xFFFF_FFFF { - status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; - } - - if status & STATUS_F2_PKT_AVAILABLE != 0 { - let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(&mut buf, len).await; - trace!("rx {:02x}", Bytes(&slice8_mut(&mut buf)[..(len as usize).min(48)])); - self.rx(&slice8_mut(&mut buf)[..len as usize]); - } - } + self.handle_irq(&mut buf).await; } } } else { warn!("TX stalled"); - ev().await; - - // Receive stuff - let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; - - if irq & IRQ_F2_PACKET_AVAILABLE != 0 { - let mut status = 0xFFFF_FFFF; - while status == 0xFFFF_FFFF { - status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; - } - - if status & STATUS_F2_PKT_AVAILABLE != 0 { - let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(&mut buf, len).await; - trace!("rx {:02x}", Bytes(&slice8_mut(&mut buf)[..(len as usize).min(48)])); - self.rx(&slice8_mut(&mut buf)[..len as usize]); - } - } + self.bus.wait_for_event().await; + self.handle_irq(&mut buf).await; } } } + /// Wait for IRQ on F2 packet available + async fn handle_irq(&mut self, buf: &mut [u32; 512]) { + self.bus.clear_event(); + // Receive stuff + let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; + trace!("irq{}", FormatInterrupt(irq)); + + if irq & IRQ_F2_PACKET_AVAILABLE != 0 { + self.check_status(buf).await; + } + } + + /// Handle F2 events while status register is set + async fn check_status(&mut self, buf: &mut [u32; 512]) { + loop { + let mut status = 0xFFFF_FFFF; + while status == 0xFFFF_FFFF { + status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; + } + trace!("check status{}", FormatStatus(status)); + + if status & STATUS_F2_PKT_AVAILABLE != 0 { + let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; + self.bus.wlan_read(buf, len).await; + trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); + self.rx(&slice8_mut(buf)[..len as usize]); + } else { + break; + } + + yield_now().await; + } + } + fn rx(&mut self, packet: &[u8]) { if packet.len() < SdpcmHeader::SIZE { warn!("packet too short, len={}", packet.len()); From 6a802c47083e4f6bb7b7c6c06fd2ef3e291a5212 Mon Sep 17 00:00:00 2001 From: Mateusz Butkiewicz Date: Wed, 22 Mar 2023 08:44:58 +0100 Subject: [PATCH 0738/1575] feat(stm32:qspi): add support for QSPI in stm32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented with help of Tomasz Grześ . --- embassy-stm32/build.rs | 7 + embassy-stm32/src/lib.rs | 3 +- embassy-stm32/src/qspi/mod.rs | 342 ++++++++++++++++++++++++++++++++++ 3 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32/src/qspi/mod.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index dbfc1370d..3780c5a40 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -427,6 +427,12 @@ fn main() { (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)), (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)), (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)), + (("quadspi", "BK1_IO0"), quote!(crate::qspi::D0Pin)), + (("quadspi", "BK1_IO1"), quote!(crate::qspi::D1Pin)), + (("quadspi", "BK1_IO2"), quote!(crate::qspi::D2Pin)), + (("quadspi", "BK1_IO3"), quote!(crate::qspi::D3Pin)), + (("quadspi", "CLK"), quote!(crate::qspi::SckPin)), + (("quadspi", "BK1_NCS"), quote!(crate::qspi::NSSPin)), ].into(); for p in METADATA.peripherals { @@ -507,6 +513,7 @@ fn main() { (("dcmi", "PSSI"), quote!(crate::dcmi::FrameDma)), // SDMMCv1 uses the same channel for both directions, so just implement for RX (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), + (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), ] .into(); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index eeaa04f67..8dc4df2dc 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -48,6 +48,8 @@ pub mod crc; ))] pub mod flash; pub mod pwm; +#[cfg(quadspi)] +pub mod qspi; #[cfg(rng)] pub mod rng; #[cfg(sdmmc)] @@ -60,7 +62,6 @@ pub mod usart; pub mod usb; #[cfg(otg)] pub mod usb_otg; - #[cfg(iwdg)] pub mod wdg; diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs new file mode 100644 index 000000000..f375d1b46 --- /dev/null +++ b/embassy-stm32/src/qspi/mod.rs @@ -0,0 +1,342 @@ +#![macro_use] + +use embassy_hal_common::{into_ref, PeripheralRef}; + +use crate::dma::TransferOptions; +use crate::gpio::sealed::AFType; +use crate::gpio::AnyPin; +use crate::pac::quadspi::Quadspi as Regs; +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +pub struct QspiWidth; + +#[allow(dead_code)] +impl QspiWidth { + pub const NONE: u8 = 0b00; + pub const SING: u8 = 0b01; + pub const DUAL: u8 = 0b10; + pub const QUAD: u8 = 0b11; +} + +struct QspiMode; + +#[allow(dead_code)] +impl QspiMode { + pub const INDIRECT_WRITE: u8 = 0b00; + pub const INDIRECT_READ: u8 = 0b01; + pub const AUTO_POLLING: u8 = 0b10; + pub const MEMORY_MAPPED: u8 = 0b11; +} + +pub struct QspiTransaction { + pub iwidth: u8, + pub awidth: u8, + pub dwidth: u8, + pub instruction: u8, + pub address: Option, + pub dummy: u8, + pub data_len: Option, +} + +impl Default for QspiTransaction { + fn default() -> Self { + Self { + iwidth: QspiWidth::NONE, + awidth: QspiWidth::NONE, + dwidth: QspiWidth::NONE, + instruction: 0, + address: None, + dummy: 0, + data_len: None, + } + } +} + +pub struct Config { + pub memory_size: u8, + pub address_size: u8, + pub prescaler: u8, + pub fifo_threshold: u8, + pub cs_high_time: u8, +} + +impl Default for Config { + fn default() -> Self { + Self { + memory_size: 0, + address_size: 2, + prescaler: 128, + fifo_threshold: 16, + cs_high_time: 4, + } + } +} + +#[allow(dead_code)] +pub struct Qspi<'d, T: Instance, Dma> { + _peri: PeripheralRef<'d, T>, + sck: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + nss: Option>, + dma: PeripheralRef<'d, Dma>, + config: Config, +} + +impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { + pub fn new( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, d0, d1, d2, d3, sck, nss); + + unsafe { + sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af(nss.af_num(), AFType::OutputPushPull); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af(d0.af_num(), AFType::OutputPushPull); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af(d1.af_num(), AFType::OutputPushPull); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af(d2.af_num(), AFType::OutputPushPull); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af(d3.af_num(), AFType::OutputPushPull); + d3.set_speed(crate::gpio::Speed::VeryHigh); + } + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + Some(sck.map_into()), + Some(nss.map_into()), + dma, + config, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + sck: Option>, + nss: Option>, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, dma); + + T::enable(); + unsafe { + T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold)); + + while T::REGS.sr().read().busy() {} + + T::REGS.cr().write(|w| { + w.set_prescaler(config.prescaler); + w.set_en(true); + }); + T::REGS.dcr().write(|w| { + w.set_fsize(config.memory_size); + w.set_csht(config.cs_high_time); + w.set_ckmode(false); + }); + } + + Self { + _peri: peri, + sck, + d0, + d1, + d2, + d3, + nss, + dma, + config, + } + } + + pub fn command(&mut self, transaction: QspiTransaction) { + unsafe { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + } + + pub fn read(&mut self, buf: &mut [u8], transaction: QspiTransaction) { + unsafe { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + + if let Some(len) = transaction.data_len { + let current_ar = T::REGS.ar().read().address(); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::INDIRECT_READ); + }); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + for idx in 0..len { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = *(T::REGS.dr().ptr() as *mut u8); + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + } + + pub fn write(&mut self, buf: &[u8], transaction: QspiTransaction) { + unsafe { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + + if let Some(len) = transaction.data_len { + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::INDIRECT_WRITE); + }); + + for idx in 0..len { + while !T::REGS.sr().read().ftf() {} + *(T::REGS.dr().ptr() as *mut u8) = buf[idx]; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + } + + pub fn read_dma(&mut self, buf: &mut [u8], transaction: QspiTransaction) + where + Dma: QuadDma, + { + unsafe { + self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + + let request = self.dma.request(); + let options = TransferOptions::default(); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::INDIRECT_READ); + }); + let current_ar = T::REGS.ar().read().address(); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + self.dma + .start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options); + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + while self.dma.is_running() {} + } + } + + pub fn write_dma(&mut self, buf: &[u8], transaction: QspiTransaction) + where + Dma: QuadDma, + { + unsafe { + self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + + let request = self.dma.request(); + let options = TransferOptions::default(); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::INDIRECT_WRITE); + }); + + self.dma + .start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options); + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + while self.dma.is_running() {} + } + } + + fn setup_transaction(&mut self, fmode: u8, transaction: &QspiTransaction) { + unsafe { + T::REGS.fcr().modify(|v| { + v.set_csmf(true); + v.set_ctcf(true); + v.set_ctef(true); + v.set_ctof(true); + }); + + while T::REGS.sr().read().busy() {} + + if let Some(len) = transaction.data_len { + T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); + } + + T::REGS.ccr().write(|v| { + v.set_fmode(fmode); + v.set_imode(transaction.iwidth); + v.set_instruction(transaction.instruction); + v.set_admode(transaction.awidth); + v.set_adsize(self.config.address_size); + v.set_dmode(transaction.dwidth); + v.set_abmode(QspiWidth::NONE); + v.set_dcyc(transaction.dummy); + }); + + if let Some(addr) = transaction.address { + T::REGS.ar().write(|v| { + v.set_address(addr); + }); + } + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + const REGS: Regs; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} + +pin_trait!(SckPin, Instance); +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(NSSPin, Instance); + +dma_trait!(QuadDma, Instance); + +foreach_peripheral!( + (quadspi, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); From 1c721cb20e44fdc7ec294792a9621d54361d344e Mon Sep 17 00:00:00 2001 From: kbleeke Date: Mon, 27 Mar 2023 13:39:41 +0200 Subject: [PATCH 0739/1575] cancel ioctl when future is dropped --- src/control.rs | 23 ++++++++++++++++++++--- src/ioctl.rs | 4 ++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/control.rs b/src/control.rs index 0dbf6d44f..30d5d0924 100644 --- a/src/control.rs +++ b/src/control.rs @@ -278,10 +278,27 @@ impl<'a> Control<'a> { } async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - // TODO cancel ioctl on future drop. + struct CancelOnDrop<'a>(&'a IoctlState); + + impl CancelOnDrop<'_> { + fn defuse(self) { + core::mem::forget(self); + } + } + + impl Drop for CancelOnDrop<'_> { + fn drop(&mut self) { + self.0.cancel_ioctl(); + } + } + + let ioctl = CancelOnDrop(self.ioctl_state); + + ioctl.0.do_ioctl(kind, cmd, iface, buf).await; + let resp_len = ioctl.0.wait_complete().await; + + ioctl.defuse(); - self.ioctl_state.do_ioctl(kind, cmd, iface, buf).await; - let resp_len = self.ioctl_state.wait_complete().await; resp_len } } diff --git a/src/ioctl.rs b/src/ioctl.rs index 6a7465593..f5ab410db 100644 --- a/src/ioctl.rs +++ b/src/ioctl.rs @@ -88,6 +88,10 @@ impl IoctlState { pending } + pub fn cancel_ioctl(&self) { + self.state.set(IoctlStateInner::Done { resp_len: 0 }); + } + pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { warn!("doing ioctl"); self.state From a6cef4baf220409bb20d81af538f2c507ec4c4c9 Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 27 Mar 2023 14:19:00 +0200 Subject: [PATCH 0740/1575] Add logging and interface for debugging buffer usage --- embassy-usb/src/builder.rs | 14 ++++++++++++++ embassy-usb/src/class/hid.rs | 3 +++ embassy-usb/src/lib.rs | 37 ++++++++++++++++++++++++++++++++++++ embassy-usb/src/msos.rs | 5 +++++ 4 files changed, 59 insertions(+) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 305dfa02e..6649cd5b6 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -201,6 +201,20 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.config_descriptor.end_configuration(); self.bos_descriptor.end_bos(); + info!("USB: device_descriptor used: {}", self.device_descriptor.position()); + info!("USB: config_descriptor used: {}", self.config_descriptor.position()); + info!("USB: bos_descriptor_buf used: {}", self.bos_descriptor.writer.position()); + #[cfg(feature = "msos-descriptor")] + info!("USB: device_descriptor used: {}", msos_descriptor.len()); + if self.control_buf.len() != self.config.max_packet_size_0.into() { + warn!( + "Mismatch in control buf and max packet size! buf len: {}, max ep0 size: {}", + self.control_buf.len(), + self.config.max_packet_size_0, + ); + } + info!("USB: device_descriptor used: {}", self.config_descriptor.position()); + UsbDevice::build( self.driver, self.config, diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 974268c62..597403427 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -458,6 +458,9 @@ impl<'d> Handler for Control<'d> { return None; } + // TODO(AJM): This uses a defmt-specific formatter that causes use of the `log` + // feature to fail to build + #[cfg(feature = "defmt")] trace!("HID control_out {:?} {=[u8]:x}", req, data); match req.request { HID_REQ_SET_IDLE => { diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index bfeccd5fe..3016b81cb 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -165,6 +165,25 @@ struct Interface { num_alt_settings: u8, } +/// A report of the used size of the runtime allocated buffers +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct UsbBufferReport { + /// Number of device descriptor bytes used + pub device_descriptor_used: usize, + /// Number of config descriptor bytes used + pub config_descriptor_used: usize, + /// Number of bos descriptor bytes used + pub bos_descriptor_used: usize, + /// Number of msos descriptor bytes used + /// + /// Will be `None` if the "msos-descriptor" feature is not active. + /// Otherwise will return Some(bytes). + pub msos_descriptor_used: Option, + /// Size of the control buffer + pub control_buffer_size: usize, +} + /// Main struct for the USB device stack. pub struct UsbDevice<'d, D: Driver<'d>> { control_buf: &'d mut [u8], @@ -239,6 +258,24 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { } } + /// Returns a report of the consumed buffers + /// + /// Useful for tuning buffer sizes for actual usage + pub fn buffer_usage(&self) -> UsbBufferReport { + #[cfg(not(feature = "msos-descriptor"))] + let mdu = None; + #[cfg(feature = "msos-descriptor")] + let mdu = Some(self.inner.msos_descriptor.len()); + + UsbBufferReport { + device_descriptor_used: self.inner.device_descriptor.len(), + config_descriptor_used: self.inner.config_descriptor.len(), + bos_descriptor_used: self.inner.bos_descriptor.len(), + msos_descriptor_used: mdu, + control_buffer_size: self.control_buf.len(), + } + } + /// Runs the `UsbDevice` forever. /// /// This future may leave the bus in an invalid state if it is dropped. diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index b1e0335ee..218d9931a 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -32,6 +32,11 @@ impl<'d> MsOsDescriptorSet<'d> { pub fn is_empty(&self) -> bool { self.descriptor.is_empty() } + + /// Returns the length of the descriptor field + pub fn len(&self) -> usize { + self.descriptor.len() + } } /// Writes a Microsoft OS 2.0 Descriptor set into a buffer. From 8926397f4592f22a5ed54f772a979578ca36628f Mon Sep 17 00:00:00 2001 From: kbleeke Date: Mon, 27 Mar 2023 14:37:39 +0200 Subject: [PATCH 0741/1575] address irq nits --- examples/rpi-pico-w/src/main.rs | 6 ------ examples/rpi-pico-w/src/pio.rs | 3 --- src/bus.rs | 12 ++++++------ src/runner.rs | 4 ---- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 97e2d6a60..434851378 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -227,10 +227,4 @@ impl cyw43::SpiBusCyw43 for MySpi { self.read(read).await; self.cs.set_high(); } - - async fn wait_for_event(&mut self) {} - - fn clear_event(&mut self) {} - - } diff --git a/examples/rpi-pico-w/src/pio.rs b/examples/rpi-pico-w/src/pio.rs index 6df227468..1cefb1734 100644 --- a/examples/rpi-pico-w/src/pio.rs +++ b/examples/rpi-pico-w/src/pio.rs @@ -170,9 +170,6 @@ where async fn wait_for_event(&mut self) { self.sm.wait_irq(0).await; - } - - fn clear_event(&mut self) { self.sm.clear_irq(0); } } diff --git a/src/bus.rs b/src/bus.rs index 6ec5d0bd6..d2a249f97 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,5 +1,6 @@ use core::slice; +use embassy_futures::yield_now; use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; use futures::FutureExt; @@ -20,8 +21,11 @@ pub trait SpiBusCyw43 { /// Callers that want to read `n` word from the backplane, have to provide a slice that is `n+1` words long. async fn cmd_read(&mut self, write: u32, read: &mut [u32]); - async fn wait_for_event(&mut self); - fn clear_event(&mut self); + /// Wait for events from the Device. A typical implementation would wait for the IRQ pin to be high. + /// The default implementation always reports ready, resulting in active polling of the device. + async fn wait_for_event(&mut self) { + yield_now().await; + } } pub(crate) struct Bus { @@ -305,10 +309,6 @@ where pub async fn wait_for_event(&mut self) { self.spi.wait_for_event().await; } - - pub fn clear_event(&mut self) { - self.spi.clear_event(); - } } fn swap16(x: u32) -> u32 { diff --git a/src/runner.rs b/src/runner.rs index a1de0770e..abfac3ae3 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,7 +1,6 @@ use core::slice; use embassy_futures::select::{select3, Either3}; -use embassy_futures::yield_now; use embassy_net_driver_channel as ch; use embassy_sync::pubsub::PubSubBehavior; use embassy_time::{block_for, Duration, Timer}; @@ -304,7 +303,6 @@ where /// Wait for IRQ on F2 packet available async fn handle_irq(&mut self, buf: &mut [u32; 512]) { - self.bus.clear_event(); // Receive stuff let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; trace!("irq{}", FormatInterrupt(irq)); @@ -331,8 +329,6 @@ where } else { break; } - - yield_now().await; } } From a77fdefd7c16cb6077d4f27ec5094e52457fcde5 Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 27 Mar 2023 15:37:12 +0200 Subject: [PATCH 0742/1575] Correct copy/paste errors --- embassy-usb/src/builder.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 6649cd5b6..cad1ecda9 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -201,19 +201,21 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.config_descriptor.end_configuration(); self.bos_descriptor.end_bos(); + // Log the number of allocator bytes actually used in descriptor buffers info!("USB: device_descriptor used: {}", self.device_descriptor.position()); info!("USB: config_descriptor used: {}", self.config_descriptor.position()); - info!("USB: bos_descriptor_buf used: {}", self.bos_descriptor.writer.position()); + info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); #[cfg(feature = "msos-descriptor")] - info!("USB: device_descriptor used: {}", msos_descriptor.len()); + info!("USB: msos_descriptor used: {}", msos_descriptor.len()); if self.control_buf.len() != self.config.max_packet_size_0.into() { warn!( - "Mismatch in control buf and max packet size! buf len: {}, max ep0 size: {}", + "USB: Mismatch in control buf and max packet size! buf len: {}, max ep0 size: {}", self.control_buf.len(), self.config.max_packet_size_0, ); + } else { + info!("USB: control_buf size: {}", self.control_buf.len()); } - info!("USB: device_descriptor used: {}", self.config_descriptor.position()); UsbDevice::build( self.driver, From 056df98d475c3be307b7c9c3038e02b4ef79fa08 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Mon, 27 Mar 2023 17:24:45 +0200 Subject: [PATCH 0743/1575] use send status feature of cyw43 instead of manually checking status --- examples/rpi-pico-w/src/main.rs | 14 ++++++++++++-- examples/rpi-pico-w/src/pio.rs | 24 ++++++++++++++---------- src/bus.rs | 33 +++++++++++++++++++++------------ src/consts.rs | 1 + src/runner.rs | 5 +---- 5 files changed, 49 insertions(+), 28 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 434851378..e3c59223b 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -215,16 +215,26 @@ impl MySpi { } impl cyw43::SpiBusCyw43 for MySpi { - async fn cmd_write(&mut self, write: &[u32]) { + async fn cmd_write(&mut self, write: &[u32]) -> u32 { self.cs.set_low(); self.write(write).await; + + let mut status = 0; + self.read(slice::from_mut(&mut status)).await; + self.cs.set_high(); + status } - async fn cmd_read(&mut self, write: u32, read: &mut [u32]) { + async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32 { self.cs.set_low(); self.write(slice::from_ref(&write)).await; self.read(read).await; + + let mut status = 0; + self.read(slice::from_mut(&mut status)).await; + self.cs.set_high(); + status } } diff --git a/examples/rpi-pico-w/src/pio.rs b/examples/rpi-pico-w/src/pio.rs index 1cefb1734..846113060 100644 --- a/examples/rpi-pico-w/src/pio.rs +++ b/examples/rpi-pico-w/src/pio.rs @@ -108,7 +108,7 @@ where } } - pub async fn write(&mut self, write: &[u32]) { + pub async fn write(&mut self, write: &[u32]) -> u32 { self.sm.set_enable(false); let write_bits = write.len() * 32 - 1; let read_bits = 31; @@ -125,15 +125,14 @@ where self.sm.dma_push(dma.reborrow(), write).await; - let mut status = 0; - self.sm.dma_pull(dma, slice::from_mut(&mut status)).await; - defmt::trace!("{:#08x}", status); + let status = self.sm.wait_pull().await; + status } - pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) { + pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) -> u32 { self.sm.set_enable(false); let write_bits = 31; - let read_bits = read.len() * 32 - 1; + let read_bits = read.len() * 32 + 32 - 1; defmt::trace!("write={} read={}", write_bits, read_bits); @@ -147,6 +146,9 @@ where self.sm.dma_push(dma.reborrow(), slice::from_ref(&cmd)).await; self.sm.dma_pull(dma, read).await; + + let status = self.sm.wait_pull().await; + status } } @@ -156,16 +158,18 @@ where SM: PioStateMachine, DMA: Channel, { - async fn cmd_write(&mut self, write: &[u32]) { + async fn cmd_write(&mut self, write: &[u32]) -> u32 { self.cs.set_low(); - self.write(write).await; + let status = self.write(write).await; self.cs.set_high(); + status } - async fn cmd_read(&mut self, write: u32, read: &mut [u32]) { + async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32 { self.cs.set_low(); - self.cmd_read(write, read).await; + let status = self.cmd_read(write, read).await; self.cs.set_high(); + status } async fn wait_for_event(&mut self) { diff --git a/src/bus.rs b/src/bus.rs index d2a249f97..65caea8ec 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -12,14 +12,14 @@ use crate::consts::*; pub trait SpiBusCyw43 { /// Issues a write command on the bus /// First 32 bits of `word` are expected to be a cmd word - async fn cmd_write(&mut self, write: &[u32]); + async fn cmd_write(&mut self, write: &[u32]) -> u32; /// Issues a read command on the bus /// `write` is expected to be a 32 bit cmd word /// `read` will contain the response of the device /// Backplane reads have a response delay that produces one extra unspecified word at the beginning of `read`. /// Callers that want to read `n` word from the backplane, have to provide a slice that is `n+1` words long. - async fn cmd_read(&mut self, write: u32, read: &mut [u32]); + async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32; /// Wait for events from the Device. A typical implementation would wait for the IRQ pin to be high. /// The default implementation always reports ready, resulting in active polling of the device. @@ -32,6 +32,7 @@ pub(crate) struct Bus { backplane_window: u32, pwr: PWR, spi: SPI, + status: u32, } impl Bus @@ -44,6 +45,7 @@ where backplane_window: 0xAAAA_AAAA, pwr, spi, + status: 0, } } @@ -70,8 +72,11 @@ where trace!("{:#010b}", (val & 0xff)); // 32-bit word length, little endian (which is the default endianess). - self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED | INTERRUPT_HIGH | WAKE_UP) - .await; + self.write32_swapped( + REG_BUS_CTRL, + WORD_LENGTH_32 | HIGH_SPEED | INTERRUPT_HIGH | WAKE_UP | STATUS_ENABLE, + ) + .await; let val = self.read8(FUNC_BUS, REG_BUS_CTRL).await; trace!("{:#b}", val); @@ -88,7 +93,7 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len_in_u8); let len_in_u32 = (len_in_u8 as usize + 3) / 4; - self.spi.cmd_read(cmd, &mut buf[..len_in_u32]).await; + self.status = self.spi.cmd_read(cmd, &mut buf[..len_in_u32]).await; } pub async fn wlan_write(&mut self, buf: &[u32]) { @@ -98,7 +103,7 @@ where cmd_buf[0] = cmd; cmd_buf[1..][..buf.len()].copy_from_slice(buf); - self.spi.cmd_write(&cmd_buf).await; + self.status = self.spi.cmd_write(&cmd_buf).await; } #[allow(unused)] @@ -124,7 +129,7 @@ where let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); // round `buf` to word boundary, add one extra word for the response delay - self.spi.cmd_read(cmd, &mut buf[..(len + 3) / 4 + 1]).await; + self.status = self.spi.cmd_read(cmd, &mut buf[..(len + 3) / 4 + 1]).await; // when writing out the data, we skip the response-delay byte data[..len].copy_from_slice(&slice8_mut(&mut buf[1..])[..len]); @@ -157,7 +162,7 @@ where let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); buf[0] = cmd; - self.spi.cmd_write(&buf[..(len + 3) / 4 + 1]).await; + self.status = self.spi.cmd_write(&buf[..(len + 3) / 4 + 1]).await; // Advance ptr. addr += len as u32; @@ -273,7 +278,7 @@ where // if we are reading from the backplane, we need an extra word for the response delay let len = if func == FUNC_BACKPLANE { 2 } else { 1 }; - self.spi.cmd_read(cmd, &mut buf[..len]).await; + self.status = self.spi.cmd_read(cmd, &mut buf[..len]).await; // if we read from the backplane, the result is in the second word, after the response delay if func == FUNC_BACKPLANE { @@ -286,7 +291,7 @@ where async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); - self.spi.cmd_write(&[cmd, val]).await; + self.status = self.spi.cmd_write(&[cmd, val]).await; } async fn read32_swapped(&mut self, addr: u32) -> u32 { @@ -294,7 +299,7 @@ where let cmd = swap16(cmd); let mut buf = [0; 1]; - self.spi.cmd_read(cmd, &mut buf).await; + self.status = self.spi.cmd_read(cmd, &mut buf).await; swap16(buf[0]) } @@ -303,12 +308,16 @@ where let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); let buf = [swap16(cmd), swap16(val)]; - self.spi.cmd_write(&buf).await; + self.status = self.spi.cmd_write(&buf).await; } pub async fn wait_for_event(&mut self) { self.spi.wait_for_event().await; } + + pub fn status(&self) -> u32 { + self.status + } } fn swap16(x: u32) -> u32 { diff --git a/src/consts.rs b/src/consts.rs index 70d6660e0..6ed7feb92 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -16,6 +16,7 @@ pub(crate) const WORD_LENGTH_32: u32 = 0x1; pub(crate) const HIGH_SPEED: u32 = 0x10; pub(crate) const INTERRUPT_HIGH: u32 = 1 << 5; pub(crate) const WAKE_UP: u32 = 1 << 7; +pub(crate) const STATUS_ENABLE: u32 = 0x10000; // SPI_STATUS_REGISTER bits pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; diff --git a/src/runner.rs b/src/runner.rs index abfac3ae3..ccdbbf1ac 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -315,10 +315,7 @@ where /// Handle F2 events while status register is set async fn check_status(&mut self, buf: &mut [u32; 512]) { loop { - let mut status = 0xFFFF_FFFF; - while status == 0xFFFF_FFFF { - status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await; - } + let status = self.bus.status(); trace!("check status{}", FormatStatus(status)); if status & STATUS_F2_PKT_AVAILABLE != 0 { From 20aa86d63e6967989ab4d70a638cfe8a0a62a2ca Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 27 Mar 2023 18:21:11 +0200 Subject: [PATCH 0744/1575] Address review comments --- embassy-usb/src/builder.rs | 10 +--------- embassy-usb/src/class/hid.rs | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index cad1ecda9..6b68bcd7b 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -207,15 +207,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); #[cfg(feature = "msos-descriptor")] info!("USB: msos_descriptor used: {}", msos_descriptor.len()); - if self.control_buf.len() != self.config.max_packet_size_0.into() { - warn!( - "USB: Mismatch in control buf and max packet size! buf len: {}, max ep0 size: {}", - self.control_buf.len(), - self.config.max_packet_size_0, - ); - } else { - info!("USB: control_buf size: {}", self.control_buf.len()); - } + info!("USB: control_buf size: {}", self.control_buf.len()); UsbDevice::build( self.driver, diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 597403427..03e4c1dbb 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -458,8 +458,8 @@ impl<'d> Handler for Control<'d> { return None; } - // TODO(AJM): This uses a defmt-specific formatter that causes use of the `log` - // feature to fail to build + // This uses a defmt-specific formatter that causes use of the `log` + // feature to fail to build, so leave it defmt-specific for now. #[cfg(feature = "defmt")] trace!("HID control_out {:?} {=[u8]:x}", req, data); match req.request { From 20ea35fc9639487eaa21f1dcee6c32d8a66a0fbb Mon Sep 17 00:00:00 2001 From: kbleeke Date: Mon, 27 Mar 2023 18:04:48 +0200 Subject: [PATCH 0745/1575] Move pio driver to separate crate --- .vscode/settings.json | 1 + Cargo.toml | 6 ++++++ cyw43-pio/Cargo.toml | 13 +++++++++++++ .../rpi-pico-w/src/pio.rs => cyw43-pio/src/lib.rs | 12 +++++++++--- examples/rpi-pico-w/Cargo.toml | 3 +-- examples/rpi-pico-w/src/main.rs | 5 +---- 6 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 cyw43-pio/Cargo.toml rename examples/rpi-pico-w/src/pio.rs => cyw43-pio/src/lib.rs (93%) diff --git a/.vscode/settings.json b/.vscode/settings.json index dd479929e..344307695 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.linkedProjects": [ "examples/rpi-pico-w/Cargo.toml", + "cyw43-pio/Cargo.toml", ], "rust-analyzer.server.extraEnv": { "WIFI_NETWORK": "foo", diff --git a/Cargo.toml b/Cargo.toml index a307a6cc3..04a47a3a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,9 @@ embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3 embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } + +[workspace] +members = ["cyw43-pio"] +default-members = ["cyw43-pio", "."] +exclude = ["examples"] \ No newline at end of file diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml new file mode 100644 index 000000000..2fc6b7591 --- /dev/null +++ b/cyw43-pio/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cyw43-pio" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cyw43 = { path = "../" } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } +pio-proc = "0.2" +pio = "0.2.1" +defmt = "0.3" \ No newline at end of file diff --git a/examples/rpi-pico-w/src/pio.rs b/cyw43-pio/src/lib.rs similarity index 93% rename from examples/rpi-pico-w/src/pio.rs rename to cyw43-pio/src/lib.rs index 846113060..2159796cd 100644 --- a/examples/rpi-pico-w/src/pio.rs +++ b/cyw43-pio/src/lib.rs @@ -1,3 +1,7 @@ +#![no_std] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] + use core::slice; use cyw43::SpiBusCyw43; @@ -125,7 +129,8 @@ where self.sm.dma_push(dma.reborrow(), write).await; - let status = self.sm.wait_pull().await; + let mut status = 0; + self.sm.dma_pull(dma.reborrow(), slice::from_mut(&mut status)).await; status } @@ -145,9 +150,10 @@ where self.sm.set_enable(true); self.sm.dma_push(dma.reborrow(), slice::from_ref(&cmd)).await; - self.sm.dma_pull(dma, read).await; + self.sm.dma_pull(dma.reborrow(), read).await; - let status = self.sm.wait_pull().await; + let mut status = 0; + self.sm.dma_pull(dma.reborrow(), slice::from_mut(&mut status)).await; status } } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 4a531c88c..41ee8a3c4 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } +cyw43-pio = { path = "../../cyw43-pio" } embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } @@ -20,8 +21,6 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -pio-proc = "0.2" -pio = "0.2.1" embedded-io = { version = "0.4.0", features = ["async", "defmt"] } heapless = "0.7.15" diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index e3c59223b..4b1623be7 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -4,11 +4,10 @@ #![feature(async_fn_in_trait)] #![allow(incomplete_features)] -mod pio; - use core::slice; use core::str::from_utf8; +use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; @@ -20,8 +19,6 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -use crate::pio::PioSpi; - macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; From 983a94a9c5166f5f4fea103737c6bbe436bec106 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Mon, 27 Mar 2023 22:34:48 +0200 Subject: [PATCH 0746/1575] update readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5d4347e99..d24ec82f3 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,16 @@ Working: - Sending and receiving Ethernet frames. - Using the default MAC address. - [`embassy-net`](https://embassy.dev) integration. +- RP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W. +- Using IRQ for device events +- GPIO support (for LED on the Pico W) TODO: - AP mode (creating an AP) -- GPIO support (used for the Pico W LED) - Scanning - Setting a custom MAC address. -- RP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W. Probably porting [this](https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/cyw43_driver). (Currently bitbanging is used). -- Using the IRQ pin instead of polling the bus. +- Investigate why can [this](https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/pico_cyw43_driver) use higher PIO speed. - Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) ## Running the example From 2d7f35cf571cf46716f01c63cf21a2e2c95afd5d Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 28 Mar 2023 14:28:44 +0200 Subject: [PATCH 0747/1575] Add embedded-io blocking Read + Write for BufferedUart --- embassy-stm32/src/usart/buffered.rs | 99 +++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index a27fcc1ca..3377b3f9f 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -197,6 +197,40 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { .await } + fn inner_blocking_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { + loop { + let mut do_pend = false; + let mut inner = self.inner.borrow_mut(); + let n = inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return len; + } + + 0 + }); + + if do_pend { + inner.pend(); + } + + if n > 0 { + return Ok(n); + } + } + } + async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { poll_fn(move |cx| { let mut inner = self.inner.borrow_mut(); @@ -236,6 +270,39 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { .await } + fn inner_blocking_write<'a>(&'a self, buf: &'a [u8]) -> Result { + loop { + let mut inner = self.inner.borrow_mut(); + let (n, empty) = inner.with(|state| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + return (0, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (n, empty) + }); + if empty { + inner.pend(); + } + if n != 0 { + return Ok(n); + } + } + } + + fn inner_blocking_flush(&self) -> Result<(), Error> { + loop { + if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) { + return Ok(()); + } + } + } + async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { poll_fn(move |cx| { self.inner.borrow_mut().with(|state| { @@ -419,3 +486,35 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, self.inner.inner_flush().await } } + +impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_blocking_read(buf) + } +} + +impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.inner_blocking_read(buf) + } +} + +impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner_blocking_write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.inner_blocking_flush() + } +} + +impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.inner_blocking_write(buf) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.inner_blocking_flush() + } +} From 14f6bc88ea9ab5b6eebff97371de0feeffd25a62 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 28 Mar 2023 14:34:36 +0200 Subject: [PATCH 0748/1575] Remove unnecessary lifetime --- embassy-stm32/src/usart/buffered.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 3377b3f9f..cd7d72f91 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -197,7 +197,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { .await } - fn inner_blocking_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { + fn inner_blocking_read(&self, buf: &mut [u8]) -> Result { loop { let mut do_pend = false; let mut inner = self.inner.borrow_mut(); @@ -270,7 +270,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { .await } - fn inner_blocking_write<'a>(&'a self, buf: &'a [u8]) -> Result { + fn inner_blocking_write(&self, buf: &[u8]) -> Result { loop { let mut inner = self.inner.borrow_mut(); let (n, empty) = inner.with(|state| { From 781c7f978c555bfe364472fa822f1e27f2da1afb Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 28 Mar 2023 14:03:17 +0200 Subject: [PATCH 0749/1575] make pio faster --- cyw43-pio/src/lib.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index 2159796cd..46ea0411e 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -33,18 +33,23 @@ where { let program = pio_asm!( ".side_set 1" - // "set pindirs, 1 side 0" - // "set pins, 0 side 0" + ".wrap_target" + // write out x-1 bits "lp:", "out pins, 1 side 0" "jmp x-- lp side 1" + // switch directions "set pindirs, 0 side 0" + // these nops seem to be necessary for fast clkdiv "nop side 1" + "nop side 1" + // read in y-1 bits "lp2:" - "in pins, 1 side 1" - "jmp y-- lp2 side 0" + "in pins, 1 side 0" + "jmp y-- lp2 side 1" + // wait for event and irq host "wait 1 pin 0 side 0" "irq 0 side 0" @@ -64,8 +69,15 @@ where sm.write_instr(relocated.origin() as usize, relocated.code()); + // theoretical maximum according to data sheet, 100Mhz Pio => 50Mhz SPI Freq + // does not work yet, + // sm.set_clkdiv(0x0140); + + // same speed as pico-sdk, 62.5Mhz + sm.set_clkdiv(0x0200); + // 32 Mhz - sm.set_clkdiv(0x03E8); + // sm.set_clkdiv(0x03E8); // 16 Mhz // sm.set_clkdiv(0x07d0); From 869b337715c7c784865e6b1a0c26557c4204d35d Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 28 Mar 2023 16:51:49 +0200 Subject: [PATCH 0750/1575] PIO at maximum speed --- cyw43-pio/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index 46ea0411e..9c425cbaa 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -43,6 +43,7 @@ where "set pindirs, 0 side 0" // these nops seem to be necessary for fast clkdiv "nop side 1" + "nop side 0" "nop side 1" // read in y-1 bits "lp2:" @@ -70,11 +71,10 @@ where sm.write_instr(relocated.origin() as usize, relocated.code()); // theoretical maximum according to data sheet, 100Mhz Pio => 50Mhz SPI Freq - // does not work yet, - // sm.set_clkdiv(0x0140); + sm.set_clkdiv(0x0140); // same speed as pico-sdk, 62.5Mhz - sm.set_clkdiv(0x0200); + // sm.set_clkdiv(0x0200); // 32 Mhz // sm.set_clkdiv(0x03E8); From d6ce1c4325179a813d47f2f068c178fe858ac49f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 11:31:45 +0200 Subject: [PATCH 0751/1575] Support running tests in embassy-stm32 and move impl from common back to stm32 --- embassy-hal-common/src/lib.rs | 1 - embassy-hal-common/src/stm32/flash/f4.rs | 119 ---------------------- embassy-hal-common/src/stm32/flash/f7.rs | 67 ------------ embassy-hal-common/src/stm32/flash/mod.rs | 2 - embassy-hal-common/src/stm32/mod.rs | 1 - embassy-stm32/Cargo.toml | 6 +- embassy-stm32/src/flash/f4.rs | 116 ++++++++++++++++++++- embassy-stm32/src/flash/f7.rs | 64 +++++++++++- embassy-stm32/src/flash/mod.rs | 7 ++ 9 files changed, 186 insertions(+), 197 deletions(-) delete mode 100644 embassy-hal-common/src/stm32/flash/f4.rs delete mode 100644 embassy-hal-common/src/stm32/flash/f7.rs delete mode 100644 embassy-hal-common/src/stm32/flash/mod.rs delete mode 100644 embassy-hal-common/src/stm32/mod.rs diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index 1b253719e..b2a35cd35 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -10,5 +10,4 @@ mod macros; mod peripheral; pub mod ratio; pub mod ring_buffer; -pub mod stm32; pub use peripheral::{Peripheral, PeripheralRef}; diff --git a/embassy-hal-common/src/stm32/flash/f4.rs b/embassy-hal-common/src/stm32/flash/f4.rs deleted file mode 100644 index a8069dddf..000000000 --- a/embassy-hal-common/src/stm32/flash/f4.rs +++ /dev/null @@ -1,119 +0,0 @@ -const FLASH_BASE: u32 = 0x0800_0000; -pub(crate) const SMALL_SECTOR_SIZE: u32 = 16 * 1024; -pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; -pub(crate) const LARGE_SECTOR_SIZE: u32 = 128 * 1024; -pub const SECOND_BANK_SECTOR_OFFSET: u8 = 12; - -#[derive(Debug, PartialEq)] -pub struct FlashSector { - pub index: u8, - pub start: u32, - pub size: u32, -} - -pub fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { - let offset = address - FLASH_BASE; - if !dual_bank { - get_single_bank_sector(offset) - } else { - let bank_size = flash_size / 2; - if offset < bank_size { - get_single_bank_sector(offset) - } else { - let sector = get_single_bank_sector(offset - bank_size); - FlashSector { - index: SECOND_BANK_SECTOR_OFFSET + sector.index, - start: sector.start + bank_size, - size: sector.size, - } - } - } -} - -fn get_single_bank_sector(offset: u32) -> FlashSector { - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_get_sector_single_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) - }; - - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } - - #[test] - fn can_get_sector_dual_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) - }; - - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } -} diff --git a/embassy-hal-common/src/stm32/flash/f7.rs b/embassy-hal-common/src/stm32/flash/f7.rs deleted file mode 100644 index 2f586adee..000000000 --- a/embassy-hal-common/src/stm32/flash/f7.rs +++ /dev/null @@ -1,67 +0,0 @@ -const FLASH_BASE: u32 = 0x0800_0000; -pub(crate) const SMALL_SECTOR_SIZE: u32 = 32 * 1024; -pub(crate) const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; -pub(crate) const LARGE_SECTOR_SIZE: u32 = 256 * 1024; - -#[derive(Debug, PartialEq)] -pub struct FlashSector { - pub index: u8, - pub start: u32, - pub size: u32, -} - -pub fn get_sector(address: u32) -> FlashSector { - // First 4 sectors are 32KB, then one 128KB, and rest are 256KB - let offset = address - FLASH_BASE; - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn can_get_sector() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr)) - }; - - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF); - assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000); - assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF); - - assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000); - assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); - - assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000); - assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); - assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } -} diff --git a/embassy-hal-common/src/stm32/flash/mod.rs b/embassy-hal-common/src/stm32/flash/mod.rs deleted file mode 100644 index 1452b491d..000000000 --- a/embassy-hal-common/src/stm32/flash/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod f4; -pub mod f7; diff --git a/embassy-hal-common/src/stm32/mod.rs b/embassy-hal-common/src/stm32/mod.rs deleted file mode 100644 index 2e50f82b3..000000000 --- a/embassy-hal-common/src/stm32/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod flash; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b66d724d5..1dd6202d3 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = { version = "1", features = ["rt"] } +stm32-metapac = "1" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -69,12 +69,16 @@ seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } +[dev-dependencies] +critical-section = { version = "1.1", features = ["std"] } + [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" stm32-metapac = { version = "1", default-features = false, features = ["metadata"]} [features] +default = ["stm32-metapac/rt"] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] subghz = [] diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 7428fd572..0fdfecb96 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,12 +2,15 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::stm32::flash::f4::{get_sector, SECOND_BANK_SECTOR_OFFSET}; - -use super::{FLASH_SIZE, WRITE_SIZE}; +use super::{FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +const SMALL_SECTOR_SIZE: u32 = 16 * 1024; +const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; +const LARGE_SECTOR_SIZE: u32 = 128 * 1024; +const SECOND_BANK_SECTOR_OFFSET: u8 = 12; + fn is_dual_bank() -> bool { match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration @@ -141,3 +144,110 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { + let offset = address - FLASH_BASE as u32; + if !dual_bank { + get_single_bank_sector(offset) + } else { + let bank_size = flash_size / 2; + if offset < bank_size { + get_single_bank_sector(offset) + } else { + let sector = get_single_bank_sector(offset - bank_size); + FlashSector { + index: SECOND_BANK_SECTOR_OFFSET + sector.index, + start: sector.start + bank_size, + size: sector.size, + } + } + } +} + +fn get_single_bank_sector(offset: u32) -> FlashSector { + // First 4 sectors are 16KB, then one 64KB, and rest are 128KB + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; + FlashSector { + index: small_sector_index as u8, + start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_get_sector_single_bank() { + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } + + #[test] + fn can_get_sector_dual_bank() { + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); + + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 16b684580..0d3b738c6 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -2,12 +2,14 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::stm32::flash::f7::get_sector; - -use super::WRITE_SIZE; +use super::{FlashSector, FLASH_BASE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +const SMALL_SECTOR_SIZE: u32 = 32 * 1024; +const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; +const LARGE_SECTOR_SIZE: u32 = 256 * 1024; + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -129,3 +131,59 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +fn get_sector(address: u32) -> FlashSector { + // First 4 sectors are 32KB, then one 128KB, and rest are 256KB + let offset = address - FLASH_BASE as u32; + match offset / LARGE_SECTOR_SIZE { + 0 => { + if offset < 4 * SMALL_SECTOR_SIZE { + let small_sector_index = offset / SMALL_SECTOR_SIZE; + FlashSector { + index: small_sector_index as u8, + start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, + size: SMALL_SECTOR_SIZE, + } + } else { + FlashSector { + index: 4, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, + size: MEDIUM_SECTOR_SIZE, + } + } + } + i => { + let large_sector_index = i - 1; + FlashSector { + index: (5 + large_sector_index) as u8, + start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + size: LARGE_SECTOR_SIZE, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_get_sector() { + let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + assert_eq!(FlashSector { index, start, size }, get_sector(addr)) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_7FFF); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_8000); + assert_sector(3, 0x0801_8000, SMALL_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0802_0000); + assert_sector(4, 0x0802_0000, MEDIUM_SECTOR_SIZE, 0x0803_FFFF); + + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0804_0000); + assert_sector(5, 0x0804_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); + assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1294ace4d..6906bd09a 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -19,6 +19,13 @@ pub struct Flash<'d> { _inner: PeripheralRef<'d, FLASH>, } +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub start: u32, + pub size: u32, +} + static REGION_LOCK: Mutex = Mutex::new(()); impl<'d> Flash<'d> { From 6806bb969278acc9d3cde34897453b29807157c1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 11:52:18 +0200 Subject: [PATCH 0752/1575] Expose flash region settings as an array --- embassy-stm32/build.rs | 42 ++++++++++++++++++++++------------ embassy-stm32/src/flash/mod.rs | 40 +++++++++++++++++--------------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 393efc426..53f209780 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -3,7 +3,7 @@ use std::fmt::Write as _; use std::path::PathBuf; use std::{env, fs}; -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use stm32_metapac::metadata::{MemoryRegionKind, METADATA}; @@ -106,12 +106,15 @@ fn main() { // ======== // Generate FLASH regions let mut flash_regions = TokenStream::new(); - let flash_memory_regions = METADATA + let flash_memory_regions: Vec<_> = METADATA .memory .iter() - .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()); - for region in flash_memory_regions.clone() { - let region_name = format_ident!("{}", get_flash_region_name(region.name)); + .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) + .collect(); + for region in flash_memory_regions.iter() { + let region_name = get_flash_region_name(region.name); + let region_type = format_ident!("{}", region_name); + let settings_name = format_ident!("{}_SETTINGS", region_name); let base = region.address as usize; let size = region.size as usize; let settings = region.settings.as_ref().unwrap(); @@ -121,21 +124,26 @@ fn main() { flash_regions.extend(quote! { #[allow(non_camel_case_types)] - pub struct #region_name(()); + pub struct #region_type(()); }); flash_regions.extend(quote! { - impl crate::flash::FlashRegion for #region_name { - const BASE: usize = #base; - const SIZE: usize = #size; - const ERASE_SIZE: usize = #erase_size; - const WRITE_SIZE: usize = #write_size; - const ERASE_VALUE: u8 = #erase_value; + pub const #settings_name: crate::flash::FlashRegionSettings = crate::flash::FlashRegionSettings { + base: #base, + size: #size, + erase_size: #erase_size, + write_size: #write_size, + erase_value: #erase_value, + }; + + impl crate::flash::FlashRegion for #region_type { + const SETTINGS: crate::flash::FlashRegionSettings = #settings_name; } }); } - let (fields, inits): (Vec, Vec) = flash_memory_regions + let (fields, (inits, settings)): (Vec, (Vec, Vec)) = flash_memory_regions + .iter() .map(|f| { let region_name = get_flash_region_name(f.name); let field_name = format_ident!("{}", region_name.to_lowercase()); @@ -146,11 +154,13 @@ fn main() { let init = quote! { #field_name: #field_type(()) }; + let settings_name = format_ident!("{}_SETTINGS", region_name); - (field, init) + (field, (init, settings_name)) }) .unzip(); + let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { pub struct FlashRegions { #(#fields),* @@ -163,6 +173,10 @@ fn main() { } } } + + pub const FLASH_REGIONS: [&crate::flash::FlashRegionSettings; #regions_len] = [ + #(&#settings),* + ]; }); g.extend(quote! { pub mod flash_regions { #flash_regions } }); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 6906bd09a..29db2d132 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -19,6 +19,14 @@ pub struct Flash<'d> { _inner: PeripheralRef<'d, FLASH>, } +pub struct FlashRegionSettings { + pub base: usize, + pub size: usize, + pub erase_size: usize, + pub write_size: usize, + pub erase_value: u8, +} + #[derive(Debug, PartialEq)] pub struct FlashSector { pub index: u8, @@ -122,32 +130,28 @@ impl Drop for FlashRegions { } pub trait FlashRegion { - const BASE: usize; - const SIZE: usize; - const ERASE_SIZE: usize; - const WRITE_SIZE: usize; - const ERASE_VALUE: u8; + const SETTINGS: FlashRegionSettings; fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - if offset as usize + bytes.len() > Self::SIZE { + if offset as usize + bytes.len() > Self::SETTINGS.size { return Err(Error::Size); } - let first_address = Self::BASE as u32 + offset; + let first_address = Self::SETTINGS.base as u32 + offset; let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - if offset as usize + buf.len() > Self::SIZE { + if offset as usize + buf.len() > Self::SETTINGS.size { return Err(Error::Size); } - if offset as usize % Self::WRITE_SIZE != 0 || buf.len() as usize % Self::WRITE_SIZE != 0 { + if offset as usize % Self::SETTINGS.write_size != 0 || buf.len() as usize % Self::SETTINGS.write_size != 0 { return Err(Error::Unaligned); } - let start_address = Self::BASE as u32 + offset; + let start_address = Self::SETTINGS.base as u32 + offset; trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); // Protect agains simultaneous write/erase to multiple regions. @@ -163,15 +167,15 @@ pub trait FlashRegion { } fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - if to < from || to as usize > Self::SIZE { + if to < from || to as usize > Self::SETTINGS.size { return Err(Error::Size); } - if (from as usize % Self::ERASE_SIZE) != 0 || (to as usize % Self::ERASE_SIZE) != 0 { + if (from as usize % Self::SETTINGS.erase_size) != 0 || (to as usize % Self::SETTINGS.erase_size) != 0 { return Err(Error::Unaligned); } - let start_address = Self::BASE as u32 + from; - let end_address = Self::BASE as u32 + to; + let start_address = Self::SETTINGS.base as u32 + from; + let end_address = Self::SETTINGS.base as u32 + to; trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); // Protect agains simultaneous write/erase to multiple regions. @@ -224,20 +228,20 @@ foreach_flash_region! { } impl ReadNorFlash for crate::_generated::flash_regions::$name { - const READ_SIZE: usize = ::WRITE_SIZE; + const READ_SIZE: usize = ::SETTINGS.write_size; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { - ::SIZE + ::SETTINGS.size } } impl NorFlash for crate::_generated::flash_regions::$name { - const WRITE_SIZE: usize = ::WRITE_SIZE; - const ERASE_SIZE: usize = ::ERASE_SIZE; + const WRITE_SIZE: usize = ::SETTINGS.write_size; + const ERASE_SIZE: usize = ::SETTINGS.erase_size; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.blocking_erase(from, to) From 4ee3d15519aaf3a290fd78063b88d182ff3aab53 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 12:10:24 +0200 Subject: [PATCH 0753/1575] Keep peripheral lifetime when calling into_regions() --- embassy-stm32/build.rs | 8 +++++--- embassy-stm32/src/flash/mod.rs | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 53f209780..f5bdadf5b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -162,13 +162,15 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { - pub struct FlashRegions { + pub struct FlashRegions<'d> { + _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } - impl FlashRegions { - pub(crate) const fn take() -> Self { + impl<'d> FlashRegions<'d> { + pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { + _inner: p, #(#inits),* } } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 29db2d132..1d1f034aa 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -16,7 +16,7 @@ use crate::Peripheral; mod family; pub struct Flash<'d> { - _inner: PeripheralRef<'d, FLASH>, + inner: PeripheralRef<'d, FLASH>, } pub struct FlashRegionSettings { @@ -39,11 +39,13 @@ static REGION_LOCK: Mutex = Mutex::new(()); impl<'d> Flash<'d> { pub fn new(p: impl Peripheral

+ 'd) -> Self { into_ref!(p); - Self { _inner: p } + Self { inner: p } } - pub fn into_regions(self) -> FlashRegions { - FlashRegions::take() + pub fn into_regions(self) -> FlashRegions<'d> { + let mut flash = self; + let p = unsafe { flash.inner.clone_unchecked() }; + FlashRegions::new(p) } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { @@ -123,7 +125,7 @@ impl Drop for Flash<'_> { } } -impl Drop for FlashRegions { +impl Drop for FlashRegions<'_> { fn drop(&mut self) { unsafe { family::lock() }; } From 69944675a3c35a8479fa3b1499246c0f3f971d95 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 12:49:13 +0200 Subject: [PATCH 0754/1575] Expose get_sector in favor of is_eraseable_range --- embassy-stm32/src/flash/f3.rs | 18 ++++++++++------ embassy-stm32/src/flash/f4.rs | 37 ++++++++++++++++---------------- embassy-stm32/src/flash/f7.rs | 19 +++++----------- embassy-stm32/src/flash/h7.rs | 18 ++++++++++------ embassy-stm32/src/flash/l.rs | 18 ++++++++++------ embassy-stm32/src/flash/mod.rs | 14 +++++++++++- embassy-stm32/src/flash/other.rs | 14 +++++++----- 7 files changed, 81 insertions(+), 57 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 99ac1a153..3da3962e8 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -3,11 +3,11 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, BANK1, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -41,10 +41,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { for page in (start_address..end_address).step_by(ERASE_SIZE) { pac::FLASH.cr().modify(|w| { @@ -107,3 +103,13 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +pub(crate) fn get_sector(address: u32) -> FlashSector { + let sector_size = BANK1::SETTINGS.erase_size as u32; + let index = address / sector_size; + FlashSector { + index: index as u8, + start: BANK1::SETTINGS.base as u32 + index * sector_size, + size: sector_size, + } +} diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 0fdfecb96..306359be1 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -63,24 +63,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - let dual_bank = is_dual_bank(); - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); - if sector.start != address { - return false; - } - address += sector.size; - } - address == end_address -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let dual_bank = is_dual_bank(); let mut address = start_address; while address < end_address { - let sector = get_sector(address, dual_bank, FLASH_SIZE as u32); + let sector = get_sector(address); erase_sector(sector.index)?; address += sector.size; } @@ -145,7 +131,11 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -fn get_sector(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { +pub(crate) fn get_sector(address: u32) -> FlashSector { + get_sector_inner(address, is_dual_bank(), FLASH_SIZE) +} + +fn get_sector_inner(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { let offset = address - FLASH_BASE as u32; if !dual_bank { get_single_bank_sector(offset) @@ -187,7 +177,10 @@ fn get_single_bank_sector(offset: u32) -> FlashSector { let large_sector_index = i - 1; FlashSector { index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + start: FLASH_BASE as u32 + + 4 * SMALL_SECTOR_SIZE + + MEDIUM_SECTOR_SIZE + + large_sector_index * LARGE_SECTOR_SIZE, size: LARGE_SECTOR_SIZE, } } @@ -201,7 +194,10 @@ mod tests { #[test] fn can_get_sector_single_bank() { let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, false, 1024 * 1024)) + assert_eq!( + FlashSector { index, start, size }, + get_sector_inner(addr, false, 1024 * 1024) + ) }; assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); @@ -221,7 +217,10 @@ mod tests { #[test] fn can_get_sector_dual_bank() { let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr, true, 1024 * 1024)) + assert_eq!( + FlashSector { index, start, size }, + get_sector_inner(addr, true, 1024 * 1024) + ) }; assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 0d3b738c6..588fc7a59 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -45,18 +45,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address); - if sector.start != address { - return false; - } - address += sector.size; - } - address == end_address -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { let mut address = start_address; while address < end_address { @@ -132,7 +120,7 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -fn get_sector(address: u32) -> FlashSector { +pub(crate) fn get_sector(address: u32) -> FlashSector { // First 4 sectors are 32KB, then one 128KB, and rest are 256KB let offset = address - FLASH_BASE as u32; match offset / LARGE_SECTOR_SIZE { @@ -156,7 +144,10 @@ fn get_sector(address: u32) -> FlashSector { let large_sector_index = i - 1; FlashSector { index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE + MEDIUM_SECTOR_SIZE + large_sector_index * LARGE_SECTOR_SIZE, + start: FLASH_BASE as u32 + + 4 * SMALL_SECTOR_SIZE + + MEDIUM_SECTOR_SIZE + + large_sector_index * LARGE_SECTOR_SIZE, size: LARGE_SECTOR_SIZE, } } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 21a9e45df..732af8539 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -3,11 +3,11 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FLASH_SIZE, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1, FLASH_SIZE, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; const SECOND_BANK_OFFSET: usize = 0x0010_0000; const fn is_dual_bank() -> bool { @@ -78,10 +78,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; @@ -194,3 +190,13 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { } } } + +pub(crate) fn get_sector(address: u32) -> FlashSector { + let sector_size = BANK1::SETTINGS.erase_size as u32; + let index = address / sector_size; + FlashSector { + index: index as u8, + start: BANK1::SETTINGS.base as u32 + index * sector_size, + size: sector_size, + } +} diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 57989625c..56a21885e 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -2,11 +2,11 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = super::BANK1::ERASE_SIZE; +const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] @@ -62,10 +62,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - start_address % ERASE_SIZE as u32 == 0 && end_address % ERASE_SIZE as u32 == 0 -} - pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { for page in (start_address..end_address).step_by(ERASE_SIZE) { #[cfg(any(flash_l0, flash_l1))] @@ -191,3 +187,13 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } + +pub(crate) fn get_sector(address: u32) -> FlashSector { + let sector_size = BANK1::SETTINGS.erase_size as u32; + let index = address / sector_size; + FlashSector { + index: index as u8, + start: BANK1::SETTINGS.base as u32 + index * sector_size, + size: sector_size, + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1d1f034aa..29cf3cc51 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -104,7 +104,7 @@ impl<'d> Flash<'d> { let start_address = FLASH_BASE as u32 + from; let end_address = FLASH_BASE as u32 + to; - if !family::is_eraseable_range(start_address, end_address) { + if !is_eraseable_range(start_address, end_address) { return Err(Error::Unaligned); } trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); @@ -193,6 +193,18 @@ pub trait FlashRegion { } } +fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + if sector.start != address { + return false; + } + address += sector.size; + } + address == end_address +} + fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { loop { if let Ok(guard) = REGION_LOCK.try_lock() { diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index c9836d095..4ffb4cc35 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -1,7 +1,11 @@ pub trait FlashRegion { - const BASE: usize; - const SIZE: usize; - const ERASE_SIZE: usize; - const WRITE_SIZE: usize; - const ERASE_VALUE: u8; + const SETTINGS: FlashRegionSettings; +} + +pub struct FlashRegionSettings { + pub base: usize, + pub size: usize, + pub erase_size: usize, + pub write_size: usize, + pub erase_value: u8, } From ddbd5098658612e1421cdd081956c3e6ee3c92f8 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 13:37:10 +0200 Subject: [PATCH 0755/1575] Move as much logic from families to shared module as possible --- embassy-stm32/src/flash/f3.rs | 40 +++++---- embassy-stm32/src/flash/f4.rs | 15 +--- embassy-stm32/src/flash/f7.rs | 14 +--- embassy-stm32/src/flash/h7.rs | 17 +--- embassy-stm32/src/flash/l.rs | 83 +++++++++---------- embassy-stm32/src/flash/mod.rs | 147 ++++++++++++++------------------- 6 files changed, 129 insertions(+), 187 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 3da3962e8..7b339ccc5 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -41,33 +41,31 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - for page in (start_address..end_address).step_by(ERASE_SIZE) { - pac::FLASH.cr().modify(|w| { - w.set_per(true); - }); +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + }); - pac::FLASH.ar().write(|w| w.set_far(page)); + pac::FLASH.ar().write(|w| w.set_far(sector.first)); - pac::FLASH.cr().modify(|w| { - w.set_strt(true); - }); + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = blocking_wait_ready(); - if !pac::FLASH.sr().read().eop() { - trace!("FLASH: EOP not set"); - ret = Err(Error::Prog); - } else { - pac::FLASH.sr().write(|w| w.set_eop(true)); - } + if !pac::FLASH.sr().read().eop() { + trace!("FLASH: EOP not set"); + ret = Err(Error::Prog); + } else { + pac::FLASH.sr().write(|w| w.set_eop(true)); + } - pac::FLASH.cr().modify(|w| w.set_per(false)); + pac::FLASH.cr().modify(|w| w.set_per(false)); - clear_all_err(); - if ret.is_err() { - return ret; - } + clear_all_err(); + if ret.is_err() { + return ret; } Ok(()) } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 306359be1..cb420c69f 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -63,17 +63,8 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address); - erase_sector(sector.index)?; - address += sector.size; - } - Ok(()) -} - -unsafe fn erase_sector(sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let sector = sector.index; let bank = sector / SECOND_BANK_SECTOR_OFFSET as u8; let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_OFFSET as u8); @@ -132,7 +123,7 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - get_sector_inner(address, is_dual_bank(), FLASH_SIZE) + get_sector_inner(address, is_dual_bank(), FLASH_SIZE as u32) } fn get_sector_inner(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 588fc7a59..eba7df467 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -45,20 +45,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address); - erase_sector(sector.index)?; - address += sector.size; - } - Ok(()) -} - -unsafe fn erase_sector(sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector) + w.set_snb(sector.index) }); pac::FLASH.cr().modify(|w| { diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 732af8539..28999999d 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -78,20 +78,9 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - let start_sector = (start_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - let end_sector = (end_address - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - for sector in start_sector..end_sector { - 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; - } - } - Ok(()) -} - -unsafe fn erase_sector(bank: pac::flash::Bank, sector: u8) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let bank = pac::FLASH::bank(if sector.index >= 8 { 1 } else { 0 }); + let sector = sector.index % 8; bank.cr().modify(|w| { w.set_ser(true); w.set_snb(sector) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 56a21885e..c8d060f0a 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -62,55 +62,50 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { - for page in (start_address..end_address).step_by(ERASE_SIZE) { - #[cfg(any(flash_l0, flash_l1))] - { - pac::FLASH.pecr().modify(|w| { - w.set_erase(true); - w.set_prog(true); - }); - - write_volatile(page as *mut u32, 0xFFFFFFFF); - } - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - { - let idx = (page - super::FLASH_BASE as u32) / ERASE_SIZE as u32; - - #[cfg(flash_l4)] - let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; - - pac::FLASH.cr().modify(|w| { - w.set_per(true); - w.set_pnb(idx as u8); - #[cfg(any(flash_wl, flash_wb))] - w.set_strt(true); - #[cfg(any(flash_l4))] - w.set_start(true); - #[cfg(any(flash_l4))] - w.set_bker(bank); - }); - } - - let ret: Result<(), Error> = blocking_wait_ready(); - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - pac::FLASH.cr().modify(|w| w.set_per(false)); - - #[cfg(any(flash_l0, flash_l1))] +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + #[cfg(any(flash_l0, flash_l1))] + { pac::FLASH.pecr().modify(|w| { - w.set_erase(false); - w.set_prog(false); + w.set_erase(true); + w.set_prog(true); }); - clear_all_err(); - if ret.is_err() { - return ret; - } + write_volatile(sector.start as *mut u32, 0xFFFFFFFF); } - Ok(()) + #[cfg(any(flash_wl, flash_wb, flash_l4))] + { + let idx = (sector.start - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + + #[cfg(flash_l4)] + let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; + + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + #[cfg(any(flash_wl, flash_wb))] + w.set_strt(true); + #[cfg(any(flash_l4))] + w.set_start(true); + #[cfg(any(flash_l4))] + w.set_bker(bank); + }); + } + + let ret: Result<(), Error> = blocking_wait_ready(); + + #[cfg(any(flash_wl, flash_wb, flash_l4))] + pac::FLASH.cr().modify(|w| w.set_per(false)); + + #[cfg(any(flash_l0, flash_l1))] + pac::FLASH.pecr().modify(|w| { + w.set_erase(false); + w.set_prog(false); + }); + + clear_all_err(); + + ret } pub(crate) unsafe fn clear_all_err() { diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 29cf3cc51..89fdabd4d 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,3 +1,4 @@ +use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; @@ -14,7 +15,6 @@ use crate::Peripheral; #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] mod family; - pub struct Flash<'d> { inner: PeripheralRef<'d, FLASH>, } @@ -49,73 +49,93 @@ impl<'d> Flash<'d> { } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - if offset as usize + bytes.len() > FLASH_SIZE { + Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) + } + + fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { return Err(Error::Size); } - let first_address = FLASH_BASE as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - if offset as usize + buf.len() > FLASH_SIZE { - return Err(Error::Size); - } - if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { - return Err(Error::Unaligned); - } - let start_address = FLASH_BASE as u32 + offset; - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); // No need to take lock here as we only have one mut flash reference. - unsafe { - family::clear_all_err(); - family::unlock(); - let res = Flash::blocking_write_all(start_address, buf); - family::lock(); - res - } + unsafe { Flash::blocking_write_inner(start_address, buf) } } - unsafe fn blocking_write_all(start_address: u32, buf: &[u8]) -> Result<(), Error> { - family::begin_write(); - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { - let res = unsafe { family::blocking_write(address, chunk.try_into().unwrap()) }; - if res.is_err() { - family::end_write(); - return res; - } - address += WRITE_SIZE as u32; + unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + return Err(Error::Unaligned); } - family::end_write(); + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + family::clear_all_err(); + family::unlock(); + family::begin_write(); + + let _ = OnDrop::new(|| { + family::end_write(); + family::lock(); + }); + + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; + address += WRITE_SIZE as u32; + } Ok(()) } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - if to < from || to as usize > FLASH_SIZE { - return Err(Error::Size); - } - let start_address = FLASH_BASE as u32 + from; let end_address = FLASH_BASE as u32 + to; - if !is_eraseable_range(start_address, end_address) { + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } + + unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { + // Test if the address range is aligned at sector base addresses + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { return Err(Error::Unaligned); } + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - unsafe { - family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(start_address, end_address); + family::clear_all_err(); + family::unlock(); + + let _ = OnDrop::new(|| { family::lock(); - res + }); + + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + family::blocking_erase_sector(§or)?; + address += sector.size; } + Ok(()) } } @@ -135,76 +155,35 @@ pub trait FlashRegion { const SETTINGS: FlashRegionSettings; fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - if offset as usize + bytes.len() > Self::SETTINGS.size { - return Err(Error::Size); - } - - let first_address = Self::SETTINGS.base as u32 + offset; - let flash_data = unsafe { core::slice::from_raw_parts(first_address as *const u8, bytes.len()) }; - bytes.copy_from_slice(flash_data); - Ok(()) + Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) } fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - if offset as usize + buf.len() > Self::SETTINGS.size { - return Err(Error::Size); - } - if offset as usize % Self::SETTINGS.write_size != 0 || buf.len() as usize % Self::SETTINGS.write_size != 0 { - return Err(Error::Unaligned); - } - let start_address = Self::SETTINGS.base as u32 + offset; - trace!("Writing {} bytes from 0x{:x}", buf.len(), start_address); // Protect agains simultaneous write/erase to multiple regions. let _guard = take_lock_spin(); unsafe { family::clear_all_err(); - family::unlock(); - let res = Flash::blocking_write_all(start_address, buf); - family::lock(); - res + Flash::blocking_write_inner(start_address, buf) } } fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - if to < from || to as usize > Self::SETTINGS.size { - return Err(Error::Size); - } - if (from as usize % Self::SETTINGS.erase_size) != 0 || (to as usize % Self::SETTINGS.erase_size) != 0 { - return Err(Error::Unaligned); - } - let start_address = Self::SETTINGS.base as u32 + from; let end_address = Self::SETTINGS.base as u32 + to; - trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); // Protect agains simultaneous write/erase to multiple regions. let _guard = take_lock_spin(); unsafe { family::clear_all_err(); - family::unlock(); - let res = family::blocking_erase(start_address, end_address); - family::lock(); - res + Flash::blocking_erase_inner(start_address, end_address) } } } -fn is_eraseable_range(start_address: u32, end_address: u32) -> bool { - let mut address = start_address; - while address < end_address { - let sector = family::get_sector(address); - if sector.start != address { - return false; - } - address += sector.size; - } - address == end_address -} - fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { loop { if let Ok(guard) = REGION_LOCK.try_lock() { From b7dfc8de10ceddd6c2e8c078e529eb5e266ea7db Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 13:52:52 +0200 Subject: [PATCH 0756/1575] Let flash module be conditionally included --- embassy-stm32/build.rs | 2 ++ embassy-stm32/src/flash/l.rs | 4 +--- embassy-stm32/src/flash/other.rs | 11 ----------- embassy-stm32/src/lib.rs | 11 +---------- 4 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 embassy-stm32/src/flash/other.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f5bdadf5b..d179f5e0f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -162,11 +162,13 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { + #[cfg(flash)] pub struct FlashRegions<'d> { _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } + #[cfg(flash)] impl<'d> FlashRegions<'d> { pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index c8d060f0a..edcf2b2f0 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -6,8 +6,6 @@ use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; - pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -75,7 +73,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (sector.start - super::FLASH_BASE as u32) / ERASE_SIZE as u32; + let idx = (sector.start - super::FLASH_BASE as u32) / BANK1::SETTINGS.erase_size as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs deleted file mode 100644 index 4ffb4cc35..000000000 --- a/embassy-stm32/src/flash/other.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; -} - -pub struct FlashRegionSettings { - pub base: usize, - pub size: usize, - pub erase_size: usize, - pub write_size: usize, - pub erase_value: u8, -} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ddd5c0fdd..2d49c85b4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -43,17 +43,8 @@ pub mod i2c; #[cfg(crc)] pub mod crc; -#[cfg(any( - flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 -))] +#[cfg(flash)] pub mod flash; -#[cfg(not(any( - flash_l0, flash_l1, flash_wl, flash_wb, flash_l4, flash_f3, flash_f4, flash_f7, flash_h7 -)))] -pub mod flash { - mod other; - pub use other::FlashRegion; -} pub mod pwm; #[cfg(rng)] pub mod rng; From 5a12fd6c75a41b976b2c935cbd8269fd03000a9a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 13:57:33 +0200 Subject: [PATCH 0757/1575] Add unimplemented family section --- embassy-stm32/src/flash/mod.rs | 95 ++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 89fdabd4d..d4e74553b 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -9,12 +9,45 @@ pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::peripherals::FLASH; use crate::Peripheral; -#[cfg_attr(any(flash_wl, flash_wb, flash_l0, flash_l1, flash_l4), path = "l.rs")] +#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] mod family; + +#[cfg(not(any( + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 +)))] +mod family { + use super::{Error, FlashSector}; + + pub(crate) unsafe fn lock() { + unimplemented!(); + } + pub(crate) unsafe fn unlock() { + unimplemented!(); + } + pub(crate) unsafe fn begin_write() { + unimplemented!(); + } + pub(crate) unsafe fn end_write() { + unimplemented!(); + } + pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + unimplemented!(); + } + pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { + unimplemented!(); + } + pub(crate) unsafe fn clear_all_err() { + unimplemented!(); + } + pub(crate) fn get_sector(_address: u32) -> FlashSector { + unimplemented!(); + } +} + pub struct Flash<'d> { inner: PeripheralRef<'d, FLASH>, } @@ -34,6 +67,33 @@ pub struct FlashSector { pub size: u32, } +pub trait FlashRegion { + const SETTINGS: FlashRegionSettings; + + fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) + } + + fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { + let start_address = Self::SETTINGS.base as u32 + offset; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_write_inner(start_address, buf) } + } + + fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let start_address = Self::SETTINGS.base as u32 + from; + let end_address = Self::SETTINGS.base as u32 + to; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } +} + static REGION_LOCK: Mutex = Mutex::new(()); impl<'d> Flash<'d> { @@ -151,39 +211,6 @@ impl Drop for FlashRegions<'_> { } } -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; - - fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) - } - - fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + offset; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { - family::clear_all_err(); - Flash::blocking_write_inner(start_address, buf) - } - } - - fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + from; - let end_address = Self::SETTINGS.base as u32 + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { - family::clear_all_err(); - Flash::blocking_erase_inner(start_address, end_address) - } - } -} - fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { loop { if let Ok(guard) = REGION_LOCK.try_lock() { From 15e17472207fe587c38ba6cd2c2214ae4e88ee32 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 14:10:16 +0200 Subject: [PATCH 0758/1575] Fix build of not implemented family --- embassy-stm32/src/flash/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index d4e74553b..1186a182d 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -20,7 +20,7 @@ mod family; flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 )))] mod family { - use super::{Error, FlashSector}; + use super::{Error, FlashSector, WRITE_SIZE}; pub(crate) unsafe fn lock() { unimplemented!(); From 87898501a2f66ee179562fa88bfc9a1b4a2ada9b Mon Sep 17 00:00:00 2001 From: Mateusz Butkiewicz Date: Wed, 29 Mar 2023 13:27:20 +0200 Subject: [PATCH 0759/1575] feat(stm32:qspi): convert some u8 to enum variants --- embassy-stm32/src/qspi/enums.rs | 294 ++++++++++++++++++++++++++++++++ embassy-stm32/src/qspi/mod.rs | 116 ++++++------- 2 files changed, 350 insertions(+), 60 deletions(-) create mode 100644 embassy-stm32/src/qspi/enums.rs diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs new file mode 100644 index 000000000..2dbe2b061 --- /dev/null +++ b/embassy-stm32/src/qspi/enums.rs @@ -0,0 +1,294 @@ +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum QspiMode { + IndirectWrite, + IndirectRead, + AutoPolling, + MemoryMapped, +} + +impl Into for QspiMode { + fn into(self) -> u8 { + match self { + QspiMode::IndirectWrite => 0b00, + QspiMode::IndirectRead => 0b01, + QspiMode::AutoPolling => 0b10, + QspiMode::MemoryMapped => 0b11, + } + } +} + +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum QspiWidth { + NONE, + SING, + DUAL, + QUAD, +} + +impl Into for QspiWidth { + fn into(self) -> u8 { + match self { + QspiWidth::NONE => 0b00, + QspiWidth::SING => 0b01, + QspiWidth::DUAL => 0b10, + QspiWidth::QUAD => 0b11, + } + } +} + +#[derive(Copy, Clone)] +pub enum MemorySize { + _1KiB, + _2KiB, + _4KiB, + _8KiB, + _16KiB, + _32KiB, + _64KiB, + _128KiB, + _256KiB, + _512KiB, + _1MiB, + _2MiB, + _4MiB, + _8MiB, + _16MiB, + _32MiB, + _64MiB, + _128MiB, + _256MiB, + _512MiB, + _1GiB, + _2GiB, + _4GiB, + Other(u8), +} + +impl Into for MemorySize { + fn into(self) -> u8 { + match self { + MemorySize::_1KiB => 9, + MemorySize::_2KiB => 10, + MemorySize::_4KiB => 11, + MemorySize::_8KiB => 12, + MemorySize::_16KiB => 13, + MemorySize::_32KiB => 14, + MemorySize::_64KiB => 15, + MemorySize::_128KiB => 16, + MemorySize::_256KiB => 17, + MemorySize::_512KiB => 18, + MemorySize::_1MiB => 19, + MemorySize::_2MiB => 20, + MemorySize::_4MiB => 21, + MemorySize::_8MiB => 22, + MemorySize::_16MiB => 23, + MemorySize::_32MiB => 24, + MemorySize::_64MiB => 25, + MemorySize::_128MiB => 26, + MemorySize::_256MiB => 27, + MemorySize::_512MiB => 28, + MemorySize::_1GiB => 29, + MemorySize::_2GiB => 30, + MemorySize::_4GiB => 31, + MemorySize::Other(val) => val, + } + } +} + +#[derive(Copy, Clone)] +pub enum AddressSize { + _8Bit, + _16Bit, + _24bit, + _32bit, +} + +impl Into for AddressSize { + fn into(self) -> u8 { + match self { + AddressSize::_8Bit => 0b00, + AddressSize::_16Bit => 0b01, + AddressSize::_24bit => 0b10, + AddressSize::_32bit => 0b11, + } + } +} + +#[derive(Copy, Clone)] +pub enum ChipSelectHightTime { + _1Cycle, + _2Cycle, + _3Cycle, + _4Cycle, + _5Cycle, + _6Cycle, + _7Cycle, + _8Cycle, +} + +impl Into for ChipSelectHightTime { + fn into(self) -> u8 { + match self { + ChipSelectHightTime::_1Cycle => 0, + ChipSelectHightTime::_2Cycle => 1, + ChipSelectHightTime::_3Cycle => 2, + ChipSelectHightTime::_4Cycle => 3, + ChipSelectHightTime::_5Cycle => 4, + ChipSelectHightTime::_6Cycle => 5, + ChipSelectHightTime::_7Cycle => 6, + ChipSelectHightTime::_8Cycle => 7, + } + } +} + +#[derive(Copy, Clone)] +pub enum FIFOThresholdLevel { + _1Bytes, + _2Bytes, + _3Bytes, + _4Bytes, + _5Bytes, + _6Bytes, + _7Bytes, + _8Bytes, + _9Bytes, + _10Bytes, + _11Bytes, + _12Bytes, + _13Bytes, + _14Bytes, + _15Bytes, + _16Bytes, + _17Bytes, + _18Bytes, + _19Bytes, + _20Bytes, + _21Bytes, + _22Bytes, + _23Bytes, + _24Bytes, + _25Bytes, + _26Bytes, + _27Bytes, + _28Bytes, + _29Bytes, + _30Bytes, + _31Bytes, + _32Bytes, +} + +impl Into for FIFOThresholdLevel { + fn into(self) -> u8 { + match self { + FIFOThresholdLevel::_1Bytes => 0, + FIFOThresholdLevel::_2Bytes => 1, + FIFOThresholdLevel::_3Bytes => 2, + FIFOThresholdLevel::_4Bytes => 3, + FIFOThresholdLevel::_5Bytes => 4, + FIFOThresholdLevel::_6Bytes => 5, + FIFOThresholdLevel::_7Bytes => 6, + FIFOThresholdLevel::_8Bytes => 7, + FIFOThresholdLevel::_9Bytes => 8, + FIFOThresholdLevel::_10Bytes => 9, + FIFOThresholdLevel::_11Bytes => 10, + FIFOThresholdLevel::_12Bytes => 11, + FIFOThresholdLevel::_13Bytes => 12, + FIFOThresholdLevel::_14Bytes => 13, + FIFOThresholdLevel::_15Bytes => 14, + FIFOThresholdLevel::_16Bytes => 15, + FIFOThresholdLevel::_17Bytes => 16, + FIFOThresholdLevel::_18Bytes => 17, + FIFOThresholdLevel::_19Bytes => 18, + FIFOThresholdLevel::_20Bytes => 19, + FIFOThresholdLevel::_21Bytes => 20, + FIFOThresholdLevel::_22Bytes => 21, + FIFOThresholdLevel::_23Bytes => 22, + FIFOThresholdLevel::_24Bytes => 23, + FIFOThresholdLevel::_25Bytes => 24, + FIFOThresholdLevel::_26Bytes => 25, + FIFOThresholdLevel::_27Bytes => 26, + FIFOThresholdLevel::_28Bytes => 27, + FIFOThresholdLevel::_29Bytes => 28, + FIFOThresholdLevel::_30Bytes => 29, + FIFOThresholdLevel::_31Bytes => 30, + FIFOThresholdLevel::_32Bytes => 31, + } + } +} + +#[derive(Copy, Clone)] +pub enum DummyCycles { + _0, + _1, + _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, +} + +impl Into for DummyCycles { + fn into(self) -> u8 { + match self { + DummyCycles::_0 => 0, + DummyCycles::_1 => 1, + DummyCycles::_2 => 2, + DummyCycles::_3 => 3, + DummyCycles::_4 => 4, + DummyCycles::_5 => 5, + DummyCycles::_6 => 6, + DummyCycles::_7 => 7, + DummyCycles::_8 => 8, + DummyCycles::_9 => 9, + DummyCycles::_10 => 10, + DummyCycles::_11 => 11, + DummyCycles::_12 => 12, + DummyCycles::_13 => 13, + DummyCycles::_14 => 14, + DummyCycles::_15 => 15, + DummyCycles::_16 => 16, + DummyCycles::_17 => 17, + DummyCycles::_18 => 18, + DummyCycles::_19 => 19, + DummyCycles::_20 => 20, + DummyCycles::_21 => 21, + DummyCycles::_22 => 22, + DummyCycles::_23 => 23, + DummyCycles::_24 => 24, + DummyCycles::_25 => 25, + DummyCycles::_26 => 26, + DummyCycles::_27 => 27, + DummyCycles::_28 => 28, + DummyCycles::_29 => 29, + DummyCycles::_30 => 30, + DummyCycles::_31 => 31, + } + } +} diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index f375d1b46..f33319620 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -1,6 +1,9 @@ #![macro_use] +pub mod enums; + use embassy_hal_common::{into_ref, PeripheralRef}; +use enums::*; use crate::dma::TransferOptions; use crate::gpio::sealed::AFType; @@ -9,37 +12,24 @@ use crate::pac::quadspi::Quadspi as Regs; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; -pub struct QspiWidth; - -#[allow(dead_code)] -impl QspiWidth { - pub const NONE: u8 = 0b00; - pub const SING: u8 = 0b01; - pub const DUAL: u8 = 0b10; - pub const QUAD: u8 = 0b11; -} - -struct QspiMode; - -#[allow(dead_code)] -impl QspiMode { - pub const INDIRECT_WRITE: u8 = 0b00; - pub const INDIRECT_READ: u8 = 0b01; - pub const AUTO_POLLING: u8 = 0b10; - pub const MEMORY_MAPPED: u8 = 0b11; -} - -pub struct QspiTransaction { - pub iwidth: u8, - pub awidth: u8, - pub dwidth: u8, +pub struct TransferConfig { + /// Instraction width (IMODE) + pub iwidth: QspiWidth, + /// Address width (ADMODE) + pub awidth: QspiWidth, + /// Data width (DMODE) + pub dwidth: QspiWidth, + /// Instruction Id pub instruction: u8, + /// Flash memory address pub address: Option, - pub dummy: u8, + /// Number of dummy cycles (DCYC) + pub dummy: DummyCycles, + /// Length of data pub data_len: Option, } -impl Default for QspiTransaction { +impl Default for TransferConfig { fn default() -> Self { Self { iwidth: QspiWidth::NONE, @@ -47,28 +37,34 @@ impl Default for QspiTransaction { dwidth: QspiWidth::NONE, instruction: 0, address: None, - dummy: 0, + dummy: DummyCycles::_0, data_len: None, } } } pub struct Config { - pub memory_size: u8, - pub address_size: u8, + /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. + /// If you need other value the whose predefined use `Other` variant. + pub memory_size: MemorySize, + /// Address size (8/16/24/32-bit) + pub address_size: AddressSize, + /// Scalar factor for generating CLK [0-255] pub prescaler: u8, - pub fifo_threshold: u8, - pub cs_high_time: u8, + /// Number of bytes to trigger FIFO threshold flag. + pub fifo_threshold: FIFOThresholdLevel, + /// Minimum number of cycles that chip select must be high between issued commands + pub cs_high_time: ChipSelectHightTime, } impl Default for Config { fn default() -> Self { Self { - memory_size: 0, - address_size: 2, + memory_size: MemorySize::Other(0), + address_size: AddressSize::_24bit, prescaler: 128, - fifo_threshold: 16, - cs_high_time: 4, + fifo_threshold: FIFOThresholdLevel::_17Bytes, + cs_high_time: ChipSelectHightTime::_5Cycle, } } } @@ -143,7 +139,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { T::enable(); unsafe { - T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold)); + T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); while T::REGS.sr().read().busy() {} @@ -152,8 +148,8 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { w.set_en(true); }); T::REGS.dcr().write(|w| { - w.set_fsize(config.memory_size); - w.set_csht(config.cs_high_time); + w.set_fsize(config.memory_size.into()); + w.set_csht(config.cs_high_time.into()); w.set_ckmode(false); }); } @@ -171,25 +167,25 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } } - pub fn command(&mut self, transaction: QspiTransaction) { + pub fn command(&mut self, transaction: TransferConfig) { unsafe { T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); while !T::REGS.sr().read().tcf() {} T::REGS.fcr().modify(|v| v.set_ctcf(true)); } } - pub fn read(&mut self, buf: &mut [u8], transaction: QspiTransaction) { + pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { unsafe { T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); if let Some(len) = transaction.data_len { let current_ar = T::REGS.ar().read().address(); T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::INDIRECT_READ); + v.set_fmode(QspiMode::IndirectRead.into()); }); T::REGS.ar().write(|v| { v.set_address(current_ar); @@ -206,14 +202,14 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } } - pub fn write(&mut self, buf: &[u8], transaction: QspiTransaction) { + pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { unsafe { T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); if let Some(len) = transaction.data_len { T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::INDIRECT_WRITE); + v.set_fmode(QspiMode::IndirectWrite.into()); }); for idx in 0..len { @@ -227,18 +223,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } } - pub fn read_dma(&mut self, buf: &mut [u8], transaction: QspiTransaction) + pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) where Dma: QuadDma, { unsafe { - self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); let request = self.dma.request(); let options = TransferOptions::default(); T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::INDIRECT_READ); + v.set_fmode(QspiMode::IndirectRead.into()); }); let current_ar = T::REGS.ar().read().address(); T::REGS.ar().write(|v| { @@ -254,18 +250,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } } - pub fn write_dma(&mut self, buf: &[u8], transaction: QspiTransaction) + pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) where Dma: QuadDma, { unsafe { - self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); let request = self.dma.request(); let options = TransferOptions::default(); T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::INDIRECT_WRITE); + v.set_fmode(QspiMode::IndirectWrite.into()); }); self.dma @@ -277,7 +273,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } } - fn setup_transaction(&mut self, fmode: u8, transaction: &QspiTransaction) { + fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { unsafe { T::REGS.fcr().modify(|v| { v.set_csmf(true); @@ -293,14 +289,14 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } T::REGS.ccr().write(|v| { - v.set_fmode(fmode); - v.set_imode(transaction.iwidth); + v.set_fmode(fmode.into()); + v.set_imode(transaction.iwidth.into()); v.set_instruction(transaction.instruction); - v.set_admode(transaction.awidth); - v.set_adsize(self.config.address_size); - v.set_dmode(transaction.dwidth); - v.set_abmode(QspiWidth::NONE); - v.set_dcyc(transaction.dummy); + v.set_admode(transaction.awidth.into()); + v.set_adsize(self.config.address_size.into()); + v.set_dmode(transaction.dwidth.into()); + v.set_abmode(QspiWidth::NONE.into()); + v.set_dcyc(transaction.dummy.into()); }); if let Some(addr) = transaction.address { From fc8c83e00ad6ec810bc73e750e8e943b3e725156 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 14:50:19 +0200 Subject: [PATCH 0760/1575] Fix h7 compile error --- embassy-stm32/src/flash/h7.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 28999999d..82dd4b28b 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -79,7 +79,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - let bank = pac::FLASH::bank(if sector.index >= 8 { 1 } else { 0 }); + let bank = pac::FLASH.bank(if sector.index >= 8 { 1 } else { 0 }); let sector = sector.index % 8; bank.cr().modify(|w| { w.set_ser(true); From 68c260edeb8822411ac03d569c1c0d91be2b98a5 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 15:03:48 +0200 Subject: [PATCH 0761/1575] Use stm32-metapac v2 --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1af439ebe..4f660187c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "1" +stm32-metapac = "2" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" From ef1890e9110c8ef3553e6a2d0979dfb52520b025 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 29 Mar 2023 15:45:18 +0200 Subject: [PATCH 0762/1575] Remove flash operations from FlashRegion trait and move to common module --- embassy-stm32/src/flash/common.rs | 177 ++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 200 ++---------------------------- 2 files changed, 186 insertions(+), 191 deletions(-) create mode 100644 embassy-stm32/src/flash/common.rs diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs new file mode 100644 index 000000000..f92236bb0 --- /dev/null +++ b/embassy-stm32/src/flash/common.rs @@ -0,0 +1,177 @@ +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::{Mutex, MutexGuard}; +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; + +use super::{family, Error, FlashRegion}; +pub use crate::_generated::flash_regions::*; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use crate::Peripheral; + +pub struct Flash<'d> { + inner: PeripheralRef<'d, crate::peripherals::FLASH>, +} + +impl<'d> Flash<'d> { + pub fn new(p: impl Peripheral

+ 'd) -> Self { + into_ref!(p); + Self { inner: p } + } + + pub fn into_regions(self) -> FlashRegions<'d> { + let mut flash = self; + let p = unsafe { flash.inner.clone_unchecked() }; + FlashRegions::new(p) + } + + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) + } + + fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) + } + + pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { + let start_address = FLASH_BASE as u32 + offset; + + // No need to take lock here as we only have one mut flash reference. + + unsafe { Flash::blocking_write_inner(start_address, buf) } + } + + unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + family::clear_all_err(); + family::unlock(); + family::begin_write(); + + let _ = OnDrop::new(|| { + family::end_write(); + family::lock(); + }); + + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; + address += WRITE_SIZE as u32; + } + Ok(()) + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let start_address = FLASH_BASE as u32 + from; + let end_address = FLASH_BASE as u32 + to; + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } + + unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { + // Test if the address range is aligned at sector base addresses + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); + + family::clear_all_err(); + family::unlock(); + + let _ = OnDrop::new(|| { + family::lock(); + }); + + let mut address = start_address; + while address < end_address { + let sector = family::get_sector(address); + family::blocking_erase_sector(§or)?; + address += sector.size; + } + Ok(()) + } +} + +impl Drop for Flash<'_> { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + +static REGION_LOCK: Mutex = Mutex::new(()); + +fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { + loop { + if let Ok(guard) = REGION_LOCK.try_lock() { + return guard; + } + } +} + +foreach_flash_region! { + ($name:ident) => { + impl ErrorType for crate::_generated::flash_regions::$name { + type Error = Error; + } + + impl ReadNorFlash for crate::_generated::flash_regions::$name { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) + } + + fn capacity(&self) -> usize { + ::SETTINGS.size + } + } + + impl NorFlash for crate::_generated::flash_regions::$name { + const WRITE_SIZE: usize = ::SETTINGS.write_size; + const ERASE_SIZE: usize = ::SETTINGS.erase_size; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + let start_address = Self::SETTINGS.base as u32 + from; + let end_address = Self::SETTINGS.base as u32 + to; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_erase_inner(start_address, end_address) } + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + let start_address = Self::SETTINGS.base as u32 + offset; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + unsafe { Flash::blocking_write_inner(start_address, bytes) } + } + } + }; +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1186a182d..ec7c66947 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,13 +1,4 @@ -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::mutex::{Mutex, MutexGuard}; -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; - -pub use crate::_generated::flash_regions::*; -pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; -use crate::peripherals::FLASH; -use crate::Peripheral; +use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] @@ -48,8 +39,14 @@ mod family { } } -pub struct Flash<'d> { - inner: PeripheralRef<'d, FLASH>, +#[cfg(flash)] +mod common; + +#[cfg(flash)] +pub use common::*; + +pub trait FlashRegion { + const SETTINGS: FlashRegionSettings; } pub struct FlashRegionSettings { @@ -67,158 +64,12 @@ pub struct FlashSector { pub size: u32, } -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; - - fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) - } - - fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + offset; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_write_inner(start_address, buf) } - } - - fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = Self::SETTINGS.base as u32 + from; - let end_address = Self::SETTINGS.base as u32 + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } -} - -static REGION_LOCK: Mutex = Mutex::new(()); - -impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd) -> Self { - into_ref!(p); - Self { inner: p } - } - - pub fn into_regions(self) -> FlashRegions<'d> { - let mut flash = self; - let p = unsafe { flash.inner.clone_unchecked() }; - FlashRegions::new(p) - } - - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) - } - - fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - - let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; - bytes.copy_from_slice(flash_data); - Ok(()) - } - - pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + offset; - - // No need to take lock here as we only have one mut flash reference. - - unsafe { Flash::blocking_write_inner(start_address, buf) } - } - - unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { - return Err(Error::Unaligned); - } - - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); - - family::clear_all_err(); - family::unlock(); - family::begin_write(); - - let _ = OnDrop::new(|| { - family::end_write(); - family::lock(); - }); - - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { - unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; - address += WRITE_SIZE as u32; - } - Ok(()) - } - - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + from; - let end_address = FLASH_BASE as u32 + to; - - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } - - unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { - // Test if the address range is aligned at sector base addresses - let mut address = start_address; - while address < end_address { - let sector = family::get_sector(address); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } - - trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - - family::clear_all_err(); - family::unlock(); - - let _ = OnDrop::new(|| { - family::lock(); - }); - - let mut address = start_address; - while address < end_address { - let sector = family::get_sector(address); - family::blocking_erase_sector(§or)?; - address += sector.size; - } - Ok(()) - } -} - -impl Drop for Flash<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} - impl Drop for FlashRegions<'_> { fn drop(&mut self) { unsafe { family::lock() }; } } -fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { - loop { - if let Ok(guard) = REGION_LOCK.try_lock() { - return guard; - } - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -240,36 +91,3 @@ impl NorFlashError for Error { } } } - -foreach_flash_region! { - ($name:ident) => { - impl ErrorType for crate::_generated::flash_regions::$name { - type Error = Error; - } - - impl ReadNorFlash for crate::_generated::flash_regions::$name { - const READ_SIZE: usize = ::SETTINGS.write_size; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) - } - - fn capacity(&self) -> usize { - ::SETTINGS.size - } - } - - impl NorFlash for crate::_generated::flash_regions::$name { - const WRITE_SIZE: usize = ::SETTINGS.write_size; - const ERASE_SIZE: usize = ::SETTINGS.erase_size; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } - } - }; -} From def576ac4688fb2113aacca46d5bfb4001c8dc1a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 04:24:41 +0200 Subject: [PATCH 0763/1575] Remove FlashRegion trait and rename Settings to FlashRegion --- embassy-stm32/build.rs | 53 +++++++++-------- embassy-stm32/src/flash/common.rs | 98 ++++++++++++++++++++++--------- embassy-stm32/src/flash/mod.rs | 16 ++--- 3 files changed, 103 insertions(+), 64 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d179f5e0f..8f34f510a 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -112,64 +112,58 @@ fn main() { .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) .collect(); for region in flash_memory_regions.iter() { - let region_name = get_flash_region_name(region.name); - let region_type = format_ident!("{}", region_name); - let settings_name = format_ident!("{}_SETTINGS", region_name); - let base = region.address as usize; - let size = region.size as usize; + let region_name = format_ident!("{}", get_flash_region_name(region.name)); + let base = region.address; + let size = region.size; let settings = region.settings.as_ref().unwrap(); - let erase_size = settings.erase_size as usize; - let write_size = settings.write_size as usize; + let erase_size = settings.erase_size; + let write_size = settings.write_size; let erase_value = settings.erase_value; flash_regions.extend(quote! { - #[allow(non_camel_case_types)] - pub struct #region_type(()); - }); - - flash_regions.extend(quote! { - pub const #settings_name: crate::flash::FlashRegionSettings = crate::flash::FlashRegionSettings { + pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { base: #base, size: #size, erase_size: #erase_size, write_size: #write_size, erase_value: #erase_value, }; + }); - impl crate::flash::FlashRegion for #region_type { - const SETTINGS: crate::flash::FlashRegionSettings = #settings_name; - } + let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); + flash_regions.extend(quote! { + pub struct #region_type(pub(crate) &'static crate::flash::FlashRegion); }); } - let (fields, (inits, settings)): (Vec, (Vec, Vec)) = flash_memory_regions + let (fields, (inits, region_names)): (Vec, (Vec, Vec)) = flash_memory_regions .iter() .map(|f| { let region_name = get_flash_region_name(f.name); let field_name = format_ident!("{}", region_name.to_lowercase()); - let field_type = format_ident!("{}", region_name); + let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); let field = quote! { pub #field_name: #field_type }; + let region_name = format_ident!("{}", region_name); let init = quote! { - #field_name: #field_type(()) + #field_name: #field_type(&#region_name) }; - let settings_name = format_ident!("{}_SETTINGS", region_name); - (field, (init, settings_name)) + (field, (init, region_name)) }) .unzip(); let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { #[cfg(flash)] - pub struct FlashRegions<'d> { + pub struct FlashLayout<'d> { _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } #[cfg(flash)] - impl<'d> FlashRegions<'d> { + impl<'d> FlashLayout<'d> { pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { _inner: p, @@ -178,8 +172,8 @@ fn main() { } } - pub const FLASH_REGIONS: [&crate::flash::FlashRegionSettings; #regions_len] = [ - #(&#settings),* + pub const FLASH_REGIONS: [&crate::flash::FlashRegion; #regions_len] = [ + #(&#region_names),* ]; }); @@ -651,8 +645,11 @@ fn main() { .iter() .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { + let settings = m.settings.as_ref().unwrap(); let mut row = Vec::new(); - row.push(get_flash_region_name(m.name)); + row.push(get_flash_region_type_name(m.name)); + row.push(settings.write_size.to_string()); + row.push(settings.erase_size.to_string()); flash_regions_table.push(row); } @@ -906,6 +903,10 @@ macro_rules! {} {{ .unwrap(); } +fn get_flash_region_type_name(name: &str) -> String { + name.replace("BANK_", "Bank").replace("REGION_", "Region") +} + fn get_flash_region_name(name: &str) -> String { name.replace("BANK_", "BANK").replace("REGION_", "REGION") } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index f92236bb0..b190a5a07 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -2,7 +2,6 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; -use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use super::{family, Error, FlashRegion}; pub use crate::_generated::flash_regions::*; @@ -19,10 +18,8 @@ impl<'d> Flash<'d> { Self { inner: p } } - pub fn into_regions(self) -> FlashRegions<'d> { - let mut flash = self; - let p = unsafe { flash.inner.clone_unchecked() }; - FlashRegions::new(p) + pub fn into_regions(self) -> FlashLayout<'d> { + FlashLayout::new(self.release()) } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { @@ -114,6 +111,11 @@ impl<'d> Flash<'d> { } Ok(()) } + + pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { + let mut flash = self; + unsafe { flash.inner.clone_unchecked() } + } } impl Drop for Flash<'_> { @@ -132,45 +134,85 @@ fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { } } +impl FlashRegion { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + unsafe { self.blocking_read_inner(offset, bytes) } + } + + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { self.blocking_write_inner(offset, bytes) } + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { self.blocking_erase_inner(from, to) } + } + + unsafe fn blocking_read_inner(&self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + Flash::blocking_read_inner(self.base + offset, bytes) + } + + unsafe fn blocking_write_inner(&self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let start_address = self.base + offset; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + Flash::blocking_write_inner(start_address, bytes) + } + + unsafe fn blocking_erase_inner(&self, from: u32, to: u32) -> Result<(), Error> { + let start_address = self.base + from; + let end_address = self.base + to; + + // Protect agains simultaneous write/erase to multiple regions. + let _guard = take_lock_spin(); + + Flash::blocking_erase_inner(start_address, end_address) + } +} + foreach_flash_region! { - ($name:ident) => { - impl ErrorType for crate::_generated::flash_regions::$name { + ($type_name:ident, $write_size:ident, $erase_size:ident) => { + impl crate::_generated::flash_regions::$type_name { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + unsafe { self.0.blocking_read_inner(offset, bytes) } + } + + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { self.0.blocking_write_inner(offset, bytes) } + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { self.0.blocking_erase_inner(from, to) } + } + } + + impl ErrorType for crate::_generated::flash_regions::$type_name { type Error = Error; } - impl ReadNorFlash for crate::_generated::flash_regions::$name { + impl ReadNorFlash for crate::_generated::flash_regions::$type_name { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - Flash::blocking_read_inner(Self::SETTINGS.base as u32 + offset, bytes) + unsafe { self.0.blocking_read_inner(offset, bytes) } } fn capacity(&self) -> usize { - ::SETTINGS.size + self.0.size as usize } } - impl NorFlash for crate::_generated::flash_regions::$name { - const WRITE_SIZE: usize = ::SETTINGS.write_size; - const ERASE_SIZE: usize = ::SETTINGS.erase_size; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let start_address = Self::SETTINGS.base as u32 + from; - let end_address = Self::SETTINGS.base as u32 + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } + impl NorFlash for crate::_generated::flash_regions::$type_name { + const WRITE_SIZE: usize = $write_size; + const ERASE_SIZE: usize = $erase_size; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - let start_address = Self::SETTINGS.base as u32 + offset; + unsafe { self.0.blocking_write_inner(offset, bytes) } + } - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - unsafe { Flash::blocking_write_inner(start_address, bytes) } + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + unsafe { self.0.blocking_erase_inner(from, to) } } } }; diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index ec7c66947..1e7d4c657 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -45,15 +45,11 @@ mod common; #[cfg(flash)] pub use common::*; -pub trait FlashRegion { - const SETTINGS: FlashRegionSettings; -} - -pub struct FlashRegionSettings { - pub base: usize, - pub size: usize, - pub erase_size: usize, - pub write_size: usize, +pub struct FlashRegion { + pub base: u32, + pub size: u32, + pub erase_size: u32, + pub write_size: u32, pub erase_value: u8, } @@ -64,7 +60,7 @@ pub struct FlashSector { pub size: u32, } -impl Drop for FlashRegions<'_> { +impl Drop for FlashLayout<'_> { fn drop(&mut self) { unsafe { family::lock() }; } From 91d8afd371c20d21765713a45625f62ce25d97b6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 05:27:57 +0200 Subject: [PATCH 0764/1575] Add AltFlashLayout for supported F4 chips --- embassy-stm32/build.rs | 16 +++++-- embassy-stm32/src/flash/common.rs | 4 +- embassy-stm32/src/flash/f4.rs | 75 +++++++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 58 +++++++++++++----------- 4 files changed, 120 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ec9b6dc09..6b203a3f1 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -910,10 +910,18 @@ macro_rules! {} {{ .unwrap(); } -fn get_flash_region_type_name(name: &str) -> String { - name.replace("BANK_", "Bank").replace("REGION_", "Region") +fn get_flash_region_name(name: &str) -> String { + let name = name.replace("BANK_", "BANK").replace("REGION_", "REGION"); + if name.contains("REGION") { + name + } else { + name + "_REGION" + } } -fn get_flash_region_name(name: &str) -> String { - name.replace("BANK_", "BANK").replace("REGION_", "REGION") +fn get_flash_region_type_name(name: &str) -> String { + get_flash_region_name(name) + .replace("BANK", "Bank") + .replace("REGION", "Region") + .replace("_", "") } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index b190a5a07..c239d9673 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -3,9 +3,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; -use super::{family, Error, FlashRegion}; -pub use crate::_generated::flash_regions::*; -pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{family, Error, FlashLayout, FlashRegion, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::Peripheral; pub struct Flash<'d> { diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index cb420c69f..257aae2d0 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -11,7 +11,82 @@ const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; const LARGE_SECTOR_SIZE: u32 = 128 * 1024; const SECOND_BANK_SECTOR_OFFSET: u8 = 12; +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +mod alt_regions { + use embassy_hal_common::PeripheralRef; + use stm32_metapac::FLASH_SIZE; + + use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; + use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashRegion}; + use crate::peripherals::FLASH; + + pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { + size: 3 * BANK1_REGION3.erase_size, + ..BANK1_REGION3 + }; + pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion { + base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2, + ..BANK1_REGION1 + }; + pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion { + base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2, + ..BANK1_REGION2 + }; + pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion { + base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2, + size: 3 * BANK1_REGION3.erase_size, + ..BANK1_REGION3 + }; + + pub type AltBank1Region1 = Bank1Region1; + pub type AltBank1Region2 = Bank1Region2; + pub struct AltBank1Region3(&'static FlashRegion); + pub struct AltBank2Region1(&'static FlashRegion); + pub struct AltBank2Region2(&'static FlashRegion); + pub struct AltBank2Region3(&'static FlashRegion); + + pub struct AltFlashLayout<'d> { + _inner: PeripheralRef<'d, FLASH>, + pub bank1_region1: AltBank1Region1, + pub bank1_region2: AltBank1Region2, + pub bank1_region3: AltBank1Region3, + pub bank2_region1: AltBank2Region1, + pub bank2_region2: AltBank2Region2, + pub bank2_region3: AltBank2Region3, + } + + impl<'d> Flash<'d> { + pub fn into_alt_regions(self) -> AltFlashLayout<'d> { + unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + AltFlashLayout { + _inner: self.release(), + bank1_region1: Bank1Region1(&BANK1_REGION1), + bank1_region2: Bank1Region2(&BANK1_REGION2), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3), + } + } + } + + impl Drop for AltFlashLayout<'_> { + fn drop(&mut self) { + unsafe { + super::lock(); + crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) + }; + } + } +} + +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +pub use alt_regions::AltFlashLayout; + fn is_dual_bank() -> bool { + // let asd: super::Bank1Region1; + // let sad = &super::BANK_1_REGION_1; + match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration 1024 => { diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1e7d4c657..7dc714715 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,5 +1,35 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; +#[cfg(flash)] +mod common; + +#[cfg(flash)] +pub use common::*; + +pub use crate::_generated::flash_regions::*; +pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; + +pub struct FlashRegion { + pub base: u32, + pub size: u32, + pub erase_size: u32, + pub write_size: u32, + pub erase_value: u8, +} + +#[derive(Debug, PartialEq)] +pub struct FlashSector { + pub index: u8, + pub start: u32, + pub size: u32, +} + +impl Drop for FlashLayout<'_> { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] @@ -39,32 +69,8 @@ mod family { } } -#[cfg(flash)] -mod common; - -#[cfg(flash)] -pub use common::*; - -pub struct FlashRegion { - pub base: u32, - pub size: u32, - pub erase_size: u32, - pub write_size: u32, - pub erase_value: u8, -} - -#[derive(Debug, PartialEq)] -pub struct FlashSector { - pub index: u8, - pub start: u32, - pub size: u32, -} - -impl Drop for FlashLayout<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} +#[allow(unused_imports)] +pub use family::*; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From e3c4e00be0469030f163568efa2902d73e2b8a4c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 06:01:56 +0200 Subject: [PATCH 0765/1575] Align families --- embassy-stm32/src/flash/common.rs | 6 +++++ embassy-stm32/src/flash/f3.rs | 10 +++---- embassy-stm32/src/flash/f4.rs | 3 --- embassy-stm32/src/flash/h7.rs | 14 ++++------ embassy-stm32/src/flash/l.rs | 8 +++--- embassy-stm32/src/flash/mod.rs | 44 +++++++------------------------ embassy-stm32/src/flash/other.rs | 27 +++++++++++++++++++ embassy-stm32/src/lib.rs | 1 - 8 files changed, 55 insertions(+), 58 deletions(-) create mode 100644 embassy-stm32/src/flash/other.rs diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index c239d9673..6534e1b8e 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -122,6 +122,12 @@ impl Drop for Flash<'_> { } } +impl Drop for FlashLayout<'_> { + fn drop(&mut self) { + unsafe { family::lock() }; + } +} + static REGION_LOCK: Mutex = Mutex::new(()); fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 7b339ccc5..5ac1d8fdf 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -3,12 +3,10 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; +use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -46,7 +44,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_per(true); }); - pac::FLASH.ar().write(|w| w.set_far(sector.first)); + pac::FLASH.ar().write(|w| w.set_far(sector.start)); pac::FLASH.cr().modify(|w| { w.set_strt(true); @@ -103,11 +101,11 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1::SETTINGS.erase_size as u32; + let sector_size = BANK1_REGION.erase_size; let index = address / sector_size; FlashSector { index: index as u8, - start: BANK1::SETTINGS.base as u32 + index * sector_size, + start: BANK1_REGION.base + index * sector_size, size: sector_size, } } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 257aae2d0..52ef7ad50 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -84,9 +84,6 @@ mod alt_regions { pub use alt_regions::AltFlashLayout; fn is_dual_bank() -> bool { - // let asd: super::Bank1Region1; - // let sad = &super::BANK_1_REGION_1; - match FLASH_SIZE / 1024 { // 1 MB devices depend on configuration 1024 => { diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 82dd4b28b..360ad77db 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -3,15 +3,12 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FlashSector, BANK1, FLASH_SIZE, WRITE_SIZE}; +use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const ERASE_SIZE: usize = BANK1::SETTINGS.erase_size; -const SECOND_BANK_OFFSET: usize = 0x0010_0000; - const fn is_dual_bank() -> bool { - FLASH_SIZE / 2 > ERASE_SIZE + FLASH_REGIONS.len() == 2 } pub(crate) unsafe fn lock() { @@ -24,7 +21,6 @@ pub(crate) unsafe fn lock() { pub(crate) unsafe fn unlock() { pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0x4567_0123)); pac::FLASH.bank(0).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); - if is_dual_bank() { pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0x4567_0123)); pac::FLASH.bank(1).keyr().write(|w| w.set_keyr(0xCDEF_89AB)); @@ -39,7 +35,7 @@ pub(crate) unsafe fn end_write() {} pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { // We cannot have the write setup sequence in begin_write as it depends on the address - let bank = if !is_dual_bank() || (start_address - super::FLASH_BASE as u32) < SECOND_BANK_OFFSET as u32 { + let bank = if start_address < BANK1_REGION.end() { pac::FLASH.bank(0) } else { pac::FLASH.bank(1) @@ -181,11 +177,11 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1::SETTINGS.erase_size as u32; + let sector_size = BANK1_REGION.erase_size; let index = address / sector_size; FlashSector { index: index as u8, - start: BANK1::SETTINGS.base as u32 + index * sector_size, + start: BANK1_REGION.base + index * sector_size, size: sector_size, } } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index edcf2b2f0..f4b11011f 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -2,7 +2,7 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashRegion, FlashSector, BANK1, WRITE_SIZE}; +use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -73,7 +73,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (sector.start - super::FLASH_BASE as u32) / BANK1::SETTINGS.erase_size as u32; + let idx = (sector.start - super::FLASH_BASE as u32) / BANK1_REGION.erase_size as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; @@ -182,11 +182,11 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1::SETTINGS.erase_size as u32; + let sector_size = BANK1_REGION.erase_size; let index = address / sector_size; FlashSector { index: index as u8, - start: BANK1::SETTINGS.base as u32 + index * sector_size, + start: BANK1_REGION.base + index * sector_size, size: sector_size, } } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 7dc714715..110b81496 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -24,9 +24,9 @@ pub struct FlashSector { pub size: u32, } -impl Drop for FlashLayout<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; +impl FlashRegion { + pub const fn end(&self) -> u32 { + self.base + self.size } } @@ -35,40 +35,14 @@ impl Drop for FlashLayout<'_> { #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] +#[cfg_attr( + not(any( + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 + )), + path = "other.rs" +)] mod family; -#[cfg(not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 -)))] -mod family { - use super::{Error, FlashSector, WRITE_SIZE}; - - pub(crate) unsafe fn lock() { - unimplemented!(); - } - pub(crate) unsafe fn unlock() { - unimplemented!(); - } - pub(crate) unsafe fn begin_write() { - unimplemented!(); - } - pub(crate) unsafe fn end_write() { - unimplemented!(); - } - pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { - unimplemented!(); - } - pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { - unimplemented!(); - } - pub(crate) unsafe fn clear_all_err() { - unimplemented!(); - } - pub(crate) fn get_sector(_address: u32) -> FlashSector { - unimplemented!(); - } -} - #[allow(unused_imports)] pub use family::*; diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs new file mode 100644 index 000000000..fd3551224 --- /dev/null +++ b/embassy-stm32/src/flash/other.rs @@ -0,0 +1,27 @@ +#[allow(unused)] +use super::{Error, FlashSector, WRITE_SIZE}; + +pub(crate) unsafe fn lock() { + unimplemented!(); +} +pub(crate) unsafe fn unlock() { + unimplemented!(); +} +pub(crate) unsafe fn begin_write() { + unimplemented!(); +} +pub(crate) unsafe fn end_write() { + unimplemented!(); +} +pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + unimplemented!(); +} +pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { + unimplemented!(); +} +pub(crate) unsafe fn clear_all_err() { + unimplemented!(); +} +pub(crate) fn get_sector(_address: u32) -> FlashSector { + unimplemented!(); +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 75ff2d754..3f2d078f8 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -43,7 +43,6 @@ pub mod i2c; #[cfg(crc)] pub mod crc; -#[cfg(flash)] pub mod flash; pub mod pwm; #[cfg(quadspi)] From e7129371d0d543394c070671490a796ddbb10e3f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 08:32:36 +0200 Subject: [PATCH 0766/1575] Let sector computation be shared across families --- ci.sh | 1 + embassy-stm32/build.rs | 13 +++ embassy-stm32/src/flash/common.rs | 35 +++++- embassy-stm32/src/flash/f3.rs | 16 +-- embassy-stm32/src/flash/f4.rs | 181 ++++++++++++------------------ embassy-stm32/src/flash/f7.rs | 95 +++++++++------- embassy-stm32/src/flash/h7.rs | 21 ++-- embassy-stm32/src/flash/l.rs | 18 +-- embassy-stm32/src/flash/mod.rs | 15 ++- embassy-stm32/src/flash/other.rs | 12 +- 10 files changed, 208 insertions(+), 199 deletions(-) diff --git a/ci.sh b/ci.sh index d86c93520..b9dddad3a 100755 --- a/ci.sh +++ b/ci.sh @@ -49,6 +49,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 6b203a3f1..007409242 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -113,6 +113,18 @@ fn main() { .collect(); for region in flash_memory_regions.iter() { let region_name = format_ident!("{}", get_flash_region_name(region.name)); + let bank = format_ident!( + "{}", + if region.name.starts_with("BANK_1") { + "Bank1" + } else if region.name.starts_with("BANK_2") { + "Bank2" + } else if region.name == "OTP" { + "Otp" + } else { + unimplemented!() + } + ); let base = region.address; let size = region.size; let settings = region.settings.as_ref().unwrap(); @@ -122,6 +134,7 @@ fn main() { flash_regions.extend(quote! { pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { + bank: crate::flash::FlashBank::#bank, base: #base, size: #size, erase_size: #erase_size, diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 6534e1b8e..31dd1136f 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -3,7 +3,8 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::{Mutex, MutexGuard}; -use super::{family, Error, FlashLayout, FlashRegion, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use crate::flash::FlashBank; use crate::Peripheral; pub struct Flash<'d> { @@ -79,10 +80,12 @@ impl<'d> Flash<'d> { } unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { + let regions = family::get_flash_regions(); + // Test if the address range is aligned at sector base addresses let mut address = start_address; while address < end_address { - let sector = family::get_sector(address); + let sector = get_sector(address, regions); if sector.start != address { return Err(Error::Unaligned); } @@ -103,7 +106,8 @@ impl<'d> Flash<'d> { let mut address = start_address; while address < end_address { - let sector = family::get_sector(address); + let sector = get_sector(address, regions); + trace!("Erasing sector: {}", sector); family::blocking_erase_sector(§or)?; address += sector.size; } @@ -138,6 +142,31 @@ fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { } } +pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { + let mut current_bank = FlashBank::Bank1; + let mut bank_offset = 0; + for region in regions { + if region.bank != current_bank { + current_bank = region.bank; + bank_offset = 0; + } + + if address < region.end() { + let index_in_region = (address - region.base) / region.erase_size; + return FlashSector { + bank: region.bank, + index_in_bank: bank_offset + index_in_region as u8, + start: region.base + index_in_region * region.erase_size, + size: region.erase_size, + }; + } + + bank_offset += region.sectors(); + } + + panic!("Flash sector not found"); +} + impl FlashRegion { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { unsafe { self.blocking_read_inner(offset, bytes) } diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 5ac1d8fdf..c1ed33f9d 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -3,10 +3,14 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -99,13 +103,3 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } - -pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1_REGION.erase_size; - let index = address / sector_size; - FlashSector { - index: index as u8, - start: BANK1_REGION.base + index * sector_size, - size: sector_size, - } -} diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 52ef7ad50..61ac8afd5 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,22 +2,17 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const SMALL_SECTOR_SIZE: u32 = 16 * 1024; -const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; -const LARGE_SECTOR_SIZE: u32 = 128 * 1024; -const SECOND_BANK_SECTOR_OFFSET: u8 = 12; - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { use embassy_hal_common::PeripheralRef; use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; - use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashRegion}; + use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { @@ -25,19 +20,31 @@ mod alt_regions { ..BANK1_REGION3 }; pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2, ..BANK1_REGION1 }; pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2, ..BANK1_REGION2 }; pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion { + bank: FlashBank::Bank2, base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2, size: 3 * BANK1_REGION3.erase_size, ..BANK1_REGION3 }; + pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [ + &BANK1_REGION1, + &BANK1_REGION2, + &ALT_BANK1_REGION3, + &ALT_BANK2_REGION1, + &ALT_BANK2_REGION2, + &ALT_BANK2_REGION3, + ]; + pub type AltBank1Region1 = Bank1Region1; pub type AltBank1Region2 = Bank1Region2; pub struct AltBank1Region3(&'static FlashRegion); @@ -81,25 +88,22 @@ mod alt_regions { } #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub use alt_regions::AltFlashLayout; +pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; -fn is_dual_bank() -> bool { - match FLASH_SIZE / 1024 { - // 1 MB devices depend on configuration - 1024 => { - if cfg!(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)) { - unsafe { pac::FLASH.optcr().read().db1m() } - } else { - false - } - } - // 2 MB devices are always dual bank - 2048 => true, - // All other devices are single bank - _ => false, +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { + if unsafe { pac::FLASH.optcr().read().db1m() } { + &ALT_FLASH_REGIONS + } else { + &FLASH_REGIONS } } +#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -136,11 +140,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - let sector = sector.index; - let bank = sector / SECOND_BANK_SECTOR_OFFSET as u8; - let snb = (bank << 4) + (sector % SECOND_BANK_SECTOR_OFFSET as u8); - - trace!("Erasing sector: {}", sector); + let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; pac::FLASH.cr().modify(|w| { w.set_ser(true); @@ -194,72 +194,27 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -pub(crate) fn get_sector(address: u32) -> FlashSector { - get_sector_inner(address, is_dual_bank(), FLASH_SIZE as u32) -} - -fn get_sector_inner(address: u32, dual_bank: bool, flash_size: u32) -> FlashSector { - let offset = address - FLASH_BASE as u32; - if !dual_bank { - get_single_bank_sector(offset) - } else { - let bank_size = flash_size / 2; - if offset < bank_size { - get_single_bank_sector(offset) - } else { - let sector = get_single_bank_sector(offset - bank_size); - FlashSector { - index: SECOND_BANK_SECTOR_OFFSET + sector.index, - start: sector.start + bank_size, - size: sector.size, - } - } - } -} - -fn get_single_bank_sector(offset: u32) -> FlashSector { - // First 4 sectors are 16KB, then one 64KB, and rest are 128KB - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 - + 4 * SMALL_SECTOR_SIZE - + MEDIUM_SECTOR_SIZE - + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - #[cfg(test)] mod tests { use super::*; + use crate::flash::{get_sector, FlashBank}; #[test] + #[cfg(stm32f429)] fn can_get_sector_single_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { assert_eq!( - FlashSector { index, start, size }, - get_sector_inner(addr, false, 1024 * 1024) + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) ) }; @@ -275,41 +230,43 @@ mod tests { assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - } - #[test] - fn can_get_sector_dual_bank() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { + let assert_sector = |bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { assert_eq!( - FlashSector { index, start, size }, - get_sector_inner(addr, true, 1024 * 1024) + FlashSector { + bank, + index_in_bank, + start, + size + }, + get_sector(address, &ALT_FLASH_REGIONS) ) }; - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(12, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(15, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); + assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(16, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(17, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(19, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index eba7df467..7111f5cc4 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -2,13 +2,13 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashSector, FLASH_BASE, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -const SMALL_SECTOR_SIZE: u32 = 32 * 1024; -const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; -const LARGE_SECTOR_SIZE: u32 = 256 * 1024; +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -48,7 +48,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector.index) + w.set_snb(sector.index_in_bank) }); pac::FLASH.cr().modify(|w| { @@ -110,48 +110,61 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } -pub(crate) fn get_sector(address: u32) -> FlashSector { - // First 4 sectors are 32KB, then one 128KB, and rest are 256KB - let offset = address - FLASH_BASE as u32; - match offset / LARGE_SECTOR_SIZE { - 0 => { - if offset < 4 * SMALL_SECTOR_SIZE { - let small_sector_index = offset / SMALL_SECTOR_SIZE; - FlashSector { - index: small_sector_index as u8, - start: FLASH_BASE as u32 + small_sector_index * SMALL_SECTOR_SIZE, - size: SMALL_SECTOR_SIZE, - } - } else { - FlashSector { - index: 4, - start: FLASH_BASE as u32 + 4 * SMALL_SECTOR_SIZE, - size: MEDIUM_SECTOR_SIZE, - } - } - } - i => { - let large_sector_index = i - 1; - FlashSector { - index: (5 + large_sector_index) as u8, - start: FLASH_BASE as u32 - + 4 * SMALL_SECTOR_SIZE - + MEDIUM_SECTOR_SIZE - + large_sector_index * LARGE_SECTOR_SIZE, - size: LARGE_SECTOR_SIZE, - } - } - } -} - #[cfg(test)] mod tests { use super::*; + use crate::flash::{get_sector, FlashBank}; #[test] + #[cfg(stm32f732)] fn can_get_sector() { - let assert_sector = |index: u8, start: u32, size: u32, addr: u32| { - assert_eq!(FlashSector { index, start, size }, get_sector(addr)) + const SMALL_SECTOR_SIZE: u32 = 16 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; + const LARGE_SECTOR_SIZE: u32 = 128 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ) + }; + + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + } + + #[test] + #[cfg(stm32f769)] + fn can_get_sector() { + const SMALL_SECTOR_SIZE: u32 = 32 * 1024; + const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; + const LARGE_SECTOR_SIZE: u32 = 256 * 1024; + + let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + assert_eq!( + FlashSector { + bank: FlashBank::Bank1, + index_in_bank, + start, + size + }, + get_sector(address, &FLASH_REGIONS) + ) }; assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 360ad77db..5ea57ccde 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -3,7 +3,7 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -11,6 +11,10 @@ const fn is_dual_bank() -> bool { FLASH_REGIONS.len() == 2 } +pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); if is_dual_bank() { @@ -75,11 +79,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - let bank = pac::FLASH.bank(if sector.index >= 8 { 1 } else { 0 }); - let sector = sector.index % 8; + let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector) + w.set_snb(sector.index_in_bank) }); bank.cr().modify(|w| { @@ -175,13 +178,3 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { } } } - -pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1_REGION.erase_size; - let index = address / sector_size; - FlashSector { - index: index as u8, - start: BANK1_REGION.base + index * sector_size, - size: sector_size, - } -} diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index f4b11011f..f5ebc0a5f 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -2,10 +2,14 @@ use core::ptr::write_volatile; use atomic_polyfill::{fence, Ordering}; -use super::{FlashSector, BANK1_REGION, WRITE_SIZE}; +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -73,7 +77,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4))] { - let idx = (sector.start - super::FLASH_BASE as u32) / BANK1_REGION.erase_size as u32; + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; @@ -180,13 +184,3 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { } } } - -pub(crate) fn get_sector(address: u32) -> FlashSector { - let sector_size = BANK1_REGION.erase_size; - let index = address / sector_size; - FlashSector { - index: index as u8, - start: BANK1_REGION.base + index * sector_size, - size: sector_size, - } -} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 110b81496..794d32ccd 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -10,6 +10,7 @@ pub use crate::_generated::flash_regions::*; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; pub struct FlashRegion { + pub bank: FlashBank, pub base: u32, pub size: u32, pub erase_size: u32, @@ -19,15 +20,27 @@ pub struct FlashRegion { #[derive(Debug, PartialEq)] pub struct FlashSector { - pub index: u8, + pub bank: FlashBank, + pub index_in_bank: u8, pub start: u32, pub size: u32, } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum FlashBank { + Bank1 = 0, + Bank2 = 1, + Otp, +} + impl FlashRegion { pub const fn end(&self) -> u32 { self.base + self.size } + + pub const fn sectors(&self) -> u8 { + (self.size / self.erase_size) as u8 + } } #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index fd3551224..d329dcab4 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -1,5 +1,10 @@ -#[allow(unused)] -use super::{Error, FlashSector, WRITE_SIZE}; +#![allow(unused)] + +use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; + +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} pub(crate) unsafe fn lock() { unimplemented!(); @@ -22,6 +27,3 @@ pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), pub(crate) unsafe fn clear_all_err() { unimplemented!(); } -pub(crate) fn get_sector(_address: u32) -> FlashSector { - unimplemented!(); -} From 760d4a72cbb8d008646e751e70fa212f65f26068 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 09:05:13 +0200 Subject: [PATCH 0767/1575] Ensure that embedded_storage traits are actually implemented --- embassy-stm32/src/flash/common.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 31dd1136f..47e94f753 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -205,7 +205,7 @@ impl FlashRegion { } foreach_flash_region! { - ($type_name:ident, $write_size:ident, $erase_size:ident) => { + ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { unsafe { self.0.blocking_read_inner(offset, bytes) } @@ -220,11 +220,11 @@ foreach_flash_region! { } } - impl ErrorType for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name { type Error = Error; } - impl ReadNorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -236,7 +236,7 @@ foreach_flash_region! { } } - impl NorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name { const WRITE_SIZE: usize = $write_size; const ERASE_SIZE: usize = $erase_size; From 02caec9482cbd8ce62c17ebc3fcaa13c3f47c1ee Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 09:07:23 +0200 Subject: [PATCH 0768/1575] Skip unknown banks --- embassy-stm32/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 007409242..4ca3ef3f0 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -113,7 +113,7 @@ fn main() { .collect(); for region in flash_memory_regions.iter() { let region_name = format_ident!("{}", get_flash_region_name(region.name)); - let bank = format_ident!( + let bank_variant = format_ident!( "{}", if region.name.starts_with("BANK_1") { "Bank1" @@ -122,7 +122,7 @@ fn main() { } else if region.name == "OTP" { "Otp" } else { - unimplemented!() + continue; } ); let base = region.address; @@ -134,7 +134,7 @@ fn main() { flash_regions.extend(quote! { pub const #region_name: crate::flash::FlashRegion = crate::flash::FlashRegion { - bank: crate::flash::FlashBank::#bank, + bank: crate::flash::FlashBank::#bank_variant, base: #base, size: #size, erase_size: #erase_size, From a78e10e00362bf0f5649e200fa62c75d6f3808d0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 09:17:14 +0200 Subject: [PATCH 0769/1575] Add defmt support to new flash types --- embassy-stm32/src/flash/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 794d32ccd..231ff1f9e 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -9,6 +9,8 @@ pub use common::*; pub use crate::_generated::flash_regions::*; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion { pub bank: FlashBank, pub base: u32, @@ -19,6 +21,7 @@ pub struct FlashRegion { } #[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashSector { pub bank: FlashBank, pub index_in_bank: u8, @@ -27,6 +30,7 @@ pub struct FlashSector { } #[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FlashBank { Bank1 = 0, Bank2 = 1, From b2d63d851df68effce2c4ef7276139380a496c88 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 30 Mar 2023 12:04:18 +0200 Subject: [PATCH 0770/1575] set INTERRUPT_WITH_STATUS flag in attempt to prevent hangs --- src/bus.rs | 2 +- src/consts.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index 65caea8ec..add346b2f 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -74,7 +74,7 @@ where // 32-bit word length, little endian (which is the default endianess). self.write32_swapped( REG_BUS_CTRL, - WORD_LENGTH_32 | HIGH_SPEED | INTERRUPT_HIGH | WAKE_UP | STATUS_ENABLE, + WORD_LENGTH_32 | HIGH_SPEED | INTERRUPT_HIGH | WAKE_UP | STATUS_ENABLE | INTERRUPT_WITH_STATUS, ) .await; diff --git a/src/consts.rs b/src/consts.rs index 6ed7feb92..fee2d01ab 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -16,7 +16,8 @@ pub(crate) const WORD_LENGTH_32: u32 = 0x1; pub(crate) const HIGH_SPEED: u32 = 0x10; pub(crate) const INTERRUPT_HIGH: u32 = 1 << 5; pub(crate) const WAKE_UP: u32 = 1 << 7; -pub(crate) const STATUS_ENABLE: u32 = 0x10000; +pub(crate) const STATUS_ENABLE: u32 = 1 << 16; +pub(crate) const INTERRUPT_WITH_STATUS: u32 = 1 << 17; // SPI_STATUS_REGISTER bits pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; From 69db1535b21d529abe5c92fa88ea771ca0b1d721 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 30 Mar 2023 12:24:37 +0200 Subject: [PATCH 0771/1575] clear DATA_UNAVAILABLE irq --- src/runner.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/runner.rs b/src/runner.rs index ccdbbf1ac..deb45f9f4 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -310,6 +310,12 @@ where if irq & IRQ_F2_PACKET_AVAILABLE != 0 { self.check_status(buf).await; } + + if irq & IRQ_DATA_UNAVAILABLE != 0 { + // TODO what should we do here? + warn!("IRQ DATA_UNAVAILABLE, clearing..."); + self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await; + } } /// Handle F2 events while status register is set From f3dcb5eb22dfac21024e77023d1967b4eaa0b176 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 30 Mar 2023 15:13:44 +0200 Subject: [PATCH 0772/1575] Wrap write/erase operations in cs --- embassy-stm32/src/flash/common.rs | 209 +++++++++++++----------------- 1 file changed, 91 insertions(+), 118 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 47e94f753..59429e344 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,7 +1,5 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::mutex::{Mutex, MutexGuard}; use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; use crate::flash::FlashBank; @@ -22,96 +20,21 @@ impl<'d> Flash<'d> { } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Self::blocking_read_inner(FLASH_BASE as u32 + offset, bytes) - } - - fn blocking_read_inner(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - - let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; - bytes.copy_from_slice(flash_data); - Ok(()) + let start_address = FLASH_BASE as u32 + offset; + blocking_read(start_address, bytes) } pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { let start_address = FLASH_BASE as u32 + offset; - // No need to take lock here as we only have one mut flash reference. - - unsafe { Flash::blocking_write_inner(start_address, buf) } - } - - unsafe fn blocking_write_inner(start_address: u32, buf: &[u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { - return Err(Error::Size); - } - if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { - return Err(Error::Unaligned); - } - - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); - - family::clear_all_err(); - family::unlock(); - family::begin_write(); - - let _ = OnDrop::new(|| { - family::end_write(); - family::lock(); - }); - - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { - unsafe { family::blocking_write(address, chunk.try_into().unwrap())? }; - address += WRITE_SIZE as u32; - } - Ok(()) + unsafe { blocking_write(start_address, buf) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { let start_address = FLASH_BASE as u32 + from; let end_address = FLASH_BASE as u32 + to; - unsafe { Flash::blocking_erase_inner(start_address, end_address) } - } - - unsafe fn blocking_erase_inner(start_address: u32, end_address: u32) -> Result<(), Error> { - let regions = family::get_flash_regions(); - - // Test if the address range is aligned at sector base addresses - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } - - trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); - - family::clear_all_err(); - family::unlock(); - - let _ = OnDrop::new(|| { - family::lock(); - }); - - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - trace!("Erasing sector: {}", sector); - family::blocking_erase_sector(§or)?; - address += sector.size; - } - Ok(()) + unsafe { blocking_erase(start_address, end_address) } } pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { @@ -132,14 +55,79 @@ impl Drop for FlashLayout<'_> { } } -static REGION_LOCK: Mutex = Mutex::new(()); - -fn take_lock_spin() -> MutexGuard<'static, CriticalSectionRawMutex, ()> { - loop { - if let Ok(guard) = REGION_LOCK.try_lock() { - return guard; - } +fn blocking_read(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); } + + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; + bytes.copy_from_slice(flash_data); + Ok(()) +} + +unsafe fn blocking_write(start_address: u32, buf: &[u8]) -> Result<(), Error> { + assert!(start_address >= FLASH_BASE as u32); + if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { + return Err(Error::Size); + } + if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + + let mut address = start_address; + for chunk in buf.chunks(WRITE_SIZE) { + critical_section::with(|_| { + family::clear_all_err(); + family::unlock(); + family::begin_write(); + let _ = OnDrop::new(|| { + family::end_write(); + family::lock(); + }); + family::blocking_write(address, chunk.try_into().unwrap()) + })?; + address += WRITE_SIZE as u32; + } + Ok(()) +} + +unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { + let regions = family::get_flash_regions(); + + // Test if the address range is aligned at sector base addresses + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); + + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + trace!("Erasing sector: {}", sector); + + critical_section::with(|_| { + family::clear_all_err(); + family::unlock(); + let _ = OnDrop::new(|| { + family::lock(); + }); + family::blocking_erase_sector(§or) + })?; + address += sector.size; + } + Ok(()) } pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { @@ -169,38 +157,19 @@ pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector impl FlashRegion { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - unsafe { self.blocking_read_inner(offset, bytes) } + let start_address = self.base + offset; + blocking_read(start_address, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { self.blocking_write_inner(offset, bytes) } + let start_address = self.base + offset; + unsafe { blocking_write(start_address, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { self.blocking_erase_inner(from, to) } - } - - unsafe fn blocking_read_inner(&self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - Flash::blocking_read_inner(self.base + offset, bytes) - } - - unsafe fn blocking_write_inner(&self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let start_address = self.base + offset; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - Flash::blocking_write_inner(start_address, bytes) - } - - unsafe fn blocking_erase_inner(&self, from: u32, to: u32) -> Result<(), Error> { let start_address = self.base + from; let end_address = self.base + to; - - // Protect agains simultaneous write/erase to multiple regions. - let _guard = take_lock_spin(); - - Flash::blocking_erase_inner(start_address, end_address) + unsafe { blocking_erase(start_address, end_address) } } } @@ -208,15 +177,19 @@ foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - unsafe { self.0.blocking_read_inner(offset, bytes) } + let start_address = self.0.base + offset; + blocking_read(start_address, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { self.0.blocking_write_inner(offset, bytes) } + let start_address = self.0.base + offset; + unsafe { blocking_write(start_address, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { self.0.blocking_erase_inner(from, to) } + let start_address = self.0.base + from; + let end_address = self.0.base + to; + unsafe { blocking_erase(start_address, end_address) } } } @@ -228,7 +201,7 @@ foreach_flash_region! { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - unsafe { self.0.blocking_read_inner(offset, bytes) } + self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { @@ -241,11 +214,11 @@ foreach_flash_region! { const ERASE_SIZE: usize = $erase_size; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - unsafe { self.0.blocking_write_inner(offset, bytes) } + self.blocking_write(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - unsafe { self.0.blocking_erase_inner(from, to) } + self.blocking_erase(from, to) } } }; From 80972f1e0e6b6d409cc4d86202608c22e5ee3e5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 30 Mar 2023 17:55:55 +0200 Subject: [PATCH 0773/1575] executor,sync: add support for turbo-wakers. This is a `core` patch to make wakers 1 word (the task pointer) instead of 2 (task pointer + vtable). It allows having the "waker optimization" we had a while back on `WakerRegistration/AtomicWaker`, but EVERYWHERE, without patching all crates. Advantages: - Less memory usage. - Faster. - `AtomicWaker` can actually use atomics to load/store the waker, No critical section needed. - No `dyn` call, which means `cargo-call-stack` can now see through wakes. Disadvantages: - You have to patch `core`... - Breaks all executors and other things that create wakers, unless they opt in to using the new `from_ptr` API. How to use: - Run this shell script to patch `core`. https://gist.github.com/Dirbaio/c67da7cf318515181539122c9d32b395 - Enable `build-std` - Enable `build-std-features = core/turbowakers` - Enable feature `turbowakers` in `embassy-executor`, `embassy-sync`. - Make sure you have no other crate creating wakers other than `embassy-executor`. These will panic at runtime. Note that the patched `core` is equivalent to the unpached one when the `turbowakers` feature is not enabled, so it should be fine to leave it there. --- embassy-executor/Cargo.toml | 2 + embassy-executor/src/raw/mod.rs | 1 + embassy-executor/src/raw/waker_turbo.rs | 34 +++++++++++++++ embassy-sync/Cargo.toml | 1 + embassy-sync/src/waitqueue/atomic_waker.rs | 41 +++++++++++++++++++ .../src/waitqueue/atomic_waker_turbo.rs | 30 ++++++++++++++ embassy-sync/src/waitqueue/mod.rs | 8 +++- .../{waker.rs => waker_registration.rs} | 40 ------------------ 8 files changed, 115 insertions(+), 42 deletions(-) create mode 100644 embassy-executor/src/raw/waker_turbo.rs create mode 100644 embassy-sync/src/waitqueue/atomic_waker.rs create mode 100644 embassy-sync/src/waitqueue/atomic_waker_turbo.rs rename embassy-sync/src/waitqueue/{waker.rs => waker_registration.rs} (63%) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index c2868eb98..8ad3fd698 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -38,6 +38,8 @@ wasm = ["dep:wasm-bindgen", "dep:js-sys"] # Enable nightly-only features nightly = [] +turbowakers = [] + integrated-timers = ["dep:embassy-time"] # Trace interrupt invocations with rtos-trace. diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 15ff18fc8..72c367c33 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -11,6 +11,7 @@ mod run_queue; #[cfg(feature = "integrated-timers")] mod timer_queue; pub(crate) mod util; +#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; use core::future::Future; diff --git a/embassy-executor/src/raw/waker_turbo.rs b/embassy-executor/src/raw/waker_turbo.rs new file mode 100644 index 000000000..435a0ff7e --- /dev/null +++ b/embassy-executor/src/raw/waker_turbo.rs @@ -0,0 +1,34 @@ +use core::ptr::NonNull; +use core::task::Waker; + +use super::{wake_task, TaskHeader, TaskRef}; + +pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { + Waker::from_turbo_ptr(NonNull::new_unchecked(p.as_ptr() as _)) +} + +/// Get a task pointer from a waker. +/// +/// This can be used as an optimization in wait queues to store task pointers +/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps +/// avoid dynamic dispatch. +/// +/// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task). +/// +/// # Panics +/// +/// Panics if the waker is not created by the Embassy executor. +pub fn task_from_waker(waker: &Waker) -> TaskRef { + let ptr = waker.as_turbo_ptr().as_ptr(); + + // safety: our wakers are always created with `TaskRef::as_ptr` + unsafe { TaskRef::from_ptr(ptr as *const TaskHeader) } +} + +#[inline(never)] +#[no_mangle] +fn _turbo_wake(ptr: NonNull<()>) { + // safety: our wakers are always created with `TaskRef::as_ptr` + let task = unsafe { TaskRef::from_ptr(ptr.as_ptr() as *const TaskHeader) }; + wake_task(task) +} diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 7b5d3ce48..e4871e718 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -25,6 +25,7 @@ features = ["nightly"] [features] nightly = ["embedded-io/async"] std = [] +turbowakers = [] [dependencies] defmt = { version = "0.3", optional = true } diff --git a/embassy-sync/src/waitqueue/atomic_waker.rs b/embassy-sync/src/waitqueue/atomic_waker.rs new file mode 100644 index 000000000..63fe04a6e --- /dev/null +++ b/embassy-sync/src/waitqueue/atomic_waker.rs @@ -0,0 +1,41 @@ +use core::cell::Cell; +use core::task::Waker; + +use crate::blocking_mutex::raw::CriticalSectionRawMutex; +use crate::blocking_mutex::Mutex; + +/// Utility struct to register and wake a waker. +pub struct AtomicWaker { + waker: Mutex>>, +} + +impl AtomicWaker { + /// Create a new `AtomicWaker`. + pub const fn new() -> Self { + Self { + waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + critical_section::with(|cs| { + let cell = self.waker.borrow(cs); + cell.set(match cell.replace(None) { + Some(w2) if (w2.will_wake(w)) => Some(w2), + _ => Some(w.clone()), + }) + }) + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + critical_section::with(|cs| { + let cell = self.waker.borrow(cs); + if let Some(w) = cell.replace(None) { + w.wake_by_ref(); + cell.set(Some(w)); + } + }) + } +} diff --git a/embassy-sync/src/waitqueue/atomic_waker_turbo.rs b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs new file mode 100644 index 000000000..5c6a96ec8 --- /dev/null +++ b/embassy-sync/src/waitqueue/atomic_waker_turbo.rs @@ -0,0 +1,30 @@ +use core::ptr; +use core::ptr::NonNull; +use core::sync::atomic::{AtomicPtr, Ordering}; +use core::task::Waker; + +/// Utility struct to register and wake a waker. +pub struct AtomicWaker { + waker: AtomicPtr<()>, +} + +impl AtomicWaker { + /// Create a new `AtomicWaker`. + pub const fn new() -> Self { + Self { + waker: AtomicPtr::new(ptr::null_mut()), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + self.waker.store(w.as_turbo_ptr().as_ptr() as _, Ordering::Release); + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + if let Some(ptr) = NonNull::new(self.waker.load(Ordering::Acquire)) { + unsafe { Waker::from_turbo_ptr(ptr) }.wake(); + } + } +} diff --git a/embassy-sync/src/waitqueue/mod.rs b/embassy-sync/src/waitqueue/mod.rs index 6661a6b61..6b0b0c64e 100644 --- a/embassy-sync/src/waitqueue/mod.rs +++ b/embassy-sync/src/waitqueue/mod.rs @@ -1,7 +1,11 @@ //! Async low-level wait queues -mod waker; -pub use waker::*; +#[cfg_attr(feature = "turbowakers", path = "atomic_waker_turbo.rs")] +mod atomic_waker; +pub use atomic_waker::*; + +mod waker_registration; +pub use waker_registration::*; mod multi_waker; pub use multi_waker::*; diff --git a/embassy-sync/src/waitqueue/waker.rs b/embassy-sync/src/waitqueue/waker_registration.rs similarity index 63% rename from embassy-sync/src/waitqueue/waker.rs rename to embassy-sync/src/waitqueue/waker_registration.rs index 9ce94a089..9b666e7c4 100644 --- a/embassy-sync/src/waitqueue/waker.rs +++ b/embassy-sync/src/waitqueue/waker_registration.rs @@ -1,10 +1,6 @@ -use core::cell::Cell; use core::mem; use core::task::Waker; -use crate::blocking_mutex::raw::CriticalSectionRawMutex; -use crate::blocking_mutex::Mutex; - /// Utility struct to register and wake a waker. #[derive(Debug, Default)] pub struct WakerRegistration { @@ -54,39 +50,3 @@ impl WakerRegistration { self.waker.is_some() } } - -/// Utility struct to register and wake a waker. -pub struct AtomicWaker { - waker: Mutex>>, -} - -impl AtomicWaker { - /// Create a new `AtomicWaker`. - pub const fn new() -> Self { - Self { - waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), - } - } - - /// Register a waker. Overwrites the previous waker, if any. - pub fn register(&self, w: &Waker) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); - cell.set(match cell.replace(None) { - Some(w2) if (w2.will_wake(w)) => Some(w2), - _ => Some(w.clone()), - }) - }) - } - - /// Wake the registered waker, if any. - pub fn wake(&self) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); - if let Some(w) = cell.replace(None) { - w.wake_by_ref(); - cell.set(Some(w)); - } - }) - } -} From 373760a56b1bad13ebcec19247ee3ee4ae4cb07c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 08:05:37 +0200 Subject: [PATCH 0774/1575] Split bootloader implementation into multiple files --- embassy-boot/boot/src/boot_loader.rs | 526 +++++++++ embassy-boot/boot/src/firmware_updater.rs | 537 ++++++++++ embassy-boot/boot/src/firmware_writer.rs | 97 ++ embassy-boot/boot/src/lib.rs | 1183 +-------------------- embassy-boot/boot/src/partition.rs | 22 + 5 files changed, 1194 insertions(+), 1171 deletions(-) create mode 100644 embassy-boot/boot/src/boot_loader.rs create mode 100644 embassy-boot/boot/src/firmware_updater.rs create mode 100644 embassy-boot/boot/src/firmware_writer.rs create mode 100644 embassy-boot/boot/src/partition.rs diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs new file mode 100644 index 000000000..ad6735112 --- /dev/null +++ b/embassy-boot/boot/src/boot_loader.rs @@ -0,0 +1,526 @@ +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; + +use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; + +/// Errors returned by bootloader +#[derive(PartialEq, Eq, Debug)] +pub enum BootError { + /// Error from flash. + Flash(NorFlashErrorKind), + /// Invalid bootloader magic + BadMagic, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for BootError { + fn format(&self, fmt: defmt::Formatter) { + match self { + BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), + BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), + } + } +} + +impl From for BootError +where + E: NorFlashError, +{ + fn from(error: E) -> Self { + BootError::Flash(error.kind()) + } +} + +/// Extension of the embedded-storage flash type information with block size and erase value. +pub trait Flash: NorFlash + ReadNorFlash { + /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase + /// size of the flash, but for external QSPI flash modules, this can be lower. + const BLOCK_SIZE: usize; + /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. + const ERASE_VALUE: u8 = 0xFF; +} + +/// Trait defining the flash handles used for active and DFU partition +pub trait FlashConfig { + /// Flash type used for the state partition. + type STATE: Flash; + /// Flash type used for the active partition. + type ACTIVE: Flash; + /// Flash type used for the dfu partition. + type DFU: Flash; + + /// Return flash instance used to write/read to/from active partition. + fn active(&mut self) -> &mut Self::ACTIVE; + /// Return flash instance used to write/read to/from dfu partition. + fn dfu(&mut self) -> &mut Self::DFU; + /// Return flash instance used to write/read to/from bootloader state. + fn state(&mut self) -> &mut Self::STATE; +} + +/// BootLoader works with any flash implementing embedded_storage and can also work with +/// different page sizes and flash write sizes. +pub struct BootLoader { + // Page with current state of bootloader. The state partition has the following format: + // | Range | Description | + // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | + // | WRITE_SIZE - N | Progress index used while swapping or reverting | + state: Partition, + // Location of the partition which will be booted from + active: Partition, + // Location of the partition which will be swapped in when requested + dfu: Partition, +} + +impl BootLoader { + /// Create a new instance of a bootloader with the given partitions. + /// + /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. + /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. + pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + Self { active, dfu, state } + } + + /// Return the boot address for the active partition. + pub fn boot_address(&self) -> usize { + self.active.from + } + + /// Perform necessary boot preparations like swapping images. + /// + /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap + /// algorithm to work correctly. + /// + /// SWAPPING + /// + /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. + /// The swap index contains the copy progress, as to allow continuation of the copy process on + /// power failure. The index counter is represented within 1 or more pages (depending on total + /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE) + /// contains a zero value. This ensures that index updates can be performed atomically and + /// avoid a situation where the wrong index value is set (page write size is "atomic"). + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 0 | 1 | 2 | 3 | - | + /// | DFU | 0 | 3 | 2 | 1 | X | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// The algorithm starts by copying 'backwards', and after the first step, the layout is + /// as follows: + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 1 | 1 | 2 | 1 | - | + /// | DFU | 1 | 3 | 2 | 1 | 3 | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// The next iteration performs the same steps + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 2 | 1 | 2 | 1 | - | + /// | DFU | 2 | 3 | 2 | 2 | 3 | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// And again until we're done + /// + /// +-----------+------------+--------+--------+--------+--------+ + /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+------------+--------+--------+--------+--------+ + /// | Active | 3 | 3 | 2 | 1 | - | + /// | DFU | 3 | 3 | 1 | 2 | 3 | + /// +-----------+------------+--------+--------+--------+--------+ + /// + /// REVERTING + /// + /// The reverting algorithm uses the swap index to discover that images were swapped, but that + /// the application failed to mark the boot successful. In this case, the revert algorithm will + /// run. + /// + /// The revert index is located separately from the swap index, to ensure that revert can continue + /// on power failure. + /// + /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start. + /// + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | + //*/ + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Active | 3 | 1 | 2 | 1 | - | + /// | DFU | 3 | 3 | 1 | 2 | 3 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// + /// + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Active | 3 | 1 | 2 | 1 | - | + /// | DFU | 3 | 3 | 2 | 2 | 3 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// | Active | 3 | 1 | 2 | 3 | - | + /// | DFU | 3 | 3 | 2 | 1 | 3 | + /// +-----------+--------------+--------+--------+--------+--------+ + /// + pub fn prepare_boot( + &mut self, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result { + // Ensure we have enough progress pages to store copy progress + assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); + assert_eq!(magic.len(), P::STATE::WRITE_SIZE); + + // Copy contents from partition N to active + let state = self.read_state(p, magic)?; + if state == State::Swap { + // + // Check if we already swapped. If we're in the swap state, this means we should revert + // since the app has failed to mark boot as successful + // + if !self.is_swapped(p, magic, page)? { + trace!("Swapping"); + self.swap(p, magic, page)?; + trace!("Swapping done"); + } else { + trace!("Reverting"); + self.revert(p, magic, page)?; + + // Overwrite magic and reset progress + let fstate = p.state(); + magic.fill(!P::STATE::ERASE_VALUE); + fstate.write(self.state.from as u32, magic)?; + fstate.erase(self.state.from as u32, self.state.to as u32)?; + + magic.fill(BOOT_MAGIC); + fstate.write(self.state.from as u32, magic)?; + } + } + Ok(state) + } + + fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + let progress = self.current_progress(p, magic)?; + + Ok(progress >= page_count * 2) + } + + fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { + let write_size = aligned.len(); + let max_index = ((self.state.len() - write_size) / write_size) - 1; + aligned.fill(!P::STATE::ERASE_VALUE); + + let flash = config.state(); + for i in 0..max_index { + flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; + + if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { + return Ok(i); + } + } + Ok(max_index) + } + + fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { + let flash = p.state(); + let write_size = magic.len(); + let w = self.state.from + write_size + idx * write_size; + + let aligned = magic; + aligned.fill(!P::STATE::ERASE_VALUE); + flash.write(w as u32, aligned)?; + Ok(()) + } + + fn active_addr(&self, n: usize, page_size: usize) -> usize { + self.active.from + n * page_size + } + + fn dfu_addr(&self, n: usize, page_size: usize) -> usize { + self.dfu.from + n * page_size + } + + fn copy_page_once_to_active( + &mut self, + idx: usize, + from_page: usize, + to_page: usize, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { + let mut offset = from_page; + for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { + p.dfu().read(offset as u32, chunk)?; + offset += chunk.len(); + } + + p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; + + let mut offset = to_page; + for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { + p.active().write(offset as u32, chunk)?; + offset += chunk.len(); + } + self.update_progress(idx, p, magic)?; + } + Ok(()) + } + + fn copy_page_once_to_dfu( + &mut self, + idx: usize, + from_page: usize, + to_page: usize, + p: &mut P, + magic: &mut [u8], + page: &mut [u8], + ) -> Result<(), BootError> { + let buf = page; + if self.current_progress(p, magic)? <= idx { + let mut offset = from_page; + for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { + p.active().read(offset as u32, chunk)?; + offset += chunk.len(); + } + + p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; + + let mut offset = to_page; + for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { + p.dfu().write(offset as u32, chunk)?; + offset += chunk.len(); + } + self.update_progress(idx, p, magic)?; + } + Ok(()) + } + + fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + trace!("Page count: {}", page_count); + for page_num in 0..page_count { + trace!("COPY PAGE {}", page_num); + // Copy active page to the 'next' DFU page. + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - page_num, page_size); + //trace!("Copy active {} to dfu {}", active_page, dfu_page); + self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; + + // Copy DFU page to the active page + let active_page = self.active_addr(page_count - 1 - page_num, page_size); + let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); + //trace!("Copy dfy {} to active {}", dfu_page, active_page); + self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + } + + Ok(()) + } + + fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { + let page_size = page.len(); + let page_count = self.active.len() / page_size; + for page_num in 0..page_count { + // Copy the bad active page to the DFU page + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num, page_size); + self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; + + // Copy the DFU page back to the active page + let active_page = self.active_addr(page_num, page_size); + let dfu_page = self.dfu_addr(page_num + 1, page_size); + self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + } + + Ok(()) + } + + fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { + let flash = config.state(); + flash.read(self.state.from as u32, magic)?; + + if !magic.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } +} + +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { + assert_eq!(active.len() % page_size, 0); + assert_eq!(dfu.len() % page_size, 0); + assert!(dfu.len() - active.len() >= page_size); + assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); +} + +/// A flash wrapper implementing the Flash and embedded_storage traits. +pub struct BootFlash +where + F: NorFlash + ReadNorFlash, +{ + flash: F, +} + +impl BootFlash +where + F: NorFlash + ReadNorFlash, +{ + /// Create a new instance of a bootable flash + pub fn new(flash: F) -> Self { + Self { flash } + } +} + +impl Flash for BootFlash +where + F: NorFlash + ReadNorFlash, +{ + const BLOCK_SIZE: usize = BLOCK_SIZE; + const ERASE_VALUE: u8 = ERASE_VALUE; +} + +impl ErrorType for BootFlash +where + F: ReadNorFlash + NorFlash, +{ + type Error = F::Error; +} + +impl NorFlash for BootFlash +where + F: ReadNorFlash + NorFlash, +{ + const WRITE_SIZE: usize = F::WRITE_SIZE; + const ERASE_SIZE: usize = F::ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + F::erase(&mut self.flash, from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + F::write(&mut self.flash, offset, bytes) + } +} + +impl ReadNorFlash for BootFlash +where + F: ReadNorFlash + NorFlash, +{ + const READ_SIZE: usize = F::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + F::read(&mut self.flash, offset, bytes) + } + + fn capacity(&self) -> usize { + F::capacity(&self.flash) + } +} + +/// Convenience provider that uses a single flash for all partitions. +pub struct SingleFlashConfig<'a, F> +where + F: Flash, +{ + flash: &'a mut F, +} + +impl<'a, F> SingleFlashConfig<'a, F> +where + F: Flash, +{ + /// Create a provider for a single flash. + pub fn new(flash: &'a mut F) -> Self { + Self { flash } + } +} + +impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> +where + F: Flash, +{ + type STATE = F; + type ACTIVE = F; + type DFU = F; + + fn active(&mut self) -> &mut Self::STATE { + self.flash + } + fn dfu(&mut self) -> &mut Self::ACTIVE { + self.flash + } + fn state(&mut self) -> &mut Self::DFU { + self.flash + } +} + +/// Convenience flash provider that uses separate flash instances for each partition. +pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + active: &'a mut ACTIVE, + state: &'a mut STATE, + dfu: &'a mut DFU, +} + +impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + /// Create a new flash provider with separate configuration for all three partitions. + pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { + Self { active, state, dfu } + } +} + +impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> +where + ACTIVE: Flash, + STATE: Flash, + DFU: Flash, +{ + type STATE = STATE; + type ACTIVE = ACTIVE; + type DFU = DFU; + + fn active(&mut self) -> &mut Self::ACTIVE { + self.active + } + fn dfu(&mut self) -> &mut Self::DFU { + self.dfu + } + fn state(&mut self) -> &mut Self::STATE { + self.state + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic] + fn test_range_asserts() { + const ACTIVE: Partition = Partition::new(4096, 4194304); + const DFU: Partition = Partition::new(4194304, 2 * 4194304); + const STATE: Partition = Partition::new(0, 4096); + assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + } +} diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs new file mode 100644 index 000000000..2d8712277 --- /dev/null +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -0,0 +1,537 @@ +use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; +use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; + +use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; + +/// Errors returned by FirmwareUpdater +#[derive(Debug)] +pub enum FirmwareUpdaterError { + /// Error from flash. + Flash(NorFlashErrorKind), + /// Signature errors. + Signature(signature::Error), +} + +#[cfg(feature = "defmt")] +impl defmt::Format for FirmwareUpdaterError { + fn format(&self, fmt: defmt::Formatter) { + match self { + FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), + FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), + } + } +} + +impl From for FirmwareUpdaterError +where + E: NorFlashError, +{ + fn from(error: E) -> Self { + FirmwareUpdaterError::Flash(error.kind()) + } +} + +/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to +/// 'mess up' the internal bootloader state +pub struct FirmwareUpdater { + state: Partition, + dfu: Partition, +} + +impl Default for FirmwareUpdater { + fn default() -> Self { + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + 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!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); + trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); + FirmwareUpdater::new(dfu, state) + } +} + +impl FirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. + pub const fn new(dfu: Partition, state: Partition) -> Self { + Self { dfu, state } + } + + /// Return the length of the DFU area + pub fn firmware_len(&self) -> usize { + self.dfu.len() + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result { + flash.read(self.state.from as u32, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub async fn verify_and_mark_updated( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned).await?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned).await?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic(_aligned, SWAP_MAGIC, _flash).await + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub async fn mark_updated( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC, flash).await + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_booted( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC, flash).await + } + + async fn set_magic( + &mut self, + aligned: &mut [u8], + magic: u8, + flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + flash.read(self.state.from as u32, aligned).await?; + + if aligned.iter().any(|&b| b != magic) { + aligned.fill(0); + + flash.write(self.state.from as u32, aligned).await?; + flash.erase(self.state.from as u32, self.state.to as u32).await?; + + aligned.fill(magic); + flash.write(self.state.from as u32, aligned).await?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + flash + .erase( + (self.dfu.from + offset) as u32, + (self.dfu.from + offset + data.len()) as u32, + ) + .await?; + + trace!( + "Erased from {} to {}", + self.dfu.from + offset, + self.dfu.from + offset + data.len() + ); + + FirmwareWriter(self.dfu) + .write_block(offset, data, flash, block_size) + .await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub async fn prepare_update( + &mut self, + flash: &mut F, + ) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self.dfu)) + } + + // + // Blocking API + // + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result { + flash.read(self.state.from as u32, aligned)?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub fn verify_and_mark_updated_blocking( + &mut self, + _flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: usize, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let _end = self.dfu.from + _update_len; + let _read_size = _aligned.len(); + + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_end <= self.dfu.to); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + public_key + .verify(&digest.finalize(), &signature) + .map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Sha512, Signature}; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut digest = Sha512::new(); + + let mut offset = self.dfu.from; + let last_offset = _end / _read_size * _read_size; + + while offset < last_offset { + _flash.read(offset as u32, _aligned)?; + digest.update(&_aligned); + offset += _read_size; + } + + let remaining = _end % _read_size; + + if remaining > 0 { + _flash.read(last_offset as u32, _aligned)?; + digest.update(&_aligned[0..remaining]); + } + + let message = digest.finalize(); + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub fn mark_updated_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, SWAP_MAGIC, flash) + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted_blocking( + &mut self, + flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, BOOT_MAGIC, flash) + } + + fn set_magic_blocking( + &mut self, + aligned: &mut [u8], + magic: u8, + flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + flash.read(self.state.from as u32, aligned)?; + + if aligned.iter().any(|&b| b != magic) { + aligned.fill(0); + + flash.write(self.state.from as u32, aligned)?; + flash.erase(self.state.from as u32, self.state.to as u32)?; + + aligned.fill(magic); + flash.write(self.state.from as u32, aligned)?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + flash.erase( + (self.dfu.from + offset) as u32, + (self.dfu.from + offset + data.len()) as u32, + )?; + + trace!( + "Erased from {} to {}", + self.dfu.from + offset, + self.dfu.from + offset + data.len() + ); + + FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware_blocking` allows for an optimized + /// API in exchange for added complexity. + pub fn prepare_update_blocking( + &mut self, + flash: &mut F, + ) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self.dfu)) + } +} diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs new file mode 100644 index 000000000..f992021bb --- /dev/null +++ b/embassy-boot/boot/src/firmware_writer.rs @@ -0,0 +1,97 @@ +use embedded_storage::nor_flash::NorFlash; +use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; + +use crate::Partition; + +/// FirmwareWriter allows writing blocks to an already erased flash. +pub struct FirmwareWriter(pub(crate) Partition); + +impl FirmwareWriter { + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_block( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.from + offset, + data.len() + ); + + let mut write_offset = self.0.from + offset; + for chunk in data.chunks(block_size) { + trace!("Wrote chunk at {}: {:?}", write_offset, chunk); + flash.write(write_offset as u32, chunk).await?; + write_offset += chunk.len(); + } + /* + trace!("Wrote data, reading back for verification"); + + let mut buf: [u8; 4096] = [0; 4096]; + let mut data_offset = 0; + let mut read_offset = self.dfu.from + offset; + for chunk in buf.chunks_mut(block_size) { + flash.read(read_offset as u32, chunk).await?; + trace!("Read chunk at {}: {:?}", read_offset, chunk); + assert_eq!(&data[data_offset..data_offset + block_size], chunk); + read_offset += chunk.len(); + data_offset += chunk.len(); + } + */ + + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_block_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.from + offset, + data.len() + ); + + let mut write_offset = self.0.from + offset; + for chunk in data.chunks(block_size) { + trace!("Wrote chunk at {}: {:?}", write_offset, chunk); + flash.write(write_offset as u32, chunk)?; + write_offset += chunk.len(); + } + /* + trace!("Wrote data, reading back for verification"); + + let mut buf: [u8; 4096] = [0; 4096]; + let mut data_offset = 0; + let mut read_offset = self.dfu.from + offset; + for chunk in buf.chunks_mut(block_size) { + flash.read(read_offset as u32, chunk).await?; + trace!("Read chunk at {}: {:?}", read_offset, chunk); + assert_eq!(&data[data_offset..data_offset + block_size], chunk); + read_offset += chunk.len(); + data_offset += chunk.len(); + } + */ + + Ok(()) + } +} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 0df44f36e..a2259411f 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -5,34 +5,18 @@ #![doc = include_str!("../README.md")] mod fmt; -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; +mod boot_loader; +mod firmware_updater; +mod firmware_writer; +mod partition; -const BOOT_MAGIC: u8 = 0xD0; -const SWAP_MAGIC: u8 = 0xF0; +pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; +pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; +pub use firmware_writer::FirmwareWriter; +pub use partition::Partition; -/// A region in flash used by the bootloader. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Partition { - /// Start of the flash region. - pub from: usize, - /// End of the flash region. - pub to: usize, -} - -impl Partition { - /// Create a new partition with the provided range - pub const fn new(from: usize, to: usize) -> Self { - Self { from, to } - } - - /// Return the length of the partition - #[allow(clippy::len_without_is_empty)] - pub const fn len(&self) -> usize { - self.to - self.from - } -} +pub(crate) const BOOT_MAGIC: u8 = 0xD0; +pub(crate) const SWAP_MAGIC: u8 = 0xF0; /// The state of the bootloader after running prepare. #[derive(PartialEq, Eq, Debug)] @@ -44,34 +28,6 @@ pub enum State { Swap, } -/// Errors returned by bootloader -#[derive(PartialEq, Eq, Debug)] -pub enum BootError { - /// Error from flash. - Flash(NorFlashErrorKind), - /// Invalid bootloader magic - BadMagic, -} - -#[cfg(feature = "defmt")] -impl defmt::Format for BootError { - fn format(&self, fmt: defmt::Formatter) { - match self { - BootError::Flash(_) => defmt::write!(fmt, "BootError::Flash(_)"), - BootError::BadMagic => defmt::write!(fmt, "BootError::BadMagic"), - } - } -} - -impl From for BootError -where - E: NorFlashError, -{ - fn from(error: E) -> Self { - BootError::Flash(error.kind()) - } -} - /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. #[repr(align(32))] pub struct AlignedBuffer(pub [u8; N]); @@ -88,1118 +44,12 @@ impl AsMut<[u8]> for AlignedBuffer { } } -/// Extension of the embedded-storage flash type information with block size and erase value. -pub trait Flash: NorFlash + ReadNorFlash { - /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase - /// size of the flash, but for external QSPI flash modules, this can be lower. - const BLOCK_SIZE: usize; - /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. - const ERASE_VALUE: u8 = 0xFF; -} - -/// Trait defining the flash handles used for active and DFU partition -pub trait FlashConfig { - /// Flash type used for the state partition. - type STATE: Flash; - /// Flash type used for the active partition. - type ACTIVE: Flash; - /// Flash type used for the dfu partition. - type DFU: Flash; - - /// Return flash instance used to write/read to/from active partition. - fn active(&mut self) -> &mut Self::ACTIVE; - /// Return flash instance used to write/read to/from dfu partition. - fn dfu(&mut self) -> &mut Self::DFU; - /// Return flash instance used to write/read to/from bootloader state. - fn state(&mut self) -> &mut Self::STATE; -} - -/// BootLoader works with any flash implementing embedded_storage and can also work with -/// different page sizes and flash write sizes. -pub struct BootLoader { - // Page with current state of bootloader. The state partition has the following format: - // | Range | Description | - // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - // | WRITE_SIZE - N | Progress index used while swapping or reverting | - state: Partition, - // Location of the partition which will be booted from - active: Partition, - // Location of the partition which will be swapped in when requested - dfu: Partition, -} - -impl BootLoader { - /// Create a new instance of a bootloader with the given partitions. - /// - /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. - /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. - pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { - Self { active, dfu, state } - } - - /// Return the boot address for the active partition. - pub fn boot_address(&self) -> usize { - self.active.from - } - - /// Perform necessary boot preparations like swapping images. - /// - /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap - /// algorithm to work correctly. - /// - /// SWAPPING - /// - /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. - /// The swap index contains the copy progress, as to allow continuation of the copy process on - /// power failure. The index counter is represented within 1 or more pages (depending on total - /// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE) - /// contains a zero value. This ensures that index updates can be performed atomically and - /// avoid a situation where the wrong index value is set (page write size is "atomic"). - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 0 | 1 | 2 | 3 | - | - /// | DFU | 0 | 3 | 2 | 1 | X | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// The algorithm starts by copying 'backwards', and after the first step, the layout is - /// as follows: - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 1 | 1 | 2 | 1 | - | - /// | DFU | 1 | 3 | 2 | 1 | 3 | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// The next iteration performs the same steps - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 2 | 1 | 2 | 1 | - | - /// | DFU | 2 | 3 | 2 | 2 | 3 | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// And again until we're done - /// - /// +-----------+------------+--------+--------+--------+--------+ - /// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+------------+--------+--------+--------+--------+ - /// | Active | 3 | 3 | 2 | 1 | - | - /// | DFU | 3 | 3 | 1 | 2 | 3 | - /// +-----------+------------+--------+--------+--------+--------+ - /// - /// REVERTING - /// - /// The reverting algorithm uses the swap index to discover that images were swapped, but that - /// the application failed to mark the boot successful. In this case, the revert algorithm will - /// run. - /// - /// The revert index is located separately from the swap index, to ensure that revert can continue - /// on power failure. - /// - /// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start. - /// - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - //*/ - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Active | 3 | 1 | 2 | 1 | - | - /// | DFU | 3 | 3 | 1 | 2 | 3 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// - /// - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Active | 3 | 1 | 2 | 1 | - | - /// | DFU | 3 | 3 | 2 | 2 | 3 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// | Active | 3 | 1 | 2 | 3 | - | - /// | DFU | 3 | 3 | 2 | 1 | 3 | - /// +-----------+--------------+--------+--------+--------+--------+ - /// - pub fn prepare_boot( - &mut self, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result { - // Ensure we have enough progress pages to store copy progress - assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); - assert_eq!(magic.len(), P::STATE::WRITE_SIZE); - - // Copy contents from partition N to active - let state = self.read_state(p, magic)?; - if state == State::Swap { - // - // Check if we already swapped. If we're in the swap state, this means we should revert - // since the app has failed to mark boot as successful - // - if !self.is_swapped(p, magic, page)? { - trace!("Swapping"); - self.swap(p, magic, page)?; - trace!("Swapping done"); - } else { - trace!("Reverting"); - self.revert(p, magic, page)?; - - // Overwrite magic and reset progress - let fstate = p.state(); - magic.fill(!P::STATE::ERASE_VALUE); - fstate.write(self.state.from as u32, magic)?; - fstate.erase(self.state.from as u32, self.state.to as u32)?; - - magic.fill(BOOT_MAGIC); - fstate.write(self.state.from as u32, magic)?; - } - } - Ok(state) - } - - fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - let progress = self.current_progress(p, magic)?; - - Ok(progress >= page_count * 2) - } - - fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { - let write_size = aligned.len(); - let max_index = ((self.state.len() - write_size) / write_size) - 1; - aligned.fill(!P::STATE::ERASE_VALUE); - - let flash = config.state(); - for i in 0..max_index { - flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; - - if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { - return Ok(i); - } - } - Ok(max_index) - } - - fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let flash = p.state(); - let write_size = magic.len(); - let w = self.state.from + write_size + idx * write_size; - - let aligned = magic; - aligned.fill(!P::STATE::ERASE_VALUE); - flash.write(w as u32, aligned)?; - Ok(()) - } - - fn active_addr(&self, n: usize, page_size: usize) -> usize { - self.active.from + n * page_size - } - - fn dfu_addr(&self, n: usize, page_size: usize) -> usize { - self.dfu.from + n * page_size - } - - fn copy_page_once_to_active( - &mut self, - idx: usize, - from_page: usize, - to_page: usize, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; - for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { - p.dfu().read(offset as u32, chunk)?; - offset += chunk.len(); - } - - p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; - - let mut offset = to_page; - for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { - p.active().write(offset as u32, chunk)?; - offset += chunk.len(); - } - self.update_progress(idx, p, magic)?; - } - Ok(()) - } - - fn copy_page_once_to_dfu( - &mut self, - idx: usize, - from_page: usize, - to_page: usize, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; - for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - p.active().read(offset as u32, chunk)?; - offset += chunk.len(); - } - - p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; - - let mut offset = to_page; - for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { - p.dfu().write(offset as u32, chunk)?; - offset += chunk.len(); - } - self.update_progress(idx, p, magic)?; - } - Ok(()) - } - - fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - trace!("Page count: {}", page_count); - for page_num in 0..page_count { - trace!("COPY PAGE {}", page_num); - // Copy active page to the 'next' DFU page. - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - page_num, page_size); - //trace!("Copy active {} to dfu {}", active_page, dfu_page); - self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; - - // Copy DFU page to the active page - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); - //trace!("Copy dfy {} to active {}", dfu_page, active_page); - self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; - } - - Ok(()) - } - - fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - for page_num in 0..page_count { - // Copy the bad active page to the DFU page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num, page_size); - self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; - - // Copy the DFU page back to the active page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num + 1, page_size); - self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; - } - - Ok(()) - } - - fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { - let flash = config.state(); - flash.read(self.state.from as u32, magic)?; - - if !magic.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } -} - -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { - assert_eq!(active.len() % page_size, 0); - assert_eq!(dfu.len() % page_size, 0); - assert!(dfu.len() - active.len() >= page_size); - assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); -} - -/// Convenience provider that uses a single flash for all partitions. -pub struct SingleFlashConfig<'a, F> -where - F: Flash, -{ - flash: &'a mut F, -} - -impl<'a, F> SingleFlashConfig<'a, F> -where - F: Flash, -{ - /// Create a provider for a single flash. - pub fn new(flash: &'a mut F) -> Self { - Self { flash } - } -} - -impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> -where - F: Flash, -{ - type STATE = F; - type ACTIVE = F; - type DFU = F; - - fn active(&mut self) -> &mut Self::STATE { - self.flash - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - self.flash - } - fn state(&mut self) -> &mut Self::DFU { - self.flash - } -} - -/// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash -where - F: NorFlash + ReadNorFlash, -{ - flash: F, -} - -impl BootFlash -where - F: NorFlash + ReadNorFlash, -{ - /// Create a new instance of a bootable flash - pub fn new(flash: F) -> Self { - Self { flash } - } -} - -impl Flash for BootFlash -where - F: NorFlash + ReadNorFlash, -{ - const BLOCK_SIZE: usize = BLOCK_SIZE; - const ERASE_VALUE: u8 = ERASE_VALUE; -} - -impl ErrorType for BootFlash -where - F: ReadNorFlash + NorFlash, -{ - type Error = F::Error; -} - -impl NorFlash for BootFlash -where - F: ReadNorFlash + NorFlash, -{ - const WRITE_SIZE: usize = F::WRITE_SIZE; - const ERASE_SIZE: usize = F::ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(&mut self.flash, from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(&mut self.flash, offset, bytes) - } -} - -impl ReadNorFlash for BootFlash -where - F: ReadNorFlash + NorFlash, -{ - const READ_SIZE: usize = F::READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(&mut self.flash, offset, bytes) - } - - fn capacity(&self) -> usize { - F::capacity(&self.flash) - } -} - -/// Convenience flash provider that uses separate flash instances for each partition. -pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, -{ - active: &'a mut ACTIVE, - state: &'a mut STATE, - dfu: &'a mut DFU, -} - -impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, -{ - /// Create a new flash provider with separate configuration for all three partitions. - pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { - Self { active, state, dfu } - } -} - -impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, -{ - type STATE = STATE; - type ACTIVE = ACTIVE; - type DFU = DFU; - - fn active(&mut self) -> &mut Self::ACTIVE { - self.active - } - fn dfu(&mut self) -> &mut Self::DFU { - self.dfu - } - fn state(&mut self) -> &mut Self::STATE { - self.state - } -} -/// Errors returned by FirmwareUpdater -#[derive(Debug)] -pub enum FirmwareUpdaterError { - /// Error from flash. - Flash(NorFlashErrorKind), - /// Signature errors. - Signature(signature::Error), -} - -#[cfg(feature = "defmt")] -impl defmt::Format for FirmwareUpdaterError { - fn format(&self, fmt: defmt::Formatter) { - match self { - FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), - FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), - } - } -} - -impl From for FirmwareUpdaterError -where - E: NorFlashError, -{ - fn from(error: E) -> Self { - FirmwareUpdaterError::Flash(error.kind()) - } -} - -/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to -/// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { - state: Partition, - dfu: Partition, -} - -impl Default for FirmwareUpdater { - fn default() -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - 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!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - FirmwareUpdater::new(dfu, state) - } -} - -impl FirmwareUpdater { - /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub const fn new(dfu: Partition, state: Partition) -> Self { - Self { dfu, state } - } - - /// Return the length of the DFU area - pub fn firmware_len(&self) -> usize { - self.dfu.len() - } - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub async fn get_state( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result { - flash.read(self.state.from as u32, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(feature = "_verify")] - pub async fn verify_and_mark_updated( - &mut self, - _flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: usize, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; - let _read_size = _aligned.len(); - - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); - } - - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); - } - - let message = digest.finalize(); - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic(_aligned, SWAP_MAGIC, _flash).await - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(not(feature = "_verify"))] - pub async fn mark_updated( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, flash).await - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, flash).await - } - - async fn set_magic( - &mut self, - aligned: &mut [u8], - magic: u8, - flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); - - flash.write(self.state.from as u32, aligned).await?; - flash.erase(self.state.from as u32, self.state.to as u32).await?; - - aligned.fill(magic); - flash.write(self.state.from as u32, aligned).await?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - flash - .erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - ) - .await?; - - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - - FirmwareWriter(self.dfu) - .write_block(offset, data, flash, block_size) - .await?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. - /// - /// Using this instead of `write_firmware` allows for an optimized API in - /// exchange for added complexity. - pub async fn prepare_update( - &mut self, - flash: &mut F, - ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - - Ok(FirmwareWriter(self.dfu)) - } - - // - // Blocking API - // - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub fn get_state_blocking( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result { - flash.read(self.state.from as u32, aligned)?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(feature = "_verify")] - pub fn verify_and_mark_updated_blocking( - &mut self, - _flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: usize, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; - let _read_size = _aligned.len(); - - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); - } - - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); - } - - let message = digest.finalize(); - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(not(feature = "_verify"))] - pub fn mark_updated_blocking( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, SWAP_MAGIC, flash) - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted_blocking( - &mut self, - flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, BOOT_MAGIC, flash) - } - - fn set_magic_blocking( - &mut self, - aligned: &mut [u8], - magic: u8, - flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned)?; - - if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); - - flash.write(self.state.from as u32, aligned)?; - flash.erase(self.state.from as u32, self.state.to as u32)?; - - aligned.fill(magic); - flash.write(self.state.from as u32, aligned)?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - flash.erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - )?; - - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - - FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. - /// - /// Using this instead of `write_firmware_blocking` allows for an optimized - /// API in exchange for added complexity. - pub fn prepare_update_blocking( - &mut self, - flash: &mut F, - ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - - Ok(FirmwareWriter(self.dfu)) - } -} - -/// FirmwareWriter allows writing blocks to an already erased flash. -pub struct FirmwareWriter(Partition); - -impl FirmwareWriter { - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_block( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; - for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk).await?; - write_offset += chunk.len(); - } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ - - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_block_blocking( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; - for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk)?; - write_offset += chunk.len(); - } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ - - Ok(()) - } -} - #[cfg(test)] mod tests { use core::convert::Infallible; - use embedded_storage::nor_flash::ErrorType; - use embedded_storage_async::nor_flash::ReadNorFlash as AsyncReadNorFlash; + use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; + use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; use futures::executor::block_on; use super::*; @@ -1414,15 +264,6 @@ mod tests { } } - #[test] - #[should_panic] - fn test_range_asserts() { - const ACTIVE: Partition = Partition::new(4096, 4194304); - const DFU: Partition = Partition::new(4194304, 2 * 4194304); - const STATE: Partition = Partition::new(0, 4096); - assert_partitions(ACTIVE, DFU, STATE, 4096, 4); - } - #[test] #[cfg(feature = "_verify")] fn test_verify() { diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs new file mode 100644 index 000000000..46f80a23c --- /dev/null +++ b/embassy-boot/boot/src/partition.rs @@ -0,0 +1,22 @@ +/// A region in flash used by the bootloader. +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Partition { + /// Start of the flash region. + pub from: usize, + /// End of the flash region. + pub to: usize, +} + +impl Partition { + /// Create a new partition with the provided range + pub const fn new(from: usize, to: usize) -> Self { + Self { from, to } + } + + /// Return the length of the partition + #[allow(clippy::len_without_is_empty)] + pub const fn len(&self) -> usize { + self.to - self.from + } +} From 42931b51f25ca22d7df3a6e8e98bfab7904eb11e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 10:18:19 +0200 Subject: [PATCH 0775/1575] Let bootloader partition have read/write/erase operations This change should not have any breaking changes. --- embassy-boot/boot/src/boot_loader.rs | 102 +++++++------ embassy-boot/boot/src/firmware_updater.rs | 174 +++++++--------------- embassy-boot/boot/src/firmware_writer.rs | 54 +------ embassy-boot/boot/src/partition.rs | 88 ++++++++++- 4 files changed, 195 insertions(+), 223 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index ad6735112..e2e361e3c 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -79,7 +79,7 @@ impl BootLoader { Self { active, dfu, state } } - /// Return the boot address for the active partition. + /// Return the offset of the active partition into the active flash. pub fn boot_address(&self) -> usize { self.active.from } @@ -193,13 +193,13 @@ impl BootLoader { self.revert(p, magic, page)?; // Overwrite magic and reset progress - let fstate = p.state(); + let state_flash = p.state(); magic.fill(!P::STATE::ERASE_VALUE); - fstate.write(self.state.from as u32, magic)?; - fstate.erase(self.state.from as u32, self.state.to as u32)?; + self.state.write_blocking(state_flash, 0, magic)?; + self.state.wipe_blocking(state_flash)?; magic.fill(BOOT_MAGIC); - fstate.write(self.state.from as u32, magic)?; + self.state.write_blocking(state_flash, 0, magic)?; } } Ok(state) @@ -218,9 +218,10 @@ impl BootLoader { let max_index = ((self.state.len() - write_size) / write_size) - 1; aligned.fill(!P::STATE::ERASE_VALUE); - let flash = config.state(); + let state_flash = config.state(); for i in 0..max_index { - flash.read((self.state.from + write_size + i * write_size) as u32, aligned)?; + self.state + .read_blocking(state_flash, (write_size + i * write_size) as u32, aligned)?; if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { return Ok(i); @@ -230,47 +231,39 @@ impl BootLoader { } fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let flash = p.state(); let write_size = magic.len(); - let w = self.state.from + write_size + idx * write_size; let aligned = magic; aligned.fill(!P::STATE::ERASE_VALUE); - flash.write(w as u32, aligned)?; + self.state + .write_blocking(p.state(), (write_size + idx * write_size) as u32, aligned)?; Ok(()) } - fn active_addr(&self, n: usize, page_size: usize) -> usize { - self.active.from + n * page_size - } - - fn dfu_addr(&self, n: usize, page_size: usize) -> usize { - self.dfu.from + n * page_size - } - fn copy_page_once_to_active( &mut self, idx: usize, - from_page: usize, - to_page: usize, + from_offset: u32, + to_offset: u32, p: &mut P, magic: &mut [u8], page: &mut [u8], ) -> Result<(), BootError> { let buf = page; if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; + let mut offset = from_offset; for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { - p.dfu().read(offset as u32, chunk)?; - offset += chunk.len(); + self.dfu.read_blocking(p.dfu(), offset, chunk)?; + offset += chunk.len() as u32; } - p.active().erase(to_page as u32, (to_page + buf.len()) as u32)?; + self.active + .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?; - let mut offset = to_page; + let mut offset = to_offset; for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { - p.active().write(offset as u32, chunk)?; - offset += chunk.len(); + self.active.write_blocking(p.active(), offset, chunk)?; + offset += chunk.len() as u32; } self.update_progress(idx, p, magic)?; } @@ -280,26 +273,27 @@ impl BootLoader { fn copy_page_once_to_dfu( &mut self, idx: usize, - from_page: usize, - to_page: usize, + from_offset: u32, + to_offset: u32, p: &mut P, magic: &mut [u8], page: &mut [u8], ) -> Result<(), BootError> { let buf = page; if self.current_progress(p, magic)? <= idx { - let mut offset = from_page; + let mut offset = from_offset; for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - p.active().read(offset as u32, chunk)?; - offset += chunk.len(); + self.active.read_blocking(p.active(), offset, chunk)?; + offset += chunk.len() as u32; } - p.dfu().erase(to_page as u32, (to_page + buf.len()) as u32)?; + self.dfu + .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?; - let mut offset = to_page; + let mut offset = to_offset; for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { - p.dfu().write(offset as u32, chunk)?; - offset += chunk.len(); + self.dfu.write_blocking(p.dfu(), offset, chunk)?; + offset += chunk.len() as u32; } self.update_progress(idx, p, magic)?; } @@ -312,17 +306,20 @@ impl BootLoader { trace!("Page count: {}", page_count); for page_num in 0..page_count { trace!("COPY PAGE {}", page_num); + + let idx = page_num * 2; + // Copy active page to the 'next' DFU page. - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - page_num, page_size); - //trace!("Copy active {} to dfu {}", active_page, dfu_page); - self.copy_page_once_to_dfu(page_num * 2, active_page, dfu_page, p, magic, page)?; + let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32; + let dfu_to_offset = ((page_count - page_num) * page_size) as u32; + //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; // Copy DFU page to the active page - let active_page = self.active_addr(page_count - 1 - page_num, page_size); - let dfu_page = self.dfu_addr(page_count - 1 - page_num, page_size); - //trace!("Copy dfy {} to active {}", dfu_page, active_page); - self.copy_page_once_to_active(page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32; + let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32; + //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; } Ok(()) @@ -332,23 +329,24 @@ impl BootLoader { let page_size = page.len(); let page_count = self.active.len() / page_size; for page_num in 0..page_count { + let idx = page_count * 2 + page_num * 2; + // Copy the bad active page to the DFU page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num, page_size); - self.copy_page_once_to_dfu(page_count * 2 + page_num * 2, active_page, dfu_page, p, magic, page)?; + let active_from_offset = (page_num * page_size) as u32; + let dfu_to_offset = (page_num * page_size) as u32; + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; // Copy the DFU page back to the active page - let active_page = self.active_addr(page_num, page_size); - let dfu_page = self.dfu_addr(page_num + 1, page_size); - self.copy_page_once_to_active(page_count * 2 + page_num * 2 + 1, dfu_page, active_page, p, magic, page)?; + let active_to_offset = (page_num * page_size) as u32; + let dfu_from_offset = ((page_num + 1) * page_size) as u32; + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; } Ok(()) } fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { - let flash = config.state(); - flash.read(self.state.from as u32, magic)?; + self.state.read_blocking(config.state(), 0, magic)?; if !magic.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 2d8712277..af1ba114a 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -84,10 +84,10 @@ impl FirmwareUpdater { /// `mark_booted`. pub async fn get_state( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result { - flash.read(self.state.from as u32, aligned).await?; + self.state.read(state_flash, 0, aligned).await?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -115,17 +115,16 @@ impl FirmwareUpdater { #[cfg(feature = "_verify")] pub async fn verify_and_mark_updated( &mut self, - _flash: &mut F, + _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; let _read_size = _aligned.len(); assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); + assert!(_update_len <= self.dfu.len()); #[cfg(feature = "ed25519-dalek")] { @@ -137,21 +136,10 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } public_key @@ -173,21 +161,10 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned).await?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned).await?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } let message = digest.finalize(); @@ -202,7 +179,7 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC, _flash).await + self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await } /// Mark to trigger firmware swap on next boot. @@ -213,11 +190,11 @@ impl FirmwareUpdater { #[cfg(not(feature = "_verify"))] pub async fn mark_updated( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, flash).await + self.set_magic(aligned, SWAP_MAGIC, state_flash).await } /// Mark firmware boot successful and stop rollback on reset. @@ -227,29 +204,29 @@ impl FirmwareUpdater { /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. pub async fn mark_booted( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, flash).await + self.set_magic(aligned, BOOT_MAGIC, state_flash).await } async fn set_magic( &mut self, aligned: &mut [u8], magic: u8, - flash: &mut F, + state_flash: &mut F, ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned).await?; + self.state.read(state_flash, 0, aligned).await?; if aligned.iter().any(|&b| b != magic) { aligned.fill(0); - flash.write(self.state.from as u32, aligned).await?; - flash.erase(self.state.from as u32, self.state.to as u32).await?; + self.state.write(state_flash, 0, aligned).await?; + self.state.wipe(state_flash).await?; aligned.fill(magic); - flash.write(self.state.from as u32, aligned).await?; + self.state.write(state_flash, 0, aligned).await?; } Ok(()) } @@ -265,26 +242,17 @@ impl FirmwareUpdater { &mut self, offset: usize, data: &[u8], - flash: &mut F, + dfu_flash: &mut F, block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); - flash - .erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - ) + self.dfu + .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) .await?; - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - FirmwareWriter(self.dfu) - .write_block(offset, data, flash, block_size) + .write_block(offset, data, dfu_flash, block_size) .await?; Ok(()) @@ -297,11 +265,9 @@ impl FirmwareUpdater { /// exchange for added complexity. pub async fn prepare_update( &mut self, - flash: &mut F, + dfu_flash: &mut F, ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + self.dfu.wipe(dfu_flash).await?; Ok(FirmwareWriter(self.dfu)) } @@ -317,10 +283,10 @@ impl FirmwareUpdater { /// `mark_booted`. pub fn get_state_blocking( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result { - flash.read(self.state.from as u32, aligned)?; + self.state.read_blocking(state_flash, 0, aligned)?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -348,7 +314,7 @@ impl FirmwareUpdater { #[cfg(feature = "_verify")] pub fn verify_and_mark_updated_blocking( &mut self, - _flash: &mut F, + _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: usize, @@ -370,21 +336,10 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } public_key @@ -406,21 +361,10 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - - let mut offset = self.dfu.from; - let last_offset = _end / _read_size * _read_size; - - while offset < last_offset { - _flash.read(offset as u32, _aligned)?; - digest.update(&_aligned); - offset += _read_size; - } - - let remaining = _end % _read_size; - - if remaining > 0 { - _flash.read(last_offset as u32, _aligned)?; - digest.update(&_aligned[0..remaining]); + for offset in (0.._update_len).step_by(_aligned.len()) { + self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; + let len = core::cmp::min(_update_len - offset, _aligned.len()); + digest.update(&_aligned[..len]); } let message = digest.finalize(); @@ -435,7 +379,7 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic_blocking(_aligned, SWAP_MAGIC, _flash) + self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) } /// Mark to trigger firmware swap on next boot. @@ -446,11 +390,11 @@ impl FirmwareUpdater { #[cfg(not(feature = "_verify"))] pub fn mark_updated_blocking( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, SWAP_MAGIC, flash) + self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) } /// Mark firmware boot successful and stop rollback on reset. @@ -460,29 +404,29 @@ impl FirmwareUpdater { /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. pub fn mark_booted_blocking( &mut self, - flash: &mut F, + state_flash: &mut F, aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, BOOT_MAGIC, flash) + self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) } fn set_magic_blocking( &mut self, aligned: &mut [u8], magic: u8, - flash: &mut F, + state_flash: &mut F, ) -> Result<(), FirmwareUpdaterError> { - flash.read(self.state.from as u32, aligned)?; + self.state.read_blocking(state_flash, 0, aligned)?; if aligned.iter().any(|&b| b != magic) { aligned.fill(0); - flash.write(self.state.from as u32, aligned)?; - flash.erase(self.state.from as u32, self.state.to as u32)?; + self.state.write_blocking(state_flash, 0, aligned)?; + self.state.wipe_blocking(state_flash)?; aligned.fill(magic); - flash.write(self.state.from as u32, aligned)?; + self.state.write_blocking(state_flash, 0, aligned)?; } Ok(()) } @@ -498,23 +442,15 @@ impl FirmwareUpdater { &mut self, offset: usize, data: &[u8], - flash: &mut F, + dfu_flash: &mut F, block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); - flash.erase( - (self.dfu.from + offset) as u32, - (self.dfu.from + offset + data.len()) as u32, - )?; + self.dfu + .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; - trace!( - "Erased from {} to {}", - self.dfu.from + offset, - self.dfu.from + offset + data.len() - ); - - FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; + FirmwareWriter(self.dfu).write_block_blocking(offset, data, dfu_flash, block_size)?; Ok(()) } @@ -528,9 +464,7 @@ impl FirmwareUpdater { &mut self, flash: &mut F, ) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; - - trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + self.dfu.wipe_blocking(flash)?; Ok(FirmwareWriter(self.dfu)) } diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs index f992021bb..46079e731 100644 --- a/embassy-boot/boot/src/firmware_writer.rs +++ b/embassy-boot/boot/src/firmware_writer.rs @@ -21,32 +21,11 @@ impl FirmwareWriter { flash: &mut F, block_size: usize, ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; + let mut offset = offset as u32; for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk).await?; - write_offset += chunk.len(); + self.0.write(flash, offset, chunk).await?; + offset += chunk.len() as u32; } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ Ok(()) } @@ -65,32 +44,11 @@ impl FirmwareWriter { flash: &mut F, block_size: usize, ) -> Result<(), F::Error> { - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.0.from + offset, - data.len() - ); - - let mut write_offset = self.0.from + offset; + let mut offset = offset as u32; for chunk in data.chunks(block_size) { - trace!("Wrote chunk at {}: {:?}", write_offset, chunk); - flash.write(write_offset as u32, chunk)?; - write_offset += chunk.len(); + self.0.write_blocking(flash, offset, chunk)?; + offset += chunk.len() as u32; } - /* - trace!("Wrote data, reading back for verification"); - - let mut buf: [u8; 4096] = [0; 4096]; - let mut data_offset = 0; - let mut read_offset = self.dfu.from + offset; - for chunk in buf.chunks_mut(block_size) { - flash.read(read_offset as u32, chunk).await?; - trace!("Read chunk at {}: {:?}", read_offset, chunk); - assert_eq!(&data[data_offset..data_offset + block_size], chunk); - read_offset += chunk.len(); - data_offset += chunk.len(); - } - */ Ok(()) } diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 46f80a23c..9918fb836 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -1,10 +1,13 @@ +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + /// A region in flash used by the bootloader. #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Partition { - /// Start of the flash region. + /// The offset into the flash where the partition starts. pub from: usize, - /// End of the flash region. + /// The offset into the flash where the partition ends. pub to: usize, } @@ -14,9 +17,88 @@ impl Partition { Self { from, to } } - /// Return the length of the partition + /// Return the size of the partition #[allow(clippy::len_without_is_empty)] pub const fn len(&self) -> usize { self.to - self.from } + + /// Read from the partition on the provided flash + pub(crate) async fn read( + &self, + flash: &mut F, + offset: u32, + bytes: &mut [u8], + ) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.read(offset, bytes).await + } + + /// Write to the partition on the provided flash + pub(crate) async fn write( + &self, + flash: &mut F, + offset: u32, + bytes: &[u8], + ) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.write(offset, bytes).await?; + trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); + Ok(()) + } + + /// Erase part of the partition on the provided flash + pub(crate) async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + let from = self.from as u32 + from; + let to = self.from as u32 + to; + flash.erase(from, to).await?; + trace!("Erased from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Erase the entire partition + pub(crate) async fn wipe(&self, flash: &mut F) -> Result<(), F::Error> { + let from = self.from as u32; + let to = self.to as u32; + flash.erase(from, to).await?; + trace!("Wiped from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Read from the partition on the provided flash + pub(crate) fn read_blocking( + &self, + flash: &mut F, + offset: u32, + bytes: &mut [u8], + ) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.read(offset, bytes) + } + + /// Write to the partition on the provided flash + pub(crate) fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.write(offset, bytes)?; + trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); + Ok(()) + } + + /// Erase part of the partition on the provided flash + pub(crate) fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + let from = self.from as u32 + from; + let to = self.from as u32 + to; + flash.erase(from, to)?; + trace!("Erased from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Erase the entire partition + pub(crate) fn wipe_blocking(&self, flash: &mut F) -> Result<(), F::Error> { + let from = self.from as u32; + let to = self.to as u32; + flash.erase(from, to)?; + trace!("Wiped from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } } From d9d6fd6d70f3f9971c6db65b6962199f9da7913c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 10:28:47 +0200 Subject: [PATCH 0776/1575] Add erase and wipe tests --- embassy-boot/boot/src/lib.rs | 3 +- embassy-boot/boot/src/partition.rs | 46 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index a2259411f..4c28d7aa4 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -313,7 +313,8 @@ mod tests { )) .is_ok()); } - struct MemFlash([u8; SIZE]); + + pub struct MemFlash(pub [u8; SIZE]); impl NorFlash for MemFlash diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 9918fb836..3ccd4dd76 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -102,3 +102,49 @@ impl Partition { Ok(()) } } + +#[cfg(test)] +mod tests { + use crate::tests::MemFlash; + use crate::Partition; + + #[test] + fn can_erase() { + let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let partition = Partition::new(256, 512); + + partition.erase_blocking(&mut flash, 64, 192).unwrap(); + + for (index, byte) in flash.0.iter().copied().enumerate().take(256 + 64) { + assert_eq!(0x00, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64).take(128) { + assert_eq!(0xFF, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64 + 128) { + assert_eq!(0x00, byte, "Index {}", index); + } + } + + #[test] + fn can_wipe() { + let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let partition = Partition::new(256, 512); + + partition.wipe_blocking(&mut flash).unwrap(); + + for (index, byte) in flash.0.iter().copied().enumerate().take(256) { + assert_eq!(0x00, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(256).take(256) { + assert_eq!(0xFF, byte, "Index {}", index); + } + + for (index, byte) in flash.0.iter().copied().enumerate().skip(512) { + assert_eq!(0x00, byte, "Index {}", index); + } + } +} From cfbe93c2805598652d85e21322b343032586b398 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 31 Mar 2023 10:43:30 +0200 Subject: [PATCH 0777/1575] Rework bufferedUart to get rid of PeripheralMutex in a similar fashion as nrf & rp. Also adds embedded-hal traits to bufferedUart --- embassy-stm32/src/usart/buffered.rs | 750 ++++++++++++++++++---------- embassy-stm32/src/usart/mod.rs | 9 + 2 files changed, 490 insertions(+), 269 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index cd7d72f91..6721c44a1 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,51 +1,47 @@ -use core::cell::RefCell; use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::slice; use core::task::Poll; -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; +use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_sync::waitqueue::AtomicWaker; use super::*; -pub struct State<'d, T: BasicInstance>(StateStorage>); -impl<'d, T: BasicInstance> State<'d, T> { +pub struct State { + rx_waker: AtomicWaker, + rx_buf: RingBuffer, + + tx_waker: AtomicWaker, + tx_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(), + } } } -struct StateInner<'d, T: BasicInstance> { - phantom: PhantomData<&'d mut T>, - - rx_waker: WakerRegistration, - rx: RingBuffer<'d>, - - tx_waker: WakerRegistration, - tx: RingBuffer<'d>, -} - -unsafe impl<'d, T: BasicInstance> Send for StateInner<'d, T> {} -unsafe impl<'d, T: BasicInstance> Sync for StateInner<'d, T> {} - pub struct BufferedUart<'d, T: BasicInstance> { - inner: RefCell>>, + rx: BufferedUartRx<'d, T>, + tx: BufferedUartTx<'d, T>, } -pub struct BufferedUartTx<'u, 'd, T: BasicInstance> { - inner: &'u BufferedUart<'d, T>, +pub struct BufferedUartTx<'d, T: BasicInstance> { + phantom: PhantomData<&'d mut T>, } -pub struct BufferedUartRx<'u, 'd, T: BasicInstance> { - inner: &'u BufferedUart<'d, T>, +pub struct BufferedUartRx<'d, T: BasicInstance> { + phantom: PhantomData<&'d mut T>, } -impl<'d, T: BasicInstance> Unpin for BufferedUart<'d, T> {} - impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( - state: &'d mut State<'d, T>, peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -57,11 +53,10 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::enable(); T::reset(); - Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) } pub fn new_with_rtscts( - state: &'d mut State<'d, T>, peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -86,12 +81,11 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) } #[cfg(not(usart_v1))] pub fn new_with_de( - state: &'d mut State<'d, T>, peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -113,11 +107,10 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) } fn new_inner( - state: &'d mut State<'d, T>, _peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -128,8 +121,13 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { ) -> BufferedUart<'d, T> { into_ref!(_peri, rx, tx, irq); - let r = T::regs(); + let state = T::buffered_state(); + 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 { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); @@ -147,273 +145,297 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self { - inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner { - phantom: PhantomData, - tx: RingBuffer::new(tx_buffer), - tx_waker: WakerRegistration::new(), + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); - rx: RingBuffer::new(rx_buffer), - rx_waker: WakerRegistration::new(), - })), + Self { + rx: BufferedUartRx { phantom: PhantomData }, + tx: BufferedUartTx { phantom: PhantomData }, } } - pub fn split<'u>(&'u mut self) -> (BufferedUartRx<'u, 'd, T>, BufferedUartTx<'u, 'd, T>) { - (BufferedUartRx { inner: self }, BufferedUartTx { inner: self }) + pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { + (self.rx, self.tx) } +} - async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result { +impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { + async fn read(&self, buf: &mut [u8]) -> Result { poll_fn(move |cx| { - let mut do_pend = false; - let mut inner = self.inner.borrow_mut(); - let res = inner.with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.is_full() { - do_pend = true; - } - state.rx.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx_waker.register(cx.waker()); - Poll::Pending + let state = T::buffered_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 do_pend { - inner.pend(); + if n == 0 { + state.rx_waker.register(cx.waker()); + return Poll::Pending; } - res + // FIXME: + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + // let regs = T::regs(); + // unsafe { + // regs.uartimsc().write_set(|w| { + // w.set_rxim(true); + // w.set_rtim(true); + // }); + // } + + Poll::Ready(Ok(n)) }) .await + + // poll_fn(move |cx| { + // let state = T::buffered_state(); + + // let mut do_pend = false; + // compiler_fence(Ordering::SeqCst); + + // // We have data ready in buffer? Return it. + // let data = state.rx_buf.pop_buf(); + // if !data.is_empty() { + // let len = data.len().min(buf.len()); + // buf[..len].copy_from_slice(&data[..len]); + + // if state.rx_buf.is_full() { + // do_pend = true; + // } + // state.rx_buf.pop(len); + + // return Poll::Ready(Ok(len)); + // } + + // state.rx_waker.register(cx.waker()); + + // if do_pend { + // inner.pend(); + // } + + // Poll::Pending + // }) + // .await } - fn inner_blocking_read(&self, buf: &mut [u8]) -> Result { + fn blocking_read(&self, buf: &mut [u8]) -> Result { loop { - let mut do_pend = false; - let mut inner = self.inner.borrow_mut(); - let n = inner.with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.is_full() { - do_pend = true; - } - state.rx.pop(len); - - return len; - } - - 0 + let state = T::buffered_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 do_pend { - inner.pend(); - } - if n > 0 { + // FIXME: + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + // let regs = T::regs(); + // unsafe { + // regs.uartimsc().write_set(|w| { + // w.set_rxim(true); + // w.set_rtim(true); + // }); + // } + return Ok(n); } } } - async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result { + async fn fill_buf(&self) -> Result<&[u8], Error> { poll_fn(move |cx| { - let mut inner = self.inner.borrow_mut(); - let (poll, empty) = inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - (Poll::Ready(Ok(n)), empty) - }); - if empty { - inner.pend(); + let state = T::buffered_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; } - poll + + let buf = unsafe { slice::from_raw_parts(p, n) }; + Poll::Ready(Ok(buf)) }) .await } - async fn inner_flush<'a>(&'a self) -> Result<(), Error> { + fn consume(&self, amt: usize) { + let state = T::buffered_state(); + let mut rx_reader = unsafe { state.rx_buf.reader() }; + let full = state.rx_buf.is_full(); + rx_reader.pop_done(amt); + if full { + unsafe { T::Interrupt::steal().pend() }; + } + } +} + +impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { + async fn write(&self, buf: &[u8]) -> Result { poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); - return Poll::Pending; - } + let state = T::buffered_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 n == 0 { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } - Poll::Ready(Ok(())) - }) + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. + unsafe { T::Interrupt::steal() }.pend(); + Poll::Ready(Ok(n)) }) .await } - fn inner_blocking_write(&self, buf: &[u8]) -> Result { + async fn flush(&self) -> Result<(), Error> { + poll_fn(move |cx| { + let state = T::buffered_state(); + if !state.tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + .await + } + + fn blocking_write(&self, buf: &[u8]) -> Result { loop { - let mut inner = self.inner.borrow_mut(); - let (n, empty) = inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); - if tx_buf.is_empty() { - return (0, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); - - (n, empty) + let state = T::buffered_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 empty { - inner.pend(); - } + if n != 0 { + // The TX interrupt only triggers when the there was data in the + // FIFO and the number of bytes drops below a threshold. When the + // FIFO was empty we have to manually pend the interrupt to shovel + // TX data from the buffer into the FIFO. + unsafe { T::Interrupt::steal() }.pend(); return Ok(n); } } } - fn inner_blocking_flush(&self) -> Result<(), Error> { + fn blocking_flush(&self) -> Result<(), Error> { loop { - if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) { + let state = T::buffered_state(); + if state.tx_buf.is_empty() { return Ok(()); } } } - - async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> { - poll_fn(move |cx| { - self.inner.borrow_mut().with(|state| { - compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.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)); - } - - state.rx_waker.register(cx.waker()); - Poll::>::Pending - }) - }) - .await - } - - fn inner_consume(&self, amt: usize) { - let mut inner = self.inner.borrow_mut(); - let signal = inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); - full - }); - if signal { - inner.pend(); - } - } } -impl<'d, T: BasicInstance> StateInner<'d, T> -where - Self: 'd, -{ - fn on_rx(&mut self) { - let r = T::regs(); +impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> { + fn drop(&mut self) { + let state = T::buffered_state(); unsafe { - let sr = sr(r).read(); - clear_interrupt_flags(r, sr); + state.rx_buf.deinit(); - // This read also clears the error and idle interrupt flags on v1. - let b = rdr(r).read_volatile(); - - if sr.rxne() { - if sr.pe() { - warn!("Parity error"); - } - if sr.fe() { - warn!("Framing error"); - } - if sr.ne() { - warn!("Noise error"); - } - if sr.ore() { - warn!("Overrun error"); - } - - let buf = self.rx.push_buf(); - if !buf.is_empty() { - buf[0] = b; - self.rx.push(1); - } else { - warn!("RX buffer full, discard received byte"); - } - - if self.rx.is_full() { - self.rx_waker.wake(); - } - } - - if sr.idle() { - self.rx_waker.wake(); - }; - } - } - - fn on_tx(&mut self) { - let r = T::regs(); - unsafe { - if sr(r).read().txe() { - let buf = self.tx.pop_buf(); - if !buf.is_empty() { - r.cr1().modify(|w| { - w.set_txeie(true); - }); - tdr(r).write_volatile(buf[0].into()); - self.tx.pop(1); - self.tx_waker.wake(); - } else { - // Disable interrupt until we have something to transmit again - r.cr1().modify(|w| { - w.set_txeie(false); - }); - } + // TX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.tx_buf.len() == 0 { + T::Interrupt::steal().disable(); } } } } -impl<'d, T: BasicInstance> PeripheralState for StateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.on_rx(); - self.on_tx(); +impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { + fn drop(&mut self) { + let state = T::buffered_state(); + unsafe { + state.tx_buf.deinit(); + + // RX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.rx_buf.len() == 0 { + T::Interrupt::steal().disable(); + } + } + } +} + +unsafe fn on_interrupt(_: *mut ()) { + let r = T::regs(); + let state = T::buffered_state(); + + // RX + unsafe { + let sr = sr(r).read(); + clear_interrupt_flags(r, sr); + + if sr.rxne() { + if sr.pe() { + warn!("Parity error"); + } + if sr.fe() { + warn!("Framing error"); + } + if sr.ne() { + warn!("Noise error"); + } + if sr.ore() { + warn!("Overrun error"); + } + + let mut rx_writer = state.rx_buf.writer(); + let buf = rx_writer.push_slice(); + if !buf.is_empty() { + // This read also clears the error and idle interrupt flags on v1. + buf[0] = rdr(r).read_volatile(); + rx_writer.push_done(1); + } else { + // FIXME: Should we disable any further RX interrupts when the buffer becomes full. + } + + if state.rx_buf.is_full() { + state.rx_waker.wake(); + } + } + + if sr.idle() { + state.rx_waker.wake(); + }; + } + + // TX + unsafe { + if sr(r).read().txe() { + let mut tx_reader = state.tx_buf.reader(); + let buf = tx_reader.pop_slice(); + if !buf.is_empty() { + r.cr1().modify(|w| { + w.set_txeie(true); + }); + tdr(r).write_volatile(buf[0].into()); + tx_reader.pop_done(1); + state.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.cr1().modify(|w| { + w.set_txeie(false); + }); + } + } } } @@ -427,94 +449,284 @@ impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } -impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> { type Error = Error; } -impl<'u, 'd, T: BasicInstance> embedded_io::Io for BufferedUartTx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> { type Error = Error; } impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_read(buf).await + self.rx.read(buf).await } } -impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.inner_read(buf).await + Self::read(self, buf).await } } impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner_fill_buf().await + self.rx.fill_buf().await } fn consume(&mut self, amt: usize) { - self.inner_consume(amt) + self.rx.consume(amt) } } -impl<'u, 'd, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner.inner_fill_buf().await + Self::fill_buf(self).await } fn consume(&mut self, amt: usize) { - self.inner.inner_consume(amt) + Self::consume(self, amt) } } impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { - self.inner_write(buf).await + self.tx.write(buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner_flush().await + self.tx.flush().await } } -impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { - self.inner.inner_write(buf).await + Self::write(self, buf).await } async fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.inner_flush().await + Self::flush(self).await } } impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_blocking_read(buf) + self.rx.blocking_read(buf) } } -impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.inner_blocking_read(buf) + self.blocking_read(buf) } } impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { - self.inner_blocking_write(buf) + self.tx.blocking_write(buf) } fn flush(&mut self) -> Result<(), Self::Error> { - self.inner_blocking_flush() + self.tx.blocking_flush() } } -impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> { +impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.inner_blocking_write(buf) + Self::blocking_write(self, buf) } fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.inner_blocking_flush() + Self::blocking_flush(self) + } +} + +mod eh02 { + use super::*; + + impl<'d, T: BasicInstance> embedded_hal_02::serial::Read for BufferedUartRx<'d, T> { + type Error = Error; + + fn read(&mut self) -> Result> { + let r = T::regs(); + unsafe { + let sr = sr(r).read(); + if sr.pe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Parity)) + } else if sr.fe() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Framing)) + } else if sr.ne() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Noise)) + } else if sr.ore() { + rdr(r).read_volatile(); + Err(nb::Error::Other(Error::Overrun)) + } else if sr.rxne() { + Ok(rdr(r).read_volatile()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write for BufferedUartTx<'d, T> { + type Error = Error; + + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_02::serial::Read for BufferedUart<'d, T> { + type Error = Error; + + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: BasicInstance> embedded_hal_02::blocking::serial::Write for BufferedUart<'d, T> { + type Error = Error; + + fn bwrite_all(&mut self, mut buffer: &[u8]) -> Result<(), Self::Error> { + while !buffer.is_empty() { + match self.tx.blocking_write(buffer) { + Ok(0) => panic!("zero-length write."), + Ok(n) => buffer = &buffer[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn bflush(&mut self) -> Result<(), Self::Error> { + self.tx.blocking_flush() + } + } +} + +#[cfg(feature = "unstable-traits")] +mod eh1 { + use super::*; + + impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + type Error = Error; + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + type Error = Error; + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + type Error = Error; + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUartRx<'d, T> { + fn read(&mut self) -> nb::Result { + embedded_hal_02::serial::Read::read(self) + } + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer).map(drop) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Read for BufferedUart<'d, T> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.tx.blocking_write(buffer).map(drop) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.blocking_flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.blocking_flush().map_err(nb::Error::Other) + } + } +} + +#[cfg(all( + feature = "unstable-traits", + feature = "nightly", + feature = "_todo_embedded_hal_serial" +))] +mod eha { + use core::future::Future; + + use super::*; + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> { + async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + Self::write(buf) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + Self::flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> { + async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + Self::read(buf) + } + } + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Write for BufferedUart<'d, T> { + async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + self.tx.write(buf) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + self.tx.flush() + } + } + + impl<'d, T: BasicInstance> embedded_hal_async::serial::Read for BufferedUart<'d, T> { + async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + self.rx.read(buf) + } } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index f80323e37..a42eede18 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1112,6 +1112,9 @@ pub(crate) mod sealed { fn regs() -> Regs; fn state() -> &'static State; + + #[cfg(feature = "nightly")] + fn buffered_state() -> &'static buffered::State; } pub trait FullInstance: BasicInstance { @@ -1147,6 +1150,12 @@ macro_rules! impl_lpuart { static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); &STATE } + + #[cfg(feature = "nightly")] + fn buffered_state() -> &'static buffered::State { + static STATE: buffered::State = buffered::State::new(); + &STATE + } } impl BasicInstance for peripherals::$inst {} From 50b0fb1a376dbc650db785228b181cc3939ac87f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 31 Mar 2023 15:47:45 +0200 Subject: [PATCH 0778/1575] Let get_flash_regions be public --- embassy-stm32/src/flash/f3.rs | 2 +- embassy-stm32/src/flash/f4.rs | 4 ++-- embassy-stm32/src/flash/f7.rs | 2 +- embassy-stm32/src/flash/h7.rs | 2 +- embassy-stm32/src/flash/l.rs | 2 +- embassy-stm32/src/flash/other.rs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index c1ed33f9d..10a09c42c 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -7,7 +7,7 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 61ac8afd5..2f5b417cc 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -91,7 +91,7 @@ mod alt_regions { pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub fn get_flash_regions() -> &'static [&'static FlashRegion] { if unsafe { pac::FLASH.optcr().read().db1m() } { &ALT_FLASH_REGIONS } else { @@ -100,7 +100,7 @@ pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { } #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 7111f5cc4..6427d5a09 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -6,7 +6,7 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 5ea57ccde..4f38d50c0 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -11,7 +11,7 @@ const fn is_dual_bank() -> bool { FLASH_REGIONS.len() == 2 } -pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index f5ebc0a5f..7d9cc6ea3 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -6,7 +6,7 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index d329dcab4..c151cb828 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -2,7 +2,7 @@ use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; -pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } From 472dc6b7d16ea10b9512090acb1b74aa24ed189f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 31 Mar 2023 15:50:37 +0200 Subject: [PATCH 0779/1575] Fix interrupt handling so it is similar to before the rework, and fix examples --- embassy-stm32/src/usart/buffered.rs | 154 ++++++++------------- examples/stm32f4/src/bin/usart_buffered.rs | 14 +- examples/stm32l0/src/bin/usart_irq.rs | 16 +-- 3 files changed, 62 insertions(+), 122 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 6721c44a1..3e23e7ca1 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -43,9 +43,9 @@ pub struct BufferedUartRx<'d, T: BasicInstance> { impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, @@ -53,14 +53,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::enable(); T::reset(); - Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } pub fn new_with_rtscts( peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], @@ -81,15 +81,15 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } #[cfg(not(usart_v1))] pub fn new_with_de( peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, de: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], @@ -107,14 +107,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(peri, rx, tx, irq, tx_buffer, rx_buffer, config) + Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } fn new_inner( _peri: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, @@ -155,8 +155,8 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { } } - pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { - (self.rx, self.tx) + pub fn split(self) -> (BufferedUartTx<'d, T>, BufferedUartRx<'d, T>) { + (self.tx, self.rx) } } @@ -165,85 +165,46 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { poll_fn(move |cx| { let state = T::buffered_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; + let data = rx_reader.pop_slice(); + + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + let do_pend = state.rx_buf.is_full(); + rx_reader.pop_done(len); + + if do_pend { + unsafe { T::Interrupt::steal().pend() }; + } + + return Poll::Ready(Ok(len)); } - // FIXME: - // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. - // let regs = T::regs(); - // unsafe { - // regs.uartimsc().write_set(|w| { - // w.set_rxim(true); - // w.set_rtim(true); - // }); - // } - - Poll::Ready(Ok(n)) + state.rx_waker.register(cx.waker()); + Poll::Pending }) .await - - // poll_fn(move |cx| { - // let state = T::buffered_state(); - - // let mut do_pend = false; - // compiler_fence(Ordering::SeqCst); - - // // We have data ready in buffer? Return it. - // let data = state.rx_buf.pop_buf(); - // if !data.is_empty() { - // let len = data.len().min(buf.len()); - // buf[..len].copy_from_slice(&data[..len]); - - // if state.rx_buf.is_full() { - // do_pend = true; - // } - // state.rx_buf.pop(len); - - // return Poll::Ready(Ok(len)); - // } - - // state.rx_waker.register(cx.waker()); - - // if do_pend { - // inner.pend(); - // } - - // Poll::Pending - // }) - // .await } fn blocking_read(&self, buf: &mut [u8]) -> Result { loop { let state = T::buffered_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 - }); + let data = rx_reader.pop_slice(); - if n > 0 { - // FIXME: - // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. - // let regs = T::regs(); - // unsafe { - // regs.uartimsc().write_set(|w| { - // w.set_rxim(true); - // w.set_rtim(true); - // }); - // } + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); - return Ok(n); + let do_pend = state.rx_buf.is_full(); + rx_reader.pop_done(len); + + if do_pend { + unsafe { T::Interrupt::steal().pend() }; + } + + return Ok(len); } } } @@ -279,22 +240,23 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { async fn write(&self, buf: &[u8]) -> Result { poll_fn(move |cx| { let state = T::buffered_state(); + let empty = state.tx_buf.is_empty(); + 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 n == 0 { + let data = tx_writer.push_slice(); + if data.is_empty() { state.tx_waker.register(cx.waker()); return Poll::Pending; } - // The TX interrupt only triggers when the there was data in the - // FIFO and the number of bytes drops below a threshold. When the - // FIFO was empty we have to manually pend the interrupt to shovel - // TX data from the buffer into the FIFO. - unsafe { T::Interrupt::steal() }.pend(); + let n = data.len().min(buf.len()); + data[..n].copy_from_slice(&buf[..n]); + tx_writer.push_done(n); + + if empty { + unsafe { T::Interrupt::steal() }.pend(); + } + Poll::Ready(Ok(n)) }) .await @@ -316,19 +278,19 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { fn blocking_write(&self, buf: &[u8]) -> Result { loop { let state = T::buffered_state(); + let empty = state.tx_buf.is_empty(); + let mut tx_writer = unsafe { state.tx_buf.writer() }; - let n = tx_writer.push(|data| { + let data = tx_writer.push_slice(); + if !data.is_empty() { let n = data.len().min(buf.len()); data[..n].copy_from_slice(&buf[..n]); - n - }); + tx_writer.push_done(n); + + if empty { + unsafe { T::Interrupt::steal() }.pend(); + } - if n != 0 { - // The TX interrupt only triggers when the there was data in the - // FIFO and the number of bytes drops below a threshold. When the - // FIFO was empty we have to manually pend the interrupt to shovel - // TX data from the buffer into the FIFO. - unsafe { T::Interrupt::steal() }.pend(); return Ok(n); } } diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index dd171fe13..a93f8baeb 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State}; +use embassy_stm32::usart::{BufferedUart, Config}; use embedded_io::asynch::BufRead; use {defmt_rtt as _, panic_probe as _}; @@ -16,20 +16,10 @@ async fn main(_spawner: Spawner) { let config = Config::default(); - let mut state = State::new(); let irq = interrupt::take!(USART3); let mut tx_buf = [0u8; 32]; let mut rx_buf = [0u8; 32]; - let mut buf_usart = BufferedUart::new( - &mut state, - p.USART3, - p.PD9, - p.PD8, - irq, - &mut tx_buf, - &mut rx_buf, - config, - ); + let mut buf_usart = BufferedUart::new(p.USART3, irq, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config); loop { let buf = buf_usart.fill_buf().await.unwrap(); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 8e84cd092..465347004 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::interrupt; -use embassy_stm32::usart::{BufferedUart, Config, State}; +use embassy_stm32::usart::{BufferedUart, Config}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; @@ -20,20 +20,8 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 9600; - let mut state = State::new(); let irq = interrupt::take!(USART2); - let mut usart = unsafe { - BufferedUart::new( - &mut state, - p.USART2, - p.PA3, - p.PA2, - irq, - &mut TX_BUFFER, - &mut RX_BUFFER, - config, - ) - }; + let mut usart = unsafe { BufferedUart::new(p.USART2, irq, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) }; usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); From 0e13fe992543980b1d05547bd2c7f7c08530a8d2 Mon Sep 17 00:00:00 2001 From: Thierry Fleury Date: Sat, 1 Apr 2023 11:44:49 +0200 Subject: [PATCH 0780/1575] Fix set_baudrate on RP-PICO --- embassy-rp/src/uart/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 682243a27..a945f2295 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -405,10 +405,6 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { Parity::ParityEven => (true, true), }; - // PL011 needs a (dummy) line control register write to latch in the - // divisors. We don't want to actually change LCR contents here. - r.uartlcr_h().modify(|_| {}); - r.uartlcr_h().write(|w| { w.set_wlen(config.data_bits.bits()); w.set_stp2(config.stop_bits == StopBits::STOP2); @@ -458,6 +454,10 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { // Load PL011's baud divisor registers r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + + // PL011 needs a (dummy) line control register write to latch in the + // divisors. We don't want to actually change LCR contents here. + r.uartlcr_h().modify(|_| {}); } } } From f78aa4f936b8bcdd8237f070125e60595f582eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 1 Apr 2023 14:31:24 +0200 Subject: [PATCH 0781/1575] rp: Allow zero len reads for buffered uart Prevents the read methods from getting stuck forever. --- embassy-rp/src/uart/buffered.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 1a573b311..9aa0b5714 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -175,6 +175,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { poll_fn(move |cx| { + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { @@ -202,6 +206,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { } pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + loop { let state = T::state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; From 268e29b153c9c8a6e3a798a01757669ee31e5409 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 1 Apr 2023 16:59:21 +0200 Subject: [PATCH 0782/1575] Let the FlashRegion for region types be public --- embassy-stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 4ca3ef3f0..67b081fee 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -145,7 +145,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { - pub struct #region_type(pub(crate) &'static crate::flash::FlashRegion); + pub struct #region_type(pub &'static crate::flash::FlashRegion); }); } From e11eebfa577b143b8a19fcf6d9d5398c3058c3eb Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 1 Apr 2023 17:26:32 +0200 Subject: [PATCH 0783/1575] Ensure that ranges are validated with the region size --- embassy-stm32/src/flash/common.rs | 57 ++++++++++++------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 59429e344..287c54f55 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -20,21 +20,15 @@ impl<'d> Flash<'d> { } pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + offset; - blocking_read(start_address, bytes) + blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + offset; - - unsafe { blocking_write(start_address, buf) } + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { blocking_write(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = FLASH_BASE as u32 + from; - let end_address = FLASH_BASE as u32 + to; - - unsafe { blocking_erase(start_address, end_address) } + unsafe { blocking_erase(FLASH_BASE as u32, from, to) } } pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { @@ -55,30 +49,29 @@ impl Drop for FlashLayout<'_> { } } -fn blocking_read(start_address: u32, bytes: &mut [u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + bytes.len() > FLASH_BASE + FLASH_SIZE { +fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { return Err(Error::Size); } + let start_address = base + offset; let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) } -unsafe fn blocking_write(start_address: u32, buf: &[u8]) -> Result<(), Error> { - assert!(start_address >= FLASH_BASE as u32); - if start_address as usize + buf.len() > FLASH_BASE + FLASH_SIZE { +unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { return Err(Error::Size); } - if (start_address as usize - FLASH_BASE) % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 { + if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 { return Err(Error::Unaligned); } - trace!("Writing {} bytes at 0x{:x}", buf.len(), start_address); + let mut address = base + offset; + trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); - let mut address = start_address; - for chunk in buf.chunks(WRITE_SIZE) { + for chunk in bytes.chunks(WRITE_SIZE) { critical_section::with(|_| { family::clear_all_err(); family::unlock(); @@ -94,7 +87,9 @@ unsafe fn blocking_write(start_address: u32, buf: &[u8]) -> Result<(), Error> { Ok(()) } -unsafe fn blocking_erase(start_address: u32, end_address: u32) -> Result<(), Error> { +unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { + let start_address = base + from; + let end_address = base + to; let regions = family::get_flash_regions(); // Test if the address range is aligned at sector base addresses @@ -157,19 +152,15 @@ pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector impl FlashRegion { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let start_address = self.base + offset; - blocking_read(start_address, bytes) + blocking_read(self.base, self.size, offset, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let start_address = self.base + offset; - unsafe { blocking_write(start_address, bytes) } + unsafe { blocking_write(self.base, self.size, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = self.base + from; - let end_address = self.base + to; - unsafe { blocking_erase(start_address, end_address) } + unsafe { blocking_erase(self.base, from, to) } } } @@ -177,19 +168,15 @@ foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - let start_address = self.0.base + offset; - blocking_read(start_address, bytes) + blocking_read(self.0.base, self.0.size, offset, bytes) } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let start_address = self.0.base + offset; - unsafe { blocking_write(start_address, bytes) } + unsafe { blocking_write(self.0.base, self.0.size, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let start_address = self.0.base + from; - let end_address = self.0.base + to; - unsafe { blocking_erase(start_address, end_address) } + unsafe { blocking_erase(self.0.base, from, to) } } } From dd88775871d2946df6e7e06bf2b536e94973dbaa Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 1 Apr 2023 18:10:20 +0200 Subject: [PATCH 0784/1575] Ensure that flash locking is defered to after write --- embassy-stm32/src/flash/common.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 287c54f55..41c28c561 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,3 +1,4 @@ +use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -74,12 +75,18 @@ unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Res for chunk in bytes.chunks(WRITE_SIZE) { critical_section::with(|_| { family::clear_all_err(); + fence(Ordering::SeqCst); family::unlock(); + fence(Ordering::SeqCst); family::begin_write(); - let _ = OnDrop::new(|| { + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { family::end_write(); + fence(Ordering::SeqCst); family::lock(); }); + family::blocking_write(address, chunk.try_into().unwrap()) })?; address += WRITE_SIZE as u32; @@ -114,10 +121,14 @@ unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { critical_section::with(|_| { family::clear_all_err(); + fence(Ordering::SeqCst); family::unlock(); - let _ = OnDrop::new(|| { + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { family::lock(); }); + family::blocking_erase_sector(§or) })?; address += sector.size; From 7ef6a3cfb22ee1e497fcea37a019ca32d22298b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 2 Apr 2023 14:36:32 +0200 Subject: [PATCH 0785/1575] rp: Allow zero len writes for buffered uart Prevents the write methods from getting stuck forever. --- embassy-rp/src/uart/buffered.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9aa0b5714..c620ed08c 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -301,6 +301,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { fn write<'a>(buf: &'a [u8]) -> impl Future> + 'a { poll_fn(move |cx| { + if buf.is_empty() { + return Poll::Ready(Ok(0)); + } + let state = T::state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; let n = tx_writer.push(|data| { @@ -335,6 +339,10 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } pub fn blocking_write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + loop { let state = T::state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; From b41ee47115509ba5ed302c684c0c36b6a3e3f76f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 01:11:42 +0200 Subject: [PATCH 0786/1575] executor: unify export mod. --- embassy-executor/src/lib.rs | 12 ++++-------- embassy-macros/src/macros/cortex_m_interrupt_take.rs | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 4c7e2f4cd..8707995b4 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -46,11 +46,13 @@ cfg_if::cfg_if! { } } +/// Implementation details for embassy macros. +/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. #[doc(hidden)] -/// Implementation details for embassy macros. DO NOT USE. -pub mod export { +pub mod _export { #[cfg(feature = "rtos-trace")] pub use rtos_trace::trace; + pub use static_cell::StaticCell; /// Expands the given block of code when `embassy-executor` is compiled with /// the `rtos-trace-interrupt` feature. @@ -75,9 +77,3 @@ pub mod raw; mod spawner; pub use spawner::*; - -/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. -#[doc(hidden)] -pub mod _export { - pub use static_cell::StaticCell; -} diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index e2ebf98c7..4806d1c12 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs @@ -10,12 +10,12 @@ pub fn run(name: syn::Ident) -> Result { let (isr_enter, isr_exit) = ( quote! { ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::export::trace::isr_enter(); + ::embassy_executor::_export::trace::isr_enter(); } }, quote! { ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::export::trace::isr_exit(); + ::embassy_executor::_export::trace::isr_exit(); } }, ); From 94890e544e07eabbc806dd5c05419e4336b5bf32 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 02:01:06 +0200 Subject: [PATCH 0787/1575] Update stm32-metapac. --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4f660187c..504caacb2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "2" +stm32-metapac = "3" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "2", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "3", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] From d3c4e4a20a05085eae8d568c7efdbe09bada9cf5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 01:18:27 +0200 Subject: [PATCH 0788/1575] executor: add Pender, rework Cargo features. This introduces a `Pender` struct with enum cases for thread-mode, interrupt-mode and custom callback executors. This avoids calls through function pointers when using only the thread or interrupt executors. Faster, and friendlier to `cargo-call-stack`. `embassy-executor` now has `arch-xxx` Cargo features to select the arch and to enable the builtin executors (thread and interrupt). --- docs/modules/ROOT/examples/basic/Cargo.toml | 2 +- .../layer-by-layer/blinky-async/Cargo.toml | 2 +- embassy-cortex-m/src/executor.rs | 116 --------- embassy-cortex-m/src/lib.rs | 2 +- embassy-executor/Cargo.toml | 25 +- embassy-executor/src/arch/cortex_m.rs | 242 ++++++++++++++---- embassy-executor/src/arch/riscv32.rs | 133 +++++----- embassy-executor/src/arch/std.rs | 152 ++++++----- embassy-executor/src/arch/wasm.rs | 136 +++++----- embassy-executor/src/arch/xtensa.rs | 135 +++++----- embassy-executor/src/lib.rs | 71 +++-- embassy-executor/src/raw/mod.rs | 97 +++++-- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f0/src/bin/multiprio.rs | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f3/src/bin/multiprio.rs | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/src/bin/multiprio.rs | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 50 files changed, 667 insertions(+), 522 deletions(-) delete mode 100644 embassy-cortex-m/src/executor.rs diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index d9f8a285a..e3e446e63 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index c9a963d4d..a11a7e0ba 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" cortex-m = "0.7" cortex-m-rt = "0.7" embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } -embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly"] } +embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/embassy-cortex-m/src/executor.rs b/embassy-cortex-m/src/executor.rs deleted file mode 100644 index 558539e73..000000000 --- a/embassy-cortex-m/src/executor.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Executor specific to cortex-m devices. - -use core::cell::UnsafeCell; -use core::mem::MaybeUninit; - -use atomic_polyfill::{AtomicBool, Ordering}; -use cortex_m::interrupt::InterruptNumber; -use cortex_m::peripheral::NVIC; -pub use embassy_executor::*; - -#[derive(Clone, Copy)] -struct N(u16); -unsafe impl cortex_m::interrupt::InterruptNumber for N { - fn number(self) -> u16 { - self.0 - } -} - -fn pend_by_number(n: u16) { - cortex_m::peripheral::NVIC::pend(N(n)) -} - -/// Interrupt mode executor. -/// -/// This executor runs tasks in interrupt mode. The interrupt handler is set up -/// to poll tasks, and when a task is woken the interrupt is pended from software. -/// -/// This allows running async tasks at a priority higher than thread mode. One -/// use case is to leave thread mode free for non-async tasks. Another use case is -/// to run multiple executors: one in thread mode for low priority tasks and another in -/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower -/// priority ones. -/// -/// It is even possible to run multiple interrupt mode executors at different priorities, -/// by assigning different priorities to the interrupts. For an example on how to do this, -/// See the 'multiprio' example for 'embassy-nrf'. -/// -/// To use it, you have to pick an interrupt that won't be used by the hardware. -/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). -/// If this is not the case, you may use an interrupt from any unused peripheral. -/// -/// It is somewhat more complex to use, it's recommended to use the thread-mode -/// [`Executor`] instead, if it works for your use case. -pub struct InterruptExecutor { - started: AtomicBool, - executor: UnsafeCell>, -} - -unsafe impl Send for InterruptExecutor {} -unsafe impl Sync for InterruptExecutor {} - -impl InterruptExecutor { - /// Create a new, not started `InterruptExecutor`. - #[inline] - pub const fn new() -> Self { - Self { - started: AtomicBool::new(false), - executor: UnsafeCell::new(MaybeUninit::uninit()), - } - } - - /// Executor interrupt callback. - /// - /// # Safety - /// - /// You MUST call this from the interrupt handler, and from nowhere else. - pub unsafe fn on_interrupt(&'static self) { - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.poll(); - } - - /// Start the executor. - /// - /// This initializes the executor, enables the interrupt, and returns. - /// The executor keeps running in the background through the interrupt. - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] - /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a - /// different "thread" (the interrupt), so spawning tasks on it is effectively - /// sending them. - /// - /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from - /// a task running in it. - /// - /// # Interrupt requirements - /// - /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). - /// - /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. - /// - /// You must set the interrupt priority before calling this method. You MUST NOT - /// do it after. - /// - pub fn start(&'static self, irq: impl InterruptNumber) -> SendSpawner { - if self - .started - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - panic!("InterruptExecutor::start() called multiple times on the same executor."); - } - - unsafe { - (&mut *self.executor.get()).as_mut_ptr().write(raw::Executor::new( - |ctx| pend_by_number(ctx as u16), - irq.number() as *mut (), - )) - } - - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - - unsafe { NVIC::unmask(irq) } - - executor.spawner().make_send() - } -} diff --git a/embassy-cortex-m/src/lib.rs b/embassy-cortex-m/src/lib.rs index fba23367b..e4b713a06 100644 --- a/embassy-cortex-m/src/lib.rs +++ b/embassy-cortex-m/src/lib.rs @@ -5,6 +5,6 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub mod executor; +pub use embassy_executor as executor; pub mod interrupt; pub mod peripheral; diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 8ad3fd698..bb8a46c82 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,9 +31,22 @@ flavors = [ features = ["std", "nightly", "defmt"] [features] -default = [] -std = ["critical-section/std"] -wasm = ["dep:wasm-bindgen", "dep:js-sys"] + +# Architecture +_arch = [] # some arch was picked +arch-std = ["_arch", "critical-section/std"] +arch-cortex-m = ["_arch", "dep:cortex-m"] +arch-xtensa = ["_arch"] +arch-riscv32 = ["_arch"] +arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] + +# Enable creating a `Pender` from an arbitrary function pointer callback. +pender-callback = [] + +# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) +executor-thread = [] +# Enable the interrupt-mode executor (available in Cortex-M only) +executor-interrupt = [] # Enable nightly-only features nightly = [] @@ -55,9 +68,11 @@ embassy-macros = { version = "0.1.0", path = "../embassy-macros" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" -cfg-if = "1.0.0" static_cell = "1.0" -# WASM dependencies +# arch-cortex-m dependencies +cortex-m = { version = "0.7.6", optional = true } + +# arch-wasm dependencies wasm-bindgen = { version = "0.2.82", optional = true } js-sys = { version = "0.3", optional = true } diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 4b27a264e..d6a55c4c7 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,59 +1,209 @@ -use core::arch::asm; -use core::marker::PhantomData; -use core::ptr; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::arch::asm; + use core::marker::PhantomData; -use super::{raw, Spawner}; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_cortex_m as main; -/// Thread mode executor, using WFE/SEV. -/// -/// This is the simplest and most common kind of executor. It runs on -/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction -/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction -/// is executed, to make the `WFE` exit from sleep and poll the task. -/// -/// This executor allows for ultra low power consumption for chips where `WFE` -/// triggers low-power sleep without extra steps. If your chip requires extra steps, -/// you may use [`raw::Executor`] directly to program custom behavior. -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()), - not_send: PhantomData, + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender; + + impl ThreadPender { + pub(crate) fn pend(self) { + unsafe { core::arch::asm!("sev") } } } - /// Run the executor. + /// Thread mode executor, using WFE/SEV. /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. + /// This is the simplest and most common kind of executor. It runs on + /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction + /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction + /// is executed, to make the `WFE` exit from sleep and poll the task. /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + /// This executor allows for ultra low power consumption for chips where `WFE` + /// triggers low-power sleep without extra steps. If your chip requires extra steps, + /// you may use [`raw::Executor`] directly to program custom behavior. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + asm!("wfe"); + }; + } + } + } +} + +#[cfg(feature = "executor-interrupt")] +pub use interrupt::*; +#[cfg(feature = "executor-interrupt")] +mod interrupt { + use core::cell::UnsafeCell; + use core::mem::MaybeUninit; + + use atomic_polyfill::{AtomicBool, Ordering}; + use cortex_m::interrupt::InterruptNumber; + use cortex_m::peripheral::NVIC; + + use crate::raw::{self, Pender, PenderInner}; + + #[derive(Clone, Copy)] + pub(crate) struct InterruptPender(u16); + + impl InterruptPender { + pub(crate) fn pend(self) { + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; + nvic.request(self); + } + + #[cfg(armv6m)] + cortex_m::peripheral::NVIC::pend(self); + } + } + + unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { + fn number(self) -> u16 { + self.0 + } + } + + /// Interrupt mode executor. + /// + /// This executor runs tasks in interrupt mode. The interrupt handler is set up + /// to poll tasks, and when a task is woken the interrupt is pended from software. + /// + /// This allows running async tasks at a priority higher than thread mode. One + /// use case is to leave thread mode free for non-async tasks. Another use case is + /// to run multiple executors: one in thread mode for low priority tasks and another in + /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower + /// priority ones. + /// + /// It is even possible to run multiple interrupt mode executors at different priorities, + /// by assigning different priorities to the interrupts. For an example on how to do this, + /// See the 'multiprio' example for 'embassy-nrf'. + /// + /// To use it, you have to pick an interrupt that won't be used by the hardware. + /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). + /// If this is not the case, you may use an interrupt from any unused peripheral. + /// + /// It is somewhat more complex to use, it's recommended to use the thread-mode + /// [`Executor`] instead, if it works for your use case. + pub struct InterruptExecutor { + started: AtomicBool, + executor: UnsafeCell>, + } + + unsafe impl Send for InterruptExecutor {} + unsafe impl Sync for InterruptExecutor {} + + impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { + Self { + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + + /// Start the executor. + /// + /// This initializes the executor, enables the interrupt, and returns. + /// The executor keeps running in the background through the interrupt. + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] + /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a + /// different "thread" (the interrupt), so spawning tasks on it is effectively + /// sending them. + /// + /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from + /// a task running in it. + /// + /// # Interrupt requirements + /// + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } - loop { unsafe { - self.inner.poll(); - asm!("wfe"); - }; + (&mut *self.executor.get()) + .as_mut_ptr() + .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender( + irq.number(), + ))))) + } + + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() } } } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index e97a56cda..f66daeae4 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,72 +1,83 @@ -use core::marker::PhantomData; -use core::ptr; -use core::sync::atomic::{AtomicBool, Ordering}; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); -use super::{raw, Spawner}; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::marker::PhantomData; + use core::sync::atomic::{AtomicBool, Ordering}; -/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV -/// -static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -/// RISCV32 Executor -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - // use Signal_Work_Thread_Mode as substitute for local interrupt register - inner: raw::Executor::new( - |_| { - SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); - }, - ptr::null_mut(), - ), - not_send: PhantomData, + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV + static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - //will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - core::arch::asm!("wfi"); - } - }); - // if an interrupt occurred while waiting, it will be serviced here + /// RISCV32 Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + //will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } + // if not, wait for interrupt + else { + core::arch::asm!("wfi"); + } + }); + // if an interrupt occurred while waiting, it will be serviced here + } } } } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 701f0eb18..4e4a178f0 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -1,84 +1,100 @@ -use std::marker::PhantomData; -use std::sync::{Condvar, Mutex}; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-std`."); -use super::{raw, Spawner}; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use std::marker::PhantomData; + use std::sync::{Condvar, Mutex}; -/// Single-threaded std-based executor. -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, - signaler: &'static Signaler, -} + #[cfg(feature = "nightly")] + pub use embassy_macros::main_std as main; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); - Self { - inner: raw::Executor::new( - |p| unsafe { - let s = &*(p as *const () as *const Signaler); - s.signal() - }, - signaler as *const _ as _, - ), - not_send: PhantomData, - signaler, + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; + + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender(&'static Signaler); + + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + self.0.signal() } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { self.inner.poll() }; - self.signaler.wait() - } + /// Single-threaded std-based executor. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + signaler: &'static Signaler, } -} -struct Signaler { - mutex: Mutex, - condvar: Condvar, -} + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let signaler = &*Box::leak(Box::new(Signaler::new())); + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), + not_send: PhantomData, + signaler, + } + } -impl Signaler { - fn new() -> Self { - Self { - mutex: Mutex::new(false), - condvar: Condvar::new(), + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + self.signaler.wait() + } } } - fn wait(&self) { - let mut signaled = self.mutex.lock().unwrap(); - while !*signaled { - signaled = self.condvar.wait(signaled).unwrap(); - } - *signaled = false; + struct Signaler { + mutex: Mutex, + condvar: Condvar, } - fn signal(&self) { - let mut signaled = self.mutex.lock().unwrap(); - *signaled = true; - self.condvar.notify_one(); + impl Signaler { + fn new() -> Self { + Self { + mutex: Mutex::new(false), + condvar: Condvar::new(), + } + } + + fn wait(&self) { + let mut signaled = self.mutex.lock().unwrap(); + while !*signaled { + signaled = self.condvar.wait(signaled).unwrap(); + } + *signaled = false; + } + + fn signal(&self) { + let mut signaled = self.mutex.lock().unwrap(); + *signaled = true; + self.condvar.notify_one(); + } } } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 98091cfbb..08ab16b99 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -1,74 +1,88 @@ -use core::marker::PhantomData; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); -use js_sys::Promise; -use wasm_bindgen::prelude::*; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { -use super::raw::util::UninitCell; -use super::raw::{self}; -use super::Spawner; + use core::marker::PhantomData; -/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. -pub struct Executor { - inner: raw::Executor, - ctx: &'static WasmContext, - not_send: PhantomData<*mut ()>, -} + #[cfg(feature = "nightly")] + pub use embassy_macros::main_wasm as main; + use js_sys::Promise; + use wasm_bindgen::prelude::*; -pub(crate) struct WasmContext { - promise: Promise, - closure: UninitCell>, -} + use crate::raw::util::UninitCell; + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -impl WasmContext { - pub fn new() -> Self { - Self { - promise: Promise::resolve(&JsValue::undefined()), - closure: UninitCell::uninit(), - } + /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. + pub struct Executor { + inner: raw::Executor, + ctx: &'static WasmContext, + not_send: PhantomData<*mut ()>, } -} -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - let ctx = &*Box::leak(Box::new(WasmContext::new())); - let inner = raw::Executor::new( - |p| unsafe { - let ctx = &*(p as *const () as *const WasmContext); - let _ = ctx.promise.then(ctx.closure.as_mut()); - }, - ctx as *const _ as _, - ); - Self { - inner, - not_send: PhantomData, - ctx, + pub(crate) struct WasmContext { + promise: Promise, + closure: UninitCell>, + } + + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender(&'static WasmContext); + + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() }); } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { - unsafe { - let executor = &self.inner; - self.ctx.closure.write(Closure::new(move |_| { - executor.poll(); - })); - init(self.inner.spawner()); + impl WasmContext { + pub fn new() -> Self { + Self { + promise: Promise::resolve(&JsValue::undefined()), + closure: UninitCell::uninit(), + } + } + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let ctx = &*Box::leak(Box::new(WasmContext::new())); + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), + not_send: PhantomData, + ctx, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { + unsafe { + let executor = &self.inner; + self.ctx.closure.write(Closure::new(move |_| { + executor.poll(); + })); + init(self.inner.spawner()); + } } } } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 4ee0d9f78..61ea92c16 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,73 +1,84 @@ -use core::marker::PhantomData; -use core::ptr; -use core::sync::atomic::{AtomicBool, Ordering}; +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); -use super::{raw, Spawner}; +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::marker::PhantomData; + use core::sync::atomic::{AtomicBool, Ordering}; -/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa -/// -static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + use crate::raw::{Pender, PenderInner}; + use crate::{raw, Spawner}; -/// Xtensa Executor -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} + #[derive(Copy, Clone)] + pub(crate) struct ThreadPender; -impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - // use Signal_Work_Thread_Mode as substitute for local interrupt register - inner: raw::Executor::new( - |_| { - SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); - }, - ptr::null_mut(), - ), - not_send: PhantomData, + impl ThreadPender { + #[allow(unused)] + pub(crate) fn pend(self) { + SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); } } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa + static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. - // if there is work to do, loop back to polling - // TODO can we relax this? - critical_section::with(|_| { - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here - } - }); + /// Xtensa Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + // TODO can we relax this? + critical_section::with(|_| { + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here + } + }); + } } } } diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 8707995b4..3ce687eb6 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -1,5 +1,5 @@ -#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(all(feature = "nightly", target_arch = "xtensa"), feature(asm_experimental_arch))] +#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)] +#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] @@ -10,41 +10,35 @@ pub(crate) mod fmt; #[cfg(feature = "nightly")] 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"] - mod arch; - pub use arch::*; - } - else if #[cfg(feature="wasm")] { - #[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; - } +macro_rules! check_at_most_one { + (@amo [$($feats:literal)*] [] [$($res:tt)*]) => { + #[cfg(any($($res)*))] + compile_error!(concat!("At most one of these features can be enabled at the same time:", $(" `", $feats, "`",)*)); + }; + (@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => { + check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]); + }; + ($($f:literal),*$(,)?) => { + check_at_most_one!(@amo [$($f)*] [$($f)*] []); + }; } +check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "arch-wasm",); + +#[cfg(feature = "_arch")] +#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")] +#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")] +#[cfg_attr(feature = "arch-xtensa", path = "arch/xtensa.rs")] +#[cfg_attr(feature = "arch-std", path = "arch/std.rs")] +#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")] +mod arch; + +#[cfg(feature = "_arch")] +pub use arch::*; + +pub mod raw; + +mod spawner; +pub use spawner::*; /// Implementation details for embassy macros. /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. @@ -72,8 +66,3 @@ pub mod _export { ($($tt:tt)*) => {}; } } - -pub mod raw; - -mod spawner; -pub use spawner::*; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 72c367c33..f6c66da5a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -19,7 +19,6 @@ use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; -use core::sync::atomic::AtomicPtr; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, Ordering}; @@ -290,10 +289,60 @@ impl TaskPool { } } +#[derive(Clone, Copy)] +pub(crate) enum PenderInner { + #[cfg(feature = "executor-thread")] + Thread(crate::arch::ThreadPender), + #[cfg(feature = "executor-interrupt")] + Interrupt(crate::arch::InterruptPender), + #[cfg(feature = "pender-callback")] + Callback { func: fn(*mut ()), context: *mut () }, +} + +unsafe impl Send for PenderInner {} +unsafe impl Sync for PenderInner {} + +/// Platform/architecture-specific action executed when an executor has pending work. +/// +/// When a task within an executor is woken, the `Pender` is called. This does a +/// platform/architecture-specific action to signal there is pending work in the executor. +/// When this happens, you must arrange for [`Executor::poll`] to be called. +/// +/// You can think of it as a waker, but for the whole executor. +pub struct Pender(pub(crate) PenderInner); + +impl Pender { + /// Create a `Pender` that will call an arbitrary function pointer. + /// + /// # Arguments + /// + /// - `func`: The function pointer to call. + /// - `context`: Opaque context pointer, that will be passed to the function pointer. + #[cfg(feature = "pender-callback")] + pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { + Self(PenderInner::Callback { + func, + context: context.into(), + }) + } +} + +impl Pender { + pub(crate) fn pend(&self) { + match self.0 { + #[cfg(feature = "executor-thread")] + PenderInner::Thread(x) => x.pend(), + #[cfg(feature = "executor-interrupt")] + PenderInner::Interrupt(x) => x.pend(), + #[cfg(feature = "pender-callback")] + PenderInner::Callback { func, context } => func(context), + } + } +} + pub(crate) struct SyncExecutor { run_queue: RunQueue, - signal_fn: fn(*mut ()), - signal_ctx: AtomicPtr<()>, + pender: Pender, #[cfg(feature = "integrated-timers")] pub(crate) timer_queue: timer_queue::TimerQueue, @@ -302,16 +351,13 @@ pub(crate) struct SyncExecutor { } impl SyncExecutor { - pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { + pub(crate) fn new(pender: Pender) -> Self { #[cfg(feature = "integrated-timers")] let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; - #[cfg(feature = "integrated-timers")] - driver::set_alarm_callback(alarm, signal_fn, signal_ctx); Self { run_queue: RunQueue::new(), - signal_fn, - signal_ctx: AtomicPtr::new(signal_ctx), + pender, #[cfg(feature = "integrated-timers")] timer_queue: timer_queue::TimerQueue::new(), @@ -332,10 +378,16 @@ impl SyncExecutor { trace::task_ready_begin(task.as_ptr() as u32); if self.run_queue.enqueue(cs, task) { - (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed)) + self.pender.pend(); } } + #[cfg(feature = "integrated-timers")] + fn alarm_callback(ctx: *mut ()) { + let this: &Self = unsafe { &*(ctx as *const Self) }; + this.pender.pend(); + } + pub(super) unsafe fn spawn(&'static self, task: TaskRef) { task.header().executor.set(Some(self)); @@ -351,6 +403,9 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { + #[cfg(feature = "integrated-timers")] + driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); + #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] @@ -417,14 +472,14 @@ impl SyncExecutor { /// /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks /// that "want to run"). -/// - You must supply a `signal_fn`. The executor will call it to notify you it has work +/// - You must supply a [`Pender`]. The executor will call it to notify you it has work /// to do. You must arrange for `poll()` to be called as soon as possible. /// -/// `signal_fn` can be called from *any* context: any thread, any interrupt priority +/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority /// level, etc. It may be called synchronously from any `Executor` method call as well. /// You must deal with this correctly. /// -/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates +/// In particular, you must NOT call `poll` directly from the pender callback, as this violates /// the requirement for `poll` to not be called reentrantly. #[repr(transparent)] pub struct Executor { @@ -437,15 +492,15 @@ impl Executor { pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { mem::transmute(inner) } + /// Create a new executor. /// - /// When the executor has work to do, it will call `signal_fn` with - /// `signal_ctx` as argument. + /// When the executor has work to do, it will call the [`Pender`]. /// - /// See [`Executor`] docs for details on `signal_fn`. - pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { + /// See [`Executor`] docs for details on `Pender`. + pub fn new(pender: Pender) -> Self { Self { - inner: SyncExecutor::new(signal_fn, signal_ctx), + inner: SyncExecutor::new(pender), _not_sync: PhantomData, } } @@ -468,16 +523,16 @@ impl Executor { /// This loops over all tasks that are queued to be polled (i.e. they're /// freshly spawned or they've been woken). Other tasks are not polled. /// - /// You must call `poll` after receiving a call to `signal_fn`. It is OK - /// to call `poll` even when not requested by `signal_fn`, but it wastes + /// You must call `poll` after receiving a call to the [`Pender`]. It is OK + /// to call `poll` even when not requested by the `Pender`, but it wastes /// energy. /// /// # Safety /// /// You must NOT call `poll` reentrantly on the same executor. /// - /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you - /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to + /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you + /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 888993255..e75c73cbd 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ 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-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 8d826790b..8de2d2ebd 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ 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-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } 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" } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index aa279fb76..083607de5 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 1ec0643a6..74f508515 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index a4eefe2a5..898b9a47e 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ 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-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 36eada29b..e142c8481 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 67efda748..f0e92e1ac 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 4b2e02dd2..87689e9a9 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index fecbfc51d..a6708bf51 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index d8c24dfad..7910b372a 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -17,7 +17,7 @@ log = [ [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index cc88d92c7..3ece24066 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -12,7 +12,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index e88ddf2f7..4134db46f 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ "defmt", ] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "defmt", "integrated-timers", diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 1e8870ed7..aea61eec5 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 8087df09a..ff08e378c 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "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", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 0095a680c..3b1d888f6 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 89d99b6d3..5c82c5579 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = "1.0" diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs index e0dc8c989..430a805fc 100644 --- a/examples/stm32f0/src/bin/multiprio.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 53f369b3a..387af783a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index afaf9a0c9..ffb232310 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 69ebef786..38f11201d 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 77df51ac7..5d010f799 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 7a7bab5bb..d967d8501 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 77df51ac7..5d010f799 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; use embassy_stm32::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index ea4cbd808..74e7bf53d 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index e7273c9fc..03bdbcea3 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 8a57a8ef0..4e4150350 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index a04134789..d0d6a9497 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 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", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 86933a629..413d5c18f 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -10,7 +10,7 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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 = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 6e3b2103c..cd9508d57 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32l151cb-a", "time-driver-any", "memory-x"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 644c90b1a..7c254eba3 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 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"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index f880328dc..1c662b9da 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 2b02eda92..ebef0a4f7 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index e27b4527c..ddf9729e6 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wb55cc", "time-driver-any", "exti"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 690481bbf..9fc7e0f4a 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index e0e799a34..430d0b4c7 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "wasm", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 2a4e8cf41..912749e5d 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 572a9ce88..eb447be35 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 08a775eae..17b640797 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -15,7 +15,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] 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-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } From bfebf7a43648e06b313234a2ddc7496eb526bc69 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 08:02:43 +0200 Subject: [PATCH 0789/1575] Fix formatting of sector erase log --- embassy-stm32/src/flash/common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 41c28c561..c48b2f2ea 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -117,7 +117,7 @@ unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { let mut address = start_address; while address < end_address { let sector = get_sector(address, regions); - trace!("Erasing sector: {}", sector); + trace!("Erasing sector: {:?}", sector); critical_section::with(|_| { family::clear_all_err(); From 608eb9b1fde5c2d6c5e8f4f5732379de22c2e6c1 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 30 Mar 2023 17:09:12 +0200 Subject: [PATCH 0790/1575] event queue mutexs can be noop because we are already !Sync in other places --- src/events.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/events.rs b/src/events.rs index b9c8cca6b..87f6c01a3 100644 --- a/src/events.rs +++ b/src/events.rs @@ -3,7 +3,7 @@ use core::num; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; #[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] @@ -284,9 +284,9 @@ pub enum Event { LAST = 190, } -pub type EventQueue = PubSubChannel; -pub type EventPublisher<'a> = Publisher<'a, CriticalSectionRawMutex, EventStatus, 2, 1, 1>; -pub type EventSubscriber<'a> = Subscriber<'a, CriticalSectionRawMutex, EventStatus, 2, 1, 1>; +pub type EventQueue = PubSubChannel; +pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, EventStatus, 2, 1, 1>; +pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, EventStatus, 2, 1, 1>; #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From 76ebebd0c5b58d230391be4d1989f68f1cc3d5b5 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Fri, 31 Mar 2023 14:18:39 +0200 Subject: [PATCH 0791/1575] parse data from device in-place --- src/runner.rs | 67 ++++------------------ src/structs.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 154 insertions(+), 62 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index deb45f9f4..f0f6fceeb 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -328,45 +328,23 @@ where let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; self.bus.wlan_read(buf, len).await; trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); - self.rx(&slice8_mut(buf)[..len as usize]); + self.rx(&mut slice8_mut(buf)[..len as usize]); } else { break; } } } - fn rx(&mut self, packet: &[u8]) { - if packet.len() < SdpcmHeader::SIZE { - warn!("packet too short, len={}", packet.len()); - return; - } - - let sdpcm_header = SdpcmHeader::from_bytes(packet[..SdpcmHeader::SIZE].try_into().unwrap()); - trace!("rx {:?}", sdpcm_header); - if sdpcm_header.len != !sdpcm_header.len_inv { - warn!("len inv mismatch"); - return; - } - if sdpcm_header.len as usize != packet.len() { - // TODO: is this guaranteed?? - warn!("len from header doesn't match len from spi"); - return; - } + fn rx(&mut self, packet: &mut [u8]) { + let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return }; self.update_credit(&sdpcm_header); let channel = sdpcm_header.channel_and_flags & 0x0f; - let payload = &packet[sdpcm_header.header_length as _..]; - match channel { CHANNEL_TYPE_CONTROL => { - if payload.len() < CdcHeader::SIZE { - warn!("payload too short, len={}", payload.len()); - return; - } - - let cdc_header = CdcHeader::from_bytes(payload[..CdcHeader::SIZE].try_into().unwrap()); + let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; }; trace!(" {:?}", cdc_header); if cdc_header.id == self.ioctl_id { @@ -375,28 +353,21 @@ where panic!("IOCTL error {}", cdc_header.status as i32); } - let resp_len = cdc_header.len as usize; - let response = &payload[CdcHeader::SIZE..][..resp_len]; info!("IOCTL Response: {:02x}", Bytes(response)); self.ioctl_state.ioctl_done(response); } } CHANNEL_TYPE_EVENT => { - let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); - trace!(" {:?}", bcd_header); - - let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; - - if packet_start + EventPacket::SIZE > payload.len() { + let Some((_, bcd_packet)) = BcdHeader::parse(payload) else { warn!("BCD event, incomplete header"); return; - } - let bcd_packet = &payload[packet_start..]; - trace!(" {:02x}", Bytes(&bcd_packet[..(bcd_packet.len() as usize).min(36)])); + }; - let mut event_packet = EventPacket::from_bytes(&bcd_packet[..EventPacket::SIZE].try_into().unwrap()); - event_packet.byteswap(); + let Some((event_packet, evt_data)) = EventPacket::parse(bcd_packet) else { + warn!("BCD event, incomplete data"); + return; + }; const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h if event_packet.eth.ether_type != ETH_P_LINK_CTL { @@ -427,13 +398,7 @@ where return; } - if event_packet.msg.datalen as usize >= (bcd_packet.len() - EventMessage::SIZE) { - warn!("BCD event, incomplete data"); - return; - } - let evt_type = events::Event::from(event_packet.msg.event_type as u8); - let evt_data = &bcd_packet[EventMessage::SIZE..][..event_packet.msg.datalen as usize]; debug!( "=== EVENT {:?}: {:?} {:02x}", evt_type, @@ -449,16 +414,8 @@ where } } CHANNEL_TYPE_DATA => { - let bcd_header = BcdHeader::from_bytes(&payload[..BcdHeader::SIZE].try_into().unwrap()); - trace!(" {:?}", bcd_header); - - let packet_start = BcdHeader::SIZE + 4 * bcd_header.data_offset as usize; - if packet_start > payload.len() { - warn!("packet start out of range."); - return; - } - let packet = &payload[packet_start..]; - trace!("rx pkt {:02x}", Bytes(&packet[..(packet.len() as usize).min(48)])); + let Some((_, packet)) = BcdHeader::parse(payload) else { return }; + trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); match self.ch.try_rx_buf() { Some(buf) => { diff --git a/src/structs.rs b/src/structs.rs index e16808f30..6d5d31b06 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,4 +1,5 @@ use crate::events::Event; +use crate::fmt::Bytes; macro_rules! impl_bytes { ($t:ident) => { @@ -11,8 +12,28 @@ macro_rules! impl_bytes { } #[allow(unused)] - pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self { - unsafe { core::mem::transmute(*bytes) } + pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self { + let alignment = core::mem::align_of::(); + assert_eq!( + bytes.as_ptr().align_offset(alignment), + 0, + "{} is not aligned", + core::any::type_name::() + ); + unsafe { core::mem::transmute(bytes) } + } + + #[allow(unused)] + pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self { + let alignment = core::mem::align_of::(); + assert_eq!( + bytes.as_ptr().align_offset(alignment), + 0, + "{} is not aligned", + core::any::type_name::() + ); + + unsafe { core::mem::transmute(bytes) } } } }; @@ -67,9 +88,35 @@ pub struct SdpcmHeader { } impl_bytes!(SdpcmHeader); +impl SdpcmHeader { + pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { + let packet_len = packet.len(); + if packet_len < Self::SIZE { + warn!("packet too short, len={}", packet.len()); + return None; + } + let (sdpcm_header, sdpcm_packet) = packet.split_at_mut(Self::SIZE); + let sdpcm_header = Self::from_bytes_mut(sdpcm_header.try_into().unwrap()); + trace!("rx {:?}", sdpcm_header); + + if sdpcm_header.len != !sdpcm_header.len_inv { + warn!("len inv mismatch"); + return None; + } + + if sdpcm_header.len as usize != packet_len { + warn!("len from header doesn't match len from spi"); + return None; + } + + let sdpcm_packet = &mut sdpcm_packet[(sdpcm_header.header_length as usize - Self::SIZE)..]; + Some((sdpcm_header, sdpcm_packet)) + } +} + #[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C)] +// #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C, packed(2))] pub struct CdcHeader { pub cmd: u32, pub len: u32, @@ -79,6 +126,21 @@ pub struct CdcHeader { } impl_bytes!(CdcHeader); +impl CdcHeader { + pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { + if packet.len() < Self::SIZE { + warn!("payload too short, len={}", packet.len()); + return None; + } + + let (cdc_header, payload) = packet.split_at_mut(Self::SIZE); + let cdc_header = Self::from_bytes_mut(cdc_header.try_into().unwrap()); + + let payload = &mut payload[..cdc_header.len as usize]; + Some((cdc_header, payload)) + } +} + pub const BDC_VERSION: u8 = 2; pub const BDC_VERSION_SHIFT: u8 = 4; @@ -95,6 +157,25 @@ pub struct BcdHeader { } impl_bytes!(BcdHeader); +impl BcdHeader { + pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { + if packet.len() < Self::SIZE { + return None; + } + + let (bcd_header, bcd_packet) = packet.split_at_mut(Self::SIZE); + let bcd_header = Self::from_bytes_mut(bcd_header.try_into().unwrap()); + trace!(" {:?}", bcd_header); + + let packet_start = 4 * bcd_header.data_offset as usize; + + let bcd_packet = bcd_packet.get_mut(packet_start..)?; + trace!(" {:02x}", Bytes(&bcd_packet[..bcd_packet.len().min(36)])); + + Some((bcd_header, bcd_packet)) + } +} + #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] @@ -130,8 +211,8 @@ impl EventHeader { } #[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C)] +// #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C, packed(2))] pub struct EventMessage { /// version pub version: u16, @@ -158,6 +239,45 @@ pub struct EventMessage { } impl_bytes!(EventMessage); +#[cfg(feature = "defmt")] +impl defmt::Format for EventMessage { + fn format(&self, fmt: defmt::Formatter) { + let event_type = self.event_type; + let status = self.status; + let reason = self.reason; + let auth_type = self.auth_type; + let datalen = self.datalen; + + defmt::write!( + fmt, + "EventMessage {{ \ + version: {=u16}, \ + flags: {=u16}, \ + event_type: {=u32}, \ + status: {=u32}, \ + reason: {=u32}, \ + auth_type: {=u32}, \ + datalen: {=u32}, \ + addr: {=[u8; 6]:x}, \ + ifname: {=[u8; 16]:x}, \ + ifidx: {=u8}, \ + bsscfgidx: {=u8}, \ + }} ", + self.version, + self.flags, + event_type, + status, + reason, + auth_type, + datalen, + self.addr, + self.ifname, + self.ifidx, + self.bsscfgidx + ); + } +} + impl EventMessage { pub fn byteswap(&mut self) { self.version = self.version.to_be(); @@ -172,7 +292,7 @@ impl EventMessage { #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C)] +#[repr(C, packed(2))] pub struct EventPacket { pub eth: EthernetHeader, pub hdr: EventHeader, @@ -181,6 +301,21 @@ pub struct EventPacket { impl_bytes!(EventPacket); impl EventPacket { + pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { + if packet.len() < Self::SIZE { + return None; + } + + let (event_header, event_packet) = packet.split_at_mut(Self::SIZE); + let event_header = Self::from_bytes_mut(event_header.try_into().unwrap()); + // warn!("event_header {:x}", event_header as *const _); + event_header.byteswap(); + + let event_packet = event_packet.get_mut(..event_header.msg.datalen as usize)?; + + Some((event_header, event_packet)) + } + pub fn byteswap(&mut self) { self.eth.byteswap(); self.hdr.byteswap(); From b1e2195b49129bcccf42121a6f39248bab9e341f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 14:50:41 +0200 Subject: [PATCH 0792/1575] Remove FirmwareWriter FirmwareWriter currently has a "max-write-size" parameter, but this is a limitation that should be handled by chunking inside the NorFlash driver, and not "up here" in user code. In case that the driver (e.g. qspi driver) is unaware of any max-write limitations, one could simply add an intermediate NorFlash adapter providing the chunk'ing capability. --- embassy-boot/boot/src/firmware_updater.rs | 25 ++++------- embassy-boot/boot/src/firmware_writer.rs | 55 ----------------------- embassy-boot/boot/src/lib.rs | 8 ++-- 3 files changed, 12 insertions(+), 76 deletions(-) delete mode 100644 embassy-boot/boot/src/firmware_writer.rs diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index af1ba114a..dd22b2fa7 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -1,7 +1,7 @@ use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; -use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; /// Errors returned by FirmwareUpdater #[derive(Debug)] @@ -243,7 +243,6 @@ impl FirmwareUpdater { offset: usize, data: &[u8], dfu_flash: &mut F, - block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); @@ -251,25 +250,23 @@ impl FirmwareUpdater { .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) .await?; - FirmwareWriter(self.dfu) - .write_block(offset, data, dfu_flash, block_size) - .await?; + self.dfu.write(dfu_flash, offset as u32, data).await?; Ok(()) } /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. + /// returning its `Partition`. /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. pub async fn prepare_update( &mut self, dfu_flash: &mut F, - ) -> Result { + ) -> Result { self.dfu.wipe(dfu_flash).await?; - Ok(FirmwareWriter(self.dfu)) + Ok(self.dfu) } // @@ -443,29 +440,25 @@ impl FirmwareUpdater { offset: usize, data: &[u8], dfu_flash: &mut F, - block_size: usize, ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= F::ERASE_SIZE); self.dfu .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; - FirmwareWriter(self.dfu).write_block_blocking(offset, data, dfu_flash, block_size)?; + self.dfu.write_blocking(dfu_flash, offset as u32, data)?; Ok(()) } /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning a `FirmwareWriter`. + /// returning its `Partition`. /// /// Using this instead of `write_firmware_blocking` allows for an optimized /// API in exchange for added complexity. - pub fn prepare_update_blocking( - &mut self, - flash: &mut F, - ) -> Result { + pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { self.dfu.wipe_blocking(flash)?; - Ok(FirmwareWriter(self.dfu)) + Ok(self.dfu) } } diff --git a/embassy-boot/boot/src/firmware_writer.rs b/embassy-boot/boot/src/firmware_writer.rs deleted file mode 100644 index 46079e731..000000000 --- a/embassy-boot/boot/src/firmware_writer.rs +++ /dev/null @@ -1,55 +0,0 @@ -use embedded_storage::nor_flash::NorFlash; -use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; - -use crate::Partition; - -/// FirmwareWriter allows writing blocks to an already erased flash. -pub struct FirmwareWriter(pub(crate) Partition); - -impl FirmwareWriter { - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_block( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - let mut offset = offset as u32; - for chunk in data.chunks(block_size) { - self.0.write(flash, offset, chunk).await?; - offset += chunk.len() as u32; - } - - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_block_blocking( - &mut self, - offset: usize, - data: &[u8], - flash: &mut F, - block_size: usize, - ) -> Result<(), F::Error> { - let mut offset = offset as u32; - for chunk in data.chunks(block_size) { - self.0.write_blocking(flash, offset, chunk)?; - offset += chunk.len() as u32; - } - - Ok(()) - } -} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4c28d7aa4..428e7ca2b 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,12 +7,10 @@ mod fmt; mod boot_loader; mod firmware_updater; -mod firmware_writer; mod partition; pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; -pub use firmware_writer::FirmwareWriter; pub use partition::Partition; pub(crate) const BOOT_MAGIC: u8 = 0xD0; @@ -109,7 +107,7 @@ mod tests { let mut updater = FirmwareUpdater::new(DFU, STATE); let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap(); offset += chunk.len(); } block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); @@ -182,7 +180,7 @@ mod tests { let mut offset = 0; for chunk in update.chunks(2048) { - block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); offset += chunk.len(); } block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); @@ -235,7 +233,7 @@ mod tests { let mut offset = 0; for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); + block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); offset += chunk.len(); } block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); From 8aaffe82e71dfb2b3845c95bbf59ef4a34c7096c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 14:59:55 +0200 Subject: [PATCH 0793/1575] Add incremental hash to FirmwareUpdater This adds support for computing any hash over the update in the dtu area by providing a closure to the hash update function. --- embassy-boot/boot/src/firmware_updater.rs | 67 ++++++++++++++--------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index af1ba114a..90157036a 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -118,13 +118,13 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { let _read_size = _aligned.len(); assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len()); + assert!(_update_len <= self.dfu.len() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -136,11 +136,8 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + .await?; public_key .verify(&digest.finalize(), &signature) @@ -161,11 +158,8 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read(_state_and_dfu_flash, offset as u32, _aligned).await?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + .await?; let message = digest.finalize(); let r = public_key.verify(&message, &signature); @@ -182,6 +176,22 @@ impl FirmwareUpdater { self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await } + /// Iterate through the DFU and process all bytes with the provided closure. + pub async fn incremental_hash( + &mut self, + dfu_flash: &mut F, + update_len: u32, + aligned: &mut [u8], + mut update: impl FnMut(&[u8]), + ) -> Result<(), FirmwareUpdaterError> { + for offset in (0..update_len).step_by(aligned.len()) { + self.dfu.read(dfu_flash, offset, aligned).await?; + let len = core::cmp::min((update_len - offset) as usize, aligned.len()); + update(&aligned[..len]); + } + Ok(()) + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -317,14 +327,13 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; let _read_size = _aligned.len(); assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); + assert!(_update_len <= self.dfu.len() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -336,11 +345,7 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; public_key .verify(&digest.finalize(), &signature) @@ -361,11 +366,7 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut digest = Sha512::new(); - for offset in (0.._update_len).step_by(_aligned.len()) { - self.dfu.read_blocking(_state_and_dfu_flash, offset as u32, _aligned)?; - let len = core::cmp::min(_update_len - offset, _aligned.len()); - digest.update(&_aligned[..len]); - } + self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; let message = digest.finalize(); let r = public_key.verify(&message, &signature); @@ -382,6 +383,22 @@ impl FirmwareUpdater { self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) } + /// Iterate through the DFU and process all bytes with the provided closure. + pub fn incremental_hash_blocking( + &mut self, + dfu_flash: &mut F, + update_len: u32, + aligned: &mut [u8], + mut update: impl FnMut(&[u8]), + ) -> Result<(), FirmwareUpdaterError> { + for offset in (0..update_len).step_by(aligned.len()) { + self.dfu.read_blocking(dfu_flash, offset, aligned)?; + let len = core::cmp::min((update_len - offset) as usize, aligned.len()); + update(&aligned[..len]); + } + Ok(()) + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4c28d7aa4..6d0e2d8c2 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -308,7 +308,7 @@ mod tests { &mut flash, &public_key.to_bytes(), &signature.to_bytes(), - firmware_len, + firmware_len as u32, &mut aligned, )) .is_ok()); From 7c11d85e1ea0d01e5e1b4d6564d258663d66509b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 3 Apr 2023 15:33:20 +0200 Subject: [PATCH 0794/1575] Move MemFlash to separate module and add verify_erased_before_write verification --- embassy-boot/boot/src/lib.rs | 155 +++------------------ embassy-boot/boot/src/mem_flash.rs | 213 +++++++++++++++++++++++++++++ embassy-boot/boot/src/partition.rs | 18 +-- 3 files changed, 244 insertions(+), 142 deletions(-) create mode 100644 embassy-boot/boot/src/mem_flash.rs diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4c28d7aa4..a5795781f 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -8,6 +8,7 @@ mod fmt; mod boot_loader; mod firmware_updater; mod firmware_writer; +mod mem_flash; mod partition; pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; @@ -46,13 +47,10 @@ impl AsMut<[u8]> for AlignedBuffer { #[cfg(test)] mod tests { - use core::convert::Infallible; - - use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; - use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; use futures::executor::block_on; use super::*; + use crate::mem_flash::MemFlash; /* #[test] @@ -75,8 +73,8 @@ mod tests { const ACTIVE: Partition = Partition::new(4096, 61440); const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); - flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); + let mut flash = MemFlash::<131072, 4096, 4>::default(); + flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); let mut flash = SingleFlashConfig::new(&mut flash); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -95,14 +93,14 @@ mod tests { const STATE: Partition = Partition::new(0, 4096); const ACTIVE: Partition = Partition::new(4096, 61440); const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>([0xff; 131072]); + let mut flash = MemFlash::<131072, 4096, 4>::random().with_limited_erase_before_write_verification(4..); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; let mut aligned = [0; 4]; for i in ACTIVE.from..ACTIVE.to { - flash.0[i] = original[i - ACTIVE.from]; + flash.mem[i] = original[i - ACTIVE.from]; } let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -124,12 +122,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(flash.0[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i); } // Running again should cause a revert @@ -141,12 +139,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); + assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i); } // Last page is untouched for i in DFU.from..DFU.to - 4096 { - assert_eq!(flash.0[i], update[i - DFU.from], "Index {}", i); + assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i); } // Mark as booted @@ -166,16 +164,16 @@ mod tests { const ACTIVE: Partition = Partition::new(4096, 16384); const DFU: Partition = Partition::new(0, 16384); - let mut active = MemFlash::<16384, 4096, 8>([0xff; 16384]); - let mut dfu = MemFlash::<16384, 2048, 8>([0xff; 16384]); - let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); + let mut active = MemFlash::<16384, 4096, 8>::random(); + let mut dfu = MemFlash::<16384, 2048, 8>::random(); + let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); let mut aligned = [0; 4]; let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.0[i] = original[i - ACTIVE.from]; + active.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -203,12 +201,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -220,15 +218,15 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut aligned = [0; 4]; - let mut active = MemFlash::<16384, 2048, 4>([0xff; 16384]); - let mut dfu = MemFlash::<16384, 4096, 8>([0xff; 16384]); - let mut state = MemFlash::<4096, 128, 4>([0xff; 4096]); + let mut active = MemFlash::<16384, 2048, 4>::random(); + let mut dfu = MemFlash::<16384, 4096, 8>::random(); + let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.0[i] = original[i - ACTIVE.from]; + active.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -255,12 +253,12 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.0[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.0[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -313,113 +311,4 @@ mod tests { )) .is_ok()); } - - pub struct MemFlash(pub [u8; SIZE]); - - impl NorFlash - for MemFlash - { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let from = from as usize; - let to = to as usize; - assert!(from % ERASE_SIZE == 0); - assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); - for i in from..to { - self.0[i] = 0xFF; - } - Ok(()) - } - - fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { - assert!(data.len() % WRITE_SIZE == 0); - assert!(offset as usize % WRITE_SIZE == 0); - assert!(offset as usize + data.len() <= SIZE); - - self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); - - Ok(()) - } - } - - impl ErrorType - for MemFlash - { - type Error = Infallible; - } - - impl ReadNorFlash - for MemFlash - { - const READ_SIZE: usize = 1; - - fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { - let len = buf.len(); - buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); - Ok(()) - } - - fn capacity(&self) -> usize { - SIZE - } - } - - impl super::Flash - for MemFlash - { - const BLOCK_SIZE: usize = ERASE_SIZE; - const ERASE_VALUE: u8 = 0xFF; - } - - impl AsyncReadNorFlash - for MemFlash - { - const READ_SIZE: usize = 1; - - async fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { - let len = buf.len(); - buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); - Ok(()) - } - - fn capacity(&self) -> usize { - SIZE - } - } - - impl AsyncNorFlash - for MemFlash - { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let from = from as usize; - let to = to as usize; - assert!(from % ERASE_SIZE == 0); - assert!(to % ERASE_SIZE == 0); - for i in from..to { - self.0[i] = 0xFF; - } - Ok(()) - } - - async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { - info!("Writing {} bytes to 0x{:x}", data.len(), offset); - assert!(data.len() % WRITE_SIZE == 0); - assert!(offset as usize % WRITE_SIZE == 0); - assert!( - offset as usize + data.len() <= SIZE, - "OFFSET: {}, LEN: {}, FLASH SIZE: {}", - offset, - data.len(), - SIZE - ); - - self.0[offset as usize..offset as usize + data.len()].copy_from_slice(data); - - Ok(()) - } - } } diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs new file mode 100644 index 000000000..e87ccd37a --- /dev/null +++ b/embassy-boot/boot/src/mem_flash.rs @@ -0,0 +1,213 @@ +#![allow(unused)] + +use core::ops::{Bound, Range, RangeBounds}; + +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +use crate::Flash; + +pub struct MemFlash { + pub mem: [u8; SIZE], + pub allow_same_write: bool, + pub verify_erased_before_write: Range, + pub pending_write_successes: Option, +} + +#[derive(Debug)] +pub struct MemFlashError; + +impl MemFlash { + pub const fn new(fill: u8) -> Self { + Self { + mem: [fill; SIZE], + allow_same_write: false, + verify_erased_before_write: 0..SIZE, + pending_write_successes: None, + } + } + + #[cfg(test)] + pub fn random() -> Self { + let mut mem = [0; SIZE]; + for byte in mem.iter_mut() { + *byte = rand::random::(); + } + Self { + mem, + allow_same_write: false, + verify_erased_before_write: 0..SIZE, + pending_write_successes: None, + } + } + + #[must_use] + pub fn allow_same_write(self, allow: bool) -> Self { + Self { + allow_same_write: allow, + ..self + } + } + + #[must_use] + pub fn with_limited_erase_before_write_verification>(self, verified_range: R) -> Self { + let start = match verified_range.start_bound() { + Bound::Included(start) => *start, + Bound::Excluded(start) => *start + 1, + Bound::Unbounded => 0, + }; + let end = match verified_range.end_bound() { + Bound::Included(end) => *end - 1, + Bound::Excluded(end) => *end, + Bound::Unbounded => self.mem.len(), + }; + Self { + verify_erased_before_write: start..end, + ..self + } + } +} + +impl Default + for MemFlash +{ + fn default() -> Self { + Self::new(0xFF) + } +} + +impl Flash + for MemFlash +{ + const BLOCK_SIZE: usize = ERASE_SIZE; + const ERASE_VALUE: u8 = 0xFF; +} + +impl ErrorType + for MemFlash +{ + type Error = MemFlashError; +} + +impl NorFlashError for MemFlashError { + fn kind(&self) -> NorFlashErrorKind { + NorFlashErrorKind::Other + } +} + +impl ReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + let len = bytes.len(); + bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); + Ok(()) + } + + fn capacity(&self) -> usize { + SIZE + } +} + +impl NorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + let from = from as usize; + let to = to as usize; + assert!(from % ERASE_SIZE == 0); + assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); + for i in from..to { + self.mem[i] = 0xFF; + } + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + let offset = offset as usize; + assert!(bytes.len() % WRITE_SIZE == 0); + assert!(offset % WRITE_SIZE == 0); + assert!(offset + bytes.len() <= SIZE); + + if let Some(pending_successes) = self.pending_write_successes { + if pending_successes > 0 { + self.pending_write_successes = Some(pending_successes - 1); + } else { + return Err(MemFlashError); + } + } + + for ((offset, mem_byte), new_byte) in self + .mem + .iter_mut() + .enumerate() + .skip(offset) + .take(bytes.len()) + .zip(bytes) + { + if self.allow_same_write && mem_byte == new_byte { + // Write does not change the flash memory which is allowed + } else { + if self.verify_erased_before_write.contains(&offset) { + assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); + } + *mem_byte &= *new_byte; + } + } + + Ok(()) + } +} + +impl AsyncReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + ::read(self, offset, bytes) + } + + fn capacity(&self) -> usize { + ::capacity(self) + } +} + +impl AsyncNorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + ::erase(self, from, to) + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + ::write(self, offset, bytes) + } +} + +#[cfg(test)] +mod tests { + use core::ops::Range; + + use embedded_storage::nor_flash::NorFlash; + + use super::MemFlash; + + #[test] + fn writes_only_flip_bits_from_1_to_0() { + let mut flash = MemFlash::<16, 16, 1>::default().with_limited_erase_before_write_verification(0..0); + + flash.write(0, &[0x55]).unwrap(); + flash.write(0, &[0xAA]).unwrap(); + + assert_eq!(0x00, flash.mem[0]); + } +} diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 3ccd4dd76..1157e8cd8 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -105,45 +105,45 @@ impl Partition { #[cfg(test)] mod tests { - use crate::tests::MemFlash; + use crate::mem_flash::MemFlash; use crate::Partition; #[test] fn can_erase() { - let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let mut flash = MemFlash::<1024, 64, 4>::new(0x00); let partition = Partition::new(256, 512); partition.erase_blocking(&mut flash, 64, 192).unwrap(); - for (index, byte) in flash.0.iter().copied().enumerate().take(256 + 64) { + for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) { assert_eq!(0x00, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64).take(128) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) { assert_eq!(0xFF, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(256 + 64 + 128) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) { assert_eq!(0x00, byte, "Index {}", index); } } #[test] fn can_wipe() { - let mut flash = MemFlash::<1024, 64, 4>([0x00; 1024]); + let mut flash = MemFlash::<1024, 64, 4>::new(0x00); let partition = Partition::new(256, 512); partition.wipe_blocking(&mut flash).unwrap(); - for (index, byte) in flash.0.iter().copied().enumerate().take(256) { + for (index, byte) in flash.mem.iter().copied().enumerate().take(256) { assert_eq!(0x00, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(256).take(256) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) { assert_eq!(0xFF, byte, "Index {}", index); } - for (index, byte) in flash.0.iter().copied().enumerate().skip(512) { + for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) { assert_eq!(0x00, byte, "Index {}", index); } } From 4ce1c5f27dbb06641abddd2b10aebae1511a894b Mon Sep 17 00:00:00 2001 From: Mathieu Dupont Date: Mon, 3 Apr 2023 16:41:25 +0200 Subject: [PATCH 0795/1575] Add MCO support for L4 and F4 families --- embassy-stm32/build.rs | 17 +++- embassy-stm32/src/rcc/f4.rs | 165 ++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/l4.rs | 133 +++++++++++++++++++++++++ examples/stm32f4/src/bin/mco.rs | 31 ++++++ examples/stm32l4/src/bin/mco.rs | 27 ++++++ 5 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 examples/stm32f4/src/bin/mco.rs create mode 100644 examples/stm32l4/src/bin/mco.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 3780c5a40..481fec677 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -50,10 +50,13 @@ fn main() { // We *shouldn't* have singletons for these, but the HAL currently requires // singletons, for using with RccPeripheral to enable/disable clocks to them. "rcc" => { - if r.version.starts_with("h7") { + if r.version.starts_with("h7") || r.version.starts_with("f4") { singletons.push("MCO1".to_string()); singletons.push("MCO2".to_string()); } + if r.version.starts_with("l4") { + singletons.push("MCO".to_string()); + } singletons.push(p.name.to_string()); } //"dbgmcu" => {} @@ -258,6 +261,7 @@ fn main() { (("i2c", "SCL"), quote!(crate::i2c::SclPin)), (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)), (("rcc", "MCO_2"), quote!(crate::rcc::McoPin)), + (("rcc", "MCO"), quote!(crate::rcc::McoPin)), (("dcmi", "D0"), quote!(crate::dcmi::D0Pin)), (("dcmi", "D1"), quote!(crate::dcmi::D1Pin)), (("dcmi", "D2"), quote!(crate::dcmi::D2Pin)), @@ -447,13 +451,22 @@ fn main() { // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now - if regs.version.starts_with("h7") { + if regs.version.starts_with("h7") || regs.version.starts_with("f4") { peri = format_ident!("{}", pin.signal.replace("_", "")); } else { continue; } } + if pin.signal == "MCO" { + // Supported in H7 only for now + if regs.version.starts_with("l4") { + peri = format_ident!("MCO"); + } else { + continue; + } + } + g.extend(quote! { pin_trait_impl!(#tr, #peri, #pin_name, #af); }) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 200bcce9c..d0e0d585a 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -1,8 +1,16 @@ +use core::marker::PhantomData; + +use embassy_hal_common::into_ref; +use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre}; + use super::sealed::RccPeripheral; +use crate::gpio::sealed::AFType; +use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; +use crate::{peripherals, Peripheral}; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); @@ -96,6 +104,163 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48 } } +pub enum McoClock { + DIV1, + DIV2, + DIV3, + DIV4, + DIV5, +} + +impl McoClock { + fn into_raw(&self) -> Mcopre { + match self { + McoClock::DIV1 => Mcopre::DIV1, + McoClock::DIV2 => Mcopre::DIV2, + McoClock::DIV3 => Mcopre::DIV3, + McoClock::DIV4 => Mcopre::DIV4, + McoClock::DIV5 => Mcopre::DIV5, + } + } +} + +#[derive(Copy, Clone)] +pub enum Mco1Source { + Hsi, + Lse, + Hse, + Pll, +} + +impl Default for Mco1Source { + fn default() -> Self { + Self::Hsi + } +} + +pub trait McoSource { + type Raw; + + fn into_raw(&self) -> Self::Raw; +} + +impl McoSource for Mco1Source { + type Raw = Mco1; + fn into_raw(&self) -> Self::Raw { + match self { + Mco1Source::Hsi => Mco1::HSI, + Mco1Source::Lse => Mco1::LSE, + Mco1Source::Hse => Mco1::HSE, + Mco1Source::Pll => Mco1::PLL, + } + } +} + +#[derive(Copy, Clone)] +pub enum Mco2Source { + SysClk, + Plli2s, + Hse, + Pll, +} + +impl Default for Mco2Source { + fn default() -> Self { + Self::SysClk + } +} + +impl McoSource for Mco2Source { + type Raw = Mco2; + fn into_raw(&self) -> Self::Raw { + match self { + Mco2Source::SysClk => Mco2::SYSCLK, + Mco2Source::Plli2s => Mco2::PLLI2S, + Mco2Source::Hse => Mco2::HSE, + Mco2Source::Pll => Mco2::PLL, + } + } +} + +pub(crate) mod sealed { + use stm32_metapac::rcc::vals::Mcopre; + pub trait McoInstance { + type Source; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre); + } +} + +pub trait McoInstance: sealed::McoInstance + 'static {} + +pin_trait!(McoPin, McoInstance); + +impl sealed::McoInstance for peripherals::MCO1 { + type Source = Mco1; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) { + RCC.cfgr().modify(|w| { + w.set_mco1(source); + w.set_mco1pre(prescaler); + }); + match source { + Mco1::PLL => { + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + } + Mco1::HSI => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + } + _ => {} + } + } +} +impl McoInstance for peripherals::MCO1 {} + +impl sealed::McoInstance for peripherals::MCO2 { + type Source = Mco2; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) { + RCC.cfgr().modify(|w| { + w.set_mco2(source); + w.set_mco2pre(prescaler); + }); + match source { + Mco2::PLL => { + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + } + Mco2::PLLI2S => { + RCC.cr().modify(|w| w.set_plli2son(true)); + while !RCC.cr().read().plli2srdy() {} + } + _ => {} + } + } +} +impl McoInstance for peripherals::MCO2 {} + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + source: impl McoSource, + prescaler: McoClock, + ) -> Self { + into_ref!(pin); + + critical_section::with(|_| unsafe { + T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_speed(Speed::VeryHigh); + }); + + Self { phantom: PhantomData } + } +} + unsafe fn flash_setup(sysclk: u32) { use crate::pac::flash::vals::Latency; diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index e650490fe..c1bf7d0cd 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,7 +1,15 @@ +use core::marker::PhantomData; + +use embassy_hal_common::into_ref; +use stm32_metapac::rcc::vals::{Mcopre, Mcosel}; + +use crate::gpio::sealed::AFType; +use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; +use crate::{peripherals, Peripheral}; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); @@ -298,6 +306,131 @@ impl Default for Config { } } +pub enum McoClock { + DIV1, + DIV2, + DIV4, + DIV8, + DIV16, +} + +impl McoClock { + fn into_raw(&self) -> Mcopre { + match self { + McoClock::DIV1 => Mcopre::DIV1, + McoClock::DIV2 => Mcopre::DIV2, + McoClock::DIV4 => Mcopre::DIV4, + McoClock::DIV8 => Mcopre::DIV8, + McoClock::DIV16 => Mcopre::DIV16, + } + } +} + +#[derive(Copy, Clone)] +pub enum Mco1Source { + Disabled, + Lse, + Lsi, + Hse, + Hsi16, + PllClk, + SysClk, + Msi, + #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + Hsi48, +} + +impl Default for Mco1Source { + fn default() -> Self { + Self::Hsi16 + } +} + +pub trait McoSource { + type Raw; + + fn into_raw(&self) -> Self::Raw; +} + +impl McoSource for Mco1Source { + type Raw = Mcosel; + fn into_raw(&self) -> Self::Raw { + match self { + Mco1Source::Disabled => Mcosel::NOCLOCK, + Mco1Source::Lse => Mcosel::LSE, + Mco1Source::Lsi => Mcosel::LSI, + Mco1Source::Hse => Mcosel::HSE, + Mco1Source::Hsi16 => Mcosel::HSI16, + Mco1Source::PllClk => Mcosel::PLL, + Mco1Source::SysClk => Mcosel::SYSCLK, + Mco1Source::Msi => Mcosel::MSI, + #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + Mco1Source::Hsi48 => Mcosel::HSI48, + } + } +} + +pub(crate) mod sealed { + use stm32_metapac::rcc::vals::Mcopre; + pub trait McoInstance { + type Source; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre); + } +} + +pub trait McoInstance: sealed::McoInstance + 'static {} + +pin_trait!(McoPin, McoInstance); + +impl sealed::McoInstance for peripherals::MCO { + type Source = Mcosel; + + unsafe fn apply_clock_settings(source: Self::Source, prescaler: Mcopre) { + RCC.cfgr().modify(|w| { + w.set_mcosel(source); + w.set_mcopre(prescaler); + }); + + match source { + Mcosel::HSI16 => { + RCC.cr().modify(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + } + #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] + Mcosel::HSI48 => { + RCC.crrcr().modify(|w| w.set_hsi48on(true)); + while !RCC.crrcr().read().hsi48rdy() {} + } + _ => {} + } + } +} + +impl McoInstance for peripherals::MCO {} + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + source: impl McoSource, + prescaler: McoClock, + ) -> Self { + into_ref!(pin); + + critical_section::with(|_| unsafe { + T::apply_clock_settings(source.into_raw(), prescaler.into_raw()); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_speed(Speed::VeryHigh); + }); + + Self { phantom: PhantomData } + } +} + pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw) = match config.mux { ClockSrc::MSI(range) => { diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs new file mode 100644 index 000000000..5d780f94d --- /dev/null +++ b/examples/stm32f4/src/bin/mco.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoClock}; +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 _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1); + let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4); + let mut led = Output::new(p.PB7, 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/stm32l4/src/bin/mco.rs b/examples/stm32l4/src/bin/mco.rs new file mode 100644 index 000000000..dea0c66e0 --- /dev/null +++ b/examples/stm32l4/src/bin/mco.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; +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 _mco = Mco::new(p.MCO, p.PA8, Mco1Source::Hsi16, McoClock::DIV1); + + let mut led = Output::new(p.PB14, Level::High, Speed::Low); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} From 932b80ca8a5d49bcd05d523fe7f7320e2b960e85 Mon Sep 17 00:00:00 2001 From: Mathieu Dupont Date: Mon, 3 Apr 2023 16:52:57 +0200 Subject: [PATCH 0796/1575] run fmt --- examples/stm32f4/src/bin/mco.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs index 5d780f94d..2b9ceebc3 100644 --- a/examples/stm32f4/src/bin/mco.rs +++ b/examples/stm32f4/src/bin/mco.rs @@ -14,7 +14,6 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::DIV1); let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::Pll, McoClock::DIV4); let mut led = Output::new(p.PB7, Level::High, Speed::Low); From 1349dabe1aae491d83b83d27a80da7216333bb52 Mon Sep 17 00:00:00 2001 From: Mathieu Dupont Date: Mon, 3 Apr 2023 17:55:05 +0200 Subject: [PATCH 0797/1575] add compilation time exclusion for stm32f410 --- embassy-stm32/src/rcc/f4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index d0e0d585a..2a17eb9b0 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -228,6 +228,7 @@ impl sealed::McoInstance for peripherals::MCO2 { RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} } + #[cfg(not(stm32f410))] Mco2::PLLI2S => { RCC.cr().modify(|w| w.set_plli2son(true)); while !RCC.cr().read().plli2srdy() {} From ae26a080260d81cf8dec17de538e101010af13c0 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 31 Mar 2023 14:52:35 +0200 Subject: [PATCH 0798/1575] Add HIL test for timer on nrf --- tests/nrf/src/bin/timer.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/nrf/src/bin/timer.rs diff --git a/tests/nrf/src/bin/timer.rs b/tests/nrf/src/bin/timer.rs new file mode 100644 index 000000000..9b9b5fb28 --- /dev/null +++ b/tests/nrf/src/bin/timer.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert, info}; +use embassy_executor::Spawner; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let _p = embassy_nrf::init(Default::default()); + info!("Hello World!"); + + let start = Instant::now(); + Timer::after(Duration::from_millis(100)).await; + let end = Instant::now(); + let ms = (end - start).as_millis(); + info!("slept for {} ms", ms); + assert!(ms >= 99); + assert!(ms < 110); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From ef361d2e88d7991f563781ecb34c75d4ac0dfa7a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Apr 2023 23:15:19 +0200 Subject: [PATCH 0799/1575] Update Rust nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index da75fa53a..22abacdea 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-2023-02-07" +channel = "nightly-2023-04-02" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From df3a1e1b9d337c17e08faf41c6e3c50c35eb9a6c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 07:18:29 +0200 Subject: [PATCH 0800/1575] Avoid write to not-erased magic This introduces an additional marker to the state partition right after the magic which indicates whether the current progress is valid or not. Validation in tests that we never write without an erase is added. There is currently a FIXME in the FirmwareUpdater. Let me know if we should take the erase value as a parameter. I opened a feature request in embedded-storage to get this value in the trait. Before this, the assumption about ERASE_VALUE=0xFF was the same. --- embassy-boot/boot/src/boot_loader.rs | 43 ++++++++++------ embassy-boot/boot/src/firmware_updater.rs | 34 +++++++++++-- embassy-boot/boot/src/lib.rs | 6 +-- embassy-boot/boot/src/mem_flash.rs | 61 +---------------------- 4 files changed, 63 insertions(+), 81 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index e2e361e3c..9d047f778 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -31,7 +31,7 @@ where } /// Extension of the embedded-storage flash type information with block size and erase value. -pub trait Flash: NorFlash + ReadNorFlash { +pub trait Flash: NorFlash { /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase /// size of the flash, but for external QSPI flash modules, this can be lower. const BLOCK_SIZE: usize; @@ -60,9 +60,11 @@ pub trait FlashConfig { /// different page sizes and flash write sizes. pub struct BootLoader { // Page with current state of bootloader. The state partition has the following format: - // | Range | Description | - // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - // | WRITE_SIZE - N | Progress index used while swapping or reverting | + // All ranges are in multiples of WRITE_SIZE bytes. + // | Range | Description | + // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | + // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | + // | 2..2 + N | Progress index used while swapping or reverting | state: Partition, // Location of the partition which will be booted from active: Partition, @@ -192,12 +194,17 @@ impl BootLoader { trace!("Reverting"); self.revert(p, magic, page)?; - // Overwrite magic and reset progress let state_flash = p.state(); + + // Invalidate progress magic.fill(!P::STATE::ERASE_VALUE); - self.state.write_blocking(state_flash, 0, magic)?; + self.state + .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?; + + // Clear magic and progress self.state.wipe_blocking(state_flash)?; + // Set magic magic.fill(BOOT_MAGIC); self.state.write_blocking(state_flash, 0, magic)?; } @@ -215,28 +222,34 @@ impl BootLoader { fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { let write_size = aligned.len(); - let max_index = ((self.state.len() - write_size) / write_size) - 1; + let max_index = ((self.state.len() - write_size) / write_size) - 2; aligned.fill(!P::STATE::ERASE_VALUE); let state_flash = config.state(); - for i in 0..max_index { + + self.state + .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?; + if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) { + // Progress is invalid + return Ok(max_index); + } + + for index in 0..max_index { self.state - .read_blocking(state_flash, (write_size + i * write_size) as u32, aligned)?; + .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { - return Ok(i); + return Ok(index); } } Ok(max_index) } - fn update_progress(&mut self, idx: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let write_size = magic.len(); - + fn update_progress(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { let aligned = magic; aligned.fill(!P::STATE::ERASE_VALUE); self.state - .write_blocking(p.state(), (write_size + idx * write_size) as u32, aligned)?; + .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; Ok(()) } @@ -360,7 +373,7 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s assert_eq!(active.len() % page_size, 0); assert_eq!(dfu.len() % page_size, 0); assert!(dfu.len() - active.len() >= page_size); - assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); + assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size); } /// A flash wrapper implementing the Flash and embedded_storage traits. diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index af1ba114a..fe3c0452f 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -220,11 +220,24 @@ impl FirmwareUpdater { self.state.read(state_flash, 0, aligned).await?; if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); + // Read progress validity + self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; - self.state.write(state_flash, 0, aligned).await?; + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; + } + + // Clear magic and progress self.state.wipe(state_flash).await?; + // Set magic aligned.fill(magic); self.state.write(state_flash, 0, aligned).await?; } @@ -420,11 +433,24 @@ impl FirmwareUpdater { self.state.read_blocking(state_flash, 0, aligned)?; if aligned.iter().any(|&b| b != magic) { - aligned.fill(0); + // Read progress validity + self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; - self.state.write_blocking(state_flash, 0, aligned)?; + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; + } + + // Clear magic and progress self.state.wipe_blocking(state_flash)?; + // Set magic aligned.fill(magic); self.state.write_blocking(state_flash, 0, aligned)?; } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index a5795781f..597ce3fa3 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -93,7 +93,7 @@ mod tests { const STATE: Partition = Partition::new(0, 4096); const ACTIVE: Partition = Partition::new(4096, 61440); const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>::random().with_limited_erase_before_write_verification(4..); + let mut flash = MemFlash::<131072, 4096, 4>::random(); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; @@ -166,7 +166,7 @@ mod tests { let mut active = MemFlash::<16384, 4096, 8>::random(); let mut dfu = MemFlash::<16384, 2048, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); + let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; @@ -220,7 +220,7 @@ mod tests { let mut aligned = [0; 4]; let mut active = MemFlash::<16384, 2048, 4>::random(); let mut dfu = MemFlash::<16384, 4096, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random().with_limited_erase_before_write_verification(2048 + 4..); + let mut state = MemFlash::<4096, 128, 4>::random(); let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index e87ccd37a..828aad9d9 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -9,8 +9,6 @@ use crate::Flash; pub struct MemFlash { pub mem: [u8; SIZE], - pub allow_same_write: bool, - pub verify_erased_before_write: Range, pub pending_write_successes: Option, } @@ -21,8 +19,6 @@ impl MemFla pub const fn new(fill: u8) -> Self { Self { mem: [fill; SIZE], - allow_same_write: false, - verify_erased_before_write: 0..SIZE, pending_write_successes: None, } } @@ -35,37 +31,9 @@ impl MemFla } Self { mem, - allow_same_write: false, - verify_erased_before_write: 0..SIZE, pending_write_successes: None, } } - - #[must_use] - pub fn allow_same_write(self, allow: bool) -> Self { - Self { - allow_same_write: allow, - ..self - } - } - - #[must_use] - pub fn with_limited_erase_before_write_verification>(self, verified_range: R) -> Self { - let start = match verified_range.start_bound() { - Bound::Included(start) => *start, - Bound::Excluded(start) => *start + 1, - Bound::Unbounded => 0, - }; - let end = match verified_range.end_bound() { - Bound::Included(end) => *end - 1, - Bound::Excluded(end) => *end, - Bound::Unbounded => self.mem.len(), - }; - Self { - verify_erased_before_write: start..end, - ..self - } - } } impl Default @@ -150,14 +118,8 @@ impl NorFla .take(bytes.len()) .zip(bytes) { - if self.allow_same_write && mem_byte == new_byte { - // Write does not change the flash memory which is allowed - } else { - if self.verify_erased_before_write.contains(&offset) { - assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); - } - *mem_byte &= *new_byte; - } + assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); + *mem_byte = *new_byte; } Ok(()) @@ -192,22 +154,3 @@ impl AsyncN ::write(self, offset, bytes) } } - -#[cfg(test)] -mod tests { - use core::ops::Range; - - use embedded_storage::nor_flash::NorFlash; - - use super::MemFlash; - - #[test] - fn writes_only_flip_bits_from_1_to_0() { - let mut flash = MemFlash::<16, 16, 1>::default().with_limited_erase_before_write_verification(0..0); - - flash.write(0, &[0x55]).unwrap(); - flash.write(0, &[0xAA]).unwrap(); - - assert_eq!(0x00, flash.mem[0]); - } -} From 7c6936a2e398e43ea3dc89736dc385402822933f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 12:24:30 +0200 Subject: [PATCH 0801/1575] Let hash functions take a digest::Digest trait ... and add adapters for current Sha512 implementations that does not inplement the Digest trait --- embassy-boot/boot/Cargo.toml | 4 +- .../boot/src/digest_adapters/ed25519_dalek.rs | 30 +++++ embassy-boot/boot/src/digest_adapters/mod.rs | 5 + .../boot/src/digest_adapters/salty.rs | 29 +++++ embassy-boot/boot/src/firmware_updater.rs | 108 ++++++++++++------ embassy-boot/boot/src/lib.rs | 1 + 6 files changed, 140 insertions(+), 37 deletions(-) create mode 100644 embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs create mode 100644 embassy-boot/boot/src/digest_adapters/mod.rs create mode 100644 embassy-boot/boot/src/digest_adapters/salty.rs diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 3312c2f9f..c4ebdaff2 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -24,6 +24,7 @@ features = ["defmt"] [dependencies] defmt = { version = "0.3", optional = true } +digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } @@ -37,6 +38,7 @@ log = "0.4" env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } +sha1 = "0.10.5" [dev-dependencies.ed25519-dalek] default_features = false @@ -47,4 +49,4 @@ ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] #Internal features -_verify = [] \ No newline at end of file +_verify = [] diff --git a/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs new file mode 100644 index 000000000..a184d1c51 --- /dev/null +++ b/embassy-boot/boot/src/digest_adapters/ed25519_dalek.rs @@ -0,0 +1,30 @@ +use digest::typenum::U64; +use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; +use ed25519_dalek::Digest as _; + +pub struct Sha512(ed25519_dalek::Sha512); + +impl Default for Sha512 { + fn default() -> Self { + Self(ed25519_dalek::Sha512::new()) + } +} + +impl Update for Sha512 { + fn update(&mut self, data: &[u8]) { + self.0.update(data) + } +} + +impl FixedOutput for Sha512 { + fn finalize_into(self, out: &mut digest::Output) { + let result = self.0.finalize(); + out.as_mut_slice().copy_from_slice(result.as_slice()) + } +} + +impl OutputSizeUser for Sha512 { + type OutputSize = U64; +} + +impl HashMarker for Sha512 {} diff --git a/embassy-boot/boot/src/digest_adapters/mod.rs b/embassy-boot/boot/src/digest_adapters/mod.rs new file mode 100644 index 000000000..9b4b4b60c --- /dev/null +++ b/embassy-boot/boot/src/digest_adapters/mod.rs @@ -0,0 +1,5 @@ +#[cfg(feature = "ed25519-dalek")] +pub(crate) mod ed25519_dalek; + +#[cfg(feature = "ed25519-salty")] +pub(crate) mod salty; diff --git a/embassy-boot/boot/src/digest_adapters/salty.rs b/embassy-boot/boot/src/digest_adapters/salty.rs new file mode 100644 index 000000000..2b5dcf3af --- /dev/null +++ b/embassy-boot/boot/src/digest_adapters/salty.rs @@ -0,0 +1,29 @@ +use digest::typenum::U64; +use digest::{FixedOutput, HashMarker, OutputSizeUser, Update}; + +pub struct Sha512(salty::Sha512); + +impl Default for Sha512 { + fn default() -> Self { + Self(salty::Sha512::new()) + } +} + +impl Update for Sha512 { + fn update(&mut self, data: &[u8]) { + self.0.update(data) + } +} + +impl FixedOutput for Sha512 { + fn finalize_into(self, out: &mut digest::Output) { + let result = self.0.finalize(); + out.as_mut_slice().copy_from_slice(result.as_slice()) + } +} + +impl OutputSizeUser for Sha512 { + type OutputSize = U64; +} + +impl HashMarker for Sha512 {} diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 22e3e6b00..2d1b26980 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -1,3 +1,4 @@ +use digest::Digest; use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; @@ -128,25 +129,27 @@ impl FirmwareUpdater { #[cfg(feature = "ed25519-dalek")] { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) .await?; - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? + public_key.verify(&message, &signature).map_err(into_signature_error)? } #[cfg(feature = "ed25519-salty")] { use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; fn into_signature_error(_: E) -> FirmwareUpdaterError { FirmwareUpdaterError::Signature(signature::Error::default()) @@ -157,11 +160,10 @@ impl FirmwareUpdater { let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x)) + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) .await?; - let message = digest.finalize(); let r = public_key.verify(&message, &signature); trace!( "Verifying with public key {}, signature {} and message {} yields ok: {}", @@ -176,19 +178,21 @@ impl FirmwareUpdater { self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await } - /// Iterate through the DFU and process all bytes with the provided closure. - pub async fn incremental_hash( + /// Verify the update in DFU with any digest. + pub async fn hash( &mut self, dfu_flash: &mut F, update_len: u32, - aligned: &mut [u8], - mut update: impl FnMut(&[u8]), + chunk_buf: &mut [u8], + output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - for offset in (0..update_len).step_by(aligned.len()) { - self.dfu.read(dfu_flash, offset, aligned).await?; - let len = core::cmp::min((update_len - offset) as usize, aligned.len()); - update(&aligned[..len]); + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read(dfu_flash, offset, chunk_buf).await?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); } + output.copy_from_slice(digest.finalize().as_slice()); Ok(()) } @@ -334,24 +338,26 @@ impl FirmwareUpdater { #[cfg(feature = "ed25519-dalek")] { - use ed25519_dalek::{Digest, PublicKey, Sha512, Signature, SignatureError, Verifier}; + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; + let mut message = [0; 64]; + self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; - public_key - .verify(&digest.finalize(), &signature) - .map_err(into_signature_error)? + public_key.verify(&message, &signature).map_err(into_signature_error)? } #[cfg(feature = "ed25519-salty")] { use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Sha512, Signature}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; fn into_signature_error(_: E) -> FirmwareUpdaterError { FirmwareUpdaterError::Signature(signature::Error::default()) @@ -362,10 +368,9 @@ impl FirmwareUpdater { let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - let mut digest = Sha512::new(); - self.incremental_hash_blocking(_state_and_dfu_flash, _update_len, _aligned, |x| digest.update(x))?; + let mut message = [0; 64]; + self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; - let message = digest.finalize(); let r = public_key.verify(&message, &signature); trace!( "Verifying with public key {}, signature {} and message {} yields ok: {}", @@ -380,19 +385,21 @@ impl FirmwareUpdater { self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) } - /// Iterate through the DFU and process all bytes with the provided closure. - pub fn incremental_hash_blocking( + /// Verify the update in DFU with any digest. + pub fn hash_blocking( &mut self, dfu_flash: &mut F, update_len: u32, - aligned: &mut [u8], - mut update: impl FnMut(&[u8]), + chunk_buf: &mut [u8], + output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - for offset in (0..update_len).step_by(aligned.len()) { - self.dfu.read_blocking(dfu_flash, offset, aligned)?; - let len = core::cmp::min((update_len - offset) as usize, aligned.len()); - update(&aligned[..len]); + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); } + output.copy_from_slice(digest.finalize().as_slice()); Ok(()) } @@ -479,3 +486,32 @@ impl FirmwareUpdater { Ok(self.dfu) } } + +#[cfg(test)] +mod tests { + use futures::executor::block_on; + use sha1::{Digest, Sha1}; + + use super::*; + use crate::tests::MemFlash; + + #[test] + fn can_verify() { + const STATE: Partition = Partition::new(0, 4096); + const DFU: Partition = Partition::new(65536, 131072); + + let mut flash = MemFlash::<131072, 4096, 8>([0xFF; 131072]); + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = FirmwareUpdater::new(DFU, STATE); + block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } +} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 6888a8055..da9055476 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -6,6 +6,7 @@ mod fmt; mod boot_loader; +mod digest_adapters; mod firmware_updater; mod partition; From 5e19fb6fb976db869696eaa5e8a2cdba751055b1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 12:36:50 +0200 Subject: [PATCH 0802/1575] Fix compile error when verification is enabled --- embassy-boot/boot/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index cb12f9dc7..d53c613a3 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -286,13 +286,13 @@ mod tests { const STATE: Partition = Partition::new(0, 4096); const DFU: Partition = Partition::new(4096, 8192); - let mut flash = MemFlash::<8192, 4096, 4>([0xff; 8192]); + let mut flash = MemFlash::<8192, 4096, 4>::default(); let firmware_len = firmware.len(); let mut write_buf = [0; 4096]; write_buf[0..firmware_len].copy_from_slice(firmware); - NorFlash::write(&mut flash, DFU.from as u32, &write_buf).unwrap(); + DFU.write_blocking(&mut flash, 0, &write_buf).unwrap(); // On with the test From 803c09c300a9aeb412e08c37723cd9de3caf89e9 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 12:50:53 +0200 Subject: [PATCH 0803/1575] Expose read/write/erase on partition --- embassy-boot/boot/src/partition.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 3ccd4dd76..217c457fc 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -24,7 +24,7 @@ impl Partition { } /// Read from the partition on the provided flash - pub(crate) async fn read( + pub async fn read( &self, flash: &mut F, offset: u32, @@ -35,12 +35,7 @@ impl Partition { } /// Write to the partition on the provided flash - pub(crate) async fn write( - &self, - flash: &mut F, - offset: u32, - bytes: &[u8], - ) -> Result<(), F::Error> { + pub async fn write(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { let offset = self.from as u32 + offset; flash.write(offset, bytes).await?; trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); @@ -48,7 +43,7 @@ impl Partition { } /// Erase part of the partition on the provided flash - pub(crate) async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + pub async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { let from = self.from as u32 + from; let to = self.from as u32 + to; flash.erase(from, to).await?; @@ -66,18 +61,13 @@ impl Partition { } /// Read from the partition on the provided flash - pub(crate) fn read_blocking( - &self, - flash: &mut F, - offset: u32, - bytes: &mut [u8], - ) -> Result<(), F::Error> { + pub fn read_blocking(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { let offset = self.from as u32 + offset; flash.read(offset, bytes) } /// Write to the partition on the provided flash - pub(crate) fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { + pub fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { let offset = self.from as u32 + offset; flash.write(offset, bytes)?; trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); @@ -85,7 +75,7 @@ impl Partition { } /// Erase part of the partition on the provided flash - pub(crate) fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + pub fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { let from = self.from as u32 + from; let to = self.from as u32 + to; flash.erase(from, to)?; From 8256ac104405400a15aa9a6a2d9afe38a552a98b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 19:07:45 +0200 Subject: [PATCH 0804/1575] Use MemFlash::default() in sha1 verify test --- embassy-boot/boot/src/firmware_updater.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 819e20201..fffb9a500 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -519,14 +519,14 @@ mod tests { use sha1::{Digest, Sha1}; use super::*; - use crate::tests::MemFlash; + use crate::mem_flash::MemFlash; #[test] - fn can_verify() { + fn can_verify_sha1() { const STATE: Partition = Partition::new(0, 4096); const DFU: Partition = Partition::new(65536, 131072); - let mut flash = MemFlash::<131072, 4096, 8>([0xFF; 131072]); + let mut flash = MemFlash::<131072, 4096, 8>::default(); let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let mut to_write = [0; 4096]; From 9242ad89d436422fd5abdafadb2faf845e730a16 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 20:25:55 +0200 Subject: [PATCH 0805/1575] Remove magic buffer argument from prepare_boot and use the aligned page buffer instead --- embassy-boot/boot/src/boot_loader.rs | 134 +++++++++++++++------------ embassy-boot/boot/src/lib.rs | 22 +---- 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 9d047f778..698075599 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -56,6 +56,16 @@ pub trait FlashConfig { fn state(&mut self) -> &mut Self::STATE; } +trait FlashConfigEx { + fn page_size() -> usize; +} + +impl FlashConfigEx for T { + fn page_size() -> usize { + core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) + } +} + /// BootLoader works with any flash implementing embedded_storage and can also work with /// different page sizes and flash write sizes. pub struct BootLoader { @@ -91,6 +101,9 @@ impl BootLoader { /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap /// algorithm to work correctly. /// + /// The provided aligned_buf argument must satisfy any alignment requirements + /// given by the partition flashes. All flash operations will use this buffer. + /// /// SWAPPING /// /// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition. @@ -169,87 +182,89 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot( - &mut self, - p: &mut P, - magic: &mut [u8], - page: &mut [u8], - ) -> Result { + pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); - assert_eq!(magic.len(), P::STATE::WRITE_SIZE); + assert_eq!(aligned_buf.len(), P::page_size()); + assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); + assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p, magic)?; + let state = self.read_state(p, aligned_buf)?; if state == State::Swap { // // Check if we already swapped. If we're in the swap state, this means we should revert // since the app has failed to mark boot as successful // - if !self.is_swapped(p, magic, page)? { + if !self.is_swapped(p, aligned_buf)? { trace!("Swapping"); - self.swap(p, magic, page)?; + self.swap(p, aligned_buf)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p, magic, page)?; + self.revert(p, aligned_buf)?; let state_flash = p.state(); + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; // Invalidate progress - magic.fill(!P::STATE::ERASE_VALUE); + state_word.fill(!P::STATE::ERASE_VALUE); self.state - .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, magic)?; + .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; // Clear magic and progress self.state.wipe_blocking(state_flash)?; // Set magic - magic.fill(BOOT_MAGIC); - self.state.write_blocking(state_flash, 0, magic)?; + state_word.fill(BOOT_MAGIC); + self.state.write_blocking(state_flash, 0, state_word)?; } } Ok(state) } - fn is_swapped(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result { - let page_size = page.len(); - let page_count = self.active.len() / page_size; - let progress = self.current_progress(p, magic)?; + fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { + let page_count = self.active.len() / P::page_size(); + let progress = self.current_progress(p, aligned_buf)?; Ok(progress >= page_count * 2) } - fn current_progress(&mut self, config: &mut P, aligned: &mut [u8]) -> Result { - let write_size = aligned.len(); - let max_index = ((self.state.len() - write_size) / write_size) - 2; - aligned.fill(!P::STATE::ERASE_VALUE); - + fn current_progress(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { + let max_index = ((self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE) - 2; let state_flash = config.state(); + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; self.state - .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, aligned)?; - if aligned.iter().any(|&b| b != P::STATE::ERASE_VALUE) { + .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + if state_word.iter().any(|&b| b != P::STATE::ERASE_VALUE) { // Progress is invalid return Ok(max_index); } for index in 0..max_index { - self.state - .read_blocking(state_flash, (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; + self.state.read_blocking( + state_flash, + (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, + state_word, + )?; - if aligned.iter().any(|&b| b == P::STATE::ERASE_VALUE) { + if state_word.iter().any(|&b| b == P::STATE::ERASE_VALUE) { return Ok(index); } } Ok(max_index) } - fn update_progress(&mut self, index: usize, p: &mut P, magic: &mut [u8]) -> Result<(), BootError> { - let aligned = magic; - aligned.fill(!P::STATE::ERASE_VALUE); + fn update_progress( + &mut self, + index: usize, + p: &mut P, + aligned_buf: &mut [u8], + ) -> Result<(), BootError> { + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + state_word.fill(!P::STATE::ERASE_VALUE); self.state - .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, aligned)?; + .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, state_word)?; Ok(()) } @@ -259,26 +274,24 @@ impl BootLoader { from_offset: u32, to_offset: u32, p: &mut P, - magic: &mut [u8], - page: &mut [u8], + aligned_buf: &mut [u8], ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { + if self.current_progress(p, aligned_buf)? <= idx { let mut offset = from_offset; - for chunk in buf.chunks_mut(P::DFU::BLOCK_SIZE) { + for chunk in aligned_buf.chunks_mut(P::DFU::BLOCK_SIZE) { self.dfu.read_blocking(p.dfu(), offset, chunk)?; offset += chunk.len() as u32; } self.active - .erase_blocking(p.active(), to_offset, to_offset + buf.len() as u32)?; + .erase_blocking(p.active(), to_offset, to_offset + P::page_size() as u32)?; let mut offset = to_offset; - for chunk in buf.chunks(P::ACTIVE::BLOCK_SIZE) { + for chunk in aligned_buf.chunks(P::ACTIVE::BLOCK_SIZE) { self.active.write_blocking(p.active(), offset, chunk)?; offset += chunk.len() as u32; } - self.update_progress(idx, p, magic)?; + self.update_progress(idx, p, aligned_buf)?; } Ok(()) } @@ -289,32 +302,30 @@ impl BootLoader { from_offset: u32, to_offset: u32, p: &mut P, - magic: &mut [u8], - page: &mut [u8], + aligned_buf: &mut [u8], ) -> Result<(), BootError> { - let buf = page; - if self.current_progress(p, magic)? <= idx { + if self.current_progress(p, aligned_buf)? <= idx { let mut offset = from_offset; - for chunk in buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { + for chunk in aligned_buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { self.active.read_blocking(p.active(), offset, chunk)?; offset += chunk.len() as u32; } self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + buf.len() as u32)?; + .erase_blocking(p.dfu(), to_offset as u32, to_offset + P::page_size() as u32)?; let mut offset = to_offset; - for chunk in buf.chunks(P::DFU::BLOCK_SIZE) { + for chunk in aligned_buf.chunks(P::DFU::BLOCK_SIZE) { self.dfu.write_blocking(p.dfu(), offset, chunk)?; offset += chunk.len() as u32; } - self.update_progress(idx, p, magic)?; + self.update_progress(idx, p, aligned_buf)?; } Ok(()) } - fn swap(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); + fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_size = P::page_size(); let page_count = self.active.len() / page_size; trace!("Page count: {}", page_count); for page_num in 0..page_count { @@ -326,20 +337,20 @@ impl BootLoader { let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32; let dfu_to_offset = ((page_count - page_num) * page_size) as u32; //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy DFU page to the active page let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32; let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32; //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) } - fn revert(&mut self, p: &mut P, magic: &mut [u8], page: &mut [u8]) -> Result<(), BootError> { - let page_size = page.len(); + fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_size = P::page_size(); let page_count = self.active.len() / page_size; for page_num in 0..page_count { let idx = page_count * 2 + page_num * 2; @@ -347,21 +358,22 @@ impl BootLoader { // Copy the bad active page to the DFU page let active_from_offset = (page_num * page_size) as u32; let dfu_to_offset = (page_num * page_size) as u32; - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, magic, page)?; + self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy the DFU page back to the active page let active_to_offset = (page_num * page_size) as u32; let dfu_from_offset = ((page_num + 1) * page_size) as u32; - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, magic, page)?; + self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) } - fn read_state(&mut self, config: &mut P, magic: &mut [u8]) -> Result { - self.state.read_blocking(config.state(), 0, magic)?; + fn read_state(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { + let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + self.state.read_blocking(config.state(), 0, state_word)?; - if !magic.iter().any(|&b| b != SWAP_MAGIC) { + if !state_word.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) } else { Ok(State::Boot) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index d53c613a3..896498c0b 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -77,12 +77,8 @@ mod tests { let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut magic = [0; 4]; let mut page = [0; 4096]; - assert_eq!( - State::Boot, - bootloader.prepare_boot(&mut flash, &mut magic, &mut page).unwrap() - ); + assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash, &mut page).unwrap()); } #[test] @@ -110,12 +106,11 @@ mod tests { } block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); - let mut magic = [0; 4]; let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) .unwrap() ); @@ -132,7 +127,7 @@ mod tests { assert_eq!( State::Swap, bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) .unwrap() ); @@ -150,7 +145,7 @@ mod tests { assert_eq!( State::Boot, bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut magic, &mut page) + .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) .unwrap() ); } @@ -184,17 +179,12 @@ mod tests { block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut magic = [0; 4]; let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader - .prepare_boot( - &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), - &mut magic, - &mut page - ) + .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page) .unwrap() ); @@ -237,14 +227,12 @@ mod tests { block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut magic = [0; 4]; let mut page = [0; 4096]; assert_eq!( State::Swap, bootloader .prepare_boot( &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), - &mut magic, &mut page ) .unwrap() From 25577e0eafd8a3d4ffaa4b8f17cb55399fd58038 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:09:30 +0200 Subject: [PATCH 0806/1575] Assert active and dfu have same erase size and copy in smaller chunks The copy from active to dfu (and vice versa) is now done in smaller portions depending on aligned_buf, which now does not need to be erase_size big. --- embassy-boot/boot/src/boot_loader.rs | 66 ++++++++++++------------ embassy-boot/boot/src/large_erase.rs | 76 ++++++++++++++++++++++++++++ embassy-boot/boot/src/lib.rs | 32 ++++-------- embassy-boot/boot/src/mem_flash.rs | 1 - 4 files changed, 118 insertions(+), 57 deletions(-) create mode 100644 embassy-boot/boot/src/large_erase.rs diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 698075599..db067da5b 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -32,14 +32,13 @@ where /// Extension of the embedded-storage flash type information with block size and erase value. pub trait Flash: NorFlash { - /// The block size that should be used when writing to flash. For most builtin flashes, this is the same as the erase - /// size of the flash, but for external QSPI flash modules, this can be lower. - const BLOCK_SIZE: usize; /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. const ERASE_VALUE: u8 = 0xFF; } -/// Trait defining the flash handles used for active and DFU partition +/// Trait defining the flash handles used for active and DFU partition. +/// The ACTIVE and DFU erase sizes must be equal. If this is not the case, then consider adding an adapter for the +/// smallest flash to increase its erase size such that they match. See e.g. [`crate::large_erase::LargeErase`]. pub trait FlashConfig { /// Flash type used for the state partition. type STATE: Flash; @@ -62,12 +61,12 @@ trait FlashConfigEx { impl FlashConfigEx for T { fn page_size() -> usize { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) + assert_eq!(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE); + T::ACTIVE::ERASE_SIZE } } -/// BootLoader works with any flash implementing embedded_storage and can also work with -/// different page sizes and flash write sizes. +/// BootLoader works with any flash implementing embedded_storage. pub struct BootLoader { // Page with current state of bootloader. The state partition has the following format: // All ranges are in multiples of WRITE_SIZE bytes. @@ -184,7 +183,9 @@ impl BootLoader { /// pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(aligned_buf.len(), P::page_size()); + assert_eq!(0, P::page_size() % aligned_buf.len()); + assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); + assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); @@ -277,20 +278,18 @@ impl BootLoader { aligned_buf: &mut [u8], ) -> Result<(), BootError> { if self.current_progress(p, aligned_buf)? <= idx { - let mut offset = from_offset; - for chunk in aligned_buf.chunks_mut(P::DFU::BLOCK_SIZE) { - self.dfu.read_blocking(p.dfu(), offset, chunk)?; - offset += chunk.len() as u32; - } + let page_size = P::page_size() as u32; self.active - .erase_blocking(p.active(), to_offset, to_offset + P::page_size() as u32)?; + .erase_blocking(p.active(), to_offset, to_offset + page_size)?; - let mut offset = to_offset; - for chunk in aligned_buf.chunks(P::ACTIVE::BLOCK_SIZE) { - self.active.write_blocking(p.active(), offset, chunk)?; - offset += chunk.len() as u32; + for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { + self.dfu + .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; + self.active + .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; } + self.update_progress(idx, p, aligned_buf)?; } Ok(()) @@ -305,20 +304,18 @@ impl BootLoader { aligned_buf: &mut [u8], ) -> Result<(), BootError> { if self.current_progress(p, aligned_buf)? <= idx { - let mut offset = from_offset; - for chunk in aligned_buf.chunks_mut(P::ACTIVE::BLOCK_SIZE) { - self.active.read_blocking(p.active(), offset, chunk)?; - offset += chunk.len() as u32; - } + let page_size = P::page_size() as u32; self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + P::page_size() as u32)?; + .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?; - let mut offset = to_offset; - for chunk in aligned_buf.chunks(P::DFU::BLOCK_SIZE) { - self.dfu.write_blocking(p.dfu(), offset, chunk)?; - offset += chunk.len() as u32; + for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { + self.active + .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; + self.dfu + .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; } + self.update_progress(idx, p, aligned_buf)?; } Ok(()) @@ -389,14 +386,14 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash +pub struct BootFlash where F: NorFlash + ReadNorFlash, { flash: F, } -impl BootFlash +impl BootFlash where F: NorFlash + ReadNorFlash, { @@ -406,22 +403,21 @@ where } } -impl Flash for BootFlash +impl Flash for BootFlash where F: NorFlash + ReadNorFlash, { - const BLOCK_SIZE: usize = BLOCK_SIZE; const ERASE_VALUE: u8 = ERASE_VALUE; } -impl ErrorType for BootFlash +impl ErrorType for BootFlash where F: ReadNorFlash + NorFlash, { type Error = F::Error; } -impl NorFlash for BootFlash +impl NorFlash for BootFlash where F: ReadNorFlash + NorFlash, { @@ -437,7 +433,7 @@ where } } -impl ReadNorFlash for BootFlash +impl ReadNorFlash for BootFlash where F: ReadNorFlash + NorFlash, { diff --git a/embassy-boot/boot/src/large_erase.rs b/embassy-boot/boot/src/large_erase.rs new file mode 100644 index 000000000..d00d43599 --- /dev/null +++ b/embassy-boot/boot/src/large_erase.rs @@ -0,0 +1,76 @@ +#![allow(unused)] + +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +use crate::Flash; + +pub struct LargeErase(pub F); + +impl LargeErase { + pub const fn new(flash: F) -> Self { + Self(flash) + } +} + +impl Flash for LargeErase { + const ERASE_VALUE: u8 = F::ERASE_VALUE; +} + +impl ErrorType for LargeErase { + type Error = F::Error; +} + +impl NorFlash for LargeErase { + const WRITE_SIZE: usize = F::ERASE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + assert!(ERASE_SIZE >= F::ERASE_SIZE); + assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); + self.0.erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.0.write(offset, bytes) + } +} + +impl ReadNorFlash for LargeErase { + const READ_SIZE: usize = F::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.0.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.capacity() + } +} + +impl AsyncNorFlash for LargeErase { + const WRITE_SIZE: usize = F::ERASE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + assert!(ERASE_SIZE >= F::ERASE_SIZE); + assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); + self.0.erase(from, to).await + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.0.write(offset, bytes).await + } +} + +impl AsyncReadNorFlash for LargeErase { + const READ_SIZE: usize = F::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.0.read(offset, bytes).await + } + + fn capacity(&self) -> usize { + self.0.capacity() + } +} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 896498c0b..79759124b 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,6 +7,7 @@ mod fmt; mod boot_loader; mod firmware_updater; +mod large_erase; mod mem_flash; mod partition; @@ -48,6 +49,7 @@ mod tests { use futures::executor::block_on; use super::*; + use crate::large_erase::LargeErase; use crate::mem_flash::MemFlash; /* @@ -99,14 +101,10 @@ mod tests { let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut updater = FirmwareUpdater::new(DFU, STATE); - let mut offset = 0; - for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap(); - offset += chunk.len(); - } + block_on(updater.write_firmware(0, &update, &mut flash)).unwrap(); block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); - let mut page = [0; 4096]; + let mut page = [0; 1024]; assert_eq!( State::Swap, bootloader @@ -158,7 +156,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut active = MemFlash::<16384, 4096, 8>::random(); - let mut dfu = MemFlash::<16384, 2048, 8>::random(); + let mut dfu = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 8>::random()); let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; @@ -171,11 +169,7 @@ mod tests { let mut updater = FirmwareUpdater::new(DFU, STATE); - let mut offset = 0; - for chunk in update.chunks(2048) { - block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); - offset += chunk.len(); - } + block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -194,7 +188,7 @@ mod tests { // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.0.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -206,7 +200,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut aligned = [0; 4]; - let mut active = MemFlash::<16384, 2048, 4>::random(); + let mut active = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 4>::random()); let mut dfu = MemFlash::<16384, 4096, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); @@ -214,16 +208,12 @@ mod tests { let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.mem[i] = original[i - ACTIVE.from]; + active.0.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); - let mut offset = 0; - for chunk in update.chunks(4096) { - block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap(); - offset += chunk.len(); - } + block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); @@ -239,7 +229,7 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.0.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index 828aad9d9..2598bf4de 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -47,7 +47,6 @@ impl Defaul impl Flash for MemFlash { - const BLOCK_SIZE: usize = ERASE_SIZE; const ERASE_VALUE: u8 = 0xFF; } From 6c93309df490020f0ae4a515bf404dfd251b9b69 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:18:41 +0200 Subject: [PATCH 0807/1575] Remove the Flash trait --- embassy-boot/boot/src/boot_loader.rs | 73 ++++++++++++---------------- embassy-boot/boot/src/large_erase.rs | 6 --- embassy-boot/boot/src/lib.rs | 2 +- embassy-boot/boot/src/mem_flash.rs | 8 --- 4 files changed, 32 insertions(+), 57 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index db067da5b..37fff621a 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -30,22 +30,18 @@ where } } -/// Extension of the embedded-storage flash type information with block size and erase value. -pub trait Flash: NorFlash { - /// The erase value of the flash. Typically the default of 0xFF is used, but some flashes use a different value. - const ERASE_VALUE: u8 = 0xFF; -} - /// Trait defining the flash handles used for active and DFU partition. /// The ACTIVE and DFU erase sizes must be equal. If this is not the case, then consider adding an adapter for the /// smallest flash to increase its erase size such that they match. See e.g. [`crate::large_erase::LargeErase`]. pub trait FlashConfig { + /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. + const STATE_ERASE_VALUE: u8 = 0xFF; /// Flash type used for the state partition. - type STATE: Flash; + type STATE: NorFlash; /// Flash type used for the active partition. - type ACTIVE: Flash; + type ACTIVE: NorFlash; /// Flash type used for the dfu partition. - type DFU: Flash; + type DFU: NorFlash; /// Return flash instance used to write/read to/from active partition. fn active(&mut self) -> &mut Self::ACTIVE; @@ -208,7 +204,7 @@ impl BootLoader { let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; // Invalidate progress - state_word.fill(!P::STATE::ERASE_VALUE); + state_word.fill(!P::STATE_ERASE_VALUE); self.state .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; @@ -237,7 +233,7 @@ impl BootLoader { self.state .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; - if state_word.iter().any(|&b| b != P::STATE::ERASE_VALUE) { + if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { // Progress is invalid return Ok(max_index); } @@ -249,7 +245,7 @@ impl BootLoader { state_word, )?; - if state_word.iter().any(|&b| b == P::STATE::ERASE_VALUE) { + if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { return Ok(index); } } @@ -263,7 +259,7 @@ impl BootLoader { aligned_buf: &mut [u8], ) -> Result<(), BootError> { let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - state_word.fill(!P::STATE::ERASE_VALUE); + state_word.fill(!P::STATE_ERASE_VALUE); self.state .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, state_word)?; Ok(()) @@ -386,16 +382,16 @@ fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_s } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash +pub struct BootFlash where - F: NorFlash + ReadNorFlash, + F: NorFlash, { flash: F, } -impl BootFlash +impl BootFlash where - F: NorFlash + ReadNorFlash, + F: NorFlash, { /// Create a new instance of a bootable flash pub fn new(flash: F) -> Self { @@ -403,23 +399,16 @@ where } } -impl Flash for BootFlash +impl ErrorType for BootFlash where - F: NorFlash + ReadNorFlash, -{ - const ERASE_VALUE: u8 = ERASE_VALUE; -} - -impl ErrorType for BootFlash -where - F: ReadNorFlash + NorFlash, + F: NorFlash, { type Error = F::Error; } -impl NorFlash for BootFlash +impl NorFlash for BootFlash where - F: ReadNorFlash + NorFlash, + F: NorFlash, { const WRITE_SIZE: usize = F::WRITE_SIZE; const ERASE_SIZE: usize = F::ERASE_SIZE; @@ -433,9 +422,9 @@ where } } -impl ReadNorFlash for BootFlash +impl ReadNorFlash for BootFlash where - F: ReadNorFlash + NorFlash, + F: NorFlash, { const READ_SIZE: usize = F::READ_SIZE; @@ -451,14 +440,14 @@ where /// Convenience provider that uses a single flash for all partitions. pub struct SingleFlashConfig<'a, F> where - F: Flash, + F: NorFlash, { flash: &'a mut F, } impl<'a, F> SingleFlashConfig<'a, F> where - F: Flash, + F: NorFlash, { /// Create a provider for a single flash. pub fn new(flash: &'a mut F) -> Self { @@ -468,7 +457,7 @@ where impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> where - F: Flash, + F: NorFlash, { type STATE = F; type ACTIVE = F; @@ -488,9 +477,9 @@ where /// Convenience flash provider that uses separate flash instances for each partition. pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, + ACTIVE: NorFlash, + STATE: NorFlash, + DFU: NorFlash, { active: &'a mut ACTIVE, state: &'a mut STATE, @@ -499,9 +488,9 @@ where impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, + ACTIVE: NorFlash, + STATE: NorFlash, + DFU: NorFlash, { /// Create a new flash provider with separate configuration for all three partitions. pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { @@ -511,9 +500,9 @@ where impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> where - ACTIVE: Flash, - STATE: Flash, - DFU: Flash, + ACTIVE: NorFlash, + STATE: NorFlash, + DFU: NorFlash, { type STATE = STATE; type ACTIVE = ACTIVE; diff --git a/embassy-boot/boot/src/large_erase.rs b/embassy-boot/boot/src/large_erase.rs index d00d43599..b999a046f 100644 --- a/embassy-boot/boot/src/large_erase.rs +++ b/embassy-boot/boot/src/large_erase.rs @@ -3,8 +3,6 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; -use crate::Flash; - pub struct LargeErase(pub F); impl LargeErase { @@ -13,10 +11,6 @@ impl LargeErase { } } -impl Flash for LargeErase { - const ERASE_VALUE: u8 = F::ERASE_VALUE; -} - impl ErrorType for LargeErase { type Error = F::Error; } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 79759124b..cc812d797 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -11,7 +11,7 @@ mod large_erase; mod mem_flash; mod partition; -pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; +pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; pub use partition::Partition; diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index 2598bf4de..dd85405c8 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -5,8 +5,6 @@ use core::ops::{Bound, Range, RangeBounds}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; -use crate::Flash; - pub struct MemFlash { pub mem: [u8; SIZE], pub pending_write_successes: Option, @@ -44,12 +42,6 @@ impl Defaul } } -impl Flash - for MemFlash -{ - const ERASE_VALUE: u8 = 0xFF; -} - impl ErrorType for MemFlash { From 53efb029009e3cb92bb19c8ac8f521407aa4d1e2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:30:49 +0200 Subject: [PATCH 0808/1575] Allow different erase sizes for active and dfu --- embassy-boot/boot/src/boot_loader.rs | 6 ++- embassy-boot/boot/src/large_erase.rs | 70 ---------------------------- embassy-boot/boot/src/lib.rs | 12 ++--- 3 files changed, 9 insertions(+), 79 deletions(-) delete mode 100644 embassy-boot/boot/src/large_erase.rs diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 37fff621a..25f81009e 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -56,9 +56,9 @@ trait FlashConfigEx { } impl FlashConfigEx for T { + /// Get the page size which is the "unit of operation" within the bootloader. fn page_size() -> usize { - assert_eq!(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE); - T::ACTIVE::ERASE_SIZE + core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) } } @@ -182,6 +182,8 @@ impl BootLoader { assert_eq!(0, P::page_size() % aligned_buf.len()); assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); + assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE); + assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); diff --git a/embassy-boot/boot/src/large_erase.rs b/embassy-boot/boot/src/large_erase.rs deleted file mode 100644 index b999a046f..000000000 --- a/embassy-boot/boot/src/large_erase.rs +++ /dev/null @@ -1,70 +0,0 @@ -#![allow(unused)] - -use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; -use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; - -pub struct LargeErase(pub F); - -impl LargeErase { - pub const fn new(flash: F) -> Self { - Self(flash) - } -} - -impl ErrorType for LargeErase { - type Error = F::Error; -} - -impl NorFlash for LargeErase { - const WRITE_SIZE: usize = F::ERASE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - assert!(ERASE_SIZE >= F::ERASE_SIZE); - assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); - self.0.erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.0.write(offset, bytes) - } -} - -impl ReadNorFlash for LargeErase { - const READ_SIZE: usize = F::READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.0.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.0.capacity() - } -} - -impl AsyncNorFlash for LargeErase { - const WRITE_SIZE: usize = F::ERASE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - assert!(ERASE_SIZE >= F::ERASE_SIZE); - assert_eq!(0, ERASE_SIZE % F::ERASE_SIZE); - self.0.erase(from, to).await - } - - async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.0.write(offset, bytes).await - } -} - -impl AsyncReadNorFlash for LargeErase { - const READ_SIZE: usize = F::READ_SIZE; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.0.read(offset, bytes).await - } - - fn capacity(&self) -> usize { - self.0.capacity() - } -} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index cc812d797..3109f2b47 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,7 +7,6 @@ mod fmt; mod boot_loader; mod firmware_updater; -mod large_erase; mod mem_flash; mod partition; @@ -49,7 +48,6 @@ mod tests { use futures::executor::block_on; use super::*; - use crate::large_erase::LargeErase; use crate::mem_flash::MemFlash; /* @@ -156,7 +154,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut active = MemFlash::<16384, 4096, 8>::random(); - let mut dfu = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 8>::random()); + let mut dfu = MemFlash::<16384, 2048, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; @@ -188,7 +186,7 @@ mod tests { // First DFU page is untouched for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.0.mem[i], original[i - DFU.from - 4096], "Index {}", i); + assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); } } @@ -200,7 +198,7 @@ mod tests { const DFU: Partition = Partition::new(0, 16384); let mut aligned = [0; 4]; - let mut active = LargeErase::<_, 4096>::new(MemFlash::<16384, 2048, 4>::random()); + let mut active = MemFlash::<16384, 2048, 4>::random(); let mut dfu = MemFlash::<16384, 4096, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); @@ -208,7 +206,7 @@ mod tests { let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; for i in ACTIVE.from..ACTIVE.to { - active.0.mem[i] = original[i - ACTIVE.from]; + active.mem[i] = original[i - ACTIVE.from]; } let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -229,7 +227,7 @@ mod tests { ); for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.0.mem[i], update[i - ACTIVE.from], "Index {}", i); + assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); } // First DFU page is untouched From 78e6b4d26134887b50e3ff3239a20f9b3002e8a0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:43:18 +0200 Subject: [PATCH 0809/1575] Remove comment about equal erase size requirement --- embassy-boot/boot/src/boot_loader.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 25f81009e..ccd74b237 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -31,8 +31,6 @@ where } /// Trait defining the flash handles used for active and DFU partition. -/// The ACTIVE and DFU erase sizes must be equal. If this is not the case, then consider adding an adapter for the -/// smallest flash to increase its erase size such that they match. See e.g. [`crate::large_erase::LargeErase`]. pub trait FlashConfig { /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. const STATE_ERASE_VALUE: u8 = 0xFF; From e962fe794ce3c83b094e22523d0c59264983b036 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 21:57:28 +0200 Subject: [PATCH 0810/1575] Add assertions about the aligned_buf % write sizes --- embassy-boot/boot/src/boot_loader.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index ccd74b237..2412427c0 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -179,10 +179,12 @@ impl BootLoader { // Ensure we have enough progress pages to store copy progress assert_eq!(0, P::page_size() % aligned_buf.len()); assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE); + assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active From a77ce1088d927266a23cbc971aed6facd3b7f918 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 22:22:25 +0200 Subject: [PATCH 0811/1575] Align chip specific boot projects with new prepare_boot() signature --- embassy-boot/nrf/src/lib.rs | 14 ++++++-------- embassy-boot/rp/src/lib.rs | 16 +++++++--------- embassy-boot/stm32/src/lib.rs | 14 ++++++-------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index f40ae62d6..d3774d308 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -11,13 +11,12 @@ use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. -pub struct BootLoader { +pub struct BootLoader { boot: embassy_boot::BootLoader, - magic: AlignedBuffer<4>, - page: AlignedBuffer, + aligned_buf: AlignedBuffer, } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { @@ -56,20 +55,19 @@ impl Default for BootLoader { } } -impl BootLoader { +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; 4]), - page: AlignedBuffer([0; PAGE_SIZE]), + aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, &mut self.magic.0, &mut self.page.0) { + match self.boot.prepare_boot(flash, &mut self.aligned_buf.0) { Ok(_) => self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 6df34133e..c163db131 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -5,33 +5,31 @@ mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; -use embassy_rp::flash::{Flash, ERASE_SIZE, WRITE_SIZE}; +use embassy_rp::flash::{Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. -pub struct BootLoader { +pub struct BootLoader { boot: embassy_boot::BootLoader, - magic: AlignedBuffer, - page: AlignedBuffer, + aligned_buf: AlignedBuffer, } -impl BootLoader { +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]), + aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { + match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } @@ -54,7 +52,7 @@ impl BootLoader { } } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 82f712c4d..1f63fcd63 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -7,26 +7,24 @@ mod fmt; pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; /// A bootloader for STM32 devices. -pub struct BootLoader { +pub struct BootLoader { boot: embassy_boot::BootLoader, - magic: AlignedBuffer, - page: AlignedBuffer, + aligned_buf: AlignedBuffer, } -impl BootLoader { +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; PAGE_SIZE]), + aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) { + match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), Err(_) => panic!("boot prepare error!"), } @@ -49,7 +47,7 @@ impl BootLoader Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { From 84bfe9b8c93ea8634ce2192fb719034b5c13e5da Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 4 Apr 2023 22:44:21 +0200 Subject: [PATCH 0812/1575] Align examples with bootloader changes --- embassy-boot/nrf/src/lib.rs | 2 +- embassy-boot/rp/src/lib.rs | 2 +- examples/boot/bootloader/nrf/src/main.rs | 8 +++++--- examples/boot/bootloader/rp/src/main.rs | 3 +-- examples/boot/bootloader/stm32/src/main.rs | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index d3774d308..a2176f609 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -16,7 +16,7 @@ pub struct BootLoader { aligned_buf: AlignedBuffer, } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index c163db131..0031efa63 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -52,7 +52,7 @@ impl BootLoader { } } -impl Default for BootLoader { +impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { extern "C" { diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index aca3b857a..8818a23b8 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -27,9 +27,11 @@ fn main() -> ! { wdt_config.run_during_sleep = true; wdt_config.run_during_debug_halt = false; - let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config), - ))); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::new(WatchdogFlash::start( + Nvmc::new(p.NVMC), + p.WDT, + wdt_config, + )))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index fb7f0522b..8129591fa 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -5,7 +5,6 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_rp::*; -use embassy_rp::flash::ERASE_SIZE; use embassy_time::Duration; const FLASH_SIZE: usize = 2 * 1024 * 1024; @@ -24,7 +23,7 @@ fn main() -> ! { let mut bl: BootLoader = BootLoader::default(); let flash = WatchdogFlash::::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); - let mut flash = BootFlash::<_, ERASE_SIZE>::new(flash); + let mut flash = BootFlash::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 4b17cd799..b8027d19a 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; +use embassy_stm32::flash::{Flash, ERASE_SIZE}; #[entry] fn main() -> ! { @@ -19,9 +19,9 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); + let mut bl: BootLoader = BootLoader::default(); let flash = Flash::new(p.FLASH); - let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); + let mut flash = BootFlash::new(flash); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } From 991b22b6a1e845dc15eca72bf9881e60f1803840 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 4 Apr 2023 19:35:25 -0500 Subject: [PATCH 0813/1575] stm32/pwm: add complementary pwm --- embassy-stm32/src/pwm/complementary_pwm.rs | 145 +++++++++++++++++++++ embassy-stm32/src/pwm/mod.rs | 22 ++++ 2 files changed, 167 insertions(+) create mode 100644 embassy-stm32/src/pwm/complementary_pwm.rs diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs new file mode 100644 index 000000000..b8761724a --- /dev/null +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -0,0 +1,145 @@ +use core::marker::PhantomData; + +use embassy_hal_common::{into_ref, PeripheralRef}; + +use super::*; +#[allow(unused_imports)] +use crate::gpio::sealed::{AFType, Pin}; +use crate::gpio::AnyPin; +use crate::time::Hertz; +use crate::Peripheral; + +pub struct Ch1; +pub struct Ch2; +pub struct Ch3; +pub struct Ch4; + +pub struct PwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +pub struct ComplementaryPwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +macro_rules! channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| unsafe { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| unsafe { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + ComplementaryPwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + }; +} + +channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); +channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); +channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); +channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); + +pub struct ComplementaryPwm<'d, T> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { + pub fn new( + tim: impl Peripheral

+ 'd, + _ch1: Option>, + _ch1n: Option>, + _ch2: Option>, + _ch2n: Option>, + _ch3: Option>, + _ch3n: Option>, + _ch4: Option>, + _ch4n: Option>, + freq: Hertz, + ) -> Self { + Self::new_inner(tim, freq) + } + + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + into_ref!(tim); + + T::enable(); + ::reset(); + + let mut this = Self { inner: tim }; + + this.inner.set_frequency(freq); + this.inner.start(); + + unsafe { + this.inner.enable_outputs(true); + + this.inner + .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); + } + this + } + + pub fn enable(&mut self, channel: Channel) { + unsafe { + self.inner.enable_channel(channel, true); + } + } + + pub fn disable(&mut self, channel: Channel) { + unsafe { + self.inner.enable_channel(channel, false); + } + } + + pub fn set_freq(&mut self, freq: Hertz) { + self.inner.set_frequency(freq); + } + + pub fn get_max_duty(&self) -> u16 { + unsafe { self.inner.get_max_compare_value() } + } + + pub fn set_duty(&mut self, channel: Channel, duty: u16) { + assert!(duty < self.get_max_duty()); + unsafe { self.inner.set_compare_value(channel, duty) } + } + + /* + set the value of the dead-time register + */ + pub fn set_dead_time_value(&mut self, value: u8) { + unsafe { self.inner.set_dead_time_value(value) } + } +} diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index d3713391c..6f3c12665 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,3 +1,4 @@ +pub mod complementary_pwm; pub mod simple_pwm; #[cfg(feature = "unstable-pac")] @@ -67,6 +68,10 @@ pub(crate) mod sealed { unsafe fn get_max_compare_value(&self) -> u16; } + pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + unsafe fn set_dead_time_value(&mut self, value: u8); + } + pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); @@ -82,6 +87,12 @@ pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static { } + +pub trait ComplementaryCaptureCompare16bitInstance: + sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static +{ +} + pub trait CaptureCompare32bitInstance: sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static { @@ -209,6 +220,17 @@ foreach_interrupt! { impl CaptureCompare16bitInstance for crate::peripherals::$inst { } + + impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + unsafe fn set_dead_time_value(&mut self, value: u8) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); + } + } + + impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + + } }; } From d8e2f82569e1182e2a3a7ebe43af64f91d1e57e0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 06:57:56 +0200 Subject: [PATCH 0814/1575] Let update_len be usize for now --- embassy-boot/boot/src/firmware_updater.rs | 20 +++++++++----------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index fffb9a500..48e15024e 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -119,13 +119,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: u32, + _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len() as u32); + assert!(_update_len <= self.dfu.len()); #[cfg(feature = "ed25519-dalek")] { @@ -182,10 +180,11 @@ impl FirmwareUpdater { pub async fn hash( &mut self, dfu_flash: &mut F, - update_len: u32, + update_len: usize, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { + let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read(dfu_flash, offset, chunk_buf).await?; @@ -341,13 +340,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: u32, + _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len() as u32); + assert!(_update_len <= self.dfu.len()); #[cfg(feature = "ed25519-dalek")] { @@ -402,10 +399,11 @@ impl FirmwareUpdater { pub fn hash_blocking( &mut self, dfu_flash: &mut F, - update_len: u32, + update_len: usize, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { + let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; @@ -536,7 +534,7 @@ mod tests { block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; - block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + block_on(updater.hash::<_, Sha1>(&mut flash, update.len(), &mut chunk_buf, &mut hash)).unwrap(); assert_eq!(Sha1::digest(update).as_slice(), hash); } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 605e5253c..acd90996f 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -281,7 +281,7 @@ mod tests { &mut flash, &public_key.to_bytes(), &signature.to_bytes(), - firmware_len as u32, + firmware_len, &mut aligned, )) .is_ok()); From 2deb2c624c78f4ff582441d7d00a654ac3844e33 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 08:28:31 +0200 Subject: [PATCH 0815/1575] Let Partition range be u32 instead of usize --- embassy-boot/boot/src/boot_loader.rs | 101 +++++++++++----------- embassy-boot/boot/src/firmware_updater.rs | 17 ++-- embassy-boot/boot/src/lib.rs | 60 ++++--------- embassy-boot/boot/src/mem_flash.rs | 17 ++++ embassy-boot/boot/src/partition.rs | 9 +- 5 files changed, 93 insertions(+), 111 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 2412427c0..b959de2c4 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -50,13 +50,13 @@ pub trait FlashConfig { } trait FlashConfigEx { - fn page_size() -> usize; + fn page_size() -> u32; } impl FlashConfigEx for T { /// Get the page size which is the "unit of operation" within the bootloader. - fn page_size() -> usize { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) + fn page_size() -> u32 { + core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 } } @@ -86,7 +86,7 @@ impl BootLoader { /// Return the offset of the active partition into the active flash. pub fn boot_address(&self) -> usize { - self.active.from + self.active.from as usize } /// Perform necessary boot preparations like swapping images. @@ -177,11 +177,11 @@ impl BootLoader { /// pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(0, P::page_size() % aligned_buf.len()); - assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE); - assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE); + assert_eq!(0, P::page_size() % aligned_buf.len() as u32); + assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); + assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); + assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); + assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); @@ -222,30 +222,27 @@ impl BootLoader { } fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { - let page_count = self.active.len() / P::page_size(); + let page_count = (self.active.size() / P::page_size()) as usize; let progress = self.current_progress(p, aligned_buf)?; Ok(progress >= page_count * 2) } fn current_progress(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let max_index = ((self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE) - 2; + let write_size = P::STATE::WRITE_SIZE as u32; + let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; let state_flash = config.state(); - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + let state_word = &mut aligned_buf[..write_size as usize]; - self.state - .read_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + self.state.read_blocking(state_flash, write_size, state_word)?; if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { // Progress is invalid return Ok(max_index); } for index in 0..max_index { - self.state.read_blocking( - state_flash, - (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, - state_word, - )?; + self.state + .read_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?; if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { return Ok(index); @@ -256,26 +253,29 @@ impl BootLoader { fn update_progress( &mut self, - index: usize, + progress_index: usize, p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; state_word.fill(!P::STATE_ERASE_VALUE); - self.state - .write_blocking(p.state(), (2 + index) as u32 * P::STATE::WRITE_SIZE as u32, state_word)?; + self.state.write_blocking( + p.state(), + (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32, + state_word, + )?; Ok(()) } fn copy_page_once_to_active( &mut self, - idx: usize, + progress_index: usize, from_offset: u32, to_offset: u32, p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= idx { + if self.current_progress(p, aligned_buf)? <= progress_index { let page_size = P::page_size() as u32; self.active @@ -288,20 +288,20 @@ impl BootLoader { .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(idx, p, aligned_buf)?; + self.update_progress(progress_index, p, aligned_buf)?; } Ok(()) } fn copy_page_once_to_dfu( &mut self, - idx: usize, + progress_index: usize, from_offset: u32, to_offset: u32, p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= idx { + if self.current_progress(p, aligned_buf)? <= progress_index { let page_size = P::page_size() as u32; self.dfu @@ -314,31 +314,28 @@ impl BootLoader { .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(idx, p, aligned_buf)?; + self.update_progress(progress_index, p, aligned_buf)?; } Ok(()) } fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { let page_size = P::page_size(); - let page_count = self.active.len() / page_size; - trace!("Page count: {}", page_count); + let page_count = self.active.size() / page_size; for page_num in 0..page_count { - trace!("COPY PAGE {}", page_num); - - let idx = page_num * 2; + let progress_index = (page_num * 2) as usize; // Copy active page to the 'next' DFU page. - let active_from_offset = ((page_count - 1 - page_num) * page_size) as u32; - let dfu_to_offset = ((page_count - page_num) * page_size) as u32; + let active_from_offset = (page_count - 1 - page_num) * page_size; + let dfu_to_offset = (page_count - page_num) * page_size; //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy DFU page to the active page - let active_to_offset = ((page_count - 1 - page_num) * page_size) as u32; - let dfu_from_offset = ((page_count - 1 - page_num) * page_size) as u32; + let active_to_offset = (page_count - 1 - page_num) * page_size; + let dfu_from_offset = (page_count - 1 - page_num) * page_size; //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) @@ -346,19 +343,19 @@ impl BootLoader { fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { let page_size = P::page_size(); - let page_count = self.active.len() / page_size; + let page_count = self.active.size() / page_size; for page_num in 0..page_count { - let idx = page_count * 2 + page_num * 2; + let progress_index = (page_count * 2 + page_num * 2) as usize; // Copy the bad active page to the DFU page - let active_from_offset = (page_num * page_size) as u32; - let dfu_to_offset = (page_num * page_size) as u32; - self.copy_page_once_to_dfu(idx, active_from_offset, dfu_to_offset, p, aligned_buf)?; + let active_from_offset = page_num * page_size; + let dfu_to_offset = page_num * page_size; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; // Copy the DFU page back to the active page - let active_to_offset = (page_num * page_size) as u32; - let dfu_from_offset = ((page_num + 1) * page_size) as u32; - self.copy_page_once_to_active(idx + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + let active_to_offset = page_num * page_size; + let dfu_from_offset = (page_num + 1) * page_size; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; } Ok(()) @@ -376,11 +373,11 @@ impl BootLoader { } } -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { - assert_eq!(active.len() % page_size, 0); - assert_eq!(dfu.len() % page_size, 0); - assert!(dfu.len() - active.len() >= page_size); - assert!(2 + 2 * (active.len() / page_size) <= state.len() / write_size); +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { + assert_eq!(active.size() % page_size, 0); + assert_eq!(dfu.size() % page_size, 0); + assert!(dfu.size() - active.size() >= page_size); + assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); } /// A flash wrapper implementing the Flash and embedded_storage traits. diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 2b5cc72fa..93d4a4c12 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -49,14 +49,14 @@ impl Default for FirmwareUpdater { let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; @@ -121,10 +121,8 @@ impl FirmwareUpdater { _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.len()); + assert!(_update_len as u32 <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { @@ -330,11 +328,8 @@ impl FirmwareUpdater { _update_len: usize, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let _end = self.dfu.from + _update_len; - let _read_size = _aligned.len(); - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_end <= self.dfu.to); + assert!(_update_len as u32 <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 3109f2b47..8b94b6bdc 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -89,13 +89,11 @@ mod tests { const DFU: Partition = Partition::new(61440, 122880); let mut flash = MemFlash::<131072, 4096, 4>::random(); - let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; - let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let original = [rand::random::(); ACTIVE.size() as usize]; + let update = [rand::random::(); ACTIVE.size() as usize]; let mut aligned = [0; 4]; - for i in ACTIVE.from..ACTIVE.to { - flash.mem[i] = original[i - ACTIVE.from]; - } + flash.program(ACTIVE.from, &original).unwrap(); let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -110,14 +108,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.mem[i], update[i - ACTIVE.from], "Index {}", i); - } - + flash.assert_eq(ACTIVE.from, &update); // First DFU page is untouched - for i in DFU.from + 4096..DFU.to { - assert_eq!(flash.mem[i], original[i - DFU.from - 4096], "Index {}", i); - } + flash.assert_eq(DFU.from + 4096, &original); // Running again should cause a revert assert_eq!( @@ -127,14 +120,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(flash.mem[i], original[i - ACTIVE.from], "Index {}", i); - } - + flash.assert_eq(ACTIVE.from, &original); // Last page is untouched - for i in DFU.from..DFU.to - 4096 { - assert_eq!(flash.mem[i], update[i - DFU.from], "Index {}", i); - } + flash.assert_eq(DFU.from, &update); // Mark as booted block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); @@ -158,12 +146,10 @@ mod tests { let mut state = MemFlash::<4096, 128, 4>::random(); let mut aligned = [0; 4]; - let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; - let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let original = [rand::random::(); ACTIVE.size() as usize]; + let update = [rand::random::(); ACTIVE.size() as usize]; - for i in ACTIVE.from..ACTIVE.to { - active.mem[i] = original[i - ACTIVE.from]; - } + active.program(ACTIVE.from, &original).unwrap(); let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -180,14 +166,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); - } - + active.assert_eq(ACTIVE.from, &update); // First DFU page is untouched - for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); - } + dfu.assert_eq(DFU.from + 4096, &original); } #[test] @@ -202,12 +183,10 @@ mod tests { let mut dfu = MemFlash::<16384, 4096, 8>::random(); let mut state = MemFlash::<4096, 128, 4>::random(); - let original: [u8; ACTIVE.len()] = [rand::random::(); ACTIVE.len()]; - let update: [u8; DFU.len()] = [rand::random::(); DFU.len()]; + let original = [rand::random::(); ACTIVE.size() as usize]; + let update = [rand::random::(); ACTIVE.size() as usize]; - for i in ACTIVE.from..ACTIVE.to { - active.mem[i] = original[i - ACTIVE.from]; - } + active.program(ACTIVE.from, &original).unwrap(); let mut updater = FirmwareUpdater::new(DFU, STATE); @@ -226,14 +205,9 @@ mod tests { .unwrap() ); - for i in ACTIVE.from..ACTIVE.to { - assert_eq!(active.mem[i], update[i - ACTIVE.from], "Index {}", i); - } - + active.assert_eq(ACTIVE.from, &update); // First DFU page is untouched - for i in DFU.from + 4096..DFU.to { - assert_eq!(dfu.mem[i], original[i - DFU.from - 4096], "Index {}", i); - } + dfu.assert_eq(DFU.from + 4096, &original); } #[test] diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index dd85405c8..c62379b24 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -32,6 +32,23 @@ impl MemFla pending_write_successes: None, } } + + pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { + let offset = offset as usize; + assert!(bytes.len() % WRITE_SIZE == 0); + assert!(offset % WRITE_SIZE == 0); + assert!(offset + bytes.len() <= SIZE); + + self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); + + Ok(()) + } + + pub fn assert_eq(&self, offset: u32, expectation: &[u8]) { + for i in 0..expectation.len() { + assert_eq!(self.mem[offset as usize + i], expectation[i], "Index {}", i); + } + } } impl Default diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index ac6b0ed0f..7529059b6 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -6,20 +6,19 @@ use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Partition { /// The offset into the flash where the partition starts. - pub from: usize, + pub from: u32, /// The offset into the flash where the partition ends. - pub to: usize, + pub to: u32, } impl Partition { /// Create a new partition with the provided range - pub const fn new(from: usize, to: usize) -> Self { + pub const fn new(from: u32, to: u32) -> Self { Self { from, to } } /// Return the size of the partition - #[allow(clippy::len_without_is_empty)] - pub const fn len(&self) -> usize { + pub const fn size(&self) -> u32 { self.to - self.from } From 7e5ead78fed6b9c352852f2619c523f20e7b7fb7 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 08:28:46 +0200 Subject: [PATCH 0816/1575] Remove firmware_len --- embassy-boot/boot/src/firmware_updater.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 93d4a4c12..61c902ed0 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -72,11 +72,6 @@ impl FirmwareUpdater { Self { dfu, state } } - /// Return the length of the DFU area - pub fn firmware_len(&self) -> usize { - self.dfu.len() - } - /// Obtain the current state. /// /// This is useful to check if the bootloader has just done a swap, in order From 05b2b2fb5f0f8d52689057c22dd2fa026d6cc796 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 09:56:40 +0200 Subject: [PATCH 0817/1575] Align platform specific bootloaders --- embassy-boot/nrf/src/lib.rs | 12 ++++++------ embassy-boot/rp/src/lib.rs | 12 ++++++------ embassy-boot/stm32/src/lib.rs | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index a2176f609..d46ed9f36 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -30,20 +30,20 @@ impl Default for BootLoader { let active = unsafe { Partition::new( - &__bootloader_active_start as *const u32 as usize, - &__bootloader_active_end as *const u32 as usize, + &__bootloader_active_start as *const u32 as u32, + &__bootloader_active_end as *const u32 as u32, ) }; let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 0031efa63..c3cb22299 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -66,20 +66,20 @@ impl Default for BootLoader { let active = unsafe { Partition::new( - &__bootloader_active_start as *const u32 as usize, - &__bootloader_active_end as *const u32 as usize, + &__bootloader_active_start as *const u32 as u32, + &__bootloader_active_end as *const u32 as u32, ) }; let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 1f63fcd63..94404697f 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -61,20 +61,20 @@ impl Default for BootLoader { let active = unsafe { Partition::new( - &__bootloader_active_start as *const u32 as usize, - &__bootloader_active_end as *const u32 as usize, + &__bootloader_active_start as *const u32 as u32, + &__bootloader_active_end as *const u32 as u32, ) }; let dfu = unsafe { Partition::new( - &__bootloader_dfu_start as *const u32 as usize, - &__bootloader_dfu_end as *const u32 as usize, + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, ) }; let state = unsafe { Partition::new( - &__bootloader_state_start as *const u32 as usize, - &__bootloader_state_end as *const u32 as usize, + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, ) }; From 95b31cf2db647b8d1779cc3f7439d5c7df98f379 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 10:27:13 +0200 Subject: [PATCH 0818/1575] Remove Drop on Flash and FlashLayout and propage lifetime to region types This allows the user to "split" the FlashRegions struct into each region --- embassy-stm32/build.rs | 11 ++++---- embassy-stm32/src/flash/common.rs | 20 +++------------ embassy-stm32/src/flash/f4.rs | 42 ++++++++++++++++--------------- 3 files changed, 31 insertions(+), 42 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9df5a58d6..61aceed93 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -148,7 +148,8 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { - pub struct #region_type(pub &'static crate::flash::FlashRegion); + #[cfg(flash)] + pub struct #region_type<'d>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>,); }); } @@ -159,11 +160,11 @@ fn main() { let field_name = format_ident!("{}", region_name.to_lowercase()); let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); let field = quote! { - pub #field_name: #field_type + pub #field_name: #field_type<'d> }; let region_name = format_ident!("{}", region_name); let init = quote! { - #field_name: #field_type(&#region_name) + #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}) }; (field, (init, region_name)) @@ -174,15 +175,13 @@ fn main() { flash_regions.extend(quote! { #[cfg(flash)] pub struct FlashLayout<'d> { - _inner: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, #(#fields),* } #[cfg(flash)] impl<'d> FlashLayout<'d> { - pub(crate) const fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { + pub(crate) fn new(mut p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { - _inner: p, #(#inits),* } } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index c48b2f2ea..8235d6f08 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -38,18 +38,6 @@ impl<'d> Flash<'d> { } } -impl Drop for Flash<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} - -impl Drop for FlashLayout<'_> { - fn drop(&mut self) { - unsafe { family::lock() }; - } -} - fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); @@ -177,7 +165,7 @@ impl FlashRegion { foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl crate::_generated::flash_regions::$type_name { + impl crate::_generated::flash_regions::$type_name<'_> { pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { blocking_read(self.0.base, self.0.size, offset, bytes) } @@ -191,11 +179,11 @@ foreach_flash_region! { } } - impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_> { type Error = Error; } - impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -207,7 +195,7 @@ foreach_flash_region! { } } - impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name { + impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { const WRITE_SIZE: usize = $write_size; const ERASE_SIZE: usize = $erase_size; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 2f5b417cc..2ce9df69f 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -45,34 +45,36 @@ mod alt_regions { &ALT_BANK2_REGION3, ]; - pub type AltBank1Region1 = Bank1Region1; - pub type AltBank1Region2 = Bank1Region2; - pub struct AltBank1Region3(&'static FlashRegion); - pub struct AltBank2Region1(&'static FlashRegion); - pub struct AltBank2Region2(&'static FlashRegion); - pub struct AltBank2Region3(&'static FlashRegion); + pub type AltBank1Region1<'d> = Bank1Region1<'d>; + pub type AltBank1Region2<'d> = Bank1Region2<'d>; + pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltFlashLayout<'d> { - _inner: PeripheralRef<'d, FLASH>, - pub bank1_region1: AltBank1Region1, - pub bank1_region2: AltBank1Region2, - pub bank1_region3: AltBank1Region3, - pub bank2_region1: AltBank2Region1, - pub bank2_region2: AltBank2Region2, - pub bank2_region3: AltBank2Region3, + pub bank1_region1: AltBank1Region1<'d>, + pub bank1_region2: AltBank1Region2<'d>, + pub bank1_region3: AltBank1Region3<'d>, + pub bank2_region1: AltBank2Region1<'d>, + pub bank2_region2: AltBank2Region2<'d>, + pub bank2_region3: AltBank2Region3<'d>, } impl<'d> Flash<'d> { pub fn into_alt_regions(self) -> AltFlashLayout<'d> { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + + // SAFETY: We never expose the cloned peripheral references, and their instance is not public. + // Also, all flash region operations are protected with a cs. + let mut p = self.release(); AltFlashLayout { - _inner: self.release(), - bank1_region1: Bank1Region1(&BANK1_REGION1), - bank1_region2: Bank1Region2(&BANK1_REGION2), - bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3), - bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1), - bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2), - bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3), + bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), + bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }), } } } From 57d3d4d58148fefbd6db4770918b52f31ded0124 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 10:29:45 +0200 Subject: [PATCH 0819/1575] Align stm32 bootloader example --- examples/boot/bootloader/stm32/src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index b8027d19a..49c21920b 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -5,7 +5,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, ERASE_SIZE}; +use embassy_stm32::flash::Flash; #[entry] fn main() -> ! { @@ -19,9 +19,10 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); + let mut bl: BootLoader<2048> = BootLoader::default(); let flash = Flash::new(p.FLASH); - let mut flash = BootFlash::new(flash); + let layout = flash.into_regions(); + let mut flash = BootFlash::new(layout.bank1_region); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } From 2a49e11cb0ffd3e0d9a0cc94444f293de523b47f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 5 Apr 2023 10:55:31 +0200 Subject: [PATCH 0820/1575] Align flash examples --- examples/stm32f3/src/bin/flash.rs | 2 +- examples/stm32f4/src/bin/flash.rs | 13 +++++++------ examples/stm32f7/src/bin/flash.rs | 4 ++-- examples/stm32h7/src/bin/flash.rs | 4 ++-- examples/stm32l0/src/bin/flash.rs | 2 +- examples/stm32l1/src/bin/flash.rs | 2 +- examples/stm32wl/src/bin/flash.rs | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index baa7484d0..e40ad4fc0 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 7ea068a42..bd3a7c95e 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -5,7 +5,6 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -13,6 +12,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); + // Once can also call `into_regions()` to get access to NorFlash implementations + // for each of the unique characteristics. let mut f = Flash::new(p.FLASH); // Sector 5 @@ -30,19 +31,19 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(offset, offset + size)); + unwrap!(f.blocking_erase(offset, offset + size)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write( + unwrap!(f.blocking_write( offset, &[ 1, 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, @@ -52,7 +53,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(offset, &mut buf)); + unwrap!(f.blocking_read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index 4a7bca1fa..aabfe8557 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - const ADDR: u32 = 0x8_0000; + const ADDR: u32 = 0x8_0000; // This is the offset into the third region, the absolute address is 4x32K + 128K + 0x8_0000. // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region3; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index ee86bdbf6..7ee9838c9 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -14,12 +14,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - const ADDR: u32 = 0x08_0000; + const ADDR: u32 = 0; // This is the offset into bank 2, the absolute address is 0x8_0000 // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index ffe4fb10b..337425028 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 476ed51a4..38feb0d76 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 2a8880624..e6bc2865c 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -15,7 +15,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH).into_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; From 8290236ed64435453a9c028c95246a86371bd4ce Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 2 Apr 2023 14:11:31 -0500 Subject: [PATCH 0821/1575] executor: Replace unsound critical sections with atomics --- embassy-executor/src/raw/mod.rs | 28 +++++++++++++-------------- embassy-executor/src/raw/run_queue.rs | 18 +++++++++++------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f6c66da5a..bd0cff26b 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -22,7 +22,6 @@ use core::ptr::NonNull; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, Ordering}; -use critical_section::CriticalSection; #[cfg(feature = "integrated-timers")] use embassy_time::driver::{self, AlarmHandle}; #[cfg(feature = "integrated-timers")] @@ -373,11 +372,11 @@ impl SyncExecutor { /// - `task` must be set up to run in this executor. /// - `task` must NOT be already enqueued (in this executor or another one). #[inline(always)] - unsafe fn enqueue(&self, cs: CriticalSection, task: TaskRef) { + unsafe fn enqueue(&self, task: TaskRef) { #[cfg(feature = "rtos-trace")] trace::task_ready_begin(task.as_ptr() as u32); - if self.run_queue.enqueue(cs, task) { + if self.run_queue.enqueue(task) { self.pender.pend(); } } @@ -394,9 +393,7 @@ impl SyncExecutor { #[cfg(feature = "rtos-trace")] trace::task_new(task.as_ptr() as u32); - critical_section::with(|cs| { - self.enqueue(cs, task); - }) + self.enqueue(task); } /// # Safety @@ -552,24 +549,25 @@ impl Executor { /// /// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. pub fn wake_task(task: TaskRef) { - critical_section::with(|cs| { - let header = task.header(); - let state = header.state.load(Ordering::Relaxed); + let header = task.header(); + let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { // If already scheduled, or if not started, if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { - return; + None + } else { + // Mark it as scheduled + Some(state | STATE_RUN_QUEUED) } + }); - // Mark it as scheduled - header.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); - + if res.is_ok() { // We have just marked the task as scheduled, so enqueue it. unsafe { let executor = header.executor.get().unwrap_unchecked(); - executor.enqueue(cs, task); + executor.enqueue(task); } - }) + } } #[cfg(feature = "integrated-timers")] diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 362157535..a88174a0c 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -2,7 +2,6 @@ use core::ptr; use core::ptr::NonNull; use atomic_polyfill::{AtomicPtr, Ordering}; -use critical_section::CriticalSection; use super::{TaskHeader, TaskRef}; @@ -46,11 +45,18 @@ impl RunQueue { /// /// `item` must NOT be already enqueued in any queue. #[inline(always)] - pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: TaskRef) -> bool { - let prev = self.head.load(Ordering::Relaxed); - task.header().run_queue_item.next.store(prev, Ordering::Relaxed); - self.head.store(task.as_ptr() as _, Ordering::Relaxed); - prev.is_null() + pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool { + let mut was_empty = false; + + self.head + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| { + was_empty = prev.is_null(); + task.header().run_queue_item.next.store(prev, Ordering::Relaxed); + Some(task.as_ptr() as *mut _) + }) + .ok(); + + was_empty } /// Empty the queue, then call `on_task` for each task that was in the queue. From 5d9ae3dbdbfe8ba6e1008cd2eadc09743cfc6284 Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Tue, 13 Sep 2022 23:08:03 -0400 Subject: [PATCH 0822/1575] Add implementation of STM32 v1 ADC --- embassy-stm32/src/adc/v1.rs | 277 ++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 8b1378917..923a1d97a 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1 +1,278 @@ +use core::marker::PhantomData; +use embassy_hal_common::into_ref; +use embedded_hal_02::blocking::delay::DelayUs; + +use crate::adc::{AdcPin, Instance}; +use crate::{pac, Peripheral}; + +pub const VDDA_CALIB_MV: u32 = 3300; +pub const VREF_INT: u32 = 1230; + +fn enable() { + critical_section::with(|_| unsafe { + crate::pac::RCC.apb2enr().modify(|reg| reg.set_adcen(true)); + }); +} + +pub enum Resolution { + TwelveBit, + TenBit, + EightBit, + SixBit, +} + +impl Default for Resolution { + fn default() -> Self { + Self::TwelveBit + } +} + +impl Resolution { + fn res(&self) -> pac::adc::vals::Res { + match self { + Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBIT, + Resolution::TenBit => pac::adc::vals::Res::TENBIT, + Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, + Resolution::SixBit => pac::adc::vals::Res::SIXBIT, + } + } + + pub fn to_max_count(&self) -> u32 { + match self { + Resolution::TwelveBit => (1 << 12) - 1, + Resolution::TenBit => (1 << 10) - 1, + Resolution::EightBit => (1 << 8) - 1, + Resolution::SixBit => (1 << 6) - 1, + } + } +} + +pub struct Vbat; +impl AdcPin for Vbat {} +impl super::sealed::AdcPin for Vbat { + fn channel(&self) -> u8 { + 18 + } +} + +pub struct Vref; +impl AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { + fn channel(&self) -> u8 { + 17 + } +} + +pub struct Temperature; +impl AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { + fn channel(&self) -> u8 { + 16 + } +} + +mod sample_time { + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + /// 1.5 ADC clock cycles + Cycles1_5 = 0b000, + + /// 7.5 ADC clock cycles + Cycles7_5 = 0b001, + + /// 13.5 ADC clock cycles + Cycles13_5 = 0b010, + + /// 28.5 ADC clock cycles + Cycles28_5 = 0b011, + + /// 41.5 ADC clock cycles + Cycles41_5 = 0b100, + + /// 55.5 ADC clock cycles + Cycles55_5 = 0b101, + + /// 71.5 ADC clock cycles + Cycles71_5 = 0b110, + + /// 239.5 ADC clock cycles + Cycles239_5 = 0b111, + } + + impl SampleTime { + pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { + match self { + SampleTime::Cycles1_5 => crate::pac::adc::vals::Smp::CYCLES1_5, + SampleTime::Cycles7_5 => crate::pac::adc::vals::Smp::CYCLES7_5, + SampleTime::Cycles13_5 => crate::pac::adc::vals::Smp::CYCLES13_5, + SampleTime::Cycles28_5 => crate::pac::adc::vals::Smp::CYCLES28_5, + SampleTime::Cycles41_5 => crate::pac::adc::vals::Smp::CYCLES41_5, + SampleTime::Cycles55_5 => crate::pac::adc::vals::Smp::CYCLES55_5, + SampleTime::Cycles71_5 => crate::pac::adc::vals::Smp::CYCLES71_5, + SampleTime::Cycles239_5 => crate::pac::adc::vals::Smp::CYCLES239_5, + } + } + } + + impl Default for SampleTime { + fn default() -> Self { + Self::Cycles1_5 + } + } +} + +pub use sample_time::SampleTime; + +pub struct Adc<'d, T: Instance> { + sample_time: SampleTime, + vref_mv: u32, + resolution: Resolution, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Adc<'d, T> { + pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(_peri); + enable(); + + // Delay 1μs when using HSI14 as the ADC clock. + // + // Table 57. ADC characteristics + // tstab = 14 * 1/fadc + delay.delay_us(1); + + let s = Self { + sample_time: Default::default(), + vref_mv: VDDA_CALIB_MV, + resolution: Resolution::default(), + phantom: PhantomData, + }; + s.calibrate(); + s + } + + pub fn enable_vbat(&self, _delay: &mut impl DelayUs) -> Vbat { + // SMP must be ≥ 56 ADC clock cycles when using HSI14. + // + // 6.3.20 Vbat monitoring characteristics + // ts_vbat ≥ 4μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_vbaten(true)); + } + Vbat + } + + pub fn enable_vref(&self, delay: &mut impl DelayUs) -> Vref { + // Table 28. Embedded internal reference voltage + // tstart = 10μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_vrefen(true)); + } + delay.delay_us(10); + Vref + } + + pub fn enable_temperature(&self, delay: &mut impl DelayUs) -> Temperature { + // SMP must be ≥ 56 ADC clock cycles when using HSI14. + // + // 6.3.19 Temperature sensor characteristics + // tstart ≤ 10μs + // ts_temp ≥ 4μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_tsen(true)); + } + delay.delay_us(10); + Temperature + } + + fn calibrate(&self) { + unsafe { + // A.7.1 ADC calibration code example + if T::regs().cr().read().aden() { + T::regs().cr().modify(|reg| reg.set_addis(true)); + } + while T::regs().cr().read().aden() { + // spin + } + T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); + T::regs().cr().modify(|reg| reg.set_adcal(true)); + while T::regs().cr().read().adcal() { + // spin + } + } + } + + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + pub fn set_vref_mv(&mut self, vref_mv: u32) { + self.vref_mv = vref_mv; + } + + pub fn set_resolution(&mut self, resolution: Resolution) { + self.resolution = resolution; + } + + pub fn to_millivolts(&self, sample: u16) -> u16 { + ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 + } + + fn convert(&mut self) -> u16 { + unsafe { + T::regs().isr().modify(|reg| { + reg.set_eoc(true); + reg.set_eosmp(true); + }); + + // A.7.5 Single conversion sequence code example - Software trigger + T::regs().cr().modify(|reg| reg.set_adstart(true)); + while !T::regs().isr().read().eoc() { + // spin + } + + T::regs().dr().read().0 as u16 + } + } + + pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + unsafe { + // A.7.2 ADC enable sequence code example + if T::regs().isr().read().adrdy() { + T::regs().isr().modify(|reg| reg.set_adrdy(true)); + } + T::regs().cr().modify(|reg| reg.set_aden(true)); + while !T::regs().isr().read().adrdy() { + // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration + // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the + // ADEN bit until the ADRDY flag goes high. + T::regs().cr().modify(|reg| reg.set_aden(true)); + } + + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + Self::set_channel_sample_time(pin.channel(), self.sample_time); + T::regs() + .chselr() + .write(|reg| reg.set_chselx(pin.channel() as usize, true)); + + let value = self.convert(); + + // A.7.3 ADC disable code example + T::regs().cr().modify(|reg| reg.set_adstp(true)); + while T::regs().cr().read().adstp() { + // spin + } + T::regs().cr().modify(|reg| reg.set_addis(true)); + while T::regs().cr().read().aden() { + // spin + } + + value + } + } + + unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { + T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); + } +} From 7e9e628eb9e63598a7d15ecae21af5a94ee93be4 Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Tue, 13 Sep 2022 23:10:29 -0400 Subject: [PATCH 0823/1575] Add ADC example for STM32F0 --- examples/stm32f0/src/bin/adc.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 examples/stm32f0/src/bin/adc.rs diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs new file mode 100644 index 000000000..b69bc3c33 --- /dev/null +++ b/examples/stm32f0/src/bin/adc.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_time::{Delay, 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 adc = Adc::new(p.ADC, &mut Delay); + let mut pin = p.PA1; + + loop { + let v = adc.read(&mut pin); + info!("--> {} - {} mV", v, adc.to_millivolts(v)); + Timer::after(Duration::from_millis(100)).await; + } +} From a0b60966106f96e2915d429319a347e239eb1a5f Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 13:03:42 -0400 Subject: [PATCH 0824/1575] Put ADC input pin into analog mode --- embassy-stm32/src/adc/v1.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 923a1d97a..224d17178 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -236,7 +236,10 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + pub fn read

(&mut self, pin: &mut P) -> u16 + where + P: AdcPin + crate::gpio::sealed::Pin, + { unsafe { // A.7.2 ADC enable sequence code example if T::regs().isr().read().adrdy() { @@ -252,6 +255,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); Self::set_channel_sample_time(pin.channel(), self.sample_time); + pin.set_as_analog(); T::regs() .chselr() .write(|reg| reg.set_chselx(pin.channel() as usize, true)); From 511a95124690be253dc9b789abeae1bdea3aa83a Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 14:18:37 -0400 Subject: [PATCH 0825/1575] Differentiate between `read` and `read_internal` for STM32F0 ADC The internal channels (vbat, vref, and temperature) are not real pins and do not have the `set_as_analog` method. They must be read using the `read_internal` method. --- embassy-stm32/src/adc/v1.rs | 120 +++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 224d17178..f787f72ac 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -48,25 +48,33 @@ impl Resolution { } } +pub trait InternalChannel: sealed::InternalChannel {} + +mod sealed { + pub trait InternalChannel { + fn channel(&self) -> u8; + } +} + pub struct Vbat; -impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl InternalChannel for Vbat {} +impl sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl InternalChannel for Vref {} +impl sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl InternalChannel for Temperature {} +impl sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -219,64 +227,64 @@ impl<'d, T: Instance> Adc<'d, T> { ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 } - fn convert(&mut self) -> u16 { - unsafe { - T::regs().isr().modify(|reg| { - reg.set_eoc(true); - reg.set_eosmp(true); - }); - - // A.7.5 Single conversion sequence code example - Software trigger - T::regs().cr().modify(|reg| reg.set_adstart(true)); - while !T::regs().isr().read().eoc() { - // spin - } - - T::regs().dr().read().0 as u16 - } - } - pub fn read

(&mut self, pin: &mut P) -> u16 where P: AdcPin + crate::gpio::sealed::Pin, { + let channel = pin.channel(); unsafe { - // A.7.2 ADC enable sequence code example - if T::regs().isr().read().adrdy() { - T::regs().isr().modify(|reg| reg.set_adrdy(true)); - } - T::regs().cr().modify(|reg| reg.set_aden(true)); - while !T::regs().isr().read().adrdy() { - // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration - // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the - // ADEN bit until the ADRDY flag goes high. - T::regs().cr().modify(|reg| reg.set_aden(true)); - } - - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); - Self::set_channel_sample_time(pin.channel(), self.sample_time); pin.set_as_analog(); - T::regs() - .chselr() - .write(|reg| reg.set_chselx(pin.channel() as usize, true)); - - let value = self.convert(); - - // A.7.3 ADC disable code example - T::regs().cr().modify(|reg| reg.set_adstp(true)); - while T::regs().cr().read().adstp() { - // spin - } - T::regs().cr().modify(|reg| reg.set_addis(true)); - while T::regs().cr().read().aden() { - // spin - } - - value + self.read_channel(channel) } } - unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { - T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + let channel = channel.channel(); + unsafe { + self.read_channel(channel) + } + } + + unsafe fn read_channel(&mut self, channel: u8) -> u16 { + // A.7.2 ADC enable sequence code example + if T::regs().isr().read().adrdy() { + T::regs().isr().modify(|reg| reg.set_adrdy(true)); + } + T::regs().cr().modify(|reg| reg.set_aden(true)); + while !T::regs().isr().read().adrdy() { + // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration + // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the + // ADEN bit until the ADRDY flag goes high. + T::regs().cr().modify(|reg| reg.set_aden(true)); + } + + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().isr().modify(|reg| { + reg.set_eoc(true); + reg.set_eosmp(true); + }); + + // A.7.5 Single conversion sequence code example - Software trigger + T::regs() + .chselr() + .write(|reg| reg.set_chselx(channel as usize, true)); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.sample_time())); + T::regs().cr().modify(|reg| reg.set_adstart(true)); + while !T::regs().isr().read().eoc() { + // spin + } + let value = T::regs().dr().read().0 as u16; + + // A.7.3 ADC disable code example + T::regs().cr().modify(|reg| reg.set_adstp(true)); + while T::regs().cr().read().adstp() { + // spin + } + T::regs().cr().modify(|reg| reg.set_addis(true)); + while T::regs().cr().read().aden() { + // spin + } + + value } } From 28b8ac4b62d952918ddfe143cf6925e1402fc2ce Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 14:23:31 -0400 Subject: [PATCH 0826/1575] Update STM32F0 ADC example to use `read_internal` --- examples/stm32f0/src/bin/adc.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index b69bc3c33..6205596f6 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::Adc; +use embassy_stm32::adc::{Adc, SampleTime}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,11 +14,14 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut adc = Adc::new(p.ADC, &mut Delay); + adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; + let mut vref = adc.enable_temperature(&mut Delay); loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + let r = adc.read_internal(&mut vref); + info!("--> {} - {} mV / vref = {} - {} mV", v, adc.to_millivolts(v), r, adc.to_millivolts(r)); Timer::after(Duration::from_millis(100)).await; } } From f5881054297713f6224d5d81f407f8a6cf591ef4 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 14:31:32 -0500 Subject: [PATCH 0827/1575] wip --- embassy-stm32/src/adc/mod.rs | 11 +-- embassy-stm32/src/adc/resolution.rs | 8 +- embassy-stm32/src/adc/sample_time.rs | 2 +- embassy-stm32/src/adc/v1.rs | 129 +++------------------------ 4 files changed, 20 insertions(+), 130 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ec49dace7..56ecd63ca 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -7,21 +7,18 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(any(adc_f1, adc_v1)))] +#[cfg(not(adc_f1))] mod resolution; -#[cfg(not(adc_v1))] mod sample_time; #[allow(unused)] pub use _version::*; -#[cfg(not(any(adc_f1, adc_v1)))] +#[cfg(not(adc_f1))] pub use resolution::Resolution; -#[cfg(not(adc_v1))] pub use sample_time::SampleTime; use crate::peripherals; -#[cfg(not(adc_v1))] pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, @@ -44,9 +41,9 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v2, adc_v4)))] +#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))] pub trait Instance: sealed::Instance + crate::Peripheral

{} -#[cfg(any(adc_f1, adc_v2, adc_v4))] +#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))] pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 62b52a46c..67fb9b8c0 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,4 +1,4 @@ -#[cfg(any(adc_v2, adc_v3, adc_g0))] +#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { TwelveBit, @@ -19,7 +19,7 @@ pub enum Resolution { impl Default for Resolution { fn default() -> Self { - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] { Self::TwelveBit } @@ -40,7 +40,7 @@ impl From for crate::pac::adc::vals::Res { Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } @@ -56,7 +56,7 @@ impl Resolution { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, Resolution::EightBit => (1 << 8) - 1, - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] Resolution::SixBit => (1 << 6) - 1, } } diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index bc5fb1d6f..0faa1e3c0 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -25,7 +25,7 @@ macro_rules! impl_sample_time { }; } -#[cfg(adc_f1)] +#[cfg(any(adc_f1, adc_v1))] impl_sample_time!( "1.5", Cycles1_5, diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index f787f72ac..0fdb95562 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,10 +1,8 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance}; -use crate::{pac, Peripheral}; +use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -15,39 +13,6 @@ fn enable() { }); } -pub enum Resolution { - TwelveBit, - TenBit, - EightBit, - SixBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::TwelveBit - } -} - -impl Resolution { - fn res(&self) -> pac::adc::vals::Res { - match self { - Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBIT, - Resolution::TenBit => pac::adc::vals::Res::TENBIT, - Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, - Resolution::SixBit => pac::adc::vals::Res::SIXBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - Resolution::SixBit => (1 << 6) - 1, - } - } -} - pub trait InternalChannel: sealed::InternalChannel {} mod sealed { @@ -80,68 +45,9 @@ impl sealed::InternalChannel for Temperature { } } -mod sample_time { - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5 = 0b000, - - /// 7.5 ADC clock cycles - Cycles7_5 = 0b001, - - /// 13.5 ADC clock cycles - Cycles13_5 = 0b010, - - /// 28.5 ADC clock cycles - Cycles28_5 = 0b011, - - /// 41.5 ADC clock cycles - Cycles41_5 = 0b100, - - /// 55.5 ADC clock cycles - Cycles55_5 = 0b101, - - /// 71.5 ADC clock cycles - Cycles71_5 = 0b110, - - /// 239.5 ADC clock cycles - Cycles239_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { - match self { - SampleTime::Cycles1_5 => crate::pac::adc::vals::Smp::CYCLES1_5, - SampleTime::Cycles7_5 => crate::pac::adc::vals::Smp::CYCLES7_5, - SampleTime::Cycles13_5 => crate::pac::adc::vals::Smp::CYCLES13_5, - SampleTime::Cycles28_5 => crate::pac::adc::vals::Smp::CYCLES28_5, - SampleTime::Cycles41_5 => crate::pac::adc::vals::Smp::CYCLES41_5, - SampleTime::Cycles55_5 => crate::pac::adc::vals::Smp::CYCLES55_5, - SampleTime::Cycles71_5 => crate::pac::adc::vals::Smp::CYCLES71_5, - SampleTime::Cycles239_5 => crate::pac::adc::vals::Smp::CYCLES239_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles1_5 - } - } -} - -pub use sample_time::SampleTime; - -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - vref_mv: u32, - resolution: Resolution, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); enable(); // Delay 1μs when using HSI14 as the ADC clock. @@ -151,10 +57,8 @@ impl<'d, T: Instance> Adc<'d, T> { delay.delay_us(1); let s = Self { + adc, sample_time: Default::default(), - vref_mv: VDDA_CALIB_MV, - resolution: Resolution::default(), - phantom: PhantomData, }; s.calibrate(); s @@ -215,16 +119,10 @@ impl<'d, T: Instance> Adc<'d, T> { self.sample_time = sample_time; } - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; - } - - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 + unsafe { + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } } pub fn read

(&mut self, pin: &mut P) -> u16 @@ -240,9 +138,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { let channel = channel.channel(); - unsafe { - self.read_channel(channel) - } + unsafe { self.read_channel(channel) } } unsafe fn read_channel(&mut self, channel: u8) -> u16 { @@ -258,17 +154,14 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cr().modify(|reg| reg.set_aden(true)); } - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); T::regs().isr().modify(|reg| { reg.set_eoc(true); reg.set_eosmp(true); }); // A.7.5 Single conversion sequence code example - Software trigger - T::regs() - .chselr() - .write(|reg| reg.set_chselx(channel as usize, true)); - T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.sample_time())); + T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true)); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); T::regs().cr().modify(|reg| reg.set_adstart(true)); while !T::regs().isr().read().eoc() { // spin From efd9e18321c258a188fa675804d59346a4c11cc2 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 15:12:27 -0500 Subject: [PATCH 0828/1575] Fix example --- examples/stm32f0/src/bin/adc.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index 6205596f6..c639299d2 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -16,12 +16,21 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC, &mut Delay); adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; - let mut vref = adc.enable_temperature(&mut Delay); + + let mut vrefint = adc.enable_vref(&mut Delay); + let vrefint_sample = adc.read_internal(&mut vrefint); + let convert_to_millivolts = |sample| { + // FIXME: use proper datasheet and value + // From http://www.st.com/resource/en/datasheet/CD00161566.pdf + // 5.3.4 Embedded reference voltage + const VREFINT_MV: u32 = 1200; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; loop { let v = adc.read(&mut pin); - let r = adc.read_internal(&mut vref); - info!("--> {} - {} mV / vref = {} - {} mV", v, adc.to_millivolts(v), r, adc.to_millivolts(r)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } From 37d8f2e51256403cc4839c3b45e5c2253e3a09f1 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 15:28:42 -0500 Subject: [PATCH 0829/1575] Properly enable and reset adc --- embassy-stm32/src/adc/v1.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 0fdb95562..a699d8f74 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -7,12 +7,6 @@ use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; -fn enable() { - critical_section::with(|_| unsafe { - crate::pac::RCC.apb2enr().modify(|reg| reg.set_adcen(true)); - }); -} - pub trait InternalChannel: sealed::InternalChannel {} mod sealed { @@ -48,7 +42,8 @@ impl sealed::InternalChannel for Temperature { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(adc); - enable(); + T::enable(); + T::reset(); // Delay 1μs when using HSI14 as the ADC clock. // From 20e7b5e2965f718aa1a0ece6009356ba50d2d780 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:11:21 -0500 Subject: [PATCH 0830/1575] InternalChannel --- embassy-stm32/src/adc/v1.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index a699d8f74..5f0f8c141 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,39 +1,32 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::Peripheral; +use crate::peripherals::ADC1; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; -pub trait InternalChannel: sealed::InternalChannel {} - -mod sealed { - pub trait InternalChannel { - fn channel(&self) -> u8; - } -} - pub struct Vbat; -impl InternalChannel for Vbat {} -impl sealed::InternalChannel for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl InternalChannel for Vref {} -impl sealed::InternalChannel for Vref { +impl InternalChannel for Vref {} +impl super::sealed::::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl InternalChannel for Temperature {} -impl sealed::InternalChannel for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } From 7c53ebd5766fc75dbb5ddd1953b5bff13f1cd853 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:28:28 -0500 Subject: [PATCH 0831/1575] Fix example reference voltage --- examples/stm32f0/src/bin/adc.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index c639299d2..8ed9f98f8 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -20,10 +20,9 @@ async fn main(_spawner: Spawner) { let mut vrefint = adc.enable_vref(&mut Delay); let vrefint_sample = adc.read_internal(&mut vrefint); let convert_to_millivolts = |sample| { - // FIXME: use proper datasheet and value - // From http://www.st.com/resource/en/datasheet/CD00161566.pdf - // 5.3.4 Embedded reference voltage - const VREFINT_MV: u32 = 1200; // mV + // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf + // 6.3.4 Embedded reference voltage + const VREFINT_MV: u32 = 1230; // mV (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; From 92e96bd601e8e663114b1efccc4a1e8d309332d9 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:38:06 -0500 Subject: [PATCH 0832/1575] Fix typo --- embassy-stm32/src/adc/v1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 5f0f8c141..17114732d 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -2,8 +2,8 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; -use crate::Peripheral; use crate::peripherals::ADC1; +use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -18,7 +18,7 @@ impl super::sealed::InternalChannel for Vbat { pub struct Vref; impl InternalChannel for Vref {} -impl super::sealed::::InternalChannel for Vref { +impl super::sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } From 0ef419bee4afc5a984cab3d5f16e1f1382fa6bbf Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:52:32 -0500 Subject: [PATCH 0833/1575] Change ADC1 to ADC --- embassy-stm32/src/adc/v1.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 17114732d..82a8c3efb 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -2,31 +2,31 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; -use crate::peripherals::ADC1; +use crate::peripherals::ADC; use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; pub struct Vbat; -impl InternalChannel for Vbat {} -impl super::sealed::InternalChannel for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl InternalChannel for Vref {} -impl super::sealed::InternalChannel for Vref { +impl InternalChannel for Vref {} +impl super::sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl InternalChannel for Temperature {} -impl super::sealed::InternalChannel for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } From 76772683191be15d32604ec5dd46fb5eb3949de8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 5 Apr 2023 17:50:23 -0500 Subject: [PATCH 0834/1575] stm32/pwm: cleanup and fix complementary pwm --- embassy-stm32/src/pwm/complementary_pwm.rs | 47 ++++++---------------- embassy-stm32/src/pwm/mod.rs | 18 +++++++++ 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index b8761724a..e4de1fb7a 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -1,7 +1,9 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; +use stm32_metapac::timer::vals::Ckd; +use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; @@ -9,39 +11,13 @@ use crate::gpio::AnyPin; use crate::time::Hertz; use crate::Peripheral; -pub struct Ch1; -pub struct Ch2; -pub struct Ch3; -pub struct Ch4; - -pub struct PwmPin<'d, Perip, Channel> { - _pin: PeripheralRef<'d, AnyPin>, - phantom: PhantomData<(Perip, Channel)>, -} - pub struct ComplementaryPwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, phantom: PhantomData<(Perip, Channel)>, } -macro_rules! channel_impl { +macro_rules! complementary_channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { - into_ref!(pin); - critical_section::with(|_| unsafe { - pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); - #[cfg(gpio_v2)] - pin.set_speed(crate::gpio::Speed::VeryHigh); - }); - PwmPin { - _pin: pin.map_into(), - phantom: PhantomData, - } - } - } - impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); @@ -60,10 +36,10 @@ macro_rules! channel_impl { }; } -channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); -channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); -channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); -channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); +complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); +complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); +complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); +complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); pub struct ComplementaryPwm<'d, T> { inner: PeripheralRef<'d, T>, @@ -114,11 +90,13 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { pub fn enable(&mut self, channel: Channel) { unsafe { self.inner.enable_channel(channel, true); + self.inner.enable_complementary_channel(channel, true); } } pub fn disable(&mut self, channel: Channel) { unsafe { + self.inner.enable_complementary_channel(channel, false); self.inner.enable_channel(channel, false); } } @@ -136,9 +114,10 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { unsafe { self.inner.set_compare_value(channel, duty) } } - /* - set the value of the dead-time register - */ + pub fn set_dead_time_clock_division(&mut self, value: Ckd) { + unsafe { self.inner.set_dead_time_clock_division(value) } + } + pub fn set_dead_time_value(&mut self, value: u8) { unsafe { self.inner.set_dead_time_value(value) } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 6f3c12665..0bef07089 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,6 +1,8 @@ pub mod complementary_pwm; pub mod simple_pwm; +use stm32_metapac::timer::vals::Ckd; + #[cfg(feature = "unstable-pac")] pub mod low_level { pub use super::sealed::*; @@ -69,7 +71,11 @@ pub(crate) mod sealed { } pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + unsafe fn set_dead_time_clock_division(&mut self, value: Ckd); + unsafe fn set_dead_time_value(&mut self, value: u8); + + unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); } pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { @@ -222,10 +228,22 @@ foreach_interrupt! { } impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + unsafe fn set_dead_time_clock_division(&mut self, value: Ckd) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); + } + unsafe fn set_dead_time_value(&mut self, value: u8) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); } + + unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccne(channel.raw(), enable)); + } } impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { From 9f1dac3f5daf61a2d56679d00e018e5e3557c7e5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 5 Apr 2023 18:07:07 -0500 Subject: [PATCH 0835/1575] stm32/pwm: add complementary pwm example --- embassy-stm32/src/pwm/complementary_pwm.rs | 2 +- examples/stm32f4/src/bin/pwm_complementary.rs | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f4/src/bin/pwm_complementary.rs diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index e4de1fb7a..13edfbaa3 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use stm32_metapac::timer::vals::Ckd; +pub use stm32_metapac::timer::vals::Ckd; use super::simple_pwm::*; use super::*; diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs new file mode 100644 index 000000000..795c38e48 --- /dev/null +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -0,0 +1,77 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin}; +use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::pwm::Channel; +use embassy_stm32::time::khz; +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 ch1 = PwmPin::new_ch1(p.PE9); + let ch1n = ComplementaryPwmPin::new_ch1(p.PA7); + let mut pwm = ComplementaryPwm::new( + p.TIM1, + Some(ch1), + Some(ch1n), + None, + None, + None, + None, + None, + None, + khz(10), + ); + + /* + Dead-time = T_clk * T_dts * T_dtg + + T_dts: + This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the + dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters + (ETR, TIx), + 00: tDTS=tCK_INT + 01: tDTS=2*tCK_INT + 10: tDTS=4*tCK_INT + + T_dtg: + This bit-field defines the duration of the dead-time inserted between the complementary + outputs. DT correspond to this duration. + DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS. + DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS. + DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS. + DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS. + Example if TDTS=125ns (8MHz), dead-time possible values are: + 0 to 15875 ns by 125 ns steps, + 16 us to 31750 ns by 250 ns steps, + 32 us to 63us by 1 us steps, + 64 us to 126 us by 2 us steps + */ + pwm.set_dead_time_clock_division(Ckd::DIV1); + pwm.set_dead_time_value(0); + + let max = pwm.get_max_duty(); + pwm.enable(Channel::Ch1); + + info!("PWM initialized"); + info!("PWM max duty {}", max); + + loop { + pwm.set_duty(Channel::Ch1, 0); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 4); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max / 2); + Timer::after(Duration::from_millis(300)).await; + pwm.set_duty(Channel::Ch1, max - 1); + Timer::after(Duration::from_millis(300)).await; + } +} From 31ef783ac1add908c4c4506d3ce4e5f6ad9bea56 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 5 Apr 2023 18:18:05 -0500 Subject: [PATCH 0836/1575] stm32/pwm: fix unused import --- examples/stm32f4/src/bin/pwm_complementary.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 795c38e48..6e17f3fd3 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin}; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::pwm::simple_pwm::PwmPin; use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; use embassy_time::{Duration, Timer}; From 289762c0efc43cf8357309bb98b28a5959b63d26 Mon Sep 17 00:00:00 2001 From: Eric Yanush <> Date: Mon, 20 Mar 2023 14:19:14 -0600 Subject: [PATCH 0837/1575] Add initial setup of async can for STM32 --- embassy-stm32/src/can/bxcan.rs | 157 ++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index bd92b35a0..7e5b1cfaa 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -1,9 +1,13 @@ +use core::future::poll_fn; use core::ops::{Deref, DerefMut}; +use core::task::Poll; pub use bxcan; +use bxcan::Frame; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::sealed::AFType; +use crate::interrupt::{Interrupt, InterruptExt}; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -39,8 +43,12 @@ impl<'d, T: Instance> Can<'d, T> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, + tx_irq: impl Peripheral

+ 'd, + rx0_irq: impl Peripheral

+ 'd, + rx1_irq: impl Peripheral

+ 'd, + sce_irq: impl Peripheral

+ 'd, ) -> Self { - into_ref!(peri, rx, tx); + into_ref!(peri, rx, tx, tx_irq, rx0_irq, rx1_irq, sce_irq); unsafe { rx.set_as_af(rx.af_num(), AFType::Input); @@ -50,10 +58,75 @@ impl<'d, T: Instance> Can<'d, T> { T::enable(); T::reset(); + tx_irq.unpend(); + tx_irq.set_handler(Self::tx_interrupt); + tx_irq.enable(); + + rx0_irq.unpend(); + rx0_irq.set_handler(Self::rx0_interrupt); + rx0_irq.enable(); + + rx1_irq.unpend(); + rx1_irq.set_handler(Self::rx1_interrupt); + rx1_irq.enable(); + + sce_irq.unpend(); + sce_irq.set_handler(Self::sce_interrupt); + sce_irq.enable(); + Self { can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(), } } + + pub async fn transmit_async(&mut self, frame: &Frame) { + defmt::info!("Staring async frame transmission"); + let tx_status = self.queue_transmit(frame).await; + self.wait_transission(tx_status.mailbox()).await; + } + + async fn queue_transmit(&mut self, frame: &Frame) -> bxcan::TransmitStatus { + poll_fn(|cx| { + if let Ok(status) = self.can.transmit(frame) { + defmt::info!("Frame queued successfully in mb{}", status.mailbox()); + return Poll::Ready(status); + } + defmt::info!("Mailboxes full, waiting to queue"); + T::state().tx_waker.register(cx.waker()); + Poll::Pending + }) + .await + } + + async fn wait_transission(&mut self, mb: bxcan::Mailbox) { + poll_fn(|cx| unsafe { + defmt::info!("Waiting for tx to complete"); + if T::regs().tsr().read().tme(mb.index()) { + defmt::info!("TX complete for mb {}", mb); + return Poll::Ready(()); + } + defmt::info!("TX not complete, waiting for signal on mb {}", mb); + T::state().tx_waker.register(cx.waker()); + Poll::Pending + }) + .await; + } + + unsafe fn tx_interrupt(_: *mut ()) { + defmt::info!("bxCAN TX interrupt fired!"); + T::regs().tsr().write(|v| { + v.set_rqcp(0, true); + v.set_rqcp(1, true); + v.set_rqcp(2, true); + }); + T::state().tx_waker.wake(); + } + + unsafe fn rx0_interrupt(_: *mut ()) {} + + unsafe fn rx1_interrupt(_: *mut ()) {} + + unsafe fn sce_interrupt(_: *mut ()) {} } impl<'d, T: Instance> Drop for Can<'d, T> { @@ -80,14 +153,51 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { } pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + pub struct State { + pub tx_waker: AtomicWaker, + pub rx0_waker: AtomicWaker, + pub rx1_waker: AtomicWaker, + pub sce_waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + tx_waker: AtomicWaker::new(), + rx0_waker: AtomicWaker::new(), + rx1_waker: AtomicWaker::new(), + sce_waker: AtomicWaker::new(), + } + } + } + pub trait Instance { const REGISTERS: *mut bxcan::RegisterBlock; fn regs() -> &'static crate::pac::can::Can; + fn state() -> &'static State; } } -pub trait Instance: sealed::Instance + RccPeripheral {} +pub trait TXInstance { + type TXInterrupt: crate::interrupt::Interrupt; +} + +pub trait RX0Instance { + type RX0Interrupt: crate::interrupt::Interrupt; +} + +pub trait RX1Instance { + type RX1Interrupt: crate::interrupt::Interrupt; +} + +pub trait SCEInstance { + type SCEInterrupt: crate::interrupt::Interrupt; +} + +pub trait InterruptableInstance: TXInstance + RX0Instance + RX1Instance + SCEInstance {} +pub trait Instance: sealed::Instance + RccPeripheral + InterruptableInstance + 'static {} pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>); @@ -103,10 +213,39 @@ foreach_peripheral!( fn regs() -> &'static crate::pac::can::Can { &crate::pac::$inst } + + fn state() -> &'static sealed::State { + static STATE: sealed::State = sealed::State::new(); + &STATE + } } impl Instance for peripherals::$inst {} + foreach_interrupt!( + ($inst,can,CAN,TX,$irq:ident) => { + impl TXInstance for peripherals::$inst { + type TXInterrupt = crate::interrupt::$irq; + } + }; + ($inst,can,CAN,RX0,$irq:ident) => { + impl RX0Instance for peripherals::$inst { + type RX0Interrupt = crate::interrupt::$irq; + } + }; + ($inst,can,CAN,RX1,$irq:ident) => { + impl RX1Instance for peripherals::$inst { + type RX1Interrupt = crate::interrupt::$irq; + } + }; + ($inst,can,CAN,SCE,$irq:ident) => { + impl SCEInstance for peripherals::$inst { + type SCEInterrupt = crate::interrupt::$irq; + } + }; + ); + + impl InterruptableInstance for peripherals::$inst {} }; ); @@ -147,3 +286,17 @@ foreach_peripheral!( pin_trait!(RxPin, Instance); pin_trait!(TxPin, Instance); + +trait Index { + fn index(&self) -> usize; +} + +impl Index for bxcan::Mailbox { + fn index(&self) -> usize { + match self { + bxcan::Mailbox::Mailbox0 => 0, + bxcan::Mailbox::Mailbox1 => 1, + bxcan::Mailbox::Mailbox2 => 2, + } + } +} From 9876571887845f60958c148308ae8a59433cd809 Mon Sep 17 00:00:00 2001 From: Eric Yanush <> Date: Mon, 20 Mar 2023 14:25:15 -0600 Subject: [PATCH 0838/1575] Strip out debug messages... oops --- embassy-stm32/src/can/bxcan.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 7e5b1cfaa..521049ecc 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -80,7 +80,6 @@ impl<'d, T: Instance> Can<'d, T> { } pub async fn transmit_async(&mut self, frame: &Frame) { - defmt::info!("Staring async frame transmission"); let tx_status = self.queue_transmit(frame).await; self.wait_transission(tx_status.mailbox()).await; } @@ -88,10 +87,8 @@ impl<'d, T: Instance> Can<'d, T> { async fn queue_transmit(&mut self, frame: &Frame) -> bxcan::TransmitStatus { poll_fn(|cx| { if let Ok(status) = self.can.transmit(frame) { - defmt::info!("Frame queued successfully in mb{}", status.mailbox()); return Poll::Ready(status); } - defmt::info!("Mailboxes full, waiting to queue"); T::state().tx_waker.register(cx.waker()); Poll::Pending }) @@ -100,12 +97,9 @@ impl<'d, T: Instance> Can<'d, T> { async fn wait_transission(&mut self, mb: bxcan::Mailbox) { poll_fn(|cx| unsafe { - defmt::info!("Waiting for tx to complete"); if T::regs().tsr().read().tme(mb.index()) { - defmt::info!("TX complete for mb {}", mb); return Poll::Ready(()); } - defmt::info!("TX not complete, waiting for signal on mb {}", mb); T::state().tx_waker.register(cx.waker()); Poll::Pending }) @@ -113,7 +107,6 @@ impl<'d, T: Instance> Can<'d, T> { } unsafe fn tx_interrupt(_: *mut ()) { - defmt::info!("bxCAN TX interrupt fired!"); T::regs().tsr().write(|v| { v.set_rqcp(0, true); v.set_rqcp(1, true); From 8d7abeb06fbe3e19db3cae3f5220725969ecbb81 Mon Sep 17 00:00:00 2001 From: Eric Yanush <> Date: Thu, 6 Apr 2023 08:20:34 -0600 Subject: [PATCH 0839/1575] Round out the async fns for can --- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/can/bxcan.rs | 252 ++++++++++++++++++++++++++++++--- 2 files changed, 237 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 504caacb2..5e45db360 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,6 +68,7 @@ stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } +heapless = { version = "0.7.5", default-features = false } [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 521049ecc..734efdc02 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -3,18 +3,39 @@ use core::ops::{Deref, DerefMut}; use core::task::Poll; pub use bxcan; -use bxcan::Frame; +use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::sealed::AFType; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::InterruptExt; +use crate::pac::can::vals::{Lec, RirIde}; use crate::rcc::RccPeripheral; +use crate::time::Hertz; use crate::{peripherals, Peripheral}; pub struct Can<'d, T: Instance> { can: bxcan::Can>, } +#[derive(Debug)] +pub enum BusError { + Stuff, + Form, + Acknowledge, + BitRecessive, + BitDominant, + Crc, + Software, + BusOff, + BusPassive, + BusWarning, +} + +pub enum FrameOrError { + Frame(Frame), + Error(BusError), +} + impl<'d, T: Instance> Can<'d, T> { /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. pub fn new( @@ -74,12 +95,16 @@ impl<'d, T: Instance> Can<'d, T> { sce_irq.set_handler(Self::sce_interrupt); sce_irq.enable(); - Self { - can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(), - } + let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); + Self { can } } - pub async fn transmit_async(&mut self, frame: &Frame) { + pub fn set_bitrate(&mut self, bitrate: u32) { + let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); + self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); + } + + pub async fn transmit(&mut self, frame: &Frame) { let tx_status = self.queue_transmit(frame).await; self.wait_transission(tx_status.mailbox()).await; } @@ -95,7 +120,7 @@ impl<'d, T: Instance> Can<'d, T> { .await } - async fn wait_transission(&mut self, mb: bxcan::Mailbox) { + async fn wait_transission(&self, mb: bxcan::Mailbox) { poll_fn(|cx| unsafe { if T::regs().tsr().read().tme(mb.index()) { return Poll::Ready(()); @@ -106,6 +131,45 @@ impl<'d, T: Instance> Can<'d, T> { .await; } + pub async fn receive_frame_or_error(&mut self) -> FrameOrError { + poll_fn(|cx| { + if let Some(frame) = T::state().rx_queue.dequeue() { + return Poll::Ready(FrameOrError::Frame(frame)); + } else if let Some(err) = self.curr_error() { + return Poll::Ready(FrameOrError::Error(err)); + } + T::state().rx_waker.register(cx.waker()); + T::state().err_waker.register(cx.waker()); + Poll::Pending + }) + .await + } + + fn curr_error(&self) -> Option { + let err = unsafe { T::regs().esr().read() }; + if err.boff() { + return Some(BusError::BusOff); + } else if err.epvf() { + return Some(BusError::BusPassive); + } else if err.ewgf() { + return Some(BusError::BusWarning); + } else if let Some(err) = err.lec().into_bus_err() { + return Some(err); + } + None + } + + unsafe fn sce_interrupt(_: *mut ()) { + let msr = T::regs().msr(); + let msr_val = msr.read(); + + if msr_val.erri() { + msr.modify(|v| v.set_erri(true)); + T::state().err_waker.wake(); + return; + } + } + unsafe fn tx_interrupt(_: *mut ()) { T::regs().tsr().write(|v| { v.set_rqcp(0, true); @@ -115,11 +179,146 @@ impl<'d, T: Instance> Can<'d, T> { T::state().tx_waker.wake(); } - unsafe fn rx0_interrupt(_: *mut ()) {} + unsafe fn rx0_interrupt(_: *mut ()) { + Self::receive_fifo(RxFifo::Fifo0); + } - unsafe fn rx1_interrupt(_: *mut ()) {} + unsafe fn rx1_interrupt(_: *mut ()) { + Self::receive_fifo(RxFifo::Fifi1); + } - unsafe fn sce_interrupt(_: *mut ()) {} + unsafe fn receive_fifo(fifo: RxFifo) { + let state = T::state(); + let regs = T::regs(); + let fifo_idx = match fifo { + RxFifo::Fifo0 => 0usize, + RxFifo::Fifi1 => 1usize, + }; + let rfr = regs.rfr(fifo_idx); + let fifo = regs.rx(fifo_idx); + + // If there are no pending messages, there is nothing to do + if rfr.read().fmp() == 0 { + return; + } + + let rir = fifo.rir().read(); + let id = if rir.ide() == RirIde::STANDARD { + Id::from(StandardId::new_unchecked(rir.stid())) + } else { + Id::from(ExtendedId::new_unchecked(rir.exid())) + }; + let data_len = fifo.rdtr().read().dlc() as usize; + let mut data: [u8; 8] = [0; 8]; + data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); + + let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); + + rfr.modify(|v| v.set_rfom(true)); + + match state.rx_queue.enqueue(frame) { + Ok(_) => {} + Err(_) => defmt::error!("RX queue overflow"), + } + state.rx_waker.wake(); + } + + pub fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option { + const BS1_MAX: u8 = 16; + const BS2_MAX: u8 = 8; + const MAX_SAMPLE_POINT_PERMILL: u16 = 900; + + let periph_clock = periph_clock.0; + + if can_bitrate < 1000 { + return None; + } + + // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG + // CAN in Automation, 2003 + // + // According to the source, optimal quanta per bit are: + // Bitrate Optimal Maximum + // 1000 kbps 8 10 + // 500 kbps 16 17 + // 250 kbps 16 17 + // 125 kbps 16 17 + let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 }; + + // Computing (prescaler * BS): + // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual + // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified + // let: + // BS = 1 + BS1 + BS2 -- Number of time quanta per bit + // PRESCALER_BS = PRESCALER * BS + // ==> + // PRESCALER_BS = PCLK / BITRATE + let prescaler_bs = periph_clock / can_bitrate; + + // Searching for such prescaler value so that the number of quanta per bit is highest. + let mut bs1_bs2_sum = max_quanta_per_bit - 1; + while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { + if bs1_bs2_sum <= 2 { + return None; // No solution + } + bs1_bs2_sum -= 1; + } + + let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; + if (prescaler < 1) || (prescaler > 1024) { + return None; // No solution + } + + // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. + // We need to find such values so that the sample point is as close as possible to the optimal value, + // which is 87.5%, which is 7/8. + // + // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) + // {{bs2 -> (1 + bs1)/7}} + // + // Hence: + // bs2 = (1 + bs1) / 7 + // bs1 = (7 * bs1_bs2_sum - 1) / 8 + // + // Sample point location can be computed as follows: + // Sample point location = (1 + bs1) / (1 + bs1 + bs2) + // + // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: + // - With rounding to nearest + // - With rounding to zero + let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first + let mut bs2 = bs1_bs2_sum - bs1; + assert!(bs1_bs2_sum > bs1); + + let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; + if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { + // Nope, too far; now rounding to zero + bs1 = (7 * bs1_bs2_sum - 1) / 8; + bs2 = bs1_bs2_sum - bs1; + } + + // Check is BS1 and BS2 are in range + if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { + return None; + } + + // Check if final bitrate matches the requested + if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { + return None; + } + + // One is recommended by DS-015, CANOpen, and DeviceNet + let sjw = 1; + + // Pack into BTR register values + Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1)) + } +} + +enum RxFifo { + Fifo0, + Fifi1, } impl<'d, T: Instance> Drop for Can<'d, T> { @@ -147,20 +346,22 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { pub(crate) mod sealed { use embassy_sync::waitqueue::AtomicWaker; + use heapless::mpmc::Q8; + pub struct State { pub tx_waker: AtomicWaker, - pub rx0_waker: AtomicWaker, - pub rx1_waker: AtomicWaker, - pub sce_waker: AtomicWaker, + pub rx_waker: AtomicWaker, + pub err_waker: AtomicWaker, + pub rx_queue: Q8, } impl State { pub const fn new() -> Self { Self { tx_waker: AtomicWaker::new(), - rx0_waker: AtomicWaker::new(), - rx1_waker: AtomicWaker::new(), - sce_waker: AtomicWaker::new(), + rx_waker: AtomicWaker::new(), + err_waker: AtomicWaker::new(), + rx_queue: Q8::new(), } } } @@ -293,3 +494,22 @@ impl Index for bxcan::Mailbox { } } } + +trait IntoBusError { + fn into_bus_err(self) -> Option; +} + +impl IntoBusError for Lec { + fn into_bus_err(self) -> Option { + match self { + Lec::STUFF => Some(BusError::Stuff), + Lec::FORM => Some(BusError::Form), + Lec::ACK => Some(BusError::Acknowledge), + Lec::BITRECESSIVE => Some(BusError::BitRecessive), + Lec::BITDOMINANT => Some(BusError::BitDominant), + Lec::CRC => Some(BusError::Crc), + Lec::CUSTOM => Some(BusError::Software), + _ => None, + } + } +} From 9f28d8097704f51fb7d7dcc8d459ce86aaf07eff Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 18:50:53 +0200 Subject: [PATCH 0840/1575] stm32/usb: add support for 32bit usbram. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/usb/usb.rs | 131 +++++++++++++++++++++++------------ 2 files changed, 87 insertions(+), 48 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 504caacb2..da1dc24bf 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -60,7 +60,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "3" +stm32-metapac = "4" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "3", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "4", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 0355c5f14..e6ee39549 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -12,22 +12,29 @@ use embassy_usb_driver as driver; use embassy_usb_driver::{ Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; -use pac::common::{Reg, RW}; -use pac::usb::vals::{EpType, Stat}; use super::{DmPin, DpPin, Instance}; use crate::gpio::sealed::AFType; use crate::interrupt::InterruptExt; use crate::pac::usb::regs; +use crate::pac::usb::vals::{EpType, Stat}; +use crate::pac::USBRAM; use crate::rcc::sealed::RccPeripheral; -use crate::{pac, Peripheral}; +use crate::Peripheral; const EP_COUNT: usize = 8; -#[cfg(any(usb_v1_x1, usb_v1_x2))] -const EP_MEMORY_SIZE: usize = 512; -#[cfg(not(any(usb_v1_x1, usb_v1_x2)))] -const EP_MEMORY_SIZE: usize = 1024; +#[cfg(any(usbram_16x1_512, usbram_16x2_512))] +const USBRAM_SIZE: usize = 512; +#[cfg(usbram_16x2_1024)] +const USBRAM_SIZE: usize = 1024; +#[cfg(usbram_32_2048)] +const USBRAM_SIZE: usize = 2048; + +#[cfg(not(usbram_32_2048))] +const USBRAM_ALIGN: usize = 2; +#[cfg(usbram_32_2048)] +const USBRAM_ALIGN: usize = 4; const NEW_AW: AtomicWaker = AtomicWaker::new(); static BUS_WAKER: AtomicWaker = NEW_AW; @@ -57,25 +64,60 @@ fn invariant(mut r: regs::Epr) -> regs::Epr { r } +fn align_len_up(len: u16) -> u16 { + ((len as usize + USBRAM_ALIGN - 1) / USBRAM_ALIGN * USBRAM_ALIGN) as u16 +} + // Returns (actual_len, len_bits) fn calc_out_len(len: u16) -> (u16, u16) { match len { - 2..=62 => ((len + 1) / 2 * 2, ((len + 1) / 2) << 10), - 63..=480 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000), + // NOTE: this could be 2..=62 with 16bit USBRAM, but not with 32bit. Limit it to 60 for simplicity. + 2..=60 => (align_len_up(len), align_len_up(len) / 2 << 10), + 61..=1024 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000), _ => panic!("invalid OUT length {}", len), } } -fn ep_in_addr(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 0) + +#[cfg(not(usbram_32_2048))] +mod btable { + use super::*; + + pub(super) unsafe fn write_in(index: usize, addr: u16) { + USBRAM.mem(index * 4 + 0).write_value(addr); + } + + pub(super) unsafe fn write_in_len(index: usize, _addr: u16, len: u16) { + USBRAM.mem(index * 4 + 1).write_value(len); + } + + pub(super) unsafe fn write_out(index: usize, addr: u16, max_len_bits: u16) { + USBRAM.mem(index * 4 + 2).write_value(addr); + USBRAM.mem(index * 4 + 3).write_value(max_len_bits); + } + + pub(super) unsafe fn read_out_len(index: usize) -> u16 { + USBRAM.mem(index * 4 + 3).read() + } } -fn ep_in_len(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 1) -} -fn ep_out_addr(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 2) -} -fn ep_out_len(index: usize) -> Reg { - T::regs().ep_mem(index * 4 + 3) +#[cfg(usbram_32_2048)] +mod btable { + use super::*; + + pub(super) unsafe fn write_in(_index: usize, _addr: u16) {} + + pub(super) unsafe fn write_in_len(index: usize, addr: u16, len: u16) { + USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); + } + + pub(super) unsafe fn write_out(index: usize, addr: u16, max_len_bits: u16) { + USBRAM + .mem(index * 2 + 1) + .write_value((addr as u32) | ((max_len_bits as u32) << 16)); + } + + pub(super) unsafe fn read_out_len(index: usize) -> u16 { + (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 + } } struct EndpointBuffer { @@ -87,23 +129,25 @@ struct EndpointBuffer { impl EndpointBuffer { fn read(&mut self, buf: &mut [u8]) { assert!(buf.len() <= self.len as usize); - for i in 0..((buf.len() + 1) / 2) { - let val = unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).read() }; - buf[i * 2] = val as u8; - if i * 2 + 1 < buf.len() { - buf[i * 2 + 1] = (val >> 8) as u8; - } + for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN { + let val = unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read() }; + let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN); + buf[i * USBRAM_ALIGN..][..n].copy_from_slice(&val.to_le_bytes()[..n]); } } fn write(&mut self, buf: &[u8]) { assert!(buf.len() <= self.len as usize); - for i in 0..((buf.len() + 1) / 2) { - let mut val = buf[i * 2] as u16; - if i * 2 + 1 < buf.len() { - val |= (buf[i * 2 + 1] as u16) << 8; - } - unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).write_value(val) }; + for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN { + let mut val = [0u8; USBRAM_ALIGN]; + let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN); + val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]); + + #[cfg(not(usbram_32_2048))] + let val = u16::from_le_bytes(val); + #[cfg(usbram_32_2048)] + let val = u32::from_le_bytes(val); + unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val) }; } } } @@ -139,8 +183,7 @@ impl<'d, T: Instance> Driver<'d, T> { #[cfg(stm32l5)] unsafe { crate::peripherals::PWR::enable(); - - pac::PWR.cr2().modify(|w| w.set_usv(true)); + crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); } unsafe { @@ -256,8 +299,9 @@ impl<'d, T: Instance> Driver<'d, T> { } fn alloc_ep_mem(&mut self, len: u16) -> u16 { + assert!(len as usize % USBRAM_ALIGN == 0); let addr = self.ep_mem_free; - if addr + len > EP_MEMORY_SIZE as _ { + if addr + len > USBRAM_SIZE as _ { panic!("Endpoint memory full"); } self.ep_mem_free += len; @@ -306,10 +350,7 @@ impl<'d, T: Instance> Driver<'d, T> { let addr = self.alloc_ep_mem(len); trace!(" len_bits = {:04x}", len_bits); - unsafe { - ep_out_addr::(index).write_value(addr); - ep_out_len::(index).write_value(len_bits); - } + unsafe { btable::write_out::(index, addr, len_bits) } EndpointBuffer { addr, @@ -321,13 +362,11 @@ impl<'d, T: Instance> Driver<'d, T> { assert!(!ep.used_in); ep.used_in = true; - let len = (max_packet_size + 1) / 2 * 2; + let len = align_len_up(max_packet_size); let addr = self.alloc_ep_mem(len); - unsafe { - ep_in_addr::(index).write_value(addr); - // ep_in_len is written when actually TXing packets. - } + // ep_in_len is written when actually TXing packets. + unsafe { btable::write_in::(index, addr) } EndpointBuffer { addr, @@ -398,7 +437,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { w.set_ctrm(true); }); - #[cfg(usb_v3)] + #[cfg(any(usb_v3, usb_v4))] regs.bcdr().write(|w| w.set_dppu(true)) } @@ -633,12 +672,12 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> { fn write_data(&mut self, buf: &[u8]) { let index = self.info.addr.index(); self.buf.write(buf); - unsafe { ep_in_len::(index).write_value(buf.len() as _) }; + unsafe { btable::write_in_len::(index, self.buf.addr, buf.len() as _) } } fn read_data(&mut self, buf: &mut [u8]) -> Result { let index = self.info.addr.index(); - let rx_len = unsafe { ep_out_len::(index).read() as usize } & 0x3FF; + let rx_len = unsafe { btable::read_out_len::(index) as usize } & 0x3FF; trace!("READ DONE, rx_len = {}", rx_len); if rx_len > buf.len() { return Err(EndpointError::BufferOverflow); From 611d0238290c9f7b3b23d05ec07adf5b48ea3479 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 18:53:51 +0200 Subject: [PATCH 0841/1575] stm32: add H5 support. --- ci.sh | 3 + embassy-stm32/Cargo.toml | 73 +++ embassy-stm32/build.rs | 7 +- embassy-stm32/src/eth/v2/mod.rs | 25 +- embassy-stm32/src/exti.rs | 18 +- embassy-stm32/src/rcc/h5.rs | 606 ++++++++++++++++++++++++ embassy-stm32/src/rcc/mod.rs | 13 +- embassy-stm32/src/time.rs | 46 +- embassy-stm32/src/usb/usb.rs | 5 + examples/stm32h5/.cargo/config.toml | 8 + examples/stm32h5/Cargo.toml | 71 +++ examples/stm32h5/build.rs | 5 + examples/stm32h5/memory.x | 5 + examples/stm32h5/src/bin/blinky.rs | 27 ++ examples/stm32h5/src/bin/button_exti.rs | 27 ++ examples/stm32h5/src/bin/eth.rs | 133 ++++++ examples/stm32h5/src/bin/i2c.rs | 44 ++ examples/stm32h5/src/bin/rng.rs | 20 + examples/stm32h5/src/bin/usart.rs | 43 ++ examples/stm32h5/src/bin/usart_dma.rs | 46 ++ examples/stm32h5/src/bin/usart_split.rs | 58 +++ examples/stm32h5/src/bin/usb_serial.rs | 128 +++++ 22 files changed, 1390 insertions(+), 21 deletions(-) create mode 100644 embassy-stm32/src/rcc/h5.rs create mode 100644 examples/stm32h5/.cargo/config.toml create mode 100644 examples/stm32h5/Cargo.toml create mode 100644 examples/stm32h5/build.rs create mode 100644 examples/stm32h5/memory.x create mode 100644 examples/stm32h5/src/bin/blinky.rs create mode 100644 examples/stm32h5/src/bin/button_exti.rs create mode 100644 examples/stm32h5/src/bin/eth.rs create mode 100644 examples/stm32h5/src/bin/i2c.rs create mode 100644 examples/stm32h5/src/bin/rng.rs create mode 100644 examples/stm32h5/src/bin/usart.rs create mode 100644 examples/stm32h5/src/bin/usart_dma.rs create mode 100644 examples/stm32h5/src/bin/usart_split.rs create mode 100644 examples/stm32h5/src/bin/usb_serial.rs diff --git a/ci.sh b/ci.sh index b9dddad3a..82b72ae32 100755 --- a/ci.sh +++ b/ci.sh @@ -66,6 +66,8 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ --- 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-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,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/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \ @@ -87,6 +89,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ --- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \ --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ + --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h5 \ --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ --- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \ --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index da1dc24bf..3fd1e4b4e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -834,6 +834,37 @@ stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] +stm32h503cb = [ "stm32-metapac/stm32h503cb" ] +stm32h503eb = [ "stm32-metapac/stm32h503eb" ] +stm32h503kb = [ "stm32-metapac/stm32h503kb" ] +stm32h503rb = [ "stm32-metapac/stm32h503rb" ] +stm32h562ag = [ "stm32-metapac/stm32h562ag" ] +stm32h562ai = [ "stm32-metapac/stm32h562ai" ] +stm32h562ig = [ "stm32-metapac/stm32h562ig" ] +stm32h562ii = [ "stm32-metapac/stm32h562ii" ] +stm32h562rg = [ "stm32-metapac/stm32h562rg" ] +stm32h562ri = [ "stm32-metapac/stm32h562ri" ] +stm32h562vg = [ "stm32-metapac/stm32h562vg" ] +stm32h562vi = [ "stm32-metapac/stm32h562vi" ] +stm32h562zg = [ "stm32-metapac/stm32h562zg" ] +stm32h562zi = [ "stm32-metapac/stm32h562zi" ] +stm32h563ag = [ "stm32-metapac/stm32h563ag" ] +stm32h563ai = [ "stm32-metapac/stm32h563ai" ] +stm32h563ig = [ "stm32-metapac/stm32h563ig" ] +stm32h563ii = [ "stm32-metapac/stm32h563ii" ] +stm32h563mi = [ "stm32-metapac/stm32h563mi" ] +stm32h563rg = [ "stm32-metapac/stm32h563rg" ] +stm32h563ri = [ "stm32-metapac/stm32h563ri" ] +stm32h563vg = [ "stm32-metapac/stm32h563vg" ] +stm32h563vi = [ "stm32-metapac/stm32h563vi" ] +stm32h563zg = [ "stm32-metapac/stm32h563zg" ] +stm32h563zi = [ "stm32-metapac/stm32h563zi" ] +stm32h573ai = [ "stm32-metapac/stm32h573ai" ] +stm32h573ii = [ "stm32-metapac/stm32h573ii" ] +stm32h573mi = [ "stm32-metapac/stm32h573mi" ] +stm32h573ri = [ "stm32-metapac/stm32h573ri" ] +stm32h573vi = [ "stm32-metapac/stm32h573vi" ] +stm32h573zi = [ "stm32-metapac/stm32h573zi" ] stm32h723ve = [ "stm32-metapac/stm32h723ve" ] stm32h723vg = [ "stm32-metapac/stm32h723vg" ] stm32h723ze = [ "stm32-metapac/stm32h723ze" ] @@ -1316,6 +1347,22 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] stm32l562re = [ "stm32-metapac/stm32l562re" ] stm32l562ve = [ "stm32-metapac/stm32l562ve" ] stm32l562ze = [ "stm32-metapac/stm32l562ze" ] +stm32u535cb = [ "stm32-metapac/stm32u535cb" ] +stm32u535cc = [ "stm32-metapac/stm32u535cc" ] +stm32u535ce = [ "stm32-metapac/stm32u535ce" ] +stm32u535je = [ "stm32-metapac/stm32u535je" ] +stm32u535nc = [ "stm32-metapac/stm32u535nc" ] +stm32u535ne = [ "stm32-metapac/stm32u535ne" ] +stm32u535rb = [ "stm32-metapac/stm32u535rb" ] +stm32u535rc = [ "stm32-metapac/stm32u535rc" ] +stm32u535re = [ "stm32-metapac/stm32u535re" ] +stm32u535vc = [ "stm32-metapac/stm32u535vc" ] +stm32u535ve = [ "stm32-metapac/stm32u535ve" ] +stm32u545ce = [ "stm32-metapac/stm32u545ce" ] +stm32u545je = [ "stm32-metapac/stm32u545je" ] +stm32u545ne = [ "stm32-metapac/stm32u545ne" ] +stm32u545re = [ "stm32-metapac/stm32u545re" ] +stm32u545ve = [ "stm32-metapac/stm32u545ve" ] stm32u575ag = [ "stm32-metapac/stm32u575ag" ] stm32u575ai = [ "stm32-metapac/stm32u575ai" ] stm32u575cg = [ "stm32-metapac/stm32u575cg" ] @@ -1337,6 +1384,32 @@ stm32u585qi = [ "stm32-metapac/stm32u585qi" ] stm32u585ri = [ "stm32-metapac/stm32u585ri" ] stm32u585vi = [ "stm32-metapac/stm32u585vi" ] stm32u585zi = [ "stm32-metapac/stm32u585zi" ] +stm32u595ai = [ "stm32-metapac/stm32u595ai" ] +stm32u595aj = [ "stm32-metapac/stm32u595aj" ] +stm32u595qi = [ "stm32-metapac/stm32u595qi" ] +stm32u595qj = [ "stm32-metapac/stm32u595qj" ] +stm32u595ri = [ "stm32-metapac/stm32u595ri" ] +stm32u595rj = [ "stm32-metapac/stm32u595rj" ] +stm32u595vi = [ "stm32-metapac/stm32u595vi" ] +stm32u595vj = [ "stm32-metapac/stm32u595vj" ] +stm32u595zi = [ "stm32-metapac/stm32u595zi" ] +stm32u595zj = [ "stm32-metapac/stm32u595zj" ] +stm32u599bj = [ "stm32-metapac/stm32u599bj" ] +stm32u599ni = [ "stm32-metapac/stm32u599ni" ] +stm32u599nj = [ "stm32-metapac/stm32u599nj" ] +stm32u599vi = [ "stm32-metapac/stm32u599vi" ] +stm32u599vj = [ "stm32-metapac/stm32u599vj" ] +stm32u599zi = [ "stm32-metapac/stm32u599zi" ] +stm32u599zj = [ "stm32-metapac/stm32u599zj" ] +stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] +stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] +stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] +stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] +stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] +stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] +stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] +stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] +stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 61aceed93..b01e8ba45 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -50,7 +50,7 @@ fn main() { // We *shouldn't* have singletons for these, but the HAL currently requires // singletons, for using with RccPeripheral to enable/disable clocks to them. "rcc" => { - if r.version.starts_with("h7") || r.version.starts_with("f4") { + if r.version.starts_with("h5") || r.version.starts_with("h7") || r.version.starts_with("f4") { singletons.push("MCO1".to_string()); singletons.push("MCO2".to_string()); } @@ -539,7 +539,10 @@ fn main() { // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now - if regs.version.starts_with("h7") || regs.version.starts_with("f4") { + if regs.version.starts_with("h5") + || regs.version.starts_with("h7") + || regs.version.starts_with("f4") + { peri = format_ident!("{}", pin.signal.replace("_", "")); } else { continue; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index fcb4a296c..d49b1f767 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -9,7 +9,7 @@ pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; use super::*; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Speed}; -use crate::pac::{ETH, RCC, SYSCFG}; +use crate::pac::ETH; use crate::Peripheral; const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet @@ -60,16 +60,33 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { unsafe { // Enable the necessary Clocks // NOTE(unsafe) We have exclusive access to the registers + #[cfg(not(rcc_h5))] critical_section::with(|_| { - RCC.apb4enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { + crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true)); + crate::pac::RCC.ahb1enr().modify(|w| { w.set_eth1macen(true); w.set_eth1txen(true); w.set_eth1rxen(true); }); // RMII - SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); + crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); + }); + + #[cfg(rcc_h5)] + critical_section::with(|_| { + crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true)); + + crate::pac::RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); + + // RMII + crate::pac::SBS + .pmcr() + .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4)); }); config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index e1ce09a49..10109e56a 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -25,11 +25,11 @@ fn cpu_regs() -> pac::exti::Exti { EXTI } -#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5)))] +#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] fn exticr_regs() -> pac::syscfg::Syscfg { pac::SYSCFG } -#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] +#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] fn exticr_regs() -> pac::exti::Exti { EXTI } @@ -39,9 +39,9 @@ fn exticr_regs() -> pac::afio::Afio { } pub unsafe fn on_irq() { - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] let bits = EXTI.pr(0).read().0; - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; // Mask all the channels that fired. @@ -53,9 +53,9 @@ pub unsafe fn on_irq() { } // Clear pending - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write_value(Lines(bits)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write_value(Lines(bits)); EXTI.fpr(0).write_value(Lines(bits)); @@ -213,9 +213,9 @@ impl<'a> ExtiInputFuture<'a> { EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); // clear pending bit - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5)))] + #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write(|w| w.set_line(pin, true)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5))] + #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write(|w| w.set_line(pin, true)); EXTI.fpr(0).write(|w| w.set_line(pin, true)); @@ -364,7 +364,7 @@ pub(crate) unsafe fn init() { foreach_exti_irq!(enable_irq); - #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1)))] + #[cfg(not(any(rcc_wb, rcc_wl5, rcc_wle, stm32f1, exti_h5, exti_h50)))] ::enable(); #[cfg(stm32f1)] ::enable(); diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs new file mode 100644 index 000000000..17fbc6056 --- /dev/null +++ b/embassy-stm32/src/rcc/h5.rs @@ -0,0 +1,606 @@ +use core::marker::PhantomData; + +use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre}; + +use crate::pac::pwr::vals::Vos; +use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; +use crate::pac::{FLASH, PWR, RCC}; +use crate::rcc::{set_freqs, Clocks}; +use crate::time::Hertz; +use crate::{peripherals, Peripheral}; + +/// HSI speed +pub const HSI_FREQ: Hertz = Hertz(64_000_000); + +/// CSI speed +pub const CSI_FREQ: Hertz = Hertz(4_000_000); + +/// HSI48 speed +pub const HSI48_FREQ: Hertz = Hertz(48_000_000); + +/// LSI speed +pub const LSI_FREQ: Hertz = Hertz(32_000); + +const VCO_MIN: u32 = 150_000_000; +const VCO_MAX: u32 = 420_000_000; +const VCO_WIDE_MIN: u32 = 128_000_000; +const VCO_WIDE_MAX: u32 = 560_000_000; + +/// Voltage Scale +/// +/// Represents the voltage range feeding the CPU core. The maximum core +/// clock frequency depends on this value. +#[derive(Copy, Clone, PartialEq)] +pub enum VoltageScale { + /// VOS 0 range VCORE 1.30V - 1.40V + Scale0, + /// VOS 1 range VCORE 1.15V - 1.26V + Scale1, + /// VOS 2 range VCORE 1.05V - 1.15V + Scale2, + /// VOS 3 range VCORE 0.95V - 1.05V + Scale3, +} + +pub enum HseMode { + /// crystal/ceramic oscillator (HSEBYP=0) + Oscillator, + /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) + BypassAnalog, + /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) + BypassDigital, +} + +pub struct Hse { + /// HSE frequency. + pub freq: Hertz, + /// HSE mode. + pub mode: HseMode, +} + +pub enum Hsi { + /// 64Mhz + Mhz64, + /// 32Mhz (divided by 2) + Mhz32, + /// 16Mhz (divided by 4) + Mhz16, + /// 8Mhz (divided by 8) + Mhz8, +} + +pub enum Sysclk { + /// HSI selected as sysclk + HSI, + /// HSE selected as sysclk + HSE, + /// CSI selected as sysclk + CSI, + /// PLL1_P selected as sysclk + Pll1P, +} + +pub enum PllSource { + Hsi, + Csi, + Hse, +} + +pub struct Pll { + /// Source clock selection. + pub source: PllSource, + + /// PLL pre-divider (DIVM). Must be between 1 and 63. + pub prediv: u8, + + /// PLL multiplication factor. Must be between 4 and 512. + pub mul: u16, + + /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. + /// On PLL1, it must be even (in particular, it cannot be 1.) + pub divp: Option, + /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. + pub divq: Option, + /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. + pub divr: Option, +} + +/// AHB prescaler +#[derive(Clone, Copy, PartialEq)] +pub enum AHBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div64, + Div128, + Div256, + Div512, +} + +impl AHBPrescaler { + fn div(&self, clk: Hertz) -> Hertz { + match self { + Self::NotDivided => clk, + Self::Div2 => clk / 2u32, + Self::Div4 => clk / 4u32, + Self::Div8 => clk / 8u32, + Self::Div16 => clk / 16u32, + Self::Div64 => clk / 64u32, + Self::Div128 => clk / 128u32, + Self::Div256 => clk / 256u32, + Self::Div512 => clk / 512u32, + } + } +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum APBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, +} + +impl APBPrescaler { + fn div(&self, clk: Hertz) -> Hertz { + match self { + Self::NotDivided => clk, + Self::Div2 => clk / 2u32, + Self::Div4 => clk / 4u32, + Self::Div8 => clk / 8u32, + Self::Div16 => clk / 16u32, + } + } + + fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz { + match (tim, self) { + // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a + // division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 + (TimerPrescaler::DefaultX2, Self::NotDivided) => clk, + (TimerPrescaler::DefaultX2, Self::Div2) => clk, + (TimerPrescaler::DefaultX2, Self::Div4) => clk / 2u32, + (TimerPrescaler::DefaultX2, Self::Div8) => clk / 4u32, + (TimerPrescaler::DefaultX2, Self::Div16) => clk / 8u32, + // The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2 + // corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2 + // this makes NO SENSE and is different than in the H7. Mistake in the RM?? + (TimerPrescaler::DefaultX4, Self::NotDivided) => clk * 2u32, + (TimerPrescaler::DefaultX4, Self::Div2) => clk, + (TimerPrescaler::DefaultX4, Self::Div4) => clk / 2u32, + (TimerPrescaler::DefaultX4, Self::Div8) => clk / 2u32, + (TimerPrescaler::DefaultX4, Self::Div16) => clk / 4u32, + } + } +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum TimerPrescaler { + DefaultX2, + DefaultX4, +} + +impl From for Timpre { + fn from(value: TimerPrescaler) -> Self { + match value { + TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, + TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, + } + } +} + +impl From for Ppre { + fn from(val: APBPrescaler) -> Ppre { + match val { + APBPrescaler::NotDivided => Ppre::DIV1, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +impl From for Hpre { + fn from(val: AHBPrescaler) -> Hpre { + match val { + AHBPrescaler::NotDivided => Hpre::DIV1, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, + } + } +} + +/// Configuration of the core clocks +#[non_exhaustive] +pub struct Config { + pub hsi: Option, + pub hse: Option, + pub csi: bool, + pub hsi48: bool, + pub sys: Sysclk, + + pub pll1: Option, + pub pll2: Option, + #[cfg(rcc_h5)] + pub pll3: Option, + + pub ahb_pre: AHBPrescaler, + pub apb1_pre: APBPrescaler, + pub apb2_pre: APBPrescaler, + pub apb3_pre: APBPrescaler, + pub timer_prescaler: TimerPrescaler, + + pub voltage_scale: VoltageScale, +} + +impl Default for Config { + fn default() -> Self { + Self { + hsi: Some(Hsi::Mhz64), + hse: None, + csi: false, + hsi48: false, + sys: Sysclk::HSI, + pll1: None, + pll2: None, + #[cfg(rcc_h5)] + pll3: None, + + ahb_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, + apb3_pre: APBPrescaler::NotDivided, + timer_prescaler: TimerPrescaler::DefaultX2, + + voltage_scale: VoltageScale::Scale3, + } + } +} + +pub(crate) mod sealed { + pub trait McoInstance { + type Source; + unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8); + } +} + +pub trait McoInstance: sealed::McoInstance + 'static {} + +pin_trait!(McoPin, McoInstance); + +macro_rules! impl_peri { + ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { + impl sealed::McoInstance for peripherals::$peri { + type Source = $source; + + unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) { + RCC.cfgr().modify(|w| { + w.$set_source(source); + w.$set_prescaler(prescaler); + }); + } + } + + impl McoInstance for peripherals::$peri {} + }; +} + +impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre); +impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre); + +pub struct Mco<'d, T: McoInstance> { + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: McoInstance> Mco<'d, T> { + pub fn new( + _peri: impl Peripheral

+ 'd, + _pin: impl Peripheral

> + 'd, + _source: T::Source, + ) -> Self { + todo!(); + } +} + +pub(crate) unsafe fn init(config: Config) { + let (vos, max_clk) = match config.voltage_scale { + VoltageScale::Scale0 => (Vos::SCALE0, Hertz(250_000_000)), + VoltageScale::Scale1 => (Vos::SCALE1, Hertz(200_000_000)), + VoltageScale::Scale2 => (Vos::SCALE2, Hertz(150_000_000)), + VoltageScale::Scale3 => (Vos::SCALE3, Hertz(100_000_000)), + }; + + // Configure voltage scale. + PWR.voscr().modify(|w| w.set_vos(vos)); + while !PWR.vossr().read().vosrdy() {} + + // Configure HSI + let hsi = match config.hsi { + None => { + RCC.cr().modify(|w| w.set_hsion(false)); + None + } + Some(hsi) => { + let (freq, hsidiv) = match hsi { + Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1), + Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2), + Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4), + Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8), + }; + RCC.cr().modify(|w| { + w.set_hsidiv(hsidiv); + w.set_hsion(true); + }); + while !RCC.cr().read().hsirdy() {} + Some(freq) + } + }; + + // Configure HSE + let hse = match config.hse { + None => { + RCC.cr().modify(|w| w.set_hseon(false)); + None + } + Some(hse) => { + let (byp, ext) = match hse.mode { + HseMode::Oscillator => (false, Hseext::ANALOG), + HseMode::BypassAnalog => (true, Hseext::ANALOG), + HseMode::BypassDigital => (true, Hseext::DIGITAL), + }; + + RCC.cr().modify(|w| { + w.set_hsebyp(byp); + w.set_hseext(ext); + }); + RCC.cr().modify(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + Some(hse.freq) + } + }; + + // Configure HSI48. + RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); + let _hsi48 = match config.hsi48 { + false => None, + true => { + while !RCC.cr().read().hsi48rdy() {} + Some(CSI_FREQ) + } + }; + + // Configure CSI. + RCC.cr().modify(|w| w.set_csion(config.csi)); + let csi = match config.csi { + false => None, + true => { + while !RCC.cr().read().csirdy() {} + Some(CSI_FREQ) + } + }; + + // Configure PLLs. + let pll_input = PllInput { csi, hse, hsi }; + let pll1 = init_pll(0, config.pll1, &pll_input); + let _pll2 = init_pll(1, config.pll2, &pll_input); + #[cfg(rcc_h5)] + let _pll3 = init_pll(2, config.pll3, &pll_input); + + // Configure sysclk + let (sys, sw) = match config.sys { + Sysclk::HSI => (unwrap!(hsi), Sw::HSI), + Sysclk::HSE => (unwrap!(hse), Sw::HSE), + Sysclk::CSI => (unwrap!(csi), Sw::CSI), + Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1), + }; + assert!(sys <= max_clk); + + let hclk = config.ahb_pre.div(sys); + + let apb1 = config.apb1_pre.div(hclk); + let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler); + let apb2 = config.apb2_pre.div(hclk); + let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler); + let apb3 = config.apb3_pre.div(hclk); + + flash_setup(hclk, config.voltage_scale); + + // Set hpre + let hpre = config.ahb_pre.into(); + RCC.cfgr2().modify(|w| w.set_hpre(hpre)); + while RCC.cfgr2().read().hpre() != hpre {} + + // set ppre + RCC.cfgr2().modify(|w| { + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + w.set_ppre3(config.apb3_pre.into()); + }); + + RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into())); + + RCC.cfgr().modify(|w| w.set_sw(sw)); + while RCC.cfgr().read().sws() != sw {} + + set_freqs(Clocks { + sys, + ahb1: hclk, + ahb2: hclk, + ahb3: hclk, + ahb4: hclk, + apb1, + apb2, + apb3, + apb1_tim, + apb2_tim, + adc: None, + }); +} + +struct PllInput { + hsi: Option, + hse: Option, + csi: Option, +} + +struct PllOutput { + p: Option, + #[allow(dead_code)] + q: Option, + #[allow(dead_code)] + r: Option, +} + +unsafe fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { + let Some(config) = config else { + // Stop PLL + RCC.cr().modify(|w| w.set_pllon(num, false)); + while RCC.cr().read().pllrdy(num) {} + + // "To save power when PLL1 is not used, the value of PLL1M must be set to 0."" + RCC.pllcfgr(num).write(|w| { + w.set_divm(0); + }); + + return PllOutput{ + p: None, + q: None, + r: None, + } + }; + + assert!(1 <= config.prediv && config.prediv <= 63); + assert!(4 <= config.mul && config.mul <= 512); + + let (in_clk, src) = match config.source { + PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI), + PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE), + PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI), + }; + + let ref_clk = in_clk / config.prediv as u32; + + let ref_range = match ref_clk.0 { + ..=1_999_999 => Pllrge::RANGE1, + ..=3_999_999 => Pllrge::RANGE2, + ..=7_999_999 => Pllrge::RANGE4, + ..=16_000_000 => Pllrge::RANGE8, + x => panic!("pll ref_clk out of range: {} mhz", x), + }; + + // The smaller range (150 to 420 MHz) must + // be chosen when the reference clock frequency is lower than 2 MHz. + let wide_allowed = ref_range != Pllrge::RANGE1; + + let vco_clk = ref_clk * config.mul; + let vco_range = match vco_clk.0 { + VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO, + VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO, + x => panic!("pll vco_clk out of range: {} mhz", x), + }; + + let p = config.divp.map(|div| { + assert!(1 <= div && div <= 128); + if num == 0 { + // on PLL1, DIVP must be even. + assert!(div % 2 == 0); + } + + vco_clk / div + }); + let q = config.divq.map(|div| { + assert!(1 <= div && div <= 128); + vco_clk / div + }); + let r = config.divr.map(|div| { + assert!(1 <= div && div <= 128); + vco_clk / div + }); + + RCC.pllcfgr(num).write(|w| { + w.set_pllsrc(src); + w.set_divm(config.prediv); + w.set_pllvcosel(vco_range); + w.set_pllrge(ref_range); + w.set_pllfracen(false); + w.set_pllpen(p.is_some()); + w.set_pllqen(q.is_some()); + w.set_pllren(r.is_some()); + }); + RCC.plldivr(num).write(|w| { + w.set_plln(config.mul - 1); + w.set_pllp((config.divp.unwrap_or(1) - 1) as u8); + w.set_pllq((config.divq.unwrap_or(1) - 1) as u8); + w.set_pllr((config.divr.unwrap_or(1) - 1) as u8); + }); + + RCC.cr().modify(|w| w.set_pllon(num, true)); + while !RCC.cr().read().pllrdy(num) {} + + PllOutput { p, q, r } +} + +fn flash_setup(clk: Hertz, vos: VoltageScale) { + // RM0481 Rev 1, table 37 + // LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0 + // 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz + // 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz + // 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz + // 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz + // 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz + // 5 2 170 to 200 MHz 210 to 250 MHz + + // See RM0433 Rev 7 Table 17. FLASH recommended number of wait + // states and programming delay + let (latency, wrhighfreq) = match (vos, clk.0) { + (VoltageScale::Scale0, ..=42_000_000) => (0, 0), + (VoltageScale::Scale0, ..=84_000_000) => (1, 0), + (VoltageScale::Scale0, ..=126_000_000) => (2, 1), + (VoltageScale::Scale0, ..=168_000_000) => (3, 1), + (VoltageScale::Scale0, ..=210_000_000) => (4, 2), + (VoltageScale::Scale0, ..=250_000_000) => (5, 2), + + (VoltageScale::Scale1, ..=34_000_000) => (0, 0), + (VoltageScale::Scale1, ..=68_000_000) => (1, 0), + (VoltageScale::Scale1, ..=102_000_000) => (2, 1), + (VoltageScale::Scale1, ..=136_000_000) => (3, 1), + (VoltageScale::Scale1, ..=170_000_000) => (4, 2), + (VoltageScale::Scale1, ..=200_000_000) => (5, 2), + + (VoltageScale::Scale2, ..=30_000_000) => (0, 0), + (VoltageScale::Scale2, ..=60_000_000) => (1, 0), + (VoltageScale::Scale2, ..=90_000_000) => (2, 1), + (VoltageScale::Scale2, ..=120_000_000) => (3, 1), + (VoltageScale::Scale2, ..=150_000_000) => (4, 2), + + (VoltageScale::Scale3, ..=20_000_000) => (0, 0), + (VoltageScale::Scale3, ..=40_000_000) => (1, 0), + (VoltageScale::Scale3, ..=60_000_000) => (2, 1), + (VoltageScale::Scale3, ..=80_000_000) => (3, 1), + (VoltageScale::Scale3, ..=100_000_000) => (4, 2), + + _ => unreachable!(), + }; + + defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); + + // NOTE(unsafe) Atomic write + unsafe { + FLASH.acr().write(|w| { + w.set_wrhighfreq(wrhighfreq); + w.set_latency(latency); + }); + while FLASH.acr().read().latency() != latency {} + } +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d4bd3d6b8..d6a31f17b 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -21,6 +21,7 @@ use crate::time::Hertz; #[cfg_attr(rcc_u5, path = "u5.rs")] #[cfg_attr(rcc_wb, path = "wb.rs")] #[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")] +#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] mod _version; pub use _version::*; @@ -36,7 +37,7 @@ pub struct Clocks { pub apb2: Hertz, #[cfg(not(any(rcc_c0, rcc_g0)))] pub apb2_tim: Hertz, - #[cfg(any(rcc_wl5, rcc_wle, rcc_u5))] + #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))] pub apb3: Hertz, #[cfg(any(rcc_h7, rcc_h7ab))] pub apb4: Hertz, @@ -44,14 +45,16 @@ pub struct Clocks { // AHB pub ahb1: Hertz, #[cfg(any( - rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, rcc_wl5, rcc_wle + rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb, + rcc_wl5, rcc_wle ))] pub ahb2: Hertz, #[cfg(any( - rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5, rcc_wle + rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5, + rcc_wle ))] pub ahb3: Hertz, - #[cfg(any(rcc_h7, rcc_h7ab))] + #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub ahb4: Hertz, #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] @@ -60,7 +63,7 @@ pub struct Clocks { #[cfg(stm32f1)] pub adc: Hertz, - #[cfg(any(rcc_h7, rcc_h7ab))] + #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, } diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 975517a48..f08abe331 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs @@ -1,7 +1,9 @@ //! Time units +use core::ops::{Div, Mul}; + /// Hertz -#[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Eq)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Hertz(pub u32); @@ -33,3 +35,45 @@ pub fn khz(kilohertz: u32) -> Hertz { pub fn mhz(megahertz: u32) -> Hertz { Hertz::mhz(megahertz) } + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u32) -> Self::Output { + Hertz(self.0 * rhs) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u32) -> Self::Output { + Hertz(self.0 / rhs) + } +} + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u16) -> Self::Output { + self * (rhs as u32) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u16) -> Self::Output { + self / (rhs as u32) + } +} + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u8) -> Self::Output { + self * (rhs as u32) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u8) -> Self::Output { + self / (rhs as u32) + } +} diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index e6ee39549..ad68eaba2 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -186,6 +186,11 @@ impl<'d, T: Instance> Driver<'d, T> { crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); } + #[cfg(pwr_h5)] + unsafe { + crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)) + } + unsafe { ::enable(); ::reset(); diff --git a/examples/stm32h5/.cargo/config.toml b/examples/stm32h5/.cargo/config.toml new file mode 100644 index 000000000..c8b864b6c --- /dev/null +++ b/examples/stm32h5/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv8m.main-none-eabihf] +runner = 'probe-rs-cli run --chip STM32H563ZITx' + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml new file mode 100644 index 000000000..70702863f --- /dev/null +++ b/examples/stm32h5/Cargo.toml @@ -0,0 +1,71 @@ +[package] +edition = "2021" +name = "embassy-stm32h7-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +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", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } +embedded-io = { version = "0.4.0", features = ["async"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } + +defmt = "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.2.0-alpha.0" } +embedded-nal-async = "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 } +rand_core = "0.6.3" +critical-section = "1.1" +micromath = "2.0.0" +stm32-fmc = "0.2.4" +embedded-storage = "0.3.0" +static_cell = "1.0" + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/stm32h5/build.rs b/examples/stm32h5/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32h5/build.rs @@ -0,0 +1,5 @@ +fn main() { + 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/stm32h5/memory.x b/examples/stm32h5/memory.x new file mode 100644 index 000000000..456061509 --- /dev/null +++ b/examples/stm32h5/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000 + RAM : ORIGIN = 0x20000000, LENGTH = 0x50000 +} diff --git a/examples/stm32h5/src/bin/blinky.rs b/examples/stm32h5/src/bin/blinky.rs new file mode 100644 index 000000000..f9bf90d2e --- /dev/null +++ b/examples/stm32h5/src/bin/blinky.rs @@ -0,0 +1,27 @@ +#![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 _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PB0, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after(Duration::from_millis(500)).await; + + info!("low"); + led.set_low(); + Timer::after(Duration::from_millis(500)).await; + } +} diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs new file mode 100644 index 000000000..dfe587d41 --- /dev/null +++ b/examples/stm32h5/src/bin/button_exti.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::{Input, Pull}; +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 button = Input::new(p.PC13, Pull::Down); + let mut button = ExtiInput::new(button, p.EXTI13); + + info!("Press the USER button..."); + + loop { + button.wait_for_rising_edge().await; + info!("Pressed!"); + button.wait_for_falling_edge().await; + info!("Released!"); + } +} diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs new file mode 100644 index 000000000..6d650da9e --- /dev/null +++ b/examples/stm32h5/src/bin/eth.rs @@ -0,0 +1,133 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_stm32::eth::generic_smi::GenericSMI; +use embassy_stm32::eth::{Ethernet, PacketQueue}; +use embassy_stm32::peripherals::ETH; +use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::Hertz; +use embassy_stm32::{interrupt, Config}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::Write; +use rand_core::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +type Device = Ethernet<'static, ETH, GenericSMI>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.hsi = None; + config.rcc.hsi48 = true; // needed for rng + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::BypassDigital, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::Hse, + prediv: 2, + mul: 125, + divp: Some(2), + divq: Some(2), + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::NotDivided; + config.rcc.apb1_pre = APBPrescaler::NotDivided; + config.rcc.apb2_pre = APBPrescaler::NotDivided; + config.rcc.apb3_pre = APBPrescaler::NotDivided; + config.rcc.sys = Sysclk::Pll1P; + config.rcc.voltage_scale = VoltageScale::Scale0; + let p = embassy_stm32::init(config); + info!("Hello World!"); + + // Generate random seed. + let mut rng = Rng::new(p.RNG); + let mut seed = [0; 8]; + rng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + let eth_int = interrupt::take!(ETH); + let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + + let device = Ethernet::new( + singleton!(PacketQueue::<4, 4>::new()), + p.ETH, + eth_int, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB15, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); + + let config = embassy_net::Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Init network stack + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Network task initialized"); + + // Then we can use it! + let mut rx_buffer = [0; 1024]; + let mut tx_buffer = [0; 1024]; + + loop { + let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); + info!("connecting..."); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + Timer::after(Duration::from_secs(3)).await; + continue; + } + info!("connected!"); + loop { + let r = socket.write_all(b"Hello\n").await; + if let Err(e) = r { + info!("write error: {:?}", e); + continue; + } + Timer::after(Duration::from_secs(1)).await; + } + } +} diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs new file mode 100644 index 000000000..6cbf58bbc --- /dev/null +++ b/examples/stm32h5/src/bin/i2c.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; +use embassy_stm32::interrupt; +use embassy_stm32::time::Hertz; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let irq = interrupt::take!(I2C2_EV); + let mut i2c = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + irq, + p.GPDMA1_CH4, + p.GPDMA1_CH5, + Hertz(100_000), + Default::default(), + ); + + // I2C bus can freeze if SCL line is shorted or due to a broken device that clock stretches for too long. + // TimeoutI2c allows recovering from such errors by throwing `Error::Timeout` after a given delay. + let mut timeout_i2c = TimeoutI2c::new(&mut i2c, Duration::from_millis(1000)); + + let mut data = [0u8; 1]; + + match timeout_i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data) { + Ok(()) => info!("Whoami: {}", data[0]), + Err(Error::Timeout) => error!("Operation timed out"), + Err(e) => error!("I2c Error: {:?}", e), + } +} diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs new file mode 100644 index 000000000..af9be0b62 --- /dev/null +++ b/examples/stm32h5/src/bin/rng.rs @@ -0,0 +1,20 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rng::Rng; +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 rng = Rng::new(p.RNG); + + let mut buf = [0u8; 16]; + unwrap!(rng.async_fill_bytes(&mut buf).await); + info!("random bytes: {:02x}", buf); +} diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs new file mode 100644 index 000000000..405f18ec7 --- /dev/null +++ b/examples/stm32h5/src/bin/usart.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_executor::Executor; +use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; +use embassy_stm32::usart::{Config, Uart}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn main_task() { + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); + + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let executor = EXECUTOR.init(Executor::new()); + + executor.run(|spawner| { + unwrap!(spawner.spawn(main_task())); + }) +} diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs new file mode 100644 index 000000000..43d791aae --- /dev/null +++ b/examples/stm32h5/src/bin/usart_dma.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::fmt::Write; + +use cortex_m_rt::entry; +use defmt::*; +use embassy_executor::Executor; +use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; +use embassy_stm32::usart::{Config, Uart}; +use heapless::String; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn main_task() { + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, NoDma, config); + + for n in 0u32.. { + let mut s: String<128> = String::new(); + core::write!(&mut s, "Hello DMA World {}!\r\n", n).unwrap(); + + usart.write(s.as_bytes()).await.ok(); + + info!("wrote DMA"); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let executor = EXECUTOR.init(Executor::new()); + + executor.run(|spawner| { + unwrap!(spawner.spawn(main_task())); + }) +} diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs new file mode 100644 index 000000000..16a499582 --- /dev/null +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -0,0 +1,58 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dma::NoDma; +use embassy_stm32::interrupt; +use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; +use embassy_stm32::usart::{Config, Uart, UartRx}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let irq = interrupt::take!(UART7); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, p.GPDMA1_CH1, config); + unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); + + let (mut tx, rx) = usart.split(); + + unwrap!(spawner.spawn(reader(rx))); + + loop { + let buf = CHANNEL.recv().await; + info!("writing..."); + unwrap!(tx.write(&buf).await); + } +} + +#[embassy_executor::task] +async fn reader(mut rx: UartRx<'static, UART7, GPDMA1_CH1>) { + let mut buf = [0; 8]; + loop { + info!("reading..."); + unwrap!(rx.read(&mut buf).await); + CHANNEL.send(buf).await; + } +} diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs new file mode 100644 index 000000000..6af269c1d --- /dev/null +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -0,0 +1,128 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; +use embassy_stm32::time::Hertz; +use embassy_stm32::usb::{Driver, Instance}; +use embassy_stm32::{interrupt, pac, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + config.rcc.hsi = None; + config.rcc.hsi48 = true; // needed for usb + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::BypassDigital, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::Hse, + prediv: 2, + mul: 125, + divp: Some(2), // 250mhz + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::Div2; + config.rcc.apb1_pre = APBPrescaler::Div4; + config.rcc.apb2_pre = APBPrescaler::Div2; + config.rcc.apb3_pre = APBPrescaler::Div4; + config.rcc.sys = Sysclk::Pll1P; + config.rcc.voltage_scale = VoltageScale::Scale0; + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + unsafe { + pac::RCC.ccipr4().write(|w| { + w.set_usbsel(pac::rcc::vals::Usbsel::HSI48); + }); + } + + // Create the driver, from the HAL. + let irq = interrupt::take!(USB_DRD_FS); + let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // 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; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From e2516bba09fbb99da95939656e172156ad1924fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 22:39:27 +0200 Subject: [PATCH 0842/1575] executor: fix doc features. --- embassy-executor/Cargo.toml | 19 ++++++++----------- rust-toolchain.toml | 1 + 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index bb8a46c82..29e1bd478 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -14,21 +14,18 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["nightly", "defmt"] +features = ["nightly", "defmt", "pender-callback"] flavors = [ - { name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] }, - { name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] }, - { name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] }, - { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, - { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] }, - { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] }, - { name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] }, - { name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] }, - { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, + { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, + { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, + { name = "cortex-m", target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread", "executor-interrupt"] }, + { name = "riscv32", target = "riscv32imac-unknown-none-elf", features = ["arch-riscv32", "executor-thread"] }, ] [package.metadata.docs.rs] -features = ["std", "nightly", "defmt"] +default-target = "thumbv7em-none-eabi" +targets = ["thumbv7em-none-eabi"] +features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"] [features] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 22abacdea..9785cd9eb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -9,5 +9,6 @@ targets = [ "thumbv6m-none-eabi", "thumbv7em-none-eabihf", "thumbv8m.main-none-eabihf", + "riscv32imac-unknown-none-elf", "wasm32-unknown-unknown", ] From be37eee13dbd7833e0d74ea57d31d3e5c58cd47f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Apr 2023 22:25:24 +0200 Subject: [PATCH 0843/1575] Update embedded-hal crates. --- embassy-embedded-hal/Cargo.toml | 4 +- embassy-embedded-hal/src/adapter.rs | 27 ++- embassy-embedded-hal/src/lib.rs | 2 +- .../src/shared_bus/asynch/i2c.rs | 42 ++--- .../src/shared_bus/asynch/spi.rs | 175 +++++++++++++++--- .../src/shared_bus/blocking/i2c.rs | 56 ------ .../src/shared_bus/blocking/spi.rs | 157 +++++++++++++--- embassy-lora/Cargo.toml | 4 +- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/twim.rs | 49 ++--- embassy-rp/Cargo.toml | 6 +- embassy-rp/src/i2c.rs | 107 +++-------- embassy-rp/src/spi.rs | 1 + embassy-stm32/Cargo.toml | 6 +- embassy-stm32/src/i2c/timeout.rs | 75 +++----- embassy-stm32/src/i2c/v1.rs | 73 +++----- embassy-stm32/src/i2c/v2.rs | 158 +++++++--------- embassy-time/Cargo.toml | 4 +- embassy-time/src/delay.rs | 20 +- examples/rp/.cargo/config.toml | 2 +- examples/rp/Cargo.toml | 5 +- examples/rp/src/bin/spi_display.rs | 158 ++++------------ examples/stm32f1/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 4 +- examples/stm32h7/Cargo.toml | 4 +- examples/stm32l4/Cargo.toml | 4 +- tests/rp/Cargo.toml | 4 +- tests/stm32/Cargo.toml | 4 +- 28 files changed, 543 insertions(+), 614 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 45eb0d43d..c509d6ee5 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -19,8 +19,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"] [dependencies] 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.2.0-alpha.0", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index a49f8df4b..ee919bd84 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -36,27 +36,22 @@ where E: embedded_hal_1::i2c::Error + 'static, T: blocking::i2c::WriteRead + blocking::i2c::Read + blocking::i2c::Write, { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Self::Error> { - self.wrapped.read(address, buffer) + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, read) } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Self::Error> { - self.wrapped.write(address, bytes) + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(address, write) } - async fn write_read<'a>( - &'a mut self, + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.write_read(address, write, read) + } + + async fn transaction( + &mut self, address: u8, - bytes: &'a [u8], - buffer: &'a mut [u8], - ) -> Result<(), Self::Error> { - self.wrapped.write_read(address, bytes, buffer) - } - - async fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], + operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { let _ = address; let _ = operations; diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 8da042228..a23fbdc41 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr( feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) + feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections, try_blocks) )] #![cfg_attr(feature = "nightly", allow(incomplete_features))] #![warn(missing_docs)] diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index c5e1fd415..829554045 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -54,35 +54,35 @@ where M: RawMutex + 'static, BUS: i2c::I2c + 'static, { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; - bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?; + bus.read(address, read).await.map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; - bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?; + bus.write(address, write).await.map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn write_read<'a>( - &'a mut self, + async fn write_read( + &mut self, address: u8, - wr_buffer: &'a [u8], - rd_buffer: &'a mut [u8], + write: &[u8], + read: &mut [u8], ) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; - bus.write_read(address, wr_buffer, rd_buffer) + bus.write_read(address, write, read) .await .map_err(I2cDeviceError::I2c)?; Ok(()) } - async fn transaction<'a, 'b>( - &'a mut self, + async fn transaction( + &mut self, address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], + operations: &mut [embedded_hal_async::i2c::Operation<'_>], ) -> Result<(), I2cDeviceError> { let _ = address; let _ = operations; @@ -121,25 +121,25 @@ where M: RawMutex + 'static, BUS: i2c::I2c + SetConfig + 'static, { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), I2cDeviceError> { + async fn read(&mut self, address: u8, buffer: &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(()) } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), I2cDeviceError> { + async fn write(&mut self, address: u8, bytes: &[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(()) } - async fn write_read<'a>( - &'a mut self, + async fn write_read( + &mut self, address: u8, - wr_buffer: &'a [u8], - rd_buffer: &'a mut [u8], + wr_buffer: &[u8], + rd_buffer: &mut [u8], ) -> Result<(), I2cDeviceError> { let mut bus = self.bus.lock().await; bus.set_config(&self.config); @@ -149,11 +149,7 @@ where Ok(()) } - async fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Result<(), I2cDeviceError> { + async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> { let _ = address; let _ = operations; todo!() diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index d25716655..b5549a6cd 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -25,12 +25,11 @@ //! let spi_dev2 = SpiDevice::new(spi_bus, cs_pin2); //! let display2 = ST7735::new(spi_dev2, dc2, rst2, Default::default(), 160, 128); //! ``` -use core::future::Future; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; use embedded_hal_1::digital::OutputPin; -use embedded_hal_1::spi::ErrorType; +use embedded_hal_1::spi::Operation; use embedded_hal_async::spi; use crate::shared_bus::SpiDeviceError; @@ -57,33 +56,92 @@ where type Error = SpiDeviceError; } -unsafe impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> where - M: RawMutex + 'static, - BUS: spi::SpiBusFlush + 'static, + M: RawMutex, + BUS: spi::SpiBusRead, CS: OutputPin, { - type Bus = BUS; - - async fn transaction(&mut self, f: F) -> Result - where - F: FnOnce(*mut Self::Bus) -> Fut, - Fut: Future::Error>>, - { + async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { let mut bus = self.bus.lock().await; self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut *bus).await; + let op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.read(buf).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(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + } +} + +impl spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBusWrite, + CS: OutputPin, +{ + async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.write(buf).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(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) + } +} + +impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBus, + CS: OutputPin, +{ + async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for op in operations { + match op { + Operation::Read(buf) => bus.read(buf).await?, + Operation::Write(buf) => bus.write(buf).await?, + Operation::Transfer(read, write) => bus.transfer(read, write).await?, + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).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(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) } } @@ -114,33 +172,94 @@ where type Error = SpiDeviceError; } -unsafe impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> where - M: RawMutex + 'static, - BUS: spi::SpiBusFlush + SetConfig + 'static, + M: RawMutex, + BUS: spi::SpiBusWrite + SetConfig, CS: OutputPin, { - type Bus = BUS; - - async fn transaction(&mut self, f: F) -> Result - where - F: FnOnce(*mut Self::Bus) -> Fut, - Fut: Future::Error>>, - { + async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { 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 op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.write(buf).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(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + } +} + +impl spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBusRead + SetConfig, + CS: OutputPin, +{ + async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for buf in operations { + bus.read(buf).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(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) + } +} + +impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: spi::SpiBus + SetConfig, + CS: OutputPin, +{ + async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> { + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res: Result<(), BUS::Error> = try { + for op in operations { + match op { + Operation::Read(buf) => bus.read(buf).await?, + Operation::Write(buf) => bus.write(buf).await?, + Operation::Transfer(read, write) => bus.transfer(read, write).await?, + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).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(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) } } diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index 892000b26..1fe520e6c 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -72,34 +72,6 @@ where let _ = operations; todo!() } - - fn write_iter>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - todo!() - } - - fn write_iter_read>( - &mut self, - addr: u8, - bytes: B, - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - let _ = buffer; - todo!() - } - - fn transaction_iter<'a, O: IntoIterator>>( - &mut self, - address: u8, - operations: O, - ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() - } } impl<'a, M, BUS, E> embedded_hal_02::blocking::i2c::Write for I2cDevice<'_, M, BUS> @@ -204,32 +176,4 @@ where let _ = operations; todo!() } - - fn write_iter>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - todo!() - } - - fn write_iter_read>( - &mut self, - addr: u8, - bytes: B, - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - let _ = addr; - let _ = bytes; - let _ = buffer; - todo!() - } - - fn transaction_iter<'a, O: IntoIterator>>( - &mut self, - address: u8, - operations: O, - ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() - } } diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 4a08dc36e..7982ffb6e 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -23,8 +23,7 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; use embedded_hal_1::digital::OutputPin; -use embedded_hal_1::spi; -use embedded_hal_1::spi::SpiBusFlush; +use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite}; use crate::shared_bus::SpiDeviceError; use crate::SetConfig; @@ -50,30 +49,85 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> where M: RawMutex, - BUS: SpiBusFlush, + BUS: SpiBusRead, CS: OutputPin, { - type Bus = BUS; - - fn transaction(&mut self, f: impl FnOnce(&mut Self::Bus) -> Result) -> Result { + fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut bus); + let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf)); // On failure, it's important to still flush and deassert CS. let flush_res = bus.flush(); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter().try_for_each(|buf| bus.write(buf)); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + + Ok(op_res) }) } } @@ -89,11 +143,11 @@ where self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = bus.transfer(words); + let op_res = bus.transfer(words); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) }) } } @@ -110,11 +164,11 @@ where self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = bus.write(words); + let op_res = bus.write(words); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) }) } } @@ -146,30 +200,85 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +impl embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, - BUS: SpiBusFlush + SetConfig, + BUS: SpiBusRead + SetConfig, CS: OutputPin, { - type Bus = BUS; - - fn transaction(&mut self, f: impl FnOnce(&mut Self::Bus) -> Result) -> Result { + fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { self.bus.lock(|bus| { let mut bus = bus.borrow_mut(); bus.set_config(&self.config); self.cs.set_low().map_err(SpiDeviceError::Cs)?; - let f_res = f(&mut bus); + let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf)); // On failure, it's important to still flush and deassert CS. let flush_res = bus.flush(); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(SpiDeviceError::Spi)?; + let op_res = op_res.map_err(SpiDeviceError::Spi)?; flush_res.map_err(SpiDeviceError::Spi)?; cs_res.map_err(SpiDeviceError::Cs)?; - Ok(f_res) + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBusWrite + SetConfig, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter().try_for_each(|buf| bus.write(buf)); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + Ok(op_res) + }) + } +} + +impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> +where + M: RawMutex, + BUS: SpiBus + SetConfig, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + self.bus.lock(|bus| { + let mut bus = bus.borrow_mut(); + bus.set_config(&self.config); + self.cs.set_low().map_err(SpiDeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + let op_res = op_res.map_err(SpiDeviceError::Spi)?; + flush_res.map_err(SpiDeviceError::Spi)?; + cs_res.map_err(SpiDeviceError::Cs)?; + Ok(op_res) }) } } diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index cbe78e592..c9174ea82 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -31,8 +31,8 @@ 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" } 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.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } 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-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 4e62ca89e..4a4e7c9f9 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -87,8 +87,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} embedded-io = { version = "0.4.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index ef4c929a3..9ae569609 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -846,20 +846,6 @@ mod eh1 { self.blocking_write(address, buffer) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { self.blocking_write_read(address, wr_buffer, rd_buffer) } @@ -871,13 +857,6 @@ mod eh1 { ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } @@ -885,28 +864,22 @@ mod eh1 { mod eha { use super::*; impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { - async fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Result<(), Error> { - self.read(address, buffer).await + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.read(address, read).await } - async fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Result<(), Error> { - self.write(address, bytes).await + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.write(address, write).await + } + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.write_read(address, write, read).await } - async fn write_read<'a>( - &'a mut self, + async fn transaction( + &mut self, address: u8, - wr_buffer: &'a [u8], - rd_buffer: &'a mut [u8], - ) -> Result<(), Error> { - self.write_read(address, wr_buffer, rd_buffer).await - } - - async fn transaction<'a, 'b>( - &'a mut self, - address: u8, - operations: &'a mut [embedded_hal_async::i2c::Operation<'b>], - ) -> Result<(), Error> { + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { let _ = address; let _ = operations; todo!() diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 209c665b0..cb9c7be77 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -65,9 +65,9 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90 #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.2.0-alpha.0", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} paste = "1.0" pio-proc = {version= "0.2", optional = true} diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index e48e16d81..40e85c66f 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -490,14 +490,14 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { } } - fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { - if buffer.is_empty() { + fn read_blocking_internal(&mut self, read: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { + if read.is_empty() { return Err(Error::InvalidReadBufferLength); } let p = T::regs(); - let lastindex = buffer.len() - 1; - for (i, byte) in buffer.iter_mut().enumerate() { + let lastindex = read.len() - 1; + for (i, byte) in read.iter_mut().enumerate() { let first = i == 0; let last = i == lastindex; @@ -524,15 +524,15 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { Ok(()) } - fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> { - if bytes.is_empty() { + fn write_blocking_internal(&mut self, write: &[u8], send_stop: bool) -> Result<(), Error> { + if write.is_empty() { return Err(Error::InvalidWriteBufferLength); } let p = T::regs(); - for (i, byte) in bytes.iter().enumerate() { - let last = i == bytes.len() - 1; + for (i, byte) in write.iter().enumerate() { + let last = i == write.len() - 1; // NOTE(unsafe) We have &mut self unsafe { @@ -572,21 +572,21 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { // Blocking public API // ========================= - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.read_blocking_internal(buffer, true, true) + self.read_blocking_internal(read, true, true) // Automatic Stop } - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.write_blocking_internal(bytes, true) + self.write_blocking_internal(write, true) } - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { Self::setup(address.into())?; - self.write_blocking_internal(bytes, false)?; - self.read_blocking_internal(buffer, true, true) + self.write_blocking_internal(write, false)?; + self.read_blocking_internal(read, true, true) // Automatic Stop } } @@ -644,48 +644,22 @@ mod eh1 { } impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - let mut peekable = bytes.into_iter().peekable(); - Self::setup(address.into())?; - - while let Some(tx) = peekable.next() { - self.write_blocking_internal(&[tx], peekable.peek().is_none())?; - } - Ok(()) + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - let peekable = bytes.into_iter().peekable(); - Self::setup(address.into())?; - - for tx in peekable { - self.write_blocking_internal(&[tx], false)? - } - self.read_blocking_internal(buffer, true, true) - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, address: u8, - operations: &mut [embedded_hal_1::i2c::Operation<'a>], + operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { Self::setup(address.into())?; for i in 0..operations.len() { @@ -697,22 +671,6 @@ mod eh1 { } Ok(()) } - - fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - Self::setup(address.into())?; - let mut peekable = operations.into_iter().peekable(); - while let Some(operation) = peekable.next() { - let last = peekable.peek().is_none(); - match operation { - embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, - embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, - } - } - Ok(()) - } } } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] @@ -727,36 +685,29 @@ mod nightly { A: AddressMode + Into + 'static, T: Instance + 'd, { - async fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Result<(), Self::Error> { + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); Self::setup(addr)?; self.read_async_internal(read, false, true).await } - async fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Result<(), Self::Error> { + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); Self::setup(addr)?; self.write_async_internal(write.iter().copied(), true).await } - async fn write_read<'a>( - &'a mut self, - address: A, - write: &'a [u8], - read: &'a mut [u8], - ) -> Result<(), Self::Error> { + + async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { let addr: u16 = address.into(); Self::setup(addr)?; self.write_async_internal(write.iter().cloned(), false).await?; self.read_async_internal(read, false, true).await } - async fn transaction<'a, 'b>( - &'a mut self, - address: A, - operations: &'a mut [Operation<'b>], - ) -> Result<(), Self::Error> { + + async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { let addr: u16 = address.into(); let mut iterator = operations.iter_mut(); diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index ebd621ecf..742a35d49 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -19,6 +19,7 @@ pub enum Error { } #[non_exhaustive] +#[derive(Clone)] pub struct Config { pub frequency: u32, pub phase: Phase, diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3fd1e4b4e..6710ff2d0 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -44,9 +44,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", 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-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} embedded-storage = "0.3.0" diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 4fca1ca2b..939e2750e 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -28,64 +28,64 @@ impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { } /// Blocking read with a custom timeout - pub fn blocking_read_timeout(&mut self, addr: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { - self.i2c.blocking_read_timeout(addr, buffer, timeout_fn(timeout)) + pub fn blocking_read_timeout(&mut self, addr: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_read_timeout(addr, read, timeout_fn(timeout)) } /// Blocking read with default timeout, provided in [`TimeoutI2c::new()`] - pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(addr, buffer, self.timeout) + pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, read, self.timeout) } /// Blocking write with a custom timeout - pub fn blocking_write_timeout(&mut self, addr: u8, bytes: &[u8], timeout: Duration) -> Result<(), Error> { - self.i2c.blocking_write_timeout(addr, bytes, timeout_fn(timeout)) + pub fn blocking_write_timeout(&mut self, addr: u8, write: &[u8], timeout: Duration) -> Result<(), Error> { + self.i2c.blocking_write_timeout(addr, write, timeout_fn(timeout)) } /// Blocking write with default timeout, provided in [`TimeoutI2c::new()`] - pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(addr, bytes, self.timeout) + pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, write, self.timeout) } /// Blocking write-read with a custom timeout pub fn blocking_write_read_timeout( &mut self, addr: u8, - bytes: &[u8], - buffer: &mut [u8], + write: &[u8], + read: &mut [u8], timeout: Duration, ) -> Result<(), Error> { self.i2c - .blocking_write_read_timeout(addr, bytes, buffer, timeout_fn(timeout)) + .blocking_write_read_timeout(addr, write, read, timeout_fn(timeout)) } /// Blocking write-read with default timeout, provided in [`TimeoutI2c::new()`] - pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(addr, bytes, buffer, self.timeout) + pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, write, read, self.timeout) } } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; - fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(addr, buffer) + fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, read) } } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(addr, bytes) + fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, write) } } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { type Error = Error; - fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(addr, bytes, buffer) + fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(addr, write, read) } } @@ -98,45 +98,24 @@ mod eh1 { } impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index f140e2b0d..4b47f0eb1 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -307,18 +307,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - pub fn blocking_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(addr, buffer, || Ok(())) + pub fn blocking_read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(addr, read, || Ok(())) } pub fn blocking_write_timeout( &mut self, addr: u8, - bytes: &[u8], + write: &[u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { unsafe { - self.write_bytes(addr, bytes, &check_timeout)?; + self.write_bytes(addr, write, &check_timeout)?; // Send a STOP condition T::regs().cr1().modify(|reg| reg.set_stop(true)); // Wait for STOP condition to transmit. @@ -331,49 +331,49 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - pub fn blocking_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(addr, bytes, || Ok(())) + pub fn blocking_write(&mut self, addr: u8, write: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(addr, write, || Ok(())) } pub fn blocking_write_read_timeout( &mut self, addr: u8, - bytes: &[u8], - buffer: &mut [u8], + write: &[u8], + read: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - unsafe { self.write_bytes(addr, bytes, &check_timeout)? }; - self.blocking_read_timeout(addr, buffer, &check_timeout)?; + unsafe { self.write_bytes(addr, write, &check_timeout)? }; + self.blocking_read_timeout(addr, read, &check_timeout)?; Ok(()) } - pub fn blocking_write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(addr, bytes, buffer, || Ok(())) + pub fn blocking_write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(addr, write, read, || Ok(())) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { type Error = Error; - fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(addr, buffer) + fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(addr, read) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { type Error = Error; - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(addr, bytes) + fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(addr, write) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { type Error = Error; - fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(addr, bytes, buffer) + fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(addr, write, read) } } @@ -402,46 +402,25 @@ mod eh1 { } impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 06ff07b21..28663fb36 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -345,12 +345,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { fn read_internal( &mut self, address: u8, - buffer: &mut [u8], + read: &mut [u8], restart: bool, check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - let completed_chunks = buffer.len() / 255; - let total_chunks = if completed_chunks * 255 == buffer.len() { + let completed_chunks = read.len() / 255; + let total_chunks = if completed_chunks * 255 == read.len() { completed_chunks } else { completed_chunks + 1 @@ -360,7 +360,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { unsafe { Self::master_read( address, - buffer.len().min(255), + read.len().min(255), Stop::Automatic, last_chunk_idx != 0, restart, @@ -368,7 +368,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { )?; } - for (number, chunk) in buffer.chunks_mut(255).enumerate() { + for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { @@ -391,12 +391,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { fn write_internal( &mut self, address: u8, - bytes: &[u8], + write: &[u8], send_stop: bool, check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - let completed_chunks = bytes.len() / 255; - let total_chunks = if completed_chunks * 255 == bytes.len() { + let completed_chunks = write.len() / 255; + let total_chunks = if completed_chunks * 255 == write.len() { completed_chunks } else { completed_chunks + 1 @@ -410,14 +410,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { unsafe { Self::master_write( address, - bytes.len().min(255), + write.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout, )?; } - for (number, chunk) in bytes.chunks(255).enumerate() { + for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { // NOTE(unsafe) We have &mut self unsafe { @@ -448,7 +448,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { async fn write_dma_internal( &mut self, address: u8, - bytes: &[u8], + write: &[u8], first_slice: bool, last_slice: bool, check_timeout: impl Fn() -> Result<(), Error>, @@ -456,7 +456,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { where TXDMA: crate::i2c::TxDma, { - let total_len = bytes.len(); + let total_len = write.len(); let completed_chunks = total_len / 255; let total_chunks = if completed_chunks * 255 == total_len { completed_chunks @@ -476,7 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let ch = &mut self.tx_dma; let request = ch.request(); - crate::dma::write(ch, request, bytes, dst) + crate::dma::write(ch, request, write, dst) }; let state = T::state(); @@ -641,25 +641,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // ========================= // Async public API - pub async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { - if bytes.is_empty() { - self.write_internal(address, bytes, true, || Ok(())) + if write.is_empty() { + self.write_internal(address, write, true, || Ok(())) } else { - self.write_dma_internal(address, bytes, true, true, || Ok(())).await + self.write_dma_internal(address, write, true, true, || Ok(())).await } } - pub async fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> + pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { - if bytes.is_empty() { + if write.is_empty() { return Err(Error::ZeroLengthTransfer); } - let mut iter = bytes.iter(); + let mut iter = write.iter(); let mut first = true; let mut current = iter.next(); @@ -685,21 +685,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - pub async fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { - if bytes.is_empty() { - self.write_internal(address, bytes, false, || Ok(()))?; + if write.is_empty() { + self.write_internal(address, write, false, || Ok(()))?; } else { - self.write_dma_internal(address, bytes, true, true, || Ok(())).await?; + self.write_dma_internal(address, write, true, true, || Ok(())).await?; } - if buffer.is_empty() { - self.read_internal(address, buffer, true, || Ok(()))?; + if read.is_empty() { + self.read_internal(address, read, true, || Ok(()))?; } else { - self.read_dma_internal(address, buffer, true, || Ok(())).await?; + self.read_dma_internal(address, read, true, || Ok(())).await?; } Ok(()) @@ -711,57 +711,57 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub fn blocking_read_timeout( &mut self, address: u8, - buffer: &mut [u8], + read: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - self.read_internal(address, buffer, false, &check_timeout) + self.read_internal(address, read, false, &check_timeout) // Automatic Stop } - pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_read_timeout(address, buffer, || Ok(())) + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + self.blocking_read_timeout(address, read, || Ok(())) } pub fn blocking_write_timeout( &mut self, address: u8, - bytes: &[u8], + write: &[u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - self.write_internal(address, bytes, true, &check_timeout) + self.write_internal(address, write, true, &check_timeout) } - pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> { - self.blocking_write_timeout(address, bytes, || Ok(())) + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + self.blocking_write_timeout(address, write, || Ok(())) } pub fn blocking_write_read_timeout( &mut self, address: u8, - bytes: &[u8], - buffer: &mut [u8], + write: &[u8], + read: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - self.write_internal(address, bytes, false, &check_timeout)?; - self.read_internal(address, buffer, true, &check_timeout) + self.write_internal(address, write, false, &check_timeout)?; + self.read_internal(address, read, true, &check_timeout) // Automatic Stop } - pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_write_read_timeout(address, bytes, buffer, || Ok(())) + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + self.blocking_write_read_timeout(address, write, read, || Ok(())) } pub fn blocking_write_vectored_timeout( &mut self, address: u8, - bytes: &[&[u8]], + write: &[&[u8]], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - if bytes.is_empty() { + if write.is_empty() { return Err(Error::ZeroLengthTransfer); } - let first_length = bytes[0].len(); - let last_slice_index = bytes.len() - 1; + let first_length = write[0].len(); + let last_slice_index = write.len() - 1; // NOTE(unsafe) We have &mut self unsafe { @@ -774,7 +774,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { )?; } - for (idx, slice) in bytes.iter().enumerate() { + for (idx, slice) in write.iter().enumerate() { let slice_len = slice.len(); let completed_chunks = slice_len / 255; let total_chunks = if completed_chunks * 255 == slice_len { @@ -828,8 +828,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - pub fn blocking_write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> { - self.blocking_write_vectored_timeout(address, bytes, || Ok(())) + pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { + self.blocking_write_vectored_timeout(address, write, || Ok(())) } } @@ -847,16 +847,16 @@ mod eh02 { impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { type Error = Error; - fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, bytes) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } } impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { type Error = Error; - fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, bytes, buffer) + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } } } @@ -1010,46 +1010,25 @@ mod eh1 { } impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) } - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) } - fn write_iter(&mut self, _address: u8, _bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) } - fn write_iter_read(&mut self, _address: u8, _bytes: B, _buffer: &mut [u8]) -> Result<(), Self::Error> - where - B: IntoIterator, - { - todo!(); - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction<'a>( + fn transaction( &mut self, _address: u8, - _operations: &mut [embedded_hal_1::i2c::Operation<'a>], + _operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { todo!(); } - - fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - todo!(); - } } } @@ -1059,27 +1038,22 @@ mod eha { use super::*; impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { - async fn read<'a>(&'a mut self, address: u8, read: &'a mut [u8]) -> Result<(), Self::Error> { + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.read(address, read).await } - async fn write<'a>(&'a mut self, address: u8, write: &'a [u8]) -> Result<(), Self::Error> { + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { self.write(address, write).await } - async fn write_read<'a>( - &'a mut self, - address: u8, - write: &'a [u8], - read: &'a mut [u8], - ) -> Result<(), Self::Error> { + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { self.write_read(address, write, read).await } - async fn transaction<'a, 'b>( - &'a mut self, + async fn transaction( + &mut self, address: u8, - operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], + operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { let _ = address; let _ = operations; diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 5b14814a1..38d31f1c4 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", 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 0ca176abd..cf1918724 100644 --- a/embassy-time/src/delay.rs +++ b/embassy-time/src/delay.rs @@ -19,14 +19,12 @@ mod eh1 { use super::*; impl embedded_hal_1::delay::DelayUs for Delay { - type Error = core::convert::Infallible; - - fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_micros(us as u64))) + fn delay_us(&mut self, us: u32) { + block_for(Duration::from_micros(us as u64)) } - fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { - Ok(block_for(Duration::from_millis(ms as u64))) + fn delay_ms(&mut self, ms: u32) { + block_for(Duration::from_millis(ms as u64)) } } } @@ -37,14 +35,12 @@ mod eha { use crate::Timer; impl embedded_hal_async::delay::DelayUs for Delay { - type Error = core::convert::Infallible; - - async fn delay_us(&mut self, micros: u32) -> Result<(), Self::Error> { - Ok(Timer::after(Duration::from_micros(micros as _)).await) + async fn delay_us(&mut self, micros: u32) { + Timer::after(Duration::from_micros(micros as _)).await } - async fn delay_ms(&mut self, millis: u32) -> Result<(), Self::Error> { - Ok(Timer::after(Duration::from_millis(millis as _)).await) + async fn delay_ms(&mut self, millis: u32) { + Timer::after(Duration::from_millis(millis as _)).await } } } diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index d1c8c1c5a..2ee6fcb00 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip RP2040" +runner = "probe-rs-cli run --chip RP2040" [build] target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index aea61eec5..63d0ac82a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } @@ -30,8 +31,8 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } -embedded-hal-async = "0.2.0-alpha.0" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = "0.2.0-alpha.1" embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = "1.0.0" diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 778cad3fa..85a19ce07 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -5,10 +5,13 @@ use core::cell::RefCell; use defmt::*; +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; use embassy_rp::spi::{Blocking, Spi}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; use embassy_time::Delay; use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -21,10 +24,9 @@ use st7789::{Orientation, ST7789}; use {defmt_rtt as _, panic_probe as _}; use crate::my_display_interface::SPIDeviceInterface; -use crate::shared_spi::SpiDeviceWithCs; use crate::touch::Touch; -//const DISPLAY_FREQ: u32 = 64_000_000; +const DISPLAY_FREQ: u32 = 64_000_000; const TOUCH_FREQ: u32 = 200_000; #[embassy_executor::main] @@ -43,16 +45,20 @@ async fn main(_spawner: Spawner) { //let touch_irq = p.PIN_17; // create SPI - let mut config = spi::Config::default(); - config.frequency = TOUCH_FREQ; // use the lowest freq - config.phase = spi::Phase::CaptureOnSecondTransition; - config.polarity = spi::Polarity::IdleHigh; + let mut display_config = spi::Config::default(); + display_config.frequency = DISPLAY_FREQ; + display_config.phase = spi::Phase::CaptureOnSecondTransition; + display_config.polarity = spi::Polarity::IdleHigh; + let mut touch_config = spi::Config::default(); + touch_config.frequency = TOUCH_FREQ; + touch_config.phase = spi::Phase::CaptureOnSecondTransition; + touch_config.polarity = spi::Polarity::IdleHigh; - let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); - let spi_bus = RefCell::new(spi); + let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); + let spi_bus: Mutex = Mutex::new(RefCell::new(spi)); - let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); - let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); + let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); + let touch_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(touch_cs, Level::High), touch_config); let mut touch = Touch::new(touch_spi); @@ -104,85 +110,9 @@ async fn main(_spawner: Spawner) { } } -mod shared_spi { - use core::cell::RefCell; - use core::fmt::Debug; - - use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi; - use embedded_hal_1::spi::SpiDevice; - - #[derive(Copy, Clone, Eq, PartialEq, Debug)] - pub enum SpiDeviceWithCsError { - #[allow(unused)] // will probably use in the future when adding a flush() to SpiBus - Spi(BUS), - Cs(CS), - } - - impl spi::Error for SpiDeviceWithCsError - where - BUS: spi::Error + Debug, - CS: Debug, - { - fn kind(&self) -> spi::ErrorKind { - match self { - Self::Spi(e) => e.kind(), - Self::Cs(_) => spi::ErrorKind::Other, - } - } - } - - pub struct SpiDeviceWithCs<'a, BUS, CS> { - bus: &'a RefCell, - cs: CS, - } - - impl<'a, BUS, CS> SpiDeviceWithCs<'a, BUS, CS> { - pub fn new(bus: &'a RefCell, cs: CS) -> Self { - Self { bus, cs } - } - } - - impl<'a, BUS, CS> spi::ErrorType for SpiDeviceWithCs<'a, BUS, CS> - where - BUS: spi::ErrorType, - CS: OutputPin, - { - type Error = SpiDeviceWithCsError; - } - - impl<'a, BUS, CS> SpiDevice for SpiDeviceWithCs<'a, BUS, CS> - where - BUS: spi::SpiBusFlush, - CS: OutputPin, - { - type Bus = BUS; - - fn transaction( - &mut self, - f: impl FnOnce(&mut Self::Bus) -> Result, - ) -> Result { - let mut bus = self.bus.borrow_mut(); - self.cs.set_low().map_err(SpiDeviceWithCsError::Cs)?; - - let f_res = f(&mut bus); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - let f_res = f_res.map_err(SpiDeviceWithCsError::Spi)?; - flush_res.map_err(SpiDeviceWithCsError::Spi)?; - cs_res.map_err(SpiDeviceWithCsError::Cs)?; - - Ok(f_res) - } - } -} - /// Driver for the XPT2046 resistive touchscreen sensor mod touch { - use embedded_hal_1::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; + use embedded_hal_1::spi::{Operation, SpiDevice}; struct Calibration { x1: i32, @@ -209,7 +139,6 @@ mod touch { impl Touch where SPI: SpiDevice, - SPI::Bus: SpiBus, { pub fn new(spi: SPI) -> Self { Self { spi } @@ -219,13 +148,12 @@ mod touch { let mut x = [0; 2]; let mut y = [0; 2]; self.spi - .transaction(|bus| { - bus.write(&[0x90])?; - bus.read(&mut x)?; - bus.write(&[0xd0])?; - bus.read(&mut y)?; - Ok(()) - }) + .transaction(&mut [ + Operation::Write(&[0x90]), + Operation::Read(&mut x), + Operation::Write(&[0xd0]), + Operation::Read(&mut y), + ]) .unwrap(); let x = (u16::from_be_bytes(x) >> 3) as i32; @@ -247,7 +175,7 @@ mod touch { mod my_display_interface { use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi::{SpiBusWrite, SpiDevice}; + use embedded_hal_1::spi::SpiDeviceWrite; /// SPI display interface. /// @@ -259,8 +187,7 @@ mod my_display_interface { impl SPIDeviceInterface where - SPI: SpiDevice, - SPI::Bus: SpiBusWrite, + SPI: SpiDeviceWrite, DC: OutputPin, { /// Create new SPI interface for communciation with a display driver @@ -271,42 +198,27 @@ mod my_display_interface { impl WriteOnlyDataCommand for SPIDeviceInterface where - SPI: SpiDevice, - SPI::Bus: SpiBusWrite, + SPI: SpiDeviceWrite, DC: OutputPin, { fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { - let r = self.spi.transaction(|bus| { - // 1 = data, 0 = command - if let Err(_) = self.dc.set_low() { - return Ok(Err(DisplayError::DCError)); - } + // 1 = data, 0 = command + self.dc.set_low().map_err(|_| DisplayError::DCError)?; - // Send words over SPI - send_u8(bus, cmds)?; - - Ok(Ok(())) - }); - r.map_err(|_| DisplayError::BusWriteError)? + send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; + Ok(()) } fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { - let r = self.spi.transaction(|bus| { - // 1 = data, 0 = command - if let Err(_) = self.dc.set_high() { - return Ok(Err(DisplayError::DCError)); - } + // 1 = data, 0 = command + self.dc.set_high().map_err(|_| DisplayError::DCError)?; - // Send words over SPI - send_u8(bus, buf)?; - - Ok(Ok(())) - }); - r.map_err(|_| DisplayError::BusWriteError)? + send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; + Ok(()) } } - fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { + fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { match words { DataFormat::U8(slice) => spi.write(slice), DataFormat::U16(slice) => { diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 387af783a..99f37cdda 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 70702863f..f240c3896 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -19,8 +19,8 @@ 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.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d0d6a9497..154f5a987 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -19,8 +19,8 @@ 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.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 7c254eba3..fa39df6db 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -18,8 +18,8 @@ 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.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } 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/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index eb447be35..463a370fe 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -17,8 +17,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6" } 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.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 17b640797..6070a5a87 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -25,8 +25,8 @@ 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.2.0-alpha.0" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } [profile.dev] From dee1d51ad3089b03c67aaa75c7985cf95ce90eec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 7 Apr 2023 02:20:59 +0200 Subject: [PATCH 0844/1575] stm32: remove subghz feature. It's available only on WL. if you're using a WL, you want subghz for sure. --- embassy-lora/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 1 - embassy-stm32/src/lib.rs | 5 ++--- examples/stm32wl/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index c9174ea82..784cc228d 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -19,7 +19,7 @@ flavors = [ [features] sx126x = [] sx127x = [] -stm32wl = ["embassy-stm32", "embassy-stm32/subghz"] +stm32wl = ["dep:embassy-stm32"] time = [] defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6710ff2d0..36ee58018 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -81,7 +81,6 @@ stm32-metapac = { version = "4", default-features = false, features = ["metadata default = ["stm32-metapac/rt"] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] -subghz = [] exti = [] # Enables additional driver features that depend on embassy-time diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 3f2d078f8..d4d7155bd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -53,6 +53,8 @@ pub mod rng; pub mod sdmmc; #[cfg(spi)] pub mod spi; +#[cfg(stm32wl)] +pub mod subghz; #[cfg(usart)] pub mod usart; #[cfg(all(usb, feature = "time"))] @@ -62,9 +64,6 @@ pub mod usb_otg; #[cfg(iwdg)] pub mod wdg; -#[cfg(feature = "subghz")] -pub mod subghz; - // This must go last, so that it sees all the impl_foo! macros defined earlier. pub(crate) mod _generated { #![allow(dead_code)] diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 9fc7e0f4a..0d2194ea2 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } From 8469a2409c9ded4bb66040475f365c60b261b51f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 7 Apr 2023 02:21:38 +0200 Subject: [PATCH 0845/1575] stm32/otg: add U5 support. --- embassy-stm32/src/usb_otg/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs index 84fef78cb..193e0df0d 100644 --- a/embassy-stm32/src/usb_otg/mod.rs +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -89,6 +89,9 @@ foreach_interrupt!( } else if #[cfg(stm32h7)] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; + } else if #[cfg(stm32u5)] { + const FIFO_DEPTH_WORDS: u16 = 320; + const ENDPOINT_COUNT: usize = 6; } else { compile_error!("USB_OTG_FS peripheral is not supported by this chip."); } @@ -137,6 +140,9 @@ foreach_interrupt!( ))] { const FIFO_DEPTH_WORDS: u16 = 1024; const ENDPOINT_COUNT: usize = 9; + } else if #[cfg(stm32u5)] { + const FIFO_DEPTH_WORDS: u16 = 1024; + const ENDPOINT_COUNT: usize = 9; } else { compile_error!("USB_OTG_HS peripheral is not supported by this chip."); } From f38899728c8bc9c81c4ac02d8177ec6420c07f02 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 7 Apr 2023 02:22:10 +0200 Subject: [PATCH 0846/1575] stm32: add h5 flavor. --- embassy-stm32/Cargo.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 36ee58018..4ab306223 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -8,10 +8,7 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" -# TODO: sdmmc -# TODO: net -# TODO: subghz -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, @@ -22,6 +19,7 @@ flavors = [ { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, From eb32d8ebbd86c0805c9e72f368a718b9977adf12 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Fri, 7 Apr 2023 19:54:05 +0200 Subject: [PATCH 0847/1575] update embassy --- Cargo.toml | 10 +++++----- examples/rpi-pico-w/Cargo.toml | 18 +++++++++--------- examples/rpi-pico-w/src/main.rs | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 04a47a3a5..acd3865e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,11 +28,11 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } num_enum = { version = "0.5.7", default-features = false } [patch.crates-io] -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } [workspace] members = ["cyw43-pio"] diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 41ee8a3c4..17ee25c33 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio" } -embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers"] } +embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } @@ -27,14 +27,14 @@ heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e3f8020c3bdf726dfa451b5b190f27191507a18f" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index 4b1623be7..d5610d2ba 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -14,7 +14,7 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Flex, Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_24, PIN_25, PIN_29}; -use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachineInstance, Sm0}; +use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachineInstance, Sm0}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; From 056b8ab5a2e277af7ceb845e87b38747ae9d950c Mon Sep 17 00:00:00 2001 From: kbleeke Date: Fri, 7 Apr 2023 19:54:39 +0200 Subject: [PATCH 0848/1575] update nightly to embassy nightly --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 20c10c3f1..885199792 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-2023-03-19" +channel = "nightly-2023-04-02" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", From 4d2710ed4d978b71602b9f1c809d721650e0c1e9 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Fri, 7 Apr 2023 19:55:46 +0200 Subject: [PATCH 0849/1575] pin defmt to 0.3.2. 0.3.4 introduces an undesired wire format upgrade --- examples/rpi-pico-w/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 17ee25c33..dca796e3d 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -14,7 +14,7 @@ embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium atomic-polyfill = "0.1.5" static_cell = "1.0" -defmt = "0.3" +defmt = "=0.3.2" defmt-rtt = "0.3" panic-probe = { version = "0.3", features = ["print-defmt"] } From 683ad8047996709fce819c90f6cad47c096a57b7 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Fri, 7 Apr 2023 19:56:05 +0200 Subject: [PATCH 0850/1575] update embedded-hal --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index acd3865e7..4ea5d8b6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ cortex-m = "0.7.3" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.9" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } num_enum = { version = "0.5.7", default-features = false } [patch.crates-io] From 1fbb8f0b321a15fd6b473c466d41fac40302ec3c Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Sun, 9 Apr 2023 09:15:57 +0100 Subject: [PATCH 0851/1575] feat(rp): add `Wait` impl to `OutputOpenDrain` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A while ago `OutputOpenDrain` was made to implement `InputPin`, something that allowed drivers for various one-wire protocols to be written, but it's been lacking a `Wait` implementation — something that's needed to write async versions of these drivers. This commit also adds `get_level()` to `OutputOpenDrain`, since `is_high()` and `is_low()` were already implemented, but `get_level()` itself was missing. --- embassy-rp/src/gpio.rs | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index fb45ef7cf..98e182868 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -437,6 +437,37 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { pub fn is_low(&self) -> bool { self.pin.is_low() } + + /// Returns current pin level + #[inline] + pub fn get_level(&self) -> Level { + self.is_high().into() + } + + #[inline] + pub async fn wait_for_high(&mut self) { + self.pin.wait_for_high().await; + } + + #[inline] + pub async fn wait_for_low(&mut self) { + self.pin.wait_for_low().await; + } + + #[inline] + pub async fn wait_for_rising_edge(&mut self) { + self.pin.wait_for_rising_edge().await; + } + + #[inline] + pub async fn wait_for_falling_edge(&mut self) { + self.pin.wait_for_falling_edge().await; + } + + #[inline] + pub async fn wait_for_any_edge(&mut self) { + self.pin.wait_for_any_edge().await; + } } /// GPIO flexible pin. @@ -1117,4 +1148,32 @@ mod eh1 { Ok(()) } } + + #[cfg(feature = "nightly")] + impl<'d, T: Pin> embedded_hal_async::digital::Wait for OutputOpenDrain<'d, T> { + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) + } + + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) + } + + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) + } + + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) + } + + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) + } + } } From dee8c71a2d57f3015fde55da28758a35fcbf7ca6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 9 Apr 2023 22:40:09 +0200 Subject: [PATCH 0852/1575] lora: fix embassy docs build. --- embassy-lora/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 784cc228d..604358c5b 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -9,9 +9,9 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, - { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, - { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] }, + { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, + { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x"] }, + { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, ] [lib] From 44b7fe45e279f713a49f92e786f358af6c68e157 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 15:11:07 +0200 Subject: [PATCH 0853/1575] stm32/gpdma: fix race condition when resetting channel when done. --- embassy-stm32/src/dma/gpdma.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 442fee48e..6f26fd194 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -190,6 +190,10 @@ mod low_level_api { fence(Ordering::SeqCst); let ch = dma.ch(channel_number as _); + + // Reset ch + ch.cr().write(|w| w.set_reset(true)); + ch.llr().write(|_| {}); // no linked list ch.tr1().write(|w| { w.set_sdw(data_size.into()); @@ -252,7 +256,7 @@ mod low_level_api { /// Gets the running status of the channel pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool { let ch = dma.ch(ch as _); - !ch.sr().read().idlef() + !ch.sr().read().tcf() } /// Gets the total remaining transfers for the channel @@ -291,7 +295,10 @@ mod low_level_api { } if sr.suspf() || sr.tcf() { - ch.cr().write(|w| w.set_reset(true)); + // disable all xxIEs to prevent the irq from firing again. + ch.cr().write(|_| {}); + + // Wake the future. It'll look at tcf and see it's set. STATE.channels[state_index].waker.wake(); } } From 4ef8e008e8e052e5e2f84b36733284b19d246794 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 15:11:23 +0200 Subject: [PATCH 0854/1575] stm32/spi: add v4/v5 support (for H5). --- embassy-stm32/src/spi/mod.rs | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 1f1708873..481ea4abc 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -258,7 +258,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(true); }); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] unsafe { T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff); T::REGS.cfg2().modify(|w| { @@ -317,7 +317,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] unsafe { T::REGS.cfg2().modify(|w| { w.set_cpha(cpha); @@ -330,7 +330,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn get_current_config(&self) -> Config { #[cfg(any(spi_v1, spi_f1, spi_v2))] let cfg = unsafe { T::REGS.cr1().read() }; - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] let cfg = unsafe { T::REGS.cfg2().read() }; let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow @@ -383,7 +383,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(true); }); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] unsafe { T::REGS.cr1().modify(|w| { w.set_csusp(true); @@ -429,7 +429,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -459,7 +459,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] flush_rx_fifo(T::REGS); set_rxdmaen(T::REGS, true); @@ -481,7 +481,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -514,7 +514,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] flush_rx_fifo(T::REGS); set_rxdmaen(T::REGS, true); @@ -534,7 +534,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cr1().modify(|w| { w.set_spe(true); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] T::REGS.cr1().modify(|w| { w.set_cstart(true); }); @@ -619,9 +619,9 @@ impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> { } } -#[cfg(not(any(spi_v3, spi_v4)))] +#[cfg(not(any(spi_v3, spi_v4, spi_v5)))] use vals::Br; -#[cfg(any(spi_v3, spi_v4))] +#[cfg(any(spi_v3, spi_v4, spi_v5))] use vals::Mbr as Br; fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { @@ -647,17 +647,17 @@ trait RegsExt { impl RegsExt for Regs { fn tx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] let dr = self.dr(); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] let dr = self.txdr(); dr.ptr() as *mut W } fn rx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] let dr = self.dr(); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] let dr = self.rxdr(); dr.ptr() as *mut W } @@ -667,22 +667,22 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> { if sr.ovr() { return Err(Error::Overrun); } - #[cfg(not(any(spi_f1, spi_v3, spi_v4)))] + #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] if sr.fre() { return Err(Error::Framing); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.tifre() { return Err(Error::Framing); } if sr.modf() { return Err(Error::ModeFault); } - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.crcerr() { return Err(Error::Crc); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.crce() { return Err(Error::Crc); } @@ -696,11 +696,11 @@ fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> { check_error_flags(sr)?; - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.txe() { return Ok(()); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.txp() { return Ok(()); } @@ -713,11 +713,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { check_error_flags(sr)?; - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.rxne() { return Ok(()); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] if sr.rxp() { return Ok(()); } @@ -726,11 +726,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { fn flush_rx_fifo(regs: Regs) { unsafe { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().rxne() { let _ = regs.dr().read(); } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] while regs.sr().read().rxp() { let _ = regs.rxdr().read(); } @@ -739,11 +739,11 @@ fn flush_rx_fifo(regs: Regs) { fn set_txdmaen(regs: Regs, val: bool) { unsafe { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_txdmaen(val); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cfg1().modify(|reg| { reg.set_txdmaen(val); }); @@ -752,11 +752,11 @@ fn set_txdmaen(regs: Regs, val: bool) { fn set_rxdmaen(regs: Regs, val: bool) { unsafe { - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_rxdmaen(val); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cfg1().modify(|reg| { reg.set_rxdmaen(val); }); @@ -768,9 +768,9 @@ fn finish_dma(regs: Regs) { #[cfg(spi_v2)] while regs.sr().read().ftlvl() > 0 {} - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] while !regs.sr().read().txc() {} - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().bsy() {} // Disable the spi peripheral @@ -780,12 +780,12 @@ fn finish_dma(regs: Regs) { // The peripheral automatically disables the DMA stream on completion without error, // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. - #[cfg(not(any(spi_v3, spi_v4)))] + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_txdmaen(false); reg.set_rxdmaen(false); }); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cfg1().modify(|reg| { reg.set_txdmaen(false); reg.set_rxdmaen(false); @@ -799,7 +799,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { unsafe { ptr::write_volatile(regs.tx_ptr(), tx_word); - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] regs.cr1().modify(|reg| reg.set_cstart(true)); } @@ -970,7 +970,7 @@ pub(crate) mod sealed { } } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] pub fn dsize(&self) -> u8 { match self { WordSize::EightBit => 0b0111, @@ -978,7 +978,7 @@ pub(crate) mod sealed { } } - #[cfg(any(spi_v3, spi_v4))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] pub fn _frxth(&self) -> vals::Fthlv { match self { WordSize::EightBit => vals::Fthlv::ONEFRAME, From dbfd28130f95a6f8df81823253ec71c1ae6d36e6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 15:12:47 +0200 Subject: [PATCH 0855/1575] stm32/test: add h5 hil tests. --- ci.sh | 1 + tests/stm32/Cargo.toml | 1 + tests/stm32/src/bin/gpio.rs | 2 ++ tests/stm32/src/bin/spi.rs | 19 +++++++++++-------- tests/stm32/src/bin/spi_dma.rs | 18 ++++++++++-------- tests/stm32/src/bin/usart.rs | 2 ++ tests/stm32/src/bin/usart_dma.rs | 9 +++++++++ 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/ci.sh b/ci.sh index 82b72ae32..47bf5d660 100755 --- a/ci.sh +++ b/ci.sh @@ -119,6 +119,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/nucleo-stm32h563zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 6070a5a87..bd181f235 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -11,6 +11,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo +stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 18fd85d44..6a36df8cc 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -30,6 +30,8 @@ async fn main(_spawner: Spawner) { let (mut a, mut b) = (p.PB6, p.PB7); #[cfg(feature = "stm32u585ai")] let (mut a, mut b) = (p.PD9, p.PD8); + #[cfg(feature = "stm32h563zi")] + let (mut a, mut b) = (p.PB6, p.PB7); // Test initial output { diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 1c5dc87c0..bf8098b1b 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -17,22 +17,25 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); #[cfg(feature = "stm32f103c8")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32f429zi")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32h755zi")] - let (sck, mosi, miso) = (p.PA5, p.PB5, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PB5, p.PA6); #[cfg(feature = "stm32g491re")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32g071rb")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32wb55rg")] - let (sck, mosi, miso) = (p.PA5, p.PA7, p.PA6); + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); #[cfg(feature = "stm32u585ai")] - let (sck, mosi, miso) = (p.PE13, p.PE15, p.PE14); + let (spi, sck, mosi, miso) = (p.SPI1, p.PE13, p.PE15, p.PE14); + #[cfg(feature = "stm32h563zi")] + let (spi, sck, mosi, miso) = (p.SPI4, p.PE12, p.PE14, p.PE13); + info!("asdfa;"); let mut spi = Spi::new( - p.SPI1, + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index cb2152e0b..b3dad8132 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -16,22 +16,24 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); #[cfg(feature = "stm32f103c8")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA2_CH3, p.DMA2_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA2_CH3, p.DMA2_CH2); #[cfg(feature = "stm32h755zi")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PB5, p.PA6, p.DMA1_CH0, p.DMA1_CH1); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PB5, p.PA6, p.DMA1_CH0, p.DMA1_CH1); #[cfg(feature = "stm32g491re")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32g071rb")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32wb55rg")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32u585ai")] - let (sck, mosi, miso, tx_dma, rx_dma) = (p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); + #[cfg(feature = "stm32h563zi")] + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI4, p.PE12, p.PE14, p.PE13, p.GPDMA1_CH0, p.GPDMA1_CH1); let mut spi = Spi::new( - p.SPI1, + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index af55867f2..52409567c 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -32,6 +32,8 @@ async fn main(_spawner: Spawner) { let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32u585ai")] let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); + #[cfg(feature = "stm32h563zi")] + let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index d12605a9a..3f70791c1 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -62,6 +62,15 @@ async fn main(_spawner: Spawner) { p.GPDMA1_CH0, p.GPDMA1_CH1, ); + #[cfg(feature = "stm32h563zi")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PB6, + p.PB7, + p.LPUART1, + interrupt::take!(LPUART1), + p.GPDMA1_CH0, + p.GPDMA1_CH1, + ); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); From 6760258ec39c629b911a417d0a554bc6167c5b5b Mon Sep 17 00:00:00 2001 From: Glenn Dirkx Date: Mon, 10 Apr 2023 16:20:47 +0200 Subject: [PATCH 0856/1575] fix I2C controller problems after NACK --- embassy-stm32/src/i2c/v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 28663fb36..7218f7706 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -262,7 +262,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if T::regs().isr().read().txis() { T::regs().txdr().write(|w| w.set_txdata(0)); } - if T::regs().isr().read().txe() { + if !T::regs().isr().read().txe() { T::regs().isr().modify(|w| w.set_txe(true)) } } From cae683cd41261312e8a8da70c862832f0acb847c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 10 Apr 2023 21:12:48 +0200 Subject: [PATCH 0857/1575] stm32: update pac. --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4ab306223..a8ebacd25 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "4" +stm32-metapac = "5" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -73,7 +73,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "4", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "5", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] From d3ce64254aecb24feded2408119855daf7380e80 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 11 Apr 2023 07:46:05 +0200 Subject: [PATCH 0858/1575] Let update_len be u32 --- embassy-boot/boot/src/firmware_updater.rs | 16 +++++++--------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 6aedec003..a2f822f4a 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -114,11 +114,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len as u32 <= self.dfu.size()); + assert!(_update_len <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { @@ -175,11 +175,10 @@ impl FirmwareUpdater { pub async fn hash( &mut self, dfu_flash: &mut F, - update_len: usize, + update_len: u32, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read(dfu_flash, offset, chunk_buf).await?; @@ -335,11 +334,11 @@ impl FirmwareUpdater { _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], - _update_len: usize, + _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len as u32 <= self.dfu.size()); + assert!(_update_len <= self.dfu.size()); #[cfg(feature = "ed25519-dalek")] { @@ -394,11 +393,10 @@ impl FirmwareUpdater { pub fn hash_blocking( &mut self, dfu_flash: &mut F, - update_len: usize, + update_len: u32, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - let update_len = update_len as u32; let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; @@ -529,7 +527,7 @@ mod tests { block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; - block_on(updater.hash::<_, Sha1>(&mut flash, update.len(), &mut chunk_buf, &mut hash)).unwrap(); + block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); assert_eq!(Sha1::digest(update).as_slice(), hash); } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index ef9333d36..87457b173 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -255,7 +255,7 @@ mod tests { &mut flash, &public_key.to_bytes(), &signature.to_bytes(), - firmware_len, + firmware_len as u32, &mut aligned, )) .is_ok()); From ab6179fb07f2655f96c0fd58aeaf09268fe60ef3 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Tue, 11 Apr 2023 12:03:08 +0200 Subject: [PATCH 0859/1575] Fix duplicate package name --- examples/stm32h5/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index f240c3896..b77d376ca 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-stm32h7-examples" +name = "embassy-stm32h5-examples" version = "0.1.0" license = "MIT OR Apache-2.0" From 35a34afbc22fc526b0c6dfac0946829c54d0b3b5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 11 Apr 2023 13:36:35 +0200 Subject: [PATCH 0860/1575] Update nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9785cd9eb..f7183d167 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-2023-04-02" +channel = "nightly-2023-04-11" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From f51cbebffd8a2c7a66888b5cbd288a6eb912a784 Mon Sep 17 00:00:00 2001 From: sander Date: Tue, 11 Apr 2023 13:49:32 +0200 Subject: [PATCH 0861/1575] embassy-boot: add nightly feature gates --- embassy-boot/boot/Cargo.toml | 2 +- embassy-boot/boot/src/firmware_updater.rs | 440 +++++++++++----------- embassy-boot/boot/src/mem_flash.rs | 3 + embassy-boot/boot/src/partition.rs | 70 ++-- 4 files changed, 265 insertions(+), 250 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 39f501570..7434dce85 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -45,7 +45,7 @@ default_features = false features = ["rand", "std", "u32_backend"] [features] -default = ["nightly"] +#default = ["nightly"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index a2f822f4a..f3d76a56c 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -1,5 +1,6 @@ use digest::Digest; use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; +#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; @@ -73,222 +74,6 @@ impl FirmwareUpdater { Self { dfu, state } } - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub async fn get_state( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read(state_flash, 0, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(feature = "_verify")] - pub async fn verify_and_mark_updated( - &mut self, - _state_and_dfu_flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: u32, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; - - use crate::digest_adapters::ed25519_dalek::Sha512; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; - - public_key.verify(&message, &signature).map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Signature}; - - use crate::digest_adapters::salty::Sha512; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; - - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await - } - - /// Verify the update in DFU with any digest. - pub async fn hash( - &mut self, - dfu_flash: &mut F, - update_len: u32, - chunk_buf: &mut [u8], - output: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let mut digest = D::new(); - for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read(dfu_flash, offset, chunk_buf).await?; - let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); - digest.update(&chunk_buf[..len]); - } - output.copy_from_slice(digest.finalize().as_slice()); - Ok(()) - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(not(feature = "_verify"))] - pub async fn mark_updated( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, state_flash).await - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, state_flash).await - } - - async fn set_magic( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read(state_flash, 0, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; - } - - // Clear magic and progress - self.state.wipe(state_flash).await?; - - // Set magic - aligned.fill(magic); - self.state.write(state_flash, 0, aligned).await?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - self.dfu - .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) - .await?; - - self.dfu.write(dfu_flash, offset as u32, data).await?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning its `Partition`. - /// - /// Using this instead of `write_firmware` allows for an optimized API in - /// exchange for added complexity. - pub async fn prepare_update( - &mut self, - dfu_flash: &mut F, - ) -> Result { - self.dfu.wipe(dfu_flash).await?; - - Ok(self.dfu) - } - // // Blocking API // @@ -504,7 +289,228 @@ impl FirmwareUpdater { } } -#[cfg(test)] +// Async API +#[cfg(feature = "nightly")] +impl FirmwareUpdater { + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result { + self.state.read(state_flash, 0, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub async fn verify_and_mark_updated( + &mut self, + _state_and_dfu_flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: u32, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_update_len <= self.dfu.size()); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) + .await?; + + public_key.verify(&message, &signature).map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) + .await?; + + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await + } + + /// Verify the update in DFU with any digest. + pub async fn hash( + &mut self, + dfu_flash: &mut F, + update_len: u32, + chunk_buf: &mut [u8], + output: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read(dfu_flash, offset, chunk_buf).await?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); + } + output.copy_from_slice(digest.finalize().as_slice()); + Ok(()) + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub async fn mark_updated( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC, state_flash).await + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_booted( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC, state_flash).await + } + + async fn set_magic( + &mut self, + aligned: &mut [u8], + magic: u8, + state_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + self.state.read(state_flash, 0, aligned).await?; + + if aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; + + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; + } + + // Clear magic and progress + self.state.wipe(state_flash).await?; + + // Set magic + aligned.fill(magic); + self.state.write(state_flash, 0, aligned).await?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + dfu_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + self.dfu + .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) + .await?; + + self.dfu.write(dfu_flash, offset as u32, data).await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning its `Partition`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub async fn prepare_update( + &mut self, + dfu_flash: &mut F, + ) -> Result { + self.dfu.wipe(dfu_flash).await?; + + Ok(self.dfu) + } +} + + + #[cfg(test)] mod tests { use futures::executor::block_on; use sha1::{Digest, Sha1}; diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index c62379b24..3ba84a92c 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -3,6 +3,7 @@ use core::ops::{Bound, Range, RangeBounds}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; pub struct MemFlash { @@ -134,6 +135,7 @@ impl NorFla } } +#[cfg(feature = "nightly")] impl AsyncReadNorFlash for MemFlash { @@ -148,6 +150,7 @@ impl AsyncR } } +#[cfg(feature = "nightly")] impl AsyncNorFlash for MemFlash { diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 7529059b6..67bd7abba 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -1,4 +1,5 @@ use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; /// A region in flash used by the bootloader. @@ -22,6 +23,43 @@ impl Partition { self.to - self.from } + /// Read from the partition on the provided flash + pub fn read_blocking(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.read(offset, bytes) + } + + /// Write to the partition on the provided flash + pub fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.write(offset, bytes)?; + trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); + Ok(()) + } + + /// Erase part of the partition on the provided flash + pub fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + let from = self.from as u32 + from; + let to = self.from as u32 + to; + flash.erase(from, to)?; + trace!("Erased from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Erase the entire partition + pub(crate) fn wipe_blocking(&self, flash: &mut F) -> Result<(), F::Error> { + let from = self.from as u32; + let to = self.to as u32; + flash.erase(from, to)?; + trace!("Wiped from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } +} + +// Async API +#[cfg(feature = "nightly")] +impl Partition { + /// Read from the partition on the provided flash pub async fn read( &self, @@ -58,38 +96,6 @@ impl Partition { trace!("Wiped from 0x{:x} to 0x{:x}", from, to); Ok(()) } - - /// Read from the partition on the provided flash - pub fn read_blocking(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.read(offset, bytes) - } - - /// Write to the partition on the provided flash - pub fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.write(offset, bytes)?; - trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); - Ok(()) - } - - /// Erase part of the partition on the provided flash - pub fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { - let from = self.from as u32 + from; - let to = self.from as u32 + to; - flash.erase(from, to)?; - trace!("Erased from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Erase the entire partition - pub(crate) fn wipe_blocking(&self, flash: &mut F) -> Result<(), F::Error> { - let from = self.from as u32; - let to = self.to as u32; - flash.erase(from, to)?; - trace!("Wiped from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } } #[cfg(test)] From 1b86570cfd480b0821de555780a2dbdaa7ec562d Mon Sep 17 00:00:00 2001 From: sander Date: Tue, 11 Apr 2023 13:55:19 +0200 Subject: [PATCH 0862/1575] embassy-boot: readd nightly feature as default --- embassy-boot/boot/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 7434dce85..39f501570 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -45,7 +45,7 @@ default_features = false features = ["rand", "std", "u32_backend"] [features] -#default = ["nightly"] +default = ["nightly"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] From f5df5676193cc76f7dca3271882777b7ed00e269 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 11 Apr 2023 13:37:10 +0200 Subject: [PATCH 0863/1575] stm32/test: add C0 hil tests. --- ci.sh | 1 + embassy-stm32/Cargo.toml | 4 ++-- tests/stm32/Cargo.toml | 3 ++- tests/stm32/build.rs | 6 ++++++ tests/stm32/src/bin/gpio.rs | 2 ++ tests/stm32/src/bin/spi.rs | 4 +++- tests/stm32/src/bin/spi_dma.rs | 2 ++ tests/stm32/src/bin/usart.rs | 2 ++ tests/stm32/src/bin/usart_dma.rs | 3 +++ 9 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ci.sh b/ci.sh index 47bf5d660..f81b34c05 100755 --- a/ci.sh +++ b/ci.sh @@ -117,6 +117,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/nucleo-stm32f429zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/nucleo-stm32g491re \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/nucleo-stm32c031c6 \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/nucleo-stm32h563zi \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a8ebacd25..51ca9ff71 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "5" +stm32-metapac = "6" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -73,7 +73,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "5", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "6", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bd181f235..227eabe73 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo +stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo @@ -18,7 +19,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-tim2"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } defmt = "0.3.0" defmt-rtt = "0.4" diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 6f4872249..3e67a7392 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -9,7 +9,13 @@ fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed=link_ram.x"); println!("cargo:rustc-link-arg-bins=--nmagic"); + + // too little RAM to run from RAM. + #[cfg(any(feature = "stm32c031c6"))] + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + #[cfg(not(any(feature = "stm32c031c6")))] println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); Ok(()) diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 6a36df8cc..8b99b08a5 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -32,6 +32,8 @@ async fn main(_spawner: Spawner) { let (mut a, mut b) = (p.PD9, p.PD8); #[cfg(feature = "stm32h563zi")] let (mut a, mut b) = (p.PB6, p.PB7); + #[cfg(feature = "stm32c031c6")] + let (mut a, mut b) = (p.PB6, p.PB7); // Test initial output { diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index bf8098b1b..0f5e563b1 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -32,6 +32,8 @@ async fn main(_spawner: Spawner) { let (spi, sck, mosi, miso) = (p.SPI1, p.PE13, p.PE15, p.PE14); #[cfg(feature = "stm32h563zi")] let (spi, sck, mosi, miso) = (p.SPI4, p.PE12, p.PE14, p.PE13); + #[cfg(feature = "stm32c031c6")] + let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); info!("asdfa;"); let mut spi = Spi::new( @@ -49,7 +51,7 @@ async fn main(_spawner: Spawner) { // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor. // so we should get the data we sent back. - let mut buf = data; + let mut buf = [0; 9]; spi.blocking_transfer(&mut buf, &data).unwrap(); assert_eq!(buf, data); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index b3dad8132..74776ebf8 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -31,6 +31,8 @@ async fn main(_spawner: Spawner) { let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PE13, p.PE15, p.PE14, p.GPDMA1_CH0, p.GPDMA1_CH1); #[cfg(feature = "stm32h563zi")] let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI4, p.PE12, p.PE14, p.PE13, p.GPDMA1_CH0, p.GPDMA1_CH1); + #[cfg(feature = "stm32c031c6")] + let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); let mut spi = Spi::new( spi, diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 52409567c..cca8c42ee 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -34,6 +34,8 @@ async fn main(_spawner: Spawner) { let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); #[cfg(feature = "stm32h563zi")] let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); + #[cfg(feature = "stm32c031c6")] + let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index 3f70791c1..d673df0f3 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -71,6 +71,9 @@ async fn main(_spawner: Spawner) { p.GPDMA1_CH0, p.GPDMA1_CH1, ); + #[cfg(feature = "stm32c031c6")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); let config = Config::default(); let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); From 32836129f6d0aa25f51cd60b0039072530009711 Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 11 Apr 2023 14:59:38 +0200 Subject: [PATCH 0864/1575] re-export main_riscv macro as main for riscv arch. --- embassy-executor/src/arch/riscv32.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index f66daeae4..ff7ec1575 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -8,6 +8,9 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; + #[cfg(feature = "nightly")] + pub use embassy_macros::main_riscv as main; + use crate::raw::{Pender, PenderInner}; use crate::{raw, Spawner}; From 00258bca4320bcf33f6285126a1f31fd6a9ed83c Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 11 Apr 2023 16:53:04 +0200 Subject: [PATCH 0865/1575] Add empty test binary for riscv --- ci.sh | 2 ++ tests/riscv32/.cargo/config.toml | 9 +++++++ tests/riscv32/Cargo.toml | 46 ++++++++++++++++++++++++++++++++ tests/riscv32/memory.x | 14 ++++++++++ tests/riscv32/src/bin/empty.rs | 16 +++++++++++ 5 files changed, 87 insertions(+) create mode 100644 tests/riscv32/.cargo/config.toml create mode 100644 tests/riscv32/Cargo.toml create mode 100644 tests/riscv32/memory.x create mode 100644 tests/riscv32/src/bin/empty.rs diff --git a/ci.sh b/ci.sh index f81b34c05..30fca4bc4 100755 --- a/ci.sh +++ b/ci.sh @@ -124,6 +124,8 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ + --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ + $BUILD_EXTRA diff --git a/tests/riscv32/.cargo/config.toml b/tests/riscv32/.cargo/config.toml new file mode 100644 index 000000000..1ffb0305d --- /dev/null +++ b/tests/riscv32/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.riscv32imac-unknown-none-elf] +runner = "true" +rustflags = [ + "-C", "link-arg=-Tmemory.x", + "-C", "link-arg=-Tlink.x", +] + +[build] +target = "riscv32imac-unknown-none-elf" diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml new file mode 100644 index 000000000..885776ae6 --- /dev/null +++ b/tests/riscv32/Cargo.toml @@ -0,0 +1,46 @@ +[package] +edition = "2021" +name = "embassy-riscv-tests" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +critical-section = { version = "1.1.1", features = ["restore-state-bool"] } +embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time" } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +riscv-rt = "0.11" +riscv = { version = "0.10", features = ["critical-section-single-hart"] } + + +[profile.dev] +debug = 2 +debug-assertions = true +opt-level = 's' +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = "fat" +opt-level = 's' +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/tests/riscv32/memory.x b/tests/riscv32/memory.x new file mode 100644 index 000000000..316d577d4 --- /dev/null +++ b/tests/riscv32/memory.x @@ -0,0 +1,14 @@ +MEMORY +{ + ROM : ORIGIN = 0x80000000, LENGTH = 0x00020000 + RAM : ORIGIN = 0x84000000, LENGTH = 0x00008000 +} + +REGION_ALIAS("REGION_TEXT", ROM); +REGION_ALIAS("REGION_RODATA", ROM); +REGION_ALIAS("REGION_DATA", RAM); +REGION_ALIAS("REGION_BSS", RAM); +REGION_ALIAS("REGION_HEAP", RAM); +REGION_ALIAS("REGION_STACK", RAM); + +_stack_start = ORIGIN(RAM) + LENGTH(RAM) - 4; diff --git a/tests/riscv32/src/bin/empty.rs b/tests/riscv32/src/bin/empty.rs new file mode 100644 index 000000000..d5a55c84f --- /dev/null +++ b/tests/riscv32/src/bin/empty.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; + +#[panic_handler] +fn panic (_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Don't do anything, just make sure it compiles. + loop {} +} From e18380195732c5ca339257521dc2fc9add5ac1ba Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 11 Apr 2023 17:04:25 +0200 Subject: [PATCH 0866/1575] Rustfmt --- tests/riscv32/src/bin/empty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/riscv32/src/bin/empty.rs b/tests/riscv32/src/bin/empty.rs index d5a55c84f..1874caec4 100644 --- a/tests/riscv32/src/bin/empty.rs +++ b/tests/riscv32/src/bin/empty.rs @@ -5,7 +5,7 @@ use embassy_executor::Spawner; #[panic_handler] -fn panic (_info: &core::panic::PanicInfo) -> ! { +fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } From 6e947c83b652d2e9aec8b106f9b171847a46f9dd Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 11 Apr 2023 17:22:47 +0200 Subject: [PATCH 0867/1575] Move linker flags to build script. --- tests/riscv32/.cargo/config.toml | 4 ---- tests/riscv32/build.rs | 8 ++++++++ 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 tests/riscv32/build.rs diff --git a/tests/riscv32/.cargo/config.toml b/tests/riscv32/.cargo/config.toml index 1ffb0305d..58299b54e 100644 --- a/tests/riscv32/.cargo/config.toml +++ b/tests/riscv32/.cargo/config.toml @@ -1,9 +1,5 @@ [target.riscv32imac-unknown-none-elf] runner = "true" -rustflags = [ - "-C", "link-arg=-Tmemory.x", - "-C", "link-arg=-Tlink.x", -] [build] target = "riscv32imac-unknown-none-elf" diff --git a/tests/riscv32/build.rs b/tests/riscv32/build.rs new file mode 100644 index 000000000..e4a26c4a1 --- /dev/null +++ b/tests/riscv32/build.rs @@ -0,0 +1,8 @@ +use std::error::Error; + +fn main() -> Result<(), Box> { + println!("cargo:rustc-link-arg-bins=-Tmemory.x"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + + Ok(()) +} From f426c477479c0e0972156209eb0116c7a9eeedb8 Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 11 Apr 2023 17:40:05 +0200 Subject: [PATCH 0868/1575] Remove empty line, causing build issues. --- ci.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ci.sh b/ci.sh index 30fca4bc4..1b3fac8bc 100755 --- a/ci.sh +++ b/ci.sh @@ -125,7 +125,6 @@ cargo batch \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ - $BUILD_EXTRA From 9a677ab618aa7a7612cd079b77d3240bdb02fdac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 11 Apr 2023 23:00:14 +0200 Subject: [PATCH 0869/1575] common/peripheral: do not require mut in PeripheralRef clone_unchecked. --- embassy-hal-common/src/macros.rs | 2 +- embassy-hal-common/src/peripheral.rs | 12 ++++++------ embassy-nrf/src/buffered_uarte.rs | 2 +- embassy-nrf/src/saadc.rs | 2 +- embassy-nrf/src/timer.rs | 2 +- embassy-nrf/src/uarte.rs | 2 +- embassy-nrf/src/usb/mod.rs | 2 +- embassy-stm32/build.rs | 2 +- embassy-stm32/src/flash/common.rs | 3 +-- embassy-stm32/src/flash/f4.rs | 2 +- embassy-stm32/src/gpio.rs | 2 +- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs index da7913136..5e62e048a 100644 --- a/embassy-hal-common/src/macros.rs +++ b/embassy-hal-common/src/macros.rs @@ -92,7 +92,7 @@ macro_rules! impl_peripheral { type P = $type; #[inline] - unsafe fn clone_unchecked(&mut self) -> Self::P { + unsafe fn clone_unchecked(&self) -> Self::P { $type { ..*self } } } diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-common/src/peripheral.rs index 4a6b6a600..c7133bac6 100644 --- a/embassy-hal-common/src/peripheral.rs +++ b/embassy-hal-common/src/peripheral.rs @@ -39,7 +39,7 @@ impl<'a, T> PeripheralRef<'a, T> { /// You should strongly prefer using `reborrow()` instead. It returns a /// `PeripheralRef` that borrows `self`, which allows the borrow checker /// to enforce this at compile time. - pub unsafe fn clone_unchecked(&mut self) -> PeripheralRef<'a, T> + pub unsafe fn clone_unchecked(&self) -> PeripheralRef<'a, T> where T: Peripheral

, { @@ -146,14 +146,14 @@ pub trait Peripheral: Sized { /// /// You should strongly prefer using `into_ref()` instead. It returns a /// `PeripheralRef`, which allows the borrow checker to enforce this at compile time. - unsafe fn clone_unchecked(&mut self) -> Self::P; + unsafe fn clone_unchecked(&self) -> Self::P; /// Convert a value into a `PeripheralRef`. /// /// When called on an owned `T`, yields a `PeripheralRef<'static, T>`. /// When called on an `&'a mut T`, yields a `PeripheralRef<'a, T>`. #[inline] - fn into_ref<'a>(mut self) -> PeripheralRef<'a, Self::P> + fn into_ref<'a>(self) -> PeripheralRef<'a, Self::P> where Self: 'a, { @@ -161,14 +161,14 @@ pub trait Peripheral: Sized { } } -impl<'b, T: DerefMut> Peripheral for T +impl<'b, T: Deref> Peripheral for T where T::Target: Peripheral, { type P = ::P; #[inline] - unsafe fn clone_unchecked(&mut self) -> Self::P { - self.deref_mut().clone_unchecked() + unsafe fn clone_unchecked(&self) -> Self::P { + self.deref().clone_unchecked() } } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 75f93f904..c41d8398c 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -342,7 +342,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { r.enable.write(|w| w.enable().enabled()); // Configure byte counter. - let mut timer = Timer::new_counter(timer); + let timer = Timer::new_counter(timer); timer.cc(1).write(rx_buffer.len() as u32 * 2); timer.cc(1).short_compare_clear(); timer.clear(); diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index af952f03d..8aff7df16 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -315,7 +315,7 @@ impl<'d, const N: usize> Saadc<'d, N> { Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_end), Task::from_reg(&r.tasks_start)); start_ppi.enable(); - let mut timer = Timer::new(timer); + let timer = Timer::new(timer); timer.set_frequency(frequency); timer.cc(0).write(sample_counter); timer.cc(0).short_compare_clear(); diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index a9487a9fd..48b4f1b55 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -117,7 +117,7 @@ impl<'d, T: Instance> Timer<'d, T> { let regs = T::regs(); - let mut this = Self { _p: timer }; + let this = Self { _p: timer }; // Stop the timer before doing anything else, // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 3934d1b55..e59b2332a 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -205,7 +205,7 @@ impl<'d, T: Instance> Uarte<'d, T> { ppi_ch1: impl Peripheral

+ 'd, ppi_ch2: impl Peripheral

+ 'd, ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) { - let mut timer = Timer::new(timer); + let timer = Timer::new(timer); into_ref!(ppi_ch1, ppi_ch2); diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 56de511df..c1f3f48cb 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -153,7 +153,7 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V })) } - fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { ( Bus { _p: unsafe { self._p.clone_unchecked() }, diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b01e8ba45..73bd29fcf 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -180,7 +180,7 @@ fn main() { #[cfg(flash)] impl<'d> FlashLayout<'d> { - pub(crate) fn new(mut p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { + pub(crate) fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { #(#inits),* } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 8235d6f08..1189e447e 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -33,8 +33,7 @@ impl<'d> Flash<'d> { } pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { - let mut flash = self; - unsafe { flash.inner.clone_unchecked() } + unsafe { self.inner.clone_unchecked() } } } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 2ce9df69f..60ac62c17 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -67,7 +67,7 @@ mod alt_regions { // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all flash region operations are protected with a cs. - let mut p = self.release(); + let p = self.release(); AltFlashLayout { bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 3024f1ffa..4895684e0 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -29,7 +29,7 @@ impl<'d, T: Pin> Flex<'d, T> { } #[inline] - pub fn degrade(mut self) -> Flex<'d, AnyPin> { + pub fn degrade(self) -> Flex<'d, AnyPin> { // Safety: We are about to drop the other copy of this pin, so // this clone is safe. let pin = unsafe { self.pin.clone_unchecked() }; From 8fd8ef9ca7f9b5e43ba983f506274ff05665f99b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 11 Apr 2023 23:00:34 +0200 Subject: [PATCH 0870/1575] nrf/timer: make `cc()` borrows less strict. --- embassy-nrf/src/timer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 48b4f1b55..e9d2132c1 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -213,13 +213,13 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// # Panics /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer). - pub fn cc(&mut self, n: usize) -> Cc { + pub fn cc(&self, n: usize) -> Cc<'d, T> { if n >= T::CCS { panic!("Cannot get CC register {} of timer with {} CC registers.", n, T::CCS); } Cc { n, - _p: self._p.reborrow(), + _p: unsafe { self._p.clone_unchecked() }, } } } From f3699e67b913618276dfec90e6cbf59925f80015 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 12 Apr 2023 02:07:31 +0200 Subject: [PATCH 0871/1575] Fix typo in derivation of PLLP divisor --- embassy-stm32/src/rcc/f2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index d543888c1..bcae64d0f 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -148,7 +148,7 @@ impl Into for PLLMainDiv { match self { PLLMainDiv::Div2 => Pllp::DIV2, PLLMainDiv::Div4 => Pllp::DIV4, - PLLMainDiv::Div6 => Pllp::DIV8, + PLLMainDiv::Div6 => Pllp::DIV6, PLLMainDiv::Div8 => Pllp::DIV8, } } From 4863f88d02ca1821eac534729fb9ba347a8c6726 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Thu, 13 Apr 2023 00:04:44 +0200 Subject: [PATCH 0872/1575] Make Hertz constructors `const` This allows them to be used in constant values. --- embassy-stm32/src/time.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index f08abe331..604503e61 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs @@ -8,31 +8,31 @@ use core::ops::{Div, Mul}; pub struct Hertz(pub u32); impl Hertz { - pub fn hz(hertz: u32) -> Self { + pub const fn hz(hertz: u32) -> Self { Self(hertz) } - pub fn khz(kilohertz: u32) -> Self { + pub const fn khz(kilohertz: u32) -> Self { Self(kilohertz * 1_000) } - pub fn mhz(megahertz: u32) -> Self { + pub const fn mhz(megahertz: u32) -> Self { Self(megahertz * 1_000_000) } } /// This is a convenience shortcut for [`Hertz::hz`] -pub fn hz(hertz: u32) -> Hertz { +pub const fn hz(hertz: u32) -> Hertz { Hertz::hz(hertz) } /// This is a convenience shortcut for [`Hertz::khz`] -pub fn khz(kilohertz: u32) -> Hertz { +pub const fn khz(kilohertz: u32) -> Hertz { Hertz::khz(kilohertz) } /// This is a convenience shortcut for [`Hertz::mhz`] -pub fn mhz(megahertz: u32) -> Hertz { +pub const fn mhz(megahertz: u32) -> Hertz { Hertz::mhz(megahertz) } From 0289630fe429e0a8b84073b740ae839eb8ef5909 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Apr 2023 18:04:44 -0500 Subject: [PATCH 0873/1575] stm32/rcc: add i2s pll on some f4 micros --- embassy-stm32/src/rcc/f4.rs | 67 ++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 2a17eb9b0..3e2345c6b 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -28,11 +28,65 @@ pub struct Config { pub sys_ck: Option, pub pclk1: Option, pub pclk2: Option, + pub plli2s: Option, pub pll48: bool, } -unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bool) -> PllResults { +#[cfg(stm32f410)] +unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { + None +} + +// Not currently implemented, but will be in the future +#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] +unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { + None +} + +#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] +unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { + let min_div = 2; + let max_div = 7; + let target = match plli2s { + Some(target) => target, + None => return None, + }; + + // We loop through the possible divider values to find the best configuration. Looping + // through all possible "N" values would result in more iterations. + let (n, outdiv, output, _error) = (min_div..=max_div) + .filter_map(|outdiv| { + let target_vco_out = match target.checked_mul(outdiv) { + Some(x) => x, + None => return None, + }; + let n = (target_vco_out + (vco_in >> 1)) / vco_in; + let vco_out = vco_in * n; + if !(100_000_000..=432_000_000).contains(&vco_out) { + return None; + } + let output = vco_out / outdiv; + let error = (output as i32 - target as i32).unsigned_abs(); + Some((n, outdiv, output, error)) + }) + .min_by_key(|(_, _, _, error)| *error)?; + + RCC.plli2scfgr().modify(|w| { + w.set_plli2sn(n as u16); + w.set_plli2sr(outdiv as u8); + }); + + Some(output) +} + +unsafe fn setup_pll( + pllsrcclk: u32, + use_hse: bool, + pllsysclk: Option, + plli2s: Option, + pll48clk: bool, +) -> PllResults { use crate::pac::rcc::vals::{Pllp, Pllsrc}; let sysclk = pllsysclk.unwrap_or(pllsrcclk); @@ -43,6 +97,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48 use_pll: false, pllsysclk: None, pll48clk: None, + plli2sclk: None, }; } // Input divisor from PLL source clock, must result to frequency in @@ -101,6 +156,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48 use_pll: true, pllsysclk: Some(real_pllsysclk), pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, + plli2sclk: setup_i2s_pll(vco_in, plli2s), } } @@ -286,6 +342,7 @@ pub(crate) unsafe fn init(config: Config) { pllsrcclk, config.hse.is_some(), if sysclk_on_pll { Some(sysclk) } else { None }, + config.plli2s.map(|i2s| i2s.0), config.pll48, ); @@ -376,6 +433,13 @@ pub(crate) unsafe fn init(config: Config) { while !RCC.cr().read().pllrdy() {} } + #[cfg(not(stm32f410))] + if plls.plli2sclk.is_some() { + RCC.cr().modify(|w| w.set_plli2son(true)); + + while !RCC.cr().read().plli2srdy() {} + } + RCC.cfgr().modify(|w| { w.set_ppre2(Ppre(ppre2_bits)); w.set_ppre1(Ppre(ppre1_bits)); @@ -416,6 +480,7 @@ struct PllResults { use_pll: bool, pllsysclk: Option, pll48clk: Option, + plli2sclk: Option, } mod max { From c1d5f868714accd6780913e652d8a884368c60d3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Apr 2023 18:11:55 -0500 Subject: [PATCH 0874/1575] stm32/rcc: fix warnings --- embassy-stm32/src/rcc/f4.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 3e2345c6b..e8dfba011 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -34,13 +34,13 @@ pub struct Config { } #[cfg(stm32f410)] -unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { +unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { None } // Not currently implemented, but will be in the future #[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] -unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { +unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { None } @@ -480,6 +480,7 @@ struct PllResults { use_pll: bool, pllsysclk: Option, pll48clk: Option, + #[allow(dead_code)] plli2sclk: Option, } From 6a6c673c5fd1baa1d3ca3eebb55ba430a86b1438 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Thu, 13 Apr 2023 14:21:41 -0500 Subject: [PATCH 0875/1575] Executor: Replace unnecessary atomics in runqueue --- embassy-executor/src/raw/run_queue.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index a88174a0c..f1ec19ac1 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -4,15 +4,16 @@ use core::ptr::NonNull; use atomic_polyfill::{AtomicPtr, Ordering}; use super::{TaskHeader, TaskRef}; +use crate::raw::util::SyncUnsafeCell; pub(crate) struct RunQueueItem { - next: AtomicPtr, + next: SyncUnsafeCell>, } impl RunQueueItem { pub const fn new() -> Self { Self { - next: AtomicPtr::new(ptr::null_mut()), + next: SyncUnsafeCell::new(None), } } } @@ -51,7 +52,12 @@ impl RunQueue { self.head .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| { was_empty = prev.is_null(); - task.header().run_queue_item.next.store(prev, Ordering::Relaxed); + unsafe { + // safety: the pointer is either null or valid + let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())); + // safety: there are no concurrent accesses to `next` + task.header().run_queue_item.next.set(prev); + } Some(task.as_ptr() as *mut _) }) .ok(); @@ -64,18 +70,19 @@ impl RunQueue { /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { // Atomically empty the queue. - let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); + let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); + + // safety: the pointer is either null or valid + let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) }; // Iterate the linked list of tasks that were previously in the queue. - while let Some(task) = NonNull::new(ptr) { - let task = unsafe { TaskRef::from_ptr(task.as_ptr()) }; + while let Some(task) = next { // If the task re-enqueues itself, the `next` pointer will get overwritten. // Therefore, first read the next pointer, and only then process the task. - let next = task.header().run_queue_item.next.load(Ordering::Relaxed); + // safety: there are no concurrent accesses to `next` + next = unsafe { task.header().run_queue_item.next.get() }; on_task(task); - - ptr = next } } } From 577f060d249ba048b260309f5d065dfc84e0be65 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 13 Apr 2023 23:03:21 +0200 Subject: [PATCH 0876/1575] Release embassy-sync v0.2.0 --- embassy-boot/boot/Cargo.toml | 2 +- embassy-cortex-m/Cargo.toml | 2 +- embassy-embedded-hal/Cargo.toml | 2 +- embassy-lora/Cargo.toml | 2 +- embassy-net-driver-channel/Cargo.toml | 2 +- embassy-net/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-sync/CHANGELOG.md | 22 ++++++++++++++++++++ embassy-sync/Cargo.toml | 2 +- embassy-time/Cargo.toml | 2 +- embassy-usb-logger/Cargo.toml | 2 +- embassy-usb/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 51 files changed, 72 insertions(+), 50 deletions(-) create mode 100644 embassy-sync/CHANGELOG.md diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index c4ebdaff2..d44aee349 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -27,7 +27,7 @@ defmt = { version = "0.3", optional = true } digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } -embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" embedded-storage-async = "0.4.0" salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml index 5c5718d50..c2c4759dc 100644 --- a/embassy-cortex-m/Cargo.toml +++ b/embassy-cortex-m/Cargo.toml @@ -36,7 +36,7 @@ prio-bits-8 = [] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor"} embassy-macros = { version = "0.1.0", path = "../embassy-macros"} embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common"} diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index c509d6ee5..19d512585 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -17,7 +17,7 @@ std = [] nightly = ["embedded-hal-async", "embedded-storage-async"] [dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.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.10" } embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 604358c5b..79d5660f4 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -29,7 +29,7 @@ defmt = { version = "0.3", optional = true } 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" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index 6cb6c3568..e475551e1 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -13,6 +13,6 @@ target = "thumbv7em-none-eabi" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 1854d2043..fe9514e46 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -43,7 +43,7 @@ smoltcp = { version = "0.9.0", default-features = false, features = [ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-time = { version = "0.1.0", path = "../embassy-time" } -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 4a4e7c9f9..e5e799d0a 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -80,7 +80,7 @@ _gpio-p1 = [] [dependencies] embassy-executor = { version = "0.1.0", path = "../embassy-executor", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index cb9c7be77..2ef2c8f07 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -39,7 +39,7 @@ nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", " unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 51ca9ff71..969dc3b70 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -31,7 +31,7 @@ flavors = [ ] [dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md new file mode 100644 index 000000000..a60f3f7c4 --- /dev/null +++ b/embassy-sync/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.2.0 - 2023-04-13 + +- pubsub: Fix messages not getting popped when the last subscriber that needed them gets dropped. +- pubsub: Move instead of clone messages when the last subscriber pops them. +- pubsub: Pop messages which count is 0 after unsubscribe. +- Update `embedded-io` from `0.3` to `0.4` (uses `async fn` in traits). +- impl `Default` for `WakerRegistration` +- impl `Default` for `Signal` +- Remove unnecessary uses of `atomic-polyfill` +- Add `#[must_use]` to all futures. + + +## 0.1.0 - 2022-08-26 + +- First release \ No newline at end of file diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index e4871e718..bc06b92cd 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-sync" -version = "0.1.0" +version = "0.2.0" edition = "2021" description = "no-std, no-alloc synchronization primitives with async support" repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 38d31f1c4..3a36dfde0 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -156,7 +156,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", option embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} futures-util = { version = "0.3.17", default-features = false } -embassy-sync = { version = "0.1", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } atomic-polyfill = "1.0.1" critical-section = "1.1" cfg-if = "1.0.0" diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 18d70b0c5..0f91cd36d 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -10,7 +10,7 @@ target = "thumbv7em-none-eabi" [dependencies] embassy-usb = { version = "0.1.0", path = "../embassy-usb" } -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } futures = { version = "0.3", default-features = false } static_cell = "1" diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index ae3f3ac37..03cf96a1d 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -41,7 +41,7 @@ max-handler-count-8 = [] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index e75c73cbd..cda1917b3 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 8de2d2ebd..9d34a3691 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } 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"] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 083607de5..8e978eb24 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 74f508515..fb55b166d 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 898b9a47e..ea6b905a4 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index e142c8481..6ba18564d 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index f0e92e1ac..d5b8e3e01 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 87689e9a9..ccd1fe2ee 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index a6708bf51..128afd51e 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 7910b372a..6f7cb8875 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -16,7 +16,7 @@ log = [ ] [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 3ece24066..fc614cb80 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -11,7 +11,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 4134db46f..2c3a12964 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = [ "defmt", ] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 63d0ac82a..f0fd27991 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index ff08e378c..9938c693a 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "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", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 3b1d888f6..5667200e3 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 5c82c5579..2ff252622 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -12,7 +12,7 @@ cortex-m-rt = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" panic-probe = "0.3" -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 99f37cdda..d08e00b0b 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index ffb232310..02045a79f 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 38f11201d..a62eba9ec 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index d967d8501..4b2f3d21c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 74e7bf53d..898e05c12 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 03bdbcea3..a522fb422 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 4e4150350..b275eb190 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index b77d376ca..149f8a58b 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 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", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 154f5a987..8316498ca 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 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", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 413d5c18f..d446d41b2 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -9,7 +9,7 @@ default = ["nightly"] nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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 = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index cd9508d57..e071c5d27 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32l151cb-a", "time-driver-any", "memory-x"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index fa39df6db..e83f261e6 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 1c662b9da..af305a19d 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index ebef0a4f7..65fc1b988 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index ddf9729e6..835985ec3 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wb55cc", "time-driver-any", "exti"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 0d2194ea2..df295ca49 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 430d0b4c7..5f36eb77f 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" crate-type = ["cdylib"] [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 912749e5d..22435e55b 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 885776ae6..01b424de8 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } -embassy-sync = { version = "0.1.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 463a370fe..36ff735ec 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 227eabe73..8b70a1015 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -16,7 +16,7 @@ stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board [dependencies] -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } From e7ff759f1ca78a5b53c1ea95c24a6317227dc8b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 13 Apr 2023 23:50:41 +0200 Subject: [PATCH 0877/1575] time: remove dependency on embassy-sync. --- embassy-time/Cargo.toml | 1 - embassy-time/src/driver_std.rs | 20 +++++++++----------- embassy-time/src/queue_generic.rs | 12 +++++------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 3a36dfde0..a8a7e9337 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -156,7 +156,6 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", option embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} futures-util = { version = "0.3.17", default-features = false } -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } atomic-polyfill = "1.0.1" critical-section = "1.1" cfg-if = "1.0.0" diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index da46a599d..9f8c57b5c 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -5,8 +5,7 @@ use std::time::{Duration as StdDuration, Instant as StdInstant}; use std::{mem, ptr, thread}; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::blocking_mutex::Mutex as EmbassyMutex; +use critical_section::Mutex as CsMutex; use crate::driver::{AlarmHandle, Driver}; @@ -40,7 +39,7 @@ struct TimeDriver { // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections // themselves are reentrant - alarms: UninitCell>>, + alarms: UninitCell>>, zero_instant: UninitCell, signaler: UninitCell, } @@ -58,8 +57,7 @@ crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { impl TimeDriver { fn init(&self) { self.once.call_once(|| unsafe { - self.alarms - .write(EmbassyMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT]))); + self.alarms.write(CsMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT]))); self.zero_instant.write(StdInstant::now()); self.signaler.write(Signaler::new()); @@ -72,7 +70,8 @@ impl TimeDriver { loop { let now = DRIVER.now(); - let next_alarm = unsafe { DRIVER.alarms.as_ref() }.lock(|alarms| { + let next_alarm = critical_section::with(|cs| { + let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs); loop { let pending = alarms .borrow_mut() @@ -139,8 +138,8 @@ impl Driver for TimeDriver { fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { self.init(); - unsafe { self.alarms.as_ref() }.lock(|alarms| { - let mut alarms = alarms.borrow_mut(); + critical_section::with(|cs| { + let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); let alarm = &mut alarms[alarm.id() as usize]; alarm.callback = callback as *const (); alarm.ctx = ctx; @@ -149,9 +148,8 @@ impl Driver for TimeDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { self.init(); - unsafe { self.alarms.as_ref() }.lock(|alarms| { - let mut alarms = alarms.borrow_mut(); - + critical_section::with(|cs| { + let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); let alarm = &mut alarms[alarm.id() as usize]; alarm.timestamp = timestamp; unsafe { self.signaler.as_ref() }.signal(); diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 20ae7e6cc..0f67d9dbb 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -2,8 +2,7 @@ use core::cell::RefCell; use core::cmp::{min, Ordering}; use core::task::Waker; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::blocking_mutex::Mutex; +use critical_section::Mutex; use heapless::Vec; use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; @@ -129,7 +128,7 @@ impl InnerQueue { } struct Queue { - inner: Mutex>>, + inner: Mutex>>, } impl Queue { @@ -140,8 +139,8 @@ impl Queue { } fn schedule_wake(&'static self, at: Instant, waker: &Waker) { - self.inner.lock(|inner| { - let mut inner = inner.borrow_mut(); + critical_section::with(|cs| { + let mut inner = self.inner.borrow_ref_mut(cs); if inner.is_none() {} @@ -159,8 +158,7 @@ impl Queue { } fn handle_alarm(&self) { - self.inner - .lock(|inner| inner.borrow_mut().as_mut().unwrap().handle_alarm()); + critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm()) } fn handle_alarm_callback(ctx: *mut ()) { From a7629299f47a7a54d4033152733d2774ec918d7c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 13 Apr 2023 23:57:01 +0200 Subject: [PATCH 0878/1575] Release embassy-time v0.1.1 --- embassy-time/CHANGELOG.md | 24 ++++++++++++++++++++++++ embassy-time/Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 embassy-time/CHANGELOG.md diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md new file mode 100644 index 000000000..f4a7860e6 --- /dev/null +++ b/embassy-time/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.1 - 2023-04-13 + +- Update `embedded-hal-async` to `0.2.0-alpha.1` (uses `async fn` in traits). +- Update `embedded-hal v1` to `1.0.0-alpha.10`. (Note: v0.2 support is kept unchanged). +- Remove dep on `embassy-sync`. +- Fix reentrancy issues in the `std` time driver (#1177) +- Add `Duration::from_hz()`. +- impl `From` conversions to/from `core::time::Duration`. +- Add `#[must_use]` to all futures. +- Add inherent `async fn tick()` to `Ticker`, so you can use it directly without the `Stream` trait. +- Add more tick rates. +- impl `Default` for `Signal` +- Remove unnecessary uses of `atomic-polyfill` + +## 0.1.0 - 2022-08-26 + +- First release \ No newline at end of file diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index a8a7e9337..c4edbd38a 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-time" -version = "0.1.0" +version = "0.1.1" edition = "2021" description = "Instant and Duration for embedded no-std systems, with async timer support" repository = "https://github.com/embassy-rs/embassy" From 4be1e4bd44600737bf80ef7210c2b913d63364de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Gr=C3=B6nlund?= Date: Fri, 14 Apr 2023 09:38:35 +0200 Subject: [PATCH 0879/1575] Remove MySpi MySpi was replaced by PioSpi and no longer used. --- examples/rpi-pico-w/src/main.rs | 90 +-------------------------------- 1 file changed, 2 insertions(+), 88 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index d5610d2ba..b773cea18 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -4,7 +4,6 @@ #![feature(async_fn_in_trait)] #![allow(incomplete_features)] -use core::slice; use core::str::from_utf8; use cyw43_pio::PioSpi; @@ -12,8 +11,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; -use embassy_rp::gpio::{Flex, Level, Output}; -use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_24, PIN_25, PIN_29}; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25}; use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachineInstance, Sm0}; use embedded_io::asynch::Write; use static_cell::StaticCell; @@ -150,88 +149,3 @@ async fn main(spawner: Spawner) { } } -struct MySpi { - /// SPI clock - clk: Output<'static, PIN_29>, - - /// 4 signals, all in one!! - /// - SPI MISO - /// - SPI MOSI - /// - IRQ - /// - strap to set to gSPI mode on boot. - dio: Flex<'static, PIN_24>, - - /// Chip select - cs: Output<'static, PIN_25>, -} - -impl MySpi { - async fn read(&mut self, words: &mut [u32]) { - self.dio.set_as_input(); - for word in words { - let mut w = 0; - for _ in 0..32 { - w = w << 1; - - // rising edge, sample data - if self.dio.is_high() { - w |= 0x01; - } - self.clk.set_high(); - - // falling edge - self.clk.set_low(); - } - *word = w - } - } - - async fn write(&mut self, words: &[u32]) { - self.dio.set_as_output(); - for word in words { - let mut word = *word; - for _ in 0..32 { - // falling edge, setup data - self.clk.set_low(); - if word & 0x8000_0000 == 0 { - self.dio.set_low(); - } else { - self.dio.set_high(); - } - - // rising edge - self.clk.set_high(); - - word = word << 1; - } - } - self.clk.set_low(); - - self.dio.set_as_input(); - } -} - -impl cyw43::SpiBusCyw43 for MySpi { - async fn cmd_write(&mut self, write: &[u32]) -> u32 { - self.cs.set_low(); - self.write(write).await; - - let mut status = 0; - self.read(slice::from_mut(&mut status)).await; - - self.cs.set_high(); - status - } - - async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32 { - self.cs.set_low(); - self.write(slice::from_ref(&write)).await; - self.read(read).await; - - let mut status = 0; - self.read(slice::from_mut(&mut status)).await; - - self.cs.set_high(); - status - } -} From 9ca5bcd57686bc8144c31b1937a15dccb6cf07ce Mon Sep 17 00:00:00 2001 From: mattiasgronlund Date: Fri, 14 Apr 2023 10:27:25 +0200 Subject: [PATCH 0880/1575] Update main.rs --- examples/rpi-pico-w/src/main.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index b773cea18..d075aec2a 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -61,11 +61,6 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - // let clk = Output::new(p.PIN_29, Level::Low); - // let mut dio = Flex::new(p.PIN_24); - // dio.set_low(); - // dio.set_as_output(); - // // let bus = MySpi { clk, dio }; let (_, sm, _, _, _) = p.PIO0.split(); let dma = p.DMA_CH0; From 3002ee0dcf0ae186867b2fd869daed609d7a4a23 Mon Sep 17 00:00:00 2001 From: sander Date: Fri, 14 Apr 2023 11:27:23 +0200 Subject: [PATCH 0881/1575] embassy-boot: add nightly feature gate for async usage --- embassy-boot/boot/src/firmware_updater.rs | 445 +++++++++++----------- embassy-boot/boot/src/partition.rs | 83 ++-- embassy-boot/nrf/Cargo.toml | 6 +- 3 files changed, 269 insertions(+), 265 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index f3d76a56c..1f2a6d24e 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -74,6 +74,228 @@ impl FirmwareUpdater { Self { dfu, state } } + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + #[cfg(feature = "nightly")] + pub async fn get_state( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result { + self.state.read(state_flash, 0, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(all(feature = "_verify", feature = "nightly"))] + pub async fn verify_and_mark_updated( + &mut self, + _state_and_dfu_flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: u32, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_update_len <= self.dfu.size()); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) + .await?; + + public_key.verify(&message, &signature).map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) + .await?; + + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await + } + + /// Verify the update in DFU with any digest. + #[cfg(feature = "nightly")] + pub async fn hash( + &mut self, + dfu_flash: &mut F, + update_len: u32, + chunk_buf: &mut [u8], + output: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read(dfu_flash, offset, chunk_buf).await?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); + } + output.copy_from_slice(digest.finalize().as_slice()); + Ok(()) + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(all(feature = "nightly", not(feature = "_verify")))] + pub async fn mark_updated( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC, state_flash).await + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(feature = "nightly")] + pub async fn mark_booted( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC, state_flash).await + } + + #[cfg(feature = "nightly")] + async fn set_magic( + &mut self, + aligned: &mut [u8], + magic: u8, + state_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + self.state.read(state_flash, 0, aligned).await?; + + if aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; + + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; + } + + // Clear magic and progress + self.state.wipe(state_flash).await?; + + // Set magic + aligned.fill(magic); + self.state.write(state_flash, 0, aligned).await?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + #[cfg(feature = "nightly")] + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + dfu_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + self.dfu + .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) + .await?; + + self.dfu.write(dfu_flash, offset as u32, data).await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning its `Partition`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + #[cfg(feature = "nightly")] + pub async fn prepare_update( + &mut self, + dfu_flash: &mut F, + ) -> Result { + self.dfu.wipe(dfu_flash).await?; + + Ok(self.dfu) + } + // // Blocking API // @@ -289,228 +511,7 @@ impl FirmwareUpdater { } } -// Async API -#[cfg(feature = "nightly")] -impl FirmwareUpdater { - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub async fn get_state( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read(state_flash, 0, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(feature = "_verify")] - pub async fn verify_and_mark_updated( - &mut self, - _state_and_dfu_flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: u32, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; - - use crate::digest_adapters::ed25519_dalek::Sha512; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; - - public_key.verify(&message, &signature).map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Signature}; - - use crate::digest_adapters::salty::Sha512; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; - - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await - } - - /// Verify the update in DFU with any digest. - pub async fn hash( - &mut self, - dfu_flash: &mut F, - update_len: u32, - chunk_buf: &mut [u8], - output: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let mut digest = D::new(); - for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read(dfu_flash, offset, chunk_buf).await?; - let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); - digest.update(&chunk_buf[..len]); - } - output.copy_from_slice(digest.finalize().as_slice()); - Ok(()) - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(not(feature = "_verify"))] - pub async fn mark_updated( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, state_flash).await - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, state_flash).await - } - - async fn set_magic( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read(state_flash, 0, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; - } - - // Clear magic and progress - self.state.wipe(state_flash).await?; - - // Set magic - aligned.fill(magic); - self.state.write(state_flash, 0, aligned).await?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - self.dfu - .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) - .await?; - - self.dfu.write(dfu_flash, offset as u32, data).await?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning its `Partition`. - /// - /// Using this instead of `write_firmware` allows for an optimized API in - /// exchange for added complexity. - pub async fn prepare_update( - &mut self, - dfu_flash: &mut F, - ) -> Result { - self.dfu.wipe(dfu_flash).await?; - - Ok(self.dfu) - } -} - - - #[cfg(test)] +#[cfg(test)] mod tests { use futures::executor::block_on; use sha1::{Digest, Sha1}; diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs index 67bd7abba..7b56a8240 100644 --- a/embassy-boot/boot/src/partition.rs +++ b/embassy-boot/boot/src/partition.rs @@ -23,6 +23,47 @@ impl Partition { self.to - self.from } + /// Read from the partition on the provided flash + #[cfg(feature = "nightly")] + pub async fn read( + &self, + flash: &mut F, + offset: u32, + bytes: &mut [u8], + ) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.read(offset, bytes).await + } + + /// Write to the partition on the provided flash + #[cfg(feature = "nightly")] + pub async fn write(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { + let offset = self.from as u32 + offset; + flash.write(offset, bytes).await?; + trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); + Ok(()) + } + + /// Erase part of the partition on the provided flash + #[cfg(feature = "nightly")] + pub async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { + let from = self.from as u32 + from; + let to = self.from as u32 + to; + flash.erase(from, to).await?; + trace!("Erased from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + + /// Erase the entire partition + #[cfg(feature = "nightly")] + pub(crate) async fn wipe(&self, flash: &mut F) -> Result<(), F::Error> { + let from = self.from as u32; + let to = self.to as u32; + flash.erase(from, to).await?; + trace!("Wiped from 0x{:x} to 0x{:x}", from, to); + Ok(()) + } + /// Read from the partition on the provided flash pub fn read_blocking(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { let offset = self.from as u32 + offset; @@ -56,48 +97,6 @@ impl Partition { } } -// Async API -#[cfg(feature = "nightly")] -impl Partition { - - /// Read from the partition on the provided flash - pub async fn read( - &self, - flash: &mut F, - offset: u32, - bytes: &mut [u8], - ) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.read(offset, bytes).await - } - - /// Write to the partition on the provided flash - pub async fn write(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.write(offset, bytes).await?; - trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); - Ok(()) - } - - /// Erase part of the partition on the provided flash - pub async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { - let from = self.from as u32 + from; - let to = self.from as u32 + to; - flash.erase(from, to).await?; - trace!("Erased from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Erase the entire partition - pub(crate) async fn wipe(&self, flash: &mut F) -> Result<(), F::Error> { - let from = self.from as u32; - let to = self.to as u32; - flash.erase(from, to).await?; - trace!("Wiped from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } -} - #[cfg(test)] mod tests { use crate::mem_flash::MemFlash; diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index 90a36d014..05ab87896 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -37,4 +37,8 @@ defmt = [ softdevice = [ "nrf-softdevice-mbr", ] -nightly = ["dep:embedded-storage-async", "embassy-boot/nightly", "embassy-nrf/nightly"] +nightly = [ + "dep:embedded-storage-async", + "embassy-boot/nightly", + "embassy-nrf/nightly" +] From b9fc2a6b33d8af88d95d750965f3fb8b1e9032d4 Mon Sep 17 00:00:00 2001 From: Jacob Davis-Hansson Date: Fri, 14 Apr 2023 21:05:03 +0200 Subject: [PATCH 0882/1575] Add ability to invert UART pins This is useful in some cases where the surrounding circuit for some reason inverts the UART signal, for instance if you're talking to a device via an optocoupler. --- embassy-rp/src/uart/mod.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index a945f2295..dfa56a842 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -6,6 +6,7 @@ use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; +use crate::pac::io::vals::Inover; #[cfg(feature = "nightly")] mod buffered; @@ -53,6 +54,14 @@ pub struct Config { pub data_bits: DataBits, pub stop_bits: StopBits, pub parity: Parity, + /// Invert the tx pin output + pub invert_tx: bool, + /// Invert the rx pin input + pub invert_rx: bool, + // Invert the rts pin + pub invert_rts: bool, + // Invert the cts pin + pub invert_cts: bool, } impl Default for Config { @@ -62,6 +71,10 @@ impl Default for Config { data_bits: DataBits::DataBits8, stop_bits: StopBits::STOP1, parity: Parity::ParityNone, + invert_rx: false, + invert_tx: false, + invert_rts: false, + invert_cts: false, } } } @@ -381,19 +394,31 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { let r = T::regs(); unsafe { if let Some(pin) = &tx { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_tx { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rx { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_rx { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &cts { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_cts { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_rts { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } From 650589ab3f030ed63c129245c89e3056bc5f31e5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Apr 2023 16:30:36 -0500 Subject: [PATCH 0883/1575] stm32/rcc: add plli2s to Clocks and cfg directives --- embassy-stm32/src/rcc/f4.rs | 8 ++++++++ embassy-stm32/src/rcc/mod.rs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index e8dfba011..5427d2fb4 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -28,6 +28,8 @@ pub struct Config { pub sys_ck: Option, pub pclk1: Option, pub pclk2: Option, + + #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] pub plli2s: Option, pub pll48: bool, @@ -342,7 +344,10 @@ pub(crate) unsafe fn init(config: Config) { pllsrcclk, config.hse.is_some(), if sysclk_on_pll { Some(sysclk) } else { None }, + #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] config.plli2s.map(|i2s| i2s.0), + #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] + None, config.pll48, ); @@ -473,6 +478,9 @@ pub(crate) unsafe fn init(config: Config) { ahb3: Hertz(hclk), pll48: plls.pll48clk.map(Hertz), + + #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] + plli2s: plls.plli2sclk.map(Hertz), }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d6a31f17b..7c1e416fe 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -60,6 +60,9 @@ pub struct Clocks { #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] pub pll48: Option, + #[cfg(all(stm32f4, not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))))] + pub plli2s: Option, + #[cfg(stm32f1)] pub adc: Hertz, From f681b9d4e5fc142ee0bb847e0e00260c932a1401 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Apr 2023 22:39:53 +0200 Subject: [PATCH 0884/1575] Remove the _todo_embedded_hal_serial impls. EH will probably not have these serial traits. --- embassy-embedded-hal/src/adapter.rs | 42 ---------------- embassy-nrf/src/uarte.rs | 77 ----------------------------- embassy-rp/src/uart/buffered.rs | 55 --------------------- embassy-rp/src/uart/mod.rs | 55 --------------------- embassy-stm32/src/usart/mod.rs | 67 ------------------------- 5 files changed, 296 deletions(-) diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index ee919bd84..171ff6c9f 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -131,48 +131,6 @@ where type Error = E; } -#[cfg(feature = "_todo_embedded_hal_serial")] -impl embedded_hal_async::serial::Read for BlockingAsync -where - T: serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type ReadFuture<'a> = impl Future> + 'a where T: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let mut pos = 0; - while pos < buf.len() { - match self.wrapped.read() { - Err(nb::Error::WouldBlock) => {} - Err(nb::Error::Other(e)) => return Err(e), - Ok(b) => { - buf[pos] = b; - pos += 1; - } - } - } - Ok(()) - } - } -} - -#[cfg(feature = "_todo_embedded_hal_serial")] -impl embedded_hal_async::serial::Write for BlockingAsync -where - T: blocking::serial::Write + serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type WriteFuture<'a> = impl Future> + 'a where T: 'a; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.wrapped.bwrite_all(buf) } - } - - type FlushFuture<'a> = impl Future> + 'a where T: 'a; - fn flush(&mut self) -> Result<(), Self::Error> { - async move { self.wrapped.bflush() } - } -} - /// NOR flash wrapper use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index e59b2332a..586c88b2d 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -992,80 +992,3 @@ mod eh1 { type Error = Error; } } - -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, 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, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, 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(()) } - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, 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(()) } - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, 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::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-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index c620ed08c..cb0461930 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -726,58 +726,3 @@ mod eh1 { } } } - -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: Instance> embedded_hal_async::serial::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> { - Self::write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - Self::flush() - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::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> { - Self::read(buf) - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::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> { - BufferedUartTx::<'d, T>::write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - BufferedUartTx::<'d, T>::flush() - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::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> { - BufferedUartRx::<'d, T>::read(buf) - } - } -} diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index a945f2295..7122930f5 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -651,61 +651,6 @@ mod eh1 { } } -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } -} - mod sealed { use super::*; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index a42eede18..8bbba305b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -973,73 +973,6 @@ mod eio { } } -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: BasicInstance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma> - where - TxDma: crate::usart::TxDma, - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: BasicInstance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma> - where - RxDma: crate::usart::RxDma, - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } - - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma> - where - TxDma: crate::usart::TxDma, - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Read for Uart<'d, T, TxDma, RxDma> - where - RxDma: crate::usart::RxDma, - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } -} - #[cfg(feature = "nightly")] pub use buffered::*; #[cfg(feature = "nightly")] From 224eaaf79792a04a25bf7d5e768da41b2a030f7a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Apr 2023 22:49:56 +0200 Subject: [PATCH 0885/1575] stm32/sdmmc: switch to AFIT. --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/sdmmc/mod.rs | 56 +++++++++++++--------------------- examples/stm32f4/Cargo.toml | 2 +- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 969dc3b70..18b1d4d0e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -55,7 +55,7 @@ cortex-m = "0.7.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand_core = "0.6.3" sdio-host = "0.5.0" -embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } +embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" stm32-metapac = "6" diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 03d24dcb1..ac00b5176 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1651,8 +1651,6 @@ foreach_peripheral!( #[cfg(feature = "embedded-sdmmc")] mod sdmmc_rs { - use core::future::Future; - use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx}; use super::*; @@ -1660,49 +1658,37 @@ mod sdmmc_rs { impl<'d, T: Instance, Dma: SdmmcDma> BlockDevice for Sdmmc<'d, T, Dma> { type Error = Error; - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>( - &'a mut self, - blocks: &'a mut [Block], + async fn read( + &mut self, + blocks: &mut [Block], start_block_idx: BlockIdx, _reason: &str, - ) -> Self::ReadFuture<'a> { - async move { - let mut address = start_block_idx.0; + ) -> Result<(), Self::Error> { + let mut address = start_block_idx.0; - for block in blocks.iter_mut() { - let block: &mut [u8; 512] = &mut block.contents; + for block in blocks.iter_mut() { + let block: &mut [u8; 512] = &mut block.contents; - // NOTE(unsafe) Block uses align(4) - let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) }; - self.read_block(address, block).await?; - address += 1; - } - Ok(()) + // NOTE(unsafe) Block uses align(4) + let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) }; + self.read_block(address, block).await?; + address += 1; } + Ok(()) } - fn write<'a>(&'a mut self, blocks: &'a [Block], start_block_idx: BlockIdx) -> Self::WriteFuture<'a> { - async move { - let mut address = start_block_idx.0; + async fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> { + let mut address = start_block_idx.0; - for block in blocks.iter() { - let block: &[u8; 512] = &block.contents; + for block in blocks.iter() { + let block: &[u8; 512] = &block.contents; - // NOTE(unsafe) DataBlock uses align 4 - let block = unsafe { &*(block as *const _ as *const DataBlock) }; - self.write_block(address, block).await?; - address += 1; - } - Ok(()) + // NOTE(unsafe) DataBlock uses align 4 + let block = unsafe { &*(block as *const _ as *const DataBlock) }; + self.write_block(address, block).await?; + address += 1; } + Ok(()) } fn num_blocks(&self) -> Result { diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 4b2f3d21c..1736769ef 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } From 63941432e3f68a0a3f2f4e97a807bfe16f4aff2b Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 14 Apr 2023 23:01:13 +0200 Subject: [PATCH 0886/1575] Update to rust-lorawan with afit support --- embassy-lora/Cargo.toml | 4 +- embassy-lora/src/lib.rs | 13 ++- embassy-lora/src/stm32wl/mod.rs | 12 +-- embassy-lora/src/sx126x/mod.rs | 132 +++++++++++------------- embassy-lora/src/sx127x/mod.rs | 151 ++++++++++++---------------- examples/nrf52840/Cargo.toml | 4 +- examples/stm32l0/Cargo.toml | 4 +- examples/stm32l0/src/bin/lorawan.rs | 2 +- examples/stm32wl/Cargo.toml | 4 +- examples/stm32wl/src/bin/lorawan.rs | 2 +- 10 files changed, 142 insertions(+), 186 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 79d5660f4..13b3acab2 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -38,5 +38,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", default-features = false } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false } diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 3e4748430..5c919cbb6 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] -#![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait, impl_trait_projections)] +#![allow(incomplete_features)] //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device //! crate's async LoRaWAN MAC implementation. @@ -34,13 +35,11 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer { self.start = Instant::now(); } - type AtFuture<'m> = impl core::future::Future + 'm; - fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> { - Timer::at(self.start + Duration::from_millis(millis)) + async fn at(&mut self, millis: u64) { + Timer::at(self.start + Duration::from_millis(millis)).await } - type DelayFuture<'m> = impl core::future::Future + 'm; - fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { - Timer::after(Duration::from_millis(millis)) + async fn delay_ms(&mut self, millis: u64) { + Timer::after(Duration::from_millis(millis)).await } } diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 3d52c1cc7..d76e8c43b 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,5 +1,5 @@ //! A radio driver integration for the radio found on STM32WL family devices. -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -241,14 +241,12 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm where Self: 'm; - fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { - async move { self.do_tx(config, buf).await } + async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { + self.do_tx(config, buf).await } - type RxFuture<'m> = impl Future> + 'm where Self: 'm; - fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { - async move { self.do_rx(config, buf).await } + async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { + self.do_rx(config, buf).await } } diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index 8559574cb..2f0b8c8e3 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -1,5 +1,3 @@ -use core::future::Future; - use defmt::Format; use embedded_hal::digital::v2::OutputPin; use embedded_hal_async::digital::Wait; @@ -71,83 +69,69 @@ where { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CTRL: 'm, - WAIT: 'm, - BUS: 'm; - - fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> { + async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result { trace!("TX START"); - async move { - self.lora - .set_tx_config( - config.pw, - config.rf.spreading_factor.into(), - config.rf.bandwidth.into(), - config.rf.coding_rate.into(), - 8, - false, - true, - false, - 0, - false, - ) - .await?; - self.lora.set_max_payload_length(buffer.len() as u8).await?; - self.lora.set_channel(config.rf.frequency).await?; - self.lora.send(buffer, 0xffffff).await?; - self.lora.process_irq(None, None, None).await?; - trace!("TX DONE"); - return Ok(0); - } + self.lora + .set_tx_config( + config.pw, + config.rf.spreading_factor.into(), + config.rf.bandwidth.into(), + config.rf.coding_rate.into(), + 8, + false, + true, + false, + 0, + false, + ) + .await?; + self.lora.set_max_payload_length(buffer.len() as u8).await?; + self.lora.set_channel(config.rf.frequency).await?; + self.lora.send(buffer, 0xffffff).await?; + self.lora.process_irq(None, None, None).await?; + trace!("TX DONE"); + return Ok(0); } - type RxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CTRL: 'm, - WAIT: 'm, - BUS: 'm; - - fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> { + async fn rx( + &mut self, + config: RfConfig, + receiving_buffer: &mut [u8], + ) -> Result<(usize, RxQuality), Self::PhyError> { trace!("RX START"); - async move { - self.lora - .set_rx_config( - config.spreading_factor.into(), - config.bandwidth.into(), - config.coding_rate.into(), - 8, - 4, - false, - 0u8, - true, - false, - 0, - true, - true, - ) - .await?; - self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; - self.lora.set_channel(config.frequency).await?; - self.lora.rx(90 * 1000).await?; - let mut received_len = 0u8; - self.lora - .process_irq(Some(receiving_buffer), Some(&mut received_len), None) - .await?; - trace!("RX DONE"); + self.lora + .set_rx_config( + config.spreading_factor.into(), + config.bandwidth.into(), + config.coding_rate.into(), + 8, + 4, + false, + 0u8, + true, + false, + 0, + true, + true, + ) + .await?; + self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; + self.lora.set_channel(config.frequency).await?; + self.lora.rx(90 * 1000).await?; + let mut received_len = 0u8; + self.lora + .process_irq(Some(receiving_buffer), Some(&mut received_len), None) + .await?; + trace!("RX DONE"); - let packet_status = self.lora.get_latest_packet_status(); - let mut rssi = 0i16; - let mut snr = 0i8; - if packet_status.is_some() { - rssi = packet_status.unwrap().rssi as i16; - snr = packet_status.unwrap().snr; - } - - Ok((received_len as usize, RxQuality::new(rssi, snr))) + let packet_status = self.lora.get_latest_packet_status(); + let mut rssi = 0i16; + let mut snr = 0i8; + if packet_status.is_some() { + rssi = packet_status.unwrap().rssi as i16; + snr = packet_status.unwrap().snr; } + + Ok((received_len as usize, RxQuality::new(rssi, snr))) } } diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs index 8904c9a13..4e8dc2232 100644 --- a/embassy-lora/src/sx127x/mod.rs +++ b/embassy-lora/src/sx127x/mod.rs @@ -1,5 +1,3 @@ -use core::future::Future; - use embedded_hal::digital::v2::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::*; @@ -88,101 +86,78 @@ where { type PhyError = Sx127xError; - type TxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CS: 'm, - RESET: 'm, - E: 'm, - I: 'm, - RFS: 'm; - - fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { + async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { trace!("TX START"); - async move { + self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); + self.rfs.set_tx(); + self.radio.set_tx_power(14, 0).await?; + self.radio.set_frequency(config.rf.frequency).await?; + // TODO: Modify radio to support other coding rates + self.radio.set_coding_rate_4(5).await?; + self.radio + .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth)) + .await?; + self.radio + .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor)) + .await?; + + self.radio.set_preamble_length(8).await?; + self.radio.set_lora_pa_ramp().await?; + self.radio.set_lora_sync_word().await?; + self.radio.set_invert_iq(false).await?; + self.radio.set_crc(true).await?; + + self.radio.set_dio0_tx_done().await?; + + self.radio.transmit_start(buf).await?; + + loop { + self.irq.wait_for_rising_edge().await.unwrap(); self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - self.rfs.set_tx(); - self.radio.set_tx_power(14, 0).await?; - self.radio.set_frequency(config.rf.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor)) - .await?; - - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_pa_ramp().await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(false).await?; - self.radio.set_crc(true).await?; - - self.radio.set_dio0_tx_done().await?; - - self.radio.transmit_start(buf).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { - trace!("TX DONE"); - return Ok(0); - } + let irq = self.radio.clear_irq().await.ok().unwrap(); + if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { + trace!("TX DONE"); + return Ok(0); } } } - type RxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CS: 'm, - RESET: 'm, - E: 'm, - I: 'm, - RFS: 'm; + async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { + self.rfs.set_rx(); + self.radio.reset_payload_length().await?; + self.radio.set_frequency(config.frequency).await?; + // TODO: Modify radio to support other coding rates + self.radio.set_coding_rate_4(5).await?; + self.radio + .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) + .await?; + self.radio + .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) + .await?; - fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { - trace!("RX START"); - async move { - self.rfs.set_rx(); - self.radio.reset_payload_length().await?; - self.radio.set_frequency(config.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) - .await?; + self.radio.set_preamble_length(8).await?; + self.radio.set_lora_sync_word().await?; + self.radio.set_invert_iq(true).await?; + self.radio.set_crc(true).await?; - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(true).await?; - self.radio.set_crc(true).await?; + self.radio.set_dio0_rx_done().await?; + self.radio.set_mode(RadioMode::RxContinuous).await?; - self.radio.set_dio0_rx_done().await?; - self.radio.set_mode(RadioMode::RxContinuous).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { - let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16; - let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8; - let response = if let Ok(size) = self.radio.read_packet_size().await { - self.radio.read_packet(buf).await?; - Ok((size, RxQuality::new(rssi, snr))) - } else { - Ok((0, RxQuality::new(rssi, snr))) - }; - trace!("RX DONE"); - return response; - } + loop { + self.irq.wait_for_rising_edge().await.unwrap(); + self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); + let irq = self.radio.clear_irq().await.ok().unwrap(); + if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { + let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16; + let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8; + let response = if let Ok(size) = self.radio.read_packet_size().await { + self.radio.read_packet(buf).await?; + Ok((size, RxQuality::new(rssi, snr))) + } else { + Ok((0, RxQuality::new(rssi, snr))) + }; + trace!("RX DONE"); + return response; } } } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index fc614cb80..9ef7944c4 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -20,8 +20,8 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm 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 } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index d446d41b2..9f63fee1c 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -15,8 +15,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "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 } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 27d7c29c2..ea01f610c 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); - let region = region::EU868::default().into(); + let region = region::Configuration::new(region::Region::EU868); let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index df295ca49..19a7cfcea 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -11,8 +11,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } -lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 7f34dd306..32f29cc5d 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) { radio_config.calibrate_image = CalibrateImage::ISM_863_870; let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap(); - let mut region: region::Configuration = region::EU868::default().into(); + let mut region = region::Configuration::new(region::Region::EU868); // NOTE: This is specific for TTN, as they have a special RX1 delay region.set_receive_delay1(5000); From f395ec44e8442a3133b080df96da54da18127b97 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Apr 2023 21:28:27 -0500 Subject: [PATCH 0887/1575] stm32/rcc: add pllsai clock --- embassy-stm32/src/rcc/f4.rs | 5 ++++- embassy-stm32/src/rcc/mod.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 5427d2fb4..e0929ca49 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -479,8 +479,11 @@ pub(crate) unsafe fn init(config: Config) { pll48: plls.pll48clk.map(Hertz), - #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] + #[cfg(not(stm32f410))] plli2s: plls.plli2sclk.map(Hertz), + + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] + pllsai: None, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 7c1e416fe..d6816d6a8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -60,9 +60,12 @@ pub struct Clocks { #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] pub pll48: Option, - #[cfg(all(stm32f4, not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))))] + #[cfg(all(rcc_f4, not(stm32f410)))] pub plli2s: Option, + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] + pub pllsai: Option, + #[cfg(stm32f1)] pub adc: Hertz, From 81f10e136a95c33f98f5fa06ac3996bee97e66d3 Mon Sep 17 00:00:00 2001 From: Jacob Davis-Hansson Date: Sat, 15 Apr 2023 15:13:44 +0200 Subject: [PATCH 0888/1575] outover instead of inover --- embassy-rp/src/uart/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index dfa56a842..f7a4c5a60 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -6,7 +6,7 @@ use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; -use crate::pac::io::vals::Inover; +use crate::pac::io::vals::{Inover, Outover}; #[cfg(feature = "nightly")] mod buffered; @@ -396,7 +396,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { if let Some(pin) = &tx { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_tx { Inover::INVERT } else { Inover::NORMAL }); + w.set_outover(if config.invert_tx { Outover::INVERT } else { Outover::NORMAL }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } @@ -417,7 +417,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { if let Some(pin) = &rts { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_rts { Inover::INVERT } else { Inover::NORMAL }); + w.set_outover(if config.invert_rts { Outover::INVERT } else { Outover::NORMAL }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } From 8da9c07a656f375b62f76cdb601b6eba75b6d9d0 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 11:06:05 -0500 Subject: [PATCH 0889/1575] stm32/rtc: disable nonworking versions --- embassy-stm32/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 70e6aa2bf..675fd1ec1 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -49,7 +49,7 @@ pub mod pwm; pub mod qspi; #[cfg(rng)] pub mod rng; -#[cfg(all(rtc, not(rtc_v1)))] +#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3))))] pub mod rtc; #[cfg(sdmmc)] pub mod sdmmc; From bc550cbfda773483dd1a90e87c2300f627695cea Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 11:06:24 -0500 Subject: [PATCH 0890/1575] adjust .vscode file --- .vscode/settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 700804dca..9ef7fe1ce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,10 +16,11 @@ // "embassy-executor/Cargo.toml", // "embassy-sync/Cargo.toml", "examples/nrf52840/Cargo.toml", - //"examples/nrf5340/Cargo.toml", + // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", + // "examples/stm32c0/Cargo.toml", // "examples/stm32f0/Cargo.toml", // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", @@ -28,6 +29,7 @@ // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", // "examples/stm32g4/Cargo.toml", + // "examples/stm32h5/Cargo.toml", // "examples/stm32h7/Cargo.toml", // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", @@ -35,9 +37,7 @@ // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", // "examples/stm32wb/Cargo.toml", - // "examples/stm32wb55/Cargo.toml", // "examples/stm32wl/Cargo.toml", - // "examples/stm32wl55/Cargo.toml", // "examples/wasm/Cargo.toml", ], } \ No newline at end of file From e9ede443bca345f8651a7d8229b319432650dfce Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 11:14:17 -0500 Subject: [PATCH 0891/1575] stm32/rtc: disable nonworking versions --- embassy-stm32/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 675fd1ec1..0dbc9e5c8 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -49,7 +49,7 @@ pub mod pwm; pub mod qspi; #[cfg(rng)] pub mod rng; -#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3))))] +#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))] pub mod rtc; #[cfg(sdmmc)] pub mod sdmmc; From 7a682ec02af50026d31296ce5cac6383580f5e55 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 16 Apr 2023 19:39:32 +0200 Subject: [PATCH 0892/1575] rp: add division intrinsics rp2040-hal adds division intrinsics using the hardware divider unit in the SIO, as does the pico-sdk itself. using the hardware is faster than the compiler_rt implementations, and more compact too. --- embassy-rp/src/intrinsics.rs | 198 +++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 3e75fb7fc..3b63846d4 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -274,3 +274,201 @@ macro_rules! intrinsics { intrinsics!($($rest)*); }; } + +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/sio.rs + +// This takes advantage of how AAPCS defines a 64-bit return on 32-bit registers +// by packing it into r0[0:31] and r1[32:63]. So all we need to do is put +// the remainder in the high order 32 bits of a 64 bit result. We can also +// alias the division operators to these for a similar reason r0 is the +// result either way and r1 a scratch register, so the caller can't assume it +// retains the argument value. +#[cfg(target_arch = "arm")] +core::arch::global_asm!( + ".macro hwdivider_head", + "ldr r2, =(0xd0000000)", // SIO_BASE + // Check the DIRTY state of the divider by shifting it into the C + // status bit. + "ldr r3, [r2, #0x078]", // DIV_CSR + "lsrs r3, #2", // DIRTY = 1, so shift 2 down + // We only need to save the state when DIRTY, otherwise we can just do the + // division directly. + "bcs 2f", + "1:", + // Do the actual division now, we're either not DIRTY, or we've saved the + // state and branched back here so it's safe now. + ".endm", + ".macro hwdivider_tail", + // 8 cycle delay to wait for the result. Each branch takes two cycles + // and fits into a 2-byte Thumb instruction, so this is smaller than + // 8 NOPs. + "b 3f", + "3: b 3f", + "3: b 3f", + "3: b 3f", + "3:", + // Read the quotient last, since that's what clears the dirty flag. + "ldr r1, [r2, #0x074]", // DIV_REMAINDER + "ldr r0, [r2, #0x070]", // DIV_QUOTIENT + // Either return to the caller or back to the state restore. + "bx lr", + "2:", + // Since we can't save the signed-ness of the calculation, we have to make + // sure that there's at least an 8 cycle delay before we read the result. + // The push takes 5 cycles, and we've already spent at least 7 checking + // the DIRTY state to get here. + "push {{r4-r6, lr}}", + // Read the quotient last, since that's what clears the dirty flag. + "ldr r3, [r2, #0x060]", // DIV_UDIVIDEND + "ldr r4, [r2, #0x064]", // DIV_UDIVISOR + "ldr r5, [r2, #0x074]", // DIV_REMAINDER + "ldr r6, [r2, #0x070]", // DIV_QUOTIENT + // If we get interrupted here (before a write sets the DIRTY flag) it's + // fine, since we have the full state, so the interruptor doesn't have to + // restore it. Once the write happens and the DIRTY flag is set, the + // interruptor becomes responsible for restoring our state. + "bl 1b", + // If we are interrupted here, then the interruptor will start an incorrect + // calculation using a wrong divisor, but we'll restore the divisor and + // result ourselves correctly. This sets DIRTY, so any interruptor will + // save the state. + "str r3, [r2, #0x060]", // DIV_UDIVIDEND + // If we are interrupted here, the the interruptor may start the + // calculation using incorrectly signed inputs, but we'll restore the + // result ourselves. This sets DIRTY, so any interruptor will save the + // state. + "str r4, [r2, #0x064]", // DIV_UDIVISOR + // If we are interrupted here, the interruptor will have restored + // everything but the quotient may be wrongly signed. If the calculation + // started by the above writes is still ongoing it is stopped, so it won't + // replace the result we're restoring. DIRTY and READY set, but only + // DIRTY matters to make the interruptor save the state. + "str r5, [r2, #0x074]", // DIV_REMAINDER + // State fully restored after the quotient write. This sets both DIRTY + // and READY, so whatever we may have interrupted can read the result. + "str r6, [r2, #0x070]", // DIV_QUOTIENT + "pop {{r4-r6, pc}}", + ".endm", +); + +macro_rules! division_function { + ( + $name:ident $($intrinsic:ident)* ( $argty:ty ) { + $($begin:literal),+ + } + ) => { + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] + core::arch::global_asm!( + // Mangle the name slightly, since this is a global symbol. + concat!(".global _rphal_", stringify!($name)), + concat!(".type _rphal_", stringify!($name), ", %function"), + ".align 2", + concat!("_rphal_", stringify!($name), ":"), + $( + concat!(".global ", stringify!($intrinsic)), + concat!(".type ", stringify!($intrinsic), ", %function"), + concat!(stringify!($intrinsic), ":"), + )* + + "hwdivider_head", + $($begin),+ , + "hwdivider_tail", + ); + + #[cfg(all(target_arch = "arm", not(feature = "intrinsics")))] + core::arch::global_asm!( + // Mangle the name slightly, since this is a global symbol. + concat!(".global _rphal_", stringify!($name)), + concat!(".type _rphal_", stringify!($name), ", %function"), + ".align 2", + concat!("_rphal_", stringify!($name), ":"), + + "hwdivider_head", + $($begin),+ , + "hwdivider_tail", + ); + + #[cfg(target_arch = "arm")] + extern "aapcs" { + // Connect a local name to global symbol above through FFI. + #[link_name = concat!("_rphal_", stringify!($name)) ] + fn $name(n: $argty, d: $argty) -> u64; + } + + #[cfg(not(target_arch = "arm"))] + #[allow(unused_variables)] + unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 } + }; +} + +division_function! { + unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) { + "str r0, [r2, #0x060]", // DIV_UDIVIDEND + "str r1, [r2, #0x064]" // DIV_UDIVISOR + } +} + +division_function! { + signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) { + "str r0, [r2, #0x068]", // DIV_SDIVIDEND + "str r1, [r2, #0x06c]" // DIV_SDIVISOR + } +} + +fn divider_unsigned(n: u32, d: u32) -> DivResult { + let packed = unsafe { unsigned_divmod(n, d) }; + DivResult { + quotient: packed as u32, + remainder: (packed >> 32) as u32, + } +} + +fn divider_signed(n: i32, d: i32) -> DivResult { + let packed = unsafe { signed_divmod(n, d) }; + // Double casts to avoid sign extension + DivResult { + quotient: packed as u32 as i32, + remainder: (packed >> 32) as u32 as i32, + } +} + +/// Result of divide/modulo operation +struct DivResult { + /// The quotient of divide/modulo operation + pub quotient: T, + /// The remainder of divide/modulo operation + pub remainder: T, +} + +intrinsics! { + extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { + divider_unsigned(n, d).quotient + } + + extern "C" fn __umodsi3(n: u32, d: u32) -> u32 { + divider_unsigned(n, d).remainder + } + + extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 { + let quo_rem = divider_unsigned(n, d); + if let Some(rem) = rem { + *rem = quo_rem.remainder; + } + quo_rem.quotient + } + + extern "C" fn __divsi3(n: i32, d: i32) -> i32 { + divider_signed(n, d).quotient + } + + extern "C" fn __modsi3(n: i32, d: i32) -> i32 { + divider_signed(n, d).remainder + } + + extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 { + let quo_rem = divider_signed(n, d); + *rem = quo_rem.remainder; + quo_rem.quotient + } +} From a258e15c239305ba654e5d60a5f649b87855759f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 16 Apr 2023 23:59:26 +0200 Subject: [PATCH 0893/1575] rp: switch to released rp-pac v1.0 --- embassy-rp/Cargo.toml | 3 +-- embassy-rp/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 2ef2c8f07..b7ed6ccbb 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,8 +61,7 @@ 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"] } +rp-pac = { version = "1", 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.10", optional = true} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9e99b2fbb..1d63f6c2e 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -44,9 +44,9 @@ pub use embassy_cortex_m::executor; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] -pub use rp2040_pac2 as pac; +pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] -pub(crate) use rp2040_pac2 as pac; +pub(crate) use rp_pac as pac; embassy_hal_common::peripherals! { PIN_0, From 776e001b5befc50ff409b5cd8252bbb61fbb5acc Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 17:47:25 -0500 Subject: [PATCH 0894/1575] stm32: remove TIMX singleton when used on timer driver fixes #1316. --- embassy-hal-common/src/macros.rs | 25 ++++++- embassy-stm32/build.rs | 114 ++++++++++++++++++------------- 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs index 5e62e048a..f06b46002 100644 --- a/embassy-hal-common/src/macros.rs +++ b/embassy-hal-common/src/macros.rs @@ -1,5 +1,5 @@ #[macro_export] -macro_rules! peripherals { +macro_rules! peripherals_definition { ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { /// Types for the peripheral singletons. pub mod peripherals { @@ -26,7 +26,12 @@ macro_rules! peripherals { $crate::impl_peripheral!($name); )* } + }; +} +#[macro_export] +macro_rules! peripherals_struct { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { /// Struct containing all the peripheral singletons. /// /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]. @@ -76,6 +81,24 @@ macro_rules! peripherals { }; } +#[macro_export] +macro_rules! peripherals { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + $crate::peripherals_definition!( + $( + $(#[$cfg])? + $name, + )* + ); + $crate::peripherals_struct!( + $( + $(#[$cfg])? + $name, + )* + ); + }; +} + #[macro_export] macro_rules! into_ref { ($($name:ident),*) => { diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 73bd29fcf..bb6c99963 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -81,11 +81,74 @@ fn main() { singletons.push(c.name.to_string()); } + // ======== + // Handle time-driver-XXXX features. + + let time_driver = match env::vars() + .map(|(a, _)| a) + .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_")) + .get_one() + { + Ok(x) => Some( + x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_") + .unwrap() + .to_ascii_lowercase(), + ), + Err(GetOneError::None) => None, + Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), + }; + + let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) { + None => "", + Some("tim2") => "TIM2", + Some("tim3") => "TIM3", + Some("tim4") => "TIM4", + Some("tim5") => "TIM5", + Some("tim12") => "TIM12", + Some("tim15") => "TIM15", + Some("any") => { + if singletons.contains(&"TIM2".to_string()) { + "TIM2" + } else if singletons.contains(&"TIM3".to_string()) { + "TIM3" + } else if singletons.contains(&"TIM4".to_string()) { + "TIM4" + } else if singletons.contains(&"TIM5".to_string()) { + "TIM5" + } else if singletons.contains(&"TIM12".to_string()) { + "TIM12" + } else if singletons.contains(&"TIM15".to_string()) { + "TIM15" + } else { + panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.") + } + } + _ => panic!("unknown time_driver {:?}", time_driver), + }; + + if time_driver_singleton != "" { + println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase()); + } + + // ======== + // Write singletons + let mut g = TokenStream::new(); let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); + g.extend(quote! { - embassy_hal_common::peripherals!(#(#singleton_tokens),*); + embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*); + }); + + let singleton_tokens: Vec<_> = singletons + .iter() + .filter(|s| *s != &time_driver_singleton.to_string()) + .map(|s| format_ident!("{}", s)) + .collect(); + + g.extend(quote! { + embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*); }); // ======== @@ -536,6 +599,10 @@ fn main() { let pin_name = format_ident!("{}", pin.pin); let af = pin.af.unwrap_or(0); + if peri == time_driver_singleton { + continue; + } + // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now @@ -838,51 +905,6 @@ fn main() { println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9 - // ======== - // Handle time-driver-XXXX features. - - let time_driver = match env::vars() - .map(|(a, _)| a) - .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_")) - .get_one() - { - Ok(x) => Some( - x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_") - .unwrap() - .to_ascii_lowercase(), - ), - Err(GetOneError::None) => None, - Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), - }; - - match time_driver.as_ref().map(|x| x.as_ref()) { - None => {} - Some("tim2") => println!("cargo:rustc-cfg=time_driver_tim2"), - Some("tim3") => println!("cargo:rustc-cfg=time_driver_tim3"), - Some("tim4") => println!("cargo:rustc-cfg=time_driver_tim4"), - Some("tim5") => println!("cargo:rustc-cfg=time_driver_tim5"), - Some("tim12") => println!("cargo:rustc-cfg=time_driver_tim12"), - Some("tim15") => println!("cargo:rustc-cfg=time_driver_tim15"), - Some("any") => { - if singletons.contains(&"TIM2".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim2"); - } else if singletons.contains(&"TIM3".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim3"); - } else if singletons.contains(&"TIM4".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim4"); - } else if singletons.contains(&"TIM5".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim5"); - } else if singletons.contains(&"TIM12".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim12"); - } else if singletons.contains(&"TIM15".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim15"); - } else { - panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.") - } - } - _ => panic!("unknown time_driver {:?}", time_driver), - } - // Handle time-driver-XXXX features. if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {} println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]); From 9e1ddeac8604f5df85df56159b97004de890aeb3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 18:32:55 -0500 Subject: [PATCH 0895/1575] stm32: fix defective example --- embassy-stm32/build.rs | 4 ---- examples/stm32g4/src/bin/pwm.rs | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index bb6c99963..c7d12e13a 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -599,10 +599,6 @@ fn main() { let pin_name = format_ident!("{}", pin.pin); let af = pin.af.unwrap_or(0); - if peri == time_driver_singleton { - continue; - } - // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 017e89e41..8f7842ed7 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -15,8 +15,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PA5); - let mut pwm = SimplePwm::new(p.TIM2, Some(ch1), None, None, None, khz(10)); + let ch1 = PwmPin::new_ch1(p.PC0); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); From 90c1422381f4a798c83146a2125529bb2f761598 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 19:30:42 -0500 Subject: [PATCH 0896/1575] stm32/rtc: remove chrono datetime and add converters --- .../rtc/{datetime_no_deps.rs => datetime.rs} | 56 +++++++++++++++++++ embassy-stm32/src/rtc/mod.rs | 3 - examples/stm32f4/Cargo.toml | 2 +- 3 files changed, 57 insertions(+), 4 deletions(-) rename embassy-stm32/src/rtc/{datetime_no_deps.rs => datetime.rs} (69%) diff --git a/embassy-stm32/src/rtc/datetime_no_deps.rs b/embassy-stm32/src/rtc/datetime.rs similarity index 69% rename from embassy-stm32/src/rtc/datetime_no_deps.rs rename to embassy-stm32/src/rtc/datetime.rs index 173f38377..5be68b89b 100644 --- a/embassy-stm32/src/rtc/datetime_no_deps.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -1,3 +1,8 @@ +use core::convert::From; + +#[cfg(feature = "chrono")] +use chrono::{self, Datelike, NaiveDate, Timelike, Weekday}; + use super::byte_to_bcd2; use crate::pac::rtc::Rtc; @@ -41,6 +46,35 @@ pub struct DateTime { pub second: u8, } +#[cfg(feature = "chrono")] +impl From for DateTime { + fn from(date_time: chrono::NaiveDateTime) -> Self { + Self { + year: (date_time.year() - 1970) as u16, + month: date_time.month() as u8, + day: date_time.day() as u8, + day_of_week: date_time.weekday().into(), + hour: date_time.hour() as u8, + minute: date_time.minute() as u8, + second: date_time.second() as u8, + } + } +} + +#[cfg(feature = "chrono")] +impl From for chrono::NaiveDateTime { + fn from(date_time: DateTime) -> Self { + NaiveDate::from_ymd_opt( + (date_time.year + 1970) as i32, + date_time.month as u32, + date_time.day as u32, + ) + .unwrap() + .and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32) + .unwrap() + } +} + /// A day of the week #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] @@ -55,6 +89,28 @@ pub enum DayOfWeek { Sunday = 6, } +#[cfg(feature = "chrono")] +impl From for DayOfWeek { + fn from(weekday: Weekday) -> Self { + day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap() + } +} + +#[cfg(feature = "chrono")] +impl From for chrono::Weekday { + fn from(weekday: DayOfWeek) -> Self { + match weekday { + DayOfWeek::Monday => Weekday::Mon, + DayOfWeek::Tuesday => Weekday::Tue, + DayOfWeek::Wednesday => Weekday::Wed, + DayOfWeek::Thursday => Weekday::Thu, + DayOfWeek::Friday => Weekday::Fri, + DayOfWeek::Saturday => Weekday::Sat, + DayOfWeek::Sunday => Weekday::Sun, + } + } +} + fn day_of_week_from_u8(v: u8) -> Result { Ok(match v { 0 => DayOfWeek::Monday, diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index ee3349b27..170783b2d 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,8 +1,5 @@ //! RTC peripheral abstraction use core::marker::PhantomData; - -#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")] -#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")] mod datetime; pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 1736769ef..69dcab64c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } From 27ec29e2c53e4bc618ec97f0071befe52c8520b9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 19:32:15 -0500 Subject: [PATCH 0897/1575] stm32/rtc: remove unused import --- embassy-stm32/src/rtc/datetime.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index 5be68b89b..6274c1e05 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "chrono")] use core::convert::From; #[cfg(feature = "chrono")] From 4044d728a64d457d8b0fade83dcc40abbf1867c6 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 17 Apr 2023 15:35:02 +0200 Subject: [PATCH 0898/1575] update to released versions --- embassy-lora/Cargo.toml | 4 ++-- examples/nrf52840/Cargo.toml | 4 ++-- examples/stm32l0/Cargo.toml | 4 ++-- examples/stm32wl/Cargo.toml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 13b3acab2..50449dd46 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -38,5 +38,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } +lorawan = { version = "0.7.2", default-features = false } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9ef7944c4..af19413cc 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -20,8 +20,8 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm 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", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 9f63fee1c..b04faf535 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -15,8 +15,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} -lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 19a7cfcea..cb3526fa4 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -11,8 +11,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } -lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"] } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } +lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" From df7ef1d98f48a18027f5f0dcd0bc39cef0bfe8b9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:13:25 +0200 Subject: [PATCH 0899/1575] stm32/sdmmc: remove cfg_if. --- embassy-stm32/src/sdmmc/mod.rs | 178 ++++++++++++++++----------------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ac00b5176..92c54d815 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -135,57 +135,53 @@ enum Response { Long = 3, } -cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to - /// `sdmmc_ck` in Hertz. - /// - /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), - /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. - fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { - // sdmmc_v1 maximum clock is 50 MHz - if sdmmc_ck > 50_000_000 { - return Err(Error::BadClock); - } +/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to +/// `sdmmc_ck` in Hertz. +/// +/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), +/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. +#[cfg(sdmmc_v1)] +fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { + // sdmmc_v1 maximum clock is 50 MHz + if sdmmc_ck > 50_000_000 { + return Err(Error::BadClock); + } - // bypass divisor - if ker_ck.0 <= sdmmc_ck { - return Ok((true, 0, ker_ck)); - } + // bypass divisor + if ker_ck.0 <= sdmmc_ck { + return Ok((true, 0, ker_ck)); + } - // `ker_ck / sdmmc_ck` rounded up - let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { - 0 | 1 => Ok(0), - x @ 2..=258 => { - Ok((x - 2) as u8) - } - _ => Err(Error::BadClock), - }?; + // `ker_ck / sdmmc_ck` rounded up + let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { + 0 | 1 => Ok(0), + x @ 2..=258 => Ok((x - 2) as u8), + _ => Err(Error::BadClock), + }?; - // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] - let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); - Ok((false, clk_div, clk_f)) - } - } else if #[cfg(sdmmc_v2)] { - /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to - /// `sdmmc_ck` in Hertz. - /// - /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), - /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. - fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { - // `ker_ck / sdmmc_ck` rounded up - match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { - 0 | 1 => Ok((false, 0, ker_ck)), - x @ 2..=2046 => { - // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] - let clk_div = ((x + 1) / 2) as u16; - let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); - - Ok((false, clk_div, clk)) - } - _ => Err(Error::BadClock), - } + // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] + let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); + Ok((false, clk_div, clk_f)) +} + +/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to +/// `sdmmc_ck` in Hertz. +/// +/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), +/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. +#[cfg(sdmmc_v2)] +fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { + // `ker_ck / sdmmc_ck` rounded up + match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { + 0 | 1 => Ok((false, 0, ker_ck)), + x @ 2..=2046 => { + // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] + let clk_div = ((x + 1) / 2) as u16; + let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); + + Ok((false, clk_div, clk)) } + _ => Err(Error::BadClock), } } @@ -904,13 +900,10 @@ impl SdmmcInner { // NOTE(unsafe) Atomic read with no side-effects unsafe { let status = regs.star().read(); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - status.rxact() || status.txact() - } else if #[cfg(sdmmc_v2)] { - status.dpsmact() - } - } + #[cfg(sdmmc_v1)] + return status.rxact() || status.txact(); + #[cfg(sdmmc_v2)] + return status.dpsmact(); } } @@ -922,13 +915,10 @@ impl SdmmcInner { // NOTE(unsafe) Atomic read with no side-effects unsafe { let status = regs.star().read(); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - status.cmdact() - } else if #[cfg(sdmmc_v2)] { - status.cpsmact() - } - } + #[cfg(sdmmc_v1)] + return status.cmdact(); + #[cfg(sdmmc_v2)] + return status.cpsmact(); } } @@ -961,20 +951,26 @@ impl SdmmcInner { regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); regs.dlenr().write(|w| w.set_datalength(length_bytes)); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - let request = dma.request(); - dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { + #[cfg(sdmmc_v1)] + { + let request = dma.request(); + dma.start_read( + request, + regs.fifor().ptr() as *const u32, + buffer, + crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, fifo_threshold: Some(crate::dma::FifoThreshold::Full), ..Default::default() - }); - } else if #[cfg(sdmmc_v2)] { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); } regs.dctrl().modify(|w| { @@ -1011,20 +1007,27 @@ impl SdmmcInner { regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); regs.dlenr().write(|w| w.set_datalength(length_bytes)); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - let request = dma.request(); - dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { + #[cfg(sdmmc_v1)] + { + let request = dma.request(); + dma.start_write( + request, + buffer, + regs.fifor().ptr() as *mut u32, + crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, fifo_threshold: Some(crate::dma::FifoThreshold::Full), ..Default::default() - }); - } else if #[cfg(sdmmc_v2)] { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *const u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r() + .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); } regs.dctrl().modify(|w| { @@ -1043,16 +1046,13 @@ impl SdmmcInner { let regs = self.0; unsafe { - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - regs.dctrl().modify(|w| { - w.set_dmaen(false); - w.set_dten(false); - }); - } else if #[cfg(sdmmc_v2)] { - regs.idmactrlr().modify(|w| w.set_idmaen(false)); - } - } + #[cfg(sdmmc_v1)] + regs.dctrl().modify(|w| { + w.set_dmaen(false); + w.set_dten(false); + }); + #[cfg(sdmmc_v2)] + regs.idmactrlr().modify(|w| w.set_idmaen(false)); } } From e14fa11fc35c1b163d86404935d180b9ef88e461 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:13:34 +0200 Subject: [PATCH 0900/1575] stm32/sdmmc: remove unneeded pointer casts. --- embassy-stm32/src/sdmmc/mod.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 92c54d815..22b578dcf 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -793,7 +793,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(buffer as *mut [u32; 128], 512, 9, data_transfer_timeout, dma); + self.prepare_datapath_read(buffer, 512, 9, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::read_single_block(address), true)?; @@ -1121,7 +1121,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); + self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 @@ -1202,7 +1202,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); + self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::card_status(0), true)?; @@ -1320,7 +1320,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(&mut scr as *mut [u32], 8, 3, data_transfer_timeout, dma); + self.prepare_datapath_read(&mut scr[..], 8, 3, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::cmd51(), true)?; @@ -1560,15 +1560,14 @@ pin_trait!(D5Pin, Instance); pin_trait!(D6Pin, Instance); pin_trait!(D7Pin, Instance); -cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - dma_trait!(SdmmcDma, Instance); - } else if #[cfg(sdmmc_v2)] { - // SDMMCv2 uses internal DMA - pub trait SdmmcDma {} - impl SdmmcDma for NoDma {} - } -} +#[cfg(sdmmc_v1)] +dma_trait!(SdmmcDma, Instance); + +// SDMMCv2 uses internal DMA +#[cfg(sdmmc_v2)] +pub trait SdmmcDma {} +#[cfg(sdmmc_v2)] +impl SdmmcDma for NoDma {} cfg_if::cfg_if! { // TODO, these could not be implemented, because required clocks are not exposed in RCC: From 0dfa19299224f28ab686062d472c0ccea72ffd5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:54:22 +0200 Subject: [PATCH 0901/1575] stm32/sdmmc: remove "inner" layer. --- embassy-stm32/src/sdmmc/mod.rs | 1444 ++++++++++++++------------------ 1 file changed, 642 insertions(+), 802 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 22b578dcf..23ece3a2a 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -41,7 +41,7 @@ impl Default for Signalling { } #[repr(align(4))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataBlock(pub [u8; 512]); @@ -61,7 +61,7 @@ impl DerefMut for DataBlock { /// Errors #[non_exhaustive] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { Timeout, @@ -175,7 +175,7 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { 0 | 1 => Ok((false, 0, ker_ck)), x @ 2..=2046 => { - // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] + // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] let clk_div = ((x + 1) / 2) as u16; let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); @@ -204,9 +204,10 @@ impl Default for Config { } /// Sdmmc device -pub struct Sdmmc<'d, T: Instance, Dma = NoDma> { +pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma = NoDma> { _peri: PeripheralRef<'d, T>, irq: PeripheralRef<'d, T::Interrupt>, + #[allow(unused)] dma: PeripheralRef<'d, Dma>, clk: PeripheralRef<'d, AnyPin>, @@ -305,49 +306,6 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { config, ) } - - fn new_inner( - sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - dma: impl Peripheral

+ 'd, - clk: PeripheralRef<'d, AnyPin>, - cmd: PeripheralRef<'d, AnyPin>, - d0: PeripheralRef<'d, AnyPin>, - d1: Option>, - d2: Option>, - d3: Option>, - config: Config, - ) -> Self { - into_ref!(sdmmc, irq, dma); - - T::enable(); - T::reset(); - - let inner = T::inner(); - unsafe { inner.new_inner() }; - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self { - _peri: sdmmc, - irq, - dma, - - clk, - cmd, - d0, - d1, - d2, - d3, - - config, - clock: SD_INIT_FREQ, - signalling: Default::default(), - card: None, - } - } } #[cfg(sdmmc_v2)] @@ -375,6 +333,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, irq, + NoDma.into_ref(), clk.map_into(), cmd.map_into(), d0.map_into(), @@ -417,6 +376,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, irq, + NoDma.into_ref(), clk.map_into(), cmd.map_into(), d0.map_into(), @@ -426,10 +386,13 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { config, ) } +} +impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { fn new_inner( sdmmc: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, clk: PeripheralRef<'d, AnyPin>, cmd: PeripheralRef<'d, AnyPin>, d0: PeripheralRef<'d, AnyPin>, @@ -438,22 +401,41 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { d3: Option>, config: Config, ) -> Self { - into_ref!(sdmmc, irq); + into_ref!(sdmmc, irq, dma); T::enable(); T::reset(); - let inner = T::inner(); - unsafe { inner.new_inner() }; - irq.set_handler(Self::on_interrupt); irq.unpend(); irq.enable(); + let regs = T::regs(); + unsafe { + regs.clkcr().write(|w| { + w.set_pwrsav(false); + w.set_negedge(false); + + // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. + // See chip erratas for more details. + #[cfg(sdmmc_v1)] + w.set_hwfc_en(false); + #[cfg(sdmmc_v2)] + w.set_hwfc_en(true); + + #[cfg(sdmmc_v1)] + w.set_clken(true); + }); + + // 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)); + } + Self { _peri: sdmmc, irq, - dma: NoDma.into_ref(), + dma, clk, cmd, @@ -468,177 +450,552 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { card: None, } } -} -impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { + /// Data transfer is in progress #[inline(always)] + fn data_active() -> bool { + let regs = T::regs(); + + // NOTE(unsafe) Atomic read with no side-effects + unsafe { + let status = regs.star().read(); + #[cfg(sdmmc_v1)] + return status.rxact() || status.txact(); + #[cfg(sdmmc_v2)] + return status.dpsmact(); + } + } + + /// Coammand transfer is in progress + #[inline(always)] + fn cmd_active() -> bool { + let regs = T::regs(); + + // NOTE(unsafe) Atomic read with no side-effects + unsafe { + let status = regs.star().read(); + #[cfg(sdmmc_v1)] + return status.cmdact(); + #[cfg(sdmmc_v2)] + return status.cpsmact(); + } + } + + /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) + #[inline(always)] + fn wait_idle() { + while Self::data_active() || Self::cmd_active() {} + } + + /// # Safety + /// + /// `buffer` must be valid for the whole transfer and word aligned + unsafe fn prepare_datapath_read(&mut self, buffer: *mut [u32], length_bytes: u32, block_size: u8) { + assert!(block_size <= 14, "Block size up to 2^14 bytes"); + let regs = T::regs(); + + // Command AND Data state machines must be idle + Self::wait_idle(); + Self::clear_interrupt_flags(); + + // NOTE(unsafe) We have exclusive access to the regisers + + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); + + #[cfg(sdmmc_v1)] + { + let request = self.dma.request(); + self.dma.start_read( + request, + regs.fifor().ptr() as *const u32, + buffer, + crate::dma::TransferOptions { + pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, + flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), + ..Default::default() + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + } + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(true); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + } + + /// # Safety + /// + /// `buffer` must be valid for the whole transfer and word aligned + unsafe fn prepare_datapath_write(&mut self, buffer: *const [u32], length_bytes: u32, block_size: u8) { + assert!(block_size <= 14, "Block size up to 2^14 bytes"); + let regs = T::regs(); + + // Command AND Data state machines must be idle + Self::wait_idle(); + Self::clear_interrupt_flags(); + + // NOTE(unsafe) We have exclusive access to the regisers + + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); + + #[cfg(sdmmc_v1)] + { + let request = self.dma.request(); + self.dma.start_write( + request, + buffer, + regs.fifor().ptr() as *mut u32, + crate::dma::TransferOptions { + pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, + flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), + ..Default::default() + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r() + .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + } + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(false); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + } + + /// Stops the DMA datapath + fn stop_datapath() { + let regs = T::regs(); + + unsafe { + #[cfg(sdmmc_v1)] + regs.dctrl().modify(|w| { + w.set_dmaen(false); + w.set_dten(false); + }); + #[cfg(sdmmc_v2)] + regs.idmactrlr().modify(|w| w.set_idmaen(false)); + } + } + + /// Sets the CLKDIV field in CLKCR. Updates clock field in self + fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { + let regs = T::regs(); + + let width_u32 = match width { + BusWidth::One => 1u32, + BusWidth::Four => 4u32, + BusWidth::Eight => 8u32, + _ => panic!("Invalid Bus Width"), + }; + + let ker_ck = T::kernel_clk(); + let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; + + // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 + // Section 55.5.8 + let sdmmc_bus_bandwidth = new_clock.0 * width_u32; + assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); + self.clock = new_clock; + + // NOTE(unsafe) We have exclusive access to the regblock + unsafe { + // CPSMACT and DPSMACT must be 0 to set CLKDIV + Self::wait_idle(); + regs.clkcr().modify(|w| { + w.set_clkdiv(clkdiv); + #[cfg(sdmmc_v1)] + w.set_bypass(_bypass); + }); + } + + Ok(()) + } + + /// Switch mode using CMD6. + /// + /// Attempt to set a new signalling mode. The selected + /// signalling mode is returned. Expects the current clock + /// frequency to be > 12.5MHz. + async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result { + // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not + // necessary" + + let set_function = 0x8000_0000 + | match signalling { + // See PLSS v7_10 Table 4-11 + Signalling::DDR50 => 0xFF_FF04, + Signalling::SDR104 => 0xFF_1F03, + Signalling::SDR50 => 0xFF_1F02, + Signalling::SDR25 => 0xFF_FF01, + Signalling::SDR12 => 0xFF_FF00, + }; + + let mut status = [0u32; 16]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + + unsafe { + self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + } + self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = unsafe { regs.star().read() }; + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } else if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } else if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + // Host is allowed to use the new functions at least 8 + // clocks after the end of the switch command + // transaction. We know the current clock period is < 80ns, + // so a total delay of 640ns is required here + for _ in 0..300 { + cortex_m::asm::nop(); + } + + match res { + Ok(_) => { + on_drop.defuse(); + Self::stop_datapath(); + + // Function Selection of Function Group 1 + let selection = (u32::from_be(status[4]) >> 24) & 0xF; + + match selection { + 0 => Ok(Signalling::SDR12), + 1 => Ok(Signalling::SDR25), + 2 => Ok(Signalling::SDR50), + 3 => Ok(Signalling::SDR104), + 4 => Ok(Signalling::DDR50), + _ => Err(Error::UnsupportedCardType), + } + } + Err(e) => Err(e), + } + } + + /// Query the card status (CMD13, returns R1) + fn read_status(&self, card: &Card) -> Result { + let regs = T::regs(); + let rca = card.rca; + + self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 + + // NOTE(unsafe) Atomic read with no side-effects + let r1 = unsafe { regs.respr(0).read().cardstatus() }; + Ok(r1.into()) + } + + /// Reads the SD Status (ACMD13) + async fn read_sd_status(&mut self) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?; + let rca = card.rca; + + self.cmd(Cmd::set_block_length(64), false)?; // CMD16 + self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP + + let mut status = [0u32; 16]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + + unsafe { + self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + } + self.cmd(Cmd::card_status(0), true)?; + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = unsafe { regs.star().read() }; + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } else if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } else if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); + + for byte in status.iter_mut() { + *byte = u32::from_be(*byte); + } + self.card.as_mut().unwrap().status = status.into(); + } + res + } + + /// Select one card and place it into the _Tranfer State_ + /// + /// If `None` is specifed for `card`, all cards are put back into + /// _Stand-by State_ + fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { + // Determine Relative Card Address (RCA) of given card + let rca = card.map(|c| c.rca << 16).unwrap_or(0); + + let r = self.cmd(Cmd::sel_desel_card(rca), false); + match (r, rca) { + (Err(Error::Timeout), 0) => Ok(()), + _ => r, + } + } + + /// Clear flags in interrupt clear register + #[inline(always)] + fn clear_interrupt_flags() { + let regs = T::regs(); + // NOTE(unsafe) Atomic write + unsafe { + regs.icr().write(|w| { + w.set_ccrcfailc(true); + w.set_dcrcfailc(true); + w.set_ctimeoutc(true); + w.set_dtimeoutc(true); + w.set_txunderrc(true); + w.set_rxoverrc(true); + w.set_cmdrendc(true); + w.set_cmdsentc(true); + w.set_dataendc(true); + w.set_dbckendc(true); + w.set_sdioitc(true); + + #[cfg(sdmmc_v2)] + { + w.set_dholdc(true); + w.set_dabortc(true); + w.set_busyd0endc(true); + w.set_ackfailc(true); + w.set_acktimeoutc(true); + w.set_vswendc(true); + w.set_ckstopc(true); + w.set_idmatec(true); + w.set_idmabtcc(true); + } + }); + } + } + + /// Enables the interrupts for data transfer + #[inline(always)] + fn data_interrupts(enable: bool) { + let regs = T::regs(); + // NOTE(unsafe) Atomic write + unsafe { + regs.maskr().write(|w| { + w.set_dcrcfailie(enable); + w.set_dtimeoutie(enable); + w.set_dataendie(enable); + + #[cfg(sdmmc_v2)] + w.set_dabortie(enable); + }); + } + } + + async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { + // Read the the 64-bit SCR register + self.cmd(Cmd::set_block_length(8), false)?; // CMD16 + self.cmd(Cmd::app_cmd(card.rca << 16), false)?; + + let mut scr = [0u32; 2]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + + unsafe { + self.prepare_datapath_read(&mut scr[..], 8, 3); + Self::data_interrupts(true); + } + self.cmd(Cmd::cmd51(), true)?; + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = unsafe { regs.star().read() }; + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } else if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } else if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); + + unsafe { + let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); + card.scr = SCR(u64::from_be_bytes(*scr_bytes)); + } + } + res + } + + /// Send command to card + #[allow(unused_variables)] + fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { + let regs = T::regs(); + + Self::clear_interrupt_flags(); + // NOTE(safety) Atomic operations + unsafe { + // CP state machine must be idle + while Self::cmd_active() {} + + // Command arg + regs.argr().write(|w| w.set_cmdarg(cmd.arg)); + + // Command index and start CP State Machine + regs.cmdr().write(|w| { + w.set_waitint(false); + w.set_waitresp(cmd.resp as u8); + w.set_cmdindex(cmd.cmd); + w.set_cpsmen(true); + + #[cfg(sdmmc_v2)] + { + // Special mode in CP State Machine + // CMD12: Stop Transmission + let cpsm_stop_transmission = cmd.cmd == 12; + w.set_cmdstop(cpsm_stop_transmission); + w.set_cmdtrans(data); + } + }); + + let mut status; + if cmd.resp == Response::None { + // Wait for CMDSENT or a timeout + while { + status = regs.star().read(); + !(status.ctimeout() || status.cmdsent()) + } {} + } else { + // Wait for CMDREND or CCRCFAIL or a timeout + while { + status = regs.star().read(); + !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) + } {} + } + + if status.ctimeout() { + return Err(Error::Timeout); + } else if status.ccrcfail() { + return Err(Error::Crc); + } + Ok(()) + } + } + + /// # Safety + /// + /// Ensure that `regs` has exclusive access to the regblocks + unsafe fn on_drop() { + let regs = T::regs(); + if Self::data_active() { + Self::clear_interrupt_flags(); + // Send abort + // CP state machine must be idle + while Self::cmd_active() {} + + // Command arg + regs.argr().write(|w| w.set_cmdarg(0)); + + // Command index and start CP State Machine + regs.cmdr().write(|w| { + w.set_waitint(false); + w.set_waitresp(Response::Short as u8); + w.set_cmdindex(12); + w.set_cpsmen(true); + + #[cfg(sdmmc_v2)] + { + w.set_cmdstop(true); + w.set_cmdtrans(false); + } + }); + + // Wait for the abort + while Self::data_active() {} + } + Self::data_interrupts(false); + Self::clear_interrupt_flags(); + Self::stop_datapath(); + } + + /// Initializes card (if present) and sets the bus at the + /// specified frequency. pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { - let inner = T::inner(); - let freq = freq.into(); + let regs = T::regs(); + let ker_ck = T::kernel_clk(); let bus_width = match self.d3.is_some() { true => BusWidth::Four, false => BusWidth::One, }; - inner - .init_card( - freq, - bus_width, - &mut self.card, - &mut self.signalling, - T::kernel_clk(), - &mut self.clock, - T::state(), - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await - } - - #[inline(always)] - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - let card_capacity = self.card()?.card_type; - let inner = T::inner(); - let state = T::state(); - - // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; - inner - .read_block( - block_idx, - buf, - card_capacity, - state, - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await - } - - pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { - let card = self.card.as_mut().ok_or(Error::NoCard)?; - let inner = T::inner(); - let state = T::state(); - - // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; - inner - .write_block( - block_idx, - buf, - card, - state, - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await - } - - /// Get a reference to the initialized card - /// - /// # Errors - /// - /// Returns Error::NoCard if [`init_card`](#method.init_card) - /// has not previously succeeded - #[inline(always)] - pub fn card(&self) -> Result<&Card, Error> { - self.card.as_ref().ok_or(Error::NoCard) - } - - /// Get the current SDMMC bus clock - pub fn clock(&self) -> Hertz { - self.clock - } - - #[inline(always)] - fn on_interrupt(_: *mut ()) { - let regs = T::inner(); - let state = T::state(); - - regs.data_interrupts(false); - state.wake(); - } -} - -impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { - fn drop(&mut self) { - self.irq.disable(); - let inner = T::inner(); - unsafe { inner.on_drop() }; - - critical_section::with(|_| unsafe { - self.clk.set_as_disconnected(); - self.cmd.set_as_disconnected(); - self.d0.set_as_disconnected(); - if let Some(x) = &mut self.d1 { - x.set_as_disconnected(); - } - if let Some(x) = &mut self.d2 { - x.set_as_disconnected(); - } - if let Some(x) = &mut self.d3 { - x.set_as_disconnected(); - } - }); - } -} - -pub struct SdmmcInner(pub(crate) RegBlock); - -impl SdmmcInner { - /// # Safety - /// - /// Access to `regs` registers should be exclusive - unsafe fn new_inner(&self) { - let regs = self.0; - - regs.clkcr().write(|w| { - w.set_pwrsav(false); - w.set_negedge(false); - - // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. - // See chip erratas for more details. - #[cfg(sdmmc_v1)] - w.set_hwfc_en(false); - #[cfg(sdmmc_v2)] - w.set_hwfc_en(true); - - #[cfg(sdmmc_v1)] - w.set_clken(true); - }); - - // 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)); - } - - /// Initializes card (if present) and sets the bus at the - /// specified frequency. - #[allow(clippy::too_many_arguments)] - async fn init_card>( - &self, - freq: Hertz, - bus_width: BusWidth, - old_card: &mut Option, - signalling: &mut Signalling, - ker_ck: Hertz, - clock: &mut Hertz, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - let regs = self.0; - // 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 (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); - *clock = init_clock; + self.clock = init_clock; // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); + Self::wait_idle(); regs.clkcr().modify(|w| { w.set_widbus(0); @@ -712,7 +1069,7 @@ impl SdmmcInner { self.select_card(Some(&card))?; - self.get_scr(&mut card, waker_reg, data_transfer_timeout, dma).await?; + self.get_scr(&mut card).await?; // Set bus width let (width, acmd_arg) = match bus_width { @@ -724,7 +1081,7 @@ impl SdmmcInner { self.cmd(Cmd::cmd6(acmd_arg), false)?; // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); + Self::wait_idle(); regs.clkcr().modify(|w| { w.set_widbus(match width { @@ -738,25 +1095,24 @@ impl SdmmcInner { // Set Clock if freq.0 <= 25_000_000 { // Final clock frequency - self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; + self.clkcr_set_clkdiv(freq.0, width)?; } else { // Switch to max clock for SDR12 - self.clkcr_set_clkdiv(25_000_000, width, ker_ck, clock)?; + self.clkcr_set_clkdiv(25_000_000, width)?; } + self.card = Some(card); + // Read status - self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) - .await?; + self.read_sd_status().await?; if freq.0 > 25_000_000 { // Switch to SDR25 - *signalling = self - .switch_signalling_mode(Signalling::SDR25, waker_reg, data_transfer_timeout, dma) - .await?; + self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; - if *signalling == Signalling::SDR25 { + if self.signalling == Signalling::SDR25 { // Set final clock frequency - self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; + self.clkcr_set_clkdiv(freq.0, width)?; if self.read_status(&card)?.state() != CurrentState::Transfer { return Err(Error::SignalingSwitchFailed); @@ -764,42 +1120,38 @@ impl SdmmcInner { } } // Read status after signalling change - self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) - .await?; - old_card.replace(card); + self.read_sd_status().await?; } Ok(()) } - async fn read_block>( - &self, - block_idx: u32, - buffer: &mut [u32; 128], - capacity: CardCapacity, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { + #[inline(always)] + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let card_capacity = self.card()?.card_type; + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; + // Always read 1 block of 512 bytes // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match capacity { + let address = match card_capacity { CardCapacity::SDSC => block_idx * 512, _ => block_idx, }; self.cmd(Cmd::set_block_length(512), false)?; // CMD16 - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); unsafe { - self.prepare_datapath_read(buffer, 512, 9, data_transfer_timeout, dma); - self.data_interrupts(true); + self.prepare_datapath_read(buffer, 512, 9); + Self::data_interrupts(true); } self.cmd(Cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -812,24 +1164,21 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); + Self::clear_interrupt_flags(); if res.is_ok() { on_drop.defuse(); - self.stop_datapath(); + Self::stop_datapath(); } res } - async fn write_block>( - &self, - block_idx: u32, - buffer: &[u32; 128], - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { + pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?; + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; + // Always read 1 block of 512 bytes // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes let address = match card.card_type { @@ -838,23 +1187,23 @@ impl SdmmcInner { }; self.cmd(Cmd::set_block_length(512), false)?; // CMD16 - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes #[cfg(sdmmc_v1)] self.cmd(Cmd::write_single_block(address), true)?; unsafe { - self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); - self.data_interrupts(true); + self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9); + Self::data_interrupts(true); } #[cfg(sdmmc_v2)] self.cmd(Cmd::write_single_block(address), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -867,19 +1216,19 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); + Self::clear_interrupt_flags(); match res { Ok(_) => { on_drop.defuse(); - self.stop_datapath(); + Self::stop_datapath(); // TODO: Make this configurable let mut timeout: u32 = 0x00FF_FFFF; // Try to read card status (ACMD13) while timeout > 0 { - match self.read_sd_status(card, waker_reg, data_transfer_timeout, dma).await { + match self.read_sd_status().await { Ok(_) => return Ok(()), Err(Error::Timeout) => (), // Try again Err(e) => return Err(e), @@ -892,557 +1241,49 @@ impl SdmmcInner { } } - /// Data transfer is in progress - #[inline(always)] - fn data_active(&self) -> bool { - let regs = self.0; - - // NOTE(unsafe) Atomic read with no side-effects - unsafe { - let status = regs.star().read(); - #[cfg(sdmmc_v1)] - return status.rxact() || status.txact(); - #[cfg(sdmmc_v2)] - return status.dpsmact(); - } - } - - /// Coammand transfer is in progress - #[inline(always)] - fn cmd_active(&self) -> bool { - let regs = self.0; - - // NOTE(unsafe) Atomic read with no side-effects - unsafe { - let status = regs.star().read(); - #[cfg(sdmmc_v1)] - return status.cmdact(); - #[cfg(sdmmc_v2)] - return status.cpsmact(); - } - } - - /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) - #[inline(always)] - fn wait_idle(&self) { - while self.data_active() || self.cmd_active() {} - } - - /// # Safety + /// Get a reference to the initialized card /// - /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_read>( - &self, - buffer: *mut [u32], - length_bytes: u32, - block_size: u8, - data_transfer_timeout: u32, - #[allow(unused_variables)] dma: &mut Dma, - ) { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = self.0; + /// # Errors + /// + /// Returns Error::NoCard if [`init_card`](#method.init_card) + /// has not previously succeeded + #[inline(always)] + pub fn card(&self) -> Result<&Card, Error> { + self.card.as_ref().ok_or(Error::NoCard) + } - // Command AND Data state machines must be idle - self.wait_idle(); - self.clear_interrupt_flags(); + /// Get the current SDMMC bus clock + pub fn clock(&self) -> Hertz { + self.clock + } - // NOTE(unsafe) We have exclusive access to the regisers + #[inline(always)] + fn on_interrupt(_: *mut ()) { + Self::data_interrupts(false); + T::state().wake(); + } +} - regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); +impl<'d, T: Instance, Dma: SdmmcDma + 'd> Drop for Sdmmc<'d, T, Dma> { + fn drop(&mut self) { + self.irq.disable(); + unsafe { Self::on_drop() }; - #[cfg(sdmmc_v1)] - { - let request = dma.request(); - dma.start_read( - request, - regs.fifor().ptr() as *const u32, - buffer, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(true); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); + critical_section::with(|_| unsafe { + self.clk.set_as_disconnected(); + self.cmd.set_as_disconnected(); + self.d0.set_as_disconnected(); + if let Some(x) = &mut self.d1 { + x.set_as_disconnected(); + } + if let Some(x) = &mut self.d2 { + x.set_as_disconnected(); + } + if let Some(x) = &mut self.d3 { + x.set_as_disconnected(); } }); } - - /// # Safety - /// - /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_write>( - &self, - buffer: *const [u32], - length_bytes: u32, - block_size: u8, - data_transfer_timeout: u32, - #[allow(unused_variables)] dma: &mut Dma, - ) { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = self.0; - - // Command AND Data state machines must be idle - self.wait_idle(); - self.clear_interrupt_flags(); - - // NOTE(unsafe) We have exclusive access to the regisers - - regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); - - #[cfg(sdmmc_v1)] - { - let request = dma.request(); - dma.start_write( - request, - buffer, - regs.fifor().ptr() as *mut u32, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r() - .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(false); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); - } - - /// Stops the DMA datapath - fn stop_datapath(&self) { - let regs = self.0; - - unsafe { - #[cfg(sdmmc_v1)] - regs.dctrl().modify(|w| { - w.set_dmaen(false); - w.set_dten(false); - }); - #[cfg(sdmmc_v2)] - regs.idmactrlr().modify(|w| w.set_idmaen(false)); - } - } - - /// Sets the CLKDIV field in CLKCR. Updates clock field in self - fn clkcr_set_clkdiv(&self, freq: u32, width: BusWidth, ker_ck: Hertz, clock: &mut Hertz) -> Result<(), Error> { - let regs = self.0; - - let width_u32 = match width { - BusWidth::One => 1u32, - BusWidth::Four => 4u32, - BusWidth::Eight => 8u32, - _ => panic!("Invalid Bus Width"), - }; - - let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; - - // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 - // Section 55.5.8 - let sdmmc_bus_bandwidth = new_clock.0 * width_u32; - assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); - *clock = new_clock; - - // NOTE(unsafe) We have exclusive access to the regblock - unsafe { - // CPSMACT and DPSMACT must be 0 to set CLKDIV - self.wait_idle(); - regs.clkcr().modify(|w| { - w.set_clkdiv(clkdiv); - #[cfg(sdmmc_v1)] - w.set_bypass(_bypass); - }); - } - - Ok(()) - } - - /// Switch mode using CMD6. - /// - /// Attempt to set a new signalling mode. The selected - /// signalling mode is returned. Expects the current clock - /// frequency to be > 12.5MHz. - async fn switch_signalling_mode>( - &self, - signalling: Signalling, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result { - // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not - // necessary" - - let set_function = 0x8000_0000 - | match signalling { - // See PLSS v7_10 Table 4-11 - Signalling::DDR50 => 0xFF_FF04, - Signalling::SDR104 => 0xFF_1F03, - Signalling::SDR50 => 0xFF_1F02, - Signalling::SDR25 => 0xFF_FF01, - Signalling::SDR12 => 0xFF_FF00, - }; - - let mut status = [0u32; 16]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); - - unsafe { - self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); - self.data_interrupts(true); - } - self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 - - let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); - let status = unsafe { regs.star().read() }; - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - self.clear_interrupt_flags(); - - // Host is allowed to use the new functions at least 8 - // clocks after the end of the switch command - // transaction. We know the current clock period is < 80ns, - // so a total delay of 640ns is required here - for _ in 0..300 { - cortex_m::asm::nop(); - } - - match res { - Ok(_) => { - on_drop.defuse(); - self.stop_datapath(); - - // Function Selection of Function Group 1 - let selection = (u32::from_be(status[4]) >> 24) & 0xF; - - match selection { - 0 => Ok(Signalling::SDR12), - 1 => Ok(Signalling::SDR25), - 2 => Ok(Signalling::SDR50), - 3 => Ok(Signalling::SDR104), - 4 => Ok(Signalling::DDR50), - _ => Err(Error::UnsupportedCardType), - } - } - Err(e) => Err(e), - } - } - - /// Query the card status (CMD13, returns R1) - fn read_status(&self, card: &Card) -> Result { - let regs = self.0; - let rca = card.rca; - - self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 - - // NOTE(unsafe) Atomic read with no side-effects - let r1 = unsafe { regs.respr(0).read().cardstatus() }; - Ok(r1.into()) - } - - /// Reads the SD Status (ACMD13) - async fn read_sd_status>( - &self, - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - let rca = card.rca; - self.cmd(Cmd::set_block_length(64), false)?; // CMD16 - self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP - - let mut status = [0u32; 16]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); - - unsafe { - self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); - self.data_interrupts(true); - } - self.cmd(Cmd::card_status(0), true)?; - - let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); - let status = unsafe { regs.star().read() }; - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - self.clear_interrupt_flags(); - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - - for byte in status.iter_mut() { - *byte = u32::from_be(*byte); - } - card.status = status.into(); - } - res - } - - /// Select one card and place it into the _Tranfer State_ - /// - /// If `None` is specifed for `card`, all cards are put back into - /// _Stand-by State_ - fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { - // Determine Relative Card Address (RCA) of given card - let rca = card.map(|c| c.rca << 16).unwrap_or(0); - - let r = self.cmd(Cmd::sel_desel_card(rca), false); - match (r, rca) { - (Err(Error::Timeout), 0) => Ok(()), - _ => r, - } - } - - /// Clear flags in interrupt clear register - #[inline(always)] - fn clear_interrupt_flags(&self) { - let regs = self.0; - // NOTE(unsafe) Atomic write - unsafe { - regs.icr().write(|w| { - w.set_ccrcfailc(true); - w.set_dcrcfailc(true); - w.set_ctimeoutc(true); - w.set_dtimeoutc(true); - w.set_txunderrc(true); - w.set_rxoverrc(true); - w.set_cmdrendc(true); - w.set_cmdsentc(true); - w.set_dataendc(true); - w.set_dbckendc(true); - w.set_sdioitc(true); - - #[cfg(sdmmc_v2)] - { - w.set_dholdc(true); - w.set_dabortc(true); - w.set_busyd0endc(true); - w.set_ackfailc(true); - w.set_acktimeoutc(true); - w.set_vswendc(true); - w.set_ckstopc(true); - w.set_idmatec(true); - w.set_idmabtcc(true); - } - }); - } - } - - /// Enables the interrupts for data transfer - #[inline(always)] - fn data_interrupts(&self, enable: bool) { - let regs = self.0; - // NOTE(unsafe) Atomic write - unsafe { - regs.maskr().write(|w| { - w.set_dcrcfailie(enable); - w.set_dtimeoutie(enable); - w.set_dataendie(enable); - - #[cfg(sdmmc_v2)] - w.set_dabortie(enable); - }); - } - } - - async fn get_scr>( - &self, - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - // Read the the 64-bit SCR register - self.cmd(Cmd::set_block_length(8), false)?; // CMD16 - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; - - let mut scr = [0u32; 2]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); - - unsafe { - self.prepare_datapath_read(&mut scr[..], 8, 3, data_transfer_timeout, dma); - self.data_interrupts(true); - } - self.cmd(Cmd::cmd51(), true)?; - - let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); - let status = unsafe { regs.star().read() }; - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - self.clear_interrupt_flags(); - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - - unsafe { - let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); - card.scr = SCR(u64::from_be_bytes(*scr_bytes)); - } - } - res - } - - /// Send command to card - #[allow(unused_variables)] - fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { - let regs = self.0; - - self.clear_interrupt_flags(); - // NOTE(safety) Atomic operations - unsafe { - // CP state machine must be idle - while self.cmd_active() {} - - // Command arg - regs.argr().write(|w| w.set_cmdarg(cmd.arg)); - - // Command index and start CP State Machine - regs.cmdr().write(|w| { - w.set_waitint(false); - w.set_waitresp(cmd.resp as u8); - w.set_cmdindex(cmd.cmd); - w.set_cpsmen(true); - - #[cfg(sdmmc_v2)] - { - // Special mode in CP State Machine - // CMD12: Stop Transmission - let cpsm_stop_transmission = cmd.cmd == 12; - w.set_cmdstop(cpsm_stop_transmission); - w.set_cmdtrans(data); - } - }); - - let mut status; - if cmd.resp == Response::None { - // Wait for CMDSENT or a timeout - while { - status = regs.star().read(); - !(status.ctimeout() || status.cmdsent()) - } {} - } else { - // Wait for CMDREND or CCRCFAIL or a timeout - while { - status = regs.star().read(); - !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) - } {} - } - - if status.ctimeout() { - return Err(Error::Timeout); - } else if status.ccrcfail() { - return Err(Error::Crc); - } - Ok(()) - } - } - - /// # Safety - /// - /// Ensure that `regs` has exclusive access to the regblocks - unsafe fn on_drop(&self) { - let regs = self.0; - if self.data_active() { - self.clear_interrupt_flags(); - // Send abort - // CP state machine must be idle - while self.cmd_active() {} - - // Command arg - regs.argr().write(|w| w.set_cmdarg(0)); - - // Command index and start CP State Machine - regs.cmdr().write(|w| { - w.set_waitint(false); - w.set_waitresp(Response::Short as u8); - w.set_cmdindex(12); - w.set_cpsmen(true); - - #[cfg(sdmmc_v2)] - { - w.set_cmdstop(true); - w.set_cmdtrans(false); - } - }); - - // Wait for the abort - while self.data_active() {} - } - self.data_interrupts(false); - self.clear_interrupt_flags(); - self.stop_datapath(); - } } /// SD card Commands @@ -1540,7 +1381,7 @@ pub(crate) mod sealed { pub trait Instance { type Interrupt: Interrupt; - fn inner() -> SdmmcInner; + fn regs() -> RegBlock; fn state() -> &'static AtomicWaker; fn kernel_clk() -> Hertz; } @@ -1629,9 +1470,8 @@ foreach_peripheral!( impl sealed::Instance for peripherals::$inst { type Interrupt = crate::interrupt::$inst; - fn inner() -> SdmmcInner { - const INNER: SdmmcInner = SdmmcInner(crate::pac::$inst); - INNER + fn regs() -> RegBlock { + crate::pac::$inst } fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { From 82dd7a5f8c08fe9b03b9d885ad992fcab1a9b00f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 21:48:47 +0200 Subject: [PATCH 0902/1575] stm32/sdmmc: add init_card retry. --- examples/stm32f4/src/bin/sdmmc.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index ebdfdb22d..eeecbd321 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -39,7 +39,18 @@ async fn main(_spawner: Spawner) { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_card(mhz(48)).await); + let mut err = None; + loop { + match sdmmc.init_card(mhz(24)).await { + Ok(_) => break, + Err(e) => { + if err != Some(e) { + info!("waiting for card error, retrying: {:?}", e); + err = Some(e); + } + } + } + } let card = unwrap!(sdmmc.card()); From e63a34ba2167cd7637601ff2e7770dca00b1e3e1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 21:49:34 +0200 Subject: [PATCH 0903/1575] stm32/sdmmc: add hil test for f4. --- tests/stm32/Cargo.toml | 43 +++++++++- tests/stm32/gen_test.py | 44 +++++++++++ tests/stm32/src/bin/sdmmc.rs | 148 +++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 tests/stm32/gen_test.py create mode 100644 tests/stm32/src/bin/sdmmc.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 8b70a1015..3047c34ce 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [features] stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc"] # Nucleo stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo @@ -15,6 +15,8 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board +sdmmc = [] + [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } @@ -31,6 +33,45 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } +# BEGIN TESTS +# Generated by gen_test.py. DO NOT EDIT. +[[bin]] +name = "gpio" +path = "src/bin/gpio.rs" +required-features = [] + +[[bin]] +name = "sdmmc" +path = "src/bin/sdmmc.rs" +required-features = [ "sdmmc",] + +[[bin]] +name = "spi" +path = "src/bin/spi.rs" +required-features = [] + +[[bin]] +name = "spi_dma" +path = "src/bin/spi_dma.rs" +required-features = [] + +[[bin]] +name = "timer" +path = "src/bin/timer.rs" +required-features = [] + +[[bin]] +name = "usart" +path = "src/bin/usart.rs" +required-features = [] + +[[bin]] +name = "usart_dma" +path = "src/bin/usart_dma.rs" +required-features = [] + +# END TESTS + [profile.dev] debug = 2 debug-assertions = true diff --git a/tests/stm32/gen_test.py b/tests/stm32/gen_test.py new file mode 100644 index 000000000..8ff156c0e --- /dev/null +++ b/tests/stm32/gen_test.py @@ -0,0 +1,44 @@ +import os +import toml +from glob import glob + +abspath = os.path.abspath(__file__) +dname = os.path.dirname(abspath) +os.chdir(dname) + +# ======= load test list +tests = {} +for f in sorted(glob('./src/bin/*.rs')): + name = os.path.splitext(os.path.basename(f))[0] + features = [] + with open(f, 'r') as f: + for line in f: + if line.startswith('// required-features:'): + features = line.split(':', 2)[1].strip().split(',') + + tests[name] = features + +# ========= Update Cargo.toml + +things = { + 'bin': [ + { + 'name': f'{name}', + 'path': f'src/bin/{name}.rs', + 'required-features': features, + } + for name, features in tests.items() + ] +} + +SEPARATOR_START = '# BEGIN TESTS\n' +SEPARATOR_END = '# END TESTS\n' +HELP = '# Generated by gen_test.py. DO NOT EDIT.\n' +with open('Cargo.toml', 'r') as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + \ + toml.dumps(things) + SEPARATOR_END + after +with open('Cargo.toml', 'w') as f: + f.write(data) diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs new file mode 100644 index 000000000..c4e50cb4a --- /dev/null +++ b/tests/stm32/src/bin/sdmmc.rs @@ -0,0 +1,148 @@ +// required-features: sdmmc +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; +use embassy_stm32::time::mhz; +use embassy_stm32::{interrupt, Config}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.sys_ck = Some(mhz(48)); + config.rcc.pll48 = true; + let p = embassy_stm32::init(config); + + #[cfg(feature = "stm32f429zi")] + let (mut sdmmc, mut irq, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) = ( + p.SDIO, + interrupt::take!(SDIO), + p.DMA2_CH3, + p.PC12, + p.PD2, + p.PC8, + p.PC9, + p.PC10, + p.PC11, + ); + + // Arbitrary block index + let block_idx = 16; + + let mut pattern1 = DataBlock([0u8; 512]); + let mut pattern2 = DataBlock([0u8; 512]); + for i in 0..512 { + pattern1[i] = i as u8; + pattern2[i] = !i as u8; + } + + let mut block = DataBlock([0u8; 512]); + + // ======== Try 4bit. ============== + info!("initializing in 4-bit mode..."); + let mut s = Sdmmc::new_4bit( + &mut sdmmc, + &mut irq, + &mut dma, + &mut clk, + &mut cmd, + &mut d0, + &mut d1, + &mut d2, + &mut d3, + Default::default(), + ); + + let mut err = None; + loop { + match s.init_card(mhz(24)).await { + Ok(_) => break, + Err(e) => { + if err != Some(e) { + info!("waiting for card: {:?}", e); + err = Some(e); + } + } + } + } + + let card = unwrap!(s.card()); + + info!("Card: {:#?}", Debug2Format(card)); + info!("Clock: {}", s.clock()); + + info!("writing pattern1..."); + s.write_block(block_idx, &pattern1).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern1); + + info!("writing pattern2..."); + s.write_block(block_idx, &pattern2).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern2); + + drop(s); + + // ======== Try 1bit. ============== + info!("initializing in 1-bit mode..."); + let mut s = Sdmmc::new_1bit( + &mut sdmmc, + &mut irq, + &mut dma, + &mut clk, + &mut cmd, + &mut d0, + Default::default(), + ); + + let mut err = None; + loop { + match s.init_card(mhz(24)).await { + Ok(_) => break, + Err(e) => { + if err != Some(e) { + info!("waiting for card: {:?}", e); + err = Some(e); + } + } + } + } + + let card = unwrap!(s.card()); + + info!("Card: {:#?}", Debug2Format(card)); + info!("Clock: {}", s.clock()); + + info!("reading pattern2 written in 4bit mode..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern2); + + info!("writing pattern1..."); + s.write_block(block_idx, &pattern1).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern1); + + info!("writing pattern2..."); + s.write_block(block_idx, &pattern2).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern2); + + drop(s); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From f5216624bb228b5b70f97f0e7ebc40ea4d7e1a27 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Apr 2023 15:24:24 -0500 Subject: [PATCH 0904/1575] stm32/i2c: fix races when using dma. fixes #1341. --- embassy-stm32/src/i2c/v2.rs | 103 ++++++++++++++---------------------- 1 file changed, 41 insertions(+), 62 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7218f7706..44237b890 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,6 +1,5 @@ use core::cmp; use core::future::poll_fn; -use core::sync::atomic::{AtomicUsize, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; @@ -35,14 +34,12 @@ impl Default for Config { pub struct State { waker: AtomicWaker, - chunks_transferred: AtomicUsize, } impl State { pub(crate) const fn new() -> Self { Self { waker: AtomicWaker::new(), - chunks_transferred: AtomicUsize::new(0), } } } @@ -130,10 +127,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let isr = regs.isr().read(); if isr.tcr() || isr.tc() { - let state = T::state(); - let transferred = state.chunks_transferred.load(Ordering::Relaxed); - state.chunks_transferred.store(transferred + 1, Ordering::Relaxed); - state.waker.wake(); + T::state().waker.wake(); } // The flag can only be cleared by writting to nbytes, we won't do that here, so disable // the interrupt @@ -457,12 +451,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { TXDMA: crate::i2c::TxDma, { let total_len = write.len(); - let completed_chunks = total_len / 255; - let total_chunks = if completed_chunks * 255 == total_len { - completed_chunks - } else { - completed_chunks + 1 - }; let dma_transfer = unsafe { let regs = T::regs(); @@ -480,7 +468,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }; let state = T::state(); - state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; let on_drop = OnDrop::new(|| { @@ -495,33 +482,31 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } }); - // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers - if first_slice { - unsafe { - Self::master_write( - address, - total_len.min(255), - Stop::Software, - (total_chunks != 1) || !last_slice, - &check_timeout, - )?; - } - } else { - unsafe { - Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?; - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - } - poll_fn(|cx| { state.waker.register(cx.waker()); - let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); - if chunks_transferred == total_chunks { + if remaining_len == total_len { + // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers + if first_slice { + unsafe { + Self::master_write( + address, + total_len.min(255), + Stop::Software, + (total_len > 255) || !last_slice, + &check_timeout, + )?; + } + } else { + unsafe { + Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?; + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + } + } else if remaining_len == 0 { return Poll::Ready(Ok(())); - } else if chunks_transferred != 0 { - remaining_len = remaining_len.saturating_sub(255); - let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice; + } else { + let last_piece = (remaining_len <= 255) && last_slice; // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers unsafe { @@ -531,6 +516,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|w| w.set_tcie(true)); } } + + remaining_len = remaining_len.saturating_sub(255); Poll::Pending }) .await?; @@ -559,12 +546,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { RXDMA: crate::i2c::RxDma, { let total_len = buffer.len(); - let completed_chunks = total_len / 255; - let total_chunks = if completed_chunks * 255 == total_len { - completed_chunks - } else { - completed_chunks + 1 - }; let dma_transfer = unsafe { let regs = T::regs(); @@ -580,7 +561,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }; let state = T::state(); - state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; let on_drop = OnDrop::new(|| { @@ -593,27 +573,24 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } }); - // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers - unsafe { - Self::master_read( - address, - total_len.min(255), - Stop::Software, - total_chunks != 1, - restart, - &check_timeout, - )?; - } - poll_fn(|cx| { state.waker.register(cx.waker()); - let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); - - if chunks_transferred == total_chunks { + if remaining_len == total_len { + // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers + unsafe { + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_len > 255, + restart, + &check_timeout, + )?; + } + } else if remaining_len == 0 { return Poll::Ready(Ok(())); - } else if chunks_transferred != 0 { - remaining_len = remaining_len.saturating_sub(255); - let last_piece = chunks_transferred + 1 == total_chunks; + } else { + let last_piece = remaining_len <= 255; // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { @@ -623,6 +600,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|w| w.set_tcie(true)); } } + + remaining_len = remaining_len.saturating_sub(255); Poll::Pending }) .await?; From 1c68c62ebdbfdad0ad0a9cc8d7718baf6a3c94e8 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 18 Apr 2023 13:48:37 +0200 Subject: [PATCH 0905/1575] Implement embedded-storage traits for full flash struct --- embassy-stm32/src/flash/common.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 1189e447e..95fe26126 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -162,6 +162,35 @@ impl FlashRegion { } } +impl embedded_storage::nor_flash::ErrorType for Flash<'_> { + type Error = Error; +} + +impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl embedded_storage::nor_flash::NorFlash for Flash<'_> { + const WRITE_SIZE: usize = 8; + const ERASE_SIZE: usize = 2048; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } +} + foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name<'_> { From 095f5ef279b58da0e3b63559b4f1a039538673c9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 18 Apr 2023 15:49:33 +0200 Subject: [PATCH 0906/1575] Add MAX_ERASE_SIZE const in build script, and use it in flash-wide implementation of embedded-storage traits --- embassy-stm32/build.rs | 8 ++++++++ embassy-stm32/src/flash/common.rs | 6 +++--- embassy-stm32/src/flash/mod.rs | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index c7d12e13a..7959cca03 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -255,6 +255,14 @@ fn main() { ]; }); + let max_erase_size = flash_memory_regions + .iter() + .map(|region| region.settings.as_ref().unwrap().erase_size) + .max() + .unwrap(); + + g.extend(quote! { pub const MAX_ERASE_SIZE: u32 = #max_erase_size }); + g.extend(quote! { pub mod flash_regions { #flash_regions } }); // ======== diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 95fe26126..6d7f55974 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -2,7 +2,7 @@ use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; use crate::flash::FlashBank; use crate::Peripheral; @@ -179,8 +179,8 @@ impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { } impl embedded_storage::nor_flash::NorFlash for Flash<'_> { - const WRITE_SIZE: usize = 8; - const ERASE_SIZE: usize = 2048; + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = MAX_ERASE_SIZE; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { self.blocking_write(offset, bytes) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 231ff1f9e..7d5596b1f 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -7,6 +7,7 @@ mod common; pub use common::*; pub use crate::_generated::flash_regions::*; +pub use crate::_generated::MAX_ERASE_SIZE; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; #[derive(Debug)] From bba8b0ded52383e9a958e11322ac5c75d01e70de Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 18 Apr 2023 15:54:13 +0200 Subject: [PATCH 0907/1575] Missing semi-colon --- embassy-stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 7959cca03..a85d3db6e 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -261,7 +261,7 @@ fn main() { .max() .unwrap(); - g.extend(quote! { pub const MAX_ERASE_SIZE: u32 = #max_erase_size }); + g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; }); g.extend(quote! { pub mod flash_regions { #flash_regions } }); From 173c65b5430e57548cc747f0387dd001e30b1ac1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:04:54 +0200 Subject: [PATCH 0908/1575] stm32/dma: refactor. --- embassy-stm32/build.rs | 12 +- embassy-stm32/src/dcmi.rs | 13 +- embassy-stm32/src/dma/bdma.rs | 415 +++++++++++++---------- embassy-stm32/src/dma/dma.rs | 580 ++++++++++++++++---------------- embassy-stm32/src/dma/dmamux.rs | 22 +- embassy-stm32/src/dma/gpdma.rs | 414 +++++++++++++---------- embassy-stm32/src/dma/mod.rs | 317 +++-------------- embassy-stm32/src/i2c/v2.rs | 6 +- embassy-stm32/src/lib.rs | 7 +- embassy-stm32/src/qspi/mod.rs | 32 +- embassy-stm32/src/sdmmc/mod.rs | 247 +++++++------- embassy-stm32/src/spi/mod.rs | 23 +- embassy-stm32/src/traits.rs | 2 +- embassy-stm32/src/usart/mod.rs | 27 +- 14 files changed, 1025 insertions(+), 1092 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index c7d12e13a..9f84caefb 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -260,7 +260,7 @@ fn main() { // ======== // Generate DMA IRQs. - let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new(); + let mut dma_irqs: HashMap<&str, Vec<(&str, &str, &str)>> = HashMap::new(); for p in METADATA.peripherals { if let Some(r) = &p.registers { @@ -270,7 +270,10 @@ fn main() { continue; } for irq in p.interrupts { - dma_irqs.entry(irq.interrupt).or_default().push((p.name, irq.signal)); + dma_irqs + .entry(irq.interrupt) + .or_default() + .push((r.kind, p.name, irq.signal)); } } } @@ -279,13 +282,14 @@ fn main() { for (irq, channels) in dma_irqs { let irq = format_ident!("{}", irq); - let channels = channels.iter().map(|(dma, ch)| format_ident!("{}_{}", dma, ch)); + let xdma = format_ident!("{}", channels[0].0); + let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch)); g.extend(quote! { #[crate::interrupt] unsafe fn #irq () { #( - ::on_irq(); + ::on_irq(); )* } }); diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 20e1a4070..0b34553cf 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -4,6 +4,7 @@ use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::interrupt::{Interrupt, InterruptExt}; @@ -385,14 +386,11 @@ where return self.capture_giant(buffer).await; } } - async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> { - let channel = &mut self.dma; - let request = channel.request(); - let r = self.inner.regs(); let src = r.dr().ptr() as *mut u32; - let dma_read = crate::dma::read(channel, request, src, buffer); + let request = self.dma.request(); + let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) }; Self::clear_interrupt_flags(); Self::enable_irqs(); @@ -436,7 +434,9 @@ where result } - async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> { + async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> { + todo!() + /* use crate::dma::TransferOptions; let data_len = buffer.len(); @@ -542,6 +542,7 @@ where unsafe { Self::toggle(false) }; result + */ } } diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 5a7a408bb..cf1222c46 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -1,18 +1,31 @@ #![macro_use] +use core::future::Future; +use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; +use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::Priority; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{TransferOptions, Word, WordSize}; +use super::{Dir, Word, WordSize}; use crate::_generated::BDMA_CHANNEL_COUNT; -use crate::dma::Request; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac; use crate::pac::bdma::vals; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TransferOptions {} + +impl Default for TransferOptions { + fn default() -> Self { + Self {} + } +} + impl From for vals::Size { fn from(raw: WordSize) -> Self { match raw { @@ -23,6 +36,15 @@ impl From for vals::Size { } } +impl From

for vals::Dir { + fn from(raw: Dir) -> Self { + match raw { + Dir::MemoryToPeripheral => Self::FROMMEMORY, + Dir::PeripheralToMemory => Self::FROMPERIPHERAL, + } + } +} + struct State { ch_wakers: [AtomicWaker; BDMA_CHANNEL_COUNT], } @@ -55,228 +77,267 @@ foreach_dma_channel! { // BDMA1 in H7 doesn't use DMAMUX, which breaks }; ($channel_peri:ident, $dma_peri:ident, bdma, $channel_num:expr, $index:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { - - unsafe fn start_write(&mut self, _request: Request, buf: *const[W], reg_addr: *mut W, options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - #[cfg(any(bdma_v2, dmamux))] - _request, - vals::Dir::FROMMEMORY, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); + impl sealed::Channel for crate::peripherals::$channel_peri { + fn regs(&self) -> pac::bdma::Dma { + pac::$dma_peri } - - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - #[cfg(any(bdma_v2, dmamux))] - _request, - vals::Dir::FROMMEMORY, - reg_addr as *const u32, - repeated as *mut u32, - count, - false, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) + fn num(&self) -> usize { + $channel_num } - - unsafe fn start_read(&mut self, _request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts_mut(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - #[cfg(any(bdma_v2, dmamux))] - _request, - vals::Dir::FROMPERIPHERAL, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); + fn index(&self) -> usize { + $index } - - unsafe fn start_double_buffered_read( - &mut self, - _request: Request, - _reg_addr: *const W, - _buffer0: *mut W, - _buffer1: *mut W, - _buffer_len: usize, - _options: TransferOptions, - ) { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - unsafe fn set_buffer0(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - unsafe fn set_buffer1(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - unsafe fn is_buffer0_accessible(&mut self) -> bool { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - fn request_stop(&mut self){ - unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} - } - - fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} - } - fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} - } - - fn set_waker(&mut self, waker: &Waker) { - unsafe { low_level_api::set_waker($index, waker) } - } - fn on_irq() { - unsafe { - low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); - } + unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) } } } - impl crate::dma::Channel for crate::peripherals::$channel_peri {} + impl Channel for crate::peripherals::$channel_peri {} }; } -mod low_level_api { +/// Safety: Must be called with a matching set of parameters for a valid dma channel +pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index: usize) { + let isr = dma.isr().read(); + let cr = dma.ch(channel_num).cr(); + + if isr.teif(channel_num) { + panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); + } + if isr.tcif(channel_num) && cr.read().tcie() { + cr.write(|_| ()); // Disable channel interrupts with the default value. + STATE.ch_wakers[index].wake(); + } +} + +#[cfg(any(bdma_v2, dmamux))] +pub type Request = u8; +#[cfg(not(any(bdma_v2, dmamux)))] +pub type Request = (); + +#[cfg(dmamux)] +pub trait Channel: sealed::Channel + Peripheral

+ 'static + super::dmamux::MuxChannel {} +#[cfg(not(dmamux))] +pub trait Channel: sealed::Channel + Peripheral

+ 'static {} + +pub(crate) mod sealed { use super::*; - pub unsafe fn start_transfer( - dma: pac::bdma::Dma, - channel_number: u8, - #[cfg(any(bdma_v2, dmamux))] request: Request, - dir: vals::Dir, + pub trait Channel { + fn regs(&self) -> pac::bdma::Dma; + fn num(&self) -> usize; + fn index(&self) -> usize; + fn on_irq(); + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: PeripheralRef<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Self { + Self::new_read_raw(channel, request, peri_addr, buf, options) + } + + pub unsafe fn new_read_raw( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts_mut(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::PeripheralToMemory, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + request: Request, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + Self::new_write_raw(channel, request, buf, peri_addr, options) + } + + pub unsafe fn new_write_raw( + channel: impl Peripheral

+ 'a, + request: Request, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write_repeated( + channel: impl Peripheral

+ 'a, + request: Request, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + repeated as *const W as *mut u32, + count, + false, + W::bits(), + options, + ) + } + + unsafe fn new_inner( + channel: PeripheralRef<'a, C>, + _request: Request, + dir: Dir, peri_addr: *const u32, mem_addr: *mut u32, mem_len: usize, incr_mem: bool, - data_size: vals::Size, - options: TransferOptions, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, - ) { - assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!( - options.flow_ctrl == crate::dma::FlowControl::Dma, - "Peripheral flow control not supported" - ); - assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); - - let ch = dma.ch(channel_number as _); - - reset_status(dma, channel_number); - - #[cfg(dmamux)] - super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); - - #[cfg(bdma_v2)] - critical_section::with(|_| dma.cselr().modify(|w| w.set_cs(channel_number as _, request))); + data_size: WordSize, + _options: TransferOptions, + ) -> Self { + let ch = channel.regs().ch(channel.num()); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); + #[cfg(bdma_v2)] + critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request))); + + let mut this = Self { channel }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + ch.par().write_value(peri_addr as u32); ch.mar().write_value(mem_addr as u32); ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); ch.cr().write(|w| { - w.set_psize(data_size); - w.set_msize(data_size); + w.set_psize(data_size.into()); + w.set_msize(data_size.into()); if incr_mem { w.set_minc(vals::Inc::ENABLED); } else { w.set_minc(vals::Inc::DISABLED); } - w.set_dir(dir); + w.set_dir(dir.into()); w.set_teie(true); w.set_tcie(true); w.set_en(true); }); + + this } - pub unsafe fn request_stop(dma: pac::bdma::Dma, channel_number: u8) { - reset_status(dma, channel_number); - - let ch = dma.ch(channel_number as _); - - // Disable the channel and interrupts with the default value. - ch.cr().write(|_| ()); - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::SeqCst); + fn clear_irqs(&mut self) { + unsafe { + self.channel.regs().ifcr().write(|w| { + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }) + } } - pub unsafe fn is_running(dma: pac::bdma::Dma, ch: u8) -> bool { - let ch = dma.ch(ch as _); - ch.cr().read().en() + pub fn request_stop(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }) + } + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.cr().read() }.en() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. - pub unsafe fn get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.ch(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.ndtr().read().ndt() as u16 + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.ndtr().read() }.ndt() } - /// Sets the waker for the specified DMA channel - pub unsafe fn set_waker(state_number: usize, waker: &Waker) { - STATE.ch_wakers[state_number].register(waker); + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); } +} - pub unsafe fn reset_status(dma: pac::bdma::Dma, channel_number: u8) { - dma.ifcr().write(|w| { - w.set_tcif(channel_number as _, true); - w.set_teif(channel_number as _, true); - }); +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); } +} - /// Safety: Must be called with a matching set of parameters for a valid dma channel - pub unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: u8, index: u8) { - let channel_num = channel_num as usize; - let index = index as usize; +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + STATE.ch_wakers[self.channel.index()].register(cx.waker()); - let isr = dma.isr().read(); - let cr = dma.ch(channel_num).cr(); - - if isr.teif(channel_num) { - panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); - } - if isr.tcif(channel_num) && cr.read().tcie() { - cr.write(|_| ()); // Disable channel interrupts with the default value. - STATE.ch_wakers[index].wake(); + if self.is_running() { + Poll::Pending + } else { + Poll::Ready(()) } } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 59937f4b0..9052aa110 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,15 +1,44 @@ +use core::future::Future; +use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; +use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::Priority; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use pac::dma::regs; -use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize}; +use super::{Dir, Word, WordSize}; use crate::_generated::DMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; -use crate::pac::dma::{regs, vals}; +use crate::pac::dma::vals; use crate::{interrupt, pac}; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TransferOptions { + /// Peripheral burst transfer configuration + pub pburst: Burst, + /// Memory burst transfer configuration + pub mburst: Burst, + /// Flow control configuration + pub flow_ctrl: FlowControl, + /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. + pub fifo_threshold: Option, +} + +impl Default for TransferOptions { + fn default() -> Self { + Self { + pburst: Burst::Single, + mburst: Burst::Single, + flow_ctrl: FlowControl::Dma, + fifo_threshold: None, + } + } +} + impl From for vals::Size { fn from(raw: WordSize) -> Self { match raw { @@ -20,6 +49,28 @@ impl From for vals::Size { } } +impl From

for vals::Dir { + fn from(raw: Dir) -> Self { + match raw { + Dir::MemoryToPeripheral => Self::MEMORYTOPERIPHERAL, + Dir::PeripheralToMemory => Self::PERIPHERALTOMEMORY, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Burst { + /// Single transfer + Single, + /// Incremental burst of 4 beats + Incr4, + /// Incremental burst of 8 beats + Incr8, + /// Incremental burst of 16 beats + Incr16, +} + impl From for vals::Burst { fn from(burst: Burst) -> Self { match burst { @@ -31,6 +82,15 @@ impl From for vals::Burst { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FlowControl { + /// Flow control by DMA + Dma, + /// Flow control by peripheral + Peripheral, +} + impl From for vals::Pfctrl { fn from(flow: FlowControl) -> Self { match flow { @@ -40,6 +100,19 @@ impl From for vals::Pfctrl { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FifoThreshold { + /// 1/4 full FIFO + Quarter, + /// 1/2 full FIFO + Half, + /// 3/4 full FIFO + ThreeQuarters, + /// Full FIFO + Full, +} + impl From for vals::Fth { fn from(value: FifoThreshold) -> Self { match value { @@ -51,27 +124,15 @@ impl From for vals::Fth { } } -struct ChannelState { - waker: AtomicWaker, -} - -impl ChannelState { - const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } -} - struct State { - channels: [ChannelState; DMA_CHANNEL_COUNT], + ch_wakers: [AtomicWaker; DMA_CHANNEL_COUNT], } impl State { const fn new() -> Self { - const CH: ChannelState = ChannelState::new(); + const AW: AtomicWaker = AtomicWaker::new(); Self { - channels: [CH; DMA_CHANNEL_COUNT], + ch_wakers: [AW; DMA_CHANNEL_COUNT], } } } @@ -92,158 +153,183 @@ pub(crate) unsafe fn init(irq_priority: Priority) { foreach_dma_channel! { ($channel_peri:ident, $dma_peri:ident, dma, $channel_num:expr, $index:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { - unsafe fn start_write(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::MEMORYTOPERIPHERAL, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) + impl sealed::Channel for crate::peripherals::$channel_peri { + fn regs(&self) -> pac::dma::Dma { + pac::$dma_peri } - - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::MEMORYTOPERIPHERAL, - reg_addr as *const u32, - repeated as *mut u32, - count, - false, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) + fn num(&self) -> usize { + $channel_num } - - unsafe fn start_read(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts_mut(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::PERIPHERALTOMEMORY, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); + fn index(&self) -> usize { + $index } - - unsafe fn start_double_buffered_read( - &mut self, - request: Request, - reg_addr: *const W, - buffer0: *mut W, - buffer1: *mut W, - buffer_len: usize, - options: TransferOptions, - ) { - low_level_api::start_dbm_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::PERIPHERALTOMEMORY, - reg_addr as *const u32, - buffer0 as *mut u32, - buffer1 as *mut u32, - buffer_len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); - } - - unsafe fn set_buffer0(&mut self, buffer: *mut W) { - low_level_api::set_dbm_buffer0(pac::$dma_peri, $channel_num, buffer as *mut u32); - } - - unsafe fn set_buffer1(&mut self, buffer: *mut W) { - low_level_api::set_dbm_buffer1(pac::$dma_peri, $channel_num, buffer as *mut u32); - } - - unsafe fn is_buffer0_accessible(&mut self) -> bool { - low_level_api::is_buffer0_accessible(pac::$dma_peri, $channel_num) - } - - fn request_stop(&mut self) { - unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} - } - - fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} - } - - fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} - } - - fn set_waker(&mut self, waker: &Waker) { - unsafe {low_level_api::set_waker($index, waker )} - } - fn on_irq() { - unsafe { - low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); - } + unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) } } } - impl crate::dma::Channel for crate::peripherals::$channel_peri { } + + impl Channel for crate::peripherals::$channel_peri {} }; } -mod low_level_api { +/// Safety: Must be called with a matching set of parameters for a valid dma channel +pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: usize) { + let cr = dma.st(channel_num).cr(); + let isr = dma.isr(channel_num / 4).read(); + + if isr.teif(channel_num % 4) { + panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); + } + + if isr.tcif(channel_num % 4) && cr.read().tcie() { + /* acknowledge transfer complete interrupt */ + dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); + STATE.ch_wakers[index].wake(); + } +} + +#[cfg(any(dma_v2, dmamux))] +pub type Request = u8; +#[cfg(not(any(dma_v2, dmamux)))] +pub type Request = (); + +#[cfg(dmamux)] +pub trait Channel: sealed::Channel + Peripheral

+ 'static + super::dmamux::MuxChannel {} +#[cfg(not(dmamux))] +pub trait Channel: sealed::Channel + Peripheral

+ 'static {} + +pub(crate) mod sealed { use super::*; - pub unsafe fn start_transfer( - dma: pac::dma::Dma, - channel_number: u8, + pub trait Channel { + fn regs(&self) -> pac::dma::Dma; + fn num(&self) -> usize; + fn index(&self) -> usize; + fn on_irq(); + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: PeripheralRef<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, request: Request, - dir: vals::Dir, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Self { + Self::new_read_raw(channel, request, peri_addr, buf, options) + } + + pub unsafe fn new_read_raw( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts_mut(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::PeripheralToMemory, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + request: Request, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + Self::new_write_raw(channel, request, buf, peri_addr, options) + } + + pub unsafe fn new_write_raw( + channel: impl Peripheral

+ 'a, + request: Request, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write_repeated( + channel: impl Peripheral

+ 'a, + request: Request, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + repeated as *const W as *mut u32, + count, + false, + W::bits(), + options, + ) + } + + unsafe fn new_inner( + channel: PeripheralRef<'a, C>, + _request: Request, + dir: Dir, peri_addr: *const u32, mem_addr: *mut u32, mem_len: usize, incr_mem: bool, - data_size: vals::Size, + data_size: WordSize, options: TransferOptions, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, - ) { - #[cfg(dmamux)] - super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + ) -> Self { + let ch = channel.regs().st(channel.num()); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); - reset_status(dma, channel_number); + let mut this = Self { channel }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); - let ch = dma.st(channel_number as _); ch.par().write_value(peri_addr as u32); ch.m0ar().write_value(mem_addr as u32); ch.ndtr().write_value(regs::Ndtr(mem_len as _)); @@ -258,15 +344,14 @@ mod low_level_api { } }); ch.cr().write(|w| { - w.set_dir(dir); - w.set_msize(data_size); - w.set_psize(data_size); + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); w.set_pl(vals::Pl::VERYHIGH); - if incr_mem { - w.set_minc(vals::Inc::INCREMENTED); - } else { - w.set_minc(vals::Inc::FIXED); - } + w.set_minc(match incr_mem { + true => vals::Inc::INCREMENTED, + false => vals::Inc::FIXED, + }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); w.set_tcie(true); @@ -274,7 +359,7 @@ mod low_level_api { w.set_trbuff(true); #[cfg(dma_v2)] - w.set_chsel(request); + w.set_chsel(_request); w.set_pburst(options.pburst.into()); w.set_mburst(options.mburst.into()); @@ -282,159 +367,76 @@ mod low_level_api { w.set_en(true); }); + + this } - pub unsafe fn start_dbm_transfer( - dma: pac::dma::Dma, - channel_number: u8, - request: Request, - dir: vals::Dir, - peri_addr: *const u32, - mem0_addr: *mut u32, - mem1_addr: *mut u32, - mem_len: usize, - incr_mem: bool, - data_size: vals::Size, - options: TransferOptions, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, - ) { - #[cfg(dmamux)] - super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + fn clear_irqs(&mut self) { + let isrn = self.channel.num() / 4; + let isrbit = self.channel.num() % 4; - trace!( - "Starting DBM transfer with 0: 0x{:x}, 1: 0x{:x}, len: 0x{:x}", - mem0_addr as u32, - mem1_addr as u32, - mem_len - ); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::SeqCst); - - reset_status(dma, channel_number); - - let ch = dma.st(channel_number as _); - ch.par().write_value(peri_addr as u32); - ch.m0ar().write_value(mem0_addr as u32); - // configures the second buffer for DBM - ch.m1ar().write_value(mem1_addr as u32); - ch.ndtr().write_value(regs::Ndtr(mem_len as _)); - ch.cr().write(|w| { - w.set_dir(dir); - w.set_msize(data_size); - w.set_psize(data_size); - w.set_pl(vals::Pl::VERYHIGH); - if incr_mem { - w.set_minc(vals::Inc::INCREMENTED); - } else { - w.set_minc(vals::Inc::FIXED); - } - w.set_pinc(vals::Inc::FIXED); - w.set_teie(true); - w.set_tcie(true); - - #[cfg(dma_v1)] - w.set_trbuff(true); - - #[cfg(dma_v2)] - w.set_chsel(request); - - // enable double buffered mode - w.set_dbm(vals::Dbm::ENABLED); - - w.set_pburst(options.pburst.into()); - w.set_mburst(options.mburst.into()); - w.set_pfctrl(options.flow_ctrl.into()); - - w.set_en(true); - }); + unsafe { + self.channel.regs().ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }) + } } - pub unsafe fn set_dbm_buffer0(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); - // change M0AR to the new address - ch.m0ar().write_value(mem_addr as _); - } - - pub unsafe fn set_dbm_buffer1(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); - // change M1AR to the new address - ch.m1ar().write_value(mem_addr as _); - } - - pub unsafe fn is_buffer0_accessible(dma: pac::dma::Dma, channel_number: u8) -> bool { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); - // check the current target register value - ch.cr().read().ct() == vals::Ct::MEMORY1 - } - - /// Stops the DMA channel. - pub unsafe fn request_stop(dma: pac::dma::Dma, channel_number: u8) { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - ch.cr().write(|w| { - w.set_teie(true); - w.set_tcie(true); - }); - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::SeqCst); + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }) + } } - /// Gets the running status of the channel - pub unsafe fn is_running(dma: pac::dma::Dma, ch: u8) -> bool { - // get a handle on the channel itself - let ch = dma.st(ch as _); - // Get whether it's enabled (running) - ch.cr().read().en() + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().read() }.en() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. - pub unsafe fn get_remaining_transfers(dma: pac::dma::Dma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.st(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.ndtr().read().ndt() + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.ndtr().read() }.ndt() } - /// Sets the waker for the specified DMA channel - pub unsafe fn set_waker(state_number: usize, waker: &Waker) { - STATE.channels[state_number].waker.register(waker); + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); } +} - pub unsafe fn reset_status(dma: pac::dma::Dma, channel_number: u8) { - let isrn = channel_number as usize / 4; - let isrbit = channel_number as usize % 4; +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} - dma.ifcr(isrn).write(|w| { - w.set_tcif(isrbit, true); - w.set_teif(isrbit, true); - }); + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); } +} - /// Safety: Must be called with a matching set of parameters for a valid dma channel - pub unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: u8, state_index: u8) { - let channel_num = channel_num as usize; - let state_index = state_index as usize; +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + STATE.ch_wakers[self.channel.index()].register(cx.waker()); - let cr = dma.st(channel_num).cr(); - let isr = dma.isr(channel_num / 4).read(); - - if isr.teif(channel_num % 4) { - panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); - } - - if isr.tcif(channel_num % 4) && cr.read().tcie() { - /* acknowledge transfer complete interrupt */ - dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); - STATE.channels[state_index].waker.wake(); + if self.is_running() { + Poll::Pending + } else { + Poll::Ready(()) } } } diff --git a/embassy-stm32/src/dma/dmamux.rs b/embassy-stm32/src/dma/dmamux.rs index e9967e349..a8c4c5827 100644 --- a/embassy-stm32/src/dma/dmamux.rs +++ b/embassy-stm32/src/dma/dmamux.rs @@ -2,8 +2,8 @@ use crate::{pac, peripherals}; -pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_ch_num: u8, request: u8) { - let ch_mux_regs = dmamux_regs.ccr(dmamux_ch_num as _); +pub(crate) unsafe fn configure_dmamux(channel: &mut M, request: u8) { + let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num()); ch_mux_regs.write(|reg| { reg.set_nbreq(0); reg.set_dmareq_id(request); @@ -14,11 +14,11 @@ pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_c }); } -pub(crate) mod sealed { +pub(crate) mod dmamux_sealed { use super::*; pub trait MuxChannel { - const DMAMUX_CH_NUM: u8; - const DMAMUX_REGS: pac::dmamux::Dmamux; + fn mux_regs(&self) -> pac::dmamux::Dmamux; + fn mux_num(&self) -> usize; } } @@ -26,15 +26,19 @@ pub struct DMAMUX1; #[cfg(stm32h7)] pub struct DMAMUX2; -pub trait MuxChannel: sealed::MuxChannel + super::Channel { +pub trait MuxChannel: dmamux_sealed::MuxChannel { type Mux; } foreach_dma_channel! { ($channel_peri:ident, $dma_peri:ident, $version:ident, $channel_num:expr, $index:expr, {dmamux: $dmamux:ident, dmamux_channel: $dmamux_channel:expr}) => { - impl sealed::MuxChannel for peripherals::$channel_peri { - const DMAMUX_CH_NUM: u8 = $dmamux_channel; - const DMAMUX_REGS: pac::dmamux::Dmamux = pac::$dmamux; + impl dmamux_sealed::MuxChannel for peripherals::$channel_peri { + fn mux_regs(&self) -> pac::dmamux::Dmamux { + pac::$dmamux + } + fn mux_num(&self) -> usize { + $dmamux_channel + } } impl MuxChannel for peripherals::$channel_peri { type Mux = $dmamux; diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 6f26fd194..5c6676a5f 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -1,13 +1,30 @@ -use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; +#![macro_use] +use core::future::Future; +use core::pin::Pin; +use core::sync::atomic::{fence, Ordering}; +use core::task::{Context, Poll}; + +use embassy_cortex_m::interrupt::Priority; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{Request, TransferOptions, Word, WordSize}; +use super::{Dir, Word, WordSize}; use crate::_generated::GPDMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; -use crate::pac::gpdma::{vals, Gpdma}; -use crate::{interrupt, pac}; +use crate::pac; +use crate::pac::gpdma::vals; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TransferOptions {} + +impl Default for TransferOptions { + fn default() -> Self { + Self {} + } +} impl From for vals::ChTr1Dw { fn from(raw: WordSize) -> Self { @@ -19,27 +36,15 @@ impl From for vals::ChTr1Dw { } } -struct ChannelState { - waker: AtomicWaker, -} - -impl ChannelState { - const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } -} - struct State { - channels: [ChannelState; GPDMA_CHANNEL_COUNT], + ch_wakers: [AtomicWaker; GPDMA_CHANNEL_COUNT], } impl State { const fn new() -> Self { - const CH: ChannelState = ChannelState::new(); + const AW: AtomicWaker = AtomicWaker::new(); Self { - channels: [CH; GPDMA_CHANNEL_COUNT], + ch_wakers: [AW; GPDMA_CHANNEL_COUNT], } } } @@ -47,10 +52,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, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { - interrupt::$irq::steal().enable(); + let irq = crate::interrupt::$irq::steal(); + irq.set_priority(irq_priority); + irq.enable(); }; } crate::_generated::init_gpdma(); @@ -58,117 +65,171 @@ pub(crate) unsafe fn init() { foreach_dma_channel! { ($channel_peri:ident, $dma_peri:ident, gpdma, $channel_num:expr, $index:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { - unsafe fn start_write(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - low_level_api::Dir::MemoryToPeripheral, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - W::bits(), - options, - ) + impl sealed::Channel for crate::peripherals::$channel_peri { + fn regs(&self) -> pac::gpdma::Gpdma { + pac::$dma_peri } - - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - low_level_api::Dir::MemoryToPeripheral, - reg_addr as *const u32, - repeated as *mut u32, - count, - false, - W::bits(), - options, - ) + fn num(&self) -> usize { + $channel_num } - - unsafe fn start_read(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts_mut(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - low_level_api::Dir::PeripheralToMemory, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - W::bits(), - options, - ); + fn index(&self) -> usize { + $index } - - unsafe fn start_double_buffered_read( - &mut self, - _request: Request, - _reg_addr: *const W, - _buffer0: *mut W, - _buffer1: *mut W, - _buffer_len: usize, - _options: TransferOptions, - ) { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - unsafe fn set_buffer0(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - unsafe fn set_buffer1(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - unsafe fn is_buffer0_accessible(&mut self) -> bool { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - fn request_stop(&mut self) { - unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} - } - - fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} - } - - fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} - } - - fn set_waker(&mut self, waker: &Waker) { - unsafe {low_level_api::set_waker($index, waker )} - } - fn on_irq() { - unsafe { - low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); - } + unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) } } } - impl crate::dma::Channel for crate::peripherals::$channel_peri { } + + impl Channel for crate::peripherals::$channel_peri {} }; } -mod low_level_api { - use super::*; +/// Safety: Must be called with a matching set of parameters for a valid dma channel +pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, index: usize) { + let ch = dma.ch(channel_num); + let sr = ch.sr().read(); - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum Dir { - MemoryToPeripheral, - PeripheralToMemory, + if sr.dtef() { + panic!( + "DMA: data transfer error on DMA@{:08x} channel {}", + dma.0 as u32, channel_num + ); + } + if sr.usef() { + panic!( + "DMA: user settings error on DMA@{:08x} channel {}", + dma.0 as u32, channel_num + ); } - pub unsafe fn start_transfer( - dma: Gpdma, - channel_number: u8, + if sr.suspf() || sr.tcf() { + // disable all xxIEs to prevent the irq from firing again. + ch.cr().write(|_| {}); + + // Wake the future. It'll look at tcf and see it's set. + STATE.ch_wakers[index].wake(); + } +} + +pub type Request = u8; + +#[cfg(dmamux)] +pub trait Channel: sealed::Channel + Peripheral

+ 'static + super::dmamux::MuxChannel {} +#[cfg(not(dmamux))] +pub trait Channel: sealed::Channel + Peripheral

+ 'static {} + +pub(crate) mod sealed { + use super::*; + + pub trait Channel { + fn regs(&self) -> pac::gpdma::Gpdma; + fn num(&self) -> usize; + fn index(&self) -> usize; + fn on_irq(); + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: PeripheralRef<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Self { + Self::new_read_raw(channel, request, peri_addr, buf, options) + } + + pub unsafe fn new_read_raw( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts_mut(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::PeripheralToMemory, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + request: Request, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + Self::new_write_raw(channel, request, buf, peri_addr, options) + } + + pub unsafe fn new_write_raw( + channel: impl Peripheral

+ 'a, + request: Request, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write_repeated( + channel: impl Peripheral

+ 'a, + request: Request, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + repeated as *const W as *mut u32, + count, + false, + W::bits(), + options, + ) + } + + unsafe fn new_inner( + channel: PeripheralRef<'a, C>, request: Request, dir: Dir, peri_addr: *const u32, @@ -176,24 +237,19 @@ mod low_level_api { mem_len: usize, incr_mem: bool, data_size: WordSize, - options: TransferOptions, - ) { - assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!( - options.flow_ctrl == crate::dma::FlowControl::Dma, - "Peripheral flow control not supported" - ); - assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); + _options: TransferOptions, + ) -> Self { + let ch = channel.regs().ch(channel.num()); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); - let ch = dma.ch(channel_number as _); + let this = Self { channel }; + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, request); - // Reset ch ch.cr().write(|w| w.set_reset(true)); - ch.llr().write(|_| {}); // no linked list ch.tr1().write(|w| { w.set_sdw(data_size.into()); @@ -234,72 +290,66 @@ mod low_level_api { // Start it w.set_en(true); }); + + this } - /// Stops the DMA channel. - pub unsafe fn request_stop(dma: Gpdma, channel_number: u8) { - // get a handle on the channel itself - let ch = dma.ch(channel_number as _); + pub fn request_stop(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - ch.cr().write(|w| { - w.set_tcie(true); - w.set_useie(true); - w.set_dteie(true); - w.set_suspie(true); - }); - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::SeqCst); + unsafe { + ch.cr().write(|w| { + w.set_tcie(true); + w.set_useie(true); + w.set_dteie(true); + w.set_suspie(true); + }) + } } - /// Gets the running status of the channel - pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool { - let ch = dma.ch(ch as _); - !ch.sr().read().tcf() + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + !unsafe { ch.sr().read() }.tcf() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. - pub unsafe fn get_remaining_transfers(dma: Gpdma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.ch(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.br1().read().bndt() + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.br1().read() }.bndt() } - /// Sets the waker for the specified DMA channel - pub unsafe fn set_waker(state_number: usize, waker: &Waker) { - STATE.channels[state_number].waker.register(waker); + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); } +} - /// Safety: Must be called with a matching set of parameters for a valid dma channel - pub unsafe fn on_irq_inner(dma: Gpdma, channel_num: u8, state_index: u8) { - let channel_num = channel_num as usize; - let state_index = state_index as usize; +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} - let ch = dma.ch(channel_num); - let sr = ch.sr().read(); + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} - if sr.dtef() { - panic!( - "DMA: data transfer error on DMA@{:08x} channel {}", - dma.0 as u32, channel_num - ); - } - if sr.usef() { - panic!( - "DMA: user settings error on DMA@{:08x} channel {}", - dma.0 as u32, channel_num - ); - } +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + STATE.ch_wakers[self.channel.index()].register(cx.waker()); - if sr.suspf() || sr.tcf() { - // disable all xxIEs to prevent the irq from firing again. - ch.cr().write(|_| {}); - - // Wake the future. It'll look at tcf and see it's set. - STATE.channels[state_index].waker.wake(); + if self.is_running() { + Poll::Pending + } else { + Poll::Ready(()) } } } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 0030bd575..d29ef4a1f 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -1,124 +1,39 @@ -#[cfg(bdma)] -pub(crate) mod bdma; #[cfg(dma)] pub(crate) mod dma; +#[cfg(dma)] +pub use dma::*; + +// stm32h7 has both dma and bdma. In that case, we export dma as "main" dma, +// and bdma as "secondary", under `embassy_stm32::dma::bdma`. +#[cfg(all(bdma, dma))] +pub mod bdma; + +#[cfg(all(bdma, not(dma)))] +pub(crate) mod bdma; +#[cfg(all(bdma, not(dma)))] +pub use bdma::*; + +#[cfg(gpdma)] +pub(crate) mod gpdma; +#[cfg(gpdma)] +pub use gpdma::*; + #[cfg(dmamux)] mod dmamux; -#[cfg(gpdma)] -mod gpdma; -use core::future::Future; 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}; +use embassy_hal_common::impl_peripheral; #[cfg(dmamux)] pub use self::dmamux::*; -use crate::Peripheral; -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::transfers::*; -} - -pub(crate) use transfers::*; - -#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))] -pub type Request = u8; -#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))] -pub type Request = (); - -pub(crate) mod sealed { - use super::*; - - pub trait Word {} - - pub trait Channel { - /// Starts this channel for writing a stream of words. - /// - /// Safety: - /// - `buf` must point to a valid buffer for DMA reading. - /// - `buf` must be alive for the entire duration of the DMA transfer. - /// - `reg_addr` must be a valid peripheral register address to write to. - unsafe fn start_write( - &mut self, - request: Request, - buf: *const [W], - reg_addr: *mut W, - options: TransferOptions, - ); - - /// Starts this channel for writing a word repeatedly. - /// - /// Safety: - /// - `reg_addr` must be a valid peripheral register address to write to. - unsafe fn start_write_repeated( - &mut self, - request: Request, - repeated: *const W, - count: usize, - reg_addr: *mut W, - options: TransferOptions, - ); - - /// Starts this channel for reading a stream of words. - /// - /// Safety: - /// - `buf` must point to a valid buffer for DMA writing. - /// - `buf` must be alive for the entire duration of the DMA transfer. - /// - `reg_addr` must be a valid peripheral register address to read from. - unsafe fn start_read( - &mut self, - request: Request, - reg_addr: *const W, - buf: *mut [W], - options: TransferOptions, - ); - - /// DMA double-buffered mode is unsafe as UB can happen when the hardware writes to a buffer currently owned by the software - /// more information can be found here: https://github.com/embassy-rs/embassy/issues/702 - /// This feature is now used solely for the purposes of implementing giant DMA transfers required for DCMI - unsafe fn start_double_buffered_read( - &mut self, - request: Request, - reg_addr: *const W, - buffer0: *mut W, - buffer1: *mut W, - buffer_len: usize, - options: TransferOptions, - ); - - unsafe fn set_buffer0(&mut self, buffer: *mut W); - - unsafe fn set_buffer1(&mut self, buffer: *mut W); - - unsafe fn is_buffer0_accessible(&mut self) -> bool; - - /// Requests the channel to stop. - /// NOTE: The channel does not immediately stop, you have to wait - /// for `is_running() = false`. - fn request_stop(&mut self); - - /// Returns whether this channel is running or stopped. - /// - /// The channel stops running when it either completes or is manually stopped. - fn is_running(&self) -> bool; - - /// Returns the total number of remaining transfers. - fn remaining_transfers(&mut self) -> u16; - - /// Sets the waker that is called when this channel stops (either completed or manually stopped) - fn set_waker(&mut self, waker: &Waker); - - /// This is called when this channel triggers an interrupt. - /// Note: Because some channels share an interrupt, this function might be - /// called for a channel that didn't trigger an interrupt. - fn on_irq(); - } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum Dir { + MemoryToPeripheral, + PeripheralToMemory, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -139,191 +54,39 @@ impl WordSize { } } -pub trait Word: sealed::Word { +mod word_sealed { + pub trait Word {} +} + +pub trait Word: word_sealed::Word { fn bits() -> WordSize; } -impl sealed::Word for u8 {} +impl word_sealed::Word for u8 {} impl Word for u8 { fn bits() -> WordSize { WordSize::OneByte } } -impl sealed::Word for u16 {} +impl word_sealed::Word for u16 {} impl Word for u16 { fn bits() -> WordSize { WordSize::TwoBytes } } -impl sealed::Word for u32 {} +impl word_sealed::Word for u32 {} impl Word for u32 { fn bits() -> WordSize { WordSize::FourBytes } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Burst { - /// Single transfer - Single, - /// Incremental burst of 4 beats - Incr4, - /// Incremental burst of 8 beats - Incr8, - /// Incremental burst of 16 beats - Incr16, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FlowControl { - /// Flow control by DMA - Dma, - /// Flow control by peripheral - Peripheral, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FifoThreshold { - /// 1/4 full FIFO - Quarter, - /// 1/2 full FIFO - Half, - /// 3/4 full FIFO - ThreeQuarters, - /// Full FIFO - Full, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TransferOptions { - /// Peripheral burst transfer configuration - pub pburst: Burst, - /// Memory burst transfer configuration - pub mburst: Burst, - /// Flow control configuration - pub flow_ctrl: FlowControl, - /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. - pub fifo_threshold: Option, -} - -impl Default for TransferOptions { - fn default() -> Self { - Self { - pburst: Burst::Single, - mburst: Burst::Single, - flow_ctrl: FlowControl::Dma, - fifo_threshold: None, - } - } -} - -mod transfers { - use embassy_hal_common::PeripheralRef; - - use super::*; - - #[allow(unused)] - pub fn read<'a, W: Word>( - channel: impl Peripheral

+ 'a, - request: Request, - reg_addr: *mut W, - buf: &'a mut [W], - ) -> impl Future + 'a { - assert!(buf.len() > 0 && buf.len() <= 0xFFFF); - into_ref!(channel); - - unsafe { channel.start_read::(request, reg_addr, buf, Default::default()) }; - - Transfer::new(channel) - } - - #[allow(unused)] - pub fn write<'a, W: Word>( - channel: impl Peripheral

+ 'a, - request: Request, - buf: &'a [W], - reg_addr: *mut W, - ) -> impl Future + 'a { - assert!(buf.len() > 0 && buf.len() <= 0xFFFF); - into_ref!(channel); - - unsafe { channel.start_write::(request, buf, reg_addr, Default::default()) }; - - Transfer::new(channel) - } - - #[allow(unused)] - pub fn write_repeated<'a, W: Word>( - channel: impl Peripheral

+ 'a, - request: Request, - repeated: *const W, - count: usize, - reg_addr: *mut W, - ) -> impl Future + 'a { - into_ref!(channel); - - unsafe { channel.start_write_repeated::(request, repeated, count, reg_addr, Default::default()) }; - - Transfer::new(channel) - } - - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct Transfer<'a, C: Channel> { - channel: PeripheralRef<'a, C>, - } - - impl<'a, C: Channel> Transfer<'a, C> { - pub(crate) fn new(channel: impl Peripheral

+ 'a) -> Self { - into_ref!(channel); - Self { channel } - } - } - - impl<'a, C: Channel> Drop for Transfer<'a, C> { - fn drop(&mut self) { - self.channel.request_stop(); - while self.channel.is_running() {} - } - } - - impl<'a, C: Channel> Unpin for Transfer<'a, C> {} - impl<'a, C: Channel> Future for Transfer<'a, C> { - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.channel.set_waker(cx.waker()); - if self.channel.is_running() { - Poll::Pending - } else { - Poll::Ready(()) - } - } - } -} - -pub trait Channel: sealed::Channel + Peripheral

+ 'static {} - pub struct NoDma; impl_peripheral!(NoDma); -// safety: must be called only once at startup -pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) { - #[cfg(bdma)] - bdma::init(bdma_priority); - #[cfg(dma)] - dma::init(dma_priority); - #[cfg(dmamux)] - dmamux::init(); - #[cfg(gpdma)] - gpdma::init(); -} - // TODO: replace transmutes with core::ptr::metadata once it's stable #[allow(unused)] pub(crate) fn slice_ptr_parts(slice: *const [T]) -> (usize, usize) { @@ -334,3 +97,19 @@ pub(crate) fn slice_ptr_parts(slice: *const [T]) -> (usize, usize) { pub(crate) fn slice_ptr_parts_mut(slice: *mut [T]) -> (usize, usize) { unsafe { mem::transmute(slice) } } + +// safety: must be called only once at startup +pub(crate) unsafe fn init( + #[cfg(bdma)] bdma_priority: Priority, + #[cfg(dma)] dma_priority: Priority, + #[cfg(gpdma)] gpdma_priority: Priority, +) { + #[cfg(bdma)] + bdma::init(bdma_priority); + #[cfg(dma)] + dma::init(dma_priority); + #[cfg(gpdma)] + gpdma::init(gpdma_priority); + #[cfg(dmamux)] + dmamux::init(); +} diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7218f7706..39e6702e5 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -8,7 +8,7 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use crate::dma::NoDma; +use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; @@ -476,7 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let ch = &mut self.tx_dma; let request = ch.request(); - crate::dma::write(ch, request, write, dst) + Transfer::new_write(ch, request, write, dst, Default::default()) }; let state = T::state(); @@ -576,7 +576,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let ch = &mut self.rx_dma; let request = ch.request(); - crate::dma::read(ch, request, src, buffer) + Transfer::new_read(ch, request, src, buffer, Default::default()) }; let state = T::state(); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0dbc9e5c8..bbde2da57 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -78,7 +78,6 @@ 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}; @@ -96,6 +95,8 @@ pub struct Config { pub bdma_interrupt_priority: Priority, #[cfg(dma)] pub dma_interrupt_priority: Priority, + #[cfg(gpdma)] + pub gpdma_interrupt_priority: Priority, } impl Default for Config { @@ -108,6 +109,8 @@ impl Default for Config { bdma_interrupt_priority: Priority::P0, #[cfg(dma)] dma_interrupt_priority: Priority::P0, + #[cfg(gpdma)] + gpdma_interrupt_priority: Priority::P0, } } } @@ -151,6 +154,8 @@ pub fn init(config: Config) -> Peripherals { config.bdma_interrupt_priority, #[cfg(dma)] config.dma_interrupt_priority, + #[cfg(gpdma)] + config.gpdma_interrupt_priority, ); #[cfg(feature = "exti")] exti::init(); diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index f33319620..c3126b37f 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -5,7 +5,7 @@ pub mod enums; use embassy_hal_common::{into_ref, PeripheralRef}; use enums::*; -use crate::dma::TransferOptions; +use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::AnyPin; use crate::pac::quadspi::Quadspi as Regs; @@ -230,9 +230,6 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { unsafe { self.setup_transaction(QspiMode::IndirectWrite, &transaction); - let request = self.dma.request(); - let options = TransferOptions::default(); - T::REGS.ccr().modify(|v| { v.set_fmode(QspiMode::IndirectRead.into()); }); @@ -241,12 +238,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { v.set_address(current_ar); }); - self.dma - .start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options); + let request = self.dma.request(); + let transfer = Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().ptr() as *mut u8, + buf, + Default::default(), + ); T::REGS.cr().modify(|v| v.set_dmaen(true)); - while self.dma.is_running() {} + transfer.blocking_wait(); } } @@ -257,19 +260,22 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { unsafe { self.setup_transaction(QspiMode::IndirectWrite, &transaction); - let request = self.dma.request(); - let options = TransferOptions::default(); - T::REGS.ccr().modify(|v| { v.set_fmode(QspiMode::IndirectWrite.into()); }); - self.dma - .start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options); + let request = self.dma.request(); + let transfer = Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().ptr() as *mut u8, + Default::default(), + ); T::REGS.cr().modify(|v| v.set_dmaen(true)); - while self.dma.is_running() {} + transfer.blocking_wait(); } } diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 23ece3a2a..433f73d79 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -185,6 +185,21 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { } } +#[cfg(sdmmc_v1)] +type Transfer<'a, C> = crate::dma::Transfer<'a, C>; +#[cfg(sdmmc_v2)] +type Transfer<'a, C> = core::marker::PhantomData<&'a mut C>; + +#[cfg(all(sdmmc_v1, dma))] +const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { + pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, + flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), +}; +#[cfg(all(sdmmc_v1, not(dma)))] +const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {}; + /// SDMMC configuration /// /// Default values: @@ -490,7 +505,12 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_read(&mut self, buffer: *mut [u32], length_bytes: u32, block_size: u8) { + fn prepare_datapath_read<'a>( + &'a mut self, + buffer: &'a mut [u32], + length_bytes: u32, + block_size: u8, + ) -> Transfer<'a, Dma> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); let regs = T::regs(); @@ -499,48 +519,52 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::clear_interrupt_flags(); // NOTE(unsafe) We have exclusive access to the regisers + unsafe { + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); - regs.dtimer() - .write(|w| w.set_datatime(self.config.data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); - - #[cfg(sdmmc_v1)] - { - let request = self.dma.request(); - self.dma.start_read( - request, - regs.fifor().ptr() as *const u32, - buffer, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(true); #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); + let transfer = { + let request = self.dma.request(); + Transfer::new_read( + &mut self.dma, + request, + regs.fifor().ptr() as *mut u32, + buffer, + DMA_TRANSFER_OPTIONS, + ) + }; + #[cfg(sdmmc_v2)] + let transfer = { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + core::marker::PhantomData + }; + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(true); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + + transfer + } } /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_write(&mut self, buffer: *const [u32], length_bytes: u32, block_size: u8) { + fn prepare_datapath_write<'a>( + &'a mut self, + buffer: &'a [u32], + length_bytes: u32, + block_size: u8, + ) -> Transfer<'a, Dma> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); let regs = T::regs(); @@ -549,43 +573,41 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::clear_interrupt_flags(); // NOTE(unsafe) We have exclusive access to the regisers + unsafe { + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); - regs.dtimer() - .write(|w| w.set_datatime(self.config.data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); - - #[cfg(sdmmc_v1)] - { - let request = self.dma.request(); - self.dma.start_write( - request, - buffer, - regs.fifor().ptr() as *mut u32, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r() - .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(false); #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); + let transfer = { + let request = self.dma.request(); + Transfer::new_write( + &mut self.dma, + request, + buffer, + regs.fifor().ptr() as *mut u32, + DMA_TRANSFER_OPTIONS, + ) + }; + #[cfg(sdmmc_v2)] + let transfer = { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + core::marker::PhantomData + }; + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(false); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + + transfer + } } /// Stops the DMA datapath @@ -662,11 +684,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(&mut status, 64, 6); - Self::data_interrupts(true); - } - self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 + let transfer = self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + Self::cmd(Cmd::cmd6(set_function), true)?; // CMD6 let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -696,6 +716,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Ok(_) => { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); // Function Selection of Function Group 1 let selection = (u32::from_be(status[4]) >> 24) & 0xF; @@ -718,7 +739,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let rca = card.rca; - self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 + Self::cmd(Cmd::card_status(rca << 16), false)?; // CMD13 // NOTE(unsafe) Atomic read with no side-effects let r1 = unsafe { regs.respr(0).read().cardstatus() }; @@ -730,8 +751,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let card = self.card.as_mut().ok_or(Error::NoCard)?; let rca = card.rca; - self.cmd(Cmd::set_block_length(64), false)?; // CMD16 - self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP + Self::cmd(Cmd::set_block_length(64), false)?; // CMD16 + Self::cmd(Cmd::app_cmd(rca << 16), false)?; // APP let mut status = [0u32; 16]; @@ -739,11 +760,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(&mut status, 64, 6); - Self::data_interrupts(true); - } - self.cmd(Cmd::card_status(0), true)?; + let transfer = self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + Self::cmd(Cmd::card_status(0), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -764,6 +783,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if res.is_ok() { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); for byte in status.iter_mut() { *byte = u32::from_be(*byte); @@ -781,7 +801,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { // Determine Relative Card Address (RCA) of given card let rca = card.map(|c| c.rca << 16).unwrap_or(0); - let r = self.cmd(Cmd::sel_desel_card(rca), false); + let r = Self::cmd(Cmd::sel_desel_card(rca), false); match (r, rca) { (Err(Error::Timeout), 0) => Ok(()), _ => r, @@ -842,8 +862,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { // Read the the 64-bit SCR register - self.cmd(Cmd::set_block_length(8), false)?; // CMD16 - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; + Self::cmd(Cmd::set_block_length(8), false)?; // CMD16 + Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; let mut scr = [0u32; 2]; @@ -851,11 +871,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(&mut scr[..], 8, 3); - Self::data_interrupts(true); - } - self.cmd(Cmd::cmd51(), true)?; + let transfer = self.prepare_datapath_read(&mut scr[..], 8, 3); + Self::data_interrupts(true); + Self::cmd(Cmd::cmd51(), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -876,6 +894,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if res.is_ok() { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); unsafe { let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); @@ -887,7 +906,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { /// Send command to card #[allow(unused_variables)] - fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { + fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> { let regs = T::regs(); Self::clear_interrupt_flags(); @@ -1005,10 +1024,10 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { }); regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); - self.cmd(Cmd::idle(), false)?; + Self::cmd(Cmd::idle(), false)?; // Check if cards supports CMD8 (with pattern) - self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; + Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; let r1 = regs.respr(0).read().cardstatus(); let mut card = if r1 == 0x1AA { @@ -1020,14 +1039,14 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let ocr = loop { // Signal that next command is a app command - self.cmd(Cmd::app_cmd(0), false)?; // CMD55 + Self::cmd(Cmd::app_cmd(0), false)?; // CMD55 let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 | CmdAppOper::HIGH_CAPACITY as u32 | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; // Initialize card - match self.cmd(Cmd::app_op_cmd(arg), false) { + match Self::cmd(Cmd::app_op_cmd(arg), false) { // ACMD41 Ok(_) => (), Err(Error::Crc) => (), @@ -1048,7 +1067,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { } card.ocr = ocr; - self.cmd(Cmd::all_send_cid(), false)?; // CMD2 + Self::cmd(Cmd::all_send_cid(), false)?; // CMD2 let cid0 = regs.respr(0).read().cardstatus() as u128; let cid1 = regs.respr(1).read().cardstatus() as u128; let cid2 = regs.respr(2).read().cardstatus() as u128; @@ -1056,10 +1075,10 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); card.cid = cid.into(); - self.cmd(Cmd::send_rel_addr(), false)?; + Self::cmd(Cmd::send_rel_addr(), false)?; card.rca = regs.respr(0).read().cardstatus() >> 16; - self.cmd(Cmd::send_csd(card.rca << 16), false)?; + Self::cmd(Cmd::send_csd(card.rca << 16), false)?; let csd0 = regs.respr(0).read().cardstatus() as u128; let csd1 = regs.respr(1).read().cardstatus() as u128; let csd2 = regs.respr(2).read().cardstatus() as u128; @@ -1077,8 +1096,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), _ => (BusWidth::One, 0), }; - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; - self.cmd(Cmd::cmd6(acmd_arg), false)?; + Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; + Self::cmd(Cmd::cmd6(acmd_arg), false)?; // CPSMACT and DPSMACT must be 0 to set WIDBUS Self::wait_idle(); @@ -1139,16 +1158,14 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { CardCapacity::SDSC => block_idx * 512, _ => block_idx, }; - self.cmd(Cmd::set_block_length(512), false)?; // CMD16 + Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(buffer, 512, 9); - Self::data_interrupts(true); - } - self.cmd(Cmd::read_single_block(address), true)?; + let transfer = self.prepare_datapath_read(buffer, 512, 9); + Self::data_interrupts(true); + Self::cmd(Cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1169,6 +1186,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if res.is_ok() { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); } res } @@ -1185,22 +1203,20 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { CardCapacity::SDSC => block_idx * 512, _ => block_idx, }; - self.cmd(Cmd::set_block_length(512), false)?; // CMD16 + Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes #[cfg(sdmmc_v1)] - self.cmd(Cmd::write_single_block(address), true)?; + Self::cmd(Cmd::write_single_block(address), true)?; - unsafe { - self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9); - Self::data_interrupts(true); - } + let transfer = self.prepare_datapath_write(buffer, 512, 9); + Self::data_interrupts(true); #[cfg(sdmmc_v2)] - self.cmd(Cmd::write_single_block(address), true)?; + Self::cmd(Cmd::write_single_block(address), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1222,6 +1238,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Ok(_) => { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); // TODO: Make this configurable let mut timeout: u32 = 0x00FF_FFFF; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 481ea4abc..7858cb3e8 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -421,8 +421,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - unsafe { self.txdma.start_write(tx_request, data, tx_dst, Default::default()) } - let tx_f = Transfer::new(&mut self.txdma); + let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; unsafe { set_txdmaen(T::REGS, true); @@ -468,13 +467,21 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::REGS.rx_ptr(); - unsafe { self.rxdma.start_read(rx_request, rx_src, data, Default::default()) }; - let rx_f = Transfer::new(&mut self.rxdma); + let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) }; let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); let clock_byte = 0x00u8; - let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst); + let tx_f = unsafe { + Transfer::new_write_repeated( + &mut self.txdma, + tx_request, + &clock_byte, + clock_byte_count, + tx_dst, + Default::default(), + ) + }; unsafe { set_txdmaen(T::REGS, true); @@ -521,13 +528,11 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::REGS.rx_ptr(); - unsafe { self.rxdma.start_read(rx_request, rx_src, read, Default::default()) }; - let rx_f = Transfer::new(&mut self.rxdma); + let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) }; let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - unsafe { self.txdma.start_write(tx_request, write, tx_dst, Default::default()) } - let tx_f = Transfer::new(&mut self.txdma); + let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) }; unsafe { set_txdmaen(T::REGS, true); diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/traits.rs index 45cc4e725..ffce7bd42 100644 --- a/embassy-stm32/src/traits.rs +++ b/embassy-stm32/src/traits.rs @@ -34,7 +34,7 @@ macro_rules! dma_trait_impl { (crate::$mod:ident::$trait:ident, $instance:ident, {dmamux: $dmamux:ident}, $request:expr) => { impl crate::$mod::$trait for T where - T: crate::dma::MuxChannel, + T: crate::dma::Channel + crate::dma::MuxChannel, { fn request(&self) -> crate::dma::Request { $request diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8bbba305b..b8656b586 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -6,11 +6,11 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_cortex_m::interrupt::InterruptExt; -use embassy_futures::select::{select, Either}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use futures::future::{select, Either}; -use crate::dma::NoDma; +use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; #[cfg(any(lpuart_v1, lpuart_v2))] use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; @@ -91,7 +91,7 @@ enum ReadCompletionEvent { // DMA Read transfer completed first DmaCompleted, // Idle line detected first - Idle, + Idle(usize), } pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { @@ -183,7 +183,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - let transfer = crate::dma::write(ch, request, buffer, tdr(T::regs())); + let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) }; transfer.await; Ok(()) } @@ -430,10 +430,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let ch = &mut self.rx_dma; let request = ch.request(); + let buffer_len = buffer.len(); + // Start USART DMA // will not do anything yet because DMAR is not yet set // future which will complete when DMA Read request completes - let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); + let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) }; // SAFETY: The only way we might have a problem is using split rx and tx // here we only modify or read Rx related flags, interrupts and DMA channel @@ -565,13 +567,15 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // when transfer is dropped, it will stop the DMA request let r = match select(transfer, idle).await { // DMA transfer completed first - Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), + Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted), // Idle line detected first - Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle), + Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle( + buffer_len - transfer.get_remaining_transfers() as usize, + )), // error occurred - Either::Second(Err(e)) => Err(e), + Either::Right((Err(e), _)) => Err(e), }; drop(on_drop); @@ -594,14 +598,9 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // wait for DMA to complete or IDLE line detection if requested let res = self.inner_read_run(buffer, enable_idle_line_detection).await; - let ch = &mut self.rx_dma; - match res { Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), - Ok(ReadCompletionEvent::Idle) => { - let n = buffer_len - (ch.remaining_transfers() as usize); - Ok(n) - } + Ok(ReadCompletionEvent::Idle(n)) => Ok(n), Err(e) => Err(e), } } From efc70debb3bbf7fb7e9b1a23a42e5db149de8ed6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Apr 2023 16:16:33 +0200 Subject: [PATCH 0909/1575] stm32/dma: add double buffered mode for DMA, update DCMI. --- embassy-stm32/src/dcmi.rs | 37 +++++--- embassy-stm32/src/dma/dma.rs | 159 ++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 0b34553cf..c19be86c6 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -434,9 +434,13 @@ where result } + #[cfg(not(dma))] async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> { - todo!() - /* + panic!("capturing to buffers larger than 0xffff is only supported on DMA for now, not on BDMA or GPDMA."); + } + + #[cfg(dma)] + async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> { use crate::dma::TransferOptions; let data_len = buffer.len(); @@ -460,16 +464,24 @@ where let r = self.inner.regs(); let src = r.dr().ptr() as *mut u32; - unsafe { - channel.start_double_buffered_read(request, src, m0ar, m1ar, chunk_size, TransferOptions::default()); - } + let mut transfer = unsafe { + crate::dma::DoubleBuffered::new_read( + &mut self.dma, + request, + src, + m0ar, + m1ar, + chunk_size, + TransferOptions::default(), + ) + }; let mut last_chunk_set_for_transfer = false; let mut buffer0_last_accessible = false; let dma_result = poll_fn(|cx| { - channel.set_waker(cx.waker()); + transfer.set_waker(cx.waker()); - let buffer0_currently_accessible = unsafe { channel.is_buffer0_accessible() }; + let buffer0_currently_accessible = transfer.is_buffer0_accessible(); // check if the accessible buffer changed since last poll if buffer0_last_accessible == buffer0_currently_accessible { @@ -480,21 +492,21 @@ where if remaining_chunks != 0 { if remaining_chunks % 2 == 0 && buffer0_currently_accessible { m0ar = unsafe { m0ar.add(2 * chunk_size) }; - unsafe { channel.set_buffer0(m0ar) } + unsafe { transfer.set_buffer0(m0ar) } remaining_chunks -= 1; } else if !buffer0_currently_accessible { m1ar = unsafe { m1ar.add(2 * chunk_size) }; - unsafe { channel.set_buffer1(m1ar) }; + unsafe { transfer.set_buffer1(m1ar) }; remaining_chunks -= 1; } } else { if buffer0_currently_accessible { - unsafe { channel.set_buffer0(buffer.as_mut_ptr()) } + unsafe { transfer.set_buffer0(buffer.as_mut_ptr()) } } else { - unsafe { channel.set_buffer1(buffer.as_mut_ptr()) } + unsafe { transfer.set_buffer1(buffer.as_mut_ptr()) } } if last_chunk_set_for_transfer { - channel.request_stop(); + transfer.request_stop(); return Poll::Ready(()); } last_chunk_set_for_transfer = true; @@ -542,7 +554,6 @@ where unsafe { Self::toggle(false) }; result - */ } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9052aa110..62c092241 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,7 +1,8 @@ use core::future::Future; +use core::marker::PhantomData; use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; -use core::task::{Context, Poll}; +use core::task::{Context, Poll, Waker}; use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -440,3 +441,159 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { } } } + +// ================================== + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct DoubleBuffered<'a, C: Channel, W: Word> { + channel: PeripheralRef<'a, C>, + _phantom: PhantomData, +} + +impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buf0: *mut W, + buf1: *mut W, + len: usize, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::PeripheralToMemory; + let data_size = W::bits(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + let mut this = Self { + channel, + _phantom: PhantomData, + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.st(channel_number); + ch.par().write_value(peri_addr as u32); + ch.m0ar().write_value(buf0 as u32); + ch.m1ar().write_value(buf1 as u32); + ch.ndtr().write_value(regs::Ndtr(len as _)); + ch.fcr().write(|w| { + if let Some(fth) = options.fifo_threshold { + // FIFO mode + w.set_dmdis(vals::Dmdis::DISABLED); + w.set_fth(fth.into()); + } else { + // Direct mode + w.set_dmdis(vals::Dmdis::ENABLED); + } + }); + ch.cr().write(|w| { + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); + w.set_pl(vals::Pl::VERYHIGH); + w.set_minc(vals::Inc::INCREMENTED); + w.set_pinc(vals::Inc::FIXED); + w.set_teie(true); + w.set_tcie(true); + #[cfg(dma_v1)] + w.set_trbuff(true); + + #[cfg(dma_v2)] + w.set_chsel(_request); + + w.set_pburst(options.pburst.into()); + w.set_mburst(options.mburst.into()); + w.set_pfctrl(options.flow_ctrl.into()); + + w.set_en(true); + }); + + this + } + + fn clear_irqs(&mut self) { + let channel_number = self.channel.num(); + let dma = self.channel.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + unsafe { + dma.ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }) + } + } + + pub unsafe fn set_buffer0(&mut self, buffer: *mut W) { + let ch = self.channel.regs().st(self.channel.num()); + ch.m0ar().write_value(buffer as _); + } + + pub unsafe fn set_buffer1(&mut self, buffer: *mut W) { + let ch = self.channel.regs().st(self.channel.num()); + ch.m1ar().write_value(buffer as _); + } + + pub fn is_buffer0_accessible(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1 + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }) + } + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().read() }.en() + } + + /// Gets the total remaining transfers for the channel + /// Note: this will be zero for transfers that completed without cancellation. + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.ndtr().read() }.ndt() + } + + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); + } +} + +impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} From dbded7a6ce1297f382b48c0d9e7c0b8b6ccfdad3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Apr 2023 22:04:09 +0200 Subject: [PATCH 0910/1575] Update nightly. Includes this TAIT breaking change. https://github.com/rust-lang/rust/pull/110237 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f7183d167..2301ddc8d 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-2023-04-11" +channel = "nightly-2023-04-18" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From 21ea98810aed1a4a820ac8cc357d66821f80c3fc Mon Sep 17 00:00:00 2001 From: Jacob Davis-Hansson Date: Tue, 18 Apr 2023 17:44:19 +0200 Subject: [PATCH 0911/1575] Pass rx pin to right init arg --- embassy-rp/src/uart/mod.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f7a4c5a60..f9e30a78b 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -5,8 +5,8 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; -use crate::{pac, peripherals, Peripheral}; use crate::pac::io::vals::{Inover, Outover}; +use crate::{pac, peripherals, Peripheral}; #[cfg(feature = "nightly")] mod buffered; @@ -180,7 +180,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { } impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { - /// Create a new DMA-enabled UART which can only send data + /// Create a new DMA-enabled UART which can only recieve data pub fn new( _uart: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -188,7 +188,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { config: Config, ) -> Self { into_ref!(rx, rx_dma); - Uart::::init(Some(rx.map_into()), None, None, None, config); + Uart::::init(None, Some(rx.map_into()), None, None, config); Self::new_inner(Some(rx_dma.map_into())) } @@ -396,28 +396,44 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { if let Some(pin) = &tx { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_outover(if config.invert_tx { Outover::INVERT } else { Outover::NORMAL }); + w.set_outover(if config.invert_tx { + Outover::INVERT + } else { + Outover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rx { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_rx { Inover::INVERT } else { Inover::NORMAL }); + w.set_inover(if config.invert_rx { + Inover::INVERT + } else { + Inover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &cts { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_cts { Inover::INVERT } else { Inover::NORMAL }); + w.set_inover(if config.invert_cts { + Inover::INVERT + } else { + Inover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_outover(if config.invert_rts { Outover::INVERT } else { Outover::NORMAL }); + w.set_outover(if config.invert_rts { + Outover::INVERT + } else { + Outover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } From 2080d8bb6de58eb2da2ca5df2437ac8cc6577661 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Apr 2023 20:56:23 +0200 Subject: [PATCH 0912/1575] stm32/spi: add support for all word sizes. Co-Authored-By: anton smeenk --- embassy-stm32/src/dma/bdma.rs | 9 +- embassy-stm32/src/dma/dma.rs | 11 +- embassy-stm32/src/dma/gpdma.rs | 9 +- embassy-stm32/src/dma/mod.rs | 49 +-------- embassy-stm32/src/dma/word.rs | 79 +++++++++++++++ embassy-stm32/src/spi/mod.rs | 180 ++++++++++++++++++--------------- 6 files changed, 197 insertions(+), 140 deletions(-) create mode 100644 embassy-stm32/src/dma/word.rs diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index cf1222c46..a23bb8cd7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{Dir, Word, WordSize}; +use super::word::{Word, WordSize}; +use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac; @@ -167,7 +168,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -202,7 +203,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -225,7 +226,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 62c092241..ef1d27573 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -9,7 +9,8 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::regs; -use super::{Dir, Word, WordSize}; +use super::word::{Word, WordSize}; +use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::dma::vals; @@ -246,7 +247,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -281,7 +282,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -304,7 +305,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } @@ -464,7 +465,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { assert!(len > 0 && len <= 0xFFFF); let dir = Dir::PeripheralToMemory; - let data_size = W::bits(); + let data_size = W::size(); let channel_number = channel.num(); let dma = channel.regs(); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 5c6676a5f..5a516ccda 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{Dir, Word, WordSize}; +use super::word::{Word, WordSize}; +use super::Dir; use crate::_generated::GPDMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac; @@ -165,7 +166,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -200,7 +201,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -223,7 +224,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index d29ef4a1f..3312ca752 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -21,6 +21,8 @@ pub use gpdma::*; #[cfg(dmamux)] mod dmamux; +pub mod word; + use core::mem; use embassy_cortex_m::interrupt::Priority; @@ -36,53 +38,6 @@ enum Dir { PeripheralToMemory, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WordSize { - OneByte, - TwoBytes, - FourBytes, -} - -impl WordSize { - pub fn bytes(&self) -> usize { - match self { - Self::OneByte => 1, - Self::TwoBytes => 2, - Self::FourBytes => 4, - } - } -} - -mod word_sealed { - pub trait Word {} -} - -pub trait Word: word_sealed::Word { - fn bits() -> WordSize; -} - -impl word_sealed::Word for u8 {} -impl Word for u8 { - fn bits() -> WordSize { - WordSize::OneByte - } -} - -impl word_sealed::Word for u16 {} -impl Word for u16 { - fn bits() -> WordSize { - WordSize::TwoBytes - } -} - -impl word_sealed::Word for u32 {} -impl Word for u32 { - fn bits() -> WordSize { - WordSize::FourBytes - } -} - pub struct NoDma; impl_peripheral!(NoDma); diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs new file mode 100644 index 000000000..aef6e9700 --- /dev/null +++ b/embassy-stm32/src/dma/word.rs @@ -0,0 +1,79 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum WordSize { + OneByte, + TwoBytes, + FourBytes, +} + +impl WordSize { + pub fn bytes(&self) -> usize { + match self { + Self::OneByte => 1, + Self::TwoBytes => 2, + Self::FourBytes => 4, + } + } +} + +mod sealed { + pub trait Word {} +} + +pub trait Word: sealed::Word + Default + Copy + 'static { + fn size() -> WordSize; + fn bits() -> usize; +} + +macro_rules! impl_word { + (_, $T:ident, $bits:literal, $size:ident) => { + impl sealed::Word for $T {} + impl Word for $T { + fn bits() -> usize { + $bits + } + fn size() -> WordSize { + WordSize::$size + } + } + }; + ($T:ident, $uX:ident, $bits:literal, $size:ident) => { + #[repr(transparent)] + #[derive(Copy, Clone, Default)] + pub struct $T(pub $uX); + impl_word!(_, $T, $bits, $size); + }; +} + +impl_word!(U1, u8, 1, OneByte); +impl_word!(U2, u8, 2, OneByte); +impl_word!(U3, u8, 3, OneByte); +impl_word!(U4, u8, 4, OneByte); +impl_word!(U5, u8, 5, OneByte); +impl_word!(U6, u8, 6, OneByte); +impl_word!(U7, u8, 7, OneByte); +impl_word!(_, u8, 8, OneByte); +impl_word!(U9, u16, 9, TwoBytes); +impl_word!(U10, u16, 10, TwoBytes); +impl_word!(U11, u16, 11, TwoBytes); +impl_word!(U12, u16, 12, TwoBytes); +impl_word!(U13, u16, 13, TwoBytes); +impl_word!(U14, u16, 14, TwoBytes); +impl_word!(U15, u16, 15, TwoBytes); +impl_word!(_, u16, 16, TwoBytes); +impl_word!(U17, u32, 17, FourBytes); +impl_word!(U18, u32, 18, FourBytes); +impl_word!(U19, u32, 19, FourBytes); +impl_word!(U20, u32, 20, FourBytes); +impl_word!(U21, u32, 21, FourBytes); +impl_word!(U22, u32, 22, FourBytes); +impl_word!(U23, u32, 23, FourBytes); +impl_word!(U24, u32, 24, FourBytes); +impl_word!(U25, u32, 25, FourBytes); +impl_word!(U26, u32, 26, FourBytes); +impl_word!(U27, u32, 27, FourBytes); +impl_word!(U28, u32, 28, FourBytes); +impl_word!(U29, u32, 29, FourBytes); +impl_word!(U30, u32, 30, FourBytes); +impl_word!(U31, u32, 31, FourBytes); +impl_word!(_, u32, 32, FourBytes); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 7858cb3e8..9ce0cebfe 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -7,8 +7,7 @@ use embassy_futures::join::join; 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, Transfer}; +use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Pull}; use crate::pac::spi::{regs, vals, Spi as Regs}; @@ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> { miso: Option>, txdma: PeripheralRef<'d, Tx>, rxdma: PeripheralRef<'d, Rx>, - current_word_size: WordSize, + current_word_size: word_impl::Config, } impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { @@ -234,14 +233,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { if mosi.is_none() { w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); } - w.set_dff(WordSize::EightBit.dff()) + w.set_dff(::CONFIG) }); } #[cfg(spi_v2)] unsafe { T::REGS.cr2().modify(|w| { - w.set_frxth(WordSize::EightBit.frxth()); - w.set_ds(WordSize::EightBit.ds()); + let (ds, frxth) = ::CONFIG; + w.set_frxth(frxth); + w.set_ds(ds); w.set_ssoe(false); }); T::REGS.cr1().modify(|w| { @@ -279,7 +279,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cfg1().modify(|w| { w.set_crcen(false); w.set_mbr(br); - w.set_dsize(WordSize::EightBit.dsize()); + w.set_dsize(::CONFIG); + w.set_fthlv(vals::Fthlv::ONEFRAME); }); T::REGS.cr2().modify(|w| { w.set_tsize(0); @@ -297,7 +298,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso, txdma, rxdma, - current_word_size: WordSize::EightBit, + current_word_size: ::CONFIG, } } @@ -355,7 +356,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } } - fn set_word_size(&mut self, word_size: WordSize) { + fn set_word_size(&mut self, word_size: word_impl::Config) { if self.current_word_size == word_size { return; } @@ -364,7 +365,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { unsafe { T::REGS.cr1().modify(|reg| { reg.set_spe(false); - reg.set_dff(word_size.dff()) + reg.set_dff(word_size) }); T::REGS.cr1().modify(|reg| { reg.set_spe(true); @@ -376,8 +377,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(false); }); T::REGS.cr2().modify(|w| { - w.set_frxth(word_size.frxth()); - w.set_ds(word_size.ds()); + w.set_frxth(word_size.1); + w.set_ds(word_size.0); }); T::REGS.cr1().modify(|w| { w.set_spe(true); @@ -393,7 +394,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(false); }); T::REGS.cfg1().modify(|w| { - w.set_dsize(word_size.dsize()); + w.set_dsize(word_size); }); T::REGS.cr1().modify(|w| { w.set_csusp(false); @@ -412,7 +413,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -450,7 +451,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -513,7 +514,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -571,7 +572,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter() { let _ = transfer_word(T::REGS, *word)?; } @@ -581,7 +582,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_read(&mut self, words: &mut [W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(T::REGS, W::default())?; } @@ -591,7 +592,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(T::REGS, *word)?; } @@ -601,7 +602,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); let len = read.len().max(write.len()); for i in 0..len { let wb = write.get(i).copied().unwrap_or_default(); @@ -933,70 +934,89 @@ pub(crate) mod sealed { const REGS: Regs; } - pub trait Word: Copy + 'static { - const WORDSIZE: WordSize; - } - - impl Word for u8 { - const WORDSIZE: WordSize = WordSize::EightBit; - } - impl Word for u16 { - const WORDSIZE: WordSize = WordSize::SixteenBit; - } - - #[derive(Copy, Clone, PartialOrd, PartialEq)] - pub enum WordSize { - EightBit, - SixteenBit, - } - - impl WordSize { - #[cfg(any(spi_v1, spi_f1))] - pub fn dff(&self) -> vals::Dff { - match self { - WordSize::EightBit => vals::Dff::EIGHTBIT, - WordSize::SixteenBit => vals::Dff::SIXTEENBIT, - } - } - - #[cfg(spi_v2)] - pub fn ds(&self) -> vals::Ds { - match self { - WordSize::EightBit => vals::Ds::EIGHTBIT, - WordSize::SixteenBit => vals::Ds::SIXTEENBIT, - } - } - - #[cfg(spi_v2)] - pub fn frxth(&self) -> vals::Frxth { - match self { - WordSize::EightBit => vals::Frxth::QUARTER, - WordSize::SixteenBit => vals::Frxth::HALF, - } - } - - #[cfg(any(spi_v3, spi_v4, spi_v5))] - pub fn dsize(&self) -> u8 { - match self { - WordSize::EightBit => 0b0111, - WordSize::SixteenBit => 0b1111, - } - } - - #[cfg(any(spi_v3, spi_v4, spi_v5))] - pub fn _frxth(&self) -> vals::Fthlv { - match self { - WordSize::EightBit => vals::Fthlv::ONEFRAME, - WordSize::SixteenBit => vals::Fthlv::ONEFRAME, - } - } + pub trait Word { + const CONFIG: word_impl::Config; } } -pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {} +pub trait Word: word::Word + sealed::Word {} -impl Word for u8 {} -impl Word for u16 {} +macro_rules! impl_word { + ($T:ty, $config:expr) => { + impl sealed::Word for $T { + const CONFIG: Config = $config; + } + impl Word for $T {} + }; +} + +#[cfg(any(spi_v1, spi_f1))] +mod word_impl { + use super::*; + + pub type Config = vals::Dff; + + impl_word!(u8, vals::Dff::EIGHTBIT); + impl_word!(u16, vals::Dff::SIXTEENBIT); +} + +#[cfg(any(spi_v2))] +mod word_impl { + use super::*; + + pub type Config = (vals::Ds, vals::Frxth); + + impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER)); + impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER)); + impl_word!(word::U6, (vals::Ds::SIXBIT, vals::Frxth::QUARTER)); + impl_word!(word::U7, (vals::Ds::SEVENBIT, vals::Frxth::QUARTER)); + impl_word!(u8, (vals::Ds::EIGHTBIT, vals::Frxth::QUARTER)); + impl_word!(word::U9, (vals::Ds::NINEBIT, vals::Frxth::HALF)); + impl_word!(word::U10, (vals::Ds::TENBIT, vals::Frxth::HALF)); + impl_word!(word::U11, (vals::Ds::ELEVENBIT, vals::Frxth::HALF)); + impl_word!(word::U12, (vals::Ds::TWELVEBIT, vals::Frxth::HALF)); + impl_word!(word::U13, (vals::Ds::THIRTEENBIT, vals::Frxth::HALF)); + impl_word!(word::U14, (vals::Ds::FOURTEENBIT, vals::Frxth::HALF)); + impl_word!(word::U15, (vals::Ds::FIFTEENBIT, vals::Frxth::HALF)); + impl_word!(u16, (vals::Ds::SIXTEENBIT, vals::Frxth::HALF)); +} + +#[cfg(any(spi_v3, spi_v4, spi_v5))] +mod word_impl { + use super::*; + + pub type Config = u8; + + impl_word!(word::U4, 4 - 1); + impl_word!(word::U5, 5 - 1); + impl_word!(word::U6, 6 - 1); + impl_word!(word::U7, 7 - 1); + impl_word!(u8, 8 - 1); + impl_word!(word::U9, 9 - 1); + impl_word!(word::U10, 10 - 1); + impl_word!(word::U11, 11 - 1); + impl_word!(word::U12, 12 - 1); + impl_word!(word::U13, 13 - 1); + impl_word!(word::U14, 14 - 1); + impl_word!(word::U15, 15 - 1); + impl_word!(u16, 16 - 1); + impl_word!(word::U17, 17 - 1); + impl_word!(word::U18, 18 - 1); + impl_word!(word::U19, 19 - 1); + impl_word!(word::U20, 20 - 1); + impl_word!(word::U21, 21 - 1); + impl_word!(word::U22, 22 - 1); + impl_word!(word::U23, 23 - 1); + impl_word!(word::U24, 24 - 1); + impl_word!(word::U25, 25 - 1); + impl_word!(word::U26, 26 - 1); + impl_word!(word::U27, 27 - 1); + impl_word!(word::U28, 28 - 1); + impl_word!(word::U29, 29 - 1); + impl_word!(word::U30, 30 - 1); + impl_word!(word::U31, 31 - 1); + impl_word!(u32, 32 - 1); +} pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} pin_trait!(SckPin, Instance); From 3260f6b2aff824a5f7f2a81283b234bdb1bd3e24 Mon Sep 17 00:00:00 2001 From: anton smeenk Date: Tue, 18 Apr 2023 20:56:57 +0200 Subject: [PATCH 0913/1575] stm32/spi: add new_txonly_nosck constructor, for neopixels, with an example in the stm32g0 directory. --- embassy-stm32/src/spi/mod.rs | 17 ++++ examples/stm32g0/src/bin/spi_neopixel.rs | 101 +++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 examples/stm32g0/src/bin/spi_neopixel.rs diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 9ce0cebfe..492d0649a 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -177,6 +177,23 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { ) } + pub fn new_txonly_nosck( + peri: impl Peripheral

+ 'd, + mosi: impl Peripheral

> + 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ 'd, // TODO: remove + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(mosi); + unsafe { + mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); + mosi.set_speed(crate::gpio::Speed::Medium); + } + + Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) + } + /// Useful for on chip peripherals like SUBGHZ which are hardwired. /// The bus can optionally be exposed externally with `Spi::new()` still. #[allow(dead_code)] diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs new file mode 100644 index 000000000..81fdd15cb --- /dev/null +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -0,0 +1,101 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dma::word::U5; +use embassy_stm32::dma::NoDma; +use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const NR_PIXELS: usize = 15; +const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw +const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL; + +struct RGB { + r: u8, + g: u8, + b: u8, +} +impl Default for RGB { + fn default() -> RGB { + RGB { r: 0, g: 0, b: 0 } + } +} +pub struct Ws2812 { + // Note that the U5 type controls the selection of 5 bits to output + bitbuffer: [U5; TOTAL_BITS], +} + +impl Ws2812 { + pub fn new() -> Ws2812 { + Ws2812 { + bitbuffer: [U5(0); TOTAL_BITS], + } + } + fn len(&self) -> usize { + return NR_PIXELS; + } + fn set(&mut self, idx: usize, rgb: RGB) { + self.render_color(idx, 0, rgb.g); + self.render_color(idx, 8, rgb.r); + self.render_color(idx, 16, rgb.b); + } + // transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern + fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) { + let mut bits = color as usize; + let mut idx = pixel_idx * BITS_PER_PIXEL + offset; + + // render one bit in one spi byte. High time first, then the low time + // clock should be 4 Mhz, 5 bits, each bit is 0.25 us. + // a one bit is send as a pulse of 0.75 high -- 0.50 low + // a zero bit is send as a pulse of 0.50 high -- 0.75 low + // clock frequency for the neopixel is exact 800 khz + // note that the mosi output should have a resistor to ground of 10k, + // to assure that between the bursts the line is low + for _i in 0..8 { + if idx >= TOTAL_BITS { + return; + } + let pattern = match bits & 0x80 { + 0x80 => 0b0000_1110, + _ => 0b000_1100, + }; + bits = bits << 1; + self.bitbuffer[idx] = U5(pattern); + idx += 1; + } + } +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Start test using spi as neopixel driver"); + + let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); + + let mut neopixels = Ws2812::new(); + + loop { + let mut cnt: usize = 0; + for _i in 0..10 { + for idx in 0..neopixels.len() { + let color = match (cnt + idx) % 3 { + 0 => RGB { r: 0x21, g: 0, b: 0 }, + 1 => RGB { r: 0, g: 0x31, b: 0 }, + _ => RGB { r: 0, g: 0, b: 0x41 }, + }; + neopixels.set(idx, color); + } + cnt += 1; + // start sending the neopixel bit patters over spi to the neopixel string + spi.write(&neopixels.bitbuffer).await.ok(); + Timer::after(Duration::from_millis(500)).await; + } + Timer::after(Duration::from_millis(1000)).await; + } +} From 8a9136e4e47a2d02084f343cd6d212f419c37f04 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 18 Apr 2023 21:07:36 +0200 Subject: [PATCH 0914/1575] enable inline-asm feature for cortex-m in examples inline assembly is supported since rust 1.59, we're way past that. enabling this makes the compiled code more compact, and on rp2040 even decreses memory usage by not needing thunks in sram. --- examples/boot/application/nrf/Cargo.toml | 4 ++-- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 2 +- examples/boot/bootloader/rp/Cargo.toml | 2 +- examples/boot/bootloader/stm32/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index cda1917b3..d6a30602d 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -18,9 +18,9 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] ed25519-dalek = ["embassy-boot/ed25519-dalek"] -ed25519-salty = ["embassy-boot/ed25519-salty"] \ No newline at end of file +ed25519-salty = ["embassy-boot/ed25519-salty"] diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 9d34a3691..62ef42d6b 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -18,7 +18,7 @@ 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 = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 8e978eb24..e5fb1b01d 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index fb55b166d..a6ac1cadf 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index ea6b905a4..5b8ee555f 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 6ba18564d..05ce5c10a 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index d5b8e3e01..14af99e96 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index ccd1fe2ee..90ae97725 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 128afd51e..08403a4ec 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 8a6f53643..cd0be5b48 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -11,7 +11,7 @@ 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 } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } cfg-if = "1.0.0" diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index a16cebe31..b4167bcd8 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -13,7 +13,7 @@ embassy-rp = { path = "../../../../embassy-rp", default-features = false, featur embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" embedded-storage-async = "0.4.0" diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index b1791620f..f2675aa73 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -11,7 +11,7 @@ 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 } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" embedded-storage-async = "0.4.0" diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 6f7cb8875..0a7141c4e 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -21,7 +21,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3" } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index af19413cc..10c269a76 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -27,7 +27,7 @@ defmt = "0.3" defmt-rtt = "0.4" static_cell = "1.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 2c3a12964..ebbc25bc6 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -43,7 +43,7 @@ defmt = "0.3" defmt-rtt = "0.4" static_cell = "1.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = [ diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index f0fd27991..8067f7ba5 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -20,7 +20,7 @@ defmt = "0.3" defmt-rtt = "0.4" #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -cortex-m = { version = "0.7.6" } +cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 2ff252622..e969538d3 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index d08e00b0b..706b5a722 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 02045a79f..62947bf49 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index a62eba9ec..7ba9ff0c1 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 69dcab64c..77985a017 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -15,7 +15,7 @@ embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-io = "0.4.0" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 898e05c12..d9e9d668c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index a522fb422..6bbd3a53f 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 149f8a58b..67e6c76a7 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "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.10" } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 8316498ca..bd175a5b1 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "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.10" } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index b04faf535..d08e2b61a 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -24,7 +24,7 @@ defmt-rtt = "0.4" embedded-storage = "0.3.0" embedded-io = "0.4.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index e071c5d27..ff95571e6 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index af305a19d..d3dee5250 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -18,7 +18,7 @@ defmt = "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"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 65fc1b988..86bc83dab 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -14,7 +14,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 835985ec3..18b27b28e 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index cb3526fa4..07f136b40 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -17,7 +17,7 @@ lorawan = { version = "0.7.2", default-features = false, features = ["default-cr defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-storage = "0.3.0" From bfa3cbaf30fae513c7569a2f5c88a45b0911f02e Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 18 Apr 2023 21:47:28 +0200 Subject: [PATCH 0915/1575] Add embassy-net without dhcp to ci.sh --- ci.sh | 1 + ci_stable.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index 1b3fac8bc..657975041 100755 --- a/ci.sh +++ b/ci.sh @@ -22,6 +22,7 @@ cargo batch \ --- 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,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ diff --git a/ci_stable.sh b/ci_stable.sh index b4b0b83e7..18271ee73 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -13,6 +13,7 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ From a2ac1eed1bd357f31c8a0cd5f8957f3017c5df21 Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 18 Apr 2023 22:11:15 +0200 Subject: [PATCH 0916/1575] Add extra feature flags to fix build without dhcp. --- embassy-net/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 7b9d0e773..5dfb5843e 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -27,12 +27,10 @@ use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; +use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] -use smoltcp::iface::SocketHandle; -use smoltcp::iface::{Interface, SocketSet, SocketStorage}; +use smoltcp::socket::dhcpv4::{self, RetryConfig}; #[cfg(feature = "dhcpv4")] -use smoltcp::socket::dhcpv4; -use smoltcp::socket::dhcpv4::RetryConfig; use smoltcp::time::Duration; // smoltcp reexports pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; @@ -76,6 +74,7 @@ pub struct StaticConfig { pub dns_servers: Vec, } +#[cfg(feature = "dhcpv4")] #[derive(Debug, Clone, PartialEq, Eq)] pub struct DhcpConfig { pub max_lease_duration: Option, @@ -88,6 +87,7 @@ pub struct DhcpConfig { pub client_port: u16, } +#[cfg(feature = "dhcpv4")] impl Default for DhcpConfig { fn default() -> Self { Self { @@ -384,6 +384,7 @@ impl Inner { self.config = Some(config) } + #[cfg(feature = "dhcpv4")] fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { socket.set_ignore_naks(config.ignore_naks); socket.set_max_lease_duration(config.max_lease_duration); From f589247c1f80a6a9f95a16bedf7594cdef62185f Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Apr 2023 17:02:40 -0500 Subject: [PATCH 0917/1575] stm32/rtc: cleanup and consolidate --- embassy-stm32/src/rtc/datetime_chrono.rs | 85 -------------------- embassy-stm32/src/rtc/mod.rs | 2 +- embassy-stm32/src/rtc/{v2/mod.rs => v2.rs} | 92 ++++++++++++++++++---- embassy-stm32/src/rtc/v2/v2f0.rs | 41 ---------- embassy-stm32/src/rtc/v2/v2f2.rs | 31 -------- embassy-stm32/src/rtc/v2/v2f3.rs | 31 -------- embassy-stm32/src/rtc/v2/v2f4.rs | 31 -------- embassy-stm32/src/rtc/v2/v2f7.rs | 41 ---------- embassy-stm32/src/rtc/v2/v2h7.rs | 33 -------- embassy-stm32/src/rtc/v2/v2l0.rs | 26 ------ embassy-stm32/src/rtc/v2/v2l1.rs | 24 ------ embassy-stm32/src/rtc/v2/v2l4.rs | 41 ---------- embassy-stm32/src/rtc/v2/v2wb.rs | 39 --------- examples/stm32f4/Cargo.toml | 1 + examples/stm32f4/src/bin/rtc.rs | 30 +++++++ 15 files changed, 110 insertions(+), 438 deletions(-) delete mode 100644 embassy-stm32/src/rtc/datetime_chrono.rs rename embassy-stm32/src/rtc/{v2/mod.rs => v2.rs} (71%) delete mode 100644 embassy-stm32/src/rtc/v2/v2f0.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2f2.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2f3.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2f4.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2f7.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2h7.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2l0.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2l1.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2l4.rs delete mode 100644 embassy-stm32/src/rtc/v2/v2wb.rs create mode 100644 examples/stm32f4/src/bin/rtc.rs diff --git a/embassy-stm32/src/rtc/datetime_chrono.rs b/embassy-stm32/src/rtc/datetime_chrono.rs deleted file mode 100644 index b46316cc9..000000000 --- a/embassy-stm32/src/rtc/datetime_chrono.rs +++ /dev/null @@ -1,85 +0,0 @@ -use chrono::{Datelike, Timelike}; - -use super::byte_to_bcd2; -use crate::pac::rtc::Rtc; - -/// Alias for [`chrono::NaiveDateTime`] -pub type DateTime = chrono::NaiveDateTime; -/// Alias for [`chrono::Weekday`] -pub type DayOfWeek = chrono::Weekday; - -/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. -/// -/// [`DateTimeFilter`]: struct.DateTimeFilter.html -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Error { - /// The [DateTime] has an invalid year. The year must be between 0 and 4095. - InvalidYear, - /// The [DateTime] contains an invalid date. - InvalidDate, - /// The [DateTime] contains an invalid time. - InvalidTime, -} - -pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { - dotw.num_days_from_monday() as u8 -} - -pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { - if dt.year() < 0 || dt.year() > 4095 { - // rp2040 can't hold these years - Err(Error::InvalidYear) - } else { - // The rest of the chrono date is assumed to be valid - Ok(()) - } -} - -pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) { - let (ht, hu) = byte_to_bcd2(t.hour() as u8); - let (mnt, mnu) = byte_to_bcd2(t.minute() as u8); - let (st, su) = byte_to_bcd2(t.second() as u8); - - let (dt, du) = byte_to_bcd2(t.day() as u8); - let (mt, mu) = byte_to_bcd2(t.month() as u8); - let yr = t.year() as u16; - let yr_offset = (yr - 1970_u16) as u8; - let (yt, yu) = byte_to_bcd2(yr_offset); - - unsafe { - rtc.tr().write(|w| { - w.set_ht(ht); - w.set_hu(hu); - w.set_mnt(mnt); - w.set_mnu(mnu); - w.set_st(st); - w.set_su(su); - w.set_pm(stm32_metapac::rtc::vals::Ampm::AM); - }); - - rtc.dr().write(|w| { - w.set_dt(dt); - w.set_du(du); - w.set_mt(mt > 0); - w.set_mu(mu); - w.set_yt(yt); - w.set_yu(yu); - w.set_wdu(day_of_week_to_u8(t.weekday())); - }); - } -} - -pub(super) fn datetime( - year: u16, - month: u8, - day: u8, - _day_of_week: u8, - hour: u8, - minute: u8, - second: u8, -) -> Result { - let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into()) - .ok_or(Error::InvalidDate)?; - let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?; - Ok(DateTime::new(date, time)) -} diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 170783b2d..c2f07a066 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -10,7 +10,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; any( rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb ), - path = "v2/mod.rs" + path = "v2.rs" )] #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] mod versions; diff --git a/embassy-stm32/src/rtc/v2/mod.rs b/embassy-stm32/src/rtc/v2.rs similarity index 71% rename from embassy-stm32/src/rtc/v2/mod.rs rename to embassy-stm32/src/rtc/v2.rs index 296adae89..abf3da3be 100644 --- a/embassy-stm32/src/rtc/v2/mod.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -3,20 +3,6 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; use super::{Instance, RtcConfig}; use crate::pac::rtc::Rtc; -#[cfg_attr(rtc_v2f0, path = "v2f0.rs")] -#[cfg_attr(rtc_v2f2, path = "v2f2.rs")] -#[cfg_attr(rtc_v2f3, path = "v2f3.rs")] -#[cfg_attr(rtc_v2f4, path = "v2f4.rs")] -#[cfg_attr(rtc_v2f7, path = "v2f7.rs")] -#[cfg_attr(rtc_v2h7, path = "v2h7.rs")] -#[cfg_attr(rtc_v2l0, path = "v2l0.rs")] -#[cfg_attr(rtc_v2l1, path = "v2l1.rs")] -#[cfg_attr(rtc_v2l4, path = "v2l4.rs")] -#[cfg_attr(rtc_v2wb, path = "v2wb.rs")] -mod family; - -pub use family::*; - impl<'d, T: Instance> super::Rtc<'d, T> { /// Applies the RTC config /// It this changes the RTC clock source the time will be reset @@ -169,3 +155,81 @@ pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) } } } + +pub(crate) unsafe fn enable_peripheral_clk() { + #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2l4, rtc_v2wb))] + { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); + } +} + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + #[cfg(not(rtc_v2wb))] + use stm32_metapac::rcc::vals::Rtcsel; + + #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] + let cr = crate::pac::PWR.cr(); + #[cfg(any(rtc_v2f0, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] + let cr = crate::pac::PWR.cr1(); + + // TODO: Missing from PAC for l0? + #[cfg(not(rtc_v2l0))] + { + cr.modify(|w| w.set_dbp(true)); + while !cr.read().dbp() {} + } + + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let reg = crate::pac::RCC.bdcr().read(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let reg = crate::pac::RCC.csr().read(); + + #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + #[cfg(rtc_v2wb)] + let rtcsel = reg.rtcsel(); + #[cfg(not(rtc_v2wb))] + let rtcsel = reg.rtcsel().0; + + if !reg.rtcen() || rtcsel != clock_config { + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let cr = crate::pac::RCC.bdcr(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let cr = crate::pac::RCC.csr(); + + cr.modify(|w| { + // Reset + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + w.set_bdrst(false); + + // Select RTC source + #[cfg(not(rtc_v2wb))] + w.set_rtcsel(Rtcsel(clock_config)); + #[cfg(rtc_v2wb)] + w.set_rtcsel(clock_config); + w.set_rtcen(true); + + // Restore bcdr + #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2l4, rtc_v2wb))] + w.set_lscosel(reg.lscosel()); + #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2l4, rtc_v2wb))] + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + + #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} diff --git a/embassy-stm32/src/rtc/v2/v2f0.rs b/embassy-stm32/src/rtc/v2/v2f0.rs deleted file mode 100644 index d6871d91e..000000000 --- a/embassy-stm32/src/rtc/v2/v2f0.rs +++ /dev/null @@ -1,41 +0,0 @@ -use stm32_metapac::rcc::vals::Rtcsel; - -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(Rtcsel(clock_config)); - w.set_rtcen(true); - - // Restore bcdr - w.set_lscosel(reg.lscosel()); - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // enable peripheral clock for communication - crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // read to allow the pwr clock to enable - crate::pac::PWR.cr1().read(); -} diff --git a/embassy-stm32/src/rtc/v2/v2f2.rs b/embassy-stm32/src/rtc/v2/v2f2.rs deleted file mode 100644 index e041f3f4e..000000000 --- a/embassy-stm32/src/rtc/v2/v2f2.rs +++ /dev/null @@ -1,31 +0,0 @@ -use stm32_metapac::rcc::vals::Rtcsel; - -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(Rtcsel(clock_config)); - w.set_rtcen(true); - - w.set_lseon(reg.lseon()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // Nothing to do -} diff --git a/embassy-stm32/src/rtc/v2/v2f3.rs b/embassy-stm32/src/rtc/v2/v2f3.rs deleted file mode 100644 index e041f3f4e..000000000 --- a/embassy-stm32/src/rtc/v2/v2f3.rs +++ /dev/null @@ -1,31 +0,0 @@ -use stm32_metapac::rcc::vals::Rtcsel; - -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(Rtcsel(clock_config)); - w.set_rtcen(true); - - w.set_lseon(reg.lseon()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // Nothing to do -} diff --git a/embassy-stm32/src/rtc/v2/v2f4.rs b/embassy-stm32/src/rtc/v2/v2f4.rs deleted file mode 100644 index 4dd21cae4..000000000 --- a/embassy-stm32/src/rtc/v2/v2f4.rs +++ /dev/null @@ -1,31 +0,0 @@ -use stm32_metapac::rcc::vals::Rtcsel; - -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(Rtcsel(clock_config)); - w.set_rtcen(true); - - w.set_lseon(reg.lseon()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // Nothing to do -} diff --git a/embassy-stm32/src/rtc/v2/v2f7.rs b/embassy-stm32/src/rtc/v2/v2f7.rs deleted file mode 100644 index d6871d91e..000000000 --- a/embassy-stm32/src/rtc/v2/v2f7.rs +++ /dev/null @@ -1,41 +0,0 @@ -use stm32_metapac::rcc::vals::Rtcsel; - -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(Rtcsel(clock_config)); - w.set_rtcen(true); - - // Restore bcdr - w.set_lscosel(reg.lscosel()); - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // enable peripheral clock for communication - crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // read to allow the pwr clock to enable - crate::pac::PWR.cr1().read(); -} diff --git a/embassy-stm32/src/rtc/v2/v2h7.rs b/embassy-stm32/src/rtc/v2/v2h7.rs deleted file mode 100644 index f3b180683..000000000 --- a/embassy-stm32/src/rtc/v2/v2h7.rs +++ /dev/null @@ -1,33 +0,0 @@ -use stm32_metapac::rcc::vals::Rtcsel; - -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(Rtcsel(clock_config)); - w.set_rtcen(true); - - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // Nothing to do -} diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs deleted file mode 100644 index dbd3b0882..000000000 --- a/embassy-stm32/src/rtc/v2/v2l0.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - // TODO: Missing from PAC? - // crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); - // while !crate::pac::PWR.cr().read().dbp() {} - - let reg = crate::pac::RCC.csr().read(); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.csr().modify(|w| { - // Select RTC source - w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config)); - w.set_rtcen(true); - - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // Nothing to do -} diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs deleted file mode 100644 index 1ac78b31a..000000000 --- a/embassy-stm32/src/rtc/v2/v2l1.rs +++ /dev/null @@ -1,24 +0,0 @@ -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr().read().dbp() {} - - let reg = crate::pac::RCC.csr().read(); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.csr().modify(|w| { - // Select RTC source - w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config)); - w.set_rtcen(true); - - w.set_lseon(reg.lseon()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // Nothing to do -} diff --git a/embassy-stm32/src/rtc/v2/v2l4.rs b/embassy-stm32/src/rtc/v2/v2l4.rs deleted file mode 100644 index d6871d91e..000000000 --- a/embassy-stm32/src/rtc/v2/v2l4.rs +++ /dev/null @@ -1,41 +0,0 @@ -use stm32_metapac::rcc::vals::Rtcsel; - -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - if !reg.rtcen() || reg.rtcsel().0 != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(Rtcsel(clock_config)); - w.set_rtcen(true); - - // Restore bcdr - w.set_lscosel(reg.lscosel()); - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // enable peripheral clock for communication - crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // read to allow the pwr clock to enable - crate::pac::PWR.cr1().read(); -} diff --git a/embassy-stm32/src/rtc/v2/v2wb.rs b/embassy-stm32/src/rtc/v2/v2wb.rs deleted file mode 100644 index 98761fa60..000000000 --- a/embassy-stm32/src/rtc/v2/v2wb.rs +++ /dev/null @@ -1,39 +0,0 @@ -pub const BACKUP_REGISTER_COUNT: usize = 20; - -/// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - - let reg = crate::pac::RCC.bdcr().read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - if !reg.rtcen() || reg.rtcsel() != clock_config { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); - - // Select RTC source - w.set_rtcsel(clock_config); - w.set_rtcen(true); - - // Restore bcdr - w.set_lscosel(reg.lscosel()); - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - // enable peripheral clock for communication - crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // read to allow the pwr clock to enable - crate::pac::PWR.cr1().read(); -} diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 69dcab64c..275c2c1a7 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -26,6 +26,7 @@ nb = "1.0.0" embedded-storage = "0.3.0" micromath = "2.0.0" static_cell = "1.0" +chrono = { version = "^0.4", default-features = false} [profile.release] debug = 2 diff --git a/examples/stm32f4/src/bin/rtc.rs b/examples/stm32f4/src/bin/rtc.rs new file mode 100644 index 000000000..0eca58203 --- /dev/null +++ b/examples/stm32f4/src/bin/rtc.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +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 now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + // In reality the delay would be much longer + Timer::after(Duration::from_millis(20000)).await; + + let _then: NaiveDateTime = rtc.now().unwrap().into(); +} From 4de4039417df43e45795fd3848cf02904151ddbb Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Apr 2023 19:07:58 -0500 Subject: [PATCH 0918/1575] stm32/rtc: build more chips --- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/rtc/v2.rs | 132 ++++++++++++++++++------------------ embassy-stm32/src/rtc/v3.rs | 39 ++++------- 3 files changed, 79 insertions(+), 94 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0dbc9e5c8..70e6aa2bf 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -49,7 +49,7 @@ pub mod pwm; pub mod qspi; #[cfg(rng)] pub mod rng; -#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))] +#[cfg(all(rtc, not(rtc_v1)))] pub mod rtc; #[cfg(sdmmc)] pub mod sdmmc; diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index abf3da3be..8ab06a59a 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -9,7 +9,70 @@ impl<'d, T: Instance> super::Rtc<'d, T> { pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { // Unlock the backup domain unsafe { - unlock_backup_domain(rtc_config.clock_config as u8); + let clock_config = rtc_config.clock_config as u8; + + #[cfg(not(rtc_v2wb))] + use stm32_metapac::rcc::vals::Rtcsel; + + #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] + let cr = crate::pac::PWR.cr(); + #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] + let cr = crate::pac::PWR.cr1(); + + // TODO: Missing from PAC for l0 and f0? + #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] + { + cr.modify(|w| w.set_dbp(true)); + while !cr.read().dbp() {} + } + + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let reg = crate::pac::RCC.bdcr().read(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let reg = crate::pac::RCC.csr().read(); + + #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + #[cfg(rtc_v2wb)] + let rtcsel = reg.rtcsel(); + #[cfg(not(rtc_v2wb))] + let rtcsel = reg.rtcsel().0; + + if !reg.rtcen() || rtcsel != clock_config { + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let cr = crate::pac::RCC.bdcr(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let cr = crate::pac::RCC.csr(); + + cr.modify(|w| { + // Reset + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + w.set_bdrst(false); + + // Select RTC source + #[cfg(not(rtc_v2wb))] + w.set_rtcsel(Rtcsel(clock_config)); + #[cfg(rtc_v2wb)] + w.set_rtcsel(clock_config); + w.set_rtcen(true); + + // Restore bcdr + #[cfg(any(rtc_v2l4, rtc_v2wb))] + w.set_lscosel(reg.lscosel()); + #[cfg(any(rtc_v2l4, rtc_v2wb))] + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + + #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } } self.write(true, |rtc| unsafe { @@ -157,7 +220,7 @@ pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { } pub(crate) unsafe fn enable_peripheral_clk() { - #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2l4, rtc_v2wb))] + #[cfg(any(rtc_v2l4, rtc_v2wb))] { // enable peripheral clock for communication crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); @@ -168,68 +231,3 @@ pub(crate) unsafe fn enable_peripheral_clk() { } pub const BACKUP_REGISTER_COUNT: usize = 20; - -pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { - #[cfg(not(rtc_v2wb))] - use stm32_metapac::rcc::vals::Rtcsel; - - #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] - let cr = crate::pac::PWR.cr(); - #[cfg(any(rtc_v2f0, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] - let cr = crate::pac::PWR.cr1(); - - // TODO: Missing from PAC for l0? - #[cfg(not(rtc_v2l0))] - { - cr.modify(|w| w.set_dbp(true)); - while !cr.read().dbp() {} - } - - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - let reg = crate::pac::RCC.bdcr().read(); - #[cfg(any(rtc_v2l0, rtc_v2l1))] - let reg = crate::pac::RCC.csr().read(); - - #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - #[cfg(rtc_v2wb)] - let rtcsel = reg.rtcsel(); - #[cfg(not(rtc_v2wb))] - let rtcsel = reg.rtcsel().0; - - if !reg.rtcen() || rtcsel != clock_config { - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - let cr = crate::pac::RCC.bdcr(); - #[cfg(any(rtc_v2l0, rtc_v2l1))] - let cr = crate::pac::RCC.csr(); - - cr.modify(|w| { - // Reset - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - w.set_bdrst(false); - - // Select RTC source - #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel(clock_config)); - #[cfg(rtc_v2wb)] - w.set_rtcsel(clock_config); - w.set_rtcen(true); - - // Restore bcdr - #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2l4, rtc_v2wb))] - w.set_lscosel(reg.lscosel()); - #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2l4, rtc_v2wb))] - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - - #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } -} diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 6998c48c2..c2b3c88c2 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -9,43 +9,30 @@ impl<'d, T: Instance> super::Rtc<'d, T> { pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { // Unlock the backup domain unsafe { - #[cfg(feature = "stm32g0c1ve")] + #[cfg(any(rtc_v3u5, rcc_g0))] + use crate::pac::rcc::vals::Rtcsel; + #[cfg(not(any(rtc_v3u5, rcc_g0, rcc_g4, rcc_wl5, rcc_wle)))] + use crate::pac::rtc::vals::Rtcsel; + + #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] { crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); while !crate::pac::PWR.cr1().read().dbp() {} } - - #[cfg(not(any( - feature = "stm32g0c1ve", - feature = "stm32g491re", - feature = "stm32u585zi", - feature = "stm32g473cc" - )))] + #[cfg(any(rcc_wl5, rcc_wle))] { - crate::pac::PWR - .cr1() - .modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED)); - while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {} + use crate::pac::pwr::vals::Dbp; + + crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); + while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} } let reg = crate::pac::RCC.bdcr().read(); assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); let config_rtcsel = rtc_config.clock_config as u8; - #[cfg(not(any( - feature = "stm32wl54jc-cm0p", - feature = "stm32wle5ub", - feature = "stm32g0c1ve", - feature = "stm32wl55jc-cm4", - feature = "stm32wl55uc-cm4", - feature = "stm32g491re", - feature = "stm32g473cc", - feature = "stm32u585zi", - feature = "stm32wle5jb" - )))] - let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); - #[cfg(feature = "stm32g0c1ve")] - let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel); + #[cfg(not(any(rcc_wl5, rcc_wle, rcc_g4)))] + let config_rtcsel = Rtcsel(config_rtcsel); if !reg.rtcen() || reg.rtcsel() != config_rtcsel { crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); From e24421a393a549a000c8824d2ede275203b64a26 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 18 Apr 2023 20:35:43 -0500 Subject: [PATCH 0919/1575] stm32/rtc: impl. functions on trait --- embassy-stm32/src/rtc/mod.rs | 44 +++++++++++++++++--------- embassy-stm32/src/rtc/v2.rs | 60 +++++++++++++++++------------------- embassy-stm32/src/rtc/v3.rs | 42 ++++++++++--------------- 3 files changed, 74 insertions(+), 72 deletions(-) diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index c2f07a066..962927fb1 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -13,9 +13,9 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; path = "v2.rs" )] #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] -mod versions; +mod _version; +pub use _version::*; use embassy_hal_common::Peripheral; -pub use versions::*; /// Errors that can occur on methods on [RtcClock] #[derive(Clone, Debug, PartialEq, Eq)] @@ -113,7 +113,7 @@ impl Default for RtcCalibrationCyclePeriod { impl<'d, T: Instance> Rtc<'d, T> { pub fn new(_rtc: impl Peripheral

+ 'd, rtc_config: RtcConfig) -> Self { - unsafe { enable_peripheral_clk() }; + unsafe { T::enable_peripheral_clk() }; let mut rtc_struct = Self { phantom: PhantomData, @@ -179,14 +179,14 @@ impl<'d, T: Instance> Rtc<'d, T> { self.rtc_config } - pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT; + pub const BACKUP_REGISTER_COUNT: usize = T::BACKUP_REGISTER_COUNT; /// Read content of the backup register. /// /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn read_backup_register(&self, register: usize) -> Option { - read_backup_register(&T::regs(), register) + T::read_backup_register(&T::regs(), register) } /// Set content of the backup register. @@ -194,7 +194,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn write_backup_register(&self, register: usize, value: u32) { - write_backup_register(&T::regs(), register, value) + T::write_backup_register(&T::regs(), register, value) } } @@ -219,17 +219,31 @@ pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 { } pub(crate) mod sealed { + use crate::pac::rtc::Rtc; + pub trait Instance { - fn regs() -> crate::pac::rtc::Rtc; + const BACKUP_REGISTER_COUNT: usize; + + fn regs() -> Rtc { + crate::pac::RTC + } + + unsafe fn enable_peripheral_clk() {} + + /// Read content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + fn read_backup_register(rtc: &Rtc, register: usize) -> Option; + + /// Set content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + fn write_backup_register(rtc: &Rtc, register: usize, value: u32); + + // fn apply_config(&mut self, rtc_config: RtcConfig); } } pub trait Instance: sealed::Instance + 'static {} - -impl sealed::Instance for crate::peripherals::RTC { - fn regs() -> crate::pac::rtc::Rtc { - crate::pac::RTC - } -} - -impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 8ab06a59a..adaafe67a 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,6 +1,6 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; -use super::{Instance, RtcConfig}; +use super::{sealed, Instance, RtcConfig}; use crate::pac::rtc::Rtc; impl<'d, T: Instance> super::Rtc<'d, T> { @@ -197,37 +197,33 @@ impl<'d, T: Instance> super::Rtc<'d, T> { } } -/// Read content of the backup register. -/// -/// The registers retain their values during wakes from standby mode or system resets. They also -/// retain their value when Vdd is switched off as long as V_BAT is powered. -pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option { - if register < BACKUP_REGISTER_COUNT { - Some(unsafe { rtc.bkpr(register).read().bkp() }) - } else { - None +impl sealed::Instance for crate::peripherals::RTC { + const BACKUP_REGISTER_COUNT: usize = 20; + + unsafe fn enable_peripheral_clk() { + #[cfg(any(rtc_v2l4, rtc_v2wb))] + { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); + } + } + + fn read_backup_register(rtc: &Rtc, register: usize) -> Option { + if register < Self::BACKUP_REGISTER_COUNT { + Some(unsafe { rtc.bkpr(register).read().bkp() }) + } else { + None + } + } + + fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { + if register < Self::BACKUP_REGISTER_COUNT { + unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) } + } } } -/// Set content of the backup register. -/// -/// The registers retain their values during wakes from standby mode or system resets. They also -/// retain their value when Vdd is switched off as long as V_BAT is powered. -pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { - if register < BACKUP_REGISTER_COUNT { - unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) } - } -} - -pub(crate) unsafe fn enable_peripheral_clk() { - #[cfg(any(rtc_v2l4, rtc_v2wb))] - { - // enable peripheral clock for communication - crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // read to allow the pwr clock to enable - crate::pac::PWR.cr1().read(); - } -} - -pub const BACKUP_REGISTER_COUNT: usize = 20; +impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index c2b3c88c2..19c52ee02 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,6 +1,6 @@ use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; -use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig}; +use super::{sealed, Instance, RtcCalibrationCyclePeriod, RtcConfig}; use crate::pac::rtc::Rtc; impl<'d, T: Instance> super::Rtc<'d, T> { @@ -182,32 +182,24 @@ impl<'d, T: Instance> super::Rtc<'d, T> { } } -pub(super) unsafe fn enable_peripheral_clk() { - // Nothing to do -} +impl sealed::Instance for crate::peripherals::RTC { + const BACKUP_REGISTER_COUNT: usize = 32; -pub const BACKUP_REGISTER_COUNT: usize = 32; + fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { + if register < Self::BACKUP_REGISTER_COUNT { + //Some(rtc.bkpr()[register].read().bits()) + None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC + } else { + None + } + } -/// Read content of the backup register. -/// -/// The registers retain their values during wakes from standby mode or system resets. They also -/// retain their value when Vdd is switched off as long as V_BAT is powered. -pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { - if register < BACKUP_REGISTER_COUNT { - //Some(rtc.bkpr()[register].read().bits()) - None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC - } else { - None + fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) { + if register < Self::BACKUP_REGISTER_COUNT { + // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC + //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) } + } } } -/// Set content of the backup register. -/// -/// The registers retain their values during wakes from standby mode or system resets. They also -/// retain their value when Vdd is switched off as long as V_BAT is powered. -pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) { - if register < BACKUP_REGISTER_COUNT { - // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC - //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) } - } -} +impl Instance for crate::peripherals::RTC {} From fdd6e08ed6b5445a53c8cd0752f80f203c4860ac Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 19 Apr 2023 01:57:37 +0200 Subject: [PATCH 0920/1575] rp: hook up softfloat rom intrinsics rp-hal has done this very well already, so we'll just copy their entire impl again. only div.rs needed some massaging because our sio access works a little differently, everything else worked as is. --- embassy-rp/src/float/add_sub.rs | 92 ++++++++++++ embassy-rp/src/float/cmp.rs | 201 +++++++++++++++++++++++++ embassy-rp/src/float/conv.rs | 157 ++++++++++++++++++++ embassy-rp/src/float/div.rs | 141 ++++++++++++++++++ embassy-rp/src/float/functions.rs | 239 ++++++++++++++++++++++++++++++ embassy-rp/src/float/mod.rs | 149 +++++++++++++++++++ embassy-rp/src/float/mul.rs | 70 +++++++++ embassy-rp/src/lib.rs | 1 + tests/rp/.cargo/config.toml | 6 +- tests/rp/Cargo.toml | 2 +- tests/rp/src/bin/float.rs | 53 +++++++ 11 files changed, 1108 insertions(+), 3 deletions(-) create mode 100644 embassy-rp/src/float/add_sub.rs create mode 100644 embassy-rp/src/float/cmp.rs create mode 100644 embassy-rp/src/float/conv.rs create mode 100644 embassy-rp/src/float/div.rs create mode 100644 embassy-rp/src/float/functions.rs create mode 100644 embassy-rp/src/float/mod.rs create mode 100644 embassy-rp/src/float/mul.rs create mode 100644 tests/rp/src/bin/float.rs diff --git a/embassy-rp/src/float/add_sub.rs b/embassy-rp/src/float/add_sub.rs new file mode 100644 index 000000000..673544cfe --- /dev/null +++ b/embassy-rp/src/float/add_sub.rs @@ -0,0 +1,92 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/add_sub.rs + +use super::{Float, Int}; +use crate::rom_data; + +trait ROMAdd { + fn rom_add(self, b: Self) -> Self; +} + +impl ROMAdd for f32 { + fn rom_add(self, b: Self) -> Self { + rom_data::float_funcs::fadd(self, b) + } +} + +impl ROMAdd for f64 { + fn rom_add(self, b: Self) -> Self { + rom_data::double_funcs::dadd(self, b) + } +} + +fn add(a: F, b: F) -> F { + if a.is_not_finite() { + if b.is_not_finite() { + let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK); + let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK); + + if class_a == F::Int::ZERO && class_b == F::Int::ZERO { + // inf + inf = inf + return a; + } + if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK { + // -inf + (-inf) = -inf + return a; + } + + // Sign mismatch, or either is NaN already + return F::NAN; + } + + // [-]inf/NaN + X = [-]inf/NaN + return a; + } + + if b.is_not_finite() { + // X + [-]inf/NaN = [-]inf/NaN + return b; + } + + a.rom_add(b) +} + +intrinsics! { + #[alias = __addsf3vfp] + #[aeabi = __aeabi_fadd] + extern "C" fn __addsf3(a: f32, b: f32) -> f32 { + add(a, b) + } + + #[bootrom_v2] + #[alias = __adddf3vfp] + #[aeabi = __aeabi_dadd] + extern "C" fn __adddf3(a: f64, b: f64) -> f64 { + add(a, b) + } + + // The ROM just implements subtraction the same way, so just do it here + // and save the work of implementing more complicated NaN/inf handling. + + #[alias = __subsf3vfp] + #[aeabi = __aeabi_fsub] + extern "C" fn __subsf3(a: f32, b: f32) -> f32 { + add(a, -b) + } + + #[bootrom_v2] + #[alias = __subdf3vfp] + #[aeabi = __aeabi_dsub] + extern "C" fn __subdf3(a: f64, b: f64) -> f64 { + add(a, -b) + } + + extern "aapcs" fn __aeabi_frsub(a: f32, b: f32) -> f32 { + add(b, -a) + } + + #[bootrom_v2] + extern "aapcs" fn __aeabi_drsub(a: f64, b: f64) -> f64 { + add(b, -a) + } +} diff --git a/embassy-rp/src/float/cmp.rs b/embassy-rp/src/float/cmp.rs new file mode 100644 index 000000000..e540e3918 --- /dev/null +++ b/embassy-rp/src/float/cmp.rs @@ -0,0 +1,201 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/cmp.rs + +use super::Float; +use crate::rom_data; + +trait ROMCmp { + fn rom_cmp(self, b: Self) -> i32; +} + +impl ROMCmp for f32 { + fn rom_cmp(self, b: Self) -> i32 { + rom_data::float_funcs::fcmp(self, b) + } +} + +impl ROMCmp for f64 { + fn rom_cmp(self, b: Self) -> i32 { + rom_data::double_funcs::dcmp(self, b) + } +} + +fn le_abi(a: F, b: F) -> i32 { + if a.is_nan() || b.is_nan() { + 1 + } else { + a.rom_cmp(b) + } +} + +fn ge_abi(a: F, b: F) -> i32 { + if a.is_nan() || b.is_nan() { + -1 + } else { + a.rom_cmp(b) + } +} + +intrinsics! { + #[slower_than_default] + #[bootrom_v2] + #[alias = __eqsf2, __ltsf2, __nesf2] + extern "C" fn __lesf2(a: f32, b: f32) -> i32 { + le_abi(a, b) + } + + #[slower_than_default] + #[bootrom_v2] + #[alias = __eqdf2, __ltdf2, __nedf2] + extern "C" fn __ledf2(a: f64, b: f64) -> i32 { + le_abi(a, b) + } + + #[slower_than_default] + #[bootrom_v2] + #[alias = __gtsf2] + extern "C" fn __gesf2(a: f32, b: f32) -> i32 { + ge_abi(a, b) + } + + #[slower_than_default] + #[bootrom_v2] + #[alias = __gtdf2] + extern "C" fn __gedf2(a: f64, b: f64) -> i32 { + ge_abi(a, b) + } + + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 { + (le_abi(a, b) == 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 { + (le_abi(a, b) == 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gesf2vfp(a: f32, b: f32) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gedf2vfp(a: f64, b: f64) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gtsf2vfp(a: f32, b: f32) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gtdf2vfp(a: f64, b: f64) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __ltsf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __ltdf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __lesf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __ledf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __nesf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) != 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __nedf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) != 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __eqsf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) == 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __eqdf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) == 0) as i32 + } +} diff --git a/embassy-rp/src/float/conv.rs b/embassy-rp/src/float/conv.rs new file mode 100644 index 000000000..021826e28 --- /dev/null +++ b/embassy-rp/src/float/conv.rs @@ -0,0 +1,157 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs + +use super::Float; +use crate::rom_data; + +// Some of these are also not connected in the Pico SDK. This is probably +// because the ROM version actually does a fixed point conversion, just with +// the fractional width set to zero. + +intrinsics! { + // Not connected in the Pico SDK + #[slower_than_default] + #[aeabi = __aeabi_i2f] + extern "C" fn __floatsisf(i: i32) -> f32 { + rom_data::float_funcs::int_to_float(i) + } + + // Not connected in the Pico SDK + #[slower_than_default] + #[aeabi = __aeabi_i2d] + extern "C" fn __floatsidf(i: i32) -> f64 { + rom_data::double_funcs::int_to_double(i) + } + + // Questionable gain + #[aeabi = __aeabi_l2f] + extern "C" fn __floatdisf(i: i64) -> f32 { + rom_data::float_funcs::int64_to_float(i) + } + + #[bootrom_v2] + #[aeabi = __aeabi_l2d] + extern "C" fn __floatdidf(i: i64) -> f64 { + rom_data::double_funcs::int64_to_double(i) + } + + // Not connected in the Pico SDK + #[slower_than_default] + #[aeabi = __aeabi_ui2f] + extern "C" fn __floatunsisf(i: u32) -> f32 { + rom_data::float_funcs::uint_to_float(i) + } + + // Questionable gain + #[bootrom_v2] + #[aeabi = __aeabi_ui2d] + extern "C" fn __floatunsidf(i: u32) -> f64 { + rom_data::double_funcs::uint_to_double(i) + } + + // Questionable gain + #[bootrom_v2] + #[aeabi = __aeabi_ul2f] + extern "C" fn __floatundisf(i: u64) -> f32 { + rom_data::float_funcs::uint64_to_float(i) + } + + #[bootrom_v2] + #[aeabi = __aeabi_ul2d] + extern "C" fn __floatundidf(i: u64) -> f64 { + rom_data::double_funcs::uint64_to_double(i) + } + + + // The Pico SDK does some optimization here (e.x. fast paths for zero and + // one), but we can just directly connect it. + #[aeabi = __aeabi_f2iz] + extern "C" fn __fixsfsi(f: f32) -> i32 { + rom_data::float_funcs::float_to_int(f) + } + + #[bootrom_v2] + #[aeabi = __aeabi_f2lz] + extern "C" fn __fixsfdi(f: f32) -> i64 { + rom_data::float_funcs::float_to_int64(f) + } + + // Not connected in the Pico SDK + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_d2iz] + extern "C" fn __fixdfsi(f: f64) -> i32 { + rom_data::double_funcs::double_to_int(f) + } + + // Like with the 32 bit version, there's optimization that we just + // skip. + #[bootrom_v2] + #[aeabi = __aeabi_d2lz] + extern "C" fn __fixdfdi(f: f64) -> i64 { + rom_data::double_funcs::double_to_int64(f) + } + + #[slower_than_default] + #[aeabi = __aeabi_f2uiz] + extern "C" fn __fixunssfsi(f: f32) -> u32 { + rom_data::float_funcs::float_to_uint(f) + } + + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_f2ulz] + extern "C" fn __fixunssfdi(f: f32) -> u64 { + rom_data::float_funcs::float_to_uint64(f) + } + + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_d2uiz] + extern "C" fn __fixunsdfsi(f: f64) -> u32 { + rom_data::double_funcs::double_to_uint(f) + } + + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_d2ulz] + extern "C" fn __fixunsdfdi(f: f64) -> u64 { + rom_data::double_funcs::double_to_uint64(f) + } + + #[bootrom_v2] + #[alias = __extendsfdf2vfp] + #[aeabi = __aeabi_f2d] + extern "C" fn __extendsfdf2(f: f32) -> f64 { + if f.is_not_finite() { + return f64::from_repr( + // Not finite + f64::EXPONENT_MASK | + // Preserve NaN or inf + ((f.repr() & f32::SIGNIFICAND_MASK) as u64) | + // Preserve sign + ((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS) + ); + } + rom_data::float_funcs::float_to_double(f) + } + + #[bootrom_v2] + #[alias = __truncdfsf2vfp] + #[aeabi = __aeabi_d2f] + extern "C" fn __truncdfsf2(f: f64) -> f32 { + if f.is_not_finite() { + let mut repr: u32 = + // Not finite + f32::EXPONENT_MASK | + // Preserve sign + ((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32; + // Set NaN + if (f.repr() & f64::SIGNIFICAND_MASK) != 0 { + repr |= 1; + } + return f32::from_repr(repr); + } + rom_data::double_funcs::double_to_float(f) + } +} diff --git a/embassy-rp/src/float/div.rs b/embassy-rp/src/float/div.rs new file mode 100644 index 000000000..094dec446 --- /dev/null +++ b/embassy-rp/src/float/div.rs @@ -0,0 +1,141 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs + +use super::Float; +use crate::rom_data; + +// Make sure this stays as a separate call, because when it's inlined the +// compiler will move the save of the registers used to contain the divider +// state into the function prologue. That save and restore (push/pop) takes +// longer than the actual division, so doing it in the common case where +// they are not required wastes a lot of time. +#[inline(never)] +#[cold] +fn save_divider_and_call(f: F) -> R +where + F: FnOnce() -> R, +{ + let sio = rp_pac::SIO; + + unsafe { + // Since we can't save the signed-ness of the calculation, we have to make + // sure that there's at least an 8 cycle delay before we read the result. + // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads. + // Since we can't be sure the Rust implementation will optimize to the same, + // just use an explicit wait. + while !sio.div().csr().read().ready() {} + + // Read the quotient last, since that's what clears the dirty flag + let dividend = sio.div().udividend().read(); + let divisor = sio.div().udivisor().read(); + let remainder = sio.div().remainder().read(); + let quotient = sio.div().quotient().read(); + + // If we get interrupted here (before a write sets the DIRTY flag) its fine, since + // we have the full state, so the interruptor doesn't have to restore it. Once the + // write happens and the DIRTY flag is set, the interruptor becomes responsible for + // restoring our state. + let result = f(); + + // If we are interrupted here, then the interruptor will start an incorrect calculation + // using a wrong divisor, but we'll restore the divisor and result ourselves correctly. + // This sets DIRTY, so any interruptor will save the state. + sio.div().udividend().write_value(dividend); + // If we are interrupted here, the the interruptor may start the calculation using + // incorrectly signed inputs, but we'll restore the result ourselves. + // This sets DIRTY, so any interruptor will save the state. + sio.div().udivisor().write_value(divisor); + // If we are interrupted here, the interruptor will have restored everything but the + // quotient may be wrongly signed. If the calculation started by the above writes is + // still ongoing it is stopped, so it won't replace the result we're restoring. + // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state. + sio.div().remainder().write_value(remainder); + // State fully restored after the quotient write. This sets both DIRTY and READY, so + // whatever we may have interrupted can read the result. + sio.div().quotient().write_value(quotient); + + result + } +} + +fn save_divider(f: F) -> R +where + F: FnOnce() -> R, +{ + let sio = rp_pac::SIO; + if unsafe { !sio.div().csr().read().dirty() } { + // Not dirty, so nothing is waiting for the calculation. So we can just + // issue it directly without a save/restore. + f() + } else { + save_divider_and_call(f) + } +} + +trait ROMDiv { + fn rom_div(self, b: Self) -> Self; +} + +impl ROMDiv for f32 { + fn rom_div(self, b: Self) -> Self { + // ROM implementation uses the hardware divider, so we have to save it + save_divider(|| rom_data::float_funcs::fdiv(self, b)) + } +} + +impl ROMDiv for f64 { + fn rom_div(self, b: Self) -> Self { + // ROM implementation uses the hardware divider, so we have to save it + save_divider(|| rom_data::double_funcs::ddiv(self, b)) + } +} + +fn div(a: F, b: F) -> F { + if a.is_not_finite() { + if b.is_not_finite() { + // inf/NaN / inf/NaN = NaN + return F::NAN; + } + + if b.is_zero() { + // inf/NaN / 0 = NaN + return F::NAN; + } + + return if b.is_sign_negative() { + // [+/-]inf/NaN / (-X) = [-/+]inf/NaN + a.negate() + } else { + // [-]inf/NaN / X = [-]inf/NaN + a + }; + } + + if b.is_nan() { + // X / NaN = NaN + return b; + } + + // ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only + // need to catch 0 / 0 + if b.is_zero() && a.is_zero() { + return F::NAN; + } + + a.rom_div(b) +} + +intrinsics! { + #[alias = __divsf3vfp] + #[aeabi = __aeabi_fdiv] + extern "C" fn __divsf3(a: f32, b: f32) -> f32 { + div(a, b) + } + + #[bootrom_v2] + #[alias = __divdf3vfp] + #[aeabi = __aeabi_ddiv] + extern "C" fn __divdf3(a: f64, b: f64) -> f64 { + div(a, b) + } +} diff --git a/embassy-rp/src/float/functions.rs b/embassy-rp/src/float/functions.rs new file mode 100644 index 000000000..de29ce336 --- /dev/null +++ b/embassy-rp/src/float/functions.rs @@ -0,0 +1,239 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/functions.rs + +use crate::float::{Float, Int}; +use crate::rom_data; + +trait ROMFunctions { + fn sqrt(self) -> Self; + fn ln(self) -> Self; + fn exp(self) -> Self; + fn sin(self) -> Self; + fn cos(self) -> Self; + fn tan(self) -> Self; + fn atan2(self, y: Self) -> Self; + + fn to_trig_range(self) -> Self; +} + +impl ROMFunctions for f32 { + fn sqrt(self) -> Self { + rom_data::float_funcs::fsqrt(self) + } + + fn ln(self) -> Self { + rom_data::float_funcs::fln(self) + } + + fn exp(self) -> Self { + rom_data::float_funcs::fexp(self) + } + + fn sin(self) -> Self { + rom_data::float_funcs::fsin(self) + } + + fn cos(self) -> Self { + rom_data::float_funcs::fcos(self) + } + + fn tan(self) -> Self { + rom_data::float_funcs::ftan(self) + } + + fn atan2(self, y: Self) -> Self { + rom_data::float_funcs::fatan2(self, y) + } + + fn to_trig_range(self) -> Self { + // -128 < X < 128, logic from the Pico SDK + let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS; + if exponent < 134 { + self + } else { + self % (core::f32::consts::PI * 2.0) + } + } +} + +impl ROMFunctions for f64 { + fn sqrt(self) -> Self { + rom_data::double_funcs::dsqrt(self) + } + + fn ln(self) -> Self { + rom_data::double_funcs::dln(self) + } + + fn exp(self) -> Self { + rom_data::double_funcs::dexp(self) + } + + fn sin(self) -> Self { + rom_data::double_funcs::dsin(self) + } + + fn cos(self) -> Self { + rom_data::double_funcs::dcos(self) + } + fn tan(self) -> Self { + rom_data::double_funcs::dtan(self) + } + + fn atan2(self, y: Self) -> Self { + rom_data::double_funcs::datan2(self, y) + } + + fn to_trig_range(self) -> Self { + // -1024 < X < 1024, logic from the Pico SDK + let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS; + if exponent < 1033 { + self + } else { + self % (core::f64::consts::PI * 2.0) + } + } +} + +fn is_negative_nonzero_or_nan(f: F) -> bool { + let repr = f.repr(); + if (repr & F::SIGN_MASK) != F::Int::ZERO { + // Negative, so anything other than exactly zero + return (repr & (!F::SIGN_MASK)) != F::Int::ZERO; + } + // NaN + (repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK +} + +fn sqrt(f: F) -> F { + if is_negative_nonzero_or_nan(f) { + F::NAN + } else { + f.sqrt() + } +} + +fn ln(f: F) -> F { + if is_negative_nonzero_or_nan(f) { + F::NAN + } else { + f.ln() + } +} + +fn exp(f: F) -> F { + if f.is_nan() { + F::NAN + } else { + f.exp() + } +} + +fn sin(f: F) -> F { + if f.is_not_finite() { + F::NAN + } else { + f.to_trig_range().sin() + } +} + +fn cos(f: F) -> F { + if f.is_not_finite() { + F::NAN + } else { + f.to_trig_range().cos() + } +} + +fn tan(f: F) -> F { + if f.is_not_finite() { + F::NAN + } else { + f.to_trig_range().tan() + } +} + +fn atan2(x: F, y: F) -> F { + if x.is_nan() || y.is_nan() { + F::NAN + } else { + x.to_trig_range().atan2(y) + } +} + +// Name collisions +mod intrinsics { + intrinsics! { + extern "C" fn sqrtf(f: f32) -> f32 { + super::sqrt(f) + } + + #[bootrom_v2] + extern "C" fn sqrt(f: f64) -> f64 { + super::sqrt(f) + } + + extern "C" fn logf(f: f32) -> f32 { + super::ln(f) + } + + #[bootrom_v2] + extern "C" fn log(f: f64) -> f64 { + super::ln(f) + } + + extern "C" fn expf(f: f32) -> f32 { + super::exp(f) + } + + #[bootrom_v2] + extern "C" fn exp(f: f64) -> f64 { + super::exp(f) + } + + #[slower_than_default] + extern "C" fn sinf(f: f32) -> f32 { + super::sin(f) + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn sin(f: f64) -> f64 { + super::sin(f) + } + + #[slower_than_default] + extern "C" fn cosf(f: f32) -> f32 { + super::cos(f) + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn cos(f: f64) -> f64 { + super::cos(f) + } + + #[slower_than_default] + extern "C" fn tanf(f: f32) -> f32 { + super::tan(f) + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn tan(f: f64) -> f64 { + super::tan(f) + } + + // Questionable gain + #[bootrom_v2] + extern "C" fn atan2f(a: f32, b: f32) -> f32 { + super::atan2(a, b) + } + + // Questionable gain + #[bootrom_v2] + extern "C" fn atan2(a: f64, b: f64) -> f64 { + super::atan2(a, b) + } + } +} diff --git a/embassy-rp/src/float/mod.rs b/embassy-rp/src/float/mod.rs new file mode 100644 index 000000000..945afff90 --- /dev/null +++ b/embassy-rp/src/float/mod.rs @@ -0,0 +1,149 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs + +use core::ops; + +// Borrowed and simplified from compiler-builtins so we can use bit ops +// on floating point without macro soup. +pub(crate) trait Int: + Copy + + core::fmt::Debug + + PartialEq + + PartialOrd + + ops::AddAssign + + ops::SubAssign + + ops::BitAndAssign + + ops::BitOrAssign + + ops::BitXorAssign + + ops::ShlAssign + + ops::ShrAssign + + ops::Add + + ops::Sub + + ops::Div + + ops::Shl + + ops::Shr + + ops::BitOr + + ops::BitXor + + ops::BitAnd + + ops::Not +{ + const ZERO: Self; +} + +macro_rules! int_impl { + ($ty:ty) => { + impl Int for $ty { + const ZERO: Self = 0; + } + }; +} + +int_impl!(u32); +int_impl!(u64); + +pub(crate) trait Float: + Copy + + core::fmt::Debug + + PartialEq + + PartialOrd + + ops::AddAssign + + ops::MulAssign + + ops::Add + + ops::Sub + + ops::Div + + ops::Rem +{ + /// A uint of the same with as the float + type Int: Int; + + /// NaN representation for the float + const NAN: Self; + + /// The bitwidth of the float type + const BITS: u32; + + /// The bitwidth of the significand + const SIGNIFICAND_BITS: u32; + + /// A mask for the sign bit + const SIGN_MASK: Self::Int; + + /// A mask for the significand + const SIGNIFICAND_MASK: Self::Int; + + /// A mask for the exponent + const EXPONENT_MASK: Self::Int; + + /// Returns `self` transmuted to `Self::Int` + fn repr(self) -> Self::Int; + + /// Returns a `Self::Int` transmuted back to `Self` + fn from_repr(a: Self::Int) -> Self; + + /// Return a sign swapped `self` + fn negate(self) -> Self; + + /// Returns true if `self` is either NaN or infinity + fn is_not_finite(self) -> bool { + (self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK + } + + /// Returns true if `self` is infinity + fn is_infinity(self) -> bool { + (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK + } + + /// Returns true if `self is NaN + fn is_nan(self) -> bool { + (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK + } + + /// Returns true if `self` is negative + fn is_sign_negative(self) -> bool { + (self.repr() & Self::SIGN_MASK) != Self::Int::ZERO + } + + /// Returns true if `self` is zero (either sign) + fn is_zero(self) -> bool { + (self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO + } +} + +macro_rules! float_impl { + ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => { + impl Float for $ty { + type Int = $ity; + + const NAN: Self = <$ty>::NAN; + + const BITS: u32 = $bits; + const SIGNIFICAND_BITS: u32 = $significand_bits; + + const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1); + const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1; + const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK); + + fn repr(self) -> Self::Int { + self.to_bits() + } + + fn from_repr(a: Self::Int) -> Self { + Self::from_bits(a) + } + + fn negate(self) -> Self { + -self + } + } + }; +} + +float_impl!(f32, u32, 32, 23); +float_impl!(f64, u64, 64, 52); + +mod add_sub; +mod cmp; +mod conv; +mod div; +mod functions; +mod mul; diff --git a/embassy-rp/src/float/mul.rs b/embassy-rp/src/float/mul.rs new file mode 100644 index 000000000..ceb0210e3 --- /dev/null +++ b/embassy-rp/src/float/mul.rs @@ -0,0 +1,70 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mul.rs + +use super::Float; +use crate::rom_data; + +trait ROMMul { + fn rom_mul(self, b: Self) -> Self; +} + +impl ROMMul for f32 { + fn rom_mul(self, b: Self) -> Self { + rom_data::float_funcs::fmul(self, b) + } +} + +impl ROMMul for f64 { + fn rom_mul(self, b: Self) -> Self { + rom_data::double_funcs::dmul(self, b) + } +} + +fn mul(a: F, b: F) -> F { + if a.is_not_finite() { + if b.is_zero() { + // [-]inf/NaN * 0 = NaN + return F::NAN; + } + + return if b.is_sign_negative() { + // [+/-]inf/NaN * (-X) = [-/+]inf/NaN + a.negate() + } else { + // [-]inf/NaN * X = [-]inf/NaN + a + }; + } + + if b.is_not_finite() { + if a.is_zero() { + // 0 * [-]inf/NaN = NaN + return F::NAN; + } + + return if b.is_sign_negative() { + // (-X) * [+/-]inf/NaN = [-/+]inf/NaN + b.negate() + } else { + // X * [-]inf/NaN = [-]inf/NaN + b + }; + } + + a.rom_mul(b) +} + +intrinsics! { + #[alias = __mulsf3vfp] + #[aeabi = __aeabi_fmul] + extern "C" fn __mulsf3(a: f32, b: f32) -> f32 { + mul(a, b) + } + + #[bootrom_v2] + #[alias = __muldf3vfp] + #[aeabi = __aeabi_dmul] + extern "C" fn __muldf3(a: f64, b: f64) -> f64 { + mul(a, b) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 1d63f6c2e..3841bb83a 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -12,6 +12,7 @@ mod intrinsics; pub mod adc; pub mod dma; +mod float; pub mod gpio; pub mod i2c; pub mod interrupt; diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index 9611db3a0..e1744c703 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -1,6 +1,8 @@ [unstable] -build-std = ["core"] -build-std-features = ["panic_immediate_abort"] +# enabling these breaks the float tests during linking, with intrinsics +# duplicated between embassy-rp and compilter_builtins +#build-std = ["core"] +#build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] #runner = "teleprobe client run --target rpi-pico --elf" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 36ff735ec..6778f53d7 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs new file mode 100644 index 000000000..6715271e6 --- /dev/null +++ b/tests/rp/src/bin/float.rs @@ -0,0 +1,53 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::pac; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + embassy_rp::init(Default::default()); + info!("Hello World!"); + + const PI_F: f32 = 3.1415926535f32; + const PI_D: f64 = 3.14159265358979323846f64; + + unsafe { + pac::BUSCTRL + .perfsel(0) + .write(|r| r.set_perfsel(pac::busctrl::vals::Perfsel::ROM)); + } + + for i in 0..=360 { + let rad_f = (i as f32) * PI_F / 180.0; + info!( + "{}° float: {=f32} / {=f32} / {=f32} / {=f32}", + i, + rad_f, + rad_f - PI_F, + rad_f + PI_F, + rad_f % PI_F + ); + let rad_d = (i as f64) * PI_D / 180.0; + info!( + "{}° double: {=f64} / {=f64} / {=f64} / {=f64}", + i, + rad_d, + rad_d - PI_D, + rad_d + PI_D, + rad_d % PI_D + ); + Timer::after(Duration::from_millis(10)).await; + } + + let rom_accesses = unsafe { pac::BUSCTRL.perfctr(0).read().perfctr() }; + // every float operation used here uses at least 10 cycles + defmt::assert!(rom_accesses >= 360 * 12 * 10); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 64b80c2e4d390b16a384e197f1c7ed2a9a6fc946 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 19 Apr 2023 16:16:44 -0500 Subject: [PATCH 0921/1575] stm32/i2c: ignore wakes without interrupt --- embassy-stm32/src/i2c/v2.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 44237b890..92fc05591 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -485,6 +485,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { poll_fn(|cx| { state.waker.register(cx.waker()); + let isr = unsafe { T::regs().isr().read() }; if remaining_len == total_len { // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers if first_slice { @@ -503,6 +504,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|w| w.set_tcie(true)); } } + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; } else if remaining_len == 0 { return Poll::Ready(Ok(())); } else { @@ -575,6 +579,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { poll_fn(|cx| { state.waker.register(cx.waker()); + + let isr = unsafe { T::regs().isr().read() }; if remaining_len == total_len { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { @@ -587,6 +593,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { &check_timeout, )?; } + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; } else if remaining_len == 0 { return Poll::Ready(Ok(())); } else { From 837cdacd16eaf10c70758f413867c251e68460d0 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 19 Apr 2023 21:56:18 +0200 Subject: [PATCH 0922/1575] rp: optimize rom-func-cache for runtime storing a full function pointer initialized to a resolver trampoline lets us avoid the runtime cost of checking whether we need to do the initialization. --- embassy-rp/src/rom_data.rs | 117 +++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 757a27114..805c1f09f 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -56,50 +56,11 @@ macro_rules! declare_rom_function { fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty $lookup:block ) => { - #[doc = r"Additional access for the `"] - #[doc = stringify!($name)] - #[doc = r"` ROM function."] - pub mod $name { - /// Retrieve a function pointer. - #[cfg(not(feature = "rom-func-cache"))] - pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { - let p: *const u32 = $lookup; - unsafe { - let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); - func - } - } - - /// Retrieve a function pointer. - #[cfg(feature = "rom-func-cache")] - pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { - use core::sync::atomic::{AtomicU16, Ordering}; - - // All pointers in the ROM fit in 16 bits, so we don't need a - // full width word to store the cached value. - static CACHED_PTR: AtomicU16 = AtomicU16::new(0); - // This is safe because the lookup will always resolve - // to the same value. So even if an interrupt or another - // core starts at the same time, it just repeats some - // work and eventually writes back the correct value. - let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { - 0 => { - let raw: *const u32 = $lookup; - CACHED_PTR.store(raw as u16, Ordering::Relaxed); - raw - }, - val => val as *const u32, - }; - unsafe { - let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); - func - } - } - } - - $(#[$outer])* - pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { - $name::ptr()($($argname),*) + declare_rom_function!{ + __internal , + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret + $lookup } }; @@ -107,6 +68,21 @@ macro_rules! declare_rom_function { $(#[$outer:meta])* unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty $lookup:block + ) => { + declare_rom_function!{ + __internal unsafe , + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret + $lookup + } + }; + + ( + __internal + $( $maybe_unsafe:ident )? , + $(#[$outer:meta])* + fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block ) => { #[doc = r"Additional access for the `"] #[doc = stringify!($name)] @@ -114,43 +90,58 @@ macro_rules! declare_rom_function { pub mod $name { /// Retrieve a function pointer. #[cfg(not(feature = "rom-func-cache"))] - pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { let p: *const u32 = $lookup; unsafe { - let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret + = core::mem::transmute(p); func } } + #[cfg(feature = "rom-func-cache")] + // unlike rp2040-hal we store a full word, containing the full function pointer. + // rp2040-hal saves two bytes by storing only the rom offset, at the cost of + // having to do an indirection and an atomic operation on every rom call. + static mut CACHE: $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret + = trampoline; + + #[cfg(feature = "rom-func-cache")] + $( $maybe_unsafe )? extern "C" fn trampoline( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{compiler_fence, Ordering}; + + let p: *const u32 = $lookup; + #[allow(unused_unsafe)] + unsafe { + CACHE = core::mem::transmute(p); + compiler_fence(Ordering::Release); + CACHE($($argname),*) + } + } + /// Retrieve a function pointer. #[cfg(feature = "rom-func-cache")] - pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { - use core::sync::atomic::{AtomicU16, Ordering}; + pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{compiler_fence, Ordering}; - // All pointers in the ROM fit in 16 bits, so we don't need a - // full width word to store the cached value. - static CACHED_PTR: AtomicU16 = AtomicU16::new(0); // This is safe because the lookup will always resolve // to the same value. So even if an interrupt or another // core starts at the same time, it just repeats some // work and eventually writes back the correct value. - let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { - 0 => { - let raw: *const u32 = $lookup; - CACHED_PTR.store(raw as u16, Ordering::Relaxed); - raw - }, - val => val as *const u32, - }; + // + // We easily get away with using only compiler fences here + // because RP2040 SRAM is not cached. If it were we'd need + // to make sure updates propagate quickly, or just take the + // hit and let each core resolve every function once. + compiler_fence(Ordering::Acquire); unsafe { - let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); - func + CACHE } } } $(#[$outer])* - pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { + pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret { $name::ptr()($($argname),*) } }; @@ -369,6 +360,7 @@ pub fn fplib_start() -> *const u8 { } /// See Table 180 in the RP2040 datasheet for the contents of this table. +#[cfg_attr(feature = "rom-func-cache", inline(never))] pub fn soft_float_table() -> *const usize { rom_table_lookup(DATA_TABLE, *b"SF") } @@ -379,6 +371,7 @@ pub fn fplib_end() -> *const u8 { } /// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. +#[cfg_attr(feature = "rom-func-cache", inline(never))] pub fn soft_double_table() -> *const usize { if rom_version_number() < 2 { panic!( From 5de6bb3adfa5a82ec764f81409bc60e42fc26d20 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Thu, 20 Apr 2023 09:19:26 +0300 Subject: [PATCH 0923/1575] feat: add embassy-boot-rp to the doc builder Signed-off-by: Lachezar Lechev --- .github/workflows/doc.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index cb222803b..411b7589f 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -61,6 +61,7 @@ jobs: mkdir crates builder ./embassy-boot/boot crates/embassy-boot/git.zup builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup + builder ./embassy-boot/rp crates/embassy-boot-rp/git.zup builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup @@ -84,5 +85,3 @@ jobs: echo "${{secrets.KUBECONFIG}}" > ~/.kube/config POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp crates $POD:/data - - \ No newline at end of file From f67eb84ec73c74170de37a15f26cd9d82756d706 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Thu, 20 Apr 2023 09:20:02 +0300 Subject: [PATCH 0924/1575] chore: add embassy-boot-rp to README Signed-off-by: Lachezar Lechev --- embassy-boot/boot/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-boot/boot/README.md b/embassy-boot/boot/README.md index 414405377..56f4a6279 100644 --- a/embassy-boot/boot/README.md +++ b/embassy-boot/boot/README.md @@ -13,6 +13,7 @@ By design, the bootloader does not provide any network capabilities. Networking The bootloader supports different hardware in separate crates: * `embassy-boot-nrf` - for the nRF microcontrollers. +* `embassy-boot-rp` - for the RP2040 microcontrollers. * `embassy-boot-stm32` - for the STM32 microcontrollers. ## Minimum supported Rust version (MSRV) From b153a5b0d764c32eea6a3c312781743af98b3a11 Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 20 Apr 2023 10:04:41 +0200 Subject: [PATCH 0925/1575] embassy-boot: add nightly feature to stm32 and rp as well --- embassy-boot/boot/Cargo.toml | 1 - embassy-boot/nrf/Cargo.toml | 1 - embassy-boot/nrf/src/lib.rs | 1 - embassy-boot/rp/Cargo.toml | 12 +++++++++--- embassy-boot/rp/src/lib.rs | 1 - embassy-boot/stm32/Cargo.toml | 9 +++++++-- embassy-boot/stm32/src/lib.rs | 1 - 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 956b0f987..f641d5e1c 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -45,7 +45,6 @@ default_features = false features = ["rand", "std", "u32_backend"] [features] -default = ["nightly"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index 05ab87896..e46736889 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -28,7 +28,6 @@ cfg-if = "1.0.0" nrf-softdevice-mbr = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-softdevice.git", branch = "master", optional = true } [features] -default = ["nightly"] defmt = [ "dep:defmt", "embassy-boot/defmt", diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 48bbd7e2a..14bea1f79 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; diff --git a/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml index 96024cdda..5147392ce 100644 --- a/embassy-boot/rp/Cargo.toml +++ b/embassy-boot/rp/Cargo.toml @@ -18,14 +18,14 @@ 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-rp = { path = "../../embassy-rp", default-features = false } embassy-boot = { path = "../boot", default-features = false } -embassy-time = { path = "../../embassy-time", features = ["nightly"] } +embassy-time = { path = "../../embassy-time" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" -embedded-storage-async = "0.4.0" +embedded-storage-async = { version = "0.4.0", optional = true } cfg-if = "1.0.0" [features] @@ -40,6 +40,12 @@ log = [ "embassy-rp/log", ] debug = ["defmt-rtt"] +nightly = [ + "dep:embedded-storage-async", + "embassy-boot/nightly", + "embassy-rp/nightly", + "embassy-time/nightly" +] [profile.dev] debug = 2 diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index c3cb22299..0d577f08a 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index 7061063bb..99a6b8e0e 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml @@ -19,12 +19,12 @@ defmt-rtt = { version = "0.4", optional = true } log = { version = "0.4", optional = true } embassy-sync = { path = "../../embassy-sync" } -embassy-stm32 = { path = "../../embassy-stm32", default-features = false, features = ["nightly"] } +embassy-stm32 = { path = "../../embassy-stm32", default-features = false } 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.4.0" +embedded-storage-async = { version = "0.4.0", optional = true } cfg-if = "1.0.0" [features] @@ -39,6 +39,11 @@ log = [ "embassy-stm32/log", ] debug = ["defmt-rtt"] +nightly = [ + "dep:embedded-storage-async", + "embassy-boot/nightly", + "embassy-stm32/nightly" +] [profile.dev] debug = 2 diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 94404697f..88ce1c878 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; From f64d1131b6c57c5bde9c6211d6d465f0bd256066 Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 20 Apr 2023 10:22:44 +0200 Subject: [PATCH 0926/1575] embassy-boot: update ci and examples to use the nightly flag --- ci.sh | 8 ++++---- ci_stable.sh | 4 ++++ examples/boot/application/nrf/Cargo.toml | 4 ++-- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- 11 files changed, 18 insertions(+), 14 deletions(-) diff --git a/ci.sh b/ci.sh index 657975041..4b7d73711 100755 --- a/ci.sh +++ b/ci.sh @@ -69,10 +69,10 @@ cargo batch \ --- 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-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,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/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ - --- 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 embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,nightly \ + --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,nightly \ + --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ + --- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4,nightly \ --- 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 \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ diff --git a/ci_stable.sh b/ci_stable.sh index 18271ee73..55a2f89a0 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -9,6 +9,10 @@ export DEFMT_LOG=trace sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml cargo batch \ + --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ + --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ + --- 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 embassy-executor/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index d6a30602d..88528e763 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -9,8 +9,8 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } -embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot" } -embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf" } +embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] } +embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 62ef42d6b..782ccef2b 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } 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-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = "0.3" diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index e5fb1b01d..881191876 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } +embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index a6ac1cadf..21665553b 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } +embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5b8ee555f..eef8b4138 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } +embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 05ce5c10a..2bcf292bf 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } -embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } +embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 14af99e96..f452db72b 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } +embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 90ae97725..2c94ed3e9 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } +embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 08403a4ec..f9dfb7c76 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } -embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32" } +embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } From 8cd117fd5d692aef695afc46018c6b48f047bcbd Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 20 Apr 2023 10:26:02 +0200 Subject: [PATCH 0927/1575] embassy-boot: update readme MSRV to stable --- embassy-boot/boot/README.md | 2 +- embassy-boot/nrf/README.md | 2 +- embassy-boot/rp/README.md | 2 +- embassy-boot/stm32/README.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-boot/boot/README.md b/embassy-boot/boot/README.md index 414405377..346aa6f58 100644 --- a/embassy-boot/boot/README.md +++ b/embassy-boot/boot/README.md @@ -17,7 +17,7 @@ The bootloader supports different hardware in separate crates: ## Minimum supported Rust version (MSRV) -`embassy-boot` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals. +`embassy-boot` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. ## License diff --git a/embassy-boot/nrf/README.md b/embassy-boot/nrf/README.md index 02f35c0a6..7ce3c7021 100644 --- a/embassy-boot/nrf/README.md +++ b/embassy-boot/nrf/README.md @@ -13,7 +13,7 @@ An adaptation of `embassy-boot` for nRF. ## 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. +`embassy-boot-nrf` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. ## License diff --git a/embassy-boot/rp/README.md b/embassy-boot/rp/README.md index c0c2d85fa..315d655e3 100644 --- a/embassy-boot/rp/README.md +++ b/embassy-boot/rp/README.md @@ -13,7 +13,7 @@ NOTE: The applications using this bootloader should not link with the `link-rp.x ## 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. +`embassy-boot-rp` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. ## License diff --git a/embassy-boot/stm32/README.md b/embassy-boot/stm32/README.md index cb134b534..b4d7ba5a4 100644 --- a/embassy-boot/stm32/README.md +++ b/embassy-boot/stm32/README.md @@ -11,7 +11,7 @@ An adaptation of `embassy-boot` for STM32. ## 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. +`embassy-boot-stm32` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release. ## License From 0e01b28d5ecd56fc2d8b14fa0b90f3ec2b564b0f Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 20 Apr 2023 10:40:40 +0200 Subject: [PATCH 0928/1575] embassy-boot: resolve conflicts --- embassy-boot/boot/Cargo.toml | 1 - embassy-boot/nrf/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 956b0f987..f641d5e1c 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -45,7 +45,6 @@ default_features = false features = ["rand", "std", "u32_backend"] [features] -default = ["nightly"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index 05ab87896..e46736889 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -28,7 +28,6 @@ cfg-if = "1.0.0" nrf-softdevice-mbr = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-softdevice.git", branch = "master", optional = true } [features] -default = ["nightly"] defmt = [ "dep:defmt", "embassy-boot/defmt", From 3bf41e9a06bab1b48b08fbc0b8f10d00b47b936e Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 20 Apr 2023 10:47:40 +0200 Subject: [PATCH 0929/1575] ci: ad nightly flag to embassy-boot tests --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3bfe5ef03..b8af7fcc9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -71,7 +71,7 @@ jobs: - name: Test boot working-directory: ./embassy-boot/boot - run: cargo test && cargo test --features "ed25519-dalek" && cargo test --features "ed25519-salty" + run: cargo test --features nightly && cargo test --features "ed25519-dalek,nightly" && cargo test --features "ed25519-salty,nightly" - name: Test sync working-directory: ./embassy-sync From a73f9474a0dc4af1ebfb22d7960e6c4b3aca81bd Mon Sep 17 00:00:00 2001 From: sander Date: Thu, 20 Apr 2023 10:56:59 +0200 Subject: [PATCH 0930/1575] embassy-boot: ensure tests can run on the stable compiler --- .github/workflows/rust.yml | 2 +- embassy-boot/boot/src/firmware_updater.rs | 1 + embassy-boot/boot/src/lib.rs | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b8af7fcc9..79354fe70 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -71,7 +71,7 @@ jobs: - name: Test boot working-directory: ./embassy-boot/boot - run: cargo test --features nightly && cargo test --features "ed25519-dalek,nightly" && cargo test --features "ed25519-salty,nightly" + run: cargo test && cargo test --features nightly && cargo test --features "ed25519-dalek,nightly" && cargo test --features "ed25519-salty,nightly" - name: Test sync working-directory: ./embassy-sync diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 1f2a6d24e..92987825f 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -520,6 +520,7 @@ mod tests { use crate::mem_flash::MemFlash; #[test] + #[cfg(feature = "nightly")] fn can_verify_sha1() { const STATE: Partition = Partition::new(0, 4096); const DFU: Partition = Partition::new(65536, 131072); diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index e268d8883..8eb3ba96d 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -83,7 +83,7 @@ mod tests { } #[test] - #[cfg(not(feature = "_verify"))] + #[cfg(all(feature = "nightly", not(feature = "_verify")))] fn test_swap_state() { const STATE: Partition = Partition::new(0, 4096); const ACTIVE: Partition = Partition::new(4096, 61440); @@ -136,7 +136,7 @@ mod tests { } #[test] - #[cfg(not(feature = "_verify"))] + #[cfg(all(feature = "nightly", not(feature = "_verify")))] fn test_separate_flash_active_page_biggest() { const STATE: Partition = Partition::new(2048, 4096); const ACTIVE: Partition = Partition::new(4096, 16384); @@ -173,7 +173,7 @@ mod tests { } #[test] - #[cfg(not(feature = "_verify"))] + #[cfg(all(feature = "nightly", not(feature = "_verify")))] fn test_separate_flash_dfu_page_biggest() { const STATE: Partition = Partition::new(2048, 4096); const ACTIVE: Partition = Partition::new(4096, 16384); @@ -212,7 +212,7 @@ mod tests { } #[test] - #[cfg(feature = "_verify")] + #[cfg(all(feature = "nightly", feature = "_verify"))] fn test_verify() { // The following key setup is based on: // https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example From 02c86bca5275328a15376176ff44487ab7655866 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Fri, 21 Apr 2023 01:20:46 -0500 Subject: [PATCH 0931/1575] Add external LoRa physical layer functionality. --- embassy-lora/Cargo.toml | 8 +- embassy-lora/src/iv.rs | 325 ++++++++++++++++++ embassy-lora/src/lib.rs | 3 + embassy-stm32/src/spi/mod.rs | 2 +- embassy-stm32/src/subghz/mod.rs | 2 +- examples/nrf52840/Cargo.toml | 10 +- examples/nrf52840/src/bin/lora_cad.rs | 92 +++++ .../src/bin/lora_p2p_receive_duty_cycle.rs | 124 +++++++ examples/nrf52840/src/bin/lora_p2p_report.rs | 81 ----- examples/rp/Cargo.toml | 6 +- examples/stm32l0/Cargo.toml | 10 +- examples/stm32l0/src/bin/lora_p2p_receive.rs | 120 +++++++ examples/stm32wl/Cargo.toml | 13 +- examples/stm32wl/src/bin/lora_lorawan.rs | 88 +++++ examples/stm32wl/src/bin/lora_p2p_send.rs | 103 ++++++ 15 files changed, 884 insertions(+), 103 deletions(-) create mode 100644 embassy-lora/src/iv.rs create mode 100644 examples/nrf52840/src/bin/lora_cad.rs create mode 100644 examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs delete mode 100644 examples/nrf52840/src/bin/lora_p2p_report.rs create mode 100644 examples/stm32l0/src/bin/lora_p2p_receive.rs create mode 100644 examples/stm32wl/src/bin/lora_lorawan.rs create mode 100644 examples/stm32wl/src/bin/lora_p2p_send.rs diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 50449dd46..20cfaf1bd 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -22,6 +22,7 @@ sx127x = [] stm32wl = ["dep:embassy-stm32"] time = [] defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] +external-lora-phy = ["dep:lora-phy"] [dependencies] @@ -35,8 +36,9 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } 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"] } +embedded-hal = { version = "0.2.6" } bit_field = { version = "0.10" } -lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.2", default-features = false } +lora-phy = { version = "1", path = "../../lora-phy", optional = true } +lorawan-device = { version = "0.9.0", path = "../../rust-lorawan/device", default-features = false, features = ["async"] } +lorawan = { version = "0.7.2", path = "../../rust-lorawan/encoding", default-features = false } diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs new file mode 100644 index 000000000..f81134405 --- /dev/null +++ b/embassy-lora/src/iv.rs @@ -0,0 +1,325 @@ +#[cfg(feature = "stm32wl")] +use embassy_stm32::interrupt::*; +#[cfg(feature = "stm32wl")] +use embassy_stm32::{pac, PeripheralRef}; +#[cfg(feature = "stm32wl")] +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +#[cfg(feature = "stm32wl")] +use embassy_sync::signal::Signal; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal_async::delay::DelayUs; +use embedded_hal_async::digital::Wait; +use lora_phy::mod_params::RadioError::*; +use lora_phy::mod_params::{BoardType, RadioError}; +use lora_phy::mod_traits::InterfaceVariant; + +#[cfg(feature = "stm32wl")] +static IRQ_SIGNAL: Signal = Signal::new(); + +#[cfg(feature = "stm32wl")] +/// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination +pub struct Stm32wlInterfaceVariant<'a, CTRL> { + board_type: BoardType, + irq: PeripheralRef<'a, SUBGHZ_RADIO>, + rf_switch_rx: Option, + rf_switch_tx: Option, +} + +#[cfg(feature = "stm32wl")] +impl<'a, CTRL> Stm32wlInterfaceVariant<'a, CTRL> +where + CTRL: OutputPin, +{ + /// Create an InterfaceVariant instance for an stm32wl/sx1262 combination + pub fn new( + irq: PeripheralRef<'a, SUBGHZ_RADIO>, + rf_switch_rx: Option, + rf_switch_tx: Option, + ) -> Result { + irq.disable(); + irq.set_handler(Self::on_interrupt); + Ok(Self { + board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board + irq, + rf_switch_rx, + rf_switch_tx, + }) + } + + fn on_interrupt(_: *mut ()) { + unsafe { SUBGHZ_RADIO::steal() }.disable(); + IRQ_SIGNAL.signal(()); + } +} + +#[cfg(feature = "stm32wl")] +impl InterfaceVariant for Stm32wlInterfaceVariant<'_, CTRL> +where + CTRL: OutputPin, +{ + fn set_board_type(&mut self, board_type: BoardType) { + self.board_type = board_type; + } + async fn set_nss_low(&mut self) -> Result<(), RadioError> { + let pwr = pac::PWR; + unsafe { + pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW)); + } + Ok(()) + } + async fn set_nss_high(&mut self) -> Result<(), RadioError> { + let pwr = pac::PWR; + unsafe { + pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH)); + } + Ok(()) + } + async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> { + let rcc = pac::RCC; + unsafe { + rcc.csr().modify(|w| w.set_rfrst(true)); + rcc.csr().modify(|w| w.set_rfrst(false)); + } + Ok(()) + } + async fn wait_on_busy(&mut self) -> Result<(), RadioError> { + let pwr = pac::PWR; + while unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } {} + Ok(()) + } + + async fn await_irq(&mut self) -> Result<(), RadioError> { + self.irq.enable(); + IRQ_SIGNAL.wait().await; + Ok(()) + } + + async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_tx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?, + None => (), + }; + match &mut self.rf_switch_rx { + Some(pin) => pin.set_high().map_err(|_| RfSwitchRx), + None => Ok(()), + } + } + async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_rx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, + None => (), + }; + match &mut self.rf_switch_tx { + Some(pin) => pin.set_high().map_err(|_| RfSwitchTx), + None => Ok(()), + } + } + async fn disable_rf_switch(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_rx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, + None => (), + }; + match &mut self.rf_switch_tx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchTx), + None => Ok(()), + } + } +} + +/// Base for the InterfaceVariant implementation for an stm32l0/sx1276 combination +pub struct Stm32l0InterfaceVariant { + board_type: BoardType, + nss: CTRL, + reset: CTRL, + irq: WAIT, + rf_switch_rx: Option, + rf_switch_tx: Option, +} + +impl Stm32l0InterfaceVariant +where + CTRL: OutputPin, + WAIT: Wait, +{ + /// Create an InterfaceVariant instance for an stm32l0/sx1276 combination + pub fn new( + nss: CTRL, + reset: CTRL, + irq: WAIT, + rf_switch_rx: Option, + rf_switch_tx: Option, + ) -> Result { + Ok(Self { + board_type: BoardType::Stm32l0Sx1276, // updated when associated with a specific LoRa board + nss, + reset, + irq, + rf_switch_rx, + rf_switch_tx, + }) + } +} + +impl InterfaceVariant for Stm32l0InterfaceVariant +where + CTRL: OutputPin, + WAIT: Wait, +{ + fn set_board_type(&mut self, board_type: BoardType) { + self.board_type = board_type; + } + async fn set_nss_low(&mut self) -> Result<(), RadioError> { + self.nss.set_low().map_err(|_| NSS) + } + async fn set_nss_high(&mut self) -> Result<(), RadioError> { + self.nss.set_high().map_err(|_| NSS) + } + async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> { + delay.delay_ms(10).await; + self.reset.set_low().map_err(|_| Reset)?; + delay.delay_ms(10).await; + self.reset.set_high().map_err(|_| Reset)?; + delay.delay_ms(10).await; + Ok(()) + } + async fn wait_on_busy(&mut self) -> Result<(), RadioError> { + Ok(()) + } + async fn await_irq(&mut self) -> Result<(), RadioError> { + self.irq.wait_for_high().await.map_err(|_| Irq) + } + + async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_tx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?, + None => (), + }; + match &mut self.rf_switch_rx { + Some(pin) => pin.set_high().map_err(|_| RfSwitchRx), + None => Ok(()), + } + } + async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_rx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, + None => (), + }; + match &mut self.rf_switch_tx { + Some(pin) => pin.set_high().map_err(|_| RfSwitchTx), + None => Ok(()), + } + } + async fn disable_rf_switch(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_rx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, + None => (), + }; + match &mut self.rf_switch_tx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchTx), + None => Ok(()), + } + } +} + +/// Base for the InterfaceVariant implementation for a generic Sx126x LoRa board +pub struct GenericSx126xInterfaceVariant { + board_type: BoardType, + nss: CTRL, + reset: CTRL, + dio1: WAIT, + busy: WAIT, + rf_switch_rx: Option, + rf_switch_tx: Option, +} + +impl GenericSx126xInterfaceVariant +where + CTRL: OutputPin, + WAIT: Wait, +{ + /// Create an InterfaceVariant instance for an nrf52840/sx1262 combination + pub fn new( + nss: CTRL, + reset: CTRL, + dio1: WAIT, + busy: WAIT, + rf_switch_rx: Option, + rf_switch_tx: Option, + ) -> Result { + Ok(Self { + board_type: BoardType::Rak4631Sx1262, // updated when associated with a specific LoRa board + nss, + reset, + dio1, + busy, + rf_switch_rx, + rf_switch_tx, + }) + } +} + +impl InterfaceVariant for GenericSx126xInterfaceVariant +where + CTRL: OutputPin, + WAIT: Wait, +{ + fn set_board_type(&mut self, board_type: BoardType) { + self.board_type = board_type; + } + async fn set_nss_low(&mut self) -> Result<(), RadioError> { + self.nss.set_low().map_err(|_| NSS) + } + async fn set_nss_high(&mut self) -> Result<(), RadioError> { + self.nss.set_high().map_err(|_| NSS) + } + async fn reset(&mut self, delay: &mut impl DelayUs) -> Result<(), RadioError> { + delay.delay_ms(10).await; + self.reset.set_low().map_err(|_| Reset)?; + delay.delay_ms(20).await; + self.reset.set_high().map_err(|_| Reset)?; + delay.delay_ms(10).await; + Ok(()) + } + async fn wait_on_busy(&mut self) -> Result<(), RadioError> { + self.busy.wait_for_low().await.map_err(|_| Busy) + } + async fn await_irq(&mut self) -> Result<(), RadioError> { + if self.board_type != BoardType::RpPicoWaveshareSx1262 { + self.dio1.wait_for_high().await.map_err(|_| DIO1)?; + } else { + self.dio1.wait_for_rising_edge().await.map_err(|_| DIO1)?; + } + Ok(()) + } + + async fn enable_rf_switch_rx(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_tx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchTx)?, + None => (), + }; + match &mut self.rf_switch_rx { + Some(pin) => pin.set_high().map_err(|_| RfSwitchRx), + None => Ok(()), + } + } + async fn enable_rf_switch_tx(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_rx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, + None => (), + }; + match &mut self.rf_switch_tx { + Some(pin) => pin.set_high().map_err(|_| RfSwitchTx), + None => Ok(()), + } + } + async fn disable_rf_switch(&mut self) -> Result<(), RadioError> { + match &mut self.rf_switch_rx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchRx)?, + None => (), + }; + match &mut self.rf_switch_tx { + Some(pin) => pin.set_low().map_err(|_| RfSwitchTx), + None => Ok(()), + } + } +} diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 5c919cbb6..d8bc3cd98 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -5,6 +5,9 @@ //! crate's async LoRaWAN MAC implementation. pub(crate) mod fmt; +#[cfg(feature = "external-lora-phy")] +/// interface variants required by the external lora crate +pub mod iv; #[cfg(feature = "stm32wl")] pub mod stm32wl; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 492d0649a..aefa42435 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -197,7 +197,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { /// Useful for on chip peripherals like SUBGHZ which are hardwired. /// The bus can optionally be exposed externally with `Spi::new()` still. #[allow(dead_code)] - pub(crate) fn new_internal( + pub fn new_subghz( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs index 33398fa1d..cd566ba24 100644 --- a/embassy-stm32/src/subghz/mod.rs +++ b/embassy-stm32/src/subghz/mod.rs @@ -224,7 +224,7 @@ impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { let mut config = SpiConfig::default(); config.mode = MODE_0; config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new_internal(peri, txdma, rxdma, clk, config); + let spi = Spi::new_subghz(peri, txdma, rxdma, clk, config); unsafe { wakeup() }; diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 10c269a76..7a1fe281a 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -13,15 +13,15 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } - -lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } +lora-phy = { version = "1", path = "../../../lora-phy" } +lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true } +lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/nrf52840/src/bin/lora_cad.rs b/examples/nrf52840/src/bin/lora_cad.rs new file mode 100644 index 000000000..8899c1b23 --- /dev/null +++ b/examples/nrf52840/src/bin/lora_cad.rs @@ -0,0 +1,92 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LORA CAD functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M16; + + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); + + let iv = + GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + let mdltn_params = { + match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora.prepare_for_cad(&mdltn_params, true).await { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.cad().await { + Ok(cad_activity_detected) => { + if cad_activity_detected { + info!("cad successful with activity detected") + } else { + info!("cad successful without activity detected") + } + debug_indicator.set_high(); + Timer::after(Duration::from_secs(15)).await; + debug_indicator.set_low(); + } + Err(err) => info!("cad unsuccessful = {}", err), + } +} diff --git a/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs new file mode 100644 index 000000000..d84701742 --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs @@ -0,0 +1,124 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstates LoRa Rx duty cycle functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M16; + + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); + + let iv = + GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + let mut receiving_buffer = [00u8; 100]; + + let mdltn_params = { + match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let rx_pkt_params = { + match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + // See "RM0453 Reference manual STM32WL5x advanced Arm®-based 32-bit MCUs with sub-GHz radio solution" for the best explanation of Rx duty cycle processing. + match lora + .prepare_for_rx( + &mdltn_params, + &rx_pkt_params, + Some(&DutyCycleParams { + rx_time: 300_000, // 300_000 units * 15.625 us/unit = 4.69 s + sleep_time: 200_000, // 200_000 units * 15.625 us/unit = 3.13 s + }), + false, + false, + 0, + 0, + ) + .await + { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + receiving_buffer = [00u8; 100]; + match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { + Ok((received_len, _rx_pkt_status)) => { + if (received_len == 3) + && (receiving_buffer[0] == 0x01u8) + && (receiving_buffer[1] == 0x02u8) + && (receiving_buffer[2] == 0x03u8) + { + info!("rx successful"); + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } else { + info!("rx unknown packet") + } + } + Err(err) => info!("rx unsuccessful = {}", err), + } +} diff --git a/examples/nrf52840/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs deleted file mode 100644 index e24f0db03..000000000 --- a/examples/nrf52840/src/bin/lora_p2p_report.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. -//! Other nrf/sx126x combinations may work with appropriate pin modifications. -//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs. -#![no_std] -#![no_main] -#![macro_use] -#![allow(dead_code)] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_lora::sx126x::*; -use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{bind_interrupts, peripherals, spim}; -use embassy_time::{Duration, Timer}; -use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; -}); - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M16; - - let mut radio = { - let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); - - let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); - let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); - let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); - - match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { - Ok(r) => r, - Err(err) => { - info!("Sx126xRadio error = {}", err); - return; - } - } - }; - - let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); - let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); - - start_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - start_indicator.set_low(); - - loop { - let rf_config = RfConfig { - frequency: 903900000, // channel in Hz - bandwidth: Bandwidth::_250KHz, - spreading_factor: SpreadingFactor::_10, - coding_rate: CodingRate::_4_8, - }; - - let mut buffer = [00u8; 100]; - - // P2P receive - match radio.rx(rf_config, &mut buffer).await { - Ok((buffer_len, rx_quality)) => info!( - "RX received = {:?} with length = {} rssi = {} snr = {}", - &buffer[0..buffer_len], - buffer_len, - rx_quality.rssi(), - rx_quality.snr() - ), - Err(err) => info!("RX error = {}", err), - } - - debug_indicator.set_high(); - Timer::after(Duration::from_secs(2)).await; - debug_indicator.set_low(); - } -} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 8067f7ba5..1787e4f8a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,12 +9,16 @@ license = "MIT OR Apache-2.0" embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"] } +lora-phy = { version = "1", path = "../../../lora-phy" } +lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] } +lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index d08e2b61a..7e9827a84 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -11,12 +11,12 @@ nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} - -lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } +lora-phy = { version = "1", path = "../../../lora-phy" } +lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true } +lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..a5c5f75ea --- /dev/null +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -0,0 +1,120 @@ +//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. +//! It demonstrates LORA P2P receive functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::Stm32l0InterfaceVariant; +use embassy_stm32::exti::{Channel, ExtiInput}; +use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; +use embassy_stm32::spi; +use embassy_stm32::time::khz; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1276_7_8_9::SX1276_7_8_9; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; + config.rcc.enable_hsi48 = true; + let p = embassy_stm32::init(config); + + // SPI for sx1276 + let spi = spi::Spi::new( + p.SPI1, + p.PB3, + p.PA7, + p.PA6, + p.DMA1_CH3, + p.DMA1_CH2, + khz(200), + spi::Config::default(), + ); + + let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); + let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); + + let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); + let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); + + let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low); + let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + let mut receiving_buffer = [00u8; 100]; + + let mdltn_params = { + match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let rx_pkt_params = { + match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .await + { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + loop { + receiving_buffer = [00u8; 100]; + match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { + Ok((received_len, _rx_pkt_status)) => { + if (received_len == 3) + && (receiving_buffer[0] == 0x01u8) + && (receiving_buffer[1] == 0x02u8) + && (receiving_buffer[2] == 0x03u8) + { + info!("rx successful"); + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } else { + info!("rx unknown packet"); + } + } + Err(err) => info!("rx unsuccessful = {}", err), + } + } +} diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 07f136b40..80c7bccd4 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -7,12 +7,13 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } - -lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } +embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt", "external-lora-phy"] } +lora-phy = { version = "1", path = "../../../lora-phy" } +lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] } +lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..f7cf03595 --- /dev/null +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -0,0 +1,88 @@ +//! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio. +//! It demonstrates LoRaWAN functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait, async_fn_in_trait)] +#![allow(incomplete_features)] + +use defmt::info; +use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_executor::Spawner; +use embassy_lora::iv::Stm32wlInterfaceVariant; +use embassy_lora::LoraTimer; +use embassy_stm32::dma::NoDma; +use embassy_stm32::gpio::{Level, Output, Pin, Speed}; +use embassy_stm32::peripherals::SUBGHZSPI; +use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::rng::Rng; +use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{interrupt, into_ref, pac, Peripheral}; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use lorawan::default_crypto::DefaultFactory as Crypto; +use lorawan_device::async_device::lora_radio::LoRaRadio; +use lorawan_device::async_device::{region, Device, JoinMode}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; + config.rcc.enable_lsi = true; + let p = embassy_stm32::init(config); + + unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } + + let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); + let mut spi_config = SpiConfig::default(); + spi_config.mode = MODE_0; + spi_config.bit_order = BitOrder::MsbFirst; + let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config); + + let spi = BlockingAsync::new(spi); + + let irq = interrupt::take!(SUBGHZ_RADIO); + into_ref!(irq); + // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx + let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); + let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); + let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); + let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap(); + + let mut delay = Delay; + + let lora = { + match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), true, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + let radio = LoRaRadio::new(lora); + let region: region::Configuration = region::Configuration::new(region::Region::EU868); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + + defmt::info!("Joining LoRaWAN network"); + + // TODO: Adjust the EUI and Keys according to your network credentials + match device + .join(&JoinMode::OTAA { + deveui: [0, 0, 0, 0, 0, 0, 0, 0], + appeui: [0, 0, 0, 0, 0, 0, 0, 0], + appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }) + .await + { + Ok(()) => defmt::info!("LoRaWAN network joined"), + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; +} diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..11c35a8fe --- /dev/null +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -0,0 +1,103 @@ +//! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio. +//! It demonstrates LORA P2P send functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait, async_fn_in_trait)] +#![allow(incomplete_features)] + +use defmt::info; +use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_executor::Spawner; +use embassy_lora::iv::Stm32wlInterfaceVariant; +use embassy_stm32::dma::NoDma; +use embassy_stm32::gpio::{Level, Output, Pin, Speed}; +use embassy_stm32::peripherals::SUBGHZSPI; +use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{interrupt, into_ref, Peripheral}; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; + let p = embassy_stm32::init(config); + + let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); + let mut spi_config = SpiConfig::default(); + spi_config.mode = MODE_0; + spi_config.bit_order = BitOrder::MsbFirst; + let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config); + + let spi = BlockingAsync::new(spi); + + let irq = interrupt::take!(SUBGHZ_RADIO); + into_ref!(irq); + // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx + let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); + let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); + let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); + let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mdltn_params = { + match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut tx_pkt_params = { + match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora.prepare_for_tx(&mdltn_params, 20, false).await { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + let buffer = [0x01u8, 0x02u8, 0x03u8]; + match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { + Ok(()) => { + info!("TX DONE"); + } + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.sleep(&mut delay).await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } +} From 6a1a3e6877053b1b72adb3c1446f4f077ad3b03e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 21 Apr 2023 14:37:04 +0200 Subject: [PATCH 0932/1575] Workaround regex breaking change. --- examples/rpi-pico-w/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index dca796e3d..d211f9260 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -25,6 +25,9 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-io = { version = "0.4.0", features = ["async", "defmt"] } heapless = "0.7.15" +[build-dependencies] +# Workaround https://github.com/embassy-rs/cyw43/issues/68 +regex = { version = "~1.7.3", default-features = false } [patch.crates-io] embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } From 0a2f7b4661ee7e346a1f19c69724d955a239d532 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Fri, 21 Apr 2023 17:41:25 -0500 Subject: [PATCH 0933/1575] Use released lora-phy. --- embassy-lora/Cargo.toml | 4 ++-- examples/nrf52840/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 20cfaf1bd..552788898 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -36,9 +36,9 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } 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.6" } +embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lora-phy = { version = "1", path = "../../lora-phy", optional = true } +lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.9.0", path = "../../rust-lorawan/device", default-features = false, features = ["async"] } lorawan = { version = "0.7.2", path = "../../rust-lorawan/encoding", default-features = false } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 7a1fe281a..a1bcc4b6f 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -19,7 +19,7 @@ embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defm embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } -lora-phy = { version = "1", path = "../../../lora-phy" } +lora-phy = { version = "1" } lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 1787e4f8a..5f0a397ea 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -16,7 +16,7 @@ embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defm embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"] } -lora-phy = { version = "1", path = "../../../lora-phy" } +lora-phy = { version = "1" } lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] } lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 7e9827a84..fe4b4f3c3 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -14,7 +14,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } -lora-phy = { version = "1", path = "../../../lora-phy" } +lora-phy = { version = "1" } lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 80c7bccd4..45e720f01 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -11,7 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["ni embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt", "external-lora-phy"] } -lora-phy = { version = "1", path = "../../../lora-phy" } +lora-phy = { version = "1" } lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] } lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] } From cc5bca8e83b4fe372d8d87b35d82ff466d1a290a Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sun, 23 Apr 2023 15:50:46 +0200 Subject: [PATCH 0934/1575] Added feature flag to embassy-rp intrinsics to avoid conflicts with rp2040-hal --- embassy-rp/src/intrinsics.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 3b63846d4..50934e04f 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -402,6 +402,7 @@ macro_rules! division_function { }; } +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] division_function! { unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) { "str r0, [r2, #0x060]", // DIV_UDIVIDEND @@ -409,6 +410,7 @@ division_function! { } } +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] division_function! { signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) { "str r0, [r2, #0x068]", // DIV_SDIVIDEND @@ -416,6 +418,7 @@ division_function! { } } +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] fn divider_unsigned(n: u32, d: u32) -> DivResult { let packed = unsafe { unsigned_divmod(n, d) }; DivResult { @@ -424,6 +427,7 @@ fn divider_unsigned(n: u32, d: u32) -> DivResult { } } +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] fn divider_signed(n: i32, d: i32) -> DivResult { let packed = unsafe { signed_divmod(n, d) }; // Double casts to avoid sign extension @@ -441,6 +445,7 @@ struct DivResult { pub remainder: T, } +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { divider_unsigned(n, d).quotient From 8285263fc2178be409beff217cb6af4562ea5107 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sun, 23 Apr 2023 15:59:56 +0200 Subject: [PATCH 0935/1575] embassy-rp : Added intrinsic feature flag to global_asm macro --- embassy-rp/src/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 50934e04f..04da74cfd 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -284,7 +284,7 @@ macro_rules! intrinsics { // alias the division operators to these for a similar reason r0 is the // result either way and r1 a scratch register, so the caller can't assume it // retains the argument value. -#[cfg(target_arch = "arm")] +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] core::arch::global_asm!( ".macro hwdivider_head", "ldr r2, =(0xd0000000)", // SIO_BASE From ba47fe9c416818b7fff5bca8092d9f0265407089 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sun, 23 Apr 2023 16:37:44 +0200 Subject: [PATCH 0936/1575] embassy-rp : Added feature flag to otherwise unused definitions --- embassy-rp/src/intrinsics.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 04da74cfd..d5cb5fe9a 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -284,7 +284,7 @@ macro_rules! intrinsics { // alias the division operators to these for a similar reason r0 is the // result either way and r1 a scratch register, so the caller can't assume it // retains the argument value. -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] +#[cfg(target_arch = "arm")] core::arch::global_asm!( ".macro hwdivider_head", "ldr r2, =(0xd0000000)", // SIO_BASE @@ -352,6 +352,7 @@ core::arch::global_asm!( ".endm", ); +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] macro_rules! division_function { ( $name:ident $($intrinsic:ident)* ( $argty:ty ) { @@ -438,6 +439,7 @@ fn divider_signed(n: i32, d: i32) -> DivResult { } /// Result of divide/modulo operation +#[cfg(all(target_arch = "arm", feature = "intrinsics"))] struct DivResult { /// The quotient of divide/modulo operation pub quotient: T, From b283f213d99c5bfb888f450270dca699ae096fca Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sun, 23 Apr 2023 19:05:32 +0200 Subject: [PATCH 0937/1575] embassy-rs : @pennae Fix division intrinsics naming clash with rp2040-hal --- embassy-rp/src/intrinsics.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index d5cb5fe9a..3baabb287 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -352,7 +352,6 @@ core::arch::global_asm!( ".endm", ); -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] macro_rules! division_function { ( $name:ident $($intrinsic:ident)* ( $argty:ty ) { @@ -362,10 +361,11 @@ macro_rules! division_function { #[cfg(all(target_arch = "arm", feature = "intrinsics"))] core::arch::global_asm!( // Mangle the name slightly, since this is a global symbol. - concat!(".global _rphal_", stringify!($name)), - concat!(".type _rphal_", stringify!($name), ", %function"), + concat!(".section .text._erphal_", stringify!($name)), + concat!(".global _erphal_", stringify!($name)), + concat!(".type _erphal_", stringify!($name), ", %function"), ".align 2", - concat!("_rphal_", stringify!($name), ":"), + concat!("_erphal_", stringify!($name), ":"), $( concat!(".global ", stringify!($intrinsic)), concat!(".type ", stringify!($intrinsic), ", %function"), @@ -380,10 +380,11 @@ macro_rules! division_function { #[cfg(all(target_arch = "arm", not(feature = "intrinsics")))] core::arch::global_asm!( // Mangle the name slightly, since this is a global symbol. - concat!(".global _rphal_", stringify!($name)), - concat!(".type _rphal_", stringify!($name), ", %function"), + concat!(".section .text._erphal_", stringify!($name)), + concat!(".global _erphal_", stringify!($name)), + concat!(".type _erphal_", stringify!($name), ", %function"), ".align 2", - concat!("_rphal_", stringify!($name), ":"), + concat!("_erphal_", stringify!($name), ":"), "hwdivider_head", $($begin),+ , @@ -393,7 +394,7 @@ macro_rules! division_function { #[cfg(target_arch = "arm")] extern "aapcs" { // Connect a local name to global symbol above through FFI. - #[link_name = concat!("_rphal_", stringify!($name)) ] + #[link_name = concat!("_erphal_", stringify!($name)) ] fn $name(n: $argty, d: $argty) -> u64; } @@ -403,7 +404,6 @@ macro_rules! division_function { }; } -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] division_function! { unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) { "str r0, [r2, #0x060]", // DIV_UDIVIDEND @@ -411,7 +411,6 @@ division_function! { } } -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] division_function! { signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) { "str r0, [r2, #0x068]", // DIV_SDIVIDEND @@ -419,7 +418,6 @@ division_function! { } } -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] fn divider_unsigned(n: u32, d: u32) -> DivResult { let packed = unsafe { unsigned_divmod(n, d) }; DivResult { @@ -428,7 +426,6 @@ fn divider_unsigned(n: u32, d: u32) -> DivResult { } } -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] fn divider_signed(n: i32, d: i32) -> DivResult { let packed = unsafe { signed_divmod(n, d) }; // Double casts to avoid sign extension @@ -439,7 +436,6 @@ fn divider_signed(n: i32, d: i32) -> DivResult { } /// Result of divide/modulo operation -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] struct DivResult { /// The quotient of divide/modulo operation pub quotient: T, @@ -447,7 +443,6 @@ struct DivResult { pub remainder: T, } -#[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { divider_unsigned(n, d).quotient From a4866ad2782b5f66ed1ea67620d4117b0d474ab5 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Apr 2023 00:57:28 +0200 Subject: [PATCH 0938/1575] rp: add PWM api --- embassy-rp/Cargo.toml | 3 +- embassy-rp/src/clocks.rs | 4 +- embassy-rp/src/lib.rs | 10 ++ embassy-rp/src/pwm.rs | 338 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/usb.rs | 2 +- examples/rp/src/bin/pwm.rs | 27 +++ tests/rp/src/bin/pwm.rs | 142 ++++++++++++++++ 7 files changed, 522 insertions(+), 4 deletions(-) create mode 100644 embassy-rp/src/pwm.rs create mode 100644 examples/rp/src/bin/pwm.rs create mode 100644 tests/rp/src/bin/pwm.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b7ed6ccbb..f784ab33d 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -60,8 +60,9 @@ chrono = { version = "0.4", default-features = false, optional = true } embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-storage = { version = "0.3" } rand_core = "0.6.4" +fixed = "1.23.1" -rp-pac = { version = "1", features = ["rt"] } +rp-pac = { version = "2", 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.10", optional = true} diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 85c9bbb7a..8a34b293d 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -155,8 +155,8 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: let cs = p.cs().read(); let prim = p.prim().read(); if cs.lock() - && cs.refdiv() == refdiv as _ - && p.fbdiv_int().read().fbdiv_int() == fbdiv as _ + && cs.refdiv() == refdiv as u8 + && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16 && prim.postdiv1() == post_div1 && prim.postdiv2() == post_div2 { diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 3841bb83a..aa8660320 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -21,6 +21,7 @@ pub mod interrupt; pub mod pio; #[cfg(feature = "pio")] pub mod pio_instr_util; +pub mod pwm; #[cfg(feature = "pio")] pub mod relocate; @@ -109,6 +110,15 @@ embassy_hal_common::peripherals! { DMA_CH10, DMA_CH11, + PWM_CH0, + PWM_CH1, + PWM_CH2, + PWM_CH3, + PWM_CH4, + PWM_CH5, + PWM_CH6, + PWM_CH7, + USB, RTC, diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs new file mode 100644 index 000000000..d2bf79584 --- /dev/null +++ b/embassy-rp/src/pwm.rs @@ -0,0 +1,338 @@ +//! Pulse Width Modulation (PWM) + +use embassy_embedded_hal::SetConfig; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use fixed::traits::ToFixed; +use fixed::FixedU16; +use pac::pwm::regs::{ChDiv, Intr}; +use pac::pwm::vals::Divmode; + +use crate::gpio::sealed::Pin as _; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::{pac, peripherals, RegExt}; + +#[non_exhaustive] +#[derive(Clone)] +pub struct Config { + pub invert_a: bool, + pub invert_b: bool, + pub phase_correct: bool, + pub enable: bool, + pub divider: fixed::FixedU16, + pub compare_a: u16, + pub compare_b: u16, + pub top: u16, +} + +impl Default for Config { + fn default() -> Self { + Self { + invert_a: false, + invert_b: false, + phase_correct: false, + enable: true, // differs from reset value + divider: 1.to_fixed(), + compare_a: 0, + compare_b: 0, + top: 0xffff, + } + } +} + +pub enum InputMode { + Level, + RisingEdge, + FallingEdge, +} + +impl From for Divmode { + fn from(value: InputMode) -> Self { + match value { + InputMode::Level => Divmode::LEVEL, + InputMode::RisingEdge => Divmode::RISE, + InputMode::FallingEdge => Divmode::FALL, + } + } +} + +pub struct Pwm<'d, T: Channel> { + inner: PeripheralRef<'d, T>, + pin_a: Option>, + pin_b: Option>, +} + +impl<'d, T: Channel> Pwm<'d, T> { + fn new_inner( + inner: impl Peripheral

+ 'd, + a: Option>, + b: Option>, + config: Config, + divmode: Divmode, + ) -> Self { + into_ref!(inner); + + let p = inner.regs(); + unsafe { + p.csr().modify(|w| { + w.set_divmode(divmode); + w.set_en(false); + }); + p.ctr().write(|w| w.0 = 0); + Self::configure(p, &config); + + if let Some(pin) = &a { + pin.io().ctrl().write(|w| w.set_funcsel(4)); + } + if let Some(pin) = &b { + pin.io().ctrl().write(|w| w.set_funcsel(4)); + } + } + Self { + inner, + pin_a: a.into(), + pin_b: b.into(), + } + } + + #[inline] + pub fn new_free(inner: impl Peripheral

+ 'd, config: Config) -> Self { + Self::new_inner(inner, None, None, config, Divmode::DIV) + } + + #[inline] + pub fn new_output_a( + inner: impl Peripheral

+ 'd, + a: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(a); + Self::new_inner(inner, Some(a.map_into()), None, config, Divmode::DIV) + } + + #[inline] + pub fn new_output_b( + inner: impl Peripheral

+ 'd, + b: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(b); + Self::new_inner(inner, None, Some(b.map_into()), config, Divmode::DIV) + } + + #[inline] + pub fn new_output_ab( + inner: impl Peripheral

+ 'd, + a: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(a, b); + Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, Divmode::DIV) + } + + #[inline] + pub fn new_input( + inner: impl Peripheral

+ 'd, + b: impl Peripheral

> + 'd, + mode: InputMode, + config: Config, + ) -> Self { + into_ref!(b); + Self::new_inner(inner, None, Some(b.map_into()), config, mode.into()) + } + + #[inline] + pub fn new_output_input( + inner: impl Peripheral

+ 'd, + a: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, + mode: InputMode, + config: Config, + ) -> Self { + into_ref!(a, b); + Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into()) + } + + fn configure(p: pac::pwm::Channel, config: &Config) { + if config.divider > FixedU16::::from_bits(0xFF_F) { + panic!("Requested divider is too large"); + } + + unsafe { + p.div().write_value(ChDiv(config.divider.to_bits() as u32)); + p.cc().write(|w| { + w.set_a(config.compare_a); + w.set_b(config.compare_b); + }); + p.top().write(|w| w.set_top(config.top)); + p.csr().modify(|w| { + w.set_a_inv(config.invert_a); + w.set_b_inv(config.invert_b); + w.set_ph_correct(config.phase_correct); + w.set_en(config.enable); + }); + } + } + + #[inline] + pub unsafe fn phase_advance(&mut self) { + let p = self.inner.regs(); + p.csr().write_set(|w| w.set_ph_adv(true)); + while p.csr().read().ph_adv() {} + } + + #[inline] + pub unsafe fn phase_retard(&mut self) { + let p = self.inner.regs(); + p.csr().write_set(|w| w.set_ph_ret(true)); + while p.csr().read().ph_ret() {} + } + + #[inline] + pub fn counter(&self) -> u16 { + unsafe { self.inner.regs().ctr().read().ctr() } + } + + #[inline] + pub fn set_counter(&self, ctr: u16) { + unsafe { self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) } + } + + #[inline] + pub fn wait_for_wrap(&mut self) { + while !self.wrapped() {} + self.clear_wrapped(); + } + + #[inline] + pub fn wrapped(&mut self) -> bool { + unsafe { pac::PWM.intr().read().0 & self.bit() != 0 } + } + + #[inline] + pub fn clear_wrapped(&mut self) { + unsafe { + pac::PWM.intr().write_value(Intr(self.bit() as _)); + } + } + + #[inline] + fn bit(&self) -> u32 { + 1 << self.inner.number() as usize + } +} + +pub struct PwmBatch(u32); + +impl PwmBatch { + #[inline] + pub fn enable(&mut self, pwm: &Pwm<'_, impl Channel>) { + self.0 |= pwm.bit(); + } + + #[inline] + pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) { + let mut en = PwmBatch(0); + batch(&mut en); + unsafe { + if enabled { + pac::PWM.en().write_set(|w| w.0 = en.0); + } else { + pac::PWM.en().write_clear(|w| w.0 = en.0); + } + } + } +} + +impl<'d, T: Channel> Drop for Pwm<'d, T> { + fn drop(&mut self) { + unsafe { + self.inner.regs().csr().write_clear(|w| w.set_en(false)); + if let Some(pin) = &self.pin_a { + pin.io().ctrl().write(|w| w.set_funcsel(31)); + } + if let Some(pin) = &self.pin_b { + pin.io().ctrl().write(|w| w.set_funcsel(31)); + } + } + } +} + +mod sealed { + pub trait Channel {} +} + +pub trait Channel: Peripheral

+ sealed::Channel + Sized + 'static { + fn number(&self) -> u8; + + fn regs(&self) -> pac::pwm::Channel { + pac::PWM.ch(self.number() as _) + } +} + +macro_rules! channel { + ($name:ident, $num:expr) => { + impl sealed::Channel for peripherals::$name {} + impl Channel for peripherals::$name { + fn number(&self) -> u8 { + $num + } + } + }; +} + +channel!(PWM_CH0, 0); +channel!(PWM_CH1, 1); +channel!(PWM_CH2, 2); +channel!(PWM_CH3, 3); +channel!(PWM_CH4, 4); +channel!(PWM_CH5, 5); +channel!(PWM_CH6, 6); +channel!(PWM_CH7, 7); + +pub trait PwmPinA: GpioPin {} +pub trait PwmPinB: GpioPin {} + +macro_rules! impl_pin { + ($pin:ident, $channel:ident, $kind:ident) => { + impl $kind for peripherals::$pin {} + }; +} + +impl_pin!(PIN_0, PWM_CH0, PwmPinA); +impl_pin!(PIN_1, PWM_CH0, PwmPinB); +impl_pin!(PIN_2, PWM_CH1, PwmPinA); +impl_pin!(PIN_3, PWM_CH1, PwmPinB); +impl_pin!(PIN_4, PWM_CH2, PwmPinA); +impl_pin!(PIN_5, PWM_CH2, PwmPinB); +impl_pin!(PIN_6, PWM_CH3, PwmPinA); +impl_pin!(PIN_7, PWM_CH3, PwmPinB); +impl_pin!(PIN_8, PWM_CH4, PwmPinA); +impl_pin!(PIN_9, PWM_CH4, PwmPinB); +impl_pin!(PIN_10, PWM_CH5, PwmPinA); +impl_pin!(PIN_11, PWM_CH5, PwmPinB); +impl_pin!(PIN_12, PWM_CH6, PwmPinA); +impl_pin!(PIN_13, PWM_CH6, PwmPinB); +impl_pin!(PIN_14, PWM_CH7, PwmPinA); +impl_pin!(PIN_15, PWM_CH7, PwmPinB); +impl_pin!(PIN_16, PWM_CH0, PwmPinA); +impl_pin!(PIN_17, PWM_CH0, PwmPinB); +impl_pin!(PIN_18, PWM_CH1, PwmPinA); +impl_pin!(PIN_19, PWM_CH1, PwmPinB); +impl_pin!(PIN_20, PWM_CH2, PwmPinA); +impl_pin!(PIN_21, PWM_CH2, PwmPinB); +impl_pin!(PIN_22, PWM_CH3, PwmPinA); +impl_pin!(PIN_23, PWM_CH3, PwmPinB); +impl_pin!(PIN_24, PWM_CH4, PwmPinA); +impl_pin!(PIN_25, PWM_CH4, PwmPinB); +impl_pin!(PIN_26, PWM_CH5, PwmPinA); +impl_pin!(PIN_27, PWM_CH5, PwmPinB); +impl_pin!(PIN_28, PWM_CH6, PwmPinA); +impl_pin!(PIN_29, PWM_CH6, PwmPinB); + +impl<'d, T: Channel> SetConfig for Pwm<'d, T> { + type Config = Config; + fn set_config(&mut self, config: &Self::Config) { + Self::configure(self.inner.regs(), config); + } +} diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 2e3708eff..a049e4769 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -231,7 +231,7 @@ impl<'d, T: Instance> Driver<'d, T> { let len = (max_packet_size + 63) / 64 * 64; let addr = self.ep_mem_free; - if addr + len > EP_MEMORY_SIZE as _ { + if addr + len > EP_MEMORY_SIZE as u16 { warn!("Endpoint memory full"); return Err(EndpointAllocError); } diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs new file mode 100644 index 000000000..69d315553 --- /dev/null +++ b/examples/rp/src/bin/pwm.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_embedded_hal::SetConfig; +use embassy_executor::Spawner; +use embassy_rp::pwm::{Config, Pwm}; +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 mut c: Config = Default::default(); + c.top = 0x8000; + c.compare_b = 8; + let mut pwm = Pwm::new_output_b(p.PWM_CH4, p.PIN_25, c.clone()); + + loop { + info!("current LED duty cycle: {}/32768", c.compare_b); + Timer::after(Duration::from_secs(1)).await; + c.compare_b = c.compare_b.rotate_left(4); + pwm.set_config(&c); + } +} diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs new file mode 100644 index 000000000..b8cbe74c8 --- /dev/null +++ b/tests/rp/src/bin/pwm.rs @@ -0,0 +1,142 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert, assert_eq, assert_ne, *}; +use embassy_executor::Spawner; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::pwm::{Config, InputMode, Pwm}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + // Connections on CI device: 6 -> 9, 7 -> 11 + let (mut p6, mut p7, mut p9, mut p11) = (p.PIN_6, p.PIN_7, p.PIN_9, p.PIN_11); + + let cfg = { + let mut c = Config::default(); + c.divider = 125.into(); + c.top = 10000; + c.compare_a = 5000; + c.compare_b = 5000; + c + }; + + // Test free-running clock + { + let pwm = Pwm::new_free(&mut p.PWM_CH3, cfg.clone()); + cortex_m::asm::delay(125); + let ctr = pwm.counter(); + assert!(ctr > 0); + assert!(ctr < 100); + cortex_m::asm::delay(125); + assert!(ctr < pwm.counter()); + } + + for invert_a in [false, true] { + info!("free-running, invert A: {}", invert_a); + let mut cfg = cfg.clone(); + cfg.invert_a = invert_a; + cfg.invert_b = !invert_a; + + // Test output from A + { + let pin1 = Input::new(&mut p9, Pull::None); + let _pwm = Pwm::new_output_a(&mut p.PWM_CH3, &mut p6, cfg.clone()); + Timer::after(Duration::from_millis(1)).await; + assert_eq!(pin1.is_low(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pin1.is_high(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pin1.is_low(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pin1.is_high(), invert_a); + } + + // Test output from B + { + let pin2 = Input::new(&mut p11, Pull::None); + let _pwm = Pwm::new_output_b(&mut p.PWM_CH3, &mut p7, cfg.clone()); + Timer::after(Duration::from_millis(1)).await; + assert_ne!(pin2.is_low(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_ne!(pin2.is_high(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_ne!(pin2.is_low(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_ne!(pin2.is_high(), invert_a); + } + + // Test output from A+B + { + let pin1 = Input::new(&mut p9, Pull::None); + let pin2 = Input::new(&mut p11, Pull::None); + let _pwm = Pwm::new_output_ab(&mut p.PWM_CH3, &mut p6, &mut p7, cfg.clone()); + Timer::after(Duration::from_millis(1)).await; + assert_eq!(pin1.is_low(), invert_a); + assert_ne!(pin2.is_low(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pin1.is_high(), invert_a); + assert_ne!(pin2.is_high(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pin1.is_low(), invert_a); + assert_ne!(pin2.is_low(), invert_a); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pin1.is_high(), invert_a); + assert_ne!(pin2.is_high(), invert_a); + } + } + + // Test level-gated + { + let mut pin2 = Output::new(&mut p11, Level::Low); + let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::Level, cfg.clone()); + assert_eq!(pwm.counter(), 0); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pwm.counter(), 0); + pin2.set_high(); + Timer::after(Duration::from_millis(1)).await; + pin2.set_low(); + let ctr = pwm.counter(); + assert!(ctr >= 1000); + Timer::after(Duration::from_millis(1)).await; + assert_eq!(pwm.counter(), ctr); + } + + // Test rising-gated + { + let mut pin2 = Output::new(&mut p11, Level::Low); + let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::RisingEdge, cfg.clone()); + assert_eq!(pwm.counter(), 0); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pwm.counter(), 0); + pin2.set_high(); + Timer::after(Duration::from_millis(1)).await; + pin2.set_low(); + assert_eq!(pwm.counter(), 1); + Timer::after(Duration::from_millis(1)).await; + assert_eq!(pwm.counter(), 1); + } + + // Test falling-gated + { + let mut pin2 = Output::new(&mut p11, Level::High); + let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::FallingEdge, cfg.clone()); + assert_eq!(pwm.counter(), 0); + Timer::after(Duration::from_millis(5)).await; + assert_eq!(pwm.counter(), 0); + pin2.set_low(); + Timer::after(Duration::from_millis(1)).await; + pin2.set_high(); + assert_eq!(pwm.counter(), 1); + Timer::after(Duration::from_millis(1)).await; + assert_eq!(pwm.counter(), 1); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 73f25093c7793ad2e8bd6fceeca46d9b5b1031ad Mon Sep 17 00:00:00 2001 From: ceekdee Date: Sun, 23 Apr 2023 18:32:34 -0500 Subject: [PATCH 0939/1575] Add lora-phy examples. --- embassy-lora/src/lib.rs | 3 + examples/nrf52840/src/bin/lora_cad.rs | 13 +- examples/nrf52840/src/bin/lora_lorawan.rs | 83 +++++++++++ examples/nrf52840/src/bin/lora_p2p_receive.rs | 121 +++++++++++++++ .../src/bin/lora_p2p_receive_duty_cycle.rs | 11 +- examples/nrf52840/src/bin/lora_p2p_send.rs | 104 +++++++++++++ examples/nrf52840/src/bin/lora_p2p_sense.rs | 128 ---------------- examples/rp/src/bin/lora_lorawan.rs | 80 ++++++++++ examples/rp/src/bin/lora_p2p_receive.rs | 115 +++++++++++++++ examples/rp/src/bin/lora_p2p_send.rs | 103 +++++++++++++ .../rp/src/bin/lora_p2p_send_multicore.rs | 139 ++++++++++++++++++ examples/stm32l0/src/bin/lora_cad.rs | 105 +++++++++++++ examples/stm32l0/src/bin/lora_lorawan.rs | 88 +++++++++++ examples/stm32l0/src/bin/lora_p2p_receive.rs | 11 +- examples/stm32l0/src/bin/lora_p2p_send.rs | 110 ++++++++++++++ examples/stm32l0/src/bin/lorawan.rs | 74 ---------- examples/stm32wl/src/bin/lora_lorawan.rs | 6 +- examples/stm32wl/src/bin/lora_p2p_receive.rs | 127 ++++++++++++++++ examples/stm32wl/src/bin/lora_p2p_send.rs | 9 +- examples/stm32wl/src/bin/lorawan.rs | 104 ------------- 20 files changed, 1218 insertions(+), 316 deletions(-) create mode 100644 examples/nrf52840/src/bin/lora_lorawan.rs create mode 100644 examples/nrf52840/src/bin/lora_p2p_receive.rs create mode 100644 examples/nrf52840/src/bin/lora_p2p_send.rs delete mode 100644 examples/nrf52840/src/bin/lora_p2p_sense.rs create mode 100644 examples/rp/src/bin/lora_lorawan.rs create mode 100644 examples/rp/src/bin/lora_p2p_receive.rs create mode 100644 examples/rp/src/bin/lora_p2p_send.rs create mode 100644 examples/rp/src/bin/lora_p2p_send_multicore.rs create mode 100644 examples/stm32l0/src/bin/lora_cad.rs create mode 100644 examples/stm32l0/src/bin/lora_lorawan.rs create mode 100644 examples/stm32l0/src/bin/lora_p2p_send.rs delete mode 100644 examples/stm32l0/src/bin/lorawan.rs create mode 100644 examples/stm32wl/src/bin/lora_p2p_receive.rs delete mode 100644 examples/stm32wl/src/bin/lorawan.rs diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index d8bc3cd98..c01d3f71a 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -10,10 +10,13 @@ pub(crate) mod fmt; pub mod iv; #[cfg(feature = "stm32wl")] +#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] pub mod stm32wl; #[cfg(feature = "sx126x")] +#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] pub mod sx126x; #[cfg(feature = "sx127x")] +#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] pub mod sx127x; #[cfg(feature = "time")] diff --git a/examples/nrf52840/src/bin/lora_cad.rs b/examples/nrf52840/src/bin/lora_cad.rs index 8899c1b23..beca061ed 100644 --- a/examples/nrf52840/src/bin/lora_cad.rs +++ b/examples/nrf52840/src/bin/lora_cad.rs @@ -1,6 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. //! Other nrf/sx126x combinations may work with appropriate pin modifications. -//! It demonstates LORA CAD functionality. +//! It demonstrates LORA CAD functionality. #![no_std] #![no_main] #![macro_use] @@ -17,6 +17,8 @@ use lora_phy::sx1261_2::SX1261_2; use lora_phy::LoRa; use {defmt_rtt as _, panic_probe as _}; +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + bind_interrupts!(struct Irqs { SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; }); @@ -59,7 +61,12 @@ async fn main(_spawner: Spawner) { start_indicator.set_low(); let mdltn_params = { - match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { Ok(mp) => mp, Err(err) => { info!("Radio error = {}", err); @@ -84,7 +91,7 @@ async fn main(_spawner: Spawner) { info!("cad successful without activity detected") } debug_indicator.set_high(); - Timer::after(Duration::from_secs(15)).await; + Timer::after(Duration::from_secs(5)).await; debug_indicator.set_low(); } Err(err) => info!("cad unsuccessful = {}", err), diff --git a/examples/nrf52840/src/bin/lora_lorawan.rs b/examples/nrf52840/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..c953680c6 --- /dev/null +++ b/examples/nrf52840/src/bin/lora_lorawan.rs @@ -0,0 +1,83 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstrates LoRaWAN join functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_lora::LoraTimer; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::rng::Rng; +use embassy_nrf::{bind_interrupts, peripherals, rng, spim}; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use lorawan::default_crypto::DefaultFactory as Crypto; +use lorawan_device::async_device::lora_radio::LoRaRadio; +use lorawan_device::async_device::{region, Device, JoinMode}; +use {defmt_rtt as _, panic_probe as _}; + +const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region + +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; + RNG => rng::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M16; + + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); + + let iv = + GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); + + let mut delay = Delay; + + let lora = { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), true, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let radio = LoRaRadio::new(lora); + let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); + + defmt::info!("Joining LoRaWAN network"); + + // TODO: Adjust the EUI and Keys according to your network credentials + match device + .join(&JoinMode::OTAA { + deveui: [0, 0, 0, 0, 0, 0, 0, 0], + appeui: [0, 0, 0, 0, 0, 0, 0, 0], + appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }) + .await + { + Ok(()) => defmt::info!("LoRaWAN network joined"), + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; +} diff --git a/examples/nrf52840/src/bin/lora_p2p_receive.rs b/examples/nrf52840/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..563fe42ec --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_receive.rs @@ -0,0 +1,121 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M16; + + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); + + let iv = + GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + let mut receiving_buffer = [00u8; 100]; + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let rx_pkt_params = { + match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .await + { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + loop { + receiving_buffer = [00u8; 100]; + match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { + Ok((received_len, _rx_pkt_status)) => { + if (received_len == 3) + && (receiving_buffer[0] == 0x01u8) + && (receiving_buffer[1] == 0x02u8) + && (receiving_buffer[2] == 0x03u8) + { + info!("rx successful"); + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } else { + info!("rx unknown packet"); + } + } + Err(err) => info!("rx unsuccessful = {}", err), + } + } +} diff --git a/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs index d84701742..1fd8f61a2 100644 --- a/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs +++ b/examples/nrf52840/src/bin/lora_p2p_receive_duty_cycle.rs @@ -1,6 +1,6 @@ //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. //! Other nrf/sx126x combinations may work with appropriate pin modifications. -//! It demonstates LoRa Rx duty cycle functionality. +//! It demonstrates LoRa Rx duty cycle functionality in conjunction with the lora_p2p_send example. #![no_std] #![no_main] #![macro_use] @@ -17,6 +17,8 @@ use lora_phy::sx1261_2::SX1261_2; use lora_phy::LoRa; use {defmt_rtt as _, panic_probe as _}; +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + bind_interrupts!(struct Irqs { SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; }); @@ -61,7 +63,12 @@ async fn main(_spawner: Spawner) { let mut receiving_buffer = [00u8; 100]; let mdltn_params = { - match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { Ok(mp) => mp, Err(err) => { info!("Radio error = {}", err); diff --git a/examples/nrf52840/src/bin/lora_p2p_send.rs b/examples/nrf52840/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..1c8bbc27a --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_send.rs @@ -0,0 +1,104 @@ +//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. +//! Other nrf/sx126x combinations may work with appropriate pin modifications. +//! It demonstrates LORA P2P send functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; +use embassy_nrf::{bind_interrupts, peripherals, spim}; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +bind_interrupts!(struct Irqs { + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut spi_config = spim::Config::default(); + spi_config.frequency = spim::Frequency::M16; + + let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); + + let nss = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); + let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); + let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); + let busy = Input::new(p.P1_14.degrade(), Pull::Down); + let rf_switch_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); + let rf_switch_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); + + let iv = + GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, Some(rf_switch_rx), Some(rf_switch_tx)).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1261_2::new(BoardType::Rak4631Sx1262, spim, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut tx_pkt_params = { + match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora.prepare_for_tx(&mdltn_params, 20, false).await { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + let buffer = [0x01u8, 0x02u8, 0x03u8]; + match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { + Ok(()) => { + info!("TX DONE"); + } + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.sleep(&mut delay).await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } +} diff --git a/examples/nrf52840/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs deleted file mode 100644 index b6f41ffcc..000000000 --- a/examples/nrf52840/src/bin/lora_p2p_sense.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. -//! Other nrf/sx126x combinations may work with appropriate pin modifications. -//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs. -#![no_std] -#![no_main] -#![macro_use] -#![feature(type_alias_impl_trait)] -#![feature(alloc_error_handler)] -#![allow(incomplete_features)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_lora::sx126x::*; -use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; -use embassy_nrf::{bind_interrupts, peripherals, spim}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::pubsub::{PubSubChannel, Publisher}; -use embassy_time::{Duration, Timer}; -use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; -use {defmt_rtt as _, panic_probe as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => spim::InterruptHandler; -}); - -// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) -static MESSAGE_BUS: PubSubChannel = PubSubChannel::new(); - -#[derive(Clone, defmt::Format)] -enum Message { - Temperature(i32), - MotionDetected, -} - -#[embassy_executor::task] -async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { - // Publish a fake temperature every 43 seconds, minimizing LORA traffic. - loop { - Timer::after(Duration::from_secs(43)).await; - publisher.publish(Message::Temperature(9)).await; - } -} - -#[embassy_executor::task] -async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { - // Publish a fake motion detection every 79 seconds, minimizing LORA traffic. - loop { - Timer::after(Duration::from_secs(79)).await; - publisher.publish(Message::MotionDetected).await; - } -} - -#[embassy_executor::main] -async fn main(spawner: Spawner) { - let p = embassy_nrf::init(Default::default()); - // set up to funnel temperature and motion detection events to the Lora Tx task - let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber()); - let temperature_publisher = unwrap!(MESSAGE_BUS.publisher()); - let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); - - let mut spi_config = spim::Config::default(); - spi_config.frequency = spim::Frequency::M16; - - let mut radio = { - let spim = spim::Spim::new(p.TWISPI1, Irqs, p.P1_11, p.P1_13, p.P1_12, spi_config); - - let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); - let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); - let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); - let busy = Input::new(p.P1_14.degrade(), Pull::Down); - let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); - let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); - - match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { - Ok(r) => r, - Err(err) => { - info!("Sx126xRadio error = {}", err); - return; - } - } - }; - - let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); - - start_indicator.set_high(); - Timer::after(Duration::from_secs(5)).await; - start_indicator.set_low(); - - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), - } - - unwrap!(spawner.spawn(temperature_task(temperature_publisher))); - unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher))); - - loop { - let message = lora_tx_subscriber.next_message_pure().await; - - let tx_config = TxConfig { - // 11 byte maximum payload for Bandwidth 125 and SF 10 - pw: 10, // up to 20 - rf: RfConfig { - frequency: 903900000, // channel in Hz, not MHz - bandwidth: Bandwidth::_250KHz, - spreading_factor: SpreadingFactor::_10, - coding_rate: CodingRate::_4_8, - }, - }; - - let mut buffer = [0x00u8]; - match message { - Message::Temperature(temperature) => buffer[0] = temperature as u8, - Message::MotionDetected => buffer[0] = 0x01u8, - }; - - // unencrypted - match radio.tx(tx_config, &buffer).await { - Ok(ret_val) => info!("TX ret_val = {}", ret_val), - Err(err) => info!("TX error = {}", err), - } - - match radio.lora.sleep().await { - Ok(()) => info!("Sleep successful"), - Err(err) => info!("Sleep unsuccessful = {}", err), - } - } -} diff --git a/examples/rp/src/bin/lora_lorawan.rs b/examples/rp/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..a9c84bf95 --- /dev/null +++ b/examples/rp/src/bin/lora_lorawan.rs @@ -0,0 +1,80 @@ +//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. +//! It demonstrates LoRaWAN join functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_lora::LoraTimer; +use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; +use embassy_rp::spi::{Config, Spi}; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use lorawan::default_crypto::DefaultFactory as Crypto; +use lorawan_device::async_device::lora_radio::LoRaRadio; +use lorawan_device::async_device::{region, Device, JoinMode}; +use {defmt_rtt as _, panic_probe as _}; + +const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let miso = p.PIN_12; + let mosi = p.PIN_11; + let clk = p.PIN_10; + let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + + let nss = Output::new(p.PIN_3.degrade(), Level::High); + let reset = Output::new(p.PIN_15.degrade(), Level::High); + let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); + let busy = Input::new(p.PIN_2.degrade(), Pull::None); + + let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); + + let mut delay = Delay; + + let lora = { + match LoRa::new( + SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), + true, + &mut delay, + ) + .await + { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let radio = LoRaRadio::new(lora); + let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), embassy_rp::clocks::RoscRng); + + defmt::info!("Joining LoRaWAN network"); + + // TODO: Adjust the EUI and Keys according to your network credentials + match device + .join(&JoinMode::OTAA { + deveui: [0, 0, 0, 0, 0, 0, 0, 0], + appeui: [0, 0, 0, 0, 0, 0, 0, 0], + appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }) + .await + { + Ok(()) => defmt::info!("LoRaWAN network joined"), + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; +} diff --git a/examples/rp/src/bin/lora_p2p_receive.rs b/examples/rp/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..250419202 --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_receive.rs @@ -0,0 +1,115 @@ +//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. +//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; +use embassy_rp::spi::{Config, Spi}; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let miso = p.PIN_12; + let mosi = p.PIN_11; + let clk = p.PIN_10; + let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + + let nss = Output::new(p.PIN_3.degrade(), Level::High); + let reset = Output::new(p.PIN_15.degrade(), Level::High); + let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); + let busy = Input::new(p.PIN_2.degrade(), Pull::None); + + let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new( + SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), + false, + &mut delay, + ) + .await + { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.PIN_25, Level::Low); + + let mut receiving_buffer = [00u8; 100]; + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let rx_pkt_params = { + match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .await + { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + loop { + receiving_buffer = [00u8; 100]; + match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { + Ok((received_len, _rx_pkt_status)) => { + if (received_len == 3) + && (receiving_buffer[0] == 0x01u8) + && (receiving_buffer[1] == 0x02u8) + && (receiving_buffer[2] == 0x03u8) + { + info!("rx successful"); + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } else { + info!("rx unknown packet"); + } + } + Err(err) => info!("rx unsuccessful = {}", err), + } + } +} diff --git a/examples/rp/src/bin/lora_p2p_send.rs b/examples/rp/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..3a0544b17 --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_send.rs @@ -0,0 +1,103 @@ +//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. +//! It demonstrates LORA P2P send functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_rp::gpio::{Input, Level, Output, Pin, Pull}; +use embassy_rp::spi::{Config, Spi}; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + + let miso = p.PIN_12; + let mosi = p.PIN_11; + let clk = p.PIN_10; + let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + + let nss = Output::new(p.PIN_3.degrade(), Level::High); + let reset = Output::new(p.PIN_15.degrade(), Level::High); + let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); + let busy = Input::new(p.PIN_2.degrade(), Pull::None); + + let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new( + SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), + false, + &mut delay, + ) + .await + { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut tx_pkt_params = { + match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora.prepare_for_tx(&mdltn_params, 20, false).await { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + let buffer = [0x01u8, 0x02u8, 0x03u8]; + match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { + Ok(()) => { + info!("TX DONE"); + } + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.sleep(&mut delay).await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } +} diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs new file mode 100644 index 000000000..5585606d8 --- /dev/null +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -0,0 +1,139 @@ +//! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. +//! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_lora::iv::GenericSx126xInterfaceVariant; +use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pin, Pull}; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::peripherals::SPI1; +use embassy_rp::spi::{Async, Config, Spi}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<4096> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); +static CHANNEL: Channel = Channel::new(); + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + + let miso = p.PIN_12; + let mosi = p.PIN_11; + let clk = p.PIN_10; + let spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + + let nss = Output::new(p.PIN_3.degrade(), Level::High); + let reset = Output::new(p.PIN_15.degrade(), Level::High); + let dio1 = Input::new(p.PIN_20.degrade(), Pull::None); + let busy = Input::new(p.PIN_2.degrade(), Pull::None); + + let iv = GenericSx126xInterfaceVariant::new(nss, reset, dio1, busy, None, None).unwrap(); + + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(spi, iv)))); + }); + + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +} + +#[embassy_executor::task] +async fn core0_task() { + info!("Hello from core 0"); + loop { + CHANNEL.send([0x01u8, 0x02u8, 0x03u8]).await; + Timer::after(Duration::from_millis(60 * 1000)).await; + } +} + +#[embassy_executor::task] +async fn core1_task( + spi: Spi<'static, SPI1, Async>, + iv: GenericSx126xInterfaceVariant, Input<'static, AnyPin>>, +) { + info!("Hello from core 1"); + let mut delay = Delay; + + let mut lora = { + match LoRa::new( + SX1261_2::new(BoardType::RpPicoWaveshareSx1262, spi, iv), + false, + &mut delay, + ) + .await + { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut tx_pkt_params = { + match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + loop { + let buffer: [u8; 3] = CHANNEL.recv().await; + match lora.prepare_for_tx(&mdltn_params, 20, false).await { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { + Ok(()) => { + info!("TX DONE"); + } + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.sleep(&mut delay).await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } + } +} diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs new file mode 100644 index 000000000..588cea1e5 --- /dev/null +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -0,0 +1,105 @@ +//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. +//! It demonstrates LORA P2P CAD functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::Stm32l0InterfaceVariant; +use embassy_stm32::exti::{Channel, ExtiInput}; +use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; +use embassy_stm32::spi; +use embassy_stm32::time::khz; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1276_7_8_9::SX1276_7_8_9; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; + config.rcc.enable_hsi48 = true; + let p = embassy_stm32::init(config); + + // SPI for sx1276 + let spi = spi::Spi::new( + p.SPI1, + p.PB3, + p.PA7, + p.PA6, + p.DMA1_CH3, + p.DMA1_CH2, + khz(200), + spi::Config::default(), + ); + + let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); + let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); + + let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); + let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); + + let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.PB5, Level::Low, Speed::Low); + let mut start_indicator = Output::new(p.PB6, Level::Low, Speed::Low); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora.prepare_for_cad(&mdltn_params, true).await { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.cad().await { + Ok(cad_activity_detected) => { + if cad_activity_detected { + info!("cad successful with activity detected") + } else { + info!("cad successful without activity detected") + } + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } + Err(err) => info!("cad unsuccessful = {}", err), + } +} diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs new file mode 100644 index 000000000..c397edd58 --- /dev/null +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -0,0 +1,88 @@ +//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. +//! It demonstrates LoRaWAN join functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::Stm32l0InterfaceVariant; +use embassy_lora::LoraTimer; +use embassy_stm32::exti::{Channel, ExtiInput}; +use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; +use embassy_stm32::rng::Rng; +use embassy_stm32::spi; +use embassy_stm32::time::khz; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1276_7_8_9::SX1276_7_8_9; +use lora_phy::LoRa; +use lorawan::default_crypto::DefaultFactory as Crypto; +use lorawan_device::async_device::lora_radio::LoRaRadio; +use lorawan_device::async_device::{region, Device, JoinMode}; +use {defmt_rtt as _, panic_probe as _}; + +const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; + config.rcc.enable_hsi48 = true; + let p = embassy_stm32::init(config); + + // SPI for sx1276 + let spi = spi::Spi::new( + p.SPI1, + p.PB3, + p.PA7, + p.PA6, + p.DMA1_CH3, + p.DMA1_CH2, + khz(200), + spi::Config::default(), + ); + + let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); + let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); + + let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); + let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); + + let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); + + let mut delay = Delay; + + let lora = { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), true, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let radio = LoRaRadio::new(lora); + let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + + defmt::info!("Joining LoRaWAN network"); + + // TODO: Adjust the EUI and Keys according to your network credentials + match device + .join(&JoinMode::OTAA { + deveui: [0, 0, 0, 0, 0, 0, 0, 0], + appeui: [0, 0, 0, 0, 0, 0, 0, 0], + appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }) + .await + { + Ok(()) => defmt::info!("LoRaWAN network joined"), + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; +} diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index a5c5f75ea..bb7509509 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -1,5 +1,5 @@ //! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. -//! It demonstrates LORA P2P receive functionality. +//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. #![no_std] #![no_main] #![macro_use] @@ -18,6 +18,8 @@ use lora_phy::sx1276_7_8_9::SX1276_7_8_9; use lora_phy::LoRa; use {defmt_rtt as _, panic_probe as _}; +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -67,7 +69,12 @@ async fn main(_spawner: Spawner) { let mut receiving_buffer = [00u8; 100]; let mdltn_params = { - match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { Ok(mp) => mp, Err(err) => { info!("Radio error = {}", err); diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs new file mode 100644 index 000000000..e6fadc01d --- /dev/null +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -0,0 +1,110 @@ +//! This example runs on the STM32 LoRa Discovery board, which has a builtin Semtech Sx1276 radio. +//! It demonstrates LORA P2P send functionality. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_lora::iv::Stm32l0InterfaceVariant; +use embassy_stm32::exti::{Channel, ExtiInput}; +use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; +use embassy_stm32::spi; +use embassy_stm32::time::khz; +use embassy_time::Delay; +use lora_phy::mod_params::*; +use lora_phy::sx1276_7_8_9::SX1276_7_8_9; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; + config.rcc.enable_hsi48 = true; + let p = embassy_stm32::init(config); + + // SPI for sx1276 + let spi = spi::Spi::new( + p.SPI1, + p.PB3, + p.PA7, + p.PA6, + p.DMA1_CH3, + p.DMA1_CH2, + khz(200), + spi::Config::default(), + ); + + let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); + let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); + + let irq_pin = Input::new(p.PB4.degrade(), Pull::Up); + let irq = ExtiInput::new(irq_pin, p.EXTI4.degrade()); + + let iv = Stm32l0InterfaceVariant::new(nss, reset, irq, None, None).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1276_7_8_9::new(BoardType::Stm32l0Sx1276, spi, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut tx_pkt_params = { + match lora.create_tx_packet_params(4, false, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora.prepare_for_tx(&mdltn_params, 17, true).await { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + let buffer = [0x01u8, 0x02u8, 0x03u8]; + match lora.tx(&mdltn_params, &mut tx_pkt_params, &buffer, 0xffffff).await { + Ok(()) => { + info!("TX DONE"); + } + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + match lora.sleep(&mut delay).await { + Ok(()) => info!("Sleep successful"), + Err(err) => info!("Sleep unsuccessful = {}", err), + } +} diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs deleted file mode 100644 index ea01f610c..000000000 --- a/examples/stm32l0/src/bin/lorawan.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! This example runs on the STM32 LoRa Discovery board which has a builtin Semtech Sx127x radio -#![no_std] -#![no_main] -#![macro_use] -#![allow(dead_code)] -#![feature(type_alias_impl_trait)] - -use embassy_executor::Spawner; -use embassy_lora::sx127x::*; -use embassy_lora::LoraTimer; -use embassy_stm32::exti::ExtiInput; -use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; -use embassy_stm32::rng::Rng; -use embassy_stm32::spi; -use embassy_stm32::time::khz; -use lorawan::default_crypto::DefaultFactory as Crypto; -use lorawan_device::async_device::{region, Device, JoinMode}; -use {defmt_rtt as _, panic_probe as _}; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut config = embassy_stm32::Config::default(); - config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; - config.rcc.enable_hsi48 = true; - let p = embassy_stm32::init(config); - - // SPI for sx127x - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - khz(200), - spi::Config::default(), - ); - - let cs = Output::new(p.PA15, Level::High, Speed::Low); - let reset = Output::new(p.PC0, Level::High, Speed::Low); - let _ = Input::new(p.PB1, Pull::None); - - let ready = Input::new(p.PB4, Pull::Up); - let ready_pin = ExtiInput::new(ready, p.EXTI4); - - let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); - - let region = region::Configuration::new(region::Region::EU868); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); - - defmt::info!("Joining LoRaWAN network"); - - // TODO: Adjust the EUI and Keys according to your network credentials - device - .join(&JoinMode::OTAA { - deveui: [0, 0, 0, 0, 0, 0, 0, 0], - appeui: [0, 0, 0, 0, 0, 0, 0, 0], - appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - }) - .await - .ok() - .unwrap(); - defmt::info!("LoRaWAN network joined"); - - defmt::info!("Sending 'PING'"); - device.send(b"PING", 1, false).await.ok().unwrap(); - defmt::info!("Message sent!"); -} - -pub struct DummySwitch; -impl RadioSwitch for DummySwitch { - fn set_rx(&mut self) {} - fn set_tx(&mut self) {} -} diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index f7cf03595..4bcc5420e 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -1,5 +1,5 @@ //! This example runs on a STM32WL board, which has a builtin Semtech Sx1262 radio. -//! It demonstrates LoRaWAN functionality. +//! It demonstrates LoRaWAN join functionality. #![no_std] #![no_main] #![macro_use] @@ -28,6 +28,8 @@ use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; use {defmt_rtt as _, panic_probe as _}; +const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -65,7 +67,7 @@ async fn main(_spawner: Spawner) { } }; let radio = LoRaRadio::new(lora); - let region: region::Configuration = region::Configuration::new(region::Region::EU868); + let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs new file mode 100644 index 000000000..bb31518c4 --- /dev/null +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs @@ -0,0 +1,127 @@ +//! This example runs on the STM32WL board, which has a builtin Semtech Sx1262 radio. +//! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. +#![no_std] +#![no_main] +#![macro_use] +#![feature(type_alias_impl_trait, async_fn_in_trait)] +#![allow(incomplete_features)] + +use defmt::info; +use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_executor::Spawner; +use embassy_lora::iv::Stm32wlInterfaceVariant; +use embassy_stm32::dma::NoDma; +use embassy_stm32::gpio::{Level, Output, Pin, Speed}; +use embassy_stm32::peripherals::SUBGHZSPI; +use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{interrupt, into_ref, Peripheral}; +use embassy_time::{Delay, Duration, Timer}; +use lora_phy::mod_params::*; +use lora_phy::sx1261_2::SX1261_2; +use lora_phy::LoRa; +use {defmt_rtt as _, panic_probe as _}; + +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; + let p = embassy_stm32::init(config); + + let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); + let mut spi_config = SpiConfig::default(); + spi_config.mode = MODE_0; + spi_config.bit_order = BitOrder::MsbFirst; + let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config); + + let spi = BlockingAsync::new(spi); + + let irq = interrupt::take!(SUBGHZ_RADIO); + into_ref!(irq); + // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx + let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); + let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); + let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); + let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap(); + + let mut delay = Delay; + + let mut lora = { + match LoRa::new(SX1261_2::new(BoardType::Stm32wlSx1262, spi, iv), false, &mut delay).await { + Ok(l) => l, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let mut debug_indicator = Output::new(p.PB9, Level::Low, Speed::Low); + let mut start_indicator = Output::new(p.PB15, Level::Low, Speed::Low); + + start_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + start_indicator.set_low(); + + let mut receiving_buffer = [00u8; 100]; + + let mdltn_params = { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { + Ok(mp) => mp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + let rx_pkt_params = { + match lora.create_rx_packet_params(4, false, receiving_buffer.len() as u8, true, false, &mdltn_params) { + Ok(pp) => pp, + Err(err) => { + info!("Radio error = {}", err); + return; + } + } + }; + + match lora + .prepare_for_rx(&mdltn_params, &rx_pkt_params, None, true, false, 0, 0x00ffffffu32) + .await + { + Ok(()) => {} + Err(err) => { + info!("Radio error = {}", err); + return; + } + }; + + loop { + receiving_buffer = [00u8; 100]; + match lora.rx(&rx_pkt_params, &mut receiving_buffer).await { + Ok((received_len, _rx_pkt_status)) => { + if (received_len == 3) + && (receiving_buffer[0] == 0x01u8) + && (receiving_buffer[1] == 0x02u8) + && (receiving_buffer[2] == 0x03u8) + { + info!("rx successful"); + debug_indicator.set_high(); + Timer::after(Duration::from_secs(5)).await; + debug_indicator.set_low(); + } else { + info!("rx unknown packet"); + } + } + Err(err) => info!("rx unsuccessful = {}", err), + } + } +} diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs index 11c35a8fe..8a38402fa 100644 --- a/examples/stm32wl/src/bin/lora_p2p_send.rs +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -23,6 +23,8 @@ use lora_phy::sx1261_2::SX1261_2; use lora_phy::LoRa; use {defmt_rtt as _, panic_probe as _}; +const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -58,7 +60,12 @@ async fn main(_spawner: Spawner) { }; let mdltn_params = { - match lora.create_modulation_params(SpreadingFactor::_10, Bandwidth::_250KHz, CodingRate::_4_8, 903900000) { + match lora.create_modulation_params( + SpreadingFactor::_10, + Bandwidth::_250KHz, + CodingRate::_4_8, + LORA_FREQUENCY_IN_HZ, + ) { Ok(mp) => mp, Err(err) => { info!("Radio error = {}", err); diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs deleted file mode 100644 index 32f29cc5d..000000000 --- a/examples/stm32wl/src/bin/lorawan.rs +++ /dev/null @@ -1,104 +0,0 @@ -#![no_std] -#![no_main] -#![macro_use] -#![allow(dead_code)] -#![feature(type_alias_impl_trait)] - -use embassy_executor::Spawner; -use embassy_lora::stm32wl::*; -use embassy_lora::LoraTimer; -use embassy_stm32::dma::NoDma; -use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; -use embassy_stm32::rng::Rng; -use embassy_stm32::subghz::*; -use embassy_stm32::{interrupt, pac}; -use lorawan::default_crypto::DefaultFactory as Crypto; -use lorawan_device::async_device::{region, Device, JoinMode}; -use {defmt_rtt as _, panic_probe as _}; - -struct RadioSwitch<'a> { - ctrl1: Output<'a, AnyPin>, - ctrl2: Output<'a, AnyPin>, - ctrl3: Output<'a, AnyPin>, -} - -impl<'a> RadioSwitch<'a> { - fn new(ctrl1: Output<'a, AnyPin>, ctrl2: Output<'a, AnyPin>, ctrl3: Output<'a, AnyPin>) -> Self { - Self { ctrl1, ctrl2, ctrl3 } - } -} - -impl<'a> embassy_lora::stm32wl::RadioSwitch for RadioSwitch<'a> { - fn set_rx(&mut self) { - self.ctrl1.set_high(); - self.ctrl2.set_low(); - self.ctrl3.set_high(); - } - - fn set_tx(&mut self) { - self.ctrl1.set_high(); - self.ctrl2.set_high(); - self.ctrl3.set_high(); - } -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut config = embassy_stm32::Config::default(); - config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; - config.rcc.enable_lsi = true; - let p = embassy_stm32::init(config); - - unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } - - let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High); - let ctrl2 = Output::new(p.PC4.degrade(), Level::High, Speed::High); - let ctrl3 = Output::new(p.PC5.degrade(), Level::High, Speed::High); - let rfs = RadioSwitch::new(ctrl1, ctrl2, ctrl3); - - let radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); - let irq = interrupt::take!(SUBGHZ_RADIO); - - let mut radio_config = SubGhzRadioConfig::default(); - radio_config.calibrate_image = CalibrateImage::ISM_863_870; - let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap(); - - let mut region = region::Configuration::new(region::Region::EU868); - - // NOTE: This is specific for TTN, as they have a special RX1 delay - region.set_receive_delay1(5000); - - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); - - // Depending on network, this might be part of JOIN - device.set_datarate(region::DR::_0); // SF12 - - // device.set_datarate(region::DR::_1); // SF11 - // device.set_datarate(region::DR::_2); // SF10 - // device.set_datarate(region::DR::_3); // SF9 - // device.set_datarate(region::DR::_4); // SF8 - // device.set_datarate(region::DR::_5); // SF7 - - defmt::info!("Joining LoRaWAN network"); - - // TODO: Adjust the EUI and Keys according to your network credentials - device - .join(&JoinMode::OTAA { - deveui: [0, 0, 0, 0, 0, 0, 0, 0], - appeui: [0, 0, 0, 0, 0, 0, 0, 0], - appkey: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - }) - .await - .ok() - .unwrap(); - defmt::info!("LoRaWAN network joined"); - - let mut rx: [u8; 255] = [0; 255]; - defmt::info!("Sending 'PING'"); - let len = device.send_recv(b"PING", &mut rx[..], 1, true).await.ok().unwrap(); - if len > 0 { - defmt::info!("Message sent, received downlink: {:?}", &rx[..len]); - } else { - defmt::info!("Message sent!"); - } -} From 2d7ba44621fa35abad07d2ddb8b253e815ce2c1f Mon Sep 17 00:00:00 2001 From: kbleeke Date: Sun, 2 Apr 2023 20:19:47 +0200 Subject: [PATCH 0940/1575] rework event handling to allow sending data --- src/bus.rs | 8 +--- src/consts.rs | 44 ++++++++++++++++++++ src/control.rs | 19 +++++---- src/events.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++++--- src/ioctl.rs | 6 +++ src/lib.rs | 13 ++++-- src/runner.rs | 33 +++++++-------- 7 files changed, 192 insertions(+), 42 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index add346b2f..e26f11120 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,11 +1,10 @@ -use core::slice; - use embassy_futures::yield_now; use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; use futures::FutureExt; use crate::consts::*; +use crate::slice8_mut; /// Custom Spi Trait that _only_ supports the bus operation of the cyw43 /// Implementors are expected to hold the CS pin low during an operation. @@ -327,8 +326,3 @@ fn swap16(x: u32) -> u32 { fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) } - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } -} diff --git a/src/consts.rs b/src/consts.rs index fee2d01ab..18502bd1a 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -109,6 +109,50 @@ pub(crate) const READ: bool = false; pub(crate) const INC_ADDR: bool = true; pub(crate) const FIXED_ADDR: bool = false; +#[allow(non_camel_case_types)] +#[derive(Copy, Clone)] +#[repr(u8)] +pub enum EStatus { + /// operation was successful + SUCCESS = 0, + /// operation failed + FAIL = 1, + /// operation timed out + TIMEOUT = 2, + /// failed due to no matching network found + NO_NETWORKS = 3, + /// operation was aborted + ABORT = 4, + /// protocol failure: packet not ack'd + NO_ACK = 5, + /// AUTH or ASSOC packet was unsolicited + UNSOLICITED = 6, + /// attempt to assoc to an auto auth configuration + ATTEMPT = 7, + /// scan results are incomplete + PARTIAL = 8, + /// scan aborted by another scan + NEWSCAN = 9, + /// scan aborted due to assoc in progress + NEWASSOC = 10, + /// 802.11h quiet period started + _11HQUIET = 11, + /// user disabled scanning (WLC_SET_SCANSUPPRESS) + SUPPRESS = 12, + /// no allowable channels to scan + NOCHANS = 13, + /// scan aborted due to CCX fast roam + CCXFASTRM = 14, + /// abort channel select + CS_ABORT = 15, +} + +impl PartialEq for u32 { + fn eq(&self, other: &EStatus) -> bool { + *self == *other as Self + } +} + #[allow(dead_code)] pub(crate) struct FormatStatus(pub u32); diff --git a/src/control.rs b/src/control.rs index 30d5d0924..824c55125 100644 --- a/src/control.rs +++ b/src/control.rs @@ -6,7 +6,7 @@ use embassy_time::{Duration, Timer}; pub use crate::bus::SpiBusCyw43; use crate::consts::*; -use crate::events::{Event, EventQueue}; +use crate::events::{Event, Events}; use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType}; use crate::structs::*; @@ -14,15 +14,15 @@ use crate::{countries, PowerManagementMode}; pub struct Control<'a> { state_ch: ch::StateRunner<'a>, - event_sub: &'a EventQueue, + events: &'a Events, ioctl_state: &'a IoctlState, } impl<'a> Control<'a> { - pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a EventQueue, ioctl_state: &'a IoctlState) -> Self { + pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { Self { state_ch, - event_sub, + events: event_sub, ioctl_state, } } @@ -195,24 +195,25 @@ impl<'a> Control<'a> { } async fn wait_for_join(&mut self, i: SsidInfo) { - let mut subscriber = self.event_sub.subscriber().unwrap(); + self.events.mask.enable(&[Event::JOIN, Event::AUTH]); + let mut subscriber = self.events.queue.subscriber().unwrap(); self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) .await; // set_ssid loop { let msg = subscriber.next_message_pure().await; - if msg.event_type == Event::AUTH && msg.status != 0 { + if msg.header.event_type == Event::AUTH && msg.header.status != 0 { // retry - warn!("JOIN failed with status={}", msg.status); + warn!("JOIN failed with status={}", msg.header.status); self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) .await; - } else if msg.event_type == Event::JOIN && msg.status == 0 { + } else if msg.header.event_type == Event::JOIN && msg.header.status == 0 { // successful join break; } } - + self.events.mask.disable_all(); self.state_ch.set_link_state(LinkState::Up); info!("JOINED"); } diff --git a/src/events.rs b/src/events.rs index 87f6c01a3..fbdfbc888 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![allow(non_camel_case_types)] -use core::num; +use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; @@ -284,13 +284,114 @@ pub enum Event { LAST = 190, } -pub type EventQueue = PubSubChannel; -pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, EventStatus, 2, 1, 1>; -pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, EventStatus, 2, 1, 1>; +// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. +pub type EventQueue = PubSubChannel; +pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, Message, 2, 1, 1>; +pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; + +pub struct Events { + pub queue: EventQueue, + pub mask: SharedEventMask, +} + +impl Events { + pub fn new() -> Self { + Self { + queue: EventQueue::new(), + mask: SharedEventMask::default(), + } + } +} #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EventStatus { +pub struct Status { pub event_type: Event, pub status: u32, } + +#[derive(Clone, Copy)] +pub enum Payload { + None, +} + +#[derive(Clone, Copy)] + +pub struct Message { + pub header: Status, + pub payload: Payload, +} + +impl Message { + pub fn new(status: Status, payload: Payload) -> Self { + Self { + header: status, + payload, + } + } +} + +const EVENT_BITS: usize = ((Event::LAST as usize + 31) & !31) / 32; + +#[derive(Default)] +struct EventMask { + mask: [u32; EVENT_BITS], +} + +impl EventMask { + fn enable(&mut self, event: Event) { + let n = event as u32; + let word = n >> 5; + let bit = n & 0b11111; + + self.mask[word as usize] |= (1 << bit); + } + + fn disable(&mut self, event: Event) { + let n = event as u32; + let word = n >> 5; + let bit = n & 0b11111; + + self.mask[word as usize] &= !(1 << bit); + } + + fn is_enabled(&self, event: Event) -> bool { + let n = event as u32; + let word = n >> 5; + let bit = n & 0b11111; + + self.mask[word as usize] & (1 << bit) > 0 + } +} + +#[derive(Default)] + +pub struct SharedEventMask { + mask: RefCell, +} + +impl SharedEventMask { + pub fn enable(&self, events: &[Event]) { + let mut mask = self.mask.borrow_mut(); + for event in events { + mask.enable(*event); + } + } + + pub fn disable(&self, events: &[Event]) { + let mut mask = self.mask.borrow_mut(); + for event in events { + mask.disable(*event); + } + } + + pub fn disable_all(&self) { + let mut mask = self.mask.borrow_mut(); + mask.mask = Default::default(); + } + + pub fn is_enabled(&self, event: Event) -> bool { + let mask = self.mask.borrow(); + mask.is_enabled(event) + } +} diff --git a/src/ioctl.rs b/src/ioctl.rs index 89b20a2d6..803934cf9 100644 --- a/src/ioctl.rs +++ b/src/ioctl.rs @@ -4,6 +4,8 @@ use core::task::{Poll, Waker}; use embassy_sync::waitqueue::WakerRegistration; +use crate::fmt::Bytes; + #[derive(Clone, Copy)] pub enum IoctlType { Get = 0, @@ -100,6 +102,8 @@ impl IoctlState { pub fn ioctl_done(&self, response: &[u8]) { if let IoctlStateInner::Sent { buf } = self.state.get() { + info!("IOCTL Response: {:02x}", Bytes(response)); + // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); @@ -107,6 +111,8 @@ impl IoctlState { resp_len: response.len(), }); self.wake_control(); + } else { + warn!("IOCTL Response but no pending Ioctl"); } } } diff --git a/src/lib.rs b/src/lib.rs index 069ca40f4..f9244bddb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,9 +18,11 @@ mod control; mod nvram; mod runner; +use core::slice; + use embassy_net_driver_channel as ch; use embedded_hal_1::digital::OutputPin; -use events::EventQueue; +use events::Events; use ioctl::IoctlState; use crate::bus::Bus; @@ -103,7 +105,7 @@ const CHIP: Chip = Chip { pub struct State { ioctl_state: IoctlState, ch: ch::State, - events: EventQueue, + events: Events, } impl State { @@ -111,7 +113,7 @@ impl State { Self { ioctl_state: IoctlState::new(), ch: ch::State::new(), - events: EventQueue::new(), + events: Events::new(), } } } @@ -225,3 +227,8 @@ where runner, ) } + +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +} diff --git a/src/runner.rs b/src/runner.rs index f0f6fceeb..554a711d8 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,5 +1,3 @@ -use core::slice; - use embassy_futures::select::{select3, Either3}; use embassy_net_driver_channel as ch; use embassy_sync::pubsub::PubSubBehavior; @@ -9,12 +7,12 @@ use embedded_hal_1::digital::OutputPin; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; use crate::consts::*; -use crate::events::{EventQueue, EventStatus}; +use crate::events::{Events, Status}; use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; use crate::nvram::NVRAM; use crate::structs::*; -use crate::{events, Core, CHIP, MTU}; +use crate::{events, slice8_mut, Core, CHIP, MTU}; #[cfg(feature = "firmware-logs")] struct LogState { @@ -45,7 +43,7 @@ pub struct Runner<'a, PWR, SPI> { sdpcm_seq: u8, sdpcm_seq_max: u8, - events: &'a EventQueue, + events: &'a Events, #[cfg(feature = "firmware-logs")] log: LogState, @@ -60,7 +58,7 @@ where ch: ch::Runner<'a, MTU>, bus: Bus, ioctl_state: &'a IoctlState, - events: &'a EventQueue, + events: &'a Events, ) -> Self { Self { ch, @@ -353,8 +351,6 @@ where panic!("IOCTL error {}", cdc_header.status as i32); } - info!("IOCTL Response: {:02x}", Bytes(response)); - self.ioctl_state.ioctl_done(response); } } @@ -406,11 +402,17 @@ where Bytes(evt_data) ); - if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { - self.events.publish_immediate(EventStatus { - status: event_packet.msg.status, - event_type: evt_type, - }); + if self.events.mask.is_enabled(evt_type) { + let status = event_packet.msg.status; + let event_payload = events::Payload::None; + + self.events.queue.publish_immediate(events::Message::new( + Status { + event_type: evt_type, + status, + }, + event_payload, + )); } } CHANNEL_TYPE_DATA => { @@ -548,8 +550,3 @@ where true } } - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } -} From 582a15a69320d987de5db3121af2f805f8508a6b Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 25 Apr 2023 18:38:17 +0200 Subject: [PATCH 0941/1575] cleanup EventMask --- src/events.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/events.rs b/src/events.rs index fbdfbc888..d6f114ed9 100644 --- a/src/events.rs +++ b/src/events.rs @@ -331,34 +331,34 @@ impl Message { } } -const EVENT_BITS: usize = ((Event::LAST as usize + 31) & !31) / 32; - #[derive(Default)] struct EventMask { - mask: [u32; EVENT_BITS], + mask: [u32; Self::WORD_COUNT], } impl EventMask { + const WORD_COUNT: usize = ((Event::LAST as u32 + (u32::BITS - 1)) / u32::BITS) as usize; + fn enable(&mut self, event: Event) { let n = event as u32; - let word = n >> 5; - let bit = n & 0b11111; + let word = n / u32::BITS; + let bit = n % u32::BITS; self.mask[word as usize] |= (1 << bit); } fn disable(&mut self, event: Event) { let n = event as u32; - let word = n >> 5; - let bit = n & 0b11111; + let word = n / u32::BITS; + let bit = n % u32::BITS; self.mask[word as usize] &= !(1 << bit); } fn is_enabled(&self, event: Event) -> bool { let n = event as u32; - let word = n >> 5; - let bit = n & 0b11111; + let word = n / u32::BITS; + let bit = n % u32::BITS; self.mask[word as usize] & (1 << bit) > 0 } From 9e96655757180d7fe32ebff1ed93a35a4c3cff28 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Tue, 25 Apr 2023 19:08:47 +0200 Subject: [PATCH 0942/1575] comment some choices for current event handling --- src/control.rs | 6 ++++-- src/runner.rs | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/control.rs b/src/control.rs index 824c55125..0c06009b9 100644 --- a/src/control.rs +++ b/src/control.rs @@ -197,18 +197,20 @@ impl<'a> Control<'a> { async fn wait_for_join(&mut self, i: SsidInfo) { self.events.mask.enable(&[Event::JOIN, Event::AUTH]); let mut subscriber = self.events.queue.subscriber().unwrap(); + // the actual join operation starts here + // we make sure to enable events before so we don't miss any self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) .await; // set_ssid loop { let msg = subscriber.next_message_pure().await; - if msg.header.event_type == Event::AUTH && msg.header.status != 0 { + if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { // retry warn!("JOIN failed with status={}", msg.header.status); self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) .await; - } else if msg.header.event_type == Event::JOIN && msg.header.status == 0 { + } else if msg.header.event_type == Event::JOIN && msg.header.status == EStatus::SUCCESS { // successful join break; } diff --git a/src/runner.rs b/src/runner.rs index 554a711d8..806ddfc49 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -406,6 +406,10 @@ where let status = event_packet.msg.status; let event_payload = events::Payload::None; + // this intentionally uses the non-blocking publish immediate + // publish() is a deadlock risk in the current design as awaiting here prevents ioctls + // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event + // (if they are actively awaiting the queue) self.events.queue.publish_immediate(events::Message::new( Status { event_type: evt_type, From f729d2d060043889eacb04dc924757a536eb247f Mon Sep 17 00:00:00 2001 From: ceekdee Date: Tue, 25 Apr 2023 13:51:19 -0500 Subject: [PATCH 0943/1575] Deprecate original LoRa drivers. Update rust-lorawan releases. --- embassy-lora/Cargo.toml | 4 +- embassy-lora/src/stm32wl/mod.rs | 1 + embassy-stm32/src/lib.rs | 1 + examples/nrf52840/Cargo.toml | 4 +- examples/rp/Cargo.toml | 4 +- examples/stm32l0/Cargo.toml | 4 +- examples/stm32wl/Cargo.toml | 4 +- examples/stm32wl/src/bin/subghz.rs | 119 ----------------------------- 8 files changed, 12 insertions(+), 129 deletions(-) delete mode 100644 examples/stm32wl/src/bin/subghz.rs diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 552788898..692a82040 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -40,5 +40,5 @@ embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } lora-phy = { version = "1", optional = true } -lorawan-device = { version = "0.9.0", path = "../../rust-lorawan/device", default-features = false, features = ["async"] } -lorawan = { version = "0.7.2", path = "../../rust-lorawan/encoding", default-features = false } +lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } +lorawan = { version = "0.7.3", default-features = false } diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index d76e8c43b..dae9a195c 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,4 +1,5 @@ //! A radio driver integration for the radio found on STM32WL family devices. +#![allow(deprecated)] use core::future::poll_fn; use core::task::Poll; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index bbde2da57..f0fc152ce 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -56,6 +56,7 @@ pub mod sdmmc; #[cfg(spi)] pub mod spi; #[cfg(stm32wl)] +#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] pub mod subghz; #[cfg(usart)] pub mod usart; diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index a1bcc4b6f..be8b5328e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -20,8 +20,8 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } lora-phy = { version = "1" } -lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true } -lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } +lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 5f0a397ea..45af8762e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -17,8 +17,8 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"] } lora-phy = { version = "1" } -lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] } -lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] } +lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } +lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index fe4b4f3c3..c51f1b904 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -15,8 +15,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["ni embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } lora-phy = { version = "1" } -lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"], optional = true } -lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } +lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 45e720f01..0eb24bc44 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -12,8 +12,8 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt", "external-lora-phy"] } lora-phy = { version = "1" } -lorawan-device = { version = "0.9.0", path = "../../../rust-lorawan/device", default-features = false, features = ["async", "external-lora-phy"] } -lorawan = { version = "0.7.2", path = "../../../rust-lorawan/encoding", default-features = false, features = ["default-crypto"] } +lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } +lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs deleted file mode 100644 index 32c8b5515..000000000 --- a/examples/stm32wl/src/bin/subghz.rs +++ /dev/null @@ -1,119 +0,0 @@ -#![no_std] -#![no_main] -#![macro_use] -#![allow(dead_code)] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; -use embassy_stm32::exti::ExtiInput; -use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; -use embassy_stm32::interrupt::{Interrupt, InterruptExt}; -use embassy_stm32::subghz::*; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::signal::Signal; -use {defmt_rtt as _, panic_probe as _}; - -const PING_DATA: &str = "PING"; -const DATA_LEN: u8 = PING_DATA.len() as u8; -const PING_DATA_BYTES: &[u8] = PING_DATA.as_bytes(); -const PREAMBLE_LEN: u16 = 5 * 8; - -const RF_FREQ: RfFreq = RfFreq::from_frequency(867_500_000); - -const SYNC_WORD: [u8; 8] = [0x79, 0x80, 0x0C, 0xC0, 0x29, 0x95, 0xF8, 0x4A]; -const SYNC_WORD_LEN: u8 = SYNC_WORD.len() as u8; -const SYNC_WORD_LEN_BITS: u8 = SYNC_WORD_LEN * 8; - -const TX_BUF_OFFSET: u8 = 128; -const RX_BUF_OFFSET: u8 = 0; -const LORA_PACKET_PARAMS: LoRaPacketParams = LoRaPacketParams::new() - .set_crc_en(true) - .set_preamble_len(PREAMBLE_LEN) - .set_payload_len(DATA_LEN) - .set_invert_iq(false) - .set_header_type(HeaderType::Fixed); - -const LORA_MOD_PARAMS: LoRaModParams = LoRaModParams::new() - .set_bw(LoRaBandwidth::Bw125) - .set_cr(CodingRate::Cr45) - .set_ldro_en(true) - .set_sf(SpreadingFactor::Sf7); - -// configuration for +10 dBm output power -// see table 35 "PA optimal setting and operating modes" -const PA_CONFIG: PaConfig = PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp); - -const TCXO_MODE: TcxoMode = TcxoMode::new() - .set_txco_trim(TcxoTrim::Volts1pt7) - .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(10))); - -const TX_PARAMS: TxParams = TxParams::new().set_power(0x0D).set_ramp_time(RampTime::Micros40); - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let mut config = embassy_stm32::Config::default(); - config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; - let p = embassy_stm32::init(config); - - let mut led1 = Output::new(p.PB15, Level::High, Speed::Low); - let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low); - let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low); - - let button = Input::new(p.PA0, Pull::Up); - let mut pin = ExtiInput::new(button, p.EXTI0); - - static IRQ_SIGNAL: Signal = Signal::new(); - let radio_irq = interrupt::take!(SUBGHZ_RADIO); - radio_irq.set_handler(|_| { - IRQ_SIGNAL.signal(()); - unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); - }); - - let mut radio = SubGhz::new(p.SUBGHZSPI, NoDma, NoDma); - - defmt::info!("Radio ready for use"); - - led1.set_low(); - - led2.set_high(); - - unwrap!(radio.set_standby(StandbyClk::Rc)); - unwrap!(radio.set_tcxo_mode(&TCXO_MODE)); - unwrap!(radio.set_standby(StandbyClk::Hse)); - unwrap!(radio.set_regulator_mode(RegMode::Ldo)); - unwrap!(radio.set_buffer_base_address(TX_BUF_OFFSET, RX_BUF_OFFSET)); - unwrap!(radio.set_pa_config(&PA_CONFIG)); - unwrap!(radio.set_pa_ocp(Ocp::Max60m)); - unwrap!(radio.set_tx_params(&TX_PARAMS)); - unwrap!(radio.set_packet_type(PacketType::LoRa)); - unwrap!(radio.set_lora_sync_word(LoRaSyncWord::Public)); - unwrap!(radio.set_lora_mod_params(&LORA_MOD_PARAMS)); - unwrap!(radio.set_lora_packet_params(&LORA_PACKET_PARAMS)); - unwrap!(radio.calibrate_image(CalibrateImage::ISM_863_870)); - unwrap!(radio.set_rf_frequency(&RF_FREQ)); - - defmt::info!("Status: {:?}", unwrap!(radio.status())); - - led2.set_low(); - - loop { - pin.wait_for_rising_edge().await; - led3.set_high(); - unwrap!(radio.set_irq_cfg(&CfgIrq::new().irq_enable_all(Irq::TxDone))); - unwrap!(radio.write_buffer(TX_BUF_OFFSET, PING_DATA_BYTES)); - unwrap!(radio.set_tx(Timeout::DISABLED)); - - radio_irq.enable(); - IRQ_SIGNAL.wait().await; - - let (_, irq_status) = unwrap!(radio.irq_status()); - if irq_status & Irq::TxDone.mask() != 0 { - defmt::info!("TX done"); - } - unwrap!(radio.clear_irq_status(irq_status)); - led3.set_low(); - } -} From 0d82ebea29d5bad6d1b40258419a215c44786e1d Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Apr 2023 17:35:01 -0500 Subject: [PATCH 0944/1575] stm32/rtc: fix datetime and add f4 test --- embassy-stm32/src/rtc/datetime.rs | 18 +++++------ tests/stm32/Cargo.toml | 10 +++++- tests/stm32/src/bin/rtc.rs | 52 +++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 tests/stm32/src/bin/rtc.rs diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index 6274c1e05..0a590c1bb 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -51,7 +51,7 @@ pub struct DateTime { impl From for DateTime { fn from(date_time: chrono::NaiveDateTime) -> Self { Self { - year: (date_time.year() - 1970) as u16, + year: date_time.year() as u16, month: date_time.month() as u8, day: date_time.day() as u8, day_of_week: date_time.weekday().into(), @@ -65,14 +65,10 @@ impl From for DateTime { #[cfg(feature = "chrono")] impl From for chrono::NaiveDateTime { fn from(date_time: DateTime) -> Self { - NaiveDate::from_ymd_opt( - (date_time.year + 1970) as i32, - date_time.month as u32, - date_time.day as u32, - ) - .unwrap() - .and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32) - .unwrap() + NaiveDate::from_ymd_opt(date_time.year as i32, date_time.month as u32, date_time.day as u32) + .unwrap() + .and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32) + .unwrap() } } @@ -159,6 +155,8 @@ pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) { let (yt, yu) = byte_to_bcd2(yr_offset); unsafe { + use crate::pac::rtc::vals::Ampm; + rtc.tr().write(|w| { w.set_ht(ht); w.set_hu(hu); @@ -166,7 +164,7 @@ pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) { w.set_mnu(mnu); w.set_st(st); w.set_su(su); - w.set_pm(stm32_metapac::rtc::vals::Ampm::AM); + w.set_pm(Ampm::AM); }); rtc.dr().write(|w| { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 3047c34ce..c48d26456 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [features] stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono"] # Nucleo stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo @@ -16,6 +16,7 @@ stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] +chrono = ["embassy-stm32/chrono", "dep:chrono"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } @@ -33,6 +34,8 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } +chrono = { version = "^0.4", default-features = false, optional = true} + # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. [[bin]] @@ -40,6 +43,11 @@ name = "gpio" path = "src/bin/gpio.rs" required-features = [] +[[bin]] +name = "rtc" +path = "src/bin/rtc.rs" +required-features = [ "chrono",] + [[bin]] name = "sdmmc" path = "src/bin/sdmmc.rs" diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs new file mode 100644 index 000000000..ccf2ca609 --- /dev/null +++ b/tests/stm32/src/bin/rtc.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// required-features: chrono + +#[path = "../example_common.rs"] +mod example_common; +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::assert; +use embassy_executor::Spawner; +use embassy_stm32::pac; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_time::{Duration, Timer}; +use example_common::*; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(config()); + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + info!("Starting LSI"); + + unsafe { + pac::RCC.csr().modify(|w| w.set_lsion(true)); + while !pac::RCC.csr().read().lsirdy() {} + } + + info!("Started LSI"); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + info!("Waiting 5 seconds"); + Timer::after(Duration::from_millis(5000)).await; + + let then: NaiveDateTime = rtc.now().unwrap().into(); + let seconds = (then - now).num_seconds(); + + defmt::info!("measured = {}", seconds); + + assert!(seconds > 3 && seconds < 7); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 123c11042719648265d22f0d42b9f9e0ff8d73fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Apr 2023 16:19:23 +0200 Subject: [PATCH 0945/1575] Revert "Workaround regex breaking change." This reverts commit 6a1a3e6877053b1b72adb3c1446f4f077ad3b03e. --- examples/rpi-pico-w/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index d211f9260..dca796e3d 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -25,9 +25,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-io = { version = "0.4.0", features = ["async", "defmt"] } heapless = "0.7.15" -[build-dependencies] -# Workaround https://github.com/embassy-rs/cyw43/issues/68 -regex = { version = "~1.7.3", default-features = false } [patch.crates-io] embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } From 0c7ce803849779af2ac6a2d3df401f5dafd57323 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Apr 2023 16:20:23 +0200 Subject: [PATCH 0946/1575] Fix missing defmt impl. --- src/structs.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/structs.rs b/src/structs.rs index 6d5d31b06..f54ec7fcf 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -115,7 +115,6 @@ impl SdpcmHeader { } #[derive(Debug, Clone, Copy)] -// #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C, packed(2))] pub struct CdcHeader { pub cmd: u32, @@ -126,6 +125,25 @@ pub struct CdcHeader { } impl_bytes!(CdcHeader); +#[cfg(feature = "defmt")] +impl defmt::Format for CdcHeader { + fn format(&self, fmt: defmt::Formatter) { + fn copy(t: T) -> T { + t + } + + defmt::write!( + fmt, + "CdcHeader{{cmd: {=u32:08x}, len: {=u32:08x}, flags: {=u16:04x}, id: {=u16:04x}, status: {=u32:08x}}}", + copy(self.cmd), + copy(self.len), + copy(self.flags), + copy(self.id), + copy(self.status), + ) + } +} + impl CdcHeader { pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { if packet.len() < Self::SIZE { From 0a2d6f0be069233bdfa9d9eee6f41184fdda72f3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Apr 2023 16:20:49 +0200 Subject: [PATCH 0947/1575] ci: build with DEFMT_LOG=trace to catch all defmt issues. --- .vscode/settings.json | 1 - ci.sh | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 344307695..dd479929e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,6 @@ "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.linkedProjects": [ "examples/rpi-pico-w/Cargo.toml", - "cyw43-pio/Cargo.toml", ], "rust-analyzer.server.extraEnv": { "WIFI_NETWORK": "foo", diff --git a/ci.sh b/ci.sh index 1b33564fb..d41b3aa81 100755 --- a/ci.sh +++ b/ci.sh @@ -2,6 +2,8 @@ set -euxo pipefail +export DEFMT_LOG=trace + # build examples #================== From 054ca17f660bd7fc760854293248ffc6a092b6da Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Apr 2023 17:00:51 +0200 Subject: [PATCH 0948/1575] Switch from probe-run to probe-rs-cli. - probe-run screwed up the last release 2 weeks ago and it's still not fixed (issue 391). Doesn't look well maintained. - Even when it's not broken, it lags behind probe-rs-cli in new chips support because it's slow in updating probe-rs. --- README.md | 4 ++-- examples/boot/application/nrf/.cargo/config.toml | 4 ++-- examples/boot/application/rp/.cargo/config.toml | 2 +- examples/boot/application/stm32f3/.cargo/config.toml | 4 ++-- examples/boot/application/stm32f7/.cargo/config.toml | 4 ++-- examples/boot/application/stm32h7/.cargo/config.toml | 4 ++-- examples/boot/application/stm32l0/.cargo/config.toml | 4 ++-- examples/boot/application/stm32l1/.cargo/config.toml | 4 ++-- examples/boot/application/stm32l4/.cargo/config.toml | 4 ++-- examples/boot/application/stm32wl/.cargo/config.toml | 4 ++-- examples/boot/bootloader/nrf/.cargo/config.toml | 2 +- examples/boot/bootloader/rp/.cargo/config.toml | 2 +- examples/nrf-rtos-trace/.cargo/config.toml | 4 ++-- examples/nrf52840/.cargo/config.toml | 4 ++-- examples/nrf52840/src/bin/nvmc.rs | 2 +- examples/nrf52840/src/bin/wdt.rs | 2 +- examples/nrf5340/.cargo/config.toml | 4 ++-- examples/stm32c0/.cargo/config.toml | 2 +- examples/stm32f0/.cargo/config.toml | 2 +- examples/stm32f1/.cargo/config.toml | 4 ++-- examples/stm32f2/.cargo/config.toml | 4 ++-- examples/stm32f3/.cargo/config.toml | 4 ++-- examples/stm32f4/.cargo/config.toml | 4 ++-- examples/stm32f7/.cargo/config.toml | 4 ++-- examples/stm32g0/.cargo/config.toml | 4 ++-- examples/stm32g4/.cargo/config.toml | 4 ++-- examples/stm32h7/.cargo/config.toml | 2 +- examples/stm32l0/.cargo/config.toml | 4 ++-- examples/stm32l1/.cargo/config.toml | 4 ++-- examples/stm32l4/.cargo/config.toml | 8 ++++---- examples/stm32l5/.cargo/config.toml | 4 ++-- examples/stm32u5/.cargo/config.toml | 4 ++-- examples/stm32wb/.cargo/config.toml | 4 ++-- examples/stm32wl/.cargo/config.toml | 4 ++-- 34 files changed, 62 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 33f38dafe..270f27a8f 100644 --- a/README.md +++ b/README.md @@ -106,10 +106,10 @@ git submodule init git submodule update ``` -- Install `probe-run` with defmt support. +- Install `probe-rs-cli` with defmt support. ```bash -cargo install probe-run +cargo install probe-rs-cli ``` - Change directory to the sample's base directory. For example: diff --git a/examples/boot/application/nrf/.cargo/config.toml b/examples/boot/application/nrf/.cargo/config.toml index 8ca28df39..3872e7189 100644 --- a/examples/boot/application/nrf/.cargo/config.toml +++ b/examples/boot/application/nrf/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/examples/boot/application/rp/.cargo/config.toml b/examples/boot/application/rp/.cargo/config.toml index edbd0a867..278c24a57 100644 --- a/examples/boot/application/rp/.cargo/config.toml +++ b/examples/boot/application/rp/.cargo/config.toml @@ -3,7 +3,7 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip RP2040" +runner = "probe-rs-cli run --chip RP2040" [build] target = "thumbv6m-none-eabi" diff --git a/examples/boot/application/stm32f3/.cargo/config.toml b/examples/boot/application/stm32f3/.cargo/config.toml index a76d6cab4..9fc2396e8 100644 --- a/examples/boot/application/stm32f3/.cargo/config.toml +++ b/examples/boot/application/stm32f3/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32F303VCTx" +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F303VCTx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32f7/.cargo/config.toml b/examples/boot/application/stm32f7/.cargo/config.toml index a90e1ccbb..17388cda9 100644 --- a/examples/boot/application/stm32f7/.cargo/config.toml +++ b/examples/boot/application/stm32f7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32F767ZITx -v" +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F767ZITx -v" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32h7/.cargo/config.toml b/examples/boot/application/stm32h7/.cargo/config.toml index fefdd370e..067a5c89b 100644 --- a/examples/boot/application/stm32h7/.cargo/config.toml +++ b/examples/boot/application/stm32h7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32H743ZITx" +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32H743ZITx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32l0/.cargo/config.toml b/examples/boot/application/stm32l0/.cargo/config.toml index 2627967ab..ce0e460bd 100644 --- a/examples/boot/application/stm32l0/.cargo/config.toml +++ b/examples/boot/application/stm32l0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32L072CZTx" +# replace your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32L072CZTx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/boot/application/stm32l1/.cargo/config.toml b/examples/boot/application/stm32l1/.cargo/config.toml index 404b6b55c..1401500a0 100644 --- a/examples/boot/application/stm32l1/.cargo/config.toml +++ b/examples/boot/application/stm32l1/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32L151CBxxA" +# replace your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32L151CBxxA" [build] target = "thumbv7m-none-eabi" diff --git a/examples/boot/application/stm32l4/.cargo/config.toml b/examples/boot/application/stm32l4/.cargo/config.toml index 43520e323..48ff3734b 100644 --- a/examples/boot/application/stm32l4/.cargo/config.toml +++ b/examples/boot/application/stm32l4/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32L475VG" +# replace your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32L475VG" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32wl/.cargo/config.toml b/examples/boot/application/stm32wl/.cargo/config.toml index e395d75b4..b49b582e0 100644 --- a/examples/boot/application/stm32wl/.cargo/config.toml +++ b/examples/boot/application/stm32wl/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32WLE5JCIx" +# replace your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32WLE5JCIx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/bootloader/nrf/.cargo/config.toml b/examples/boot/bootloader/nrf/.cargo/config.toml index 1060800a3..d636b1d23 100644 --- a/examples/boot/bootloader/nrf/.cargo/config.toml +++ b/examples/boot/bootloader/nrf/.cargo/config.toml @@ -4,7 +4,7 @@ build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] #runner = "./fruitrunner" -runner = "probe-run --chip nrf52840_xxAA" +runner = "probe-rs-cli run --chip nrf52840_xxAA" rustflags = [ # Code-size optimizations. diff --git a/examples/boot/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml index 18bd4dfe8..795ee043a 100644 --- a/examples/boot/bootloader/rp/.cargo/config.toml +++ b/examples/boot/bootloader/rp/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip RP2040" +runner = "probe-rs-cli run --chip RP2040" [build] target = "thumbv6m-none-eabi" diff --git a/examples/nrf-rtos-trace/.cargo/config.toml b/examples/nrf-rtos-trace/.cargo/config.toml index 8ca28df39..3872e7189 100644 --- a/examples/nrf-rtos-trace/.cargo/config.toml +++ b/examples/nrf-rtos-trace/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/examples/nrf52840/.cargo/config.toml b/examples/nrf52840/.cargo/config.toml index 8ca28df39..3872e7189 100644 --- a/examples/nrf52840/.cargo/config.toml +++ b/examples/nrf52840/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/examples/nrf52840/src/bin/nvmc.rs b/examples/nrf52840/src/bin/nvmc.rs index 75d090fbb..33a44516d 100644 --- a/examples/nrf52840/src/bin/nvmc.rs +++ b/examples/nrf52840/src/bin/nvmc.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Hello NVMC!"); - // probe-run breaks without this, I'm not sure why. + // probe-rs-cli run breaks without this, I'm not sure why. Timer::after(Duration::from_secs(1)).await; let mut f = Nvmc::new(p.NVMC); diff --git a/examples/nrf52840/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs index b0b9c3b81..ccfd0e439 100644 --- a/examples/nrf52840/src/bin/wdt.rs +++ b/examples/nrf52840/src/bin/wdt.rs @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.timeout_ticks = 32768 * 3; // 3 seconds - // This is needed for `probe-run` to be able to catch the panic message + // This is needed for `probe-rs-cli run` to be able to catch the panic message // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. config.run_during_debug_halt = false; diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml index ff0879c8c..d25355894 100644 --- a/examples/nrf5340/.cargo/config.toml +++ b/examples/nrf5340/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF5340_xxAA with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip nRF5340_xxAA" +# replace nRF5340_xxAA with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip nRF5340_xxAA" [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/stm32c0/.cargo/config.toml b/examples/stm32c0/.cargo/config.toml index eb07f6190..517101fae 100644 --- a/examples/stm32c0/.cargo/config.toml +++ b/examples/stm32c0/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32G071C8Rx with your chip as listed in `probe-run --list-chips` +# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` runner = "probe-rs-cli run --speed 100 --chip STM32c031c6tx" [build] diff --git a/examples/stm32f0/.cargo/config.toml b/examples/stm32f0/.cargo/config.toml index 16abc29bc..bd0c0cd97 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 STM32F091RCTX' +runner = 'probe-rs-cli run --chip STM32F091RCTX' [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32f1/.cargo/config.toml b/examples/stm32f1/.cargo/config.toml index e61e739fe..81199c5aa 100644 --- a/examples/stm32f1/.cargo/config.toml +++ b/examples/stm32f1/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F103C8 with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32F103C8" +# replace STM32F103C8 with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F103C8" [build] target = "thumbv7m-none-eabi" diff --git a/examples/stm32f2/.cargo/config.toml b/examples/stm32f2/.cargo/config.toml index 197fadf92..5532779c8 100644 --- a/examples/stm32f2/.cargo/config.toml +++ b/examples/stm32f2/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F207ZGTx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32F207ZGTx" +# replace STM32F207ZGTx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F207ZGTx" [build] target = "thumbv7m-none-eabi" diff --git a/examples/stm32f3/.cargo/config.toml b/examples/stm32f3/.cargo/config.toml index d4bcd263d..7f3fda529 100644 --- a/examples/stm32f3/.cargo/config.toml +++ b/examples/stm32f3/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32F303ZETx" +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F303ZETx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32f4/.cargo/config.toml b/examples/stm32f4/.cargo/config.toml index 4d4363c05..bed04b68f 100644 --- a/examples/stm32f4/.cargo/config.toml +++ b/examples/stm32f4/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32F429ZITx" +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F429ZITx" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml index b07ad158c..7d6c88a99 100644 --- a/examples/stm32f7/.cargo/config.toml +++ b/examples/stm32f7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32F767ZITx" +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F767ZITx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32g0/.cargo/config.toml b/examples/stm32g0/.cargo/config.toml index 4f776d68f..a7a5fbd84 100644 --- a/examples/stm32g0/.cargo/config.toml +++ b/examples/stm32g0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32G071C8Rx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32G071RBTx" +# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32G071RBTx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32g4/.cargo/config.toml b/examples/stm32g4/.cargo/config.toml index 99feae119..606d7d5a3 100644 --- a/examples/stm32g4/.cargo/config.toml +++ b/examples/stm32g4/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32G071C8Rx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32G484VETx" +# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32G484VETx" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml index d38be23e0..f08f57a54 100644 --- a/examples/stm32h7/.cargo/config.toml +++ b/examples/stm32h7/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv7em-none-eabihf] -runner = 'probe-run --chip STM32H743ZITx' +runner = 'probe-rs-cli run --chip STM32H743ZITx' [build] target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml index a81a48f97..526f5a1f7 100644 --- a/examples/stm32l0/.cargo/config.toml +++ b/examples/stm32l0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32L053R8Tx" +# replace your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32L053R8Tx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32l1/.cargo/config.toml b/examples/stm32l1/.cargo/config.toml index 404b6b55c..1401500a0 100644 --- a/examples/stm32l1/.cargo/config.toml +++ b/examples/stm32l1/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32L151CBxxA" +# replace your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32L151CBxxA" [build] target = "thumbv7m-none-eabi" diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index 5534053c5..abf55eb2e 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -1,8 +1,8 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` -#runner = "probe-run --chip STM32L475VGT6" -#runner = "probe-run --chip STM32L475VG" -runner = "probe-run --chip STM32L4S5VI" +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +#runner = "probe-rs-cli run --chip STM32L475VGT6" +#runner = "probe-rs-cli run --chip STM32L475VG" +runner = "probe-rs-cli run --chip STM32L4S5VI" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l5/.cargo/config.toml b/examples/stm32l5/.cargo/config.toml index f2af6b556..1dc3a6fb7 100644 --- a/examples/stm32l5/.cargo/config.toml +++ b/examples/stm32l5/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32L552ZETxQ with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32L552ZETxQ" +# replace STM32L552ZETxQ with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32L552ZETxQ" [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/stm32u5/.cargo/config.toml b/examples/stm32u5/.cargo/config.toml index 975630a14..cecd01938 100644 --- a/examples/stm32u5/.cargo/config.toml +++ b/examples/stm32u5/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32U585AIIx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32U585AIIx" +# replace STM32U585AIIx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32U585AIIx" [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index fcf95741a..5d78d79e5 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32WB55CCUx with your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" +# replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32wl/.cargo/config.toml b/examples/stm32wl/.cargo/config.toml index e395d75b4..b49b582e0 100644 --- a/examples/stm32wl/.cargo/config.toml +++ b/examples/stm32wl/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-run --list-chips` -runner = "probe-run --chip STM32WLE5JCIx" +# replace your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32WLE5JCIx" [build] target = "thumbv7em-none-eabihf" From 91047c61b9f2946805befa5d229f314b35ded43c Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 26 Apr 2023 10:18:40 -0500 Subject: [PATCH 0949/1575] Correct nightly feature specification. --- examples/nrf52840/Cargo.toml | 4 ++-- examples/stm32l0/Cargo.toml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index be8b5328e..0fe331e98 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,8 +6,8 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", - "embassy-lora", "lorawan-device", "lorawan"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", + "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index c51f1b904..c168dee24 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -6,7 +6,8 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -nightly = ["embassy-stm32/nightly", "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] +nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", + "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } From 52decfb16c4e7eb6de23d3958d6be4830488fbec Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 26 Apr 2023 10:51:02 -0500 Subject: [PATCH 0950/1575] Add nightly feature specification for lora-phy. --- examples/nrf52840/Cargo.toml | 4 ++-- examples/stm32l0/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 0fe331e98..1020e2805 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", - "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lorawan-device", "lorawan"] + "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lora-phy", "lorawan-device", "lorawan"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -19,7 +19,7 @@ embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defm embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } -lora-phy = { version = "1" } +lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index c168dee24..4e1830b29 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", - "embassy-lora", "lorawan-device", "lorawan", "embedded-io/async"] + "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "embedded-io/async"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } @@ -15,7 +15,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } -lora-phy = { version = "1" } +lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } From 0c8e5f92c7ebe6fd148a986baaaa6ac746d939c2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Apr 2023 18:10:39 +0200 Subject: [PATCH 0951/1575] Switch from probe-run to probe-rs-cli. --- README.md | 2 +- examples/rpi-pico-w/.cargo/config.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d24ec82f3..d0920aae2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ TODO: ## Running the example -- `cargo install probe-run` +- `cargo install probe-rs-cli` - `cd examples/rpi-pico-w` - `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release` diff --git a/examples/rpi-pico-w/.cargo/config.toml b/examples/rpi-pico-w/.cargo/config.toml index 6183e70f7..f1ed8af96 100644 --- a/examples/rpi-pico-w/.cargo/config.toml +++ b/examples/rpi-pico-w/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip RP2040" +runner = "probe-rs-cli run --chip RP2040" [build] target = "thumbv6m-none-eabi" From 405649ddc79bfad8a7f7521389da3f021df78869 Mon Sep 17 00:00:00 2001 From: Malte Brieske Date: Wed, 26 Apr 2023 18:58:28 +0200 Subject: [PATCH 0952/1575] fix stm32f7 example runner command for probe-rs-cli --- examples/boot/application/stm32f7/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/boot/application/stm32f7/.cargo/config.toml b/examples/boot/application/stm32f7/.cargo/config.toml index 17388cda9..7d6c88a99 100644 --- a/examples/boot/application/stm32f7/.cargo/config.toml +++ b/examples/boot/application/stm32f7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F767ZITx -v" +runner = "probe-rs-cli run --chip STM32F767ZITx" [build] target = "thumbv7em-none-eabihf" From edef790e1a41f58c594127b56ca5c477b1007e57 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 26 Apr 2023 20:45:59 +0200 Subject: [PATCH 0953/1575] build fixes for stable --- examples/nrf52840/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 1020e2805..59f30a9be 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -13,7 +13,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 4e1830b29..ca022e254 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -12,7 +12,7 @@ nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstab [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +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 = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } lora-phy = { version = "1", optional = true } From f8b359dc5ade1e3961b0534941ee2cf3756e09d8 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 26 Apr 2023 20:03:43 +0200 Subject: [PATCH 0954/1575] Add support for setting up the nRFs internal DCDCs --- embassy-nrf/src/lib.rs | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 8defc551b..6e7aaa57a 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -184,6 +184,34 @@ pub mod config { NotConfigured, } + /// Settings for enabling the built in DCDC converters. + #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] + pub struct DcdcConfig { + /// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used. + #[cfg(feature = "nrf52840")] + pub reg0: bool, + /// Config for the second stage DCDC (VDD -> DEC4), if disabled LDO will be used. + pub reg1: bool, + } + + /// Settings for enabling the built in DCDC converters. + #[cfg(feature = "_nrf5340-app")] + pub struct DcdcConfig { + /// Config for the high voltage stage, if disabled LDO will be used. + pub regh: bool, + /// Config for the main rail, if disabled LDO will be used. + pub regmain: bool, + /// Config for the radio rail, if disabled LDO will be used. + pub regradio: bool, + } + + /// Settings for enabling the built in DCDC converter. + #[cfg(feature = "_nrf9160")] + pub struct DcdcConfig { + /// Config for the main rail, if disabled LDO will be used. + pub regmain: bool, + } + /// Configuration for peripherals. Default configuration should work on any nRF chip. #[non_exhaustive] pub struct Config { @@ -191,6 +219,9 @@ pub mod config { pub hfclk_source: HfclkSource, /// Low frequency clock source. pub lfclk_source: LfclkSource, + #[cfg(not(feature = "_nrf5340-net"))] + /// DCDC configuration. + pub dcdc: DcdcConfig, /// GPIOTE interrupt priority. Should be lower priority than softdevice if used. #[cfg(feature = "gpiote")] pub gpiote_interrupt_priority: crate::interrupt::Priority, @@ -209,6 +240,20 @@ pub mod config { // xtals if they know they have them. hfclk_source: HfclkSource::Internal, lfclk_source: LfclkSource::InternalRC, + #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] + dcdc: DcdcConfig { + #[cfg(feature = "nrf52840")] + reg0: false, + reg1: false, + }, + #[cfg(feature = "_nrf5340-app")] + dcdc: DcdcConfig { + regh: false, + regmain: false, + regradio: false, + }, + #[cfg(feature = "_nrf9160")] + dcdc: DcdcConfig { regmain: false }, #[cfg(feature = "gpiote")] gpiote_interrupt_priority: crate::interrupt::Priority::P0, #[cfg(feature = "_time-driver")] @@ -454,6 +499,41 @@ pub fn init(config: config::Config) -> Peripherals { r.tasks_lfclkstart.write(|w| unsafe { w.bits(1) }); while r.events_lfclkstarted.read().bits() == 0 {} + #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] + { + // Setup DCDCs. + let pwr = unsafe { &*pac::POWER::ptr() }; + #[cfg(feature = "nrf52840")] + if config.dcdc.reg0 { + pwr.dcdcen0.write(|w| w.dcdcen().set_bit()); + } + if config.dcdc.reg1 { + pwr.dcdcen.write(|w| w.dcdcen().set_bit()); + } + } + #[cfg(feature = "_nrf9160")] + { + // Setup DCDC. + let reg = unsafe { &*pac::REGULATORS::ptr() }; + if config.dcdc.regmain { + reg.dcdcen.write(|w| w.dcdcen().set_bit()); + } + } + #[cfg(feature = "_nrf5340-app")] + { + // Setup DCDC. + let reg = unsafe { &*pac::REGULATORS::ptr() }; + if config.dcdc.regh { + reg.vregh.dcdcen.write(|w| w.dcdcen().set_bit()); + } + if config.dcdc.regmain { + reg.vregmain.dcdcen.write(|w| w.dcdcen().set_bit()); + } + if config.dcdc.regradio { + reg.vregradio.dcdcen.write(|w| w.dcdcen().set_bit()); + } + } + // Init GPIOTE #[cfg(feature = "gpiote")] gpiote::init(config.gpiote_interrupt_priority); From d91c37dae3225ac2776c96f3205aec940e10b668 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 26 Apr 2023 22:15:10 +0200 Subject: [PATCH 0955/1575] rp: remove pio Cargo feature. We shouldn't have Cargo features if their only purpose is reduce cold build time a bit. --- embassy-rp/Cargo.toml | 5 ++--- embassy-rp/src/lib.rs | 24 ++++++++++-------------- examples/rp/Cargo.toml | 2 +- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index f784ab33d..e7a1324c1 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -29,7 +29,6 @@ time-driver = [] rom-func-cache = [] intrinsics = [] rom-v2-intrinsics = [] -pio = ["dep:pio", "dep:pio-proc"] # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] @@ -70,5 +69,5 @@ embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} paste = "1.0" -pio-proc = {version= "0.2", optional = true} -pio = {version= "0.2.1", optional = true} +pio-proc = {version= "0.2" } +pio = {version= "0.2.1" } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aa8660320..697f4308b 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -11,20 +11,16 @@ mod critical_section_impl; mod intrinsics; pub mod adc; +pub mod clocks; pub mod dma; +pub mod flash; mod float; pub mod gpio; pub mod i2c; pub mod interrupt; - -#[cfg(feature = "pio")] -pub mod pio; -#[cfg(feature = "pio")] -pub mod pio_instr_util; +pub mod multicore; pub mod pwm; -#[cfg(feature = "pio")] -pub mod relocate; - +mod reset; pub mod rom_data; pub mod rtc; pub mod spi; @@ -33,15 +29,15 @@ pub mod timer; pub mod uart; #[cfg(feature = "nightly")] pub mod usb; - -pub mod clocks; -pub mod flash; -pub mod multicore; -mod reset; pub mod watchdog; -// Reexports +// PIO +// TODO: move `pio_instr_util` and `relocate` to inside `pio` +pub mod pio; +pub mod pio_instr_util; +pub mod relocate; +// Reexports pub use embassy_cortex_m::executor; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 45af8762e..7f65a00d7 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,7 +10,7 @@ embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } From 42a8f1671de994c702a20ecf44a1b4daa46e23e3 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 27 Apr 2023 09:22:32 +0200 Subject: [PATCH 0956/1575] Bump versions preparing for -macros and -executor release --- docs/modules/ROOT/examples/basic/Cargo.toml | 2 +- .../ROOT/examples/layer-by-layer/blinky-async/Cargo.toml | 2 +- embassy-cortex-m/Cargo.toml | 4 ++-- embassy-executor/Cargo.toml | 4 ++-- embassy-macros/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 45 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index e3e446e63..237ae0ac2 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.2.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index a11a7e0ba..523de0ddf 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" cortex-m = "0.7" cortex-m-rt = "0.7" embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } -embassy-executor = { version = "0.1.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.2.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml index c2c4759dc..2eb0cce2a 100644 --- a/embassy-cortex-m/Cargo.toml +++ b/embassy-cortex-m/Cargo.toml @@ -37,8 +37,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../embassy-executor"} -embassy-macros = { version = "0.1.0", path = "../embassy-macros"} +embassy-executor = { version = "0.2.0", path = "../embassy-executor"} +embassy-macros = { version = "0.2.0", path = "../embassy-macros"} embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common"} atomic-polyfill = "1.0.1" critical-section = "1.1" diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 29e1bd478..ce032479d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.1.1" +version = "0.2.0" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" @@ -61,7 +61,7 @@ log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.1.0", path = "../embassy-macros" } +embassy-macros = { version = "0.2.0", path = "../embassy-macros" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 5c612c997..781026b99 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-macros" -version = "0.1.0" +version = "0.2.0" edition = "2021" license = "MIT OR Apache-2.0" description = "macros for creating the entry point and tasks for embassy-executor" diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index e5e799d0a..15d0f6872 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -78,7 +78,7 @@ _dppi = [] _gpio-p1 = [] [dependencies] -embassy-executor = { version = "0.1.0", path = "../embassy-executor", optional = true } +embassy-executor = { version = "0.2.0", path = "../embassy-executor", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index e7a1324c1..59d0bf338 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -39,7 +39,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../embassy-executor" } +embassy-executor = { version = "0.2.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index f27cf6a51..9686b10ce 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -32,7 +32,7 @@ flavors = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../embassy-executor" } +embassy-executor = { version = "0.2.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 88528e763..5939a43b1 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 782ccef2b..64c2b8925 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } 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", features = ["nightly"] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 881191876..24abd90d4 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 21665553b..529a01aad 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index eef8b4138..d7539a53f 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 2bcf292bf..e90da259b 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index f452db72b..8ac0fac85 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 2c94ed3e9..ec79acdeb 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index f9dfb7c76..dfaece6cf 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 0a7141c4e..a3acc56b8 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -17,7 +17,7 @@ log = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 59f30a9be..0991387c2 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -12,7 +12,7 @@ nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/night [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index ebbc25bc6..40422e7df 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = [ "defmt", ] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "defmt", "integrated-timers", diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 7f65a00d7..0d9ce5e9e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 9938c693a..36770ca9c 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "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", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 5667200e3..ad11fbd1c 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index e969538d3..9c59c45c6 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = "0.3" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = "1.0" diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 706b5a722..345e948a6 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 62947bf49..e4f97a589 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 7ba9ff0c1..3d314e6c5 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 77985a017..511a1fa8c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index d9e9d668c..6ddb7186e 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 6bbd3a53f..4d7fc4548 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index b275eb190..00e2dae4c 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 67e6c76a7..b9204fba8 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 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", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index bd175a5b1..8b534ca05 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 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", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index ca022e254..cbba98301 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -11,7 +11,7 @@ nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstab [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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 = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index ff95571e6..8b6508c87 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32l151cb-a", "time-driver-any", "memory-x"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index e83f261e6..29d091f94 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 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"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index d3dee5250..acb48c765 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 86bc83dab..be205f880 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 18b27b28e..db1816da3 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wb55cc", "time-driver-any", "exti"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 0eb24bc44..990907914 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 5f36eb77f..437e443a7 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 22435e55b..ac38229a6 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 01b424de8..81bfdfab6 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 6778f53d7..43167166e 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 3047c34ce..84261f8ea 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -19,7 +19,7 @@ sdmmc = [] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } From 278818395eb064021dadab5f9cd08954ac06a15a Mon Sep 17 00:00:00 2001 From: kalkyl Date: Thu, 27 Apr 2023 16:48:25 +0200 Subject: [PATCH 0957/1575] rp: DMA behaviour during FLASH operations --- embassy-rp/src/flash.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index f2137ebe1..a3f7d5183 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -174,25 +174,17 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { crate::multicore::pause_core1(); critical_section::with(|_| { - // Pause all DMA channels for the duration of the ram operation - for (number, status) in dma_status.iter_mut().enumerate() { - let ch = crate::pac::DMA.ch(number as _); - *status = ch.ctrl_trig().read().en(); - if *status { - ch.ctrl_trig().modify(|w| w.set_en(false)); + // Wait for all DMA channels in flash to finish before ram operation + const SRAM_LOWER: u32 = 0x2000_0000; + for n in 0..crate::dma::CHANNEL_COUNT { + let ch = crate::pac::DMA.ch(n); + if ch.read_addr().read() < SRAM_LOWER { + while ch.ctrl_trig().read().busy() {} } } // Run our flash operation in RAM operation(); - - // Re-enable previously enabled DMA channels - for (number, status) in dma_status.iter().enumerate() { - let ch = crate::pac::DMA.ch(number as _); - if *status { - ch.ctrl_trig().modify(|w| w.set_en(true)); - } - } }); // Resume CORE1 execution From 8c733c29cc2f48385acc537e7057af7d29174f8c Mon Sep 17 00:00:00 2001 From: OueslatiGhaith Date: Thu, 27 Apr 2023 16:03:22 +0100 Subject: [PATCH 0958/1575] add IPCC peripheral for stm32wb --- embassy-stm32/src/ipcc.rs | 186 ++++++++++++++++++++++++++++++++++++++ embassy-stm32/src/lib.rs | 2 + 2 files changed, 188 insertions(+) create mode 100644 embassy-stm32/src/ipcc.rs diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs new file mode 100644 index 000000000..e5ec58ada --- /dev/null +++ b/embassy-stm32/src/ipcc.rs @@ -0,0 +1,186 @@ +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; + +use crate::peripherals::IPCC; + +#[non_exhaustive] +#[derive(Clone, Copy, Default)] +pub struct Config { + // TODO: add IPCC peripheral configuration, if any, here + // reserved for future use +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub enum IpccChannel { + Channel1 = 0, + Channel2 = 1, + Channel3 = 2, + Channel4 = 3, + Channel5 = 4, + Channel6 = 5, +} + +pub(crate) mod sealed { + pub trait Instance: crate::rcc::RccPeripheral { + fn regs() -> crate::pac::ipcc::Ipcc; + fn set_cpu2(enabled: bool); + } +} + +pub struct Ipcc<'d> { + _peri: PeripheralRef<'d, IPCC>, +} + +impl<'d> Ipcc<'d> { + pub fn new(peri: impl Peripheral

+ 'd, _config: Config) -> Self { + into_ref!(peri); + + Self { _peri: peri } + } + + pub fn init(&mut self) { + IPCC::enable(); + IPCC::reset(); + IPCC::set_cpu2(true); + + unsafe { _configure_pwr() }; + + let regs = IPCC::regs(); + + unsafe { + regs.cpu(0).cr().modify(|w| { + w.set_rxoie(true); + w.set_txfie(true); + }) + } + } + + pub fn c1_set_rx_channel(&mut self, channel: IpccChannel, enabled: bool) { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel.into(), !enabled)) } + } + + pub fn c1_get_rx_channel(&self, channel: IpccChannel) -> bool { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { !regs.cpu(0).mr().read().chom(channel.into()) } + } + + pub fn c2_set_rx_channel(&mut self, channel: IpccChannel, enabled: bool) { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel.into(), !enabled)) } + } + + pub fn c2_get_rx_channel(&self, channel: IpccChannel) -> bool { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { !regs.cpu(1).mr().read().chom(channel.into()) } + } + + pub fn c1_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel.into(), !enabled)) } + } + + pub fn c1_get_tx_channel(&self, channel: IpccChannel) -> bool { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { !regs.cpu(0).mr().read().chfm(channel.into()) } + } + + pub fn c2_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel.into(), !enabled)) } + } + + pub fn c2_get_tx_channel(&self, channel: IpccChannel) -> bool { + let regs = IPCC::regs(); + + // If bit is set to 1 then interrupt is disabled + unsafe { !regs.cpu(1).mr().read().chfm(channel.into()) } + } + + /// clears IPCC receive channel status for CPU1 + pub fn c1_clear_flag_channel(&mut self, channel: IpccChannel) { + let regs = IPCC::regs(); + + unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel.into(), true)) } + } + + /// clears IPCC receive channel status for CPU2 + pub fn c2_clear_flag_channel(&mut self, channel: IpccChannel) { + let regs = IPCC::regs(); + + unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel.into(), true)) } + } + + pub fn c1_set_flag_channel(&mut self, channel: IpccChannel) { + let regs = IPCC::regs(); + + unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel.into(), true)) } + } + + pub fn c2_set_flag_channel(&mut self, channel: IpccChannel) { + let regs = IPCC::regs(); + + unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel.into(), true)) } + } + + pub fn c1_is_active_flag(&self, channel: IpccChannel) -> bool { + let regs = IPCC::regs(); + + unsafe { regs.cpu(0).sr().read().chf(channel.into()) } + } + + pub fn c2_is_active_flag(&self, channel: IpccChannel) -> bool { + let regs = IPCC::regs(); + + unsafe { regs.cpu(1).sr().read().chf(channel.into()) } + } + + pub fn is_tx_pending(&self, channel: IpccChannel) -> bool { + !self.c1_is_active_flag(channel) && self.c1_get_tx_channel(channel) + } + + pub fn is_rx_pending(&self, channel: IpccChannel) -> bool { + self.c2_is_active_flag(channel) && self.c1_get_rx_channel(channel) + } +} + +impl sealed::Instance for crate::peripherals::IPCC { + fn regs() -> crate::pac::ipcc::Ipcc { + crate::pac::IPCC + } + + fn set_cpu2(enabled: bool) { + unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) } + } +} + +/// extension trait that constrains the [`Ipcc`] peripheral +pub trait IpccExt<'d> { + fn constrain(self) -> Ipcc<'d>; +} +impl<'d> IpccExt<'d> for IPCC { + fn constrain(self) -> Ipcc<'d> { + Ipcc { _peri: self.into_ref() } + } +} + +unsafe fn _configure_pwr() { + let rcc = crate::pac::RCC; + + // set RF wake-up clock = LSE + rcc.csr().modify(|w| w.set_rfwkpsel(0b01)); +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index f0fc152ce..2f7892db2 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -44,6 +44,8 @@ pub mod i2c; #[cfg(crc)] pub mod crc; pub mod flash; +#[cfg(stm32wb)] +pub mod ipcc; pub mod pwm; #[cfg(quadspi)] pub mod qspi; From 3ba73b5ff45084e8d9a662220981f7d503aba251 Mon Sep 17 00:00:00 2001 From: OueslatiGhaith Date: Thu, 27 Apr 2023 16:08:57 +0100 Subject: [PATCH 0959/1575] fixed mistake with casting channel to a usize --- embassy-stm32/src/ipcc.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index e5ec58ada..43105c1d1 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -59,94 +59,94 @@ impl<'d> Ipcc<'d> { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel.into(), !enabled)) } + unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } } pub fn c1_get_rx_channel(&self, channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(0).mr().read().chom(channel.into()) } + unsafe { !regs.cpu(0).mr().read().chom(channel as usize) } } pub fn c2_set_rx_channel(&mut self, channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel.into(), !enabled)) } + unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } } pub fn c2_get_rx_channel(&self, channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(1).mr().read().chom(channel.into()) } + unsafe { !regs.cpu(1).mr().read().chom(channel as usize) } } pub fn c1_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel.into(), !enabled)) } + unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } } pub fn c1_get_tx_channel(&self, channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(0).mr().read().chfm(channel.into()) } + unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) } } pub fn c2_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel.into(), !enabled)) } + unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } } pub fn c2_get_tx_channel(&self, channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(1).mr().read().chfm(channel.into()) } + unsafe { !regs.cpu(1).mr().read().chfm(channel as usize) } } /// clears IPCC receive channel status for CPU1 pub fn c1_clear_flag_channel(&mut self, channel: IpccChannel) { let regs = IPCC::regs(); - unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel.into(), true)) } + unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } } /// clears IPCC receive channel status for CPU2 pub fn c2_clear_flag_channel(&mut self, channel: IpccChannel) { let regs = IPCC::regs(); - unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel.into(), true)) } + unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) } } pub fn c1_set_flag_channel(&mut self, channel: IpccChannel) { let regs = IPCC::regs(); - unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel.into(), true)) } + unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } } pub fn c2_set_flag_channel(&mut self, channel: IpccChannel) { let regs = IPCC::regs(); - unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel.into(), true)) } + unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) } } pub fn c1_is_active_flag(&self, channel: IpccChannel) -> bool { let regs = IPCC::regs(); - unsafe { regs.cpu(0).sr().read().chf(channel.into()) } + unsafe { regs.cpu(0).sr().read().chf(channel as usize) } } pub fn c2_is_active_flag(&self, channel: IpccChannel) -> bool { let regs = IPCC::regs(); - unsafe { regs.cpu(1).sr().read().chf(channel.into()) } + unsafe { regs.cpu(1).sr().read().chf(channel as usize) } } pub fn is_tx_pending(&self, channel: IpccChannel) -> bool { From 31b54e0fbd086178090b4899c2f788277bf5daac Mon Sep 17 00:00:00 2001 From: kalkyl Date: Thu, 27 Apr 2023 17:09:16 +0200 Subject: [PATCH 0960/1575] rustfmt --- embassy-rp/src/flash.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index a3f7d5183..6ab05ff0b 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -162,8 +162,6 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { /// - interrupts must be disabled /// - DMA must not access flash memory unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> { - let dma_status = &mut [false; crate::dma::CHANNEL_COUNT]; - // Make sure we're running on CORE0 let core_id: u32 = unsafe { pac::SIO.cpuid().read() }; if core_id != 0 { @@ -178,9 +176,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { const SRAM_LOWER: u32 = 0x2000_0000; for n in 0..crate::dma::CHANNEL_COUNT { let ch = crate::pac::DMA.ch(n); - if ch.read_addr().read() < SRAM_LOWER { - while ch.ctrl_trig().read().busy() {} - } + while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {} } // Run our flash operation in RAM From d960bf344ad58b2ce8132d27c1130a69bfd725b1 Mon Sep 17 00:00:00 2001 From: OueslatiGhaith Date: Thu, 27 Apr 2023 16:22:41 +0100 Subject: [PATCH 0961/1575] fixed missing imports --- embassy-stm32/Cargo.toml | 2729 +++++++++++++++++++------------------ embassy-stm32/src/ipcc.rs | 2 + 2 files changed, 1383 insertions(+), 1348 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index f27cf6a51..8015a9354 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -8,7 +8,15 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] +features = [ + "nightly", + "defmt", + "unstable-pac", + "unstable-traits", + "exti", + "time-driver-any", + "time", +] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, @@ -35,16 +43,20 @@ embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } -embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = [ + "prio-bits-4", +] } +embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } -embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } +embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver", optional = true } -embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ + "unproven", +] } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true } +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } +embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true } embedded-storage = "0.3.0" @@ -52,7 +64,9 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", +] } rand_core = "0.6.3" sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } @@ -66,7 +80,7 @@ stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } -chrono = { version = "^0.4", default-features = false, optional = true} +chrono = { version = "^0.4", default-features = false, optional = true } [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -74,11 +88,23 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "6", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "6", default-features = false, features = [ + "metadata", +] } [features] default = ["stm32-metapac/rt"] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] +defmt = [ + "dep:defmt", + "bxcan/unstable-defmt", + "embassy-sync/defmt", + "embassy-executor/defmt", + "embassy-embedded-hal/defmt", + "embassy-hal-common/defmt", + "embedded-io?/defmt", + "embassy-usb-driver?/defmt", + "embassy-net-driver/defmt", +] memory-x = ["stm32-metapac/memory-x"] exti = [] @@ -97,7 +123,14 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] +nightly = [ + "embassy-executor/nightly", + "embedded-hal-1", + "embedded-hal-async", + "dep:embedded-io", + "dep:embassy-usb-driver", + "embassy-embedded-hal/nightly", +] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. @@ -110,1337 +143,1337 @@ unstable-pac = [] unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # Chip-selection features -stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] -stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] -stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] -stm32c011j4 = [ "stm32-metapac/stm32c011j4" ] -stm32c011j6 = [ "stm32-metapac/stm32c011j6" ] -stm32c031c4 = [ "stm32-metapac/stm32c031c4" ] -stm32c031c6 = [ "stm32-metapac/stm32c031c6" ] -stm32c031f4 = [ "stm32-metapac/stm32c031f4" ] -stm32c031f6 = [ "stm32-metapac/stm32c031f6" ] -stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] -stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] -stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] -stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] -stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] -stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] -stm32f030cc = [ "stm32-metapac/stm32f030cc" ] -stm32f030f4 = [ "stm32-metapac/stm32f030f4" ] -stm32f030k6 = [ "stm32-metapac/stm32f030k6" ] -stm32f030r8 = [ "stm32-metapac/stm32f030r8" ] -stm32f030rc = [ "stm32-metapac/stm32f030rc" ] -stm32f031c4 = [ "stm32-metapac/stm32f031c4" ] -stm32f031c6 = [ "stm32-metapac/stm32f031c6" ] -stm32f031e6 = [ "stm32-metapac/stm32f031e6" ] -stm32f031f4 = [ "stm32-metapac/stm32f031f4" ] -stm32f031f6 = [ "stm32-metapac/stm32f031f6" ] -stm32f031g4 = [ "stm32-metapac/stm32f031g4" ] -stm32f031g6 = [ "stm32-metapac/stm32f031g6" ] -stm32f031k4 = [ "stm32-metapac/stm32f031k4" ] -stm32f031k6 = [ "stm32-metapac/stm32f031k6" ] -stm32f038c6 = [ "stm32-metapac/stm32f038c6" ] -stm32f038e6 = [ "stm32-metapac/stm32f038e6" ] -stm32f038f6 = [ "stm32-metapac/stm32f038f6" ] -stm32f038g6 = [ "stm32-metapac/stm32f038g6" ] -stm32f038k6 = [ "stm32-metapac/stm32f038k6" ] -stm32f042c4 = [ "stm32-metapac/stm32f042c4" ] -stm32f042c6 = [ "stm32-metapac/stm32f042c6" ] -stm32f042f4 = [ "stm32-metapac/stm32f042f4" ] -stm32f042f6 = [ "stm32-metapac/stm32f042f6" ] -stm32f042g4 = [ "stm32-metapac/stm32f042g4" ] -stm32f042g6 = [ "stm32-metapac/stm32f042g6" ] -stm32f042k4 = [ "stm32-metapac/stm32f042k4" ] -stm32f042k6 = [ "stm32-metapac/stm32f042k6" ] -stm32f042t6 = [ "stm32-metapac/stm32f042t6" ] -stm32f048c6 = [ "stm32-metapac/stm32f048c6" ] -stm32f048g6 = [ "stm32-metapac/stm32f048g6" ] -stm32f048t6 = [ "stm32-metapac/stm32f048t6" ] -stm32f051c4 = [ "stm32-metapac/stm32f051c4" ] -stm32f051c6 = [ "stm32-metapac/stm32f051c6" ] -stm32f051c8 = [ "stm32-metapac/stm32f051c8" ] -stm32f051k4 = [ "stm32-metapac/stm32f051k4" ] -stm32f051k6 = [ "stm32-metapac/stm32f051k6" ] -stm32f051k8 = [ "stm32-metapac/stm32f051k8" ] -stm32f051r4 = [ "stm32-metapac/stm32f051r4" ] -stm32f051r6 = [ "stm32-metapac/stm32f051r6" ] -stm32f051r8 = [ "stm32-metapac/stm32f051r8" ] -stm32f051t8 = [ "stm32-metapac/stm32f051t8" ] -stm32f058c8 = [ "stm32-metapac/stm32f058c8" ] -stm32f058r8 = [ "stm32-metapac/stm32f058r8" ] -stm32f058t8 = [ "stm32-metapac/stm32f058t8" ] -stm32f070c6 = [ "stm32-metapac/stm32f070c6" ] -stm32f070cb = [ "stm32-metapac/stm32f070cb" ] -stm32f070f6 = [ "stm32-metapac/stm32f070f6" ] -stm32f070rb = [ "stm32-metapac/stm32f070rb" ] -stm32f071c8 = [ "stm32-metapac/stm32f071c8" ] -stm32f071cb = [ "stm32-metapac/stm32f071cb" ] -stm32f071rb = [ "stm32-metapac/stm32f071rb" ] -stm32f071v8 = [ "stm32-metapac/stm32f071v8" ] -stm32f071vb = [ "stm32-metapac/stm32f071vb" ] -stm32f072c8 = [ "stm32-metapac/stm32f072c8" ] -stm32f072cb = [ "stm32-metapac/stm32f072cb" ] -stm32f072r8 = [ "stm32-metapac/stm32f072r8" ] -stm32f072rb = [ "stm32-metapac/stm32f072rb" ] -stm32f072v8 = [ "stm32-metapac/stm32f072v8" ] -stm32f072vb = [ "stm32-metapac/stm32f072vb" ] -stm32f078cb = [ "stm32-metapac/stm32f078cb" ] -stm32f078rb = [ "stm32-metapac/stm32f078rb" ] -stm32f078vb = [ "stm32-metapac/stm32f078vb" ] -stm32f091cb = [ "stm32-metapac/stm32f091cb" ] -stm32f091cc = [ "stm32-metapac/stm32f091cc" ] -stm32f091rb = [ "stm32-metapac/stm32f091rb" ] -stm32f091rc = [ "stm32-metapac/stm32f091rc" ] -stm32f091vb = [ "stm32-metapac/stm32f091vb" ] -stm32f091vc = [ "stm32-metapac/stm32f091vc" ] -stm32f098cc = [ "stm32-metapac/stm32f098cc" ] -stm32f098rc = [ "stm32-metapac/stm32f098rc" ] -stm32f098vc = [ "stm32-metapac/stm32f098vc" ] -stm32f100c4 = [ "stm32-metapac/stm32f100c4" ] -stm32f100c6 = [ "stm32-metapac/stm32f100c6" ] -stm32f100c8 = [ "stm32-metapac/stm32f100c8" ] -stm32f100cb = [ "stm32-metapac/stm32f100cb" ] -stm32f100r4 = [ "stm32-metapac/stm32f100r4" ] -stm32f100r6 = [ "stm32-metapac/stm32f100r6" ] -stm32f100r8 = [ "stm32-metapac/stm32f100r8" ] -stm32f100rb = [ "stm32-metapac/stm32f100rb" ] -stm32f100rc = [ "stm32-metapac/stm32f100rc" ] -stm32f100rd = [ "stm32-metapac/stm32f100rd" ] -stm32f100re = [ "stm32-metapac/stm32f100re" ] -stm32f100v8 = [ "stm32-metapac/stm32f100v8" ] -stm32f100vb = [ "stm32-metapac/stm32f100vb" ] -stm32f100vc = [ "stm32-metapac/stm32f100vc" ] -stm32f100vd = [ "stm32-metapac/stm32f100vd" ] -stm32f100ve = [ "stm32-metapac/stm32f100ve" ] -stm32f100zc = [ "stm32-metapac/stm32f100zc" ] -stm32f100zd = [ "stm32-metapac/stm32f100zd" ] -stm32f100ze = [ "stm32-metapac/stm32f100ze" ] -stm32f101c4 = [ "stm32-metapac/stm32f101c4" ] -stm32f101c6 = [ "stm32-metapac/stm32f101c6" ] -stm32f101c8 = [ "stm32-metapac/stm32f101c8" ] -stm32f101cb = [ "stm32-metapac/stm32f101cb" ] -stm32f101r4 = [ "stm32-metapac/stm32f101r4" ] -stm32f101r6 = [ "stm32-metapac/stm32f101r6" ] -stm32f101r8 = [ "stm32-metapac/stm32f101r8" ] -stm32f101rb = [ "stm32-metapac/stm32f101rb" ] -stm32f101rc = [ "stm32-metapac/stm32f101rc" ] -stm32f101rd = [ "stm32-metapac/stm32f101rd" ] -stm32f101re = [ "stm32-metapac/stm32f101re" ] -stm32f101rf = [ "stm32-metapac/stm32f101rf" ] -stm32f101rg = [ "stm32-metapac/stm32f101rg" ] -stm32f101t4 = [ "stm32-metapac/stm32f101t4" ] -stm32f101t6 = [ "stm32-metapac/stm32f101t6" ] -stm32f101t8 = [ "stm32-metapac/stm32f101t8" ] -stm32f101tb = [ "stm32-metapac/stm32f101tb" ] -stm32f101v8 = [ "stm32-metapac/stm32f101v8" ] -stm32f101vb = [ "stm32-metapac/stm32f101vb" ] -stm32f101vc = [ "stm32-metapac/stm32f101vc" ] -stm32f101vd = [ "stm32-metapac/stm32f101vd" ] -stm32f101ve = [ "stm32-metapac/stm32f101ve" ] -stm32f101vf = [ "stm32-metapac/stm32f101vf" ] -stm32f101vg = [ "stm32-metapac/stm32f101vg" ] -stm32f101zc = [ "stm32-metapac/stm32f101zc" ] -stm32f101zd = [ "stm32-metapac/stm32f101zd" ] -stm32f101ze = [ "stm32-metapac/stm32f101ze" ] -stm32f101zf = [ "stm32-metapac/stm32f101zf" ] -stm32f101zg = [ "stm32-metapac/stm32f101zg" ] -stm32f102c4 = [ "stm32-metapac/stm32f102c4" ] -stm32f102c6 = [ "stm32-metapac/stm32f102c6" ] -stm32f102c8 = [ "stm32-metapac/stm32f102c8" ] -stm32f102cb = [ "stm32-metapac/stm32f102cb" ] -stm32f102r4 = [ "stm32-metapac/stm32f102r4" ] -stm32f102r6 = [ "stm32-metapac/stm32f102r6" ] -stm32f102r8 = [ "stm32-metapac/stm32f102r8" ] -stm32f102rb = [ "stm32-metapac/stm32f102rb" ] -stm32f103c4 = [ "stm32-metapac/stm32f103c4" ] -stm32f103c6 = [ "stm32-metapac/stm32f103c6" ] -stm32f103c8 = [ "stm32-metapac/stm32f103c8" ] -stm32f103cb = [ "stm32-metapac/stm32f103cb" ] -stm32f103r4 = [ "stm32-metapac/stm32f103r4" ] -stm32f103r6 = [ "stm32-metapac/stm32f103r6" ] -stm32f103r8 = [ "stm32-metapac/stm32f103r8" ] -stm32f103rb = [ "stm32-metapac/stm32f103rb" ] -stm32f103rc = [ "stm32-metapac/stm32f103rc" ] -stm32f103rd = [ "stm32-metapac/stm32f103rd" ] -stm32f103re = [ "stm32-metapac/stm32f103re" ] -stm32f103rf = [ "stm32-metapac/stm32f103rf" ] -stm32f103rg = [ "stm32-metapac/stm32f103rg" ] -stm32f103t4 = [ "stm32-metapac/stm32f103t4" ] -stm32f103t6 = [ "stm32-metapac/stm32f103t6" ] -stm32f103t8 = [ "stm32-metapac/stm32f103t8" ] -stm32f103tb = [ "stm32-metapac/stm32f103tb" ] -stm32f103v8 = [ "stm32-metapac/stm32f103v8" ] -stm32f103vb = [ "stm32-metapac/stm32f103vb" ] -stm32f103vc = [ "stm32-metapac/stm32f103vc" ] -stm32f103vd = [ "stm32-metapac/stm32f103vd" ] -stm32f103ve = [ "stm32-metapac/stm32f103ve" ] -stm32f103vf = [ "stm32-metapac/stm32f103vf" ] -stm32f103vg = [ "stm32-metapac/stm32f103vg" ] -stm32f103zc = [ "stm32-metapac/stm32f103zc" ] -stm32f103zd = [ "stm32-metapac/stm32f103zd" ] -stm32f103ze = [ "stm32-metapac/stm32f103ze" ] -stm32f103zf = [ "stm32-metapac/stm32f103zf" ] -stm32f103zg = [ "stm32-metapac/stm32f103zg" ] -stm32f105r8 = [ "stm32-metapac/stm32f105r8" ] -stm32f105rb = [ "stm32-metapac/stm32f105rb" ] -stm32f105rc = [ "stm32-metapac/stm32f105rc" ] -stm32f105v8 = [ "stm32-metapac/stm32f105v8" ] -stm32f105vb = [ "stm32-metapac/stm32f105vb" ] -stm32f105vc = [ "stm32-metapac/stm32f105vc" ] -stm32f107rb = [ "stm32-metapac/stm32f107rb" ] -stm32f107rc = [ "stm32-metapac/stm32f107rc" ] -stm32f107vb = [ "stm32-metapac/stm32f107vb" ] -stm32f107vc = [ "stm32-metapac/stm32f107vc" ] -stm32f205rb = [ "stm32-metapac/stm32f205rb" ] -stm32f205rc = [ "stm32-metapac/stm32f205rc" ] -stm32f205re = [ "stm32-metapac/stm32f205re" ] -stm32f205rf = [ "stm32-metapac/stm32f205rf" ] -stm32f205rg = [ "stm32-metapac/stm32f205rg" ] -stm32f205vb = [ "stm32-metapac/stm32f205vb" ] -stm32f205vc = [ "stm32-metapac/stm32f205vc" ] -stm32f205ve = [ "stm32-metapac/stm32f205ve" ] -stm32f205vf = [ "stm32-metapac/stm32f205vf" ] -stm32f205vg = [ "stm32-metapac/stm32f205vg" ] -stm32f205zc = [ "stm32-metapac/stm32f205zc" ] -stm32f205ze = [ "stm32-metapac/stm32f205ze" ] -stm32f205zf = [ "stm32-metapac/stm32f205zf" ] -stm32f205zg = [ "stm32-metapac/stm32f205zg" ] -stm32f207ic = [ "stm32-metapac/stm32f207ic" ] -stm32f207ie = [ "stm32-metapac/stm32f207ie" ] -stm32f207if = [ "stm32-metapac/stm32f207if" ] -stm32f207ig = [ "stm32-metapac/stm32f207ig" ] -stm32f207vc = [ "stm32-metapac/stm32f207vc" ] -stm32f207ve = [ "stm32-metapac/stm32f207ve" ] -stm32f207vf = [ "stm32-metapac/stm32f207vf" ] -stm32f207vg = [ "stm32-metapac/stm32f207vg" ] -stm32f207zc = [ "stm32-metapac/stm32f207zc" ] -stm32f207ze = [ "stm32-metapac/stm32f207ze" ] -stm32f207zf = [ "stm32-metapac/stm32f207zf" ] -stm32f207zg = [ "stm32-metapac/stm32f207zg" ] -stm32f215re = [ "stm32-metapac/stm32f215re" ] -stm32f215rg = [ "stm32-metapac/stm32f215rg" ] -stm32f215ve = [ "stm32-metapac/stm32f215ve" ] -stm32f215vg = [ "stm32-metapac/stm32f215vg" ] -stm32f215ze = [ "stm32-metapac/stm32f215ze" ] -stm32f215zg = [ "stm32-metapac/stm32f215zg" ] -stm32f217ie = [ "stm32-metapac/stm32f217ie" ] -stm32f217ig = [ "stm32-metapac/stm32f217ig" ] -stm32f217ve = [ "stm32-metapac/stm32f217ve" ] -stm32f217vg = [ "stm32-metapac/stm32f217vg" ] -stm32f217ze = [ "stm32-metapac/stm32f217ze" ] -stm32f217zg = [ "stm32-metapac/stm32f217zg" ] -stm32f301c6 = [ "stm32-metapac/stm32f301c6" ] -stm32f301c8 = [ "stm32-metapac/stm32f301c8" ] -stm32f301k6 = [ "stm32-metapac/stm32f301k6" ] -stm32f301k8 = [ "stm32-metapac/stm32f301k8" ] -stm32f301r6 = [ "stm32-metapac/stm32f301r6" ] -stm32f301r8 = [ "stm32-metapac/stm32f301r8" ] -stm32f302c6 = [ "stm32-metapac/stm32f302c6" ] -stm32f302c8 = [ "stm32-metapac/stm32f302c8" ] -stm32f302cb = [ "stm32-metapac/stm32f302cb" ] -stm32f302cc = [ "stm32-metapac/stm32f302cc" ] -stm32f302k6 = [ "stm32-metapac/stm32f302k6" ] -stm32f302k8 = [ "stm32-metapac/stm32f302k8" ] -stm32f302r6 = [ "stm32-metapac/stm32f302r6" ] -stm32f302r8 = [ "stm32-metapac/stm32f302r8" ] -stm32f302rb = [ "stm32-metapac/stm32f302rb" ] -stm32f302rc = [ "stm32-metapac/stm32f302rc" ] -stm32f302rd = [ "stm32-metapac/stm32f302rd" ] -stm32f302re = [ "stm32-metapac/stm32f302re" ] -stm32f302vb = [ "stm32-metapac/stm32f302vb" ] -stm32f302vc = [ "stm32-metapac/stm32f302vc" ] -stm32f302vd = [ "stm32-metapac/stm32f302vd" ] -stm32f302ve = [ "stm32-metapac/stm32f302ve" ] -stm32f302zd = [ "stm32-metapac/stm32f302zd" ] -stm32f302ze = [ "stm32-metapac/stm32f302ze" ] -stm32f303c6 = [ "stm32-metapac/stm32f303c6" ] -stm32f303c8 = [ "stm32-metapac/stm32f303c8" ] -stm32f303cb = [ "stm32-metapac/stm32f303cb" ] -stm32f303cc = [ "stm32-metapac/stm32f303cc" ] -stm32f303k6 = [ "stm32-metapac/stm32f303k6" ] -stm32f303k8 = [ "stm32-metapac/stm32f303k8" ] -stm32f303r6 = [ "stm32-metapac/stm32f303r6" ] -stm32f303r8 = [ "stm32-metapac/stm32f303r8" ] -stm32f303rb = [ "stm32-metapac/stm32f303rb" ] -stm32f303rc = [ "stm32-metapac/stm32f303rc" ] -stm32f303rd = [ "stm32-metapac/stm32f303rd" ] -stm32f303re = [ "stm32-metapac/stm32f303re" ] -stm32f303vb = [ "stm32-metapac/stm32f303vb" ] -stm32f303vc = [ "stm32-metapac/stm32f303vc" ] -stm32f303vd = [ "stm32-metapac/stm32f303vd" ] -stm32f303ve = [ "stm32-metapac/stm32f303ve" ] -stm32f303zd = [ "stm32-metapac/stm32f303zd" ] -stm32f303ze = [ "stm32-metapac/stm32f303ze" ] -stm32f318c8 = [ "stm32-metapac/stm32f318c8" ] -stm32f318k8 = [ "stm32-metapac/stm32f318k8" ] -stm32f328c8 = [ "stm32-metapac/stm32f328c8" ] -stm32f334c4 = [ "stm32-metapac/stm32f334c4" ] -stm32f334c6 = [ "stm32-metapac/stm32f334c6" ] -stm32f334c8 = [ "stm32-metapac/stm32f334c8" ] -stm32f334k4 = [ "stm32-metapac/stm32f334k4" ] -stm32f334k6 = [ "stm32-metapac/stm32f334k6" ] -stm32f334k8 = [ "stm32-metapac/stm32f334k8" ] -stm32f334r6 = [ "stm32-metapac/stm32f334r6" ] -stm32f334r8 = [ "stm32-metapac/stm32f334r8" ] -stm32f358cc = [ "stm32-metapac/stm32f358cc" ] -stm32f358rc = [ "stm32-metapac/stm32f358rc" ] -stm32f358vc = [ "stm32-metapac/stm32f358vc" ] -stm32f373c8 = [ "stm32-metapac/stm32f373c8" ] -stm32f373cb = [ "stm32-metapac/stm32f373cb" ] -stm32f373cc = [ "stm32-metapac/stm32f373cc" ] -stm32f373r8 = [ "stm32-metapac/stm32f373r8" ] -stm32f373rb = [ "stm32-metapac/stm32f373rb" ] -stm32f373rc = [ "stm32-metapac/stm32f373rc" ] -stm32f373v8 = [ "stm32-metapac/stm32f373v8" ] -stm32f373vb = [ "stm32-metapac/stm32f373vb" ] -stm32f373vc = [ "stm32-metapac/stm32f373vc" ] -stm32f378cc = [ "stm32-metapac/stm32f378cc" ] -stm32f378rc = [ "stm32-metapac/stm32f378rc" ] -stm32f378vc = [ "stm32-metapac/stm32f378vc" ] -stm32f398ve = [ "stm32-metapac/stm32f398ve" ] -stm32f401cb = [ "stm32-metapac/stm32f401cb" ] -stm32f401cc = [ "stm32-metapac/stm32f401cc" ] -stm32f401cd = [ "stm32-metapac/stm32f401cd" ] -stm32f401ce = [ "stm32-metapac/stm32f401ce" ] -stm32f401rb = [ "stm32-metapac/stm32f401rb" ] -stm32f401rc = [ "stm32-metapac/stm32f401rc" ] -stm32f401rd = [ "stm32-metapac/stm32f401rd" ] -stm32f401re = [ "stm32-metapac/stm32f401re" ] -stm32f401vb = [ "stm32-metapac/stm32f401vb" ] -stm32f401vc = [ "stm32-metapac/stm32f401vc" ] -stm32f401vd = [ "stm32-metapac/stm32f401vd" ] -stm32f401ve = [ "stm32-metapac/stm32f401ve" ] -stm32f405oe = [ "stm32-metapac/stm32f405oe" ] -stm32f405og = [ "stm32-metapac/stm32f405og" ] -stm32f405rg = [ "stm32-metapac/stm32f405rg" ] -stm32f405vg = [ "stm32-metapac/stm32f405vg" ] -stm32f405zg = [ "stm32-metapac/stm32f405zg" ] -stm32f407ie = [ "stm32-metapac/stm32f407ie" ] -stm32f407ig = [ "stm32-metapac/stm32f407ig" ] -stm32f407ve = [ "stm32-metapac/stm32f407ve" ] -stm32f407vg = [ "stm32-metapac/stm32f407vg" ] -stm32f407ze = [ "stm32-metapac/stm32f407ze" ] -stm32f407zg = [ "stm32-metapac/stm32f407zg" ] -stm32f410c8 = [ "stm32-metapac/stm32f410c8" ] -stm32f410cb = [ "stm32-metapac/stm32f410cb" ] -stm32f410r8 = [ "stm32-metapac/stm32f410r8" ] -stm32f410rb = [ "stm32-metapac/stm32f410rb" ] -stm32f410t8 = [ "stm32-metapac/stm32f410t8" ] -stm32f410tb = [ "stm32-metapac/stm32f410tb" ] -stm32f411cc = [ "stm32-metapac/stm32f411cc" ] -stm32f411ce = [ "stm32-metapac/stm32f411ce" ] -stm32f411rc = [ "stm32-metapac/stm32f411rc" ] -stm32f411re = [ "stm32-metapac/stm32f411re" ] -stm32f411vc = [ "stm32-metapac/stm32f411vc" ] -stm32f411ve = [ "stm32-metapac/stm32f411ve" ] -stm32f412ce = [ "stm32-metapac/stm32f412ce" ] -stm32f412cg = [ "stm32-metapac/stm32f412cg" ] -stm32f412re = [ "stm32-metapac/stm32f412re" ] -stm32f412rg = [ "stm32-metapac/stm32f412rg" ] -stm32f412ve = [ "stm32-metapac/stm32f412ve" ] -stm32f412vg = [ "stm32-metapac/stm32f412vg" ] -stm32f412ze = [ "stm32-metapac/stm32f412ze" ] -stm32f412zg = [ "stm32-metapac/stm32f412zg" ] -stm32f413cg = [ "stm32-metapac/stm32f413cg" ] -stm32f413ch = [ "stm32-metapac/stm32f413ch" ] -stm32f413mg = [ "stm32-metapac/stm32f413mg" ] -stm32f413mh = [ "stm32-metapac/stm32f413mh" ] -stm32f413rg = [ "stm32-metapac/stm32f413rg" ] -stm32f413rh = [ "stm32-metapac/stm32f413rh" ] -stm32f413vg = [ "stm32-metapac/stm32f413vg" ] -stm32f413vh = [ "stm32-metapac/stm32f413vh" ] -stm32f413zg = [ "stm32-metapac/stm32f413zg" ] -stm32f413zh = [ "stm32-metapac/stm32f413zh" ] -stm32f415og = [ "stm32-metapac/stm32f415og" ] -stm32f415rg = [ "stm32-metapac/stm32f415rg" ] -stm32f415vg = [ "stm32-metapac/stm32f415vg" ] -stm32f415zg = [ "stm32-metapac/stm32f415zg" ] -stm32f417ie = [ "stm32-metapac/stm32f417ie" ] -stm32f417ig = [ "stm32-metapac/stm32f417ig" ] -stm32f417ve = [ "stm32-metapac/stm32f417ve" ] -stm32f417vg = [ "stm32-metapac/stm32f417vg" ] -stm32f417ze = [ "stm32-metapac/stm32f417ze" ] -stm32f417zg = [ "stm32-metapac/stm32f417zg" ] -stm32f423ch = [ "stm32-metapac/stm32f423ch" ] -stm32f423mh = [ "stm32-metapac/stm32f423mh" ] -stm32f423rh = [ "stm32-metapac/stm32f423rh" ] -stm32f423vh = [ "stm32-metapac/stm32f423vh" ] -stm32f423zh = [ "stm32-metapac/stm32f423zh" ] -stm32f427ag = [ "stm32-metapac/stm32f427ag" ] -stm32f427ai = [ "stm32-metapac/stm32f427ai" ] -stm32f427ig = [ "stm32-metapac/stm32f427ig" ] -stm32f427ii = [ "stm32-metapac/stm32f427ii" ] -stm32f427vg = [ "stm32-metapac/stm32f427vg" ] -stm32f427vi = [ "stm32-metapac/stm32f427vi" ] -stm32f427zg = [ "stm32-metapac/stm32f427zg" ] -stm32f427zi = [ "stm32-metapac/stm32f427zi" ] -stm32f429ag = [ "stm32-metapac/stm32f429ag" ] -stm32f429ai = [ "stm32-metapac/stm32f429ai" ] -stm32f429be = [ "stm32-metapac/stm32f429be" ] -stm32f429bg = [ "stm32-metapac/stm32f429bg" ] -stm32f429bi = [ "stm32-metapac/stm32f429bi" ] -stm32f429ie = [ "stm32-metapac/stm32f429ie" ] -stm32f429ig = [ "stm32-metapac/stm32f429ig" ] -stm32f429ii = [ "stm32-metapac/stm32f429ii" ] -stm32f429ne = [ "stm32-metapac/stm32f429ne" ] -stm32f429ng = [ "stm32-metapac/stm32f429ng" ] -stm32f429ni = [ "stm32-metapac/stm32f429ni" ] -stm32f429ve = [ "stm32-metapac/stm32f429ve" ] -stm32f429vg = [ "stm32-metapac/stm32f429vg" ] -stm32f429vi = [ "stm32-metapac/stm32f429vi" ] -stm32f429ze = [ "stm32-metapac/stm32f429ze" ] -stm32f429zg = [ "stm32-metapac/stm32f429zg" ] -stm32f429zi = [ "stm32-metapac/stm32f429zi" ] -stm32f437ai = [ "stm32-metapac/stm32f437ai" ] -stm32f437ig = [ "stm32-metapac/stm32f437ig" ] -stm32f437ii = [ "stm32-metapac/stm32f437ii" ] -stm32f437vg = [ "stm32-metapac/stm32f437vg" ] -stm32f437vi = [ "stm32-metapac/stm32f437vi" ] -stm32f437zg = [ "stm32-metapac/stm32f437zg" ] -stm32f437zi = [ "stm32-metapac/stm32f437zi" ] -stm32f439ai = [ "stm32-metapac/stm32f439ai" ] -stm32f439bg = [ "stm32-metapac/stm32f439bg" ] -stm32f439bi = [ "stm32-metapac/stm32f439bi" ] -stm32f439ig = [ "stm32-metapac/stm32f439ig" ] -stm32f439ii = [ "stm32-metapac/stm32f439ii" ] -stm32f439ng = [ "stm32-metapac/stm32f439ng" ] -stm32f439ni = [ "stm32-metapac/stm32f439ni" ] -stm32f439vg = [ "stm32-metapac/stm32f439vg" ] -stm32f439vi = [ "stm32-metapac/stm32f439vi" ] -stm32f439zg = [ "stm32-metapac/stm32f439zg" ] -stm32f439zi = [ "stm32-metapac/stm32f439zi" ] -stm32f446mc = [ "stm32-metapac/stm32f446mc" ] -stm32f446me = [ "stm32-metapac/stm32f446me" ] -stm32f446rc = [ "stm32-metapac/stm32f446rc" ] -stm32f446re = [ "stm32-metapac/stm32f446re" ] -stm32f446vc = [ "stm32-metapac/stm32f446vc" ] -stm32f446ve = [ "stm32-metapac/stm32f446ve" ] -stm32f446zc = [ "stm32-metapac/stm32f446zc" ] -stm32f446ze = [ "stm32-metapac/stm32f446ze" ] -stm32f469ae = [ "stm32-metapac/stm32f469ae" ] -stm32f469ag = [ "stm32-metapac/stm32f469ag" ] -stm32f469ai = [ "stm32-metapac/stm32f469ai" ] -stm32f469be = [ "stm32-metapac/stm32f469be" ] -stm32f469bg = [ "stm32-metapac/stm32f469bg" ] -stm32f469bi = [ "stm32-metapac/stm32f469bi" ] -stm32f469ie = [ "stm32-metapac/stm32f469ie" ] -stm32f469ig = [ "stm32-metapac/stm32f469ig" ] -stm32f469ii = [ "stm32-metapac/stm32f469ii" ] -stm32f469ne = [ "stm32-metapac/stm32f469ne" ] -stm32f469ng = [ "stm32-metapac/stm32f469ng" ] -stm32f469ni = [ "stm32-metapac/stm32f469ni" ] -stm32f469ve = [ "stm32-metapac/stm32f469ve" ] -stm32f469vg = [ "stm32-metapac/stm32f469vg" ] -stm32f469vi = [ "stm32-metapac/stm32f469vi" ] -stm32f469ze = [ "stm32-metapac/stm32f469ze" ] -stm32f469zg = [ "stm32-metapac/stm32f469zg" ] -stm32f469zi = [ "stm32-metapac/stm32f469zi" ] -stm32f479ag = [ "stm32-metapac/stm32f479ag" ] -stm32f479ai = [ "stm32-metapac/stm32f479ai" ] -stm32f479bg = [ "stm32-metapac/stm32f479bg" ] -stm32f479bi = [ "stm32-metapac/stm32f479bi" ] -stm32f479ig = [ "stm32-metapac/stm32f479ig" ] -stm32f479ii = [ "stm32-metapac/stm32f479ii" ] -stm32f479ng = [ "stm32-metapac/stm32f479ng" ] -stm32f479ni = [ "stm32-metapac/stm32f479ni" ] -stm32f479vg = [ "stm32-metapac/stm32f479vg" ] -stm32f479vi = [ "stm32-metapac/stm32f479vi" ] -stm32f479zg = [ "stm32-metapac/stm32f479zg" ] -stm32f479zi = [ "stm32-metapac/stm32f479zi" ] -stm32f722ic = [ "stm32-metapac/stm32f722ic" ] -stm32f722ie = [ "stm32-metapac/stm32f722ie" ] -stm32f722rc = [ "stm32-metapac/stm32f722rc" ] -stm32f722re = [ "stm32-metapac/stm32f722re" ] -stm32f722vc = [ "stm32-metapac/stm32f722vc" ] -stm32f722ve = [ "stm32-metapac/stm32f722ve" ] -stm32f722zc = [ "stm32-metapac/stm32f722zc" ] -stm32f722ze = [ "stm32-metapac/stm32f722ze" ] -stm32f723ic = [ "stm32-metapac/stm32f723ic" ] -stm32f723ie = [ "stm32-metapac/stm32f723ie" ] -stm32f723vc = [ "stm32-metapac/stm32f723vc" ] -stm32f723ve = [ "stm32-metapac/stm32f723ve" ] -stm32f723zc = [ "stm32-metapac/stm32f723zc" ] -stm32f723ze = [ "stm32-metapac/stm32f723ze" ] -stm32f730i8 = [ "stm32-metapac/stm32f730i8" ] -stm32f730r8 = [ "stm32-metapac/stm32f730r8" ] -stm32f730v8 = [ "stm32-metapac/stm32f730v8" ] -stm32f730z8 = [ "stm32-metapac/stm32f730z8" ] -stm32f732ie = [ "stm32-metapac/stm32f732ie" ] -stm32f732re = [ "stm32-metapac/stm32f732re" ] -stm32f732ve = [ "stm32-metapac/stm32f732ve" ] -stm32f732ze = [ "stm32-metapac/stm32f732ze" ] -stm32f733ie = [ "stm32-metapac/stm32f733ie" ] -stm32f733ve = [ "stm32-metapac/stm32f733ve" ] -stm32f733ze = [ "stm32-metapac/stm32f733ze" ] -stm32f745ie = [ "stm32-metapac/stm32f745ie" ] -stm32f745ig = [ "stm32-metapac/stm32f745ig" ] -stm32f745ve = [ "stm32-metapac/stm32f745ve" ] -stm32f745vg = [ "stm32-metapac/stm32f745vg" ] -stm32f745ze = [ "stm32-metapac/stm32f745ze" ] -stm32f745zg = [ "stm32-metapac/stm32f745zg" ] -stm32f746be = [ "stm32-metapac/stm32f746be" ] -stm32f746bg = [ "stm32-metapac/stm32f746bg" ] -stm32f746ie = [ "stm32-metapac/stm32f746ie" ] -stm32f746ig = [ "stm32-metapac/stm32f746ig" ] -stm32f746ne = [ "stm32-metapac/stm32f746ne" ] -stm32f746ng = [ "stm32-metapac/stm32f746ng" ] -stm32f746ve = [ "stm32-metapac/stm32f746ve" ] -stm32f746vg = [ "stm32-metapac/stm32f746vg" ] -stm32f746ze = [ "stm32-metapac/stm32f746ze" ] -stm32f746zg = [ "stm32-metapac/stm32f746zg" ] -stm32f750n8 = [ "stm32-metapac/stm32f750n8" ] -stm32f750v8 = [ "stm32-metapac/stm32f750v8" ] -stm32f750z8 = [ "stm32-metapac/stm32f750z8" ] -stm32f756bg = [ "stm32-metapac/stm32f756bg" ] -stm32f756ig = [ "stm32-metapac/stm32f756ig" ] -stm32f756ng = [ "stm32-metapac/stm32f756ng" ] -stm32f756vg = [ "stm32-metapac/stm32f756vg" ] -stm32f756zg = [ "stm32-metapac/stm32f756zg" ] -stm32f765bg = [ "stm32-metapac/stm32f765bg" ] -stm32f765bi = [ "stm32-metapac/stm32f765bi" ] -stm32f765ig = [ "stm32-metapac/stm32f765ig" ] -stm32f765ii = [ "stm32-metapac/stm32f765ii" ] -stm32f765ng = [ "stm32-metapac/stm32f765ng" ] -stm32f765ni = [ "stm32-metapac/stm32f765ni" ] -stm32f765vg = [ "stm32-metapac/stm32f765vg" ] -stm32f765vi = [ "stm32-metapac/stm32f765vi" ] -stm32f765zg = [ "stm32-metapac/stm32f765zg" ] -stm32f765zi = [ "stm32-metapac/stm32f765zi" ] -stm32f767bg = [ "stm32-metapac/stm32f767bg" ] -stm32f767bi = [ "stm32-metapac/stm32f767bi" ] -stm32f767ig = [ "stm32-metapac/stm32f767ig" ] -stm32f767ii = [ "stm32-metapac/stm32f767ii" ] -stm32f767ng = [ "stm32-metapac/stm32f767ng" ] -stm32f767ni = [ "stm32-metapac/stm32f767ni" ] -stm32f767vg = [ "stm32-metapac/stm32f767vg" ] -stm32f767vi = [ "stm32-metapac/stm32f767vi" ] -stm32f767zg = [ "stm32-metapac/stm32f767zg" ] -stm32f767zi = [ "stm32-metapac/stm32f767zi" ] -stm32f768ai = [ "stm32-metapac/stm32f768ai" ] -stm32f769ag = [ "stm32-metapac/stm32f769ag" ] -stm32f769ai = [ "stm32-metapac/stm32f769ai" ] -stm32f769bg = [ "stm32-metapac/stm32f769bg" ] -stm32f769bi = [ "stm32-metapac/stm32f769bi" ] -stm32f769ig = [ "stm32-metapac/stm32f769ig" ] -stm32f769ii = [ "stm32-metapac/stm32f769ii" ] -stm32f769ng = [ "stm32-metapac/stm32f769ng" ] -stm32f769ni = [ "stm32-metapac/stm32f769ni" ] -stm32f777bi = [ "stm32-metapac/stm32f777bi" ] -stm32f777ii = [ "stm32-metapac/stm32f777ii" ] -stm32f777ni = [ "stm32-metapac/stm32f777ni" ] -stm32f777vi = [ "stm32-metapac/stm32f777vi" ] -stm32f777zi = [ "stm32-metapac/stm32f777zi" ] -stm32f778ai = [ "stm32-metapac/stm32f778ai" ] -stm32f779ai = [ "stm32-metapac/stm32f779ai" ] -stm32f779bi = [ "stm32-metapac/stm32f779bi" ] -stm32f779ii = [ "stm32-metapac/stm32f779ii" ] -stm32f779ni = [ "stm32-metapac/stm32f779ni" ] -stm32g030c6 = [ "stm32-metapac/stm32g030c6" ] -stm32g030c8 = [ "stm32-metapac/stm32g030c8" ] -stm32g030f6 = [ "stm32-metapac/stm32g030f6" ] -stm32g030j6 = [ "stm32-metapac/stm32g030j6" ] -stm32g030k6 = [ "stm32-metapac/stm32g030k6" ] -stm32g030k8 = [ "stm32-metapac/stm32g030k8" ] -stm32g031c4 = [ "stm32-metapac/stm32g031c4" ] -stm32g031c6 = [ "stm32-metapac/stm32g031c6" ] -stm32g031c8 = [ "stm32-metapac/stm32g031c8" ] -stm32g031f4 = [ "stm32-metapac/stm32g031f4" ] -stm32g031f6 = [ "stm32-metapac/stm32g031f6" ] -stm32g031f8 = [ "stm32-metapac/stm32g031f8" ] -stm32g031g4 = [ "stm32-metapac/stm32g031g4" ] -stm32g031g6 = [ "stm32-metapac/stm32g031g6" ] -stm32g031g8 = [ "stm32-metapac/stm32g031g8" ] -stm32g031j4 = [ "stm32-metapac/stm32g031j4" ] -stm32g031j6 = [ "stm32-metapac/stm32g031j6" ] -stm32g031k4 = [ "stm32-metapac/stm32g031k4" ] -stm32g031k6 = [ "stm32-metapac/stm32g031k6" ] -stm32g031k8 = [ "stm32-metapac/stm32g031k8" ] -stm32g031y8 = [ "stm32-metapac/stm32g031y8" ] -stm32g041c6 = [ "stm32-metapac/stm32g041c6" ] -stm32g041c8 = [ "stm32-metapac/stm32g041c8" ] -stm32g041f6 = [ "stm32-metapac/stm32g041f6" ] -stm32g041f8 = [ "stm32-metapac/stm32g041f8" ] -stm32g041g6 = [ "stm32-metapac/stm32g041g6" ] -stm32g041g8 = [ "stm32-metapac/stm32g041g8" ] -stm32g041j6 = [ "stm32-metapac/stm32g041j6" ] -stm32g041k6 = [ "stm32-metapac/stm32g041k6" ] -stm32g041k8 = [ "stm32-metapac/stm32g041k8" ] -stm32g041y8 = [ "stm32-metapac/stm32g041y8" ] -stm32g050c6 = [ "stm32-metapac/stm32g050c6" ] -stm32g050c8 = [ "stm32-metapac/stm32g050c8" ] -stm32g050f6 = [ "stm32-metapac/stm32g050f6" ] -stm32g050k6 = [ "stm32-metapac/stm32g050k6" ] -stm32g050k8 = [ "stm32-metapac/stm32g050k8" ] -stm32g051c6 = [ "stm32-metapac/stm32g051c6" ] -stm32g051c8 = [ "stm32-metapac/stm32g051c8" ] -stm32g051f6 = [ "stm32-metapac/stm32g051f6" ] -stm32g051f8 = [ "stm32-metapac/stm32g051f8" ] -stm32g051g6 = [ "stm32-metapac/stm32g051g6" ] -stm32g051g8 = [ "stm32-metapac/stm32g051g8" ] -stm32g051k6 = [ "stm32-metapac/stm32g051k6" ] -stm32g051k8 = [ "stm32-metapac/stm32g051k8" ] -stm32g061c6 = [ "stm32-metapac/stm32g061c6" ] -stm32g061c8 = [ "stm32-metapac/stm32g061c8" ] -stm32g061f6 = [ "stm32-metapac/stm32g061f6" ] -stm32g061f8 = [ "stm32-metapac/stm32g061f8" ] -stm32g061g6 = [ "stm32-metapac/stm32g061g6" ] -stm32g061g8 = [ "stm32-metapac/stm32g061g8" ] -stm32g061k6 = [ "stm32-metapac/stm32g061k6" ] -stm32g061k8 = [ "stm32-metapac/stm32g061k8" ] -stm32g070cb = [ "stm32-metapac/stm32g070cb" ] -stm32g070kb = [ "stm32-metapac/stm32g070kb" ] -stm32g070rb = [ "stm32-metapac/stm32g070rb" ] -stm32g071c6 = [ "stm32-metapac/stm32g071c6" ] -stm32g071c8 = [ "stm32-metapac/stm32g071c8" ] -stm32g071cb = [ "stm32-metapac/stm32g071cb" ] -stm32g071eb = [ "stm32-metapac/stm32g071eb" ] -stm32g071g6 = [ "stm32-metapac/stm32g071g6" ] -stm32g071g8 = [ "stm32-metapac/stm32g071g8" ] -stm32g071gb = [ "stm32-metapac/stm32g071gb" ] -stm32g071k6 = [ "stm32-metapac/stm32g071k6" ] -stm32g071k8 = [ "stm32-metapac/stm32g071k8" ] -stm32g071kb = [ "stm32-metapac/stm32g071kb" ] -stm32g071r6 = [ "stm32-metapac/stm32g071r6" ] -stm32g071r8 = [ "stm32-metapac/stm32g071r8" ] -stm32g071rb = [ "stm32-metapac/stm32g071rb" ] -stm32g081cb = [ "stm32-metapac/stm32g081cb" ] -stm32g081eb = [ "stm32-metapac/stm32g081eb" ] -stm32g081gb = [ "stm32-metapac/stm32g081gb" ] -stm32g081kb = [ "stm32-metapac/stm32g081kb" ] -stm32g081rb = [ "stm32-metapac/stm32g081rb" ] -stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce" ] -stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke" ] -stm32g0b0re = [ "stm32-metapac/stm32g0b0re" ] -stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve" ] -stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb" ] -stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc" ] -stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce" ] -stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb" ] -stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc" ] -stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke" ] -stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb" ] -stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc" ] -stm32g0b1me = [ "stm32-metapac/stm32g0b1me" ] -stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne" ] -stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb" ] -stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc" ] -stm32g0b1re = [ "stm32-metapac/stm32g0b1re" ] -stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb" ] -stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc" ] -stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve" ] -stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc" ] -stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce" ] -stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc" ] -stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke" ] -stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc" ] -stm32g0c1me = [ "stm32-metapac/stm32g0c1me" ] -stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne" ] -stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc" ] -stm32g0c1re = [ "stm32-metapac/stm32g0c1re" ] -stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc" ] -stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve" ] -stm32g431c6 = [ "stm32-metapac/stm32g431c6" ] -stm32g431c8 = [ "stm32-metapac/stm32g431c8" ] -stm32g431cb = [ "stm32-metapac/stm32g431cb" ] -stm32g431k6 = [ "stm32-metapac/stm32g431k6" ] -stm32g431k8 = [ "stm32-metapac/stm32g431k8" ] -stm32g431kb = [ "stm32-metapac/stm32g431kb" ] -stm32g431m6 = [ "stm32-metapac/stm32g431m6" ] -stm32g431m8 = [ "stm32-metapac/stm32g431m8" ] -stm32g431mb = [ "stm32-metapac/stm32g431mb" ] -stm32g431r6 = [ "stm32-metapac/stm32g431r6" ] -stm32g431r8 = [ "stm32-metapac/stm32g431r8" ] -stm32g431rb = [ "stm32-metapac/stm32g431rb" ] -stm32g431v6 = [ "stm32-metapac/stm32g431v6" ] -stm32g431v8 = [ "stm32-metapac/stm32g431v8" ] -stm32g431vb = [ "stm32-metapac/stm32g431vb" ] -stm32g441cb = [ "stm32-metapac/stm32g441cb" ] -stm32g441kb = [ "stm32-metapac/stm32g441kb" ] -stm32g441mb = [ "stm32-metapac/stm32g441mb" ] -stm32g441rb = [ "stm32-metapac/stm32g441rb" ] -stm32g441vb = [ "stm32-metapac/stm32g441vb" ] -stm32g471cc = [ "stm32-metapac/stm32g471cc" ] -stm32g471ce = [ "stm32-metapac/stm32g471ce" ] -stm32g471mc = [ "stm32-metapac/stm32g471mc" ] -stm32g471me = [ "stm32-metapac/stm32g471me" ] -stm32g471qc = [ "stm32-metapac/stm32g471qc" ] -stm32g471qe = [ "stm32-metapac/stm32g471qe" ] -stm32g471rc = [ "stm32-metapac/stm32g471rc" ] -stm32g471re = [ "stm32-metapac/stm32g471re" ] -stm32g471vc = [ "stm32-metapac/stm32g471vc" ] -stm32g471ve = [ "stm32-metapac/stm32g471ve" ] -stm32g473cb = [ "stm32-metapac/stm32g473cb" ] -stm32g473cc = [ "stm32-metapac/stm32g473cc" ] -stm32g473ce = [ "stm32-metapac/stm32g473ce" ] -stm32g473mb = [ "stm32-metapac/stm32g473mb" ] -stm32g473mc = [ "stm32-metapac/stm32g473mc" ] -stm32g473me = [ "stm32-metapac/stm32g473me" ] -stm32g473pb = [ "stm32-metapac/stm32g473pb" ] -stm32g473pc = [ "stm32-metapac/stm32g473pc" ] -stm32g473pe = [ "stm32-metapac/stm32g473pe" ] -stm32g473qb = [ "stm32-metapac/stm32g473qb" ] -stm32g473qc = [ "stm32-metapac/stm32g473qc" ] -stm32g473qe = [ "stm32-metapac/stm32g473qe" ] -stm32g473rb = [ "stm32-metapac/stm32g473rb" ] -stm32g473rc = [ "stm32-metapac/stm32g473rc" ] -stm32g473re = [ "stm32-metapac/stm32g473re" ] -stm32g473vb = [ "stm32-metapac/stm32g473vb" ] -stm32g473vc = [ "stm32-metapac/stm32g473vc" ] -stm32g473ve = [ "stm32-metapac/stm32g473ve" ] -stm32g474cb = [ "stm32-metapac/stm32g474cb" ] -stm32g474cc = [ "stm32-metapac/stm32g474cc" ] -stm32g474ce = [ "stm32-metapac/stm32g474ce" ] -stm32g474mb = [ "stm32-metapac/stm32g474mb" ] -stm32g474mc = [ "stm32-metapac/stm32g474mc" ] -stm32g474me = [ "stm32-metapac/stm32g474me" ] -stm32g474pb = [ "stm32-metapac/stm32g474pb" ] -stm32g474pc = [ "stm32-metapac/stm32g474pc" ] -stm32g474pe = [ "stm32-metapac/stm32g474pe" ] -stm32g474qb = [ "stm32-metapac/stm32g474qb" ] -stm32g474qc = [ "stm32-metapac/stm32g474qc" ] -stm32g474qe = [ "stm32-metapac/stm32g474qe" ] -stm32g474rb = [ "stm32-metapac/stm32g474rb" ] -stm32g474rc = [ "stm32-metapac/stm32g474rc" ] -stm32g474re = [ "stm32-metapac/stm32g474re" ] -stm32g474vb = [ "stm32-metapac/stm32g474vb" ] -stm32g474vc = [ "stm32-metapac/stm32g474vc" ] -stm32g474ve = [ "stm32-metapac/stm32g474ve" ] -stm32g483ce = [ "stm32-metapac/stm32g483ce" ] -stm32g483me = [ "stm32-metapac/stm32g483me" ] -stm32g483pe = [ "stm32-metapac/stm32g483pe" ] -stm32g483qe = [ "stm32-metapac/stm32g483qe" ] -stm32g483re = [ "stm32-metapac/stm32g483re" ] -stm32g483ve = [ "stm32-metapac/stm32g483ve" ] -stm32g484ce = [ "stm32-metapac/stm32g484ce" ] -stm32g484me = [ "stm32-metapac/stm32g484me" ] -stm32g484pe = [ "stm32-metapac/stm32g484pe" ] -stm32g484qe = [ "stm32-metapac/stm32g484qe" ] -stm32g484re = [ "stm32-metapac/stm32g484re" ] -stm32g484ve = [ "stm32-metapac/stm32g484ve" ] -stm32g491cc = [ "stm32-metapac/stm32g491cc" ] -stm32g491ce = [ "stm32-metapac/stm32g491ce" ] -stm32g491kc = [ "stm32-metapac/stm32g491kc" ] -stm32g491ke = [ "stm32-metapac/stm32g491ke" ] -stm32g491mc = [ "stm32-metapac/stm32g491mc" ] -stm32g491me = [ "stm32-metapac/stm32g491me" ] -stm32g491rc = [ "stm32-metapac/stm32g491rc" ] -stm32g491re = [ "stm32-metapac/stm32g491re" ] -stm32g491vc = [ "stm32-metapac/stm32g491vc" ] -stm32g491ve = [ "stm32-metapac/stm32g491ve" ] -stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce" ] -stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] -stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] -stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] -stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] -stm32h503cb = [ "stm32-metapac/stm32h503cb" ] -stm32h503eb = [ "stm32-metapac/stm32h503eb" ] -stm32h503kb = [ "stm32-metapac/stm32h503kb" ] -stm32h503rb = [ "stm32-metapac/stm32h503rb" ] -stm32h562ag = [ "stm32-metapac/stm32h562ag" ] -stm32h562ai = [ "stm32-metapac/stm32h562ai" ] -stm32h562ig = [ "stm32-metapac/stm32h562ig" ] -stm32h562ii = [ "stm32-metapac/stm32h562ii" ] -stm32h562rg = [ "stm32-metapac/stm32h562rg" ] -stm32h562ri = [ "stm32-metapac/stm32h562ri" ] -stm32h562vg = [ "stm32-metapac/stm32h562vg" ] -stm32h562vi = [ "stm32-metapac/stm32h562vi" ] -stm32h562zg = [ "stm32-metapac/stm32h562zg" ] -stm32h562zi = [ "stm32-metapac/stm32h562zi" ] -stm32h563ag = [ "stm32-metapac/stm32h563ag" ] -stm32h563ai = [ "stm32-metapac/stm32h563ai" ] -stm32h563ig = [ "stm32-metapac/stm32h563ig" ] -stm32h563ii = [ "stm32-metapac/stm32h563ii" ] -stm32h563mi = [ "stm32-metapac/stm32h563mi" ] -stm32h563rg = [ "stm32-metapac/stm32h563rg" ] -stm32h563ri = [ "stm32-metapac/stm32h563ri" ] -stm32h563vg = [ "stm32-metapac/stm32h563vg" ] -stm32h563vi = [ "stm32-metapac/stm32h563vi" ] -stm32h563zg = [ "stm32-metapac/stm32h563zg" ] -stm32h563zi = [ "stm32-metapac/stm32h563zi" ] -stm32h573ai = [ "stm32-metapac/stm32h573ai" ] -stm32h573ii = [ "stm32-metapac/stm32h573ii" ] -stm32h573mi = [ "stm32-metapac/stm32h573mi" ] -stm32h573ri = [ "stm32-metapac/stm32h573ri" ] -stm32h573vi = [ "stm32-metapac/stm32h573vi" ] -stm32h573zi = [ "stm32-metapac/stm32h573zi" ] -stm32h723ve = [ "stm32-metapac/stm32h723ve" ] -stm32h723vg = [ "stm32-metapac/stm32h723vg" ] -stm32h723ze = [ "stm32-metapac/stm32h723ze" ] -stm32h723zg = [ "stm32-metapac/stm32h723zg" ] -stm32h725ae = [ "stm32-metapac/stm32h725ae" ] -stm32h725ag = [ "stm32-metapac/stm32h725ag" ] -stm32h725ie = [ "stm32-metapac/stm32h725ie" ] -stm32h725ig = [ "stm32-metapac/stm32h725ig" ] -stm32h725re = [ "stm32-metapac/stm32h725re" ] -stm32h725rg = [ "stm32-metapac/stm32h725rg" ] -stm32h725ve = [ "stm32-metapac/stm32h725ve" ] -stm32h725vg = [ "stm32-metapac/stm32h725vg" ] -stm32h725ze = [ "stm32-metapac/stm32h725ze" ] -stm32h725zg = [ "stm32-metapac/stm32h725zg" ] -stm32h730ab = [ "stm32-metapac/stm32h730ab" ] -stm32h730ib = [ "stm32-metapac/stm32h730ib" ] -stm32h730vb = [ "stm32-metapac/stm32h730vb" ] -stm32h730zb = [ "stm32-metapac/stm32h730zb" ] -stm32h733vg = [ "stm32-metapac/stm32h733vg" ] -stm32h733zg = [ "stm32-metapac/stm32h733zg" ] -stm32h735ag = [ "stm32-metapac/stm32h735ag" ] -stm32h735ig = [ "stm32-metapac/stm32h735ig" ] -stm32h735rg = [ "stm32-metapac/stm32h735rg" ] -stm32h735vg = [ "stm32-metapac/stm32h735vg" ] -stm32h735zg = [ "stm32-metapac/stm32h735zg" ] -stm32h742ag = [ "stm32-metapac/stm32h742ag" ] -stm32h742ai = [ "stm32-metapac/stm32h742ai" ] -stm32h742bg = [ "stm32-metapac/stm32h742bg" ] -stm32h742bi = [ "stm32-metapac/stm32h742bi" ] -stm32h742ig = [ "stm32-metapac/stm32h742ig" ] -stm32h742ii = [ "stm32-metapac/stm32h742ii" ] -stm32h742vg = [ "stm32-metapac/stm32h742vg" ] -stm32h742vi = [ "stm32-metapac/stm32h742vi" ] -stm32h742xg = [ "stm32-metapac/stm32h742xg" ] -stm32h742xi = [ "stm32-metapac/stm32h742xi" ] -stm32h742zg = [ "stm32-metapac/stm32h742zg" ] -stm32h742zi = [ "stm32-metapac/stm32h742zi" ] -stm32h743ag = [ "stm32-metapac/stm32h743ag" ] -stm32h743ai = [ "stm32-metapac/stm32h743ai" ] -stm32h743bg = [ "stm32-metapac/stm32h743bg" ] -stm32h743bi = [ "stm32-metapac/stm32h743bi" ] -stm32h743ig = [ "stm32-metapac/stm32h743ig" ] -stm32h743ii = [ "stm32-metapac/stm32h743ii" ] -stm32h743vg = [ "stm32-metapac/stm32h743vg" ] -stm32h743vi = [ "stm32-metapac/stm32h743vi" ] -stm32h743xg = [ "stm32-metapac/stm32h743xg" ] -stm32h743xi = [ "stm32-metapac/stm32h743xi" ] -stm32h743zg = [ "stm32-metapac/stm32h743zg" ] -stm32h743zi = [ "stm32-metapac/stm32h743zi" ] -stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7" ] -stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4" ] -stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7" ] -stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4" ] -stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7" ] -stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4" ] -stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7" ] -stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4" ] -stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7" ] -stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4" ] -stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7" ] -stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4" ] -stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7" ] -stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4" ] -stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7" ] -stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4" ] -stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7" ] -stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4" ] -stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7" ] -stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4" ] -stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7" ] -stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4" ] -stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7" ] -stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4" ] -stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7" ] -stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4" ] -stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7" ] -stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4" ] -stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7" ] -stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4" ] -stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7" ] -stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4" ] -stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7" ] -stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4" ] -stm32h750ib = [ "stm32-metapac/stm32h750ib" ] -stm32h750vb = [ "stm32-metapac/stm32h750vb" ] -stm32h750xb = [ "stm32-metapac/stm32h750xb" ] -stm32h750zb = [ "stm32-metapac/stm32h750zb" ] -stm32h753ai = [ "stm32-metapac/stm32h753ai" ] -stm32h753bi = [ "stm32-metapac/stm32h753bi" ] -stm32h753ii = [ "stm32-metapac/stm32h753ii" ] -stm32h753vi = [ "stm32-metapac/stm32h753vi" ] -stm32h753xi = [ "stm32-metapac/stm32h753xi" ] -stm32h753zi = [ "stm32-metapac/stm32h753zi" ] -stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7" ] -stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4" ] -stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7" ] -stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4" ] -stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7" ] -stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4" ] -stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7" ] -stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4" ] -stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7" ] -stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4" ] -stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7" ] -stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4" ] -stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7" ] -stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4" ] -stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7" ] -stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4" ] -stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7" ] -stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4" ] -stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] -stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] -stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] -stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii" ] -stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg" ] -stm32h7a3li = [ "stm32-metapac/stm32h7a3li" ] -stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng" ] -stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni" ] -stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi" ] -stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg" ] -stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri" ] -stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg" ] -stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi" ] -stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg" ] -stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi" ] -stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab" ] -stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib" ] -stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb" ] -stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb" ] -stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb" ] -stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai" ] -stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii" ] -stm32h7b3li = [ "stm32-metapac/stm32h7b3li" ] -stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni" ] -stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi" ] -stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri" ] -stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi" ] -stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi" ] -stm32l010c6 = [ "stm32-metapac/stm32l010c6" ] -stm32l010f4 = [ "stm32-metapac/stm32l010f4" ] -stm32l010k4 = [ "stm32-metapac/stm32l010k4" ] -stm32l010k8 = [ "stm32-metapac/stm32l010k8" ] -stm32l010r8 = [ "stm32-metapac/stm32l010r8" ] -stm32l010rb = [ "stm32-metapac/stm32l010rb" ] -stm32l011d3 = [ "stm32-metapac/stm32l011d3" ] -stm32l011d4 = [ "stm32-metapac/stm32l011d4" ] -stm32l011e3 = [ "stm32-metapac/stm32l011e3" ] -stm32l011e4 = [ "stm32-metapac/stm32l011e4" ] -stm32l011f3 = [ "stm32-metapac/stm32l011f3" ] -stm32l011f4 = [ "stm32-metapac/stm32l011f4" ] -stm32l011g3 = [ "stm32-metapac/stm32l011g3" ] -stm32l011g4 = [ "stm32-metapac/stm32l011g4" ] -stm32l011k3 = [ "stm32-metapac/stm32l011k3" ] -stm32l011k4 = [ "stm32-metapac/stm32l011k4" ] -stm32l021d4 = [ "stm32-metapac/stm32l021d4" ] -stm32l021f4 = [ "stm32-metapac/stm32l021f4" ] -stm32l021g4 = [ "stm32-metapac/stm32l021g4" ] -stm32l021k4 = [ "stm32-metapac/stm32l021k4" ] -stm32l031c4 = [ "stm32-metapac/stm32l031c4" ] -stm32l031c6 = [ "stm32-metapac/stm32l031c6" ] -stm32l031e4 = [ "stm32-metapac/stm32l031e4" ] -stm32l031e6 = [ "stm32-metapac/stm32l031e6" ] -stm32l031f4 = [ "stm32-metapac/stm32l031f4" ] -stm32l031f6 = [ "stm32-metapac/stm32l031f6" ] -stm32l031g4 = [ "stm32-metapac/stm32l031g4" ] -stm32l031g6 = [ "stm32-metapac/stm32l031g6" ] -stm32l031k4 = [ "stm32-metapac/stm32l031k4" ] -stm32l031k6 = [ "stm32-metapac/stm32l031k6" ] -stm32l041c4 = [ "stm32-metapac/stm32l041c4" ] -stm32l041c6 = [ "stm32-metapac/stm32l041c6" ] -stm32l041e6 = [ "stm32-metapac/stm32l041e6" ] -stm32l041f6 = [ "stm32-metapac/stm32l041f6" ] -stm32l041g6 = [ "stm32-metapac/stm32l041g6" ] -stm32l041k6 = [ "stm32-metapac/stm32l041k6" ] -stm32l051c6 = [ "stm32-metapac/stm32l051c6" ] -stm32l051c8 = [ "stm32-metapac/stm32l051c8" ] -stm32l051k6 = [ "stm32-metapac/stm32l051k6" ] -stm32l051k8 = [ "stm32-metapac/stm32l051k8" ] -stm32l051r6 = [ "stm32-metapac/stm32l051r6" ] -stm32l051r8 = [ "stm32-metapac/stm32l051r8" ] -stm32l051t6 = [ "stm32-metapac/stm32l051t6" ] -stm32l051t8 = [ "stm32-metapac/stm32l051t8" ] -stm32l052c6 = [ "stm32-metapac/stm32l052c6" ] -stm32l052c8 = [ "stm32-metapac/stm32l052c8" ] -stm32l052k6 = [ "stm32-metapac/stm32l052k6" ] -stm32l052k8 = [ "stm32-metapac/stm32l052k8" ] -stm32l052r6 = [ "stm32-metapac/stm32l052r6" ] -stm32l052r8 = [ "stm32-metapac/stm32l052r8" ] -stm32l052t6 = [ "stm32-metapac/stm32l052t6" ] -stm32l052t8 = [ "stm32-metapac/stm32l052t8" ] -stm32l053c6 = [ "stm32-metapac/stm32l053c6" ] -stm32l053c8 = [ "stm32-metapac/stm32l053c8" ] -stm32l053r6 = [ "stm32-metapac/stm32l053r6" ] -stm32l053r8 = [ "stm32-metapac/stm32l053r8" ] -stm32l062c8 = [ "stm32-metapac/stm32l062c8" ] -stm32l062k8 = [ "stm32-metapac/stm32l062k8" ] -stm32l063c8 = [ "stm32-metapac/stm32l063c8" ] -stm32l063r8 = [ "stm32-metapac/stm32l063r8" ] -stm32l071c8 = [ "stm32-metapac/stm32l071c8" ] -stm32l071cb = [ "stm32-metapac/stm32l071cb" ] -stm32l071cz = [ "stm32-metapac/stm32l071cz" ] -stm32l071k8 = [ "stm32-metapac/stm32l071k8" ] -stm32l071kb = [ "stm32-metapac/stm32l071kb" ] -stm32l071kz = [ "stm32-metapac/stm32l071kz" ] -stm32l071rb = [ "stm32-metapac/stm32l071rb" ] -stm32l071rz = [ "stm32-metapac/stm32l071rz" ] -stm32l071v8 = [ "stm32-metapac/stm32l071v8" ] -stm32l071vb = [ "stm32-metapac/stm32l071vb" ] -stm32l071vz = [ "stm32-metapac/stm32l071vz" ] -stm32l072cb = [ "stm32-metapac/stm32l072cb" ] -stm32l072cz = [ "stm32-metapac/stm32l072cz" ] -stm32l072kb = [ "stm32-metapac/stm32l072kb" ] -stm32l072kz = [ "stm32-metapac/stm32l072kz" ] -stm32l072rb = [ "stm32-metapac/stm32l072rb" ] -stm32l072rz = [ "stm32-metapac/stm32l072rz" ] -stm32l072v8 = [ "stm32-metapac/stm32l072v8" ] -stm32l072vb = [ "stm32-metapac/stm32l072vb" ] -stm32l072vz = [ "stm32-metapac/stm32l072vz" ] -stm32l073cb = [ "stm32-metapac/stm32l073cb" ] -stm32l073cz = [ "stm32-metapac/stm32l073cz" ] -stm32l073rb = [ "stm32-metapac/stm32l073rb" ] -stm32l073rz = [ "stm32-metapac/stm32l073rz" ] -stm32l073v8 = [ "stm32-metapac/stm32l073v8" ] -stm32l073vb = [ "stm32-metapac/stm32l073vb" ] -stm32l073vz = [ "stm32-metapac/stm32l073vz" ] -stm32l081cb = [ "stm32-metapac/stm32l081cb" ] -stm32l081cz = [ "stm32-metapac/stm32l081cz" ] -stm32l081kz = [ "stm32-metapac/stm32l081kz" ] -stm32l082cz = [ "stm32-metapac/stm32l082cz" ] -stm32l082kb = [ "stm32-metapac/stm32l082kb" ] -stm32l082kz = [ "stm32-metapac/stm32l082kz" ] -stm32l083cb = [ "stm32-metapac/stm32l083cb" ] -stm32l083cz = [ "stm32-metapac/stm32l083cz" ] -stm32l083rb = [ "stm32-metapac/stm32l083rb" ] -stm32l083rz = [ "stm32-metapac/stm32l083rz" ] -stm32l083v8 = [ "stm32-metapac/stm32l083v8" ] -stm32l083vb = [ "stm32-metapac/stm32l083vb" ] -stm32l083vz = [ "stm32-metapac/stm32l083vz" ] -stm32l100c6 = [ "stm32-metapac/stm32l100c6" ] -stm32l100c6-a = [ "stm32-metapac/stm32l100c6-a" ] -stm32l100r8 = [ "stm32-metapac/stm32l100r8" ] -stm32l100r8-a = [ "stm32-metapac/stm32l100r8-a" ] -stm32l100rb = [ "stm32-metapac/stm32l100rb" ] -stm32l100rb-a = [ "stm32-metapac/stm32l100rb-a" ] -stm32l100rc = [ "stm32-metapac/stm32l100rc" ] -stm32l151c6 = [ "stm32-metapac/stm32l151c6" ] -stm32l151c6-a = [ "stm32-metapac/stm32l151c6-a" ] -stm32l151c8 = [ "stm32-metapac/stm32l151c8" ] -stm32l151c8-a = [ "stm32-metapac/stm32l151c8-a" ] -stm32l151cb = [ "stm32-metapac/stm32l151cb" ] -stm32l151cb-a = [ "stm32-metapac/stm32l151cb-a" ] -stm32l151cc = [ "stm32-metapac/stm32l151cc" ] -stm32l151qc = [ "stm32-metapac/stm32l151qc" ] -stm32l151qd = [ "stm32-metapac/stm32l151qd" ] -stm32l151qe = [ "stm32-metapac/stm32l151qe" ] -stm32l151r6 = [ "stm32-metapac/stm32l151r6" ] -stm32l151r6-a = [ "stm32-metapac/stm32l151r6-a" ] -stm32l151r8 = [ "stm32-metapac/stm32l151r8" ] -stm32l151r8-a = [ "stm32-metapac/stm32l151r8-a" ] -stm32l151rb = [ "stm32-metapac/stm32l151rb" ] -stm32l151rb-a = [ "stm32-metapac/stm32l151rb-a" ] -stm32l151rc = [ "stm32-metapac/stm32l151rc" ] -stm32l151rc-a = [ "stm32-metapac/stm32l151rc-a" ] -stm32l151rd = [ "stm32-metapac/stm32l151rd" ] -stm32l151re = [ "stm32-metapac/stm32l151re" ] -stm32l151uc = [ "stm32-metapac/stm32l151uc" ] -stm32l151v8 = [ "stm32-metapac/stm32l151v8" ] -stm32l151v8-a = [ "stm32-metapac/stm32l151v8-a" ] -stm32l151vb = [ "stm32-metapac/stm32l151vb" ] -stm32l151vb-a = [ "stm32-metapac/stm32l151vb-a" ] -stm32l151vc = [ "stm32-metapac/stm32l151vc" ] -stm32l151vc-a = [ "stm32-metapac/stm32l151vc-a" ] -stm32l151vd = [ "stm32-metapac/stm32l151vd" ] -stm32l151vd-x = [ "stm32-metapac/stm32l151vd-x" ] -stm32l151ve = [ "stm32-metapac/stm32l151ve" ] -stm32l151zc = [ "stm32-metapac/stm32l151zc" ] -stm32l151zd = [ "stm32-metapac/stm32l151zd" ] -stm32l151ze = [ "stm32-metapac/stm32l151ze" ] -stm32l152c6 = [ "stm32-metapac/stm32l152c6" ] -stm32l152c6-a = [ "stm32-metapac/stm32l152c6-a" ] -stm32l152c8 = [ "stm32-metapac/stm32l152c8" ] -stm32l152c8-a = [ "stm32-metapac/stm32l152c8-a" ] -stm32l152cb = [ "stm32-metapac/stm32l152cb" ] -stm32l152cb-a = [ "stm32-metapac/stm32l152cb-a" ] -stm32l152cc = [ "stm32-metapac/stm32l152cc" ] -stm32l152qc = [ "stm32-metapac/stm32l152qc" ] -stm32l152qd = [ "stm32-metapac/stm32l152qd" ] -stm32l152qe = [ "stm32-metapac/stm32l152qe" ] -stm32l152r6 = [ "stm32-metapac/stm32l152r6" ] -stm32l152r6-a = [ "stm32-metapac/stm32l152r6-a" ] -stm32l152r8 = [ "stm32-metapac/stm32l152r8" ] -stm32l152r8-a = [ "stm32-metapac/stm32l152r8-a" ] -stm32l152rb = [ "stm32-metapac/stm32l152rb" ] -stm32l152rb-a = [ "stm32-metapac/stm32l152rb-a" ] -stm32l152rc = [ "stm32-metapac/stm32l152rc" ] -stm32l152rc-a = [ "stm32-metapac/stm32l152rc-a" ] -stm32l152rd = [ "stm32-metapac/stm32l152rd" ] -stm32l152re = [ "stm32-metapac/stm32l152re" ] -stm32l152uc = [ "stm32-metapac/stm32l152uc" ] -stm32l152v8 = [ "stm32-metapac/stm32l152v8" ] -stm32l152v8-a = [ "stm32-metapac/stm32l152v8-a" ] -stm32l152vb = [ "stm32-metapac/stm32l152vb" ] -stm32l152vb-a = [ "stm32-metapac/stm32l152vb-a" ] -stm32l152vc = [ "stm32-metapac/stm32l152vc" ] -stm32l152vc-a = [ "stm32-metapac/stm32l152vc-a" ] -stm32l152vd = [ "stm32-metapac/stm32l152vd" ] -stm32l152vd-x = [ "stm32-metapac/stm32l152vd-x" ] -stm32l152ve = [ "stm32-metapac/stm32l152ve" ] -stm32l152zc = [ "stm32-metapac/stm32l152zc" ] -stm32l152zd = [ "stm32-metapac/stm32l152zd" ] -stm32l152ze = [ "stm32-metapac/stm32l152ze" ] -stm32l162qc = [ "stm32-metapac/stm32l162qc" ] -stm32l162qd = [ "stm32-metapac/stm32l162qd" ] -stm32l162rc = [ "stm32-metapac/stm32l162rc" ] -stm32l162rc-a = [ "stm32-metapac/stm32l162rc-a" ] -stm32l162rd = [ "stm32-metapac/stm32l162rd" ] -stm32l162re = [ "stm32-metapac/stm32l162re" ] -stm32l162vc = [ "stm32-metapac/stm32l162vc" ] -stm32l162vc-a = [ "stm32-metapac/stm32l162vc-a" ] -stm32l162vd = [ "stm32-metapac/stm32l162vd" ] -stm32l162vd-x = [ "stm32-metapac/stm32l162vd-x" ] -stm32l162ve = [ "stm32-metapac/stm32l162ve" ] -stm32l162zc = [ "stm32-metapac/stm32l162zc" ] -stm32l162zd = [ "stm32-metapac/stm32l162zd" ] -stm32l162ze = [ "stm32-metapac/stm32l162ze" ] -stm32l412c8 = [ "stm32-metapac/stm32l412c8" ] -stm32l412cb = [ "stm32-metapac/stm32l412cb" ] -stm32l412k8 = [ "stm32-metapac/stm32l412k8" ] -stm32l412kb = [ "stm32-metapac/stm32l412kb" ] -stm32l412r8 = [ "stm32-metapac/stm32l412r8" ] -stm32l412rb = [ "stm32-metapac/stm32l412rb" ] -stm32l412t8 = [ "stm32-metapac/stm32l412t8" ] -stm32l412tb = [ "stm32-metapac/stm32l412tb" ] -stm32l422cb = [ "stm32-metapac/stm32l422cb" ] -stm32l422kb = [ "stm32-metapac/stm32l422kb" ] -stm32l422rb = [ "stm32-metapac/stm32l422rb" ] -stm32l422tb = [ "stm32-metapac/stm32l422tb" ] -stm32l431cb = [ "stm32-metapac/stm32l431cb" ] -stm32l431cc = [ "stm32-metapac/stm32l431cc" ] -stm32l431kb = [ "stm32-metapac/stm32l431kb" ] -stm32l431kc = [ "stm32-metapac/stm32l431kc" ] -stm32l431rb = [ "stm32-metapac/stm32l431rb" ] -stm32l431rc = [ "stm32-metapac/stm32l431rc" ] -stm32l431vc = [ "stm32-metapac/stm32l431vc" ] -stm32l432kb = [ "stm32-metapac/stm32l432kb" ] -stm32l432kc = [ "stm32-metapac/stm32l432kc" ] -stm32l433cb = [ "stm32-metapac/stm32l433cb" ] -stm32l433cc = [ "stm32-metapac/stm32l433cc" ] -stm32l433rb = [ "stm32-metapac/stm32l433rb" ] -stm32l433rc = [ "stm32-metapac/stm32l433rc" ] -stm32l433vc = [ "stm32-metapac/stm32l433vc" ] -stm32l442kc = [ "stm32-metapac/stm32l442kc" ] -stm32l443cc = [ "stm32-metapac/stm32l443cc" ] -stm32l443rc = [ "stm32-metapac/stm32l443rc" ] -stm32l443vc = [ "stm32-metapac/stm32l443vc" ] -stm32l451cc = [ "stm32-metapac/stm32l451cc" ] -stm32l451ce = [ "stm32-metapac/stm32l451ce" ] -stm32l451rc = [ "stm32-metapac/stm32l451rc" ] -stm32l451re = [ "stm32-metapac/stm32l451re" ] -stm32l451vc = [ "stm32-metapac/stm32l451vc" ] -stm32l451ve = [ "stm32-metapac/stm32l451ve" ] -stm32l452cc = [ "stm32-metapac/stm32l452cc" ] -stm32l452ce = [ "stm32-metapac/stm32l452ce" ] -stm32l452rc = [ "stm32-metapac/stm32l452rc" ] -stm32l452re = [ "stm32-metapac/stm32l452re" ] -stm32l452vc = [ "stm32-metapac/stm32l452vc" ] -stm32l452ve = [ "stm32-metapac/stm32l452ve" ] -stm32l462ce = [ "stm32-metapac/stm32l462ce" ] -stm32l462re = [ "stm32-metapac/stm32l462re" ] -stm32l462ve = [ "stm32-metapac/stm32l462ve" ] -stm32l471qe = [ "stm32-metapac/stm32l471qe" ] -stm32l471qg = [ "stm32-metapac/stm32l471qg" ] -stm32l471re = [ "stm32-metapac/stm32l471re" ] -stm32l471rg = [ "stm32-metapac/stm32l471rg" ] -stm32l471ve = [ "stm32-metapac/stm32l471ve" ] -stm32l471vg = [ "stm32-metapac/stm32l471vg" ] -stm32l471ze = [ "stm32-metapac/stm32l471ze" ] -stm32l471zg = [ "stm32-metapac/stm32l471zg" ] -stm32l475rc = [ "stm32-metapac/stm32l475rc" ] -stm32l475re = [ "stm32-metapac/stm32l475re" ] -stm32l475rg = [ "stm32-metapac/stm32l475rg" ] -stm32l475vc = [ "stm32-metapac/stm32l475vc" ] -stm32l475ve = [ "stm32-metapac/stm32l475ve" ] -stm32l475vg = [ "stm32-metapac/stm32l475vg" ] -stm32l476je = [ "stm32-metapac/stm32l476je" ] -stm32l476jg = [ "stm32-metapac/stm32l476jg" ] -stm32l476me = [ "stm32-metapac/stm32l476me" ] -stm32l476mg = [ "stm32-metapac/stm32l476mg" ] -stm32l476qe = [ "stm32-metapac/stm32l476qe" ] -stm32l476qg = [ "stm32-metapac/stm32l476qg" ] -stm32l476rc = [ "stm32-metapac/stm32l476rc" ] -stm32l476re = [ "stm32-metapac/stm32l476re" ] -stm32l476rg = [ "stm32-metapac/stm32l476rg" ] -stm32l476vc = [ "stm32-metapac/stm32l476vc" ] -stm32l476ve = [ "stm32-metapac/stm32l476ve" ] -stm32l476vg = [ "stm32-metapac/stm32l476vg" ] -stm32l476ze = [ "stm32-metapac/stm32l476ze" ] -stm32l476zg = [ "stm32-metapac/stm32l476zg" ] -stm32l486jg = [ "stm32-metapac/stm32l486jg" ] -stm32l486qg = [ "stm32-metapac/stm32l486qg" ] -stm32l486rg = [ "stm32-metapac/stm32l486rg" ] -stm32l486vg = [ "stm32-metapac/stm32l486vg" ] -stm32l486zg = [ "stm32-metapac/stm32l486zg" ] -stm32l496ae = [ "stm32-metapac/stm32l496ae" ] -stm32l496ag = [ "stm32-metapac/stm32l496ag" ] -stm32l496qe = [ "stm32-metapac/stm32l496qe" ] -stm32l496qg = [ "stm32-metapac/stm32l496qg" ] -stm32l496re = [ "stm32-metapac/stm32l496re" ] -stm32l496rg = [ "stm32-metapac/stm32l496rg" ] -stm32l496ve = [ "stm32-metapac/stm32l496ve" ] -stm32l496vg = [ "stm32-metapac/stm32l496vg" ] -stm32l496wg = [ "stm32-metapac/stm32l496wg" ] -stm32l496ze = [ "stm32-metapac/stm32l496ze" ] -stm32l496zg = [ "stm32-metapac/stm32l496zg" ] -stm32l4a6ag = [ "stm32-metapac/stm32l4a6ag" ] -stm32l4a6qg = [ "stm32-metapac/stm32l4a6qg" ] -stm32l4a6rg = [ "stm32-metapac/stm32l4a6rg" ] -stm32l4a6vg = [ "stm32-metapac/stm32l4a6vg" ] -stm32l4a6zg = [ "stm32-metapac/stm32l4a6zg" ] -stm32l4p5ae = [ "stm32-metapac/stm32l4p5ae" ] -stm32l4p5ag = [ "stm32-metapac/stm32l4p5ag" ] -stm32l4p5ce = [ "stm32-metapac/stm32l4p5ce" ] -stm32l4p5cg = [ "stm32-metapac/stm32l4p5cg" ] -stm32l4p5qe = [ "stm32-metapac/stm32l4p5qe" ] -stm32l4p5qg = [ "stm32-metapac/stm32l4p5qg" ] -stm32l4p5re = [ "stm32-metapac/stm32l4p5re" ] -stm32l4p5rg = [ "stm32-metapac/stm32l4p5rg" ] -stm32l4p5ve = [ "stm32-metapac/stm32l4p5ve" ] -stm32l4p5vg = [ "stm32-metapac/stm32l4p5vg" ] -stm32l4p5ze = [ "stm32-metapac/stm32l4p5ze" ] -stm32l4p5zg = [ "stm32-metapac/stm32l4p5zg" ] -stm32l4q5ag = [ "stm32-metapac/stm32l4q5ag" ] -stm32l4q5cg = [ "stm32-metapac/stm32l4q5cg" ] -stm32l4q5qg = [ "stm32-metapac/stm32l4q5qg" ] -stm32l4q5rg = [ "stm32-metapac/stm32l4q5rg" ] -stm32l4q5vg = [ "stm32-metapac/stm32l4q5vg" ] -stm32l4q5zg = [ "stm32-metapac/stm32l4q5zg" ] -stm32l4r5ag = [ "stm32-metapac/stm32l4r5ag" ] -stm32l4r5ai = [ "stm32-metapac/stm32l4r5ai" ] -stm32l4r5qg = [ "stm32-metapac/stm32l4r5qg" ] -stm32l4r5qi = [ "stm32-metapac/stm32l4r5qi" ] -stm32l4r5vg = [ "stm32-metapac/stm32l4r5vg" ] -stm32l4r5vi = [ "stm32-metapac/stm32l4r5vi" ] -stm32l4r5zg = [ "stm32-metapac/stm32l4r5zg" ] -stm32l4r5zi = [ "stm32-metapac/stm32l4r5zi" ] -stm32l4r7ai = [ "stm32-metapac/stm32l4r7ai" ] -stm32l4r7vi = [ "stm32-metapac/stm32l4r7vi" ] -stm32l4r7zi = [ "stm32-metapac/stm32l4r7zi" ] -stm32l4r9ag = [ "stm32-metapac/stm32l4r9ag" ] -stm32l4r9ai = [ "stm32-metapac/stm32l4r9ai" ] -stm32l4r9vg = [ "stm32-metapac/stm32l4r9vg" ] -stm32l4r9vi = [ "stm32-metapac/stm32l4r9vi" ] -stm32l4r9zg = [ "stm32-metapac/stm32l4r9zg" ] -stm32l4r9zi = [ "stm32-metapac/stm32l4r9zi" ] -stm32l4s5ai = [ "stm32-metapac/stm32l4s5ai" ] -stm32l4s5qi = [ "stm32-metapac/stm32l4s5qi" ] -stm32l4s5vi = [ "stm32-metapac/stm32l4s5vi" ] -stm32l4s5zi = [ "stm32-metapac/stm32l4s5zi" ] -stm32l4s7ai = [ "stm32-metapac/stm32l4s7ai" ] -stm32l4s7vi = [ "stm32-metapac/stm32l4s7vi" ] -stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ] -stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] -stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] -stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] -stm32l552cc = [ "stm32-metapac/stm32l552cc" ] -stm32l552ce = [ "stm32-metapac/stm32l552ce" ] -stm32l552me = [ "stm32-metapac/stm32l552me" ] -stm32l552qc = [ "stm32-metapac/stm32l552qc" ] -stm32l552qe = [ "stm32-metapac/stm32l552qe" ] -stm32l552rc = [ "stm32-metapac/stm32l552rc" ] -stm32l552re = [ "stm32-metapac/stm32l552re" ] -stm32l552vc = [ "stm32-metapac/stm32l552vc" ] -stm32l552ve = [ "stm32-metapac/stm32l552ve" ] -stm32l552zc = [ "stm32-metapac/stm32l552zc" ] -stm32l552ze = [ "stm32-metapac/stm32l552ze" ] -stm32l562ce = [ "stm32-metapac/stm32l562ce" ] -stm32l562me = [ "stm32-metapac/stm32l562me" ] -stm32l562qe = [ "stm32-metapac/stm32l562qe" ] -stm32l562re = [ "stm32-metapac/stm32l562re" ] -stm32l562ve = [ "stm32-metapac/stm32l562ve" ] -stm32l562ze = [ "stm32-metapac/stm32l562ze" ] -stm32u535cb = [ "stm32-metapac/stm32u535cb" ] -stm32u535cc = [ "stm32-metapac/stm32u535cc" ] -stm32u535ce = [ "stm32-metapac/stm32u535ce" ] -stm32u535je = [ "stm32-metapac/stm32u535je" ] -stm32u535nc = [ "stm32-metapac/stm32u535nc" ] -stm32u535ne = [ "stm32-metapac/stm32u535ne" ] -stm32u535rb = [ "stm32-metapac/stm32u535rb" ] -stm32u535rc = [ "stm32-metapac/stm32u535rc" ] -stm32u535re = [ "stm32-metapac/stm32u535re" ] -stm32u535vc = [ "stm32-metapac/stm32u535vc" ] -stm32u535ve = [ "stm32-metapac/stm32u535ve" ] -stm32u545ce = [ "stm32-metapac/stm32u545ce" ] -stm32u545je = [ "stm32-metapac/stm32u545je" ] -stm32u545ne = [ "stm32-metapac/stm32u545ne" ] -stm32u545re = [ "stm32-metapac/stm32u545re" ] -stm32u545ve = [ "stm32-metapac/stm32u545ve" ] -stm32u575ag = [ "stm32-metapac/stm32u575ag" ] -stm32u575ai = [ "stm32-metapac/stm32u575ai" ] -stm32u575cg = [ "stm32-metapac/stm32u575cg" ] -stm32u575ci = [ "stm32-metapac/stm32u575ci" ] -stm32u575og = [ "stm32-metapac/stm32u575og" ] -stm32u575oi = [ "stm32-metapac/stm32u575oi" ] -stm32u575qg = [ "stm32-metapac/stm32u575qg" ] -stm32u575qi = [ "stm32-metapac/stm32u575qi" ] -stm32u575rg = [ "stm32-metapac/stm32u575rg" ] -stm32u575ri = [ "stm32-metapac/stm32u575ri" ] -stm32u575vg = [ "stm32-metapac/stm32u575vg" ] -stm32u575vi = [ "stm32-metapac/stm32u575vi" ] -stm32u575zg = [ "stm32-metapac/stm32u575zg" ] -stm32u575zi = [ "stm32-metapac/stm32u575zi" ] -stm32u585ai = [ "stm32-metapac/stm32u585ai" ] -stm32u585ci = [ "stm32-metapac/stm32u585ci" ] -stm32u585oi = [ "stm32-metapac/stm32u585oi" ] -stm32u585qi = [ "stm32-metapac/stm32u585qi" ] -stm32u585ri = [ "stm32-metapac/stm32u585ri" ] -stm32u585vi = [ "stm32-metapac/stm32u585vi" ] -stm32u585zi = [ "stm32-metapac/stm32u585zi" ] -stm32u595ai = [ "stm32-metapac/stm32u595ai" ] -stm32u595aj = [ "stm32-metapac/stm32u595aj" ] -stm32u595qi = [ "stm32-metapac/stm32u595qi" ] -stm32u595qj = [ "stm32-metapac/stm32u595qj" ] -stm32u595ri = [ "stm32-metapac/stm32u595ri" ] -stm32u595rj = [ "stm32-metapac/stm32u595rj" ] -stm32u595vi = [ "stm32-metapac/stm32u595vi" ] -stm32u595vj = [ "stm32-metapac/stm32u595vj" ] -stm32u595zi = [ "stm32-metapac/stm32u595zi" ] -stm32u595zj = [ "stm32-metapac/stm32u595zj" ] -stm32u599bj = [ "stm32-metapac/stm32u599bj" ] -stm32u599ni = [ "stm32-metapac/stm32u599ni" ] -stm32u599nj = [ "stm32-metapac/stm32u599nj" ] -stm32u599vi = [ "stm32-metapac/stm32u599vi" ] -stm32u599vj = [ "stm32-metapac/stm32u599vj" ] -stm32u599zi = [ "stm32-metapac/stm32u599zi" ] -stm32u599zj = [ "stm32-metapac/stm32u599zj" ] -stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] -stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] -stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] -stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] -stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] -stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] -stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] -stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] -stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] -stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] -stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] -stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] -stm32wb35cc = [ "stm32-metapac/stm32wb35cc" ] -stm32wb35ce = [ "stm32-metapac/stm32wb35ce" ] -stm32wb50cg = [ "stm32-metapac/stm32wb50cg" ] -stm32wb55cc = [ "stm32-metapac/stm32wb55cc" ] -stm32wb55ce = [ "stm32-metapac/stm32wb55ce" ] -stm32wb55cg = [ "stm32-metapac/stm32wb55cg" ] -stm32wb55rc = [ "stm32-metapac/stm32wb55rc" ] -stm32wb55re = [ "stm32-metapac/stm32wb55re" ] -stm32wb55rg = [ "stm32-metapac/stm32wb55rg" ] -stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ] -stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ] -stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ] -stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ] -stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ] -stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ] -stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ] -stm32wl54jc-cm0p = [ "stm32-metapac/stm32wl54jc-cm0p" ] -stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4" ] -stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p" ] -stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4" ] -stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p" ] -stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] -stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] -stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] -stm32wle4j8 = [ "stm32-metapac/stm32wle4j8" ] -stm32wle4jb = [ "stm32-metapac/stm32wle4jb" ] -stm32wle4jc = [ "stm32-metapac/stm32wle4jc" ] -stm32wle5c8 = [ "stm32-metapac/stm32wle5c8" ] -stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] -stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] -stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] -stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] -stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] +stm32c011d6 = ["stm32-metapac/stm32c011d6"] +stm32c011f4 = ["stm32-metapac/stm32c011f4"] +stm32c011f6 = ["stm32-metapac/stm32c011f6"] +stm32c011j4 = ["stm32-metapac/stm32c011j4"] +stm32c011j6 = ["stm32-metapac/stm32c011j6"] +stm32c031c4 = ["stm32-metapac/stm32c031c4"] +stm32c031c6 = ["stm32-metapac/stm32c031c6"] +stm32c031f4 = ["stm32-metapac/stm32c031f4"] +stm32c031f6 = ["stm32-metapac/stm32c031f6"] +stm32c031g4 = ["stm32-metapac/stm32c031g4"] +stm32c031g6 = ["stm32-metapac/stm32c031g6"] +stm32c031k4 = ["stm32-metapac/stm32c031k4"] +stm32c031k6 = ["stm32-metapac/stm32c031k6"] +stm32f030c6 = ["stm32-metapac/stm32f030c6"] +stm32f030c8 = ["stm32-metapac/stm32f030c8"] +stm32f030cc = ["stm32-metapac/stm32f030cc"] +stm32f030f4 = ["stm32-metapac/stm32f030f4"] +stm32f030k6 = ["stm32-metapac/stm32f030k6"] +stm32f030r8 = ["stm32-metapac/stm32f030r8"] +stm32f030rc = ["stm32-metapac/stm32f030rc"] +stm32f031c4 = ["stm32-metapac/stm32f031c4"] +stm32f031c6 = ["stm32-metapac/stm32f031c6"] +stm32f031e6 = ["stm32-metapac/stm32f031e6"] +stm32f031f4 = ["stm32-metapac/stm32f031f4"] +stm32f031f6 = ["stm32-metapac/stm32f031f6"] +stm32f031g4 = ["stm32-metapac/stm32f031g4"] +stm32f031g6 = ["stm32-metapac/stm32f031g6"] +stm32f031k4 = ["stm32-metapac/stm32f031k4"] +stm32f031k6 = ["stm32-metapac/stm32f031k6"] +stm32f038c6 = ["stm32-metapac/stm32f038c6"] +stm32f038e6 = ["stm32-metapac/stm32f038e6"] +stm32f038f6 = ["stm32-metapac/stm32f038f6"] +stm32f038g6 = ["stm32-metapac/stm32f038g6"] +stm32f038k6 = ["stm32-metapac/stm32f038k6"] +stm32f042c4 = ["stm32-metapac/stm32f042c4"] +stm32f042c6 = ["stm32-metapac/stm32f042c6"] +stm32f042f4 = ["stm32-metapac/stm32f042f4"] +stm32f042f6 = ["stm32-metapac/stm32f042f6"] +stm32f042g4 = ["stm32-metapac/stm32f042g4"] +stm32f042g6 = ["stm32-metapac/stm32f042g6"] +stm32f042k4 = ["stm32-metapac/stm32f042k4"] +stm32f042k6 = ["stm32-metapac/stm32f042k6"] +stm32f042t6 = ["stm32-metapac/stm32f042t6"] +stm32f048c6 = ["stm32-metapac/stm32f048c6"] +stm32f048g6 = ["stm32-metapac/stm32f048g6"] +stm32f048t6 = ["stm32-metapac/stm32f048t6"] +stm32f051c4 = ["stm32-metapac/stm32f051c4"] +stm32f051c6 = ["stm32-metapac/stm32f051c6"] +stm32f051c8 = ["stm32-metapac/stm32f051c8"] +stm32f051k4 = ["stm32-metapac/stm32f051k4"] +stm32f051k6 = ["stm32-metapac/stm32f051k6"] +stm32f051k8 = ["stm32-metapac/stm32f051k8"] +stm32f051r4 = ["stm32-metapac/stm32f051r4"] +stm32f051r6 = ["stm32-metapac/stm32f051r6"] +stm32f051r8 = ["stm32-metapac/stm32f051r8"] +stm32f051t8 = ["stm32-metapac/stm32f051t8"] +stm32f058c8 = ["stm32-metapac/stm32f058c8"] +stm32f058r8 = ["stm32-metapac/stm32f058r8"] +stm32f058t8 = ["stm32-metapac/stm32f058t8"] +stm32f070c6 = ["stm32-metapac/stm32f070c6"] +stm32f070cb = ["stm32-metapac/stm32f070cb"] +stm32f070f6 = ["stm32-metapac/stm32f070f6"] +stm32f070rb = ["stm32-metapac/stm32f070rb"] +stm32f071c8 = ["stm32-metapac/stm32f071c8"] +stm32f071cb = ["stm32-metapac/stm32f071cb"] +stm32f071rb = ["stm32-metapac/stm32f071rb"] +stm32f071v8 = ["stm32-metapac/stm32f071v8"] +stm32f071vb = ["stm32-metapac/stm32f071vb"] +stm32f072c8 = ["stm32-metapac/stm32f072c8"] +stm32f072cb = ["stm32-metapac/stm32f072cb"] +stm32f072r8 = ["stm32-metapac/stm32f072r8"] +stm32f072rb = ["stm32-metapac/stm32f072rb"] +stm32f072v8 = ["stm32-metapac/stm32f072v8"] +stm32f072vb = ["stm32-metapac/stm32f072vb"] +stm32f078cb = ["stm32-metapac/stm32f078cb"] +stm32f078rb = ["stm32-metapac/stm32f078rb"] +stm32f078vb = ["stm32-metapac/stm32f078vb"] +stm32f091cb = ["stm32-metapac/stm32f091cb"] +stm32f091cc = ["stm32-metapac/stm32f091cc"] +stm32f091rb = ["stm32-metapac/stm32f091rb"] +stm32f091rc = ["stm32-metapac/stm32f091rc"] +stm32f091vb = ["stm32-metapac/stm32f091vb"] +stm32f091vc = ["stm32-metapac/stm32f091vc"] +stm32f098cc = ["stm32-metapac/stm32f098cc"] +stm32f098rc = ["stm32-metapac/stm32f098rc"] +stm32f098vc = ["stm32-metapac/stm32f098vc"] +stm32f100c4 = ["stm32-metapac/stm32f100c4"] +stm32f100c6 = ["stm32-metapac/stm32f100c6"] +stm32f100c8 = ["stm32-metapac/stm32f100c8"] +stm32f100cb = ["stm32-metapac/stm32f100cb"] +stm32f100r4 = ["stm32-metapac/stm32f100r4"] +stm32f100r6 = ["stm32-metapac/stm32f100r6"] +stm32f100r8 = ["stm32-metapac/stm32f100r8"] +stm32f100rb = ["stm32-metapac/stm32f100rb"] +stm32f100rc = ["stm32-metapac/stm32f100rc"] +stm32f100rd = ["stm32-metapac/stm32f100rd"] +stm32f100re = ["stm32-metapac/stm32f100re"] +stm32f100v8 = ["stm32-metapac/stm32f100v8"] +stm32f100vb = ["stm32-metapac/stm32f100vb"] +stm32f100vc = ["stm32-metapac/stm32f100vc"] +stm32f100vd = ["stm32-metapac/stm32f100vd"] +stm32f100ve = ["stm32-metapac/stm32f100ve"] +stm32f100zc = ["stm32-metapac/stm32f100zc"] +stm32f100zd = ["stm32-metapac/stm32f100zd"] +stm32f100ze = ["stm32-metapac/stm32f100ze"] +stm32f101c4 = ["stm32-metapac/stm32f101c4"] +stm32f101c6 = ["stm32-metapac/stm32f101c6"] +stm32f101c8 = ["stm32-metapac/stm32f101c8"] +stm32f101cb = ["stm32-metapac/stm32f101cb"] +stm32f101r4 = ["stm32-metapac/stm32f101r4"] +stm32f101r6 = ["stm32-metapac/stm32f101r6"] +stm32f101r8 = ["stm32-metapac/stm32f101r8"] +stm32f101rb = ["stm32-metapac/stm32f101rb"] +stm32f101rc = ["stm32-metapac/stm32f101rc"] +stm32f101rd = ["stm32-metapac/stm32f101rd"] +stm32f101re = ["stm32-metapac/stm32f101re"] +stm32f101rf = ["stm32-metapac/stm32f101rf"] +stm32f101rg = ["stm32-metapac/stm32f101rg"] +stm32f101t4 = ["stm32-metapac/stm32f101t4"] +stm32f101t6 = ["stm32-metapac/stm32f101t6"] +stm32f101t8 = ["stm32-metapac/stm32f101t8"] +stm32f101tb = ["stm32-metapac/stm32f101tb"] +stm32f101v8 = ["stm32-metapac/stm32f101v8"] +stm32f101vb = ["stm32-metapac/stm32f101vb"] +stm32f101vc = ["stm32-metapac/stm32f101vc"] +stm32f101vd = ["stm32-metapac/stm32f101vd"] +stm32f101ve = ["stm32-metapac/stm32f101ve"] +stm32f101vf = ["stm32-metapac/stm32f101vf"] +stm32f101vg = ["stm32-metapac/stm32f101vg"] +stm32f101zc = ["stm32-metapac/stm32f101zc"] +stm32f101zd = ["stm32-metapac/stm32f101zd"] +stm32f101ze = ["stm32-metapac/stm32f101ze"] +stm32f101zf = ["stm32-metapac/stm32f101zf"] +stm32f101zg = ["stm32-metapac/stm32f101zg"] +stm32f102c4 = ["stm32-metapac/stm32f102c4"] +stm32f102c6 = ["stm32-metapac/stm32f102c6"] +stm32f102c8 = ["stm32-metapac/stm32f102c8"] +stm32f102cb = ["stm32-metapac/stm32f102cb"] +stm32f102r4 = ["stm32-metapac/stm32f102r4"] +stm32f102r6 = ["stm32-metapac/stm32f102r6"] +stm32f102r8 = ["stm32-metapac/stm32f102r8"] +stm32f102rb = ["stm32-metapac/stm32f102rb"] +stm32f103c4 = ["stm32-metapac/stm32f103c4"] +stm32f103c6 = ["stm32-metapac/stm32f103c6"] +stm32f103c8 = ["stm32-metapac/stm32f103c8"] +stm32f103cb = ["stm32-metapac/stm32f103cb"] +stm32f103r4 = ["stm32-metapac/stm32f103r4"] +stm32f103r6 = ["stm32-metapac/stm32f103r6"] +stm32f103r8 = ["stm32-metapac/stm32f103r8"] +stm32f103rb = ["stm32-metapac/stm32f103rb"] +stm32f103rc = ["stm32-metapac/stm32f103rc"] +stm32f103rd = ["stm32-metapac/stm32f103rd"] +stm32f103re = ["stm32-metapac/stm32f103re"] +stm32f103rf = ["stm32-metapac/stm32f103rf"] +stm32f103rg = ["stm32-metapac/stm32f103rg"] +stm32f103t4 = ["stm32-metapac/stm32f103t4"] +stm32f103t6 = ["stm32-metapac/stm32f103t6"] +stm32f103t8 = ["stm32-metapac/stm32f103t8"] +stm32f103tb = ["stm32-metapac/stm32f103tb"] +stm32f103v8 = ["stm32-metapac/stm32f103v8"] +stm32f103vb = ["stm32-metapac/stm32f103vb"] +stm32f103vc = ["stm32-metapac/stm32f103vc"] +stm32f103vd = ["stm32-metapac/stm32f103vd"] +stm32f103ve = ["stm32-metapac/stm32f103ve"] +stm32f103vf = ["stm32-metapac/stm32f103vf"] +stm32f103vg = ["stm32-metapac/stm32f103vg"] +stm32f103zc = ["stm32-metapac/stm32f103zc"] +stm32f103zd = ["stm32-metapac/stm32f103zd"] +stm32f103ze = ["stm32-metapac/stm32f103ze"] +stm32f103zf = ["stm32-metapac/stm32f103zf"] +stm32f103zg = ["stm32-metapac/stm32f103zg"] +stm32f105r8 = ["stm32-metapac/stm32f105r8"] +stm32f105rb = ["stm32-metapac/stm32f105rb"] +stm32f105rc = ["stm32-metapac/stm32f105rc"] +stm32f105v8 = ["stm32-metapac/stm32f105v8"] +stm32f105vb = ["stm32-metapac/stm32f105vb"] +stm32f105vc = ["stm32-metapac/stm32f105vc"] +stm32f107rb = ["stm32-metapac/stm32f107rb"] +stm32f107rc = ["stm32-metapac/stm32f107rc"] +stm32f107vb = ["stm32-metapac/stm32f107vb"] +stm32f107vc = ["stm32-metapac/stm32f107vc"] +stm32f205rb = ["stm32-metapac/stm32f205rb"] +stm32f205rc = ["stm32-metapac/stm32f205rc"] +stm32f205re = ["stm32-metapac/stm32f205re"] +stm32f205rf = ["stm32-metapac/stm32f205rf"] +stm32f205rg = ["stm32-metapac/stm32f205rg"] +stm32f205vb = ["stm32-metapac/stm32f205vb"] +stm32f205vc = ["stm32-metapac/stm32f205vc"] +stm32f205ve = ["stm32-metapac/stm32f205ve"] +stm32f205vf = ["stm32-metapac/stm32f205vf"] +stm32f205vg = ["stm32-metapac/stm32f205vg"] +stm32f205zc = ["stm32-metapac/stm32f205zc"] +stm32f205ze = ["stm32-metapac/stm32f205ze"] +stm32f205zf = ["stm32-metapac/stm32f205zf"] +stm32f205zg = ["stm32-metapac/stm32f205zg"] +stm32f207ic = ["stm32-metapac/stm32f207ic"] +stm32f207ie = ["stm32-metapac/stm32f207ie"] +stm32f207if = ["stm32-metapac/stm32f207if"] +stm32f207ig = ["stm32-metapac/stm32f207ig"] +stm32f207vc = ["stm32-metapac/stm32f207vc"] +stm32f207ve = ["stm32-metapac/stm32f207ve"] +stm32f207vf = ["stm32-metapac/stm32f207vf"] +stm32f207vg = ["stm32-metapac/stm32f207vg"] +stm32f207zc = ["stm32-metapac/stm32f207zc"] +stm32f207ze = ["stm32-metapac/stm32f207ze"] +stm32f207zf = ["stm32-metapac/stm32f207zf"] +stm32f207zg = ["stm32-metapac/stm32f207zg"] +stm32f215re = ["stm32-metapac/stm32f215re"] +stm32f215rg = ["stm32-metapac/stm32f215rg"] +stm32f215ve = ["stm32-metapac/stm32f215ve"] +stm32f215vg = ["stm32-metapac/stm32f215vg"] +stm32f215ze = ["stm32-metapac/stm32f215ze"] +stm32f215zg = ["stm32-metapac/stm32f215zg"] +stm32f217ie = ["stm32-metapac/stm32f217ie"] +stm32f217ig = ["stm32-metapac/stm32f217ig"] +stm32f217ve = ["stm32-metapac/stm32f217ve"] +stm32f217vg = ["stm32-metapac/stm32f217vg"] +stm32f217ze = ["stm32-metapac/stm32f217ze"] +stm32f217zg = ["stm32-metapac/stm32f217zg"] +stm32f301c6 = ["stm32-metapac/stm32f301c6"] +stm32f301c8 = ["stm32-metapac/stm32f301c8"] +stm32f301k6 = ["stm32-metapac/stm32f301k6"] +stm32f301k8 = ["stm32-metapac/stm32f301k8"] +stm32f301r6 = ["stm32-metapac/stm32f301r6"] +stm32f301r8 = ["stm32-metapac/stm32f301r8"] +stm32f302c6 = ["stm32-metapac/stm32f302c6"] +stm32f302c8 = ["stm32-metapac/stm32f302c8"] +stm32f302cb = ["stm32-metapac/stm32f302cb"] +stm32f302cc = ["stm32-metapac/stm32f302cc"] +stm32f302k6 = ["stm32-metapac/stm32f302k6"] +stm32f302k8 = ["stm32-metapac/stm32f302k8"] +stm32f302r6 = ["stm32-metapac/stm32f302r6"] +stm32f302r8 = ["stm32-metapac/stm32f302r8"] +stm32f302rb = ["stm32-metapac/stm32f302rb"] +stm32f302rc = ["stm32-metapac/stm32f302rc"] +stm32f302rd = ["stm32-metapac/stm32f302rd"] +stm32f302re = ["stm32-metapac/stm32f302re"] +stm32f302vb = ["stm32-metapac/stm32f302vb"] +stm32f302vc = ["stm32-metapac/stm32f302vc"] +stm32f302vd = ["stm32-metapac/stm32f302vd"] +stm32f302ve = ["stm32-metapac/stm32f302ve"] +stm32f302zd = ["stm32-metapac/stm32f302zd"] +stm32f302ze = ["stm32-metapac/stm32f302ze"] +stm32f303c6 = ["stm32-metapac/stm32f303c6"] +stm32f303c8 = ["stm32-metapac/stm32f303c8"] +stm32f303cb = ["stm32-metapac/stm32f303cb"] +stm32f303cc = ["stm32-metapac/stm32f303cc"] +stm32f303k6 = ["stm32-metapac/stm32f303k6"] +stm32f303k8 = ["stm32-metapac/stm32f303k8"] +stm32f303r6 = ["stm32-metapac/stm32f303r6"] +stm32f303r8 = ["stm32-metapac/stm32f303r8"] +stm32f303rb = ["stm32-metapac/stm32f303rb"] +stm32f303rc = ["stm32-metapac/stm32f303rc"] +stm32f303rd = ["stm32-metapac/stm32f303rd"] +stm32f303re = ["stm32-metapac/stm32f303re"] +stm32f303vb = ["stm32-metapac/stm32f303vb"] +stm32f303vc = ["stm32-metapac/stm32f303vc"] +stm32f303vd = ["stm32-metapac/stm32f303vd"] +stm32f303ve = ["stm32-metapac/stm32f303ve"] +stm32f303zd = ["stm32-metapac/stm32f303zd"] +stm32f303ze = ["stm32-metapac/stm32f303ze"] +stm32f318c8 = ["stm32-metapac/stm32f318c8"] +stm32f318k8 = ["stm32-metapac/stm32f318k8"] +stm32f328c8 = ["stm32-metapac/stm32f328c8"] +stm32f334c4 = ["stm32-metapac/stm32f334c4"] +stm32f334c6 = ["stm32-metapac/stm32f334c6"] +stm32f334c8 = ["stm32-metapac/stm32f334c8"] +stm32f334k4 = ["stm32-metapac/stm32f334k4"] +stm32f334k6 = ["stm32-metapac/stm32f334k6"] +stm32f334k8 = ["stm32-metapac/stm32f334k8"] +stm32f334r6 = ["stm32-metapac/stm32f334r6"] +stm32f334r8 = ["stm32-metapac/stm32f334r8"] +stm32f358cc = ["stm32-metapac/stm32f358cc"] +stm32f358rc = ["stm32-metapac/stm32f358rc"] +stm32f358vc = ["stm32-metapac/stm32f358vc"] +stm32f373c8 = ["stm32-metapac/stm32f373c8"] +stm32f373cb = ["stm32-metapac/stm32f373cb"] +stm32f373cc = ["stm32-metapac/stm32f373cc"] +stm32f373r8 = ["stm32-metapac/stm32f373r8"] +stm32f373rb = ["stm32-metapac/stm32f373rb"] +stm32f373rc = ["stm32-metapac/stm32f373rc"] +stm32f373v8 = ["stm32-metapac/stm32f373v8"] +stm32f373vb = ["stm32-metapac/stm32f373vb"] +stm32f373vc = ["stm32-metapac/stm32f373vc"] +stm32f378cc = ["stm32-metapac/stm32f378cc"] +stm32f378rc = ["stm32-metapac/stm32f378rc"] +stm32f378vc = ["stm32-metapac/stm32f378vc"] +stm32f398ve = ["stm32-metapac/stm32f398ve"] +stm32f401cb = ["stm32-metapac/stm32f401cb"] +stm32f401cc = ["stm32-metapac/stm32f401cc"] +stm32f401cd = ["stm32-metapac/stm32f401cd"] +stm32f401ce = ["stm32-metapac/stm32f401ce"] +stm32f401rb = ["stm32-metapac/stm32f401rb"] +stm32f401rc = ["stm32-metapac/stm32f401rc"] +stm32f401rd = ["stm32-metapac/stm32f401rd"] +stm32f401re = ["stm32-metapac/stm32f401re"] +stm32f401vb = ["stm32-metapac/stm32f401vb"] +stm32f401vc = ["stm32-metapac/stm32f401vc"] +stm32f401vd = ["stm32-metapac/stm32f401vd"] +stm32f401ve = ["stm32-metapac/stm32f401ve"] +stm32f405oe = ["stm32-metapac/stm32f405oe"] +stm32f405og = ["stm32-metapac/stm32f405og"] +stm32f405rg = ["stm32-metapac/stm32f405rg"] +stm32f405vg = ["stm32-metapac/stm32f405vg"] +stm32f405zg = ["stm32-metapac/stm32f405zg"] +stm32f407ie = ["stm32-metapac/stm32f407ie"] +stm32f407ig = ["stm32-metapac/stm32f407ig"] +stm32f407ve = ["stm32-metapac/stm32f407ve"] +stm32f407vg = ["stm32-metapac/stm32f407vg"] +stm32f407ze = ["stm32-metapac/stm32f407ze"] +stm32f407zg = ["stm32-metapac/stm32f407zg"] +stm32f410c8 = ["stm32-metapac/stm32f410c8"] +stm32f410cb = ["stm32-metapac/stm32f410cb"] +stm32f410r8 = ["stm32-metapac/stm32f410r8"] +stm32f410rb = ["stm32-metapac/stm32f410rb"] +stm32f410t8 = ["stm32-metapac/stm32f410t8"] +stm32f410tb = ["stm32-metapac/stm32f410tb"] +stm32f411cc = ["stm32-metapac/stm32f411cc"] +stm32f411ce = ["stm32-metapac/stm32f411ce"] +stm32f411rc = ["stm32-metapac/stm32f411rc"] +stm32f411re = ["stm32-metapac/stm32f411re"] +stm32f411vc = ["stm32-metapac/stm32f411vc"] +stm32f411ve = ["stm32-metapac/stm32f411ve"] +stm32f412ce = ["stm32-metapac/stm32f412ce"] +stm32f412cg = ["stm32-metapac/stm32f412cg"] +stm32f412re = ["stm32-metapac/stm32f412re"] +stm32f412rg = ["stm32-metapac/stm32f412rg"] +stm32f412ve = ["stm32-metapac/stm32f412ve"] +stm32f412vg = ["stm32-metapac/stm32f412vg"] +stm32f412ze = ["stm32-metapac/stm32f412ze"] +stm32f412zg = ["stm32-metapac/stm32f412zg"] +stm32f413cg = ["stm32-metapac/stm32f413cg"] +stm32f413ch = ["stm32-metapac/stm32f413ch"] +stm32f413mg = ["stm32-metapac/stm32f413mg"] +stm32f413mh = ["stm32-metapac/stm32f413mh"] +stm32f413rg = ["stm32-metapac/stm32f413rg"] +stm32f413rh = ["stm32-metapac/stm32f413rh"] +stm32f413vg = ["stm32-metapac/stm32f413vg"] +stm32f413vh = ["stm32-metapac/stm32f413vh"] +stm32f413zg = ["stm32-metapac/stm32f413zg"] +stm32f413zh = ["stm32-metapac/stm32f413zh"] +stm32f415og = ["stm32-metapac/stm32f415og"] +stm32f415rg = ["stm32-metapac/stm32f415rg"] +stm32f415vg = ["stm32-metapac/stm32f415vg"] +stm32f415zg = ["stm32-metapac/stm32f415zg"] +stm32f417ie = ["stm32-metapac/stm32f417ie"] +stm32f417ig = ["stm32-metapac/stm32f417ig"] +stm32f417ve = ["stm32-metapac/stm32f417ve"] +stm32f417vg = ["stm32-metapac/stm32f417vg"] +stm32f417ze = ["stm32-metapac/stm32f417ze"] +stm32f417zg = ["stm32-metapac/stm32f417zg"] +stm32f423ch = ["stm32-metapac/stm32f423ch"] +stm32f423mh = ["stm32-metapac/stm32f423mh"] +stm32f423rh = ["stm32-metapac/stm32f423rh"] +stm32f423vh = ["stm32-metapac/stm32f423vh"] +stm32f423zh = ["stm32-metapac/stm32f423zh"] +stm32f427ag = ["stm32-metapac/stm32f427ag"] +stm32f427ai = ["stm32-metapac/stm32f427ai"] +stm32f427ig = ["stm32-metapac/stm32f427ig"] +stm32f427ii = ["stm32-metapac/stm32f427ii"] +stm32f427vg = ["stm32-metapac/stm32f427vg"] +stm32f427vi = ["stm32-metapac/stm32f427vi"] +stm32f427zg = ["stm32-metapac/stm32f427zg"] +stm32f427zi = ["stm32-metapac/stm32f427zi"] +stm32f429ag = ["stm32-metapac/stm32f429ag"] +stm32f429ai = ["stm32-metapac/stm32f429ai"] +stm32f429be = ["stm32-metapac/stm32f429be"] +stm32f429bg = ["stm32-metapac/stm32f429bg"] +stm32f429bi = ["stm32-metapac/stm32f429bi"] +stm32f429ie = ["stm32-metapac/stm32f429ie"] +stm32f429ig = ["stm32-metapac/stm32f429ig"] +stm32f429ii = ["stm32-metapac/stm32f429ii"] +stm32f429ne = ["stm32-metapac/stm32f429ne"] +stm32f429ng = ["stm32-metapac/stm32f429ng"] +stm32f429ni = ["stm32-metapac/stm32f429ni"] +stm32f429ve = ["stm32-metapac/stm32f429ve"] +stm32f429vg = ["stm32-metapac/stm32f429vg"] +stm32f429vi = ["stm32-metapac/stm32f429vi"] +stm32f429ze = ["stm32-metapac/stm32f429ze"] +stm32f429zg = ["stm32-metapac/stm32f429zg"] +stm32f429zi = ["stm32-metapac/stm32f429zi"] +stm32f437ai = ["stm32-metapac/stm32f437ai"] +stm32f437ig = ["stm32-metapac/stm32f437ig"] +stm32f437ii = ["stm32-metapac/stm32f437ii"] +stm32f437vg = ["stm32-metapac/stm32f437vg"] +stm32f437vi = ["stm32-metapac/stm32f437vi"] +stm32f437zg = ["stm32-metapac/stm32f437zg"] +stm32f437zi = ["stm32-metapac/stm32f437zi"] +stm32f439ai = ["stm32-metapac/stm32f439ai"] +stm32f439bg = ["stm32-metapac/stm32f439bg"] +stm32f439bi = ["stm32-metapac/stm32f439bi"] +stm32f439ig = ["stm32-metapac/stm32f439ig"] +stm32f439ii = ["stm32-metapac/stm32f439ii"] +stm32f439ng = ["stm32-metapac/stm32f439ng"] +stm32f439ni = ["stm32-metapac/stm32f439ni"] +stm32f439vg = ["stm32-metapac/stm32f439vg"] +stm32f439vi = ["stm32-metapac/stm32f439vi"] +stm32f439zg = ["stm32-metapac/stm32f439zg"] +stm32f439zi = ["stm32-metapac/stm32f439zi"] +stm32f446mc = ["stm32-metapac/stm32f446mc"] +stm32f446me = ["stm32-metapac/stm32f446me"] +stm32f446rc = ["stm32-metapac/stm32f446rc"] +stm32f446re = ["stm32-metapac/stm32f446re"] +stm32f446vc = ["stm32-metapac/stm32f446vc"] +stm32f446ve = ["stm32-metapac/stm32f446ve"] +stm32f446zc = ["stm32-metapac/stm32f446zc"] +stm32f446ze = ["stm32-metapac/stm32f446ze"] +stm32f469ae = ["stm32-metapac/stm32f469ae"] +stm32f469ag = ["stm32-metapac/stm32f469ag"] +stm32f469ai = ["stm32-metapac/stm32f469ai"] +stm32f469be = ["stm32-metapac/stm32f469be"] +stm32f469bg = ["stm32-metapac/stm32f469bg"] +stm32f469bi = ["stm32-metapac/stm32f469bi"] +stm32f469ie = ["stm32-metapac/stm32f469ie"] +stm32f469ig = ["stm32-metapac/stm32f469ig"] +stm32f469ii = ["stm32-metapac/stm32f469ii"] +stm32f469ne = ["stm32-metapac/stm32f469ne"] +stm32f469ng = ["stm32-metapac/stm32f469ng"] +stm32f469ni = ["stm32-metapac/stm32f469ni"] +stm32f469ve = ["stm32-metapac/stm32f469ve"] +stm32f469vg = ["stm32-metapac/stm32f469vg"] +stm32f469vi = ["stm32-metapac/stm32f469vi"] +stm32f469ze = ["stm32-metapac/stm32f469ze"] +stm32f469zg = ["stm32-metapac/stm32f469zg"] +stm32f469zi = ["stm32-metapac/stm32f469zi"] +stm32f479ag = ["stm32-metapac/stm32f479ag"] +stm32f479ai = ["stm32-metapac/stm32f479ai"] +stm32f479bg = ["stm32-metapac/stm32f479bg"] +stm32f479bi = ["stm32-metapac/stm32f479bi"] +stm32f479ig = ["stm32-metapac/stm32f479ig"] +stm32f479ii = ["stm32-metapac/stm32f479ii"] +stm32f479ng = ["stm32-metapac/stm32f479ng"] +stm32f479ni = ["stm32-metapac/stm32f479ni"] +stm32f479vg = ["stm32-metapac/stm32f479vg"] +stm32f479vi = ["stm32-metapac/stm32f479vi"] +stm32f479zg = ["stm32-metapac/stm32f479zg"] +stm32f479zi = ["stm32-metapac/stm32f479zi"] +stm32f722ic = ["stm32-metapac/stm32f722ic"] +stm32f722ie = ["stm32-metapac/stm32f722ie"] +stm32f722rc = ["stm32-metapac/stm32f722rc"] +stm32f722re = ["stm32-metapac/stm32f722re"] +stm32f722vc = ["stm32-metapac/stm32f722vc"] +stm32f722ve = ["stm32-metapac/stm32f722ve"] +stm32f722zc = ["stm32-metapac/stm32f722zc"] +stm32f722ze = ["stm32-metapac/stm32f722ze"] +stm32f723ic = ["stm32-metapac/stm32f723ic"] +stm32f723ie = ["stm32-metapac/stm32f723ie"] +stm32f723vc = ["stm32-metapac/stm32f723vc"] +stm32f723ve = ["stm32-metapac/stm32f723ve"] +stm32f723zc = ["stm32-metapac/stm32f723zc"] +stm32f723ze = ["stm32-metapac/stm32f723ze"] +stm32f730i8 = ["stm32-metapac/stm32f730i8"] +stm32f730r8 = ["stm32-metapac/stm32f730r8"] +stm32f730v8 = ["stm32-metapac/stm32f730v8"] +stm32f730z8 = ["stm32-metapac/stm32f730z8"] +stm32f732ie = ["stm32-metapac/stm32f732ie"] +stm32f732re = ["stm32-metapac/stm32f732re"] +stm32f732ve = ["stm32-metapac/stm32f732ve"] +stm32f732ze = ["stm32-metapac/stm32f732ze"] +stm32f733ie = ["stm32-metapac/stm32f733ie"] +stm32f733ve = ["stm32-metapac/stm32f733ve"] +stm32f733ze = ["stm32-metapac/stm32f733ze"] +stm32f745ie = ["stm32-metapac/stm32f745ie"] +stm32f745ig = ["stm32-metapac/stm32f745ig"] +stm32f745ve = ["stm32-metapac/stm32f745ve"] +stm32f745vg = ["stm32-metapac/stm32f745vg"] +stm32f745ze = ["stm32-metapac/stm32f745ze"] +stm32f745zg = ["stm32-metapac/stm32f745zg"] +stm32f746be = ["stm32-metapac/stm32f746be"] +stm32f746bg = ["stm32-metapac/stm32f746bg"] +stm32f746ie = ["stm32-metapac/stm32f746ie"] +stm32f746ig = ["stm32-metapac/stm32f746ig"] +stm32f746ne = ["stm32-metapac/stm32f746ne"] +stm32f746ng = ["stm32-metapac/stm32f746ng"] +stm32f746ve = ["stm32-metapac/stm32f746ve"] +stm32f746vg = ["stm32-metapac/stm32f746vg"] +stm32f746ze = ["stm32-metapac/stm32f746ze"] +stm32f746zg = ["stm32-metapac/stm32f746zg"] +stm32f750n8 = ["stm32-metapac/stm32f750n8"] +stm32f750v8 = ["stm32-metapac/stm32f750v8"] +stm32f750z8 = ["stm32-metapac/stm32f750z8"] +stm32f756bg = ["stm32-metapac/stm32f756bg"] +stm32f756ig = ["stm32-metapac/stm32f756ig"] +stm32f756ng = ["stm32-metapac/stm32f756ng"] +stm32f756vg = ["stm32-metapac/stm32f756vg"] +stm32f756zg = ["stm32-metapac/stm32f756zg"] +stm32f765bg = ["stm32-metapac/stm32f765bg"] +stm32f765bi = ["stm32-metapac/stm32f765bi"] +stm32f765ig = ["stm32-metapac/stm32f765ig"] +stm32f765ii = ["stm32-metapac/stm32f765ii"] +stm32f765ng = ["stm32-metapac/stm32f765ng"] +stm32f765ni = ["stm32-metapac/stm32f765ni"] +stm32f765vg = ["stm32-metapac/stm32f765vg"] +stm32f765vi = ["stm32-metapac/stm32f765vi"] +stm32f765zg = ["stm32-metapac/stm32f765zg"] +stm32f765zi = ["stm32-metapac/stm32f765zi"] +stm32f767bg = ["stm32-metapac/stm32f767bg"] +stm32f767bi = ["stm32-metapac/stm32f767bi"] +stm32f767ig = ["stm32-metapac/stm32f767ig"] +stm32f767ii = ["stm32-metapac/stm32f767ii"] +stm32f767ng = ["stm32-metapac/stm32f767ng"] +stm32f767ni = ["stm32-metapac/stm32f767ni"] +stm32f767vg = ["stm32-metapac/stm32f767vg"] +stm32f767vi = ["stm32-metapac/stm32f767vi"] +stm32f767zg = ["stm32-metapac/stm32f767zg"] +stm32f767zi = ["stm32-metapac/stm32f767zi"] +stm32f768ai = ["stm32-metapac/stm32f768ai"] +stm32f769ag = ["stm32-metapac/stm32f769ag"] +stm32f769ai = ["stm32-metapac/stm32f769ai"] +stm32f769bg = ["stm32-metapac/stm32f769bg"] +stm32f769bi = ["stm32-metapac/stm32f769bi"] +stm32f769ig = ["stm32-metapac/stm32f769ig"] +stm32f769ii = ["stm32-metapac/stm32f769ii"] +stm32f769ng = ["stm32-metapac/stm32f769ng"] +stm32f769ni = ["stm32-metapac/stm32f769ni"] +stm32f777bi = ["stm32-metapac/stm32f777bi"] +stm32f777ii = ["stm32-metapac/stm32f777ii"] +stm32f777ni = ["stm32-metapac/stm32f777ni"] +stm32f777vi = ["stm32-metapac/stm32f777vi"] +stm32f777zi = ["stm32-metapac/stm32f777zi"] +stm32f778ai = ["stm32-metapac/stm32f778ai"] +stm32f779ai = ["stm32-metapac/stm32f779ai"] +stm32f779bi = ["stm32-metapac/stm32f779bi"] +stm32f779ii = ["stm32-metapac/stm32f779ii"] +stm32f779ni = ["stm32-metapac/stm32f779ni"] +stm32g030c6 = ["stm32-metapac/stm32g030c6"] +stm32g030c8 = ["stm32-metapac/stm32g030c8"] +stm32g030f6 = ["stm32-metapac/stm32g030f6"] +stm32g030j6 = ["stm32-metapac/stm32g030j6"] +stm32g030k6 = ["stm32-metapac/stm32g030k6"] +stm32g030k8 = ["stm32-metapac/stm32g030k8"] +stm32g031c4 = ["stm32-metapac/stm32g031c4"] +stm32g031c6 = ["stm32-metapac/stm32g031c6"] +stm32g031c8 = ["stm32-metapac/stm32g031c8"] +stm32g031f4 = ["stm32-metapac/stm32g031f4"] +stm32g031f6 = ["stm32-metapac/stm32g031f6"] +stm32g031f8 = ["stm32-metapac/stm32g031f8"] +stm32g031g4 = ["stm32-metapac/stm32g031g4"] +stm32g031g6 = ["stm32-metapac/stm32g031g6"] +stm32g031g8 = ["stm32-metapac/stm32g031g8"] +stm32g031j4 = ["stm32-metapac/stm32g031j4"] +stm32g031j6 = ["stm32-metapac/stm32g031j6"] +stm32g031k4 = ["stm32-metapac/stm32g031k4"] +stm32g031k6 = ["stm32-metapac/stm32g031k6"] +stm32g031k8 = ["stm32-metapac/stm32g031k8"] +stm32g031y8 = ["stm32-metapac/stm32g031y8"] +stm32g041c6 = ["stm32-metapac/stm32g041c6"] +stm32g041c8 = ["stm32-metapac/stm32g041c8"] +stm32g041f6 = ["stm32-metapac/stm32g041f6"] +stm32g041f8 = ["stm32-metapac/stm32g041f8"] +stm32g041g6 = ["stm32-metapac/stm32g041g6"] +stm32g041g8 = ["stm32-metapac/stm32g041g8"] +stm32g041j6 = ["stm32-metapac/stm32g041j6"] +stm32g041k6 = ["stm32-metapac/stm32g041k6"] +stm32g041k8 = ["stm32-metapac/stm32g041k8"] +stm32g041y8 = ["stm32-metapac/stm32g041y8"] +stm32g050c6 = ["stm32-metapac/stm32g050c6"] +stm32g050c8 = ["stm32-metapac/stm32g050c8"] +stm32g050f6 = ["stm32-metapac/stm32g050f6"] +stm32g050k6 = ["stm32-metapac/stm32g050k6"] +stm32g050k8 = ["stm32-metapac/stm32g050k8"] +stm32g051c6 = ["stm32-metapac/stm32g051c6"] +stm32g051c8 = ["stm32-metapac/stm32g051c8"] +stm32g051f6 = ["stm32-metapac/stm32g051f6"] +stm32g051f8 = ["stm32-metapac/stm32g051f8"] +stm32g051g6 = ["stm32-metapac/stm32g051g6"] +stm32g051g8 = ["stm32-metapac/stm32g051g8"] +stm32g051k6 = ["stm32-metapac/stm32g051k6"] +stm32g051k8 = ["stm32-metapac/stm32g051k8"] +stm32g061c6 = ["stm32-metapac/stm32g061c6"] +stm32g061c8 = ["stm32-metapac/stm32g061c8"] +stm32g061f6 = ["stm32-metapac/stm32g061f6"] +stm32g061f8 = ["stm32-metapac/stm32g061f8"] +stm32g061g6 = ["stm32-metapac/stm32g061g6"] +stm32g061g8 = ["stm32-metapac/stm32g061g8"] +stm32g061k6 = ["stm32-metapac/stm32g061k6"] +stm32g061k8 = ["stm32-metapac/stm32g061k8"] +stm32g070cb = ["stm32-metapac/stm32g070cb"] +stm32g070kb = ["stm32-metapac/stm32g070kb"] +stm32g070rb = ["stm32-metapac/stm32g070rb"] +stm32g071c6 = ["stm32-metapac/stm32g071c6"] +stm32g071c8 = ["stm32-metapac/stm32g071c8"] +stm32g071cb = ["stm32-metapac/stm32g071cb"] +stm32g071eb = ["stm32-metapac/stm32g071eb"] +stm32g071g6 = ["stm32-metapac/stm32g071g6"] +stm32g071g8 = ["stm32-metapac/stm32g071g8"] +stm32g071gb = ["stm32-metapac/stm32g071gb"] +stm32g071k6 = ["stm32-metapac/stm32g071k6"] +stm32g071k8 = ["stm32-metapac/stm32g071k8"] +stm32g071kb = ["stm32-metapac/stm32g071kb"] +stm32g071r6 = ["stm32-metapac/stm32g071r6"] +stm32g071r8 = ["stm32-metapac/stm32g071r8"] +stm32g071rb = ["stm32-metapac/stm32g071rb"] +stm32g081cb = ["stm32-metapac/stm32g081cb"] +stm32g081eb = ["stm32-metapac/stm32g081eb"] +stm32g081gb = ["stm32-metapac/stm32g081gb"] +stm32g081kb = ["stm32-metapac/stm32g081kb"] +stm32g081rb = ["stm32-metapac/stm32g081rb"] +stm32g0b0ce = ["stm32-metapac/stm32g0b0ce"] +stm32g0b0ke = ["stm32-metapac/stm32g0b0ke"] +stm32g0b0re = ["stm32-metapac/stm32g0b0re"] +stm32g0b0ve = ["stm32-metapac/stm32g0b0ve"] +stm32g0b1cb = ["stm32-metapac/stm32g0b1cb"] +stm32g0b1cc = ["stm32-metapac/stm32g0b1cc"] +stm32g0b1ce = ["stm32-metapac/stm32g0b1ce"] +stm32g0b1kb = ["stm32-metapac/stm32g0b1kb"] +stm32g0b1kc = ["stm32-metapac/stm32g0b1kc"] +stm32g0b1ke = ["stm32-metapac/stm32g0b1ke"] +stm32g0b1mb = ["stm32-metapac/stm32g0b1mb"] +stm32g0b1mc = ["stm32-metapac/stm32g0b1mc"] +stm32g0b1me = ["stm32-metapac/stm32g0b1me"] +stm32g0b1ne = ["stm32-metapac/stm32g0b1ne"] +stm32g0b1rb = ["stm32-metapac/stm32g0b1rb"] +stm32g0b1rc = ["stm32-metapac/stm32g0b1rc"] +stm32g0b1re = ["stm32-metapac/stm32g0b1re"] +stm32g0b1vb = ["stm32-metapac/stm32g0b1vb"] +stm32g0b1vc = ["stm32-metapac/stm32g0b1vc"] +stm32g0b1ve = ["stm32-metapac/stm32g0b1ve"] +stm32g0c1cc = ["stm32-metapac/stm32g0c1cc"] +stm32g0c1ce = ["stm32-metapac/stm32g0c1ce"] +stm32g0c1kc = ["stm32-metapac/stm32g0c1kc"] +stm32g0c1ke = ["stm32-metapac/stm32g0c1ke"] +stm32g0c1mc = ["stm32-metapac/stm32g0c1mc"] +stm32g0c1me = ["stm32-metapac/stm32g0c1me"] +stm32g0c1ne = ["stm32-metapac/stm32g0c1ne"] +stm32g0c1rc = ["stm32-metapac/stm32g0c1rc"] +stm32g0c1re = ["stm32-metapac/stm32g0c1re"] +stm32g0c1vc = ["stm32-metapac/stm32g0c1vc"] +stm32g0c1ve = ["stm32-metapac/stm32g0c1ve"] +stm32g431c6 = ["stm32-metapac/stm32g431c6"] +stm32g431c8 = ["stm32-metapac/stm32g431c8"] +stm32g431cb = ["stm32-metapac/stm32g431cb"] +stm32g431k6 = ["stm32-metapac/stm32g431k6"] +stm32g431k8 = ["stm32-metapac/stm32g431k8"] +stm32g431kb = ["stm32-metapac/stm32g431kb"] +stm32g431m6 = ["stm32-metapac/stm32g431m6"] +stm32g431m8 = ["stm32-metapac/stm32g431m8"] +stm32g431mb = ["stm32-metapac/stm32g431mb"] +stm32g431r6 = ["stm32-metapac/stm32g431r6"] +stm32g431r8 = ["stm32-metapac/stm32g431r8"] +stm32g431rb = ["stm32-metapac/stm32g431rb"] +stm32g431v6 = ["stm32-metapac/stm32g431v6"] +stm32g431v8 = ["stm32-metapac/stm32g431v8"] +stm32g431vb = ["stm32-metapac/stm32g431vb"] +stm32g441cb = ["stm32-metapac/stm32g441cb"] +stm32g441kb = ["stm32-metapac/stm32g441kb"] +stm32g441mb = ["stm32-metapac/stm32g441mb"] +stm32g441rb = ["stm32-metapac/stm32g441rb"] +stm32g441vb = ["stm32-metapac/stm32g441vb"] +stm32g471cc = ["stm32-metapac/stm32g471cc"] +stm32g471ce = ["stm32-metapac/stm32g471ce"] +stm32g471mc = ["stm32-metapac/stm32g471mc"] +stm32g471me = ["stm32-metapac/stm32g471me"] +stm32g471qc = ["stm32-metapac/stm32g471qc"] +stm32g471qe = ["stm32-metapac/stm32g471qe"] +stm32g471rc = ["stm32-metapac/stm32g471rc"] +stm32g471re = ["stm32-metapac/stm32g471re"] +stm32g471vc = ["stm32-metapac/stm32g471vc"] +stm32g471ve = ["stm32-metapac/stm32g471ve"] +stm32g473cb = ["stm32-metapac/stm32g473cb"] +stm32g473cc = ["stm32-metapac/stm32g473cc"] +stm32g473ce = ["stm32-metapac/stm32g473ce"] +stm32g473mb = ["stm32-metapac/stm32g473mb"] +stm32g473mc = ["stm32-metapac/stm32g473mc"] +stm32g473me = ["stm32-metapac/stm32g473me"] +stm32g473pb = ["stm32-metapac/stm32g473pb"] +stm32g473pc = ["stm32-metapac/stm32g473pc"] +stm32g473pe = ["stm32-metapac/stm32g473pe"] +stm32g473qb = ["stm32-metapac/stm32g473qb"] +stm32g473qc = ["stm32-metapac/stm32g473qc"] +stm32g473qe = ["stm32-metapac/stm32g473qe"] +stm32g473rb = ["stm32-metapac/stm32g473rb"] +stm32g473rc = ["stm32-metapac/stm32g473rc"] +stm32g473re = ["stm32-metapac/stm32g473re"] +stm32g473vb = ["stm32-metapac/stm32g473vb"] +stm32g473vc = ["stm32-metapac/stm32g473vc"] +stm32g473ve = ["stm32-metapac/stm32g473ve"] +stm32g474cb = ["stm32-metapac/stm32g474cb"] +stm32g474cc = ["stm32-metapac/stm32g474cc"] +stm32g474ce = ["stm32-metapac/stm32g474ce"] +stm32g474mb = ["stm32-metapac/stm32g474mb"] +stm32g474mc = ["stm32-metapac/stm32g474mc"] +stm32g474me = ["stm32-metapac/stm32g474me"] +stm32g474pb = ["stm32-metapac/stm32g474pb"] +stm32g474pc = ["stm32-metapac/stm32g474pc"] +stm32g474pe = ["stm32-metapac/stm32g474pe"] +stm32g474qb = ["stm32-metapac/stm32g474qb"] +stm32g474qc = ["stm32-metapac/stm32g474qc"] +stm32g474qe = ["stm32-metapac/stm32g474qe"] +stm32g474rb = ["stm32-metapac/stm32g474rb"] +stm32g474rc = ["stm32-metapac/stm32g474rc"] +stm32g474re = ["stm32-metapac/stm32g474re"] +stm32g474vb = ["stm32-metapac/stm32g474vb"] +stm32g474vc = ["stm32-metapac/stm32g474vc"] +stm32g474ve = ["stm32-metapac/stm32g474ve"] +stm32g483ce = ["stm32-metapac/stm32g483ce"] +stm32g483me = ["stm32-metapac/stm32g483me"] +stm32g483pe = ["stm32-metapac/stm32g483pe"] +stm32g483qe = ["stm32-metapac/stm32g483qe"] +stm32g483re = ["stm32-metapac/stm32g483re"] +stm32g483ve = ["stm32-metapac/stm32g483ve"] +stm32g484ce = ["stm32-metapac/stm32g484ce"] +stm32g484me = ["stm32-metapac/stm32g484me"] +stm32g484pe = ["stm32-metapac/stm32g484pe"] +stm32g484qe = ["stm32-metapac/stm32g484qe"] +stm32g484re = ["stm32-metapac/stm32g484re"] +stm32g484ve = ["stm32-metapac/stm32g484ve"] +stm32g491cc = ["stm32-metapac/stm32g491cc"] +stm32g491ce = ["stm32-metapac/stm32g491ce"] +stm32g491kc = ["stm32-metapac/stm32g491kc"] +stm32g491ke = ["stm32-metapac/stm32g491ke"] +stm32g491mc = ["stm32-metapac/stm32g491mc"] +stm32g491me = ["stm32-metapac/stm32g491me"] +stm32g491rc = ["stm32-metapac/stm32g491rc"] +stm32g491re = ["stm32-metapac/stm32g491re"] +stm32g491vc = ["stm32-metapac/stm32g491vc"] +stm32g491ve = ["stm32-metapac/stm32g491ve"] +stm32g4a1ce = ["stm32-metapac/stm32g4a1ce"] +stm32g4a1ke = ["stm32-metapac/stm32g4a1ke"] +stm32g4a1me = ["stm32-metapac/stm32g4a1me"] +stm32g4a1re = ["stm32-metapac/stm32g4a1re"] +stm32g4a1ve = ["stm32-metapac/stm32g4a1ve"] +stm32h503cb = ["stm32-metapac/stm32h503cb"] +stm32h503eb = ["stm32-metapac/stm32h503eb"] +stm32h503kb = ["stm32-metapac/stm32h503kb"] +stm32h503rb = ["stm32-metapac/stm32h503rb"] +stm32h562ag = ["stm32-metapac/stm32h562ag"] +stm32h562ai = ["stm32-metapac/stm32h562ai"] +stm32h562ig = ["stm32-metapac/stm32h562ig"] +stm32h562ii = ["stm32-metapac/stm32h562ii"] +stm32h562rg = ["stm32-metapac/stm32h562rg"] +stm32h562ri = ["stm32-metapac/stm32h562ri"] +stm32h562vg = ["stm32-metapac/stm32h562vg"] +stm32h562vi = ["stm32-metapac/stm32h562vi"] +stm32h562zg = ["stm32-metapac/stm32h562zg"] +stm32h562zi = ["stm32-metapac/stm32h562zi"] +stm32h563ag = ["stm32-metapac/stm32h563ag"] +stm32h563ai = ["stm32-metapac/stm32h563ai"] +stm32h563ig = ["stm32-metapac/stm32h563ig"] +stm32h563ii = ["stm32-metapac/stm32h563ii"] +stm32h563mi = ["stm32-metapac/stm32h563mi"] +stm32h563rg = ["stm32-metapac/stm32h563rg"] +stm32h563ri = ["stm32-metapac/stm32h563ri"] +stm32h563vg = ["stm32-metapac/stm32h563vg"] +stm32h563vi = ["stm32-metapac/stm32h563vi"] +stm32h563zg = ["stm32-metapac/stm32h563zg"] +stm32h563zi = ["stm32-metapac/stm32h563zi"] +stm32h573ai = ["stm32-metapac/stm32h573ai"] +stm32h573ii = ["stm32-metapac/stm32h573ii"] +stm32h573mi = ["stm32-metapac/stm32h573mi"] +stm32h573ri = ["stm32-metapac/stm32h573ri"] +stm32h573vi = ["stm32-metapac/stm32h573vi"] +stm32h573zi = ["stm32-metapac/stm32h573zi"] +stm32h723ve = ["stm32-metapac/stm32h723ve"] +stm32h723vg = ["stm32-metapac/stm32h723vg"] +stm32h723ze = ["stm32-metapac/stm32h723ze"] +stm32h723zg = ["stm32-metapac/stm32h723zg"] +stm32h725ae = ["stm32-metapac/stm32h725ae"] +stm32h725ag = ["stm32-metapac/stm32h725ag"] +stm32h725ie = ["stm32-metapac/stm32h725ie"] +stm32h725ig = ["stm32-metapac/stm32h725ig"] +stm32h725re = ["stm32-metapac/stm32h725re"] +stm32h725rg = ["stm32-metapac/stm32h725rg"] +stm32h725ve = ["stm32-metapac/stm32h725ve"] +stm32h725vg = ["stm32-metapac/stm32h725vg"] +stm32h725ze = ["stm32-metapac/stm32h725ze"] +stm32h725zg = ["stm32-metapac/stm32h725zg"] +stm32h730ab = ["stm32-metapac/stm32h730ab"] +stm32h730ib = ["stm32-metapac/stm32h730ib"] +stm32h730vb = ["stm32-metapac/stm32h730vb"] +stm32h730zb = ["stm32-metapac/stm32h730zb"] +stm32h733vg = ["stm32-metapac/stm32h733vg"] +stm32h733zg = ["stm32-metapac/stm32h733zg"] +stm32h735ag = ["stm32-metapac/stm32h735ag"] +stm32h735ig = ["stm32-metapac/stm32h735ig"] +stm32h735rg = ["stm32-metapac/stm32h735rg"] +stm32h735vg = ["stm32-metapac/stm32h735vg"] +stm32h735zg = ["stm32-metapac/stm32h735zg"] +stm32h742ag = ["stm32-metapac/stm32h742ag"] +stm32h742ai = ["stm32-metapac/stm32h742ai"] +stm32h742bg = ["stm32-metapac/stm32h742bg"] +stm32h742bi = ["stm32-metapac/stm32h742bi"] +stm32h742ig = ["stm32-metapac/stm32h742ig"] +stm32h742ii = ["stm32-metapac/stm32h742ii"] +stm32h742vg = ["stm32-metapac/stm32h742vg"] +stm32h742vi = ["stm32-metapac/stm32h742vi"] +stm32h742xg = ["stm32-metapac/stm32h742xg"] +stm32h742xi = ["stm32-metapac/stm32h742xi"] +stm32h742zg = ["stm32-metapac/stm32h742zg"] +stm32h742zi = ["stm32-metapac/stm32h742zi"] +stm32h743ag = ["stm32-metapac/stm32h743ag"] +stm32h743ai = ["stm32-metapac/stm32h743ai"] +stm32h743bg = ["stm32-metapac/stm32h743bg"] +stm32h743bi = ["stm32-metapac/stm32h743bi"] +stm32h743ig = ["stm32-metapac/stm32h743ig"] +stm32h743ii = ["stm32-metapac/stm32h743ii"] +stm32h743vg = ["stm32-metapac/stm32h743vg"] +stm32h743vi = ["stm32-metapac/stm32h743vi"] +stm32h743xg = ["stm32-metapac/stm32h743xg"] +stm32h743xi = ["stm32-metapac/stm32h743xi"] +stm32h743zg = ["stm32-metapac/stm32h743zg"] +stm32h743zi = ["stm32-metapac/stm32h743zi"] +stm32h745bg-cm7 = ["stm32-metapac/stm32h745bg-cm7"] +stm32h745bg-cm4 = ["stm32-metapac/stm32h745bg-cm4"] +stm32h745bi-cm7 = ["stm32-metapac/stm32h745bi-cm7"] +stm32h745bi-cm4 = ["stm32-metapac/stm32h745bi-cm4"] +stm32h745ig-cm7 = ["stm32-metapac/stm32h745ig-cm7"] +stm32h745ig-cm4 = ["stm32-metapac/stm32h745ig-cm4"] +stm32h745ii-cm7 = ["stm32-metapac/stm32h745ii-cm7"] +stm32h745ii-cm4 = ["stm32-metapac/stm32h745ii-cm4"] +stm32h745xg-cm7 = ["stm32-metapac/stm32h745xg-cm7"] +stm32h745xg-cm4 = ["stm32-metapac/stm32h745xg-cm4"] +stm32h745xi-cm7 = ["stm32-metapac/stm32h745xi-cm7"] +stm32h745xi-cm4 = ["stm32-metapac/stm32h745xi-cm4"] +stm32h745zg-cm7 = ["stm32-metapac/stm32h745zg-cm7"] +stm32h745zg-cm4 = ["stm32-metapac/stm32h745zg-cm4"] +stm32h745zi-cm7 = ["stm32-metapac/stm32h745zi-cm7"] +stm32h745zi-cm4 = ["stm32-metapac/stm32h745zi-cm4"] +stm32h747ag-cm7 = ["stm32-metapac/stm32h747ag-cm7"] +stm32h747ag-cm4 = ["stm32-metapac/stm32h747ag-cm4"] +stm32h747ai-cm7 = ["stm32-metapac/stm32h747ai-cm7"] +stm32h747ai-cm4 = ["stm32-metapac/stm32h747ai-cm4"] +stm32h747bg-cm7 = ["stm32-metapac/stm32h747bg-cm7"] +stm32h747bg-cm4 = ["stm32-metapac/stm32h747bg-cm4"] +stm32h747bi-cm7 = ["stm32-metapac/stm32h747bi-cm7"] +stm32h747bi-cm4 = ["stm32-metapac/stm32h747bi-cm4"] +stm32h747ig-cm7 = ["stm32-metapac/stm32h747ig-cm7"] +stm32h747ig-cm4 = ["stm32-metapac/stm32h747ig-cm4"] +stm32h747ii-cm7 = ["stm32-metapac/stm32h747ii-cm7"] +stm32h747ii-cm4 = ["stm32-metapac/stm32h747ii-cm4"] +stm32h747xg-cm7 = ["stm32-metapac/stm32h747xg-cm7"] +stm32h747xg-cm4 = ["stm32-metapac/stm32h747xg-cm4"] +stm32h747xi-cm7 = ["stm32-metapac/stm32h747xi-cm7"] +stm32h747xi-cm4 = ["stm32-metapac/stm32h747xi-cm4"] +stm32h747zi-cm7 = ["stm32-metapac/stm32h747zi-cm7"] +stm32h747zi-cm4 = ["stm32-metapac/stm32h747zi-cm4"] +stm32h750ib = ["stm32-metapac/stm32h750ib"] +stm32h750vb = ["stm32-metapac/stm32h750vb"] +stm32h750xb = ["stm32-metapac/stm32h750xb"] +stm32h750zb = ["stm32-metapac/stm32h750zb"] +stm32h753ai = ["stm32-metapac/stm32h753ai"] +stm32h753bi = ["stm32-metapac/stm32h753bi"] +stm32h753ii = ["stm32-metapac/stm32h753ii"] +stm32h753vi = ["stm32-metapac/stm32h753vi"] +stm32h753xi = ["stm32-metapac/stm32h753xi"] +stm32h753zi = ["stm32-metapac/stm32h753zi"] +stm32h755bi-cm7 = ["stm32-metapac/stm32h755bi-cm7"] +stm32h755bi-cm4 = ["stm32-metapac/stm32h755bi-cm4"] +stm32h755ii-cm7 = ["stm32-metapac/stm32h755ii-cm7"] +stm32h755ii-cm4 = ["stm32-metapac/stm32h755ii-cm4"] +stm32h755xi-cm7 = ["stm32-metapac/stm32h755xi-cm7"] +stm32h755xi-cm4 = ["stm32-metapac/stm32h755xi-cm4"] +stm32h755zi-cm7 = ["stm32-metapac/stm32h755zi-cm7"] +stm32h755zi-cm4 = ["stm32-metapac/stm32h755zi-cm4"] +stm32h757ai-cm7 = ["stm32-metapac/stm32h757ai-cm7"] +stm32h757ai-cm4 = ["stm32-metapac/stm32h757ai-cm4"] +stm32h757bi-cm7 = ["stm32-metapac/stm32h757bi-cm7"] +stm32h757bi-cm4 = ["stm32-metapac/stm32h757bi-cm4"] +stm32h757ii-cm7 = ["stm32-metapac/stm32h757ii-cm7"] +stm32h757ii-cm4 = ["stm32-metapac/stm32h757ii-cm4"] +stm32h757xi-cm7 = ["stm32-metapac/stm32h757xi-cm7"] +stm32h757xi-cm4 = ["stm32-metapac/stm32h757xi-cm4"] +stm32h757zi-cm7 = ["stm32-metapac/stm32h757zi-cm7"] +stm32h757zi-cm4 = ["stm32-metapac/stm32h757zi-cm4"] +stm32h7a3ag = ["stm32-metapac/stm32h7a3ag"] +stm32h7a3ai = ["stm32-metapac/stm32h7a3ai"] +stm32h7a3ig = ["stm32-metapac/stm32h7a3ig"] +stm32h7a3ii = ["stm32-metapac/stm32h7a3ii"] +stm32h7a3lg = ["stm32-metapac/stm32h7a3lg"] +stm32h7a3li = ["stm32-metapac/stm32h7a3li"] +stm32h7a3ng = ["stm32-metapac/stm32h7a3ng"] +stm32h7a3ni = ["stm32-metapac/stm32h7a3ni"] +stm32h7a3qi = ["stm32-metapac/stm32h7a3qi"] +stm32h7a3rg = ["stm32-metapac/stm32h7a3rg"] +stm32h7a3ri = ["stm32-metapac/stm32h7a3ri"] +stm32h7a3vg = ["stm32-metapac/stm32h7a3vg"] +stm32h7a3vi = ["stm32-metapac/stm32h7a3vi"] +stm32h7a3zg = ["stm32-metapac/stm32h7a3zg"] +stm32h7a3zi = ["stm32-metapac/stm32h7a3zi"] +stm32h7b0ab = ["stm32-metapac/stm32h7b0ab"] +stm32h7b0ib = ["stm32-metapac/stm32h7b0ib"] +stm32h7b0rb = ["stm32-metapac/stm32h7b0rb"] +stm32h7b0vb = ["stm32-metapac/stm32h7b0vb"] +stm32h7b0zb = ["stm32-metapac/stm32h7b0zb"] +stm32h7b3ai = ["stm32-metapac/stm32h7b3ai"] +stm32h7b3ii = ["stm32-metapac/stm32h7b3ii"] +stm32h7b3li = ["stm32-metapac/stm32h7b3li"] +stm32h7b3ni = ["stm32-metapac/stm32h7b3ni"] +stm32h7b3qi = ["stm32-metapac/stm32h7b3qi"] +stm32h7b3ri = ["stm32-metapac/stm32h7b3ri"] +stm32h7b3vi = ["stm32-metapac/stm32h7b3vi"] +stm32h7b3zi = ["stm32-metapac/stm32h7b3zi"] +stm32l010c6 = ["stm32-metapac/stm32l010c6"] +stm32l010f4 = ["stm32-metapac/stm32l010f4"] +stm32l010k4 = ["stm32-metapac/stm32l010k4"] +stm32l010k8 = ["stm32-metapac/stm32l010k8"] +stm32l010r8 = ["stm32-metapac/stm32l010r8"] +stm32l010rb = ["stm32-metapac/stm32l010rb"] +stm32l011d3 = ["stm32-metapac/stm32l011d3"] +stm32l011d4 = ["stm32-metapac/stm32l011d4"] +stm32l011e3 = ["stm32-metapac/stm32l011e3"] +stm32l011e4 = ["stm32-metapac/stm32l011e4"] +stm32l011f3 = ["stm32-metapac/stm32l011f3"] +stm32l011f4 = ["stm32-metapac/stm32l011f4"] +stm32l011g3 = ["stm32-metapac/stm32l011g3"] +stm32l011g4 = ["stm32-metapac/stm32l011g4"] +stm32l011k3 = ["stm32-metapac/stm32l011k3"] +stm32l011k4 = ["stm32-metapac/stm32l011k4"] +stm32l021d4 = ["stm32-metapac/stm32l021d4"] +stm32l021f4 = ["stm32-metapac/stm32l021f4"] +stm32l021g4 = ["stm32-metapac/stm32l021g4"] +stm32l021k4 = ["stm32-metapac/stm32l021k4"] +stm32l031c4 = ["stm32-metapac/stm32l031c4"] +stm32l031c6 = ["stm32-metapac/stm32l031c6"] +stm32l031e4 = ["stm32-metapac/stm32l031e4"] +stm32l031e6 = ["stm32-metapac/stm32l031e6"] +stm32l031f4 = ["stm32-metapac/stm32l031f4"] +stm32l031f6 = ["stm32-metapac/stm32l031f6"] +stm32l031g4 = ["stm32-metapac/stm32l031g4"] +stm32l031g6 = ["stm32-metapac/stm32l031g6"] +stm32l031k4 = ["stm32-metapac/stm32l031k4"] +stm32l031k6 = ["stm32-metapac/stm32l031k6"] +stm32l041c4 = ["stm32-metapac/stm32l041c4"] +stm32l041c6 = ["stm32-metapac/stm32l041c6"] +stm32l041e6 = ["stm32-metapac/stm32l041e6"] +stm32l041f6 = ["stm32-metapac/stm32l041f6"] +stm32l041g6 = ["stm32-metapac/stm32l041g6"] +stm32l041k6 = ["stm32-metapac/stm32l041k6"] +stm32l051c6 = ["stm32-metapac/stm32l051c6"] +stm32l051c8 = ["stm32-metapac/stm32l051c8"] +stm32l051k6 = ["stm32-metapac/stm32l051k6"] +stm32l051k8 = ["stm32-metapac/stm32l051k8"] +stm32l051r6 = ["stm32-metapac/stm32l051r6"] +stm32l051r8 = ["stm32-metapac/stm32l051r8"] +stm32l051t6 = ["stm32-metapac/stm32l051t6"] +stm32l051t8 = ["stm32-metapac/stm32l051t8"] +stm32l052c6 = ["stm32-metapac/stm32l052c6"] +stm32l052c8 = ["stm32-metapac/stm32l052c8"] +stm32l052k6 = ["stm32-metapac/stm32l052k6"] +stm32l052k8 = ["stm32-metapac/stm32l052k8"] +stm32l052r6 = ["stm32-metapac/stm32l052r6"] +stm32l052r8 = ["stm32-metapac/stm32l052r8"] +stm32l052t6 = ["stm32-metapac/stm32l052t6"] +stm32l052t8 = ["stm32-metapac/stm32l052t8"] +stm32l053c6 = ["stm32-metapac/stm32l053c6"] +stm32l053c8 = ["stm32-metapac/stm32l053c8"] +stm32l053r6 = ["stm32-metapac/stm32l053r6"] +stm32l053r8 = ["stm32-metapac/stm32l053r8"] +stm32l062c8 = ["stm32-metapac/stm32l062c8"] +stm32l062k8 = ["stm32-metapac/stm32l062k8"] +stm32l063c8 = ["stm32-metapac/stm32l063c8"] +stm32l063r8 = ["stm32-metapac/stm32l063r8"] +stm32l071c8 = ["stm32-metapac/stm32l071c8"] +stm32l071cb = ["stm32-metapac/stm32l071cb"] +stm32l071cz = ["stm32-metapac/stm32l071cz"] +stm32l071k8 = ["stm32-metapac/stm32l071k8"] +stm32l071kb = ["stm32-metapac/stm32l071kb"] +stm32l071kz = ["stm32-metapac/stm32l071kz"] +stm32l071rb = ["stm32-metapac/stm32l071rb"] +stm32l071rz = ["stm32-metapac/stm32l071rz"] +stm32l071v8 = ["stm32-metapac/stm32l071v8"] +stm32l071vb = ["stm32-metapac/stm32l071vb"] +stm32l071vz = ["stm32-metapac/stm32l071vz"] +stm32l072cb = ["stm32-metapac/stm32l072cb"] +stm32l072cz = ["stm32-metapac/stm32l072cz"] +stm32l072kb = ["stm32-metapac/stm32l072kb"] +stm32l072kz = ["stm32-metapac/stm32l072kz"] +stm32l072rb = ["stm32-metapac/stm32l072rb"] +stm32l072rz = ["stm32-metapac/stm32l072rz"] +stm32l072v8 = ["stm32-metapac/stm32l072v8"] +stm32l072vb = ["stm32-metapac/stm32l072vb"] +stm32l072vz = ["stm32-metapac/stm32l072vz"] +stm32l073cb = ["stm32-metapac/stm32l073cb"] +stm32l073cz = ["stm32-metapac/stm32l073cz"] +stm32l073rb = ["stm32-metapac/stm32l073rb"] +stm32l073rz = ["stm32-metapac/stm32l073rz"] +stm32l073v8 = ["stm32-metapac/stm32l073v8"] +stm32l073vb = ["stm32-metapac/stm32l073vb"] +stm32l073vz = ["stm32-metapac/stm32l073vz"] +stm32l081cb = ["stm32-metapac/stm32l081cb"] +stm32l081cz = ["stm32-metapac/stm32l081cz"] +stm32l081kz = ["stm32-metapac/stm32l081kz"] +stm32l082cz = ["stm32-metapac/stm32l082cz"] +stm32l082kb = ["stm32-metapac/stm32l082kb"] +stm32l082kz = ["stm32-metapac/stm32l082kz"] +stm32l083cb = ["stm32-metapac/stm32l083cb"] +stm32l083cz = ["stm32-metapac/stm32l083cz"] +stm32l083rb = ["stm32-metapac/stm32l083rb"] +stm32l083rz = ["stm32-metapac/stm32l083rz"] +stm32l083v8 = ["stm32-metapac/stm32l083v8"] +stm32l083vb = ["stm32-metapac/stm32l083vb"] +stm32l083vz = ["stm32-metapac/stm32l083vz"] +stm32l100c6 = ["stm32-metapac/stm32l100c6"] +stm32l100c6-a = ["stm32-metapac/stm32l100c6-a"] +stm32l100r8 = ["stm32-metapac/stm32l100r8"] +stm32l100r8-a = ["stm32-metapac/stm32l100r8-a"] +stm32l100rb = ["stm32-metapac/stm32l100rb"] +stm32l100rb-a = ["stm32-metapac/stm32l100rb-a"] +stm32l100rc = ["stm32-metapac/stm32l100rc"] +stm32l151c6 = ["stm32-metapac/stm32l151c6"] +stm32l151c6-a = ["stm32-metapac/stm32l151c6-a"] +stm32l151c8 = ["stm32-metapac/stm32l151c8"] +stm32l151c8-a = ["stm32-metapac/stm32l151c8-a"] +stm32l151cb = ["stm32-metapac/stm32l151cb"] +stm32l151cb-a = ["stm32-metapac/stm32l151cb-a"] +stm32l151cc = ["stm32-metapac/stm32l151cc"] +stm32l151qc = ["stm32-metapac/stm32l151qc"] +stm32l151qd = ["stm32-metapac/stm32l151qd"] +stm32l151qe = ["stm32-metapac/stm32l151qe"] +stm32l151r6 = ["stm32-metapac/stm32l151r6"] +stm32l151r6-a = ["stm32-metapac/stm32l151r6-a"] +stm32l151r8 = ["stm32-metapac/stm32l151r8"] +stm32l151r8-a = ["stm32-metapac/stm32l151r8-a"] +stm32l151rb = ["stm32-metapac/stm32l151rb"] +stm32l151rb-a = ["stm32-metapac/stm32l151rb-a"] +stm32l151rc = ["stm32-metapac/stm32l151rc"] +stm32l151rc-a = ["stm32-metapac/stm32l151rc-a"] +stm32l151rd = ["stm32-metapac/stm32l151rd"] +stm32l151re = ["stm32-metapac/stm32l151re"] +stm32l151uc = ["stm32-metapac/stm32l151uc"] +stm32l151v8 = ["stm32-metapac/stm32l151v8"] +stm32l151v8-a = ["stm32-metapac/stm32l151v8-a"] +stm32l151vb = ["stm32-metapac/stm32l151vb"] +stm32l151vb-a = ["stm32-metapac/stm32l151vb-a"] +stm32l151vc = ["stm32-metapac/stm32l151vc"] +stm32l151vc-a = ["stm32-metapac/stm32l151vc-a"] +stm32l151vd = ["stm32-metapac/stm32l151vd"] +stm32l151vd-x = ["stm32-metapac/stm32l151vd-x"] +stm32l151ve = ["stm32-metapac/stm32l151ve"] +stm32l151zc = ["stm32-metapac/stm32l151zc"] +stm32l151zd = ["stm32-metapac/stm32l151zd"] +stm32l151ze = ["stm32-metapac/stm32l151ze"] +stm32l152c6 = ["stm32-metapac/stm32l152c6"] +stm32l152c6-a = ["stm32-metapac/stm32l152c6-a"] +stm32l152c8 = ["stm32-metapac/stm32l152c8"] +stm32l152c8-a = ["stm32-metapac/stm32l152c8-a"] +stm32l152cb = ["stm32-metapac/stm32l152cb"] +stm32l152cb-a = ["stm32-metapac/stm32l152cb-a"] +stm32l152cc = ["stm32-metapac/stm32l152cc"] +stm32l152qc = ["stm32-metapac/stm32l152qc"] +stm32l152qd = ["stm32-metapac/stm32l152qd"] +stm32l152qe = ["stm32-metapac/stm32l152qe"] +stm32l152r6 = ["stm32-metapac/stm32l152r6"] +stm32l152r6-a = ["stm32-metapac/stm32l152r6-a"] +stm32l152r8 = ["stm32-metapac/stm32l152r8"] +stm32l152r8-a = ["stm32-metapac/stm32l152r8-a"] +stm32l152rb = ["stm32-metapac/stm32l152rb"] +stm32l152rb-a = ["stm32-metapac/stm32l152rb-a"] +stm32l152rc = ["stm32-metapac/stm32l152rc"] +stm32l152rc-a = ["stm32-metapac/stm32l152rc-a"] +stm32l152rd = ["stm32-metapac/stm32l152rd"] +stm32l152re = ["stm32-metapac/stm32l152re"] +stm32l152uc = ["stm32-metapac/stm32l152uc"] +stm32l152v8 = ["stm32-metapac/stm32l152v8"] +stm32l152v8-a = ["stm32-metapac/stm32l152v8-a"] +stm32l152vb = ["stm32-metapac/stm32l152vb"] +stm32l152vb-a = ["stm32-metapac/stm32l152vb-a"] +stm32l152vc = ["stm32-metapac/stm32l152vc"] +stm32l152vc-a = ["stm32-metapac/stm32l152vc-a"] +stm32l152vd = ["stm32-metapac/stm32l152vd"] +stm32l152vd-x = ["stm32-metapac/stm32l152vd-x"] +stm32l152ve = ["stm32-metapac/stm32l152ve"] +stm32l152zc = ["stm32-metapac/stm32l152zc"] +stm32l152zd = ["stm32-metapac/stm32l152zd"] +stm32l152ze = ["stm32-metapac/stm32l152ze"] +stm32l162qc = ["stm32-metapac/stm32l162qc"] +stm32l162qd = ["stm32-metapac/stm32l162qd"] +stm32l162rc = ["stm32-metapac/stm32l162rc"] +stm32l162rc-a = ["stm32-metapac/stm32l162rc-a"] +stm32l162rd = ["stm32-metapac/stm32l162rd"] +stm32l162re = ["stm32-metapac/stm32l162re"] +stm32l162vc = ["stm32-metapac/stm32l162vc"] +stm32l162vc-a = ["stm32-metapac/stm32l162vc-a"] +stm32l162vd = ["stm32-metapac/stm32l162vd"] +stm32l162vd-x = ["stm32-metapac/stm32l162vd-x"] +stm32l162ve = ["stm32-metapac/stm32l162ve"] +stm32l162zc = ["stm32-metapac/stm32l162zc"] +stm32l162zd = ["stm32-metapac/stm32l162zd"] +stm32l162ze = ["stm32-metapac/stm32l162ze"] +stm32l412c8 = ["stm32-metapac/stm32l412c8"] +stm32l412cb = ["stm32-metapac/stm32l412cb"] +stm32l412k8 = ["stm32-metapac/stm32l412k8"] +stm32l412kb = ["stm32-metapac/stm32l412kb"] +stm32l412r8 = ["stm32-metapac/stm32l412r8"] +stm32l412rb = ["stm32-metapac/stm32l412rb"] +stm32l412t8 = ["stm32-metapac/stm32l412t8"] +stm32l412tb = ["stm32-metapac/stm32l412tb"] +stm32l422cb = ["stm32-metapac/stm32l422cb"] +stm32l422kb = ["stm32-metapac/stm32l422kb"] +stm32l422rb = ["stm32-metapac/stm32l422rb"] +stm32l422tb = ["stm32-metapac/stm32l422tb"] +stm32l431cb = ["stm32-metapac/stm32l431cb"] +stm32l431cc = ["stm32-metapac/stm32l431cc"] +stm32l431kb = ["stm32-metapac/stm32l431kb"] +stm32l431kc = ["stm32-metapac/stm32l431kc"] +stm32l431rb = ["stm32-metapac/stm32l431rb"] +stm32l431rc = ["stm32-metapac/stm32l431rc"] +stm32l431vc = ["stm32-metapac/stm32l431vc"] +stm32l432kb = ["stm32-metapac/stm32l432kb"] +stm32l432kc = ["stm32-metapac/stm32l432kc"] +stm32l433cb = ["stm32-metapac/stm32l433cb"] +stm32l433cc = ["stm32-metapac/stm32l433cc"] +stm32l433rb = ["stm32-metapac/stm32l433rb"] +stm32l433rc = ["stm32-metapac/stm32l433rc"] +stm32l433vc = ["stm32-metapac/stm32l433vc"] +stm32l442kc = ["stm32-metapac/stm32l442kc"] +stm32l443cc = ["stm32-metapac/stm32l443cc"] +stm32l443rc = ["stm32-metapac/stm32l443rc"] +stm32l443vc = ["stm32-metapac/stm32l443vc"] +stm32l451cc = ["stm32-metapac/stm32l451cc"] +stm32l451ce = ["stm32-metapac/stm32l451ce"] +stm32l451rc = ["stm32-metapac/stm32l451rc"] +stm32l451re = ["stm32-metapac/stm32l451re"] +stm32l451vc = ["stm32-metapac/stm32l451vc"] +stm32l451ve = ["stm32-metapac/stm32l451ve"] +stm32l452cc = ["stm32-metapac/stm32l452cc"] +stm32l452ce = ["stm32-metapac/stm32l452ce"] +stm32l452rc = ["stm32-metapac/stm32l452rc"] +stm32l452re = ["stm32-metapac/stm32l452re"] +stm32l452vc = ["stm32-metapac/stm32l452vc"] +stm32l452ve = ["stm32-metapac/stm32l452ve"] +stm32l462ce = ["stm32-metapac/stm32l462ce"] +stm32l462re = ["stm32-metapac/stm32l462re"] +stm32l462ve = ["stm32-metapac/stm32l462ve"] +stm32l471qe = ["stm32-metapac/stm32l471qe"] +stm32l471qg = ["stm32-metapac/stm32l471qg"] +stm32l471re = ["stm32-metapac/stm32l471re"] +stm32l471rg = ["stm32-metapac/stm32l471rg"] +stm32l471ve = ["stm32-metapac/stm32l471ve"] +stm32l471vg = ["stm32-metapac/stm32l471vg"] +stm32l471ze = ["stm32-metapac/stm32l471ze"] +stm32l471zg = ["stm32-metapac/stm32l471zg"] +stm32l475rc = ["stm32-metapac/stm32l475rc"] +stm32l475re = ["stm32-metapac/stm32l475re"] +stm32l475rg = ["stm32-metapac/stm32l475rg"] +stm32l475vc = ["stm32-metapac/stm32l475vc"] +stm32l475ve = ["stm32-metapac/stm32l475ve"] +stm32l475vg = ["stm32-metapac/stm32l475vg"] +stm32l476je = ["stm32-metapac/stm32l476je"] +stm32l476jg = ["stm32-metapac/stm32l476jg"] +stm32l476me = ["stm32-metapac/stm32l476me"] +stm32l476mg = ["stm32-metapac/stm32l476mg"] +stm32l476qe = ["stm32-metapac/stm32l476qe"] +stm32l476qg = ["stm32-metapac/stm32l476qg"] +stm32l476rc = ["stm32-metapac/stm32l476rc"] +stm32l476re = ["stm32-metapac/stm32l476re"] +stm32l476rg = ["stm32-metapac/stm32l476rg"] +stm32l476vc = ["stm32-metapac/stm32l476vc"] +stm32l476ve = ["stm32-metapac/stm32l476ve"] +stm32l476vg = ["stm32-metapac/stm32l476vg"] +stm32l476ze = ["stm32-metapac/stm32l476ze"] +stm32l476zg = ["stm32-metapac/stm32l476zg"] +stm32l486jg = ["stm32-metapac/stm32l486jg"] +stm32l486qg = ["stm32-metapac/stm32l486qg"] +stm32l486rg = ["stm32-metapac/stm32l486rg"] +stm32l486vg = ["stm32-metapac/stm32l486vg"] +stm32l486zg = ["stm32-metapac/stm32l486zg"] +stm32l496ae = ["stm32-metapac/stm32l496ae"] +stm32l496ag = ["stm32-metapac/stm32l496ag"] +stm32l496qe = ["stm32-metapac/stm32l496qe"] +stm32l496qg = ["stm32-metapac/stm32l496qg"] +stm32l496re = ["stm32-metapac/stm32l496re"] +stm32l496rg = ["stm32-metapac/stm32l496rg"] +stm32l496ve = ["stm32-metapac/stm32l496ve"] +stm32l496vg = ["stm32-metapac/stm32l496vg"] +stm32l496wg = ["stm32-metapac/stm32l496wg"] +stm32l496ze = ["stm32-metapac/stm32l496ze"] +stm32l496zg = ["stm32-metapac/stm32l496zg"] +stm32l4a6ag = ["stm32-metapac/stm32l4a6ag"] +stm32l4a6qg = ["stm32-metapac/stm32l4a6qg"] +stm32l4a6rg = ["stm32-metapac/stm32l4a6rg"] +stm32l4a6vg = ["stm32-metapac/stm32l4a6vg"] +stm32l4a6zg = ["stm32-metapac/stm32l4a6zg"] +stm32l4p5ae = ["stm32-metapac/stm32l4p5ae"] +stm32l4p5ag = ["stm32-metapac/stm32l4p5ag"] +stm32l4p5ce = ["stm32-metapac/stm32l4p5ce"] +stm32l4p5cg = ["stm32-metapac/stm32l4p5cg"] +stm32l4p5qe = ["stm32-metapac/stm32l4p5qe"] +stm32l4p5qg = ["stm32-metapac/stm32l4p5qg"] +stm32l4p5re = ["stm32-metapac/stm32l4p5re"] +stm32l4p5rg = ["stm32-metapac/stm32l4p5rg"] +stm32l4p5ve = ["stm32-metapac/stm32l4p5ve"] +stm32l4p5vg = ["stm32-metapac/stm32l4p5vg"] +stm32l4p5ze = ["stm32-metapac/stm32l4p5ze"] +stm32l4p5zg = ["stm32-metapac/stm32l4p5zg"] +stm32l4q5ag = ["stm32-metapac/stm32l4q5ag"] +stm32l4q5cg = ["stm32-metapac/stm32l4q5cg"] +stm32l4q5qg = ["stm32-metapac/stm32l4q5qg"] +stm32l4q5rg = ["stm32-metapac/stm32l4q5rg"] +stm32l4q5vg = ["stm32-metapac/stm32l4q5vg"] +stm32l4q5zg = ["stm32-metapac/stm32l4q5zg"] +stm32l4r5ag = ["stm32-metapac/stm32l4r5ag"] +stm32l4r5ai = ["stm32-metapac/stm32l4r5ai"] +stm32l4r5qg = ["stm32-metapac/stm32l4r5qg"] +stm32l4r5qi = ["stm32-metapac/stm32l4r5qi"] +stm32l4r5vg = ["stm32-metapac/stm32l4r5vg"] +stm32l4r5vi = ["stm32-metapac/stm32l4r5vi"] +stm32l4r5zg = ["stm32-metapac/stm32l4r5zg"] +stm32l4r5zi = ["stm32-metapac/stm32l4r5zi"] +stm32l4r7ai = ["stm32-metapac/stm32l4r7ai"] +stm32l4r7vi = ["stm32-metapac/stm32l4r7vi"] +stm32l4r7zi = ["stm32-metapac/stm32l4r7zi"] +stm32l4r9ag = ["stm32-metapac/stm32l4r9ag"] +stm32l4r9ai = ["stm32-metapac/stm32l4r9ai"] +stm32l4r9vg = ["stm32-metapac/stm32l4r9vg"] +stm32l4r9vi = ["stm32-metapac/stm32l4r9vi"] +stm32l4r9zg = ["stm32-metapac/stm32l4r9zg"] +stm32l4r9zi = ["stm32-metapac/stm32l4r9zi"] +stm32l4s5ai = ["stm32-metapac/stm32l4s5ai"] +stm32l4s5qi = ["stm32-metapac/stm32l4s5qi"] +stm32l4s5vi = ["stm32-metapac/stm32l4s5vi"] +stm32l4s5zi = ["stm32-metapac/stm32l4s5zi"] +stm32l4s7ai = ["stm32-metapac/stm32l4s7ai"] +stm32l4s7vi = ["stm32-metapac/stm32l4s7vi"] +stm32l4s7zi = ["stm32-metapac/stm32l4s7zi"] +stm32l4s9ai = ["stm32-metapac/stm32l4s9ai"] +stm32l4s9vi = ["stm32-metapac/stm32l4s9vi"] +stm32l4s9zi = ["stm32-metapac/stm32l4s9zi"] +stm32l552cc = ["stm32-metapac/stm32l552cc"] +stm32l552ce = ["stm32-metapac/stm32l552ce"] +stm32l552me = ["stm32-metapac/stm32l552me"] +stm32l552qc = ["stm32-metapac/stm32l552qc"] +stm32l552qe = ["stm32-metapac/stm32l552qe"] +stm32l552rc = ["stm32-metapac/stm32l552rc"] +stm32l552re = ["stm32-metapac/stm32l552re"] +stm32l552vc = ["stm32-metapac/stm32l552vc"] +stm32l552ve = ["stm32-metapac/stm32l552ve"] +stm32l552zc = ["stm32-metapac/stm32l552zc"] +stm32l552ze = ["stm32-metapac/stm32l552ze"] +stm32l562ce = ["stm32-metapac/stm32l562ce"] +stm32l562me = ["stm32-metapac/stm32l562me"] +stm32l562qe = ["stm32-metapac/stm32l562qe"] +stm32l562re = ["stm32-metapac/stm32l562re"] +stm32l562ve = ["stm32-metapac/stm32l562ve"] +stm32l562ze = ["stm32-metapac/stm32l562ze"] +stm32u535cb = ["stm32-metapac/stm32u535cb"] +stm32u535cc = ["stm32-metapac/stm32u535cc"] +stm32u535ce = ["stm32-metapac/stm32u535ce"] +stm32u535je = ["stm32-metapac/stm32u535je"] +stm32u535nc = ["stm32-metapac/stm32u535nc"] +stm32u535ne = ["stm32-metapac/stm32u535ne"] +stm32u535rb = ["stm32-metapac/stm32u535rb"] +stm32u535rc = ["stm32-metapac/stm32u535rc"] +stm32u535re = ["stm32-metapac/stm32u535re"] +stm32u535vc = ["stm32-metapac/stm32u535vc"] +stm32u535ve = ["stm32-metapac/stm32u535ve"] +stm32u545ce = ["stm32-metapac/stm32u545ce"] +stm32u545je = ["stm32-metapac/stm32u545je"] +stm32u545ne = ["stm32-metapac/stm32u545ne"] +stm32u545re = ["stm32-metapac/stm32u545re"] +stm32u545ve = ["stm32-metapac/stm32u545ve"] +stm32u575ag = ["stm32-metapac/stm32u575ag"] +stm32u575ai = ["stm32-metapac/stm32u575ai"] +stm32u575cg = ["stm32-metapac/stm32u575cg"] +stm32u575ci = ["stm32-metapac/stm32u575ci"] +stm32u575og = ["stm32-metapac/stm32u575og"] +stm32u575oi = ["stm32-metapac/stm32u575oi"] +stm32u575qg = ["stm32-metapac/stm32u575qg"] +stm32u575qi = ["stm32-metapac/stm32u575qi"] +stm32u575rg = ["stm32-metapac/stm32u575rg"] +stm32u575ri = ["stm32-metapac/stm32u575ri"] +stm32u575vg = ["stm32-metapac/stm32u575vg"] +stm32u575vi = ["stm32-metapac/stm32u575vi"] +stm32u575zg = ["stm32-metapac/stm32u575zg"] +stm32u575zi = ["stm32-metapac/stm32u575zi"] +stm32u585ai = ["stm32-metapac/stm32u585ai"] +stm32u585ci = ["stm32-metapac/stm32u585ci"] +stm32u585oi = ["stm32-metapac/stm32u585oi"] +stm32u585qi = ["stm32-metapac/stm32u585qi"] +stm32u585ri = ["stm32-metapac/stm32u585ri"] +stm32u585vi = ["stm32-metapac/stm32u585vi"] +stm32u585zi = ["stm32-metapac/stm32u585zi"] +stm32u595ai = ["stm32-metapac/stm32u595ai"] +stm32u595aj = ["stm32-metapac/stm32u595aj"] +stm32u595qi = ["stm32-metapac/stm32u595qi"] +stm32u595qj = ["stm32-metapac/stm32u595qj"] +stm32u595ri = ["stm32-metapac/stm32u595ri"] +stm32u595rj = ["stm32-metapac/stm32u595rj"] +stm32u595vi = ["stm32-metapac/stm32u595vi"] +stm32u595vj = ["stm32-metapac/stm32u595vj"] +stm32u595zi = ["stm32-metapac/stm32u595zi"] +stm32u595zj = ["stm32-metapac/stm32u595zj"] +stm32u599bj = ["stm32-metapac/stm32u599bj"] +stm32u599ni = ["stm32-metapac/stm32u599ni"] +stm32u599nj = ["stm32-metapac/stm32u599nj"] +stm32u599vi = ["stm32-metapac/stm32u599vi"] +stm32u599vj = ["stm32-metapac/stm32u599vj"] +stm32u599zi = ["stm32-metapac/stm32u599zi"] +stm32u599zj = ["stm32-metapac/stm32u599zj"] +stm32u5a5aj = ["stm32-metapac/stm32u5a5aj"] +stm32u5a5qj = ["stm32-metapac/stm32u5a5qj"] +stm32u5a5rj = ["stm32-metapac/stm32u5a5rj"] +stm32u5a5vj = ["stm32-metapac/stm32u5a5vj"] +stm32u5a5zj = ["stm32-metapac/stm32u5a5zj"] +stm32u5a9bj = ["stm32-metapac/stm32u5a9bj"] +stm32u5a9nj = ["stm32-metapac/stm32u5a9nj"] +stm32u5a9vj = ["stm32-metapac/stm32u5a9vj"] +stm32u5a9zj = ["stm32-metapac/stm32u5a9zj"] +stm32wb10cc = ["stm32-metapac/stm32wb10cc"] +stm32wb15cc = ["stm32-metapac/stm32wb15cc"] +stm32wb30ce = ["stm32-metapac/stm32wb30ce"] +stm32wb35cc = ["stm32-metapac/stm32wb35cc"] +stm32wb35ce = ["stm32-metapac/stm32wb35ce"] +stm32wb50cg = ["stm32-metapac/stm32wb50cg"] +stm32wb55cc = ["stm32-metapac/stm32wb55cc"] +stm32wb55ce = ["stm32-metapac/stm32wb55ce"] +stm32wb55cg = ["stm32-metapac/stm32wb55cg"] +stm32wb55rc = ["stm32-metapac/stm32wb55rc"] +stm32wb55re = ["stm32-metapac/stm32wb55re"] +stm32wb55rg = ["stm32-metapac/stm32wb55rg"] +stm32wb55vc = ["stm32-metapac/stm32wb55vc"] +stm32wb55ve = ["stm32-metapac/stm32wb55ve"] +stm32wb55vg = ["stm32-metapac/stm32wb55vg"] +stm32wb55vy = ["stm32-metapac/stm32wb55vy"] +stm32wl54cc-cm4 = ["stm32-metapac/stm32wl54cc-cm4"] +stm32wl54cc-cm0p = ["stm32-metapac/stm32wl54cc-cm0p"] +stm32wl54jc-cm4 = ["stm32-metapac/stm32wl54jc-cm4"] +stm32wl54jc-cm0p = ["stm32-metapac/stm32wl54jc-cm0p"] +stm32wl55cc-cm4 = ["stm32-metapac/stm32wl55cc-cm4"] +stm32wl55cc-cm0p = ["stm32-metapac/stm32wl55cc-cm0p"] +stm32wl55jc-cm4 = ["stm32-metapac/stm32wl55jc-cm4"] +stm32wl55jc-cm0p = ["stm32-metapac/stm32wl55jc-cm0p"] +stm32wle4c8 = ["stm32-metapac/stm32wle4c8"] +stm32wle4cb = ["stm32-metapac/stm32wle4cb"] +stm32wle4cc = ["stm32-metapac/stm32wle4cc"] +stm32wle4j8 = ["stm32-metapac/stm32wle4j8"] +stm32wle4jb = ["stm32-metapac/stm32wle4jb"] +stm32wle4jc = ["stm32-metapac/stm32wle4jc"] +stm32wle5c8 = ["stm32-metapac/stm32wle5c8"] +stm32wle5cb = ["stm32-metapac/stm32wle5cb"] +stm32wle5cc = ["stm32-metapac/stm32wle5cc"] +stm32wle5j8 = ["stm32-metapac/stm32wle5j8"] +stm32wle5jb = ["stm32-metapac/stm32wle5jb"] +stm32wle5jc = ["stm32-metapac/stm32wle5jc"] diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 43105c1d1..a54cf8faf 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,6 +1,8 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use crate::ipcc::sealed::Instance; use crate::peripherals::IPCC; +use crate::rcc::sealed::RccPeripheral; #[non_exhaustive] #[derive(Clone, Copy, Default)] From 9d610c68663d806601bbe8855e63a52e8e610d14 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Thu, 27 Apr 2023 11:05:33 -0500 Subject: [PATCH 0962/1575] Remove legacy LoRa drivers. --- README.md | 2 +- embassy-lora/Cargo.toml | 7 +- embassy-lora/src/lib.rs | 15 +- embassy-lora/src/stm32wl/mod.rs | 291 ----- embassy-lora/src/sx126x/mod.rs | 137 --- .../src/sx126x/sx126x_lora/board_specific.rs | 256 ---- embassy-lora/src/sx126x/sx126x_lora/mod.rs | 732 ------------ .../src/sx126x/sx126x_lora/mod_params.rs | 469 -------- .../src/sx126x/sx126x_lora/subroutine.rs | 674 ----------- embassy-lora/src/sx127x/mod.rs | 192 --- embassy-lora/src/sx127x/sx127x_lora/mod.rs | 539 --------- .../src/sx127x/sx127x_lora/register.rs | 107 -- embassy-stm32/src/lib.rs | 3 - embassy-stm32/src/subghz/bit_sync.rs | 160 --- embassy-stm32/src/subghz/cad_params.rs | 230 ---- embassy-stm32/src/subghz/calibrate.rs | 122 -- embassy-stm32/src/subghz/fallback_mode.rs | 37 - embassy-stm32/src/subghz/hse_trim.rs | 107 -- embassy-stm32/src/subghz/irq.rs | 292 ----- embassy-stm32/src/subghz/lora_sync_word.rs | 20 - embassy-stm32/src/subghz/mod.rs | 1004 ---------------- embassy-stm32/src/subghz/mod_params.rs | 1045 ----------------- embassy-stm32/src/subghz/ocp.rs | 14 - embassy-stm32/src/subghz/op_error.rs | 48 - embassy-stm32/src/subghz/pa_config.rs | 196 ---- embassy-stm32/src/subghz/packet_params.rs | 534 --------- embassy-stm32/src/subghz/packet_status.rs | 282 ----- embassy-stm32/src/subghz/packet_type.rs | 44 - embassy-stm32/src/subghz/pkt_ctrl.rs | 247 ---- embassy-stm32/src/subghz/pmode.rs | 27 - embassy-stm32/src/subghz/pwr_ctrl.rs | 160 --- embassy-stm32/src/subghz/reg_mode.rs | 18 - embassy-stm32/src/subghz/rf_frequency.rs | 135 --- embassy-stm32/src/subghz/rx_timeout_stop.rs | 21 - embassy-stm32/src/subghz/sleep_cfg.rs | 107 -- embassy-stm32/src/subghz/smps.rs | 45 - embassy-stm32/src/subghz/standby_clk.rs | 20 - embassy-stm32/src/subghz/stats.rs | 184 --- embassy-stm32/src/subghz/status.rs | 197 ---- embassy-stm32/src/subghz/tcxo_mode.rs | 170 --- embassy-stm32/src/subghz/timeout.rs | 492 -------- embassy-stm32/src/subghz/tx_params.rs | 192 --- embassy-stm32/src/subghz/value_error.rs | 129 -- examples/nrf52840/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- 45 files changed, 6 insertions(+), 9701 deletions(-) delete mode 100644 embassy-lora/src/stm32wl/mod.rs delete mode 100644 embassy-lora/src/sx126x/mod.rs delete mode 100644 embassy-lora/src/sx126x/sx126x_lora/board_specific.rs delete mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod.rs delete mode 100644 embassy-lora/src/sx126x/sx126x_lora/mod_params.rs delete mode 100644 embassy-lora/src/sx126x/sx126x_lora/subroutine.rs delete mode 100644 embassy-lora/src/sx127x/mod.rs delete mode 100644 embassy-lora/src/sx127x/sx127x_lora/mod.rs delete mode 100644 embassy-lora/src/sx127x/sx127x_lora/register.rs delete mode 100644 embassy-stm32/src/subghz/bit_sync.rs delete mode 100644 embassy-stm32/src/subghz/cad_params.rs delete mode 100644 embassy-stm32/src/subghz/calibrate.rs delete mode 100644 embassy-stm32/src/subghz/fallback_mode.rs delete mode 100644 embassy-stm32/src/subghz/hse_trim.rs delete mode 100644 embassy-stm32/src/subghz/irq.rs delete mode 100644 embassy-stm32/src/subghz/lora_sync_word.rs delete mode 100644 embassy-stm32/src/subghz/mod.rs delete mode 100644 embassy-stm32/src/subghz/mod_params.rs delete mode 100644 embassy-stm32/src/subghz/ocp.rs delete mode 100644 embassy-stm32/src/subghz/op_error.rs delete mode 100644 embassy-stm32/src/subghz/pa_config.rs delete mode 100644 embassy-stm32/src/subghz/packet_params.rs delete mode 100644 embassy-stm32/src/subghz/packet_status.rs delete mode 100644 embassy-stm32/src/subghz/packet_type.rs delete mode 100644 embassy-stm32/src/subghz/pkt_ctrl.rs delete mode 100644 embassy-stm32/src/subghz/pmode.rs delete mode 100644 embassy-stm32/src/subghz/pwr_ctrl.rs delete mode 100644 embassy-stm32/src/subghz/reg_mode.rs delete mode 100644 embassy-stm32/src/subghz/rf_frequency.rs delete mode 100644 embassy-stm32/src/subghz/rx_timeout_stop.rs delete mode 100644 embassy-stm32/src/subghz/sleep_cfg.rs delete mode 100644 embassy-stm32/src/subghz/smps.rs delete mode 100644 embassy-stm32/src/subghz/standby_clk.rs delete mode 100644 embassy-stm32/src/subghz/stats.rs delete mode 100644 embassy-stm32/src/subghz/status.rs delete mode 100644 embassy-stm32/src/subghz/tcxo_mode.rs delete mode 100644 embassy-stm32/src/subghz/timeout.rs delete mode 100644 embassy-stm32/src/subghz/tx_params.rs delete mode 100644 embassy-stm32/src/subghz/value_error.rs diff --git a/README.md b/README.md index 270f27a8f..b65dcbe15 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The embassy-net network stac The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. - **LoRa** - -embassy-lora supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers. +embassy-lora supports LoRa networking. - **USB** - embassy-usb implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own. diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 692a82040..aa0d2911d 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -9,19 +9,15 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/em src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" features = ["time", "defmt"] flavors = [ - { name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] }, - { name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x"] }, { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, ] [lib] [features] -sx126x = [] -sx127x = [] stm32wl = ["dep:embassy-stm32"] time = [] -defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"] +defmt = ["dep:defmt", "lorawan-device/defmt"] external-lora-phy = ["dep:lora-phy"] [dependencies] @@ -41,4 +37,3 @@ bit_field = { version = "0.10" } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.3", default-features = false } diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index c01d3f71a..c391a8029 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,24 +1,13 @@ #![no_std] #![feature(async_fn_in_trait, impl_trait_projections)] #![allow(incomplete_features)] -//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device -//! crate's async LoRaWAN MAC implementation. +//! embassy-lora holds LoRa-specific functionality. pub(crate) mod fmt; #[cfg(feature = "external-lora-phy")] -/// interface variants required by the external lora crate +/// interface variants required by the external lora physical layer crate (lora-phy) pub mod iv; -#[cfg(feature = "stm32wl")] -#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] -pub mod stm32wl; -#[cfg(feature = "sx126x")] -#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] -pub mod sx126x; -#[cfg(feature = "sx127x")] -#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] -pub mod sx127x; - #[cfg(feature = "time")] use embassy_time::{Duration, Instant, Timer}; diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs deleted file mode 100644 index dae9a195c..000000000 --- a/embassy-lora/src/stm32wl/mod.rs +++ /dev/null @@ -1,291 +0,0 @@ -//! A radio driver integration for the radio found on STM32WL family devices. -#![allow(deprecated)] -use core::future::poll_fn; -use core::task::Poll; - -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; -use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO}; -use embassy_stm32::subghz::{ - CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams, - LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk, - Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, -}; -use embassy_sync::waitqueue::AtomicWaker; -use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; -use lorawan_device::async_device::Timings; - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum State { - Idle, - Txing, - Rxing, -} - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct RadioError; - -static IRQ_WAKER: AtomicWaker = AtomicWaker::new(); - -/// The radio peripheral keeping the radio state and owning the radio IRQ. -pub struct SubGhzRadio<'d, RS> { - radio: SubGhz<'d, NoDma, NoDma>, - switch: RS, - irq: PeripheralRef<'d, SUBGHZ_RADIO>, -} - -#[derive(Default)] -#[non_exhaustive] -pub struct SubGhzRadioConfig { - pub reg_mode: RegMode, - pub calibrate_image: CalibrateImage, - pub pa_config: PaConfig, - pub tx_params: TxParams, -} - -impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> { - /// Create a new instance of a SubGhz radio for LoRaWAN. - pub fn new( - mut radio: SubGhz<'d, NoDma, NoDma>, - switch: RS, - irq: impl Peripheral

+ 'd, - config: SubGhzRadioConfig, - ) -> Result { - into_ref!(irq); - - radio.reset(); - - irq.disable(); - irq.set_handler(|_| { - IRQ_WAKER.wake(); - unsafe { SUBGHZ_RADIO::steal().disable() }; - }); - - configure_radio(&mut radio, config)?; - - Ok(Self { radio, switch, irq }) - } - - /// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form - /// the upcoming RX window start. - async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - trace!("TX request: {:?}", config); - self.switch.set_tx(); - - self.radio - .set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?; - - self.set_lora_mod_params(config.rf)?; - - let packet_params = LoRaPacketParams::new() - .set_preamble_len(8) - .set_header_type(HeaderType::Variable) - .set_payload_len(buf.len() as u8) - .set_crc_en(true) - .set_invert_iq(false); - - self.radio.set_lora_packet_params(&packet_params)?; - - let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout); - self.radio.set_irq_cfg(&irq_cfg)?; - - self.radio.set_buffer_base_address(0, 0)?; - self.radio.write_buffer(0, buf)?; - - // The maximum airtime for any LoRaWAN package is 2793.5ms. - // The value of 4000ms is copied from C driver and gives us a good safety margin. - self.radio.set_tx(Timeout::from_millis_sat(4000))?; - trace!("TX started"); - - loop { - let (_status, irq_status) = self.irq_wait().await; - - if irq_status & Irq::TxDone.mask() != 0 { - trace!("TX done"); - return Ok(0); - } - - if irq_status & Irq::Timeout.mask() != 0 { - return Err(RadioError); - } - } - } - - fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> { - let mod_params = LoRaModParams::new() - .set_sf(convert_spreading_factor(&config.spreading_factor)) - .set_bw(convert_bandwidth(&config.bandwidth)) - .set_cr(CodingRate::Cr45) - .set_ldro_en(matches!( - (config.spreading_factor, config.bandwidth), - (SpreadingFactor::_12, Bandwidth::_125KHz) - | (SpreadingFactor::_12, Bandwidth::_250KHz) - | (SpreadingFactor::_11, Bandwidth::_125KHz) - )); - self.radio.set_lora_mod_params(&mod_params) - } - - /// Perform a radio receive operation with the radio config and receive buffer. The receive buffer must - /// be able to hold a single LoRaWAN packet. - async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> { - assert!(buf.len() >= 255); - trace!("RX request: {:?}", config); - self.switch.set_rx(); - - self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?; - - self.set_lora_mod_params(config)?; - - let packet_params = LoRaPacketParams::new() - .set_preamble_len(8) - .set_header_type(HeaderType::Variable) - .set_payload_len(0xFF) - .set_crc_en(false) - .set_invert_iq(true); - self.radio.set_lora_packet_params(&packet_params)?; - - let irq_cfg = CfgIrq::new() - .irq_enable_all(Irq::RxDone) - .irq_enable_all(Irq::PreambleDetected) - .irq_enable_all(Irq::HeaderValid) - .irq_enable_all(Irq::HeaderErr) - .irq_enable_all(Irq::Err) - .irq_enable_all(Irq::Timeout); - self.radio.set_irq_cfg(&irq_cfg)?; - - self.radio.set_buffer_base_address(0, 0)?; - - // NOTE: Upper layer handles timeout by cancelling the future - self.radio.set_rx(Timeout::DISABLED)?; - - trace!("RX started"); - - loop { - let (_status, irq_status) = self.irq_wait().await; - - if irq_status & Irq::RxDone.mask() != 0 { - let (_status, len, ptr) = self.radio.rx_buffer_status()?; - let packet_status = self.radio.lora_packet_status()?; - let rssi = packet_status.rssi_pkt().to_integer(); - let snr = packet_status.snr_pkt().to_integer(); - self.radio.read_buffer(ptr, &mut buf[..len as usize])?; - self.radio.set_standby(StandbyClk::Rc)?; - - #[cfg(feature = "defmt")] - trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]); - - #[cfg(feature = "log")] - trace!("RX done: {:02x?}", &mut buf[..len as usize]); - return Ok((len as usize, RxQuality::new(rssi, snr as i8))); - } - - if irq_status & Irq::Timeout.mask() != 0 { - return Err(RadioError); - } - } - } - - async fn irq_wait(&mut self) -> (Status, u16) { - poll_fn(|cx| { - self.irq.unpend(); - self.irq.enable(); - IRQ_WAKER.register(cx.waker()); - - let (status, irq_status) = self.radio.irq_status().expect("error getting irq status"); - self.radio - .clear_irq_status(irq_status) - .expect("error clearing irq status"); - - trace!("SUGHZ IRQ 0b{:016b}, {:?}", irq_status, status); - - if irq_status == 0 { - Poll::Pending - } else { - Poll::Ready((status, irq_status)) - } - }) - .await - } -} - -fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> { - trace!("Configuring STM32WL SUBGHZ radio"); - - radio.set_regulator_mode(config.reg_mode)?; - radio.set_standby(StandbyClk::Rc)?; - - let tcxo_mode = TcxoMode::new() - .set_txco_trim(TcxoTrim::Volts1pt7) - .set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100))); - radio.set_tcxo_mode(&tcxo_mode)?; - // Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control". - // The STM32CUBE C driver also does this. - radio.set_hse_in_trim(HseTrim::MIN)?; - - // Re-calibrate everything after setting the TXCO config. - radio.calibrate(0x7F)?; - radio.calibrate_image(config.calibrate_image)?; - - radio.set_pa_config(&config.pa_config)?; - radio.set_tx_params(&config.tx_params)?; - radio.set_pa_ocp(Ocp::Max140m)?; - - radio.set_packet_type(PacketType::LoRa)?; - radio.set_lora_sync_word(LoRaSyncWord::Public)?; - - trace!("Done initializing STM32WL SUBGHZ radio"); - Ok(()) -} - -impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { - type PhyError = RadioError; - - async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - self.do_tx(config, buf).await - } - - async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { - self.do_rx(config, buf).await - } -} - -impl From for RadioError { - fn from(_: embassy_stm32::spi::Error) -> Self { - RadioError - } -} - -impl<'d, RS> Timings for SubGhzRadio<'d, RS> { - fn get_rx_window_offset_ms(&self) -> i32 { - -3 - } - fn get_rx_window_duration_ms(&self) -> u32 { - 1003 - } -} - -pub trait RadioSwitch { - fn set_rx(&mut self); - fn set_tx(&mut self); -} - -fn convert_spreading_factor(sf: &SpreadingFactor) -> SF { - match sf { - SpreadingFactor::_7 => SF::Sf7, - SpreadingFactor::_8 => SF::Sf8, - SpreadingFactor::_9 => SF::Sf9, - SpreadingFactor::_10 => SF::Sf10, - SpreadingFactor::_11 => SF::Sf11, - SpreadingFactor::_12 => SF::Sf12, - } -} - -fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth { - match bw { - Bandwidth::_125KHz => LoRaBandwidth::Bw125, - Bandwidth::_250KHz => LoRaBandwidth::Bw250, - Bandwidth::_500KHz => LoRaBandwidth::Bw500, - } -} diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs deleted file mode 100644 index 2f0b8c8e3..000000000 --- a/embassy-lora/src/sx126x/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -use defmt::Format; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::*; -use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig}; -use lorawan_device::async_device::Timings; - -mod sx126x_lora; -use sx126x_lora::LoRa; - -use self::sx126x_lora::mod_params::RadioError; - -/// Semtech Sx126x LoRa peripheral -pub struct Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - pub lora: LoRa, -} - -impl Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - pub async fn new( - spi: SPI, - cs: CTRL, - reset: CTRL, - antenna_rx: CTRL, - antenna_tx: CTRL, - dio1: WAIT, - busy: WAIT, - enable_public_network: bool, - ) -> Result> { - let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy); - lora.init().await?; - lora.set_lora_modem(enable_public_network).await?; - Ok(Self { lora }) - } -} - -impl Timings for Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - fn get_rx_window_offset_ms(&self) -> i32 { - -50 - } - fn get_rx_window_duration_ms(&self) -> u32 { - 1050 - } -} - -impl PhyRxTx for Sx126xRadio -where - SPI: SpiBus + 'static, - CTRL: OutputPin + 'static, - WAIT: Wait + 'static, - BUS: Error + Format + 'static, -{ - type PhyError = RadioError; - - async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result { - trace!("TX START"); - self.lora - .set_tx_config( - config.pw, - config.rf.spreading_factor.into(), - config.rf.bandwidth.into(), - config.rf.coding_rate.into(), - 8, - false, - true, - false, - 0, - false, - ) - .await?; - self.lora.set_max_payload_length(buffer.len() as u8).await?; - self.lora.set_channel(config.rf.frequency).await?; - self.lora.send(buffer, 0xffffff).await?; - self.lora.process_irq(None, None, None).await?; - trace!("TX DONE"); - return Ok(0); - } - - async fn rx( - &mut self, - config: RfConfig, - receiving_buffer: &mut [u8], - ) -> Result<(usize, RxQuality), Self::PhyError> { - trace!("RX START"); - self.lora - .set_rx_config( - config.spreading_factor.into(), - config.bandwidth.into(), - config.coding_rate.into(), - 8, - 4, - false, - 0u8, - true, - false, - 0, - true, - true, - ) - .await?; - self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; - self.lora.set_channel(config.frequency).await?; - self.lora.rx(90 * 1000).await?; - let mut received_len = 0u8; - self.lora - .process_irq(Some(receiving_buffer), Some(&mut received_len), None) - .await?; - trace!("RX DONE"); - - let packet_status = self.lora.get_latest_packet_status(); - let mut rssi = 0i16; - let mut snr = 0i8; - if packet_status.is_some() { - rssi = packet_status.unwrap().rssi as i16; - snr = packet_status.unwrap().snr; - } - - Ok((received_len as usize, RxQuality::new(rssi, snr))) - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs b/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs deleted file mode 100644 index a7b9e1486..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/board_specific.rs +++ /dev/null @@ -1,256 +0,0 @@ -use embassy_time::{Duration, Timer}; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::SpiBus; - -use super::mod_params::RadioError::*; -use super::mod_params::*; -use super::LoRa; - -// Defines the time required for the TCXO to wakeup [ms]. -const BRD_TCXO_WAKEUP_TIME: u32 = 10; - -// Provides board-specific functionality for Semtech SX126x-based boards. - -impl LoRa -where - SPI: SpiBus, - CTRL: OutputPin, - WAIT: Wait, -{ - // De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes. - pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently - } - - // Initialize the TCXO power pin - pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError> { - let timeout = self.brd_get_board_tcxo_wakeup_time() << 6; - self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout) - .await?; - Ok(()) - } - - // Initialize RF switch control pins - pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError> { - self.sub_set_dio2_as_rf_switch_ctrl(true).await?; - Ok(()) - } - - // Initialize the radio debug pins - pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError> { - Ok(()) // no operation currently - } - - // Hardware reset of the radio - pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError> { - Timer::after(Duration::from_millis(10)).await; - self.reset.set_low().map_err(|_| Reset)?; - Timer::after(Duration::from_millis(20)).await; - self.reset.set_high().map_err(|_| Reset)?; - Timer::after(Duration::from_millis(10)).await; - Ok(()) - } - - // Wait while the busy pin is high - pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError> { - self.busy.wait_for_low().await.map_err(|_| Busy)?; - Ok(()) - } - - // Wake up the radio - pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError> { - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?; - self.spi.write(&[0x00]).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - self.brd_set_operating_mode(RadioMode::StandbyRC); - Ok(()) - } - - // Send a command that writes data to the radio - pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError> { - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[op_code.value()]).await.map_err(SPI)?; - self.spi.write(buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - if op_code != OpCode::SetSleep { - self.brd_wait_on_busy().await?; - } - Ok(()) - } - - // Send a command that reads data from the radio, filling the provided buffer and returning a status - pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result> { - let mut status = [0u8]; - let mut input = [0u8]; - - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[op_code.value()]).await.map_err(SPI)?; - self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?; - for i in 0..buffer.len() { - self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; - buffer[i] = input[0]; - } - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - - Ok(status[0]) - } - - // Write one or more bytes of data to the radio memory - pub(super) async fn brd_write_registers( - &mut self, - start_register: Register, - buffer: &[u8], - ) -> Result<(), RadioError> { - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?; - self.spi - .write(&[ - ((start_register.addr() & 0xFF00) >> 8) as u8, - (start_register.addr() & 0x00FF) as u8, - ]) - .await - .map_err(SPI)?; - self.spi.write(buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Read one or more bytes of data from the radio memory - pub(super) async fn brd_read_registers( - &mut self, - start_register: Register, - buffer: &mut [u8], - ) -> Result<(), RadioError> { - let mut input = [0u8]; - - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?; - self.spi - .write(&[ - ((start_register.addr() & 0xFF00) >> 8) as u8, - (start_register.addr() & 0x00FF) as u8, - 0x00u8, - ]) - .await - .map_err(SPI)?; - for i in 0..buffer.len() { - self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; - buffer[i] = input[0]; - } - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Write data to the buffer holding the payload in the radio - pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError> { - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?; - self.spi.write(&[offset]).await.map_err(SPI)?; - self.spi.write(buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Read data from the buffer holding the payload in the radio - pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError> { - let mut input = [0u8]; - - self.sub_check_device_ready().await?; - - self.cs.set_low().map_err(|_| CS)?; - self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?; - self.spi.write(&[offset]).await.map_err(SPI)?; - self.spi.write(&[0x00]).await.map_err(SPI)?; - for i in 0..buffer.len() { - self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?; - buffer[i] = input[0]; - } - self.cs.set_high().map_err(|_| CS)?; - - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Set the radio output power - pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError> { - self.sub_set_tx_params(power, RampTime::Ramp40Us).await?; - Ok(()) - } - - // Get the radio type - pub(super) fn brd_get_radio_type(&mut self) -> RadioType { - RadioType::SX1262 - } - - // Quiesce the antenna(s). - pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError> { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_low().map_err(|_| AntRx)?; - Ok(()) - } - - // Prepare the antenna(s) for a receive operation - pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError> { - self.antenna_tx.set_low().map_err(|_| AntTx)?; - self.antenna_rx.set_high().map_err(|_| AntRx)?; - Ok(()) - } - - // Prepare the antenna(s) for a send operation - pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError> { - self.antenna_rx.set_low().map_err(|_| AntRx)?; - self.antenna_tx.set_high().map_err(|_| AntTx)?; - Ok(()) - } - - // Check if the given RF frequency is supported by the hardware - pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result> { - Ok(true) - } - - // Get the duration required for the TCXO to wakeup [ms]. - pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 { - BRD_TCXO_WAKEUP_TIME - } - - /* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process - pub(super) async fn brd_get_dio1_pin_state( - &mut self, - ) -> Result> { - Ok(0) - } - */ - - // Get the current radio operatiing mode - pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode { - self.operating_mode - } - - // Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts. - pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) { - self.operating_mode = mode; - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod.rs b/embassy-lora/src/sx126x/sx126x_lora/mod.rs deleted file mode 100644 index 280f26d51..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/mod.rs +++ /dev/null @@ -1,732 +0,0 @@ -#![allow(dead_code)] - -use embassy_time::{Duration, Timer}; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::SpiBus; - -mod board_specific; -pub mod mod_params; -mod subroutine; - -use mod_params::RadioError::*; -use mod_params::*; - -// Syncwords for public and private networks -const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444; -const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424; - -// Maximum number of registers that can be added to the retention list -const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4; - -// Possible LoRa bandwidths -const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz]; - -// Radio complete wakeup time with margin for temperature compensation [ms] -const RADIO_WAKEUP_TIME: u32 = 3; - -/// Provides high-level access to Semtech SX126x-based boards -pub struct LoRa { - spi: SPI, - cs: CTRL, - reset: CTRL, - antenna_rx: CTRL, - antenna_tx: CTRL, - dio1: WAIT, - busy: WAIT, - operating_mode: RadioMode, - rx_continuous: bool, - max_payload_length: u8, - modulation_params: Option, - packet_type: PacketType, - packet_params: Option, - packet_status: Option, - image_calibrated: bool, - frequency_error: u32, -} - -impl LoRa -where - SPI: SpiBus, - CTRL: OutputPin, - WAIT: Wait, -{ - /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time () - pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self { - Self { - spi, - cs, - reset, - antenna_rx, - antenna_tx, - dio1, - busy, - operating_mode: RadioMode::Sleep, - rx_continuous: false, - max_payload_length: 0xFFu8, - modulation_params: None, - packet_type: PacketType::LoRa, - packet_params: None, - packet_status: None, - image_calibrated: false, - frequency_error: 0u32, // where is volatile FrequencyError modified ??? - } - } - - /// Initialize the radio - pub async fn init(&mut self) -> Result<(), RadioError> { - self.sub_init().await?; - self.sub_set_standby(StandbyMode::RC).await?; - self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?; - self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?; - self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?; - self.sub_set_dio_irq_params( - IrqMask::All.value(), - IrqMask::All.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - self.add_register_to_retention_list(Register::RxGain.addr()).await?; - self.add_register_to_retention_list(Register::TxModulation.addr()) - .await?; - Ok(()) - } - - /// Return current radio state - pub fn get_status(&mut self) -> RadioState { - match self.brd_get_operating_mode() { - RadioMode::Transmit => RadioState::TxRunning, - RadioMode::Receive => RadioState::RxRunning, - RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting, - _ => RadioState::Idle, - } - } - - /// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired) - pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError> { - self.sub_set_packet_type(PacketType::LoRa).await?; - if enable_public_network { - self.brd_write_registers( - Register::LoRaSyncword, - &[ - ((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8, - (LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8, - ], - ) - .await?; - } else { - self.brd_write_registers( - Register::LoRaSyncword, - &[ - ((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8, - (LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8, - ], - ) - .await?; - } - - Ok(()) - } - - /// Sets the channel frequency - pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError> { - self.sub_set_rf_frequency(frequency).await?; - Ok(()) - } - - /* Checks if the channel is free for the given time. This is currently not implemented until a substitute - for switching to the FSK modem is found. - - pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool; - */ - - /// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand. - /// After calling this function either set_rx_config() or set_tx_config() must be called. - pub async fn get_random_value(&mut self) -> Result> { - self.sub_set_dio_irq_params( - IrqMask::None.value(), - IrqMask::None.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - let result = self.sub_get_random().await?; - Ok(result) - } - - /// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand. - /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] - /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] - /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] - /// preamble_length length in symbols (the hardware adds 4 more symbols) - /// symb_timeout RxSingle timeout value in symbols - /// fixed_len fixed length packets [0: variable, 1: fixed] - /// payload_len payload length when fixed length is used - /// crc_on [0: OFF, 1: ON] - /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] - /// hop_period number of symbols between each hop - /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] - /// rx_continuous reception mode [false: single mode, true: continuous mode] - pub async fn set_rx_config( - &mut self, - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - symb_timeout: u16, - fixed_len: bool, - payload_len: u8, - crc_on: bool, - _freq_hop_on: bool, - _hop_period: u8, - iq_inverted: bool, - rx_continuous: bool, - ) -> Result<(), RadioError> { - let mut symb_timeout_final = symb_timeout; - - self.rx_continuous = rx_continuous; - if self.rx_continuous { - symb_timeout_final = 0; - } - if fixed_len { - self.max_payload_length = payload_len; - } else { - self.max_payload_length = 0xFFu8; - } - - self.sub_set_stop_rx_timer_on_preamble_detect(false).await?; - - let mut low_data_rate_optimize = 0x00u8; - if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) - && (bandwidth == Bandwidth::_125KHz)) - || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) - { - low_data_rate_optimize = 0x01u8; - } - - let modulation_params = ModulationParams { - spreading_factor: spreading_factor, - bandwidth: bandwidth, - coding_rate: coding_rate, - low_data_rate_optimize: low_data_rate_optimize, - }; - - let mut preamble_length_final = preamble_length; - if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) - && (preamble_length < 12) - { - preamble_length_final = 12; - } - - let packet_params = PacketParams { - preamble_length: preamble_length_final, - implicit_header: fixed_len, - payload_length: self.max_payload_length, - crc_on: crc_on, - iq_inverted: iq_inverted, - }; - - self.modulation_params = Some(modulation_params); - self.packet_params = Some(packet_params); - - self.standby().await?; - self.sub_set_modulation_params().await?; - self.sub_set_packet_params().await?; - self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?; - - // Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) - let mut iq_polarity = [0x00u8]; - self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?; - if iq_inverted { - self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))]) - .await?; - } else { - self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)]) - .await?; - } - Ok(()) - } - - /// Set the transmission parameters for the LoRa modem (only). - /// power output power [dBm] - /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] - /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] - /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] - /// preamble_length length in symbols (the hardware adds 4 more symbols) - /// fixed_len fixed length packets [0: variable, 1: fixed] - /// crc_on [0: OFF, 1: ON] - /// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON] - /// hop_period number of symbols between each hop - /// iq_inverted invert IQ signals [0: not inverted, 1: inverted] - pub async fn set_tx_config( - &mut self, - power: i8, - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - fixed_len: bool, - crc_on: bool, - _freq_hop_on: bool, - _hop_period: u8, - iq_inverted: bool, - ) -> Result<(), RadioError> { - let mut low_data_rate_optimize = 0x00u8; - if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) - && (bandwidth == Bandwidth::_125KHz)) - || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) - { - low_data_rate_optimize = 0x01u8; - } - - let modulation_params = ModulationParams { - spreading_factor: spreading_factor, - bandwidth: bandwidth, - coding_rate: coding_rate, - low_data_rate_optimize: low_data_rate_optimize, - }; - - let mut preamble_length_final = preamble_length; - if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) - && (preamble_length < 12) - { - preamble_length_final = 12; - } - - let packet_params = PacketParams { - preamble_length: preamble_length_final, - implicit_header: fixed_len, - payload_length: self.max_payload_length, - crc_on: crc_on, - iq_inverted: iq_inverted, - }; - - self.modulation_params = Some(modulation_params); - self.packet_params = Some(packet_params); - - self.standby().await?; - self.sub_set_modulation_params().await?; - self.sub_set_packet_params().await?; - - // Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) - - let mut tx_modulation = [0x00u8]; - self.brd_read_registers(Register::TxModulation, &mut tx_modulation) - .await?; - if bandwidth == Bandwidth::_500KHz { - self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))]) - .await?; - } else { - self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)]) - .await?; - } - - self.brd_set_rf_tx_power(power).await?; - Ok(()) - } - - /// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported] - pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result> { - Ok(self.brd_check_rf_frequency(frequency).await?) - } - - /// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called) - /// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol] - /// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] - /// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] - /// preamble_length length in symbols (the hardware adds 4 more symbols) - /// fixed_len fixed length packets [0: variable, 1: fixed] - /// payload_len sets payload length when fixed length is used - /// crc_on [0: OFF, 1: ON] - pub fn get_time_on_air( - &mut self, - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - fixed_len: bool, - payload_len: u8, - crc_on: bool, - ) -> Result> { - let numerator = 1000 - * Self::get_lora_time_on_air_numerator( - spreading_factor, - bandwidth, - coding_rate, - preamble_length, - fixed_len, - payload_len, - crc_on, - ); - let denominator = bandwidth.value_in_hz(); - if denominator == 0 { - Err(RadioError::InvalidBandwidth) - } else { - Ok((numerator + denominator - 1) / denominator) - } - } - - /// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms] - pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError> { - if self.packet_params.is_some() { - self.sub_set_dio_irq_params( - IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), - IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - let mut packet_params = self.packet_params.as_mut().unwrap(); - packet_params.payload_length = buffer.len() as u8; - self.sub_set_packet_params().await?; - self.sub_send_payload(buffer, timeout).await?; - Ok(()) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - /// Set the radio in sleep mode - pub async fn sleep(&mut self) -> Result<(), RadioError> { - self.sub_set_sleep(SleepParams { - wakeup_rtc: false, - reset: false, - warm_start: true, - }) - .await?; - Timer::after(Duration::from_millis(2)).await; - Ok(()) - } - - /// Set the radio in standby mode - pub async fn standby(&mut self) -> Result<(), RadioError> { - self.sub_set_standby(StandbyMode::RC).await?; - Ok(()) - } - - /// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)] - pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError> { - self.sub_set_dio_irq_params( - IrqMask::All.value(), - IrqMask::All.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - if self.rx_continuous { - self.sub_set_rx(0xFFFFFF).await?; - } else { - self.sub_set_rx(timeout << 6).await?; - } - - Ok(()) - } - - /// Start a Channel Activity Detection - pub async fn start_cad(&mut self) -> Result<(), RadioError> { - self.sub_set_dio_irq_params( - IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), - IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - self.sub_set_cad().await?; - Ok(()) - } - - /// Sets the radio in continuous wave transmission mode - /// frequency channel RF frequency - /// power output power [dBm] - /// timeout transmission mode timeout [s] - pub async fn set_tx_continuous_wave( - &mut self, - frequency: u32, - power: i8, - _timeout: u16, - ) -> Result<(), RadioError> { - self.sub_set_rf_frequency(frequency).await?; - self.brd_set_rf_tx_power(power).await?; - self.sub_set_tx_continuous_wave().await?; - - Ok(()) - } - - /// Read the current RSSI value for the LoRa modem (only) [dBm] - pub async fn get_rssi(&mut self) -> Result> { - let value = self.sub_get_rssi_inst().await?; - Ok(value as i16) - } - - /// Write one or more radio registers with a buffer of a given size, starting at the first register address - pub async fn write_registers_from_buffer( - &mut self, - start_register: Register, - buffer: &[u8], - ) -> Result<(), RadioError> { - self.brd_write_registers(start_register, buffer).await?; - Ok(()) - } - - /// Read one or more radio registers into a buffer of a given size, starting at the first register address - pub async fn read_registers_into_buffer( - &mut self, - start_register: Register, - buffer: &mut [u8], - ) -> Result<(), RadioError> { - self.brd_read_registers(start_register, buffer).await?; - Ok(()) - } - - /// Set the maximum payload length (in bytes) for a LoRa modem (only). - pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError> { - if self.packet_params.is_some() { - let packet_params = self.packet_params.as_mut().unwrap(); - self.max_payload_length = max; - packet_params.payload_length = max; - self.sub_set_packet_params().await?; - Ok(()) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - /// Get the time required for the board plus radio to get out of sleep [ms] - pub fn get_wakeup_time(&mut self) -> u32 { - self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME - } - - /// Process the radio irq - pub async fn process_irq( - &mut self, - receiving_buffer: Option<&mut [u8]>, - received_len: Option<&mut u8>, - cad_activity_detected: Option<&mut bool>, - ) -> Result<(), RadioError> { - loop { - trace!("process_irq loop entered"); - - let de = self.sub_get_device_errors().await?; - trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}", - de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp); - let st = self.sub_get_status().await?; - trace!( - "radio status: cmd_status: {:x}, chip_mode: {:x}", - st.cmd_status, - st.chip_mode - ); - - self.dio1.wait_for_high().await.map_err(|_| DIO1)?; - let operating_mode = self.brd_get_operating_mode(); - let irq_flags = self.sub_get_irq_status().await?; - self.sub_clear_irq_status(irq_flags).await?; - trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags); - - // check for errors and unexpected interrupt masks (based on operation mode) - if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() { - if !self.rx_continuous { - self.brd_set_operating_mode(RadioMode::StandbyRC); - } - return Err(RadioError::HeaderError); - } else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() { - if operating_mode == RadioMode::Receive { - if !self.rx_continuous { - self.brd_set_operating_mode(RadioMode::StandbyRC); - } - return Err(RadioError::CRCErrorOnReceive); - } else { - return Err(RadioError::CRCErrorUnexpected); - } - } else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() { - if operating_mode == RadioMode::Transmit { - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Err(RadioError::TransmitTimeout); - } else if operating_mode == RadioMode::Receive { - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Err(RadioError::ReceiveTimeout); - } else { - return Err(RadioError::TimeoutUnexpected); - } - } else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value()) - && (operating_mode != RadioMode::Transmit) - { - return Err(RadioError::TransmitDoneUnexpected); - } else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value()) - && (operating_mode != RadioMode::Receive) - { - return Err(RadioError::ReceiveDoneUnexpected); - } else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value()) - || ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value())) - && (operating_mode != RadioMode::ChannelActivityDetection) - { - return Err(RadioError::CADUnexpected); - } - - if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() { - trace!("HeaderValid"); - } else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() { - trace!("PreambleDetected"); - } else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() { - trace!("SyncwordValid"); - } - - // handle completions - if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() { - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Ok(()); - } else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() { - if !self.rx_continuous { - self.brd_set_operating_mode(RadioMode::StandbyRC); - - // implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3) - self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?; - let mut evt_clr = [0x00u8]; - self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?; - evt_clr[0] |= 1 << 1; - self.brd_write_registers(Register::EvtClr, &evt_clr).await?; - } - - if receiving_buffer.is_some() && received_len.is_some() { - *(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?; - } - self.packet_status = self.sub_get_packet_status().await?.into(); - return Ok(()); - } else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() { - if cad_activity_detected.is_some() { - *(cad_activity_detected.unwrap()) = - (irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value(); - } - self.brd_set_operating_mode(RadioMode::StandbyRC); - return Ok(()); - } - - // if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid - // are in that category), loop to wait again - } - } - - // SX126x-specific functions - - /// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms] - pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { - self.sub_set_dio_irq_params( - IrqMask::All.value(), - IrqMask::All.value(), - IrqMask::None.value(), - IrqMask::None.value(), - ) - .await?; - - if self.rx_continuous { - self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous - } else { - self.sub_set_rx_boosted(timeout << 6).await?; - } - - Ok(()) - } - - /// Set the Rx duty cycle management parameters (SX126x radios only) - /// rx_time structure describing reception timeout value - /// sleep_time structure describing sleep timeout value - pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { - self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?; - Ok(()) - } - - pub fn get_latest_packet_status(&mut self) -> Option { - self.packet_status - } - - // Utilities - - async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError> { - let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize]; - - // Read the address and registers already added to the list - self.brd_read_registers(Register::RetentionList, &mut buffer).await?; - - let number_of_registers = buffer[0]; - for i in 0..number_of_registers { - if register_address - == ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16) - { - return Ok(()); // register already in list - } - } - - if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION { - buffer[0] += 1; // increment number of registers - - buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8; - buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8; - self.brd_write_registers(Register::RetentionList, &buffer).await?; - - Ok(()) - } else { - Err(RadioError::RetentionListExceeded) - } - } - - fn get_lora_time_on_air_numerator( - spreading_factor: SpreadingFactor, - bandwidth: Bandwidth, - coding_rate: CodingRate, - preamble_length: u16, - fixed_len: bool, - payload_len: u8, - crc_on: bool, - ) -> u32 { - let cell_denominator; - let cr_denominator = (coding_rate.value() as i32) + 4; - - // Ensure that the preamble length is at least 12 symbols when using SF5 or SF6 - let mut preamble_length_final = preamble_length; - if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6)) - && (preamble_length < 12) - { - preamble_length_final = 12; - } - - let mut low_data_rate_optimize = false; - if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12)) - && (bandwidth == Bandwidth::_125KHz)) - || ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz)) - { - low_data_rate_optimize = true; - } - - let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 }) - - (4 * spreading_factor.value() as i32) - + (if fixed_len { 0 } else { 20 }); - - if spreading_factor.value() <= 6 { - cell_denominator = 4 * (spreading_factor.value() as i32); - } else { - cell_numerator += 8; - if low_data_rate_optimize { - cell_denominator = 4 * ((spreading_factor.value() as i32) - 2); - } else { - cell_denominator = 4 * (spreading_factor.value() as i32); - } - } - - if cell_numerator < 0 { - cell_numerator = 0; - } - - let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator) - + (preamble_length_final as i32) - + 12; - - if spreading_factor.value() <= 6 { - intermediate = intermediate + 2; - } - - (((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32 - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs b/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs deleted file mode 100644 index e270b2a09..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/mod_params.rs +++ /dev/null @@ -1,469 +0,0 @@ -use core::fmt::Debug; - -use lorawan_device::async_device::radio as device; - -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum RadioError { - SPI(BUS), - CS, - Reset, - AntRx, - AntTx, - Busy, - DIO1, - PayloadSizeMismatch(usize, usize), - RetentionListExceeded, - InvalidBandwidth, - ModulationParamsMissing, - PacketParamsMissing, - HeaderError, - CRCErrorUnexpected, - CRCErrorOnReceive, - TransmitTimeout, - ReceiveTimeout, - TimeoutUnexpected, - TransmitDoneUnexpected, - ReceiveDoneUnexpected, - CADUnexpected, -} - -pub struct RadioSystemError { - pub rc_64khz_calibration: bool, - pub rc_13mhz_calibration: bool, - pub pll_calibration: bool, - pub adc_calibration: bool, - pub image_calibration: bool, - pub xosc_start: bool, - pub pll_lock: bool, - pub pa_ramp: bool, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum PacketType { - GFSK = 0x00, - LoRa = 0x01, - None = 0x0F, -} - -impl PacketType { - pub const fn value(self) -> u8 { - self as u8 - } - pub fn to_enum(value: u8) -> Self { - if value == 0x00 { - PacketType::GFSK - } else if value == 0x01 { - PacketType::LoRa - } else { - PacketType::None - } - } -} - -#[derive(Clone, Copy)] -pub struct PacketStatus { - pub rssi: i8, - pub snr: i8, - pub signal_rssi: i8, - pub freq_error: u32, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum RadioType { - SX1261, - SX1262, -} - -#[derive(Clone, Copy, PartialEq)] -pub enum RadioMode { - Sleep = 0x00, // sleep mode - StandbyRC = 0x01, // standby mode with RC oscillator - StandbyXOSC = 0x02, // standby mode with XOSC oscillator - FrequencySynthesis = 0x03, // frequency synthesis mode - Transmit = 0x04, // transmit mode - Receive = 0x05, // receive mode - ReceiveDutyCycle = 0x06, // receive duty cycle mode - ChannelActivityDetection = 0x07, // channel activity detection mode -} - -impl RadioMode { - /// Returns the value of the mode. - pub const fn value(self) -> u8 { - self as u8 - } - pub fn to_enum(value: u8) -> Self { - if value == 0x00 { - RadioMode::Sleep - } else if value == 0x01 { - RadioMode::StandbyRC - } else if value == 0x02 { - RadioMode::StandbyXOSC - } else if value == 0x03 { - RadioMode::FrequencySynthesis - } else if value == 0x04 { - RadioMode::Transmit - } else if value == 0x05 { - RadioMode::Receive - } else if value == 0x06 { - RadioMode::ReceiveDutyCycle - } else if value == 0x07 { - RadioMode::ChannelActivityDetection - } else { - RadioMode::Sleep - } - } -} - -pub enum RadioState { - Idle = 0x00, - RxRunning = 0x01, - TxRunning = 0x02, - ChannelActivityDetecting = 0x03, -} - -impl RadioState { - /// Returns the value of the state. - pub fn value(self) -> u8 { - self as u8 - } -} - -pub struct RadioStatus { - pub cmd_status: u8, - pub chip_mode: u8, -} - -impl RadioStatus { - pub fn value(self) -> u8 { - (self.chip_mode << 4) | (self.cmd_status << 1) - } -} - -#[derive(Clone, Copy)] -pub enum IrqMask { - None = 0x0000, - TxDone = 0x0001, - RxDone = 0x0002, - PreambleDetected = 0x0004, - SyncwordValid = 0x0008, - HeaderValid = 0x0010, - HeaderError = 0x0020, - CRCError = 0x0040, - CADDone = 0x0080, - CADActivityDetected = 0x0100, - RxTxTimeout = 0x0200, - All = 0xFFFF, -} - -impl IrqMask { - pub fn value(self) -> u16 { - self as u16 - } -} - -#[derive(Clone, Copy)] -pub enum Register { - PacketParams = 0x0704, // packet configuration - PayloadLength = 0x0702, // payload size - SynchTimeout = 0x0706, // recalculated number of symbols - Syncword = 0x06C0, // Syncword values - LoRaSyncword = 0x0740, // LoRa Syncword value - GeneratedRandomNumber = 0x0819, //32-bit generated random number - AnaLNA = 0x08E2, // disable the LNA - AnaMixer = 0x08E5, // disable the mixer - RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted) - XTATrim = 0x0911, // device internal trimming capacitor - OCP = 0x08E7, // over current protection max value - RetentionList = 0x029F, // retention list - IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4) - TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1) - TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) - RTCCtrl = 0x0902, // RTC control - EvtClr = 0x0944, // event clear -} - -impl Register { - pub fn addr(self) -> u16 { - self as u16 - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum OpCode { - GetStatus = 0xC0, - WriteRegister = 0x0D, - ReadRegister = 0x1D, - WriteBuffer = 0x0E, - ReadBuffer = 0x1E, - SetSleep = 0x84, - SetStandby = 0x80, - SetFS = 0xC1, - SetTx = 0x83, - SetRx = 0x82, - SetRxDutyCycle = 0x94, - SetCAD = 0xC5, - SetTxContinuousWave = 0xD1, - SetTxContinuousPremable = 0xD2, - SetPacketType = 0x8A, - GetPacketType = 0x11, - SetRFFrequency = 0x86, - SetTxParams = 0x8E, - SetPAConfig = 0x95, - SetCADParams = 0x88, - SetBufferBaseAddress = 0x8F, - SetModulationParams = 0x8B, - SetPacketParams = 0x8C, - GetRxBufferStatus = 0x13, - GetPacketStatus = 0x14, - GetRSSIInst = 0x15, - GetStats = 0x10, - ResetStats = 0x00, - CfgDIOIrq = 0x08, - GetIrqStatus = 0x12, - ClrIrqStatus = 0x02, - Calibrate = 0x89, - CalibrateImage = 0x98, - SetRegulatorMode = 0x96, - GetErrors = 0x17, - ClrErrors = 0x07, - SetTCXOMode = 0x97, - SetTxFallbackMode = 0x93, - SetRFSwitchMode = 0x9D, - SetStopRxTimerOnPreamble = 0x9F, - SetLoRaSymbTimeout = 0xA0, -} - -impl OpCode { - pub fn value(self) -> u8 { - self as u8 - } -} - -pub struct SleepParams { - pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC - pub reset: bool, - pub warm_start: bool, -} - -impl SleepParams { - pub fn value(self) -> u8 { - ((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8) - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum StandbyMode { - RC = 0x00, - XOSC = 0x01, -} - -impl StandbyMode { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum RegulatorMode { - UseLDO = 0x00, - UseDCDC = 0x01, -} - -impl RegulatorMode { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub struct CalibrationParams { - pub rc64k_enable: bool, // calibrate RC64K clock - pub rc13m_enable: bool, // calibrate RC13M clock - pub pll_enable: bool, // calibrate PLL - pub adc_pulse_enable: bool, // calibrate ADC Pulse - pub adc_bulkn_enable: bool, // calibrate ADC bulkN - pub adc_bulkp_enable: bool, // calibrate ADC bulkP - pub img_enable: bool, -} - -impl CalibrationParams { - pub fn value(self) -> u8 { - ((self.img_enable as u8) << 6) - | ((self.adc_bulkp_enable as u8) << 5) - | ((self.adc_bulkn_enable as u8) << 4) - | ((self.adc_pulse_enable as u8) << 3) - | ((self.pll_enable as u8) << 2) - | ((self.rc13m_enable as u8) << 1) - | ((self.rc64k_enable as u8) << 0) - } -} - -#[derive(Clone, Copy)] -pub enum TcxoCtrlVoltage { - Ctrl1V6 = 0x00, - Ctrl1V7 = 0x01, - Ctrl1V8 = 0x02, - Ctrl2V2 = 0x03, - Ctrl2V4 = 0x04, - Ctrl2V7 = 0x05, - Ctrl3V0 = 0x06, - Ctrl3V3 = 0x07, -} - -impl TcxoCtrlVoltage { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum RampTime { - Ramp10Us = 0x00, - Ramp20Us = 0x01, - Ramp40Us = 0x02, - Ramp80Us = 0x03, - Ramp200Us = 0x04, - Ramp800Us = 0x05, - Ramp1700Us = 0x06, - Ramp3400Us = 0x07, -} - -impl RampTime { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum SpreadingFactor { - _5 = 0x05, - _6 = 0x06, - _7 = 0x07, - _8 = 0x08, - _9 = 0x09, - _10 = 0x0A, - _11 = 0x0B, - _12 = 0x0C, -} - -impl SpreadingFactor { - pub fn value(self) -> u8 { - self as u8 - } -} - -impl From for SpreadingFactor { - fn from(sf: device::SpreadingFactor) -> Self { - match sf { - device::SpreadingFactor::_7 => SpreadingFactor::_7, - device::SpreadingFactor::_8 => SpreadingFactor::_8, - device::SpreadingFactor::_9 => SpreadingFactor::_9, - device::SpreadingFactor::_10 => SpreadingFactor::_10, - device::SpreadingFactor::_11 => SpreadingFactor::_11, - device::SpreadingFactor::_12 => SpreadingFactor::_12, - } - } -} - -#[derive(Clone, Copy, PartialEq)] -pub enum Bandwidth { - _500KHz = 0x06, - _250KHz = 0x05, - _125KHz = 0x04, -} - -impl Bandwidth { - pub fn value(self) -> u8 { - self as u8 - } - - pub fn value_in_hz(self) -> u32 { - match self { - Bandwidth::_125KHz => 125000u32, - Bandwidth::_250KHz => 250000u32, - Bandwidth::_500KHz => 500000u32, - } - } -} - -impl From for Bandwidth { - fn from(bw: device::Bandwidth) -> Self { - match bw { - device::Bandwidth::_500KHz => Bandwidth::_500KHz, - device::Bandwidth::_250KHz => Bandwidth::_250KHz, - device::Bandwidth::_125KHz => Bandwidth::_125KHz, - } - } -} - -#[derive(Clone, Copy)] -pub enum CodingRate { - _4_5 = 0x01, - _4_6 = 0x02, - _4_7 = 0x03, - _4_8 = 0x04, -} - -impl CodingRate { - pub fn value(self) -> u8 { - self as u8 - } -} - -impl From for CodingRate { - fn from(cr: device::CodingRate) -> Self { - match cr { - device::CodingRate::_4_5 => CodingRate::_4_5, - device::CodingRate::_4_6 => CodingRate::_4_6, - device::CodingRate::_4_7 => CodingRate::_4_7, - device::CodingRate::_4_8 => CodingRate::_4_8, - } - } -} - -#[derive(Clone, Copy)] -pub struct ModulationParams { - pub spreading_factor: SpreadingFactor, - pub bandwidth: Bandwidth, - pub coding_rate: CodingRate, - pub low_data_rate_optimize: u8, -} - -#[derive(Clone, Copy)] -pub struct PacketParams { - pub preamble_length: u16, // number of LoRa symbols in the preamble - pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length) - pub payload_length: u8, - pub crc_on: bool, - pub iq_inverted: bool, -} - -#[derive(Clone, Copy)] -pub enum CADSymbols { - _1 = 0x00, - _2 = 0x01, - _4 = 0x02, - _8 = 0x03, - _16 = 0x04, -} - -impl CADSymbols { - pub fn value(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum CADExitMode { - CADOnly = 0x00, - CADRx = 0x01, - CADLBT = 0x10, -} - -impl CADExitMode { - pub fn value(self) -> u8 { - self as u8 - } -} diff --git a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs b/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs deleted file mode 100644 index 2e78b919b..000000000 --- a/embassy-lora/src/sx126x/sx126x_lora/subroutine.rs +++ /dev/null @@ -1,674 +0,0 @@ -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::SpiBus; - -use super::mod_params::*; -use super::LoRa; - -// Internal frequency of the radio -const SX126X_XTAL_FREQ: u32 = 32000000; - -// Scaling factor used to perform fixed-point operations -const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14; - -// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT -const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT); - -// Maximum value for parameter symbNum -const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248; - -// Provides board-specific functionality for Semtech SX126x-based boards - -impl LoRa -where - SPI: SpiBus, - CTRL: OutputPin, - WAIT: Wait, -{ - // Initialize the radio driver - pub(super) async fn sub_init(&mut self) -> Result<(), RadioError> { - self.brd_reset().await?; - self.brd_wakeup().await?; - self.sub_set_standby(StandbyMode::RC).await?; - self.brd_io_tcxo_init().await?; - self.brd_io_rf_switch_init().await?; - self.image_calibrated = false; - Ok(()) - } - - // Wakeup the radio if it is in Sleep mode and check that Busy is low - pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError> { - let operating_mode = self.brd_get_operating_mode(); - if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle { - self.brd_wakeup().await?; - } - self.brd_wait_on_busy().await?; - Ok(()) - } - - // Save the payload to be sent in the radio buffer - pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError> { - self.brd_write_buffer(0x00, payload).await?; - Ok(()) - } - - // Read the payload received. - pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result> { - let (size, offset) = self.sub_get_rx_buffer_status().await?; - if (size as usize) > buffer.len() { - Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len())) - } else { - self.brd_read_buffer(offset, buffer).await?; - Ok(size) - } - } - - // Send a payload - pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError> { - self.sub_set_payload(payload).await?; - self.sub_set_tx(timeout).await?; - Ok(()) - } - - // Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command. - // - // The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of - // the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts - // generated during this process will not cause undesired side-effects in the software. - // - // The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing. - pub(super) async fn sub_get_random(&mut self) -> Result> { - let mut reg_ana_lna_buffer_original = [0x00u8]; - let mut reg_ana_mixer_buffer_original = [0x00u8]; - let mut reg_ana_lna_buffer = [0x00u8]; - let mut reg_ana_mixer_buffer = [0x00u8]; - let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8]; - - self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original) - .await?; - reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0)); - self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?; - - self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original) - .await?; - reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7)); - self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer) - .await?; - - // Set radio in continuous reception - self.sub_set_rx(0xFFFFFFu32).await?; - - self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer) - .await?; - - self.sub_set_standby(StandbyMode::RC).await?; - - self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original) - .await?; - self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original) - .await?; - - Ok(Self::convert_u8_buffer_to_u32(&number_buffer)) - } - - // Set the radio in sleep mode - pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError> { - self.brd_ant_sleep()?; - - if !sleep_config.warm_start { - self.image_calibrated = false; - } - - self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()]) - .await?; - self.brd_set_operating_mode(RadioMode::Sleep); - Ok(()) - } - - // Set the radio in configuration mode - pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?; - if mode == StandbyMode::RC { - self.brd_set_operating_mode(RadioMode::StandbyRC); - } else { - self.brd_set_operating_mode(RadioMode::StandbyXOSC); - } - - self.brd_ant_sleep()?; - Ok(()) - } - - // Set the radio in FS mode - pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError> { - // antenna settings ??? - self.brd_write_command(OpCode::SetFS, &[]).await?; - self.brd_set_operating_mode(RadioMode::FrequencySynthesis); - Ok(()) - } - - // Set the radio in transmission mode with timeout specified - pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError> { - let buffer = [ - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - - self.brd_ant_set_tx()?; - - self.brd_set_operating_mode(RadioMode::Transmit); - self.brd_write_command(OpCode::SetTx, &buffer).await?; - Ok(()) - } - - // Set the radio in reception mode with timeout specified - pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError> { - let buffer = [ - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - - self.brd_ant_set_rx()?; - - self.brd_set_operating_mode(RadioMode::Receive); - self.brd_write_registers(Register::RxGain, &[0x94u8]).await?; - self.brd_write_command(OpCode::SetRx, &buffer).await?; - Ok(()) - } - - // Set the radio in reception mode with Boosted LNA gain and timeout specified - pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError> { - let buffer = [ - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - - self.brd_ant_set_rx()?; - - self.brd_set_operating_mode(RadioMode::Receive); - // set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity - self.brd_write_registers(Register::RxGain, &[0x96u8]).await?; - self.brd_write_command(OpCode::SetRx, &buffer).await?; - Ok(()) - } - - // Set the Rx duty cycle management parameters - pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError> { - let buffer = [ - ((rx_time >> 16) & 0xFF) as u8, - ((rx_time >> 8) & 0xFF) as u8, - (rx_time & 0xFF) as u8, - ((sleep_time >> 16) & 0xFF) as u8, - ((sleep_time >> 8) & 0xFF) as u8, - (sleep_time & 0xFF) as u8, - ]; - - // antenna settings ??? - - self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?; - self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle); - Ok(()) - } - - // Set the radio in CAD mode - pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError> { - self.brd_ant_set_rx()?; - - self.brd_write_command(OpCode::SetCAD, &[]).await?; - self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); - Ok(()) - } - - // Set the radio in continuous wave transmission mode - pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError> { - self.brd_ant_set_tx()?; - - self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?; - self.brd_set_operating_mode(RadioMode::Transmit); - Ok(()) - } - - // Set the radio in continuous preamble transmission mode - pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError> { - self.brd_ant_set_tx()?; - - self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?; - self.brd_set_operating_mode(RadioMode::Transmit); - Ok(()) - } - - // Decide which interrupt will stop the internal radio rx timer. - // false timer stop after header/syncword detection - // true timer stop after preamble detection - pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect( - &mut self, - enable: bool, - ) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8]) - .await?; - Ok(()) - } - - // Set the number of symbols the radio will wait to validate a reception - pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError> { - let mut exp = 0u8; - let mut reg; - let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1; - while mant > 31 { - mant = (mant + 3) >> 2; - exp += 1; - } - reg = mant << ((2 * exp) + 1); - - self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?; - - if symb_num != 0 { - reg = exp + (mant << 3); - self.brd_write_registers(Register::SynchTimeout, &[reg]).await?; - } - - Ok(()) - } - - // Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled - pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()]) - .await?; - Ok(()) - } - - // Calibrate the given radio block - pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError> { - self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()]) - .await?; - Ok(()) - } - - // Calibrate the image rejection based on the given frequency - pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError> { - let mut cal_freq = [0x00u8, 0x00u8]; - - if freq > 900000000 { - cal_freq[0] = 0xE1; - cal_freq[1] = 0xE9; - } else if freq > 850000000 { - cal_freq[0] = 0xD7; - cal_freq[1] = 0xDB; - } else if freq > 770000000 { - cal_freq[0] = 0xC1; - cal_freq[1] = 0xC5; - } else if freq > 460000000 { - cal_freq[0] = 0x75; - cal_freq[1] = 0x81; - } else if freq > 425000000 { - cal_freq[0] = 0x6B; - cal_freq[1] = 0x6F; - } - self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?; - Ok(()) - } - - // Activate the extention of the timeout when a long preamble is used - pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError> { - Ok(()) // no operation currently - } - - // Set the transmission parameters - // hp_max 0 for sx1261, 7 for sx1262 - // device_sel 1 for sx1261, 0 for sx1262 - // pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT - pub(super) async fn sub_set_pa_config( - &mut self, - pa_duty_cycle: u8, - hp_max: u8, - device_sel: u8, - pa_lut: u8, - ) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut]) - .await?; - Ok(()) - } - - // Define into which mode the chip goes after a TX / RX done - pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode]) - .await?; - Ok(()) - } - - // Set the IRQ mask and DIO masks - pub(super) async fn sub_set_dio_irq_params( - &mut self, - irq_mask: u16, - dio1_mask: u16, - dio2_mask: u16, - dio3_mask: u16, - ) -> Result<(), RadioError> { - let mut buffer = [0x00u8; 8]; - - buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8; - buffer[1] = (irq_mask & 0x00FF) as u8; - buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8; - buffer[3] = (dio1_mask & 0x00FF) as u8; - buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8; - buffer[5] = (dio2_mask & 0x00FF) as u8; - buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8; - buffer[7] = (dio3_mask & 0x00FF) as u8; - self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?; - Ok(()) - } - - // Return the current IRQ status - pub(super) async fn sub_get_irq_status(&mut self) -> Result> { - let mut irq_status = [0x00u8, 0x00u8]; - self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?; - Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16)) - } - - // Indicate if DIO2 is used to control an RF Switch - pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?; - Ok(()) - } - - // Indicate if the radio main clock is supplied from a TCXO - // tcxo_voltage voltage used to control the TCXO on/off from DIO3 - // timeout duration given to the TCXO to go to 32MHz - pub(super) async fn sub_set_dio3_as_tcxo_ctrl( - &mut self, - tcxo_voltage: TcxoCtrlVoltage, - timeout: u32, - ) -> Result<(), RadioError> { - let buffer = [ - tcxo_voltage.value() & 0x07, - Self::timeout_1(timeout), - Self::timeout_2(timeout), - Self::timeout_3(timeout), - ]; - self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?; - - Ok(()) - } - - // Set the RF frequency (Hz) - pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError> { - let mut buffer = [0x00u8; 4]; - - if !self.image_calibrated { - self.sub_calibrate_image(frequency).await?; - self.image_calibrated = true; - } - - let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency); - - buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8; - buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8; - buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8; - buffer[3] = (freq_in_pll_steps & 0xFF) as u8; - self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?; - Ok(()) - } - - // Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters. - pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError> { - self.packet_type = packet_type; - self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()]) - .await?; - Ok(()) - } - - // Get the current radio protocol (LoRa or GFSK) - pub(super) fn sub_get_packet_type(&mut self) -> PacketType { - self.packet_type - } - - // Set the transmission parameters - // power RF output power [-18..13] dBm - // ramp_time transmission ramp up time - pub(super) async fn sub_set_tx_params( - &mut self, - mut power: i8, - ramp_time: RampTime, - ) -> Result<(), RadioError> { - if self.brd_get_radio_type() == RadioType::SX1261 { - if power == 15 { - self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?; - } else { - self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?; - } - - if power >= 14 { - power = 14; - } else if power < -17 { - power = -17; - } - } else { - // Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2) - let mut tx_clamp_cfg = [0x00u8]; - self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?; - tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1); - self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?; - - self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?; - - if power > 22 { - power = 22; - } else if power < -9 { - power = -9; - } - } - - // power conversion of negative number from i8 to u8 ??? - self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()]) - .await?; - Ok(()) - } - - // Set the modulation parameters - pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError> { - if self.modulation_params.is_some() { - let mut buffer = [0x00u8; 4]; - - // Since this driver only supports LoRa, ensure the packet type is set accordingly - self.sub_set_packet_type(PacketType::LoRa).await?; - - let modulation_params = self.modulation_params.unwrap(); - buffer[0] = modulation_params.spreading_factor.value(); - buffer[1] = modulation_params.bandwidth.value(); - buffer[2] = modulation_params.coding_rate.value(); - buffer[3] = modulation_params.low_data_rate_optimize; - - self.brd_write_command(OpCode::SetModulationParams, &buffer).await?; - Ok(()) - } else { - Err(RadioError::ModulationParamsMissing) - } - } - - // Set the packet parameters - pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError> { - if self.packet_params.is_some() { - let mut buffer = [0x00u8; 6]; - - // Since this driver only supports LoRa, ensure the packet type is set accordingly - self.sub_set_packet_type(PacketType::LoRa).await?; - - let packet_params = self.packet_params.unwrap(); - buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8; - buffer[1] = (packet_params.preamble_length & 0xFF) as u8; - buffer[2] = packet_params.implicit_header as u8; - buffer[3] = packet_params.payload_length; - buffer[4] = packet_params.crc_on as u8; - buffer[5] = packet_params.iq_inverted as u8; - - self.brd_write_command(OpCode::SetPacketParams, &buffer).await?; - Ok(()) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - // Set the channel activity detection (CAD) parameters - // symbols number of symbols to use for CAD operations - // det_peak limit for detection of SNR peak used in the CAD - // det_min minimum symbol recognition for CAD - // exit_mode operation to be done at the end of CAD action - // timeout timeout value to abort the CAD activity - - pub(super) async fn sub_set_cad_params( - &mut self, - symbols: CADSymbols, - det_peak: u8, - det_min: u8, - exit_mode: CADExitMode, - timeout: u32, - ) -> Result<(), RadioError> { - let mut buffer = [0x00u8; 7]; - - buffer[0] = symbols.value(); - buffer[1] = det_peak; - buffer[2] = det_min; - buffer[3] = exit_mode.value(); - buffer[4] = Self::timeout_1(timeout); - buffer[5] = Self::timeout_2(timeout); - buffer[6] = Self::timeout_3(timeout); - - self.brd_write_command(OpCode::SetCADParams, &buffer).await?; - self.brd_set_operating_mode(RadioMode::ChannelActivityDetection); - Ok(()) - } - - // Set the data buffer base address for transmission and reception - pub(super) async fn sub_set_buffer_base_address( - &mut self, - tx_base_address: u8, - rx_base_address: u8, - ) -> Result<(), RadioError> { - self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address]) - .await?; - Ok(()) - } - - // Get the current radio status - pub(super) async fn sub_get_status(&mut self) -> Result> { - let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?; - Ok(RadioStatus { - cmd_status: (status & (0x07 << 1)) >> 1, - chip_mode: (status & (0x07 << 4)) >> 4, - }) - } - - // Get the instantaneous RSSI value for the last packet received - pub(super) async fn sub_get_rssi_inst(&mut self) -> Result> { - let mut buffer = [0x00u8]; - self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?; - let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ??? - Ok(rssi) - } - - // Get the last received packet buffer status - pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError> { - if self.packet_params.is_some() { - let mut status = [0x00u8; 2]; - let mut payload_length_buffer = [0x00u8]; - - self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?; - if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header { - self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer) - .await?; - } else { - payload_length_buffer[0] = status[0]; - } - - let payload_length = payload_length_buffer[0]; - let offset = status[1]; - - Ok((payload_length, offset)) - } else { - Err(RadioError::PacketParamsMissing) - } - } - - // Get the last received packet payload status - pub(super) async fn sub_get_packet_status(&mut self) -> Result> { - let mut status = [0x00u8; 3]; - self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?; - - // check this ??? - let rssi = ((-(status[0] as i32)) >> 1) as i8; - let snr = ((status[1] as i8) + 2) >> 2; - let signal_rssi = ((-(status[2] as i32)) >> 1) as i8; - let freq_error = self.frequency_error; - - Ok(PacketStatus { - rssi, - snr, - signal_rssi, - freq_error, - }) - } - - // Get the possible system errors - pub(super) async fn sub_get_device_errors(&mut self) -> Result> { - let mut errors = [0x00u8; 2]; - self.brd_read_command(OpCode::GetErrors, &mut errors).await?; - - Ok(RadioSystemError { - rc_64khz_calibration: (errors[1] & (1 << 0)) != 0, - rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0, - pll_calibration: (errors[1] & (1 << 2)) != 0, - adc_calibration: (errors[1] & (1 << 3)) != 0, - image_calibration: (errors[1] & (1 << 4)) != 0, - xosc_start: (errors[1] & (1 << 5)) != 0, - pll_lock: (errors[1] & (1 << 6)) != 0, - pa_ramp: (errors[0] & (1 << 0)) != 0, - }) - } - - // Clear all the errors in the device - pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError> { - self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?; - Ok(()) - } - - // Clear the IRQs - pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError> { - let mut buffer = [0x00u8, 0x00u8]; - buffer[0] = ((irq >> 8) & 0xFF) as u8; - buffer[1] = (irq & 0xFF) as u8; - self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?; - Ok(()) - } - - // Utility functions - - fn timeout_1(timeout: u32) -> u8 { - ((timeout >> 16) & 0xFF) as u8 - } - fn timeout_2(timeout: u32) -> u8 { - ((timeout >> 8) & 0xFF) as u8 - } - fn timeout_3(timeout: u32) -> u8 { - (timeout & 0xFF) as u8 - } - - // check this ??? - fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 { - let b0 = buffer[0] as u32; - let b1 = buffer[1] as u32; - let b2 = buffer[2] as u32; - let b3 = buffer[3] as u32; - (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 - } - - fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 { - // Get integer and fractional parts of the frequency computed with a PLL step scaled value - let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED; - let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED); - - (steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT) - + (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED) - } -} diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs deleted file mode 100644 index 4e8dc2232..000000000 --- a/embassy-lora/src/sx127x/mod.rs +++ /dev/null @@ -1,192 +0,0 @@ -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::digital::Wait; -use embedded_hal_async::spi::*; -use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; -use lorawan_device::async_device::Timings; - -mod sx127x_lora; -use sx127x_lora::{Error as RadioError, LoRa, RadioMode, IRQ}; - -/// Trait representing a radio switch for boards using the Sx127x radio. One some -/// boards, this will be a dummy implementation that does nothing. -pub trait RadioSwitch { - fn set_tx(&mut self); - fn set_rx(&mut self); -} - -/// Semtech Sx127x radio peripheral -pub struct Sx127xRadio -where - SPI: SpiBus + 'static, - E: 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, -{ - radio: LoRa, - rfs: RFS, - irq: I, -} - -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum State { - Idle, - Txing, - Rxing, -} - -impl Sx127xRadio -where - SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, - E: 'static, -{ - pub async fn new( - spi: SPI, - cs: CS, - reset: RESET, - irq: I, - rfs: RFS, - ) -> Result> { - let mut radio = LoRa::new(spi, cs, reset); - radio.reset().await?; - Ok(Self { radio, irq, rfs }) - } -} - -impl Timings for Sx127xRadio -where - SPI: SpiBus + 'static, - CS: OutputPin + 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, -{ - fn get_rx_window_offset_ms(&self) -> i32 { - -3 - } - fn get_rx_window_duration_ms(&self) -> u32 { - 1003 - } -} - -impl PhyRxTx for Sx127xRadio -where - SPI: SpiBus + 'static, - CS: OutputPin + 'static, - E: 'static, - RESET: OutputPin + 'static, - I: Wait + 'static, - RFS: RadioSwitch + 'static, -{ - type PhyError = Sx127xError; - - async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { - trace!("TX START"); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - self.rfs.set_tx(); - self.radio.set_tx_power(14, 0).await?; - self.radio.set_frequency(config.rf.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor)) - .await?; - - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_pa_ramp().await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(false).await?; - self.radio.set_crc(true).await?; - - self.radio.set_dio0_tx_done().await?; - - self.radio.transmit_start(buf).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { - trace!("TX DONE"); - return Ok(0); - } - } - } - - async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { - self.rfs.set_rx(); - self.radio.reset_payload_length().await?; - self.radio.set_frequency(config.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) - .await?; - - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(true).await?; - self.radio.set_crc(true).await?; - - self.radio.set_dio0_rx_done().await?; - self.radio.set_mode(RadioMode::RxContinuous).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { - let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16; - let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8; - let response = if let Ok(size) = self.radio.read_packet_size().await { - self.radio.read_packet(buf).await?; - Ok((size, RxQuality::new(rssi, snr))) - } else { - Ok((0, RxQuality::new(rssi, snr))) - }; - trace!("RX DONE"); - return response; - } - } - } -} - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Sx127xError; - -impl From> for Sx127xError { - fn from(_: sx127x_lora::Error) -> Self { - Sx127xError - } -} - -fn spreading_factor_to_u8(sf: SpreadingFactor) -> u8 { - match sf { - SpreadingFactor::_7 => 7, - SpreadingFactor::_8 => 8, - SpreadingFactor::_9 => 9, - SpreadingFactor::_10 => 10, - SpreadingFactor::_11 => 11, - SpreadingFactor::_12 => 12, - } -} - -fn bandwidth_to_i64(bw: Bandwidth) -> i64 { - match bw { - Bandwidth::_125KHz => 125_000, - Bandwidth::_250KHz => 250_000, - Bandwidth::_500KHz => 500_000, - } -} diff --git a/embassy-lora/src/sx127x/sx127x_lora/mod.rs b/embassy-lora/src/sx127x/sx127x_lora/mod.rs deleted file mode 100644 index aacc9da22..000000000 --- a/embassy-lora/src/sx127x/sx127x_lora/mod.rs +++ /dev/null @@ -1,539 +0,0 @@ -// Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 -// license -// -// Modifications made to make the driver work with the rust-lorawan link layer. - -#![allow(dead_code)] - -use bit_field::BitField; -use embassy_time::{Duration, Timer}; -use embedded_hal::digital::v2::OutputPin; -use embedded_hal_async::spi::SpiBus; - -mod register; -pub use self::register::IRQ; -use self::register::{PaConfig, Register}; - -/// Provides high-level access to Semtech SX1276/77/78/79 based boards connected to a Raspberry Pi -pub struct LoRa { - spi: SPI, - cs: CS, - reset: RESET, - pub explicit_header: bool, - pub mode: RadioMode, -} - -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - Uninformative, - VersionMismatch(u8), - CS(CS), - Reset(RESET), - SPI(SPI), - Transmitting, -} - -use Error::*; - -use super::sx127x_lora::register::{FskDataModulationShaping, FskRampUpRamDown}; - -#[cfg(not(feature = "version_0x09"))] -const VERSION_CHECK: u8 = 0x12; - -#[cfg(feature = "version_0x09")] -const VERSION_CHECK: u8 = 0x09; - -impl LoRa -where - SPI: SpiBus, - CS: OutputPin, - RESET: OutputPin, -{ - /// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time. - /// This also preforms a hardware reset of the module and then puts it in standby. - pub fn new(spi: SPI, cs: CS, reset: RESET) -> Self { - Self { - spi, - cs, - reset, - explicit_header: true, - mode: RadioMode::Sleep, - } - } - - pub async fn reset(&mut self) -> Result<(), Error> { - self.reset.set_low().map_err(Reset)?; - Timer::after(Duration::from_millis(10)).await; - self.reset.set_high().map_err(Reset)?; - Timer::after(Duration::from_millis(10)).await; - let version = self.read_register(Register::RegVersion.addr()).await?; - if version == VERSION_CHECK { - self.set_mode(RadioMode::Sleep).await?; - self.write_register(Register::RegFifoTxBaseAddr.addr(), 0).await?; - self.write_register(Register::RegFifoRxBaseAddr.addr(), 0).await?; - let lna = self.read_register(Register::RegLna.addr()).await?; - self.write_register(Register::RegLna.addr(), lna | 0x03).await?; - self.write_register(Register::RegModemConfig3.addr(), 0x04).await?; - self.set_tcxo(true).await?; - self.set_mode(RadioMode::Stdby).await?; - self.cs.set_high().map_err(CS)?; - Ok(()) - } else { - Err(Error::VersionMismatch(version)) - } - } - - pub async fn set_dio0_tx_done(&mut self) -> Result<(), Error> { - self.write_register(Register::RegIrqFlagsMask.addr(), 0b1111_0111) - .await?; - let mapping = self.read_register(Register::RegDioMapping1.addr()).await?; - self.write_register(Register::RegDioMapping1.addr(), (mapping & 0x3F) | 0x40) - .await - } - - pub async fn set_dio0_rx_done(&mut self) -> Result<(), Error> { - self.write_register(Register::RegIrqFlagsMask.addr(), 0b0001_1111) - .await?; - let mapping = self.read_register(Register::RegDioMapping1.addr()).await?; - self.write_register(Register::RegDioMapping1.addr(), mapping & 0x3F) - .await - } - - pub async fn transmit_start(&mut self, buffer: &[u8]) -> Result<(), Error> { - assert!(buffer.len() < 255); - if self.transmitting().await? { - //trace!("ALREADY TRANSMNITTING"); - Err(Transmitting) - } else { - self.set_mode(RadioMode::Stdby).await?; - if self.explicit_header { - self.set_explicit_header_mode().await?; - } else { - self.set_implicit_header_mode().await?; - } - - self.write_register(Register::RegIrqFlags.addr(), 0).await?; - self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?; - self.write_register(Register::RegPayloadLength.addr(), 0).await?; - for byte in buffer.iter() { - self.write_register(Register::RegFifo.addr(), *byte).await?; - } - self.write_register(Register::RegPayloadLength.addr(), buffer.len() as u8) - .await?; - self.set_mode(RadioMode::Tx).await?; - Ok(()) - } - } - - pub async fn packet_ready(&mut self) -> Result> { - Ok(self.read_register(Register::RegIrqFlags.addr()).await?.get_bit(6)) - } - - pub async fn irq_flags_mask(&mut self) -> Result> { - Ok(self.read_register(Register::RegIrqFlagsMask.addr()).await? as u8) - } - - pub async fn irq_flags(&mut self) -> Result> { - Ok(self.read_register(Register::RegIrqFlags.addr()).await? as u8) - } - - pub async fn read_packet_size(&mut self) -> Result> { - let size = self.read_register(Register::RegRxNbBytes.addr()).await?; - Ok(size as usize) - } - - /// Returns the contents of the fifo as a fixed 255 u8 array. This should only be called is there is a - /// new packet ready to be read. - pub async fn read_packet(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.clear_irq().await?; - let size = self.read_register(Register::RegRxNbBytes.addr()).await?; - assert!(size as usize <= buffer.len()); - let fifo_addr = self.read_register(Register::RegFifoRxCurrentAddr.addr()).await?; - self.write_register(Register::RegFifoAddrPtr.addr(), fifo_addr).await?; - for i in 0..size { - let byte = self.read_register(Register::RegFifo.addr()).await?; - buffer[i as usize] = byte; - } - self.write_register(Register::RegFifoAddrPtr.addr(), 0).await?; - Ok(()) - } - - /// Returns true if the radio is currently transmitting a packet. - pub async fn transmitting(&mut self) -> Result> { - if (self.read_register(Register::RegOpMode.addr()).await?) & RadioMode::Tx.addr() == RadioMode::Tx.addr() { - Ok(true) - } else { - if (self.read_register(Register::RegIrqFlags.addr()).await? & IRQ::IrqTxDoneMask.addr()) == 1 { - self.write_register(Register::RegIrqFlags.addr(), IRQ::IrqTxDoneMask.addr()) - .await?; - } - Ok(false) - } - } - - /// Clears the radio's IRQ registers. - pub async fn clear_irq(&mut self) -> Result> { - let irq_flags = self.read_register(Register::RegIrqFlags.addr()).await?; - self.write_register(Register::RegIrqFlags.addr(), 0xFF).await?; - Ok(irq_flags) - } - - /// Sets the transmit power and pin. Levels can range from 0-14 when the output - /// pin = 0(RFO), and form 0-20 when output pin = 1(PaBoost). Power is in dB. - /// Default value is `17`. - pub async fn set_tx_power( - &mut self, - mut level: i32, - output_pin: u8, - ) -> Result<(), Error> { - if PaConfig::PaOutputRfoPin.addr() == output_pin { - // RFO - if level < 0 { - level = 0; - } else if level > 14 { - level = 14; - } - self.write_register(Register::RegPaConfig.addr(), (0x70 | level) as u8) - .await - } else { - // PA BOOST - if level > 17 { - if level > 20 { - level = 20; - } - // subtract 3 from level, so 18 - 20 maps to 15 - 17 - level -= 3; - - // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) - self.write_register(Register::RegPaDac.addr(), 0x87).await?; - self.set_ocp(140).await?; - } else { - if level < 2 { - level = 2; - } - //Default value PA_HF/LF or +17dBm - self.write_register(Register::RegPaDac.addr(), 0x84).await?; - self.set_ocp(100).await?; - } - level -= 2; - self.write_register(Register::RegPaConfig.addr(), PaConfig::PaBoost.addr() | level as u8) - .await - } - } - - pub async fn get_modem_stat(&mut self) -> Result> { - Ok(self.read_register(Register::RegModemStat.addr()).await? as u8) - } - - /// Sets the over current protection on the radio(mA). - pub async fn set_ocp(&mut self, ma: u8) -> Result<(), Error> { - let mut ocp_trim: u8 = 27; - - if ma <= 120 { - ocp_trim = (ma - 45) / 5; - } else if ma <= 240 { - ocp_trim = (ma + 30) / 10; - } - self.write_register(Register::RegOcp.addr(), 0x20 | (0x1F & ocp_trim)) - .await - } - - /// Sets the state of the radio. Default mode after initiation is `Standby`. - pub async fn set_mode(&mut self, mode: RadioMode) -> Result<(), Error> { - if self.explicit_header { - self.set_explicit_header_mode().await?; - } else { - self.set_implicit_header_mode().await?; - } - self.write_register( - Register::RegOpMode.addr(), - RadioMode::LongRangeMode.addr() | mode.addr(), - ) - .await?; - - self.mode = mode; - Ok(()) - } - - pub async fn reset_payload_length(&mut self) -> Result<(), Error> { - self.write_register(Register::RegPayloadLength.addr(), 0xFF).await - } - - /// Sets the frequency of the radio. Values are in megahertz. - /// I.E. 915 MHz must be used for North America. Check regulation for your area. - pub async fn set_frequency(&mut self, freq: u32) -> Result<(), Error> { - const FREQ_STEP: f64 = 61.03515625; - // calculate register values - let frf = (freq as f64 / FREQ_STEP) as u32; - // write registers - self.write_register(Register::RegFrfMsb.addr(), ((frf & 0x00FF_0000) >> 16) as u8) - .await?; - self.write_register(Register::RegFrfMid.addr(), ((frf & 0x0000_FF00) >> 8) as u8) - .await?; - self.write_register(Register::RegFrfLsb.addr(), (frf & 0x0000_00FF) as u8) - .await - } - - /// Sets the radio to use an explicit header. Default state is `ON`. - async fn set_explicit_header_mode(&mut self) -> Result<(), Error> { - let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0xfe) - .await?; - self.explicit_header = true; - Ok(()) - } - - /// Sets the radio to use an implicit header. Default state is `OFF`. - async fn set_implicit_header_mode(&mut self) -> Result<(), Error> { - let reg_modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register(Register::RegModemConfig1.addr(), reg_modem_config_1 & 0x01) - .await?; - self.explicit_header = false; - Ok(()) - } - - /// Sets the spreading factor of the radio. Supported values are between 6 and 12. - /// If a spreading factor of 6 is set, implicit header mode must be used to transmit - /// and receive packets. Default value is `7`. - pub async fn set_spreading_factor(&mut self, mut sf: u8) -> Result<(), Error> { - if sf < 6 { - sf = 6; - } else if sf > 12 { - sf = 12; - } - - if sf == 6 { - self.write_register(Register::RegDetectionOptimize.addr(), 0xc5).await?; - self.write_register(Register::RegDetectionThreshold.addr(), 0x0c) - .await?; - } else { - self.write_register(Register::RegDetectionOptimize.addr(), 0xc3).await?; - self.write_register(Register::RegDetectionThreshold.addr(), 0x0a) - .await?; - } - let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?; - self.write_register( - Register::RegModemConfig2.addr(), - (modem_config_2 & 0x0f) | ((sf << 4) & 0xf0), - ) - .await?; - self.set_ldo_flag().await?; - - self.write_register(Register::RegSymbTimeoutLsb.addr(), 0x05).await?; - - Ok(()) - } - - pub async fn set_tcxo(&mut self, external: bool) -> Result<(), Error> { - if external { - self.write_register(Register::RegTcxo.addr(), 0x10).await - } else { - self.write_register(Register::RegTcxo.addr(), 0x00).await - } - } - - /// Sets the signal bandwidth of the radio. Supported values are: `7800 Hz`, `10400 Hz`, - /// `15600 Hz`, `20800 Hz`, `31250 Hz`,`41700 Hz` ,`62500 Hz`,`125000 Hz` and `250000 Hz` - /// Default value is `125000 Hz` - pub async fn set_signal_bandwidth(&mut self, sbw: i64) -> Result<(), Error> { - let bw: i64 = match sbw { - 7_800 => 0, - 10_400 => 1, - 15_600 => 2, - 20_800 => 3, - 31_250 => 4, - 41_700 => 5, - 62_500 => 6, - 125_000 => 7, - 250_000 => 8, - _ => 9, - }; - let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register( - Register::RegModemConfig1.addr(), - (modem_config_1 & 0x0f) | ((bw << 4) as u8), - ) - .await?; - self.set_ldo_flag().await?; - Ok(()) - } - - /// Sets the coding rate of the radio with the numerator fixed at 4. Supported values - /// are between `5` and `8`, these correspond to coding rates of `4/5` and `4/8`. - /// Default value is `5`. - pub async fn set_coding_rate_4(&mut self, mut denominator: u8) -> Result<(), Error> { - if denominator < 5 { - denominator = 5; - } else if denominator > 8 { - denominator = 8; - } - let cr = denominator - 4; - let modem_config_1 = self.read_register(Register::RegModemConfig1.addr()).await?; - self.write_register(Register::RegModemConfig1.addr(), (modem_config_1 & 0xf1) | (cr << 1)) - .await - } - - /// Sets the preamble length of the radio. Values are between 6 and 65535. - /// Default value is `8`. - pub async fn set_preamble_length(&mut self, length: i64) -> Result<(), Error> { - self.write_register(Register::RegPreambleMsb.addr(), (length >> 8) as u8) - .await?; - self.write_register(Register::RegPreambleLsb.addr(), length as u8).await - } - - /// Enables are disables the radio's CRC check. Default value is `false`. - pub async fn set_crc(&mut self, value: bool) -> Result<(), Error> { - let modem_config_2 = self.read_register(Register::RegModemConfig2.addr()).await?; - if value { - self.write_register(Register::RegModemConfig2.addr(), modem_config_2 | 0x04) - .await - } else { - self.write_register(Register::RegModemConfig2.addr(), modem_config_2 & 0xfb) - .await - } - } - - /// Inverts the radio's IQ signals. Default value is `false`. - pub async fn set_invert_iq(&mut self, value: bool) -> Result<(), Error> { - if value { - self.write_register(Register::RegInvertiq.addr(), 0x66).await?; - self.write_register(Register::RegInvertiq2.addr(), 0x19).await - } else { - self.write_register(Register::RegInvertiq.addr(), 0x27).await?; - self.write_register(Register::RegInvertiq2.addr(), 0x1d).await - } - } - - /// Returns the spreading factor of the radio. - pub async fn get_spreading_factor(&mut self) -> Result> { - Ok(self.read_register(Register::RegModemConfig2.addr()).await? >> 4) - } - - /// Returns the signal bandwidth of the radio. - pub async fn get_signal_bandwidth(&mut self) -> Result> { - let bw = self.read_register(Register::RegModemConfig1.addr()).await? >> 4; - let bw = match bw { - 0 => 7_800, - 1 => 10_400, - 2 => 15_600, - 3 => 20_800, - 4 => 31_250, - 5 => 41_700, - 6 => 62_500, - 7 => 125_000, - 8 => 250_000, - 9 => 500_000, - _ => -1, - }; - Ok(bw) - } - - /// Returns the RSSI of the last received packet. - pub async fn get_packet_rssi(&mut self) -> Result> { - Ok(i32::from(self.read_register(Register::RegPktRssiValue.addr()).await?) - 157) - } - - /// Returns the signal to noise radio of the the last received packet. - pub async fn get_packet_snr(&mut self) -> Result> { - Ok(f64::from(self.read_register(Register::RegPktSnrValue.addr()).await?)) - } - - /// Returns the frequency error of the last received packet in Hz. - pub async fn get_packet_frequency_error(&mut self) -> Result> { - let mut freq_error: i32; - freq_error = i32::from(self.read_register(Register::RegFreqErrorMsb.addr()).await? & 0x7); - freq_error <<= 8i64; - freq_error += i32::from(self.read_register(Register::RegFreqErrorMid.addr()).await?); - freq_error <<= 8i64; - freq_error += i32::from(self.read_register(Register::RegFreqErrorLsb.addr()).await?); - - let f_xtal = 32_000_000; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14) - let f_error = ((f64::from(freq_error) * (1i64 << 24) as f64) / f64::from(f_xtal)) - * (self.get_signal_bandwidth().await? as f64 / 500_000.0f64); // p. 37 - Ok(f_error as i64) - } - - async fn set_ldo_flag(&mut self) -> Result<(), Error> { - let sw = self.get_signal_bandwidth().await?; - // Section 4.1.1.5 - let symbol_duration = 1000 / (sw / ((1_i64) << self.get_spreading_factor().await?)); - - // Section 4.1.1.6 - let ldo_on = symbol_duration > 16; - - let mut config_3 = self.read_register(Register::RegModemConfig3.addr()).await?; - config_3.set_bit(3, ldo_on); - //config_3.set_bit(2, true); - self.write_register(Register::RegModemConfig3.addr(), config_3).await - } - - async fn read_register(&mut self, reg: u8) -> Result> { - let mut buffer = [reg & 0x7f, 0]; - self.cs.set_low().map_err(CS)?; - - let _ = self.spi.transfer(&mut buffer, &[reg & 0x7f, 0]).await.map_err(SPI)?; - - self.cs.set_high().map_err(CS)?; - Ok(buffer[1]) - } - - async fn write_register(&mut self, reg: u8, byte: u8) -> Result<(), Error> { - self.cs.set_low().map_err(CS)?; - let buffer = [reg | 0x80, byte]; - self.spi.write(&buffer).await.map_err(SPI)?; - self.cs.set_high().map_err(CS)?; - Ok(()) - } - - pub async fn put_in_fsk_mode(&mut self) -> Result<(), Error> { - // Put in FSK mode - let mut op_mode = 0; - op_mode - .set_bit(7, false) // FSK mode - .set_bits(5..6, 0x00) // FSK modulation - .set_bit(3, false) //Low freq registers - .set_bits(0..2, 0b011); // Mode - - self.write_register(Register::RegOpMode as u8, op_mode).await - } - - pub async fn set_fsk_pa_ramp( - &mut self, - modulation_shaping: FskDataModulationShaping, - ramp: FskRampUpRamDown, - ) -> Result<(), Error> { - let mut pa_ramp = 0; - pa_ramp - .set_bits(5..6, modulation_shaping as u8) - .set_bits(0..3, ramp as u8); - - self.write_register(Register::RegPaRamp as u8, pa_ramp).await - } - - pub async fn set_lora_pa_ramp(&mut self) -> Result<(), Error> { - self.write_register(Register::RegPaRamp as u8, 0b1000).await - } - - pub async fn set_lora_sync_word(&mut self) -> Result<(), Error> { - self.write_register(Register::RegSyncWord as u8, 0x34).await - } -} -/// Modes of the radio and their corresponding register values. -#[derive(Clone, Copy)] -pub enum RadioMode { - LongRangeMode = 0x80, - Sleep = 0x00, - Stdby = 0x01, - Tx = 0x03, - RxContinuous = 0x05, - RxSingle = 0x06, -} - -impl RadioMode { - /// Returns the address of the mode. - pub fn addr(self) -> u8 { - self as u8 - } -} diff --git a/embassy-lora/src/sx127x/sx127x_lora/register.rs b/embassy-lora/src/sx127x/sx127x_lora/register.rs deleted file mode 100644 index 2445e21b1..000000000 --- a/embassy-lora/src/sx127x/sx127x_lora/register.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright Charles Wade (https://github.com/mr-glt/sx127x_lora). Licensed under the Apache 2.0 -// license -// -// Modifications made to make the driver work with the rust-lorawan link layer. -#![allow(dead_code, clippy::enum_variant_names)] - -#[derive(Clone, Copy)] -pub enum Register { - RegFifo = 0x00, - RegOpMode = 0x01, - RegFrfMsb = 0x06, - RegFrfMid = 0x07, - RegFrfLsb = 0x08, - RegPaConfig = 0x09, - RegPaRamp = 0x0a, - RegOcp = 0x0b, - RegLna = 0x0c, - RegFifoAddrPtr = 0x0d, - RegFifoTxBaseAddr = 0x0e, - RegFifoRxBaseAddr = 0x0f, - RegFifoRxCurrentAddr = 0x10, - RegIrqFlagsMask = 0x11, - RegIrqFlags = 0x12, - RegRxNbBytes = 0x13, - RegPktSnrValue = 0x19, - RegModemStat = 0x18, - RegPktRssiValue = 0x1a, - RegModemConfig1 = 0x1d, - RegModemConfig2 = 0x1e, - RegSymbTimeoutLsb = 0x1f, - RegPreambleMsb = 0x20, - RegPreambleLsb = 0x21, - RegPayloadLength = 0x22, - RegMaxPayloadLength = 0x23, - RegModemConfig3 = 0x26, - RegFreqErrorMsb = 0x28, - RegFreqErrorMid = 0x29, - RegFreqErrorLsb = 0x2a, - RegRssiWideband = 0x2c, - RegDetectionOptimize = 0x31, - RegInvertiq = 0x33, - RegDetectionThreshold = 0x37, - RegSyncWord = 0x39, - RegInvertiq2 = 0x3b, - RegDioMapping1 = 0x40, - RegVersion = 0x42, - RegTcxo = 0x4b, - RegPaDac = 0x4d, -} -#[derive(Clone, Copy)] -pub enum PaConfig { - PaBoost = 0x80, - PaOutputRfoPin = 0, -} - -#[derive(Clone, Copy)] -pub enum IRQ { - IrqTxDoneMask = 0x08, - IrqPayloadCrcErrorMask = 0x20, - IrqRxDoneMask = 0x40, -} - -impl Register { - pub fn addr(self) -> u8 { - self as u8 - } -} - -impl PaConfig { - pub fn addr(self) -> u8 { - self as u8 - } -} - -impl IRQ { - pub fn addr(self) -> u8 { - self as u8 - } -} - -#[derive(Clone, Copy)] -pub enum FskDataModulationShaping { - None = 1, - GaussianBt1d0 = 2, - GaussianBt0d5 = 10, - GaussianBt0d3 = 11, -} - -#[derive(Clone, Copy)] -pub enum FskRampUpRamDown { - _3d4ms = 0b000, - _2ms = 0b0001, - _1ms = 0b0010, - _500us = 0b0011, - _250us = 0b0100, - _125us = 0b0101, - _100us = 0b0110, - _62us = 0b0111, - _50us = 0b1000, - _40us = 0b1001, - _31us = 0b1010, - _25us = 0b1011, - _20us = 0b1100, - _15us = 0b1101, - _12us = 0b1110, - _10us = 0b1111, -} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index f0fc152ce..24a26eddd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -55,9 +55,6 @@ pub mod rtc; pub mod sdmmc; #[cfg(spi)] pub mod spi; -#[cfg(stm32wl)] -#[deprecated(note = "use the external LoRa physical layer crate - https://crates.io/crates/lora-phy")] -pub mod subghz; #[cfg(usart)] pub mod usart; #[cfg(all(usb, feature = "time"))] diff --git a/embassy-stm32/src/subghz/bit_sync.rs b/embassy-stm32/src/subghz/bit_sync.rs deleted file mode 100644 index f3cba05f9..000000000 --- a/embassy-stm32/src/subghz/bit_sync.rs +++ /dev/null @@ -1,160 +0,0 @@ -/// Bit synchronization. -/// -/// This must be cleared to `0x00` (the reset value) when using packet types -/// other than LoRa. -/// -/// Argument of [`set_bit_sync`](crate::subghz::SubGhz::set_bit_sync). -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct BitSync { - val: u8, -} - -impl BitSync { - /// Bit synchronization register reset value. - pub const RESET: BitSync = BitSync { val: 0x00 }; - - /// Create a new [`BitSync`] structure from a raw value. - /// - /// Reserved bits will be masked. - pub const fn from_raw(raw: u8) -> Self { - Self { val: raw & 0x70 } - } - - /// Get the raw value of the [`BitSync`] register. - pub const fn as_bits(&self) -> u8 { - self.val - } - - /// LoRa simple bit synchronization enable. - /// - /// # Example - /// - /// Enable simple bit synchronization. - /// - /// ``` - /// use stm32wlxx_hal::subghz::BitSync; - /// - /// const BIT_SYNC: BitSync = BitSync::RESET.set_simple_bit_sync_en(true); - /// # assert_eq!(u8::from(BIT_SYNC), 0x40u8); - /// ``` - #[must_use = "set_simple_bit_sync_en returns a modified BitSync"] - pub const fn set_simple_bit_sync_en(mut self, en: bool) -> BitSync { - if en { - self.val |= 1 << 6; - } else { - self.val &= !(1 << 6); - } - self - } - - /// Returns `true` if simple bit synchronization is enabled. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::BitSync; - /// - /// let bs: BitSync = BitSync::RESET; - /// assert_eq!(bs.simple_bit_sync_en(), false); - /// let bs: BitSync = bs.set_simple_bit_sync_en(true); - /// assert_eq!(bs.simple_bit_sync_en(), true); - /// let bs: BitSync = bs.set_simple_bit_sync_en(false); - /// assert_eq!(bs.simple_bit_sync_en(), false); - /// ``` - pub const fn simple_bit_sync_en(&self) -> bool { - self.val & (1 << 6) != 0 - } - - /// LoRa RX data inversion. - /// - /// # Example - /// - /// Invert receive data. - /// - /// ``` - /// use stm32wlxx_hal::subghz::BitSync; - /// - /// const BIT_SYNC: BitSync = BitSync::RESET.set_rx_data_inv(true); - /// # assert_eq!(u8::from(BIT_SYNC), 0x20u8); - /// ``` - #[must_use = "set_rx_data_inv returns a modified BitSync"] - pub const fn set_rx_data_inv(mut self, inv: bool) -> BitSync { - if inv { - self.val |= 1 << 5; - } else { - self.val &= !(1 << 5); - } - self - } - - /// Returns `true` if LoRa RX data is inverted. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::BitSync; - /// - /// let bs: BitSync = BitSync::RESET; - /// assert_eq!(bs.rx_data_inv(), false); - /// let bs: BitSync = bs.set_rx_data_inv(true); - /// assert_eq!(bs.rx_data_inv(), true); - /// let bs: BitSync = bs.set_rx_data_inv(false); - /// assert_eq!(bs.rx_data_inv(), false); - /// ``` - pub const fn rx_data_inv(&self) -> bool { - self.val & (1 << 5) != 0 - } - - /// LoRa normal bit synchronization enable. - /// - /// # Example - /// - /// Enable normal bit synchronization. - /// - /// ``` - /// use stm32wlxx_hal::subghz::BitSync; - /// - /// const BIT_SYNC: BitSync = BitSync::RESET.set_norm_bit_sync_en(true); - /// # assert_eq!(u8::from(BIT_SYNC), 0x10u8); - /// ``` - #[must_use = "set_norm_bit_sync_en returns a modified BitSync"] - pub const fn set_norm_bit_sync_en(mut self, en: bool) -> BitSync { - if en { - self.val |= 1 << 4; - } else { - self.val &= !(1 << 4); - } - self - } - - /// Returns `true` if normal bit synchronization is enabled. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::BitSync; - /// - /// let bs: BitSync = BitSync::RESET; - /// assert_eq!(bs.norm_bit_sync_en(), false); - /// let bs: BitSync = bs.set_norm_bit_sync_en(true); - /// assert_eq!(bs.norm_bit_sync_en(), true); - /// let bs: BitSync = bs.set_norm_bit_sync_en(false); - /// assert_eq!(bs.norm_bit_sync_en(), false); - /// ``` - pub const fn norm_bit_sync_en(&self) -> bool { - self.val & (1 << 4) != 0 - } -} - -impl From for u8 { - fn from(bs: BitSync) -> Self { - bs.val - } -} - -impl Default for BitSync { - fn default() -> Self { - Self::RESET - } -} diff --git a/embassy-stm32/src/subghz/cad_params.rs b/embassy-stm32/src/subghz/cad_params.rs deleted file mode 100644 index 1d90ff706..000000000 --- a/embassy-stm32/src/subghz/cad_params.rs +++ /dev/null @@ -1,230 +0,0 @@ -use super::Timeout; - -/// Number of symbols used for channel activity detection scans. -/// -/// Argument of [`CadParams::set_num_symbol`]. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum NbCadSymbol { - /// 1 symbol. - S1 = 0x0, - /// 2 symbols. - S2 = 0x1, - /// 4 symbols. - S4 = 0x2, - /// 8 symbols. - S8 = 0x3, - /// 16 symbols. - S16 = 0x4, -} - -/// Mode to enter after a channel activity detection scan is finished. -/// -/// Argument of [`CadParams::set_exit_mode`]. -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum ExitMode { - /// Standby with RC 13 MHz mode entry after CAD. - Standby = 0, - /// Standby with RC 13 MHz mode after CAD if no LoRa symbol is detected - /// during the CAD scan. - /// If a LoRa symbol is detected, the sub-GHz radio stays in RX mode - /// until a packet is received or until the CAD timeout is reached. - StandbyLoRa = 1, -} - -/// Channel activity detection (CAD) parameters. -/// -/// Argument of [`set_cad_params`]. -/// -/// # Recommended CAD settings -/// -/// This is taken directly from the datasheet. -/// -/// "The correct values selected in the table below must be carefully tested to -/// ensure a good detection at sensitivity level and to limit the number of -/// false detections" -/// -/// | SF (Spreading Factor) | [`set_det_peak`] | [`set_det_min`] | -/// |-----------------------|------------------|-----------------| -/// | 5 | 0x18 | 0x10 | -/// | 6 | 0x19 | 0x10 | -/// | 7 | 0x20 | 0x10 | -/// | 8 | 0x21 | 0x10 | -/// | 9 | 0x22 | 0x10 | -/// | 10 | 0x23 | 0x10 | -/// | 11 | 0x24 | 0x10 | -/// | 12 | 0x25 | 0x10 | -/// -/// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params -/// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak -/// [`set_det_min`]: crate::subghz::CadParams::set_det_min -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CadParams { - buf: [u8; 8], -} - -impl CadParams { - /// Create a new `CadParams`. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::CadParams; - /// - /// const CAD_PARAMS: CadParams = CadParams::new(); - /// assert_eq!(CAD_PARAMS, CadParams::default()); - /// ``` - pub const fn new() -> CadParams { - CadParams { - buf: [super::OpCode::SetCadParams as u8, 0, 0, 0, 0, 0, 0, 0], - } - .set_num_symbol(NbCadSymbol::S1) - .set_det_peak(0x18) - .set_det_min(0x10) - .set_exit_mode(ExitMode::Standby) - } - - /// Number of symbols used for a CAD scan. - /// - /// # Example - /// - /// Set the number of symbols to 4. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CadParams, NbCadSymbol}; - /// - /// const CAD_PARAMS: CadParams = CadParams::new().set_num_symbol(NbCadSymbol::S4); - /// # assert_eq!(CAD_PARAMS.as_slice()[1], 0x2); - /// ``` - #[must_use = "set_num_symbol returns a modified CadParams"] - pub const fn set_num_symbol(mut self, nb: NbCadSymbol) -> CadParams { - self.buf[1] = nb as u8; - self - } - - /// Used with [`set_det_min`] to correlate the LoRa symbol. - /// - /// See the table in [`CadParams`] docs for recommended values. - /// - /// # Example - /// - /// Setting the recommended value for a spreading factor of 7. - /// - /// ``` - /// use stm32wlxx_hal::subghz::CadParams; - /// - /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x20).set_det_min(0x10); - /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x20); - /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10); - /// ``` - /// - /// [`set_det_min`]: crate::subghz::CadParams::set_det_min - #[must_use = "set_det_peak returns a modified CadParams"] - pub const fn set_det_peak(mut self, peak: u8) -> CadParams { - self.buf[2] = peak; - self - } - - /// Used with [`set_det_peak`] to correlate the LoRa symbol. - /// - /// See the table in [`CadParams`] docs for recommended values. - /// - /// # Example - /// - /// Setting the recommended value for a spreading factor of 6. - /// - /// ``` - /// use stm32wlxx_hal::subghz::CadParams; - /// - /// const CAD_PARAMS: CadParams = CadParams::new().set_det_peak(0x18).set_det_min(0x10); - /// # assert_eq!(CAD_PARAMS.as_slice()[2], 0x18); - /// # assert_eq!(CAD_PARAMS.as_slice()[3], 0x10); - /// ``` - /// - /// [`set_det_peak`]: crate::subghz::CadParams::set_det_peak - #[must_use = "set_det_min returns a modified CadParams"] - pub const fn set_det_min(mut self, min: u8) -> CadParams { - self.buf[3] = min; - self - } - - /// Mode to enter after a channel activity detection scan is finished. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CadParams, ExitMode}; - /// - /// const CAD_PARAMS: CadParams = CadParams::new().set_exit_mode(ExitMode::Standby); - /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x00); - /// # assert_eq!(CAD_PARAMS.set_exit_mode(ExitMode::StandbyLoRa).as_slice()[4], 0x01); - /// ``` - #[must_use = "set_exit_mode returns a modified CadParams"] - pub const fn set_exit_mode(mut self, mode: ExitMode) -> CadParams { - self.buf[4] = mode as u8; - self - } - - /// Set the timeout. - /// - /// This is only used with [`ExitMode::StandbyLoRa`]. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, Timeout}; - /// - /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456); - /// const CAD_PARAMS: CadParams = CadParams::new() - /// .set_exit_mode(ExitMode::StandbyLoRa) - /// .set_timeout(TIMEOUT); - /// # assert_eq!(CAD_PARAMS.as_slice()[4], 0x01); - /// # assert_eq!(CAD_PARAMS.as_slice()[5], 0x12); - /// # assert_eq!(CAD_PARAMS.as_slice()[6], 0x34); - /// # assert_eq!(CAD_PARAMS.as_slice()[7], 0x56); - /// ``` - #[must_use = "set_timeout returns a modified CadParams"] - pub const fn set_timeout(mut self, to: Timeout) -> CadParams { - let to_bytes: [u8; 3] = to.as_bytes(); - self.buf[5] = to_bytes[0]; - self.buf[6] = to_bytes[1]; - self.buf[7] = to_bytes[2]; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CadParams, ExitMode, NbCadSymbol, Timeout}; - /// - /// const TIMEOUT: Timeout = Timeout::from_raw(0x123456); - /// const CAD_PARAMS: CadParams = CadParams::new() - /// .set_num_symbol(NbCadSymbol::S4) - /// .set_det_peak(0x18) - /// .set_det_min(0x10) - /// .set_exit_mode(ExitMode::StandbyLoRa) - /// .set_timeout(TIMEOUT); - /// - /// assert_eq!( - /// CAD_PARAMS.as_slice(), - /// &[0x88, 0x02, 0x18, 0x10, 0x01, 0x12, 0x34, 0x56] - /// ); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for CadParams { - fn default() -> Self { - Self::new() - } -} diff --git a/embassy-stm32/src/subghz/calibrate.rs b/embassy-stm32/src/subghz/calibrate.rs deleted file mode 100644 index f94538f86..000000000 --- a/embassy-stm32/src/subghz/calibrate.rs +++ /dev/null @@ -1,122 +0,0 @@ -/// Image calibration. -/// -/// Argument of [`calibrate_image`]. -/// -/// [`calibrate_image`]: crate::subghz::SubGhz::calibrate_image -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CalibrateImage(pub(crate) u8, pub(crate) u8); - -impl CalibrateImage { - /// Image calibration for the 430 - 440 MHz ISM band. - pub const ISM_430_440: CalibrateImage = CalibrateImage(0x6B, 0x6F); - - /// Image calibration for the 470 - 510 MHz ISM band. - pub const ISM_470_510: CalibrateImage = CalibrateImage(0x75, 0x81); - - /// Image calibration for the 779 - 787 MHz ISM band. - pub const ISM_779_787: CalibrateImage = CalibrateImage(0xC1, 0xC5); - - /// Image calibration for the 863 - 870 MHz ISM band. - pub const ISM_863_870: CalibrateImage = CalibrateImage(0xD7, 0xDB); - - /// Image calibration for the 902 - 928 MHz ISM band. - pub const ISM_902_928: CalibrateImage = CalibrateImage(0xE1, 0xE9); - - /// Create a new `CalibrateImage` structure from raw values. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::CalibrateImage; - /// - /// const CAL: CalibrateImage = CalibrateImage::new(0xE1, 0xE9); - /// assert_eq!(CAL, CalibrateImage::ISM_902_928); - /// ``` - pub const fn new(f1: u8, f2: u8) -> CalibrateImage { - CalibrateImage(f1, f2) - } - - /// Create a new `CalibrateImage` structure from two frequencies. - /// - /// # Arguments - /// - /// The units for `freq1` and `freq2` are in MHz. - /// - /// # Panics - /// - /// * Panics if `freq1` is less than `freq2`. - /// * Panics if `freq1` or `freq2` is not a multiple of 4MHz. - /// * Panics if `freq1` or `freq2` is greater than `1020`. - /// - /// # Example - /// - /// Create an image calibration for the 430 - 440 MHz ISM band. - /// - /// ``` - /// use stm32wlxx_hal::subghz::CalibrateImage; - /// - /// let cal: CalibrateImage = CalibrateImage::from_freq(428, 444); - /// assert_eq!(cal, CalibrateImage::ISM_430_440); - /// ``` - pub fn from_freq(freq1: u16, freq2: u16) -> CalibrateImage { - assert!(freq2 >= freq1); - assert_eq!(freq1 % 4, 0); - assert_eq!(freq2 % 4, 0); - assert!(freq1 <= 1020); - assert!(freq2 <= 1020); - CalibrateImage((freq1 / 4) as u8, (freq2 / 4) as u8) - } -} - -impl Default for CalibrateImage { - fn default() -> Self { - CalibrateImage::new(0xE1, 0xE9) - } -} - -/// Block calibration. -/// -/// Argument of [`calibrate`]. -/// -/// [`calibrate`]: crate::subghz::SubGhz::calibrate -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum Calibrate { - /// Image calibration - Image = 1 << 6, - /// RF-ADC bulk P calibration - AdcBulkP = 1 << 5, - /// RF-ADC bulk N calibration - AdcBulkN = 1 << 4, - /// RF-ADC pulse calibration - AdcPulse = 1 << 3, - /// RF-PLL calibration - Pll = 1 << 2, - /// Sub-GHz radio RC 13 MHz calibration - Rc13M = 1 << 1, - /// Sub-GHz radio RC 64 kHz calibration - Rc64K = 1, -} - -impl Calibrate { - /// Get the bitmask for the block calibration. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Calibrate; - /// - /// assert_eq!(Calibrate::Image.mask(), 0b0100_0000); - /// assert_eq!(Calibrate::AdcBulkP.mask(), 0b0010_0000); - /// assert_eq!(Calibrate::AdcBulkN.mask(), 0b0001_0000); - /// assert_eq!(Calibrate::AdcPulse.mask(), 0b0000_1000); - /// assert_eq!(Calibrate::Pll.mask(), 0b0000_0100); - /// assert_eq!(Calibrate::Rc13M.mask(), 0b0000_0010); - /// assert_eq!(Calibrate::Rc64K.mask(), 0b0000_0001); - /// ``` - pub const fn mask(self) -> u8 { - self as u8 - } -} diff --git a/embassy-stm32/src/subghz/fallback_mode.rs b/embassy-stm32/src/subghz/fallback_mode.rs deleted file mode 100644 index 50ec592f5..000000000 --- a/embassy-stm32/src/subghz/fallback_mode.rs +++ /dev/null @@ -1,37 +0,0 @@ -/// Fallback mode after successful packet transmission or packet reception. -/// -/// Argument of [`set_tx_rx_fallback_mode`]. -/// -/// [`set_tx_rx_fallback_mode`]: crate::subghz::SubGhz::set_tx_rx_fallback_mode. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum FallbackMode { - /// Standby mode entry. - Standby = 0x20, - /// Standby with HSE32 enabled. - StandbyHse = 0x30, - /// Frequency synthesizer entry. - Fs = 0x40, -} - -impl From for u8 { - fn from(fm: FallbackMode) -> Self { - fm as u8 - } -} - -impl Default for FallbackMode { - /// Default fallback mode after power-on reset. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FallbackMode; - /// - /// assert_eq!(FallbackMode::default(), FallbackMode::Standby); - /// ``` - fn default() -> Self { - FallbackMode::Standby - } -} diff --git a/embassy-stm32/src/subghz/hse_trim.rs b/embassy-stm32/src/subghz/hse_trim.rs deleted file mode 100644 index edfd52aca..000000000 --- a/embassy-stm32/src/subghz/hse_trim.rs +++ /dev/null @@ -1,107 +0,0 @@ -use super::ValueError; - -/// HSE32 load capacitor trimming. -/// -/// Argument of [`set_hse_in_trim`] and [`set_hse_out_trim`]. -/// -/// [`set_hse_in_trim`]: crate::subghz::SubGhz::set_hse_in_trim -/// [`set_hse_out_trim`]: crate::subghz::SubGhz::set_hse_out_trim -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct HseTrim { - val: u8, -} - -impl HseTrim { - /// Maximum capacitor value, ~33.4 pF - pub const MAX: HseTrim = HseTrim::from_raw(0x2F); - - /// Minimum capacitor value, ~11.3 pF - pub const MIN: HseTrim = HseTrim::from_raw(0x00); - - /// Power-on-reset capacitor value, ~20.3 pF - /// - /// This is the same as `default`. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::HseTrim; - /// - /// assert_eq!(HseTrim::POR, HseTrim::default()); - /// ``` - pub const POR: HseTrim = HseTrim::from_raw(0x12); - - /// Create a new [`HseTrim`] structure from a raw value. - /// - /// Values greater than the maximum of `0x2F` will be set to the maximum. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::HseTrim; - /// - /// assert_eq!(HseTrim::from_raw(0xFF), HseTrim::MAX); - /// assert_eq!(HseTrim::from_raw(0x2F), HseTrim::MAX); - /// assert_eq!(HseTrim::from_raw(0x00), HseTrim::MIN); - /// ``` - pub const fn from_raw(raw: u8) -> HseTrim { - if raw > 0x2F { - HseTrim { val: 0x2F } - } else { - HseTrim { val: raw } - } - } - - /// Create a HSE trim value from farads. - /// - /// Values greater than the maximum of 33.4 pF will be set to the maximum. - /// Values less than the minimum of 11.3 pF will be set to the minimum. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::HseTrim; - /// - /// assert!(HseTrim::from_farads(1.0).is_err()); - /// assert!(HseTrim::from_farads(1e-12).is_err()); - /// assert_eq!(HseTrim::from_farads(20.2e-12), Ok(HseTrim::default())); - /// ``` - pub fn from_farads(farads: f32) -> Result> { - const MAX: f32 = 33.4E-12; - const MIN: f32 = 11.3E-12; - if farads > MAX { - Err(ValueError::too_high(farads, MAX)) - } else if farads < MIN { - Err(ValueError::too_low(farads, MIN)) - } else { - Ok(HseTrim::from_raw(((farads - 11.3e-12) / 0.47e-12) as u8)) - } - } - - /// Get the capacitance as farads. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::HseTrim; - /// - /// assert_eq!((HseTrim::MAX.as_farads() * 10e11) as u8, 33); - /// assert_eq!((HseTrim::MIN.as_farads() * 10e11) as u8, 11); - /// ``` - pub fn as_farads(&self) -> f32 { - (self.val as f32) * 0.47E-12 + 11.3E-12 - } -} - -impl From for u8 { - fn from(ht: HseTrim) -> Self { - ht.val - } -} - -impl Default for HseTrim { - fn default() -> Self { - Self::POR - } -} diff --git a/embassy-stm32/src/subghz/irq.rs b/embassy-stm32/src/subghz/irq.rs deleted file mode 100644 index b56b8ad94..000000000 --- a/embassy-stm32/src/subghz/irq.rs +++ /dev/null @@ -1,292 +0,0 @@ -/// Interrupt lines. -/// -/// Argument of [`CfgIrq::irq_enable`] and [`CfgIrq::irq_disable`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum IrqLine { - /// Global interrupt. - Global, - /// Interrupt line 1. - /// - /// This will output to the [`RfIrq0`](crate::gpio::RfIrq0) pin. - Line1, - /// Interrupt line 2. - /// - /// This will output to the [`RfIrq1`](crate::gpio::RfIrq1) pin. - Line2, - /// Interrupt line 3. - /// - /// This will output to the [`RfIrq2`](crate::gpio::RfIrq2) pin. - Line3, -} - -impl IrqLine { - pub(super) const fn offset(&self) -> usize { - match self { - IrqLine::Global => 1, - IrqLine::Line1 => 3, - IrqLine::Line2 => 5, - IrqLine::Line3 => 7, - } - } -} - -/// IRQ bit mapping -/// -/// See table 37 "IRQ bit mapping and definition" in the reference manual for -/// more information. -#[repr(u16)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Irq { - /// Packet transmission finished. - /// - /// * Packet type: LoRa and GFSK - /// * Operation: TX - TxDone = (1 << 0), - /// Packet reception finished. - /// - /// * Packet type: LoRa and GFSK - /// * Operation: RX - RxDone = (1 << 1), - /// Preamble detected. - /// - /// * Packet type: LoRa and GFSK - /// * Operation: RX - PreambleDetected = (1 << 2), - /// Synchronization word valid. - /// - /// * Packet type: GFSK - /// * Operation: RX - SyncDetected = (1 << 3), - /// Header valid. - /// - /// * Packet type: LoRa - /// * Operation: RX - HeaderValid = (1 << 4), - /// Header CRC error. - /// - /// * Packet type: LoRa - /// * Operation: RX - HeaderErr = (1 << 5), - /// Dual meaning error. - /// - /// For GFSK RX this indicates a preamble, syncword, address, CRC, or length - /// error. - /// - /// For LoRa RX this indicates a CRC error. - Err = (1 << 6), - /// Channel activity detection finished. - /// - /// * Packet type: LoRa - /// * Operation: CAD - CadDone = (1 << 7), - /// Channel activity detected. - /// - /// * Packet type: LoRa - /// * Operation: CAD - CadDetected = (1 << 8), - /// RX or TX timeout. - /// - /// * Packet type: LoRa and GFSK - /// * Operation: RX and TX - Timeout = (1 << 9), -} - -impl Irq { - /// Get the bitmask for an IRQ. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Irq; - /// - /// assert_eq!(Irq::TxDone.mask(), 0x0001); - /// assert_eq!(Irq::Timeout.mask(), 0x0200); - /// ``` - pub const fn mask(self) -> u16 { - self as u16 - } -} - -/// Argument for [`set_irq_cfg`]. -/// -/// [`set_irq_cfg`]: crate::subghz::SubGhz::set_irq_cfg -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CfgIrq { - buf: [u8; 9], -} - -impl CfgIrq { - /// Create a new `CfgIrq`. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// The default value has all interrupts disabled on all lines. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::CfgIrq; - /// - /// const IRQ_CFG: CfgIrq = CfgIrq::new(); - /// ``` - pub const fn new() -> CfgIrq { - CfgIrq { - buf: [ - super::OpCode::CfgDioIrq as u8, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - ], - } - } - - /// Enable an interrupt. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine}; - /// - /// const IRQ_CFG: CfgIrq = CfgIrq::new() - /// .irq_enable(IrqLine::Global, Irq::TxDone) - /// .irq_enable(IrqLine::Global, Irq::Timeout); - /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02); - /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01); - /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00); - /// ``` - #[must_use = "irq_enable returns a modified CfgIrq"] - pub const fn irq_enable(mut self, line: IrqLine, irq: Irq) -> CfgIrq { - let mask: u16 = irq as u16; - let offset: usize = line.offset(); - self.buf[offset] |= ((mask >> 8) & 0xFF) as u8; - self.buf[offset + 1] |= (mask & 0xFF) as u8; - self - } - - /// Enable an interrupt on all lines. - /// - /// As far as I can tell with empirical testing all IRQ lines need to be - /// enabled for the internal interrupt to be pending in the NVIC. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CfgIrq, Irq}; - /// - /// const IRQ_CFG: CfgIrq = CfgIrq::new() - /// .irq_enable_all(Irq::TxDone) - /// .irq_enable_all(Irq::Timeout); - /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x02); - /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x01); - /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x02); - /// # assert_eq!(IRQ_CFG.as_slice()[4], 0x01); - /// # assert_eq!(IRQ_CFG.as_slice()[5], 0x02); - /// # assert_eq!(IRQ_CFG.as_slice()[6], 0x01); - /// # assert_eq!(IRQ_CFG.as_slice()[7], 0x02); - /// # assert_eq!(IRQ_CFG.as_slice()[8], 0x01); - /// ``` - #[must_use = "irq_enable_all returns a modified CfgIrq"] - pub const fn irq_enable_all(mut self, irq: Irq) -> CfgIrq { - let mask: [u8; 2] = irq.mask().to_be_bytes(); - - self.buf[1] |= mask[0]; - self.buf[2] |= mask[1]; - self.buf[3] |= mask[0]; - self.buf[4] |= mask[1]; - self.buf[5] |= mask[0]; - self.buf[6] |= mask[1]; - self.buf[7] |= mask[0]; - self.buf[8] |= mask[1]; - - self - } - - /// Disable an interrupt. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CfgIrq, Irq, IrqLine}; - /// - /// const IRQ_CFG: CfgIrq = CfgIrq::new() - /// .irq_enable(IrqLine::Global, Irq::TxDone) - /// .irq_enable(IrqLine::Global, Irq::Timeout) - /// .irq_disable(IrqLine::Global, Irq::TxDone) - /// .irq_disable(IrqLine::Global, Irq::Timeout); - /// # assert_eq!(IRQ_CFG.as_slice()[1], 0x00); - /// # assert_eq!(IRQ_CFG.as_slice()[2], 0x00); - /// # assert_eq!(IRQ_CFG.as_slice()[3], 0x00); - /// ``` - #[must_use = "irq_disable returns a modified CfgIrq"] - pub const fn irq_disable(mut self, line: IrqLine, irq: Irq) -> CfgIrq { - let mask: u16 = !(irq as u16); - let offset: usize = line.offset(); - self.buf[offset] &= ((mask >> 8) & 0xFF) as u8; - self.buf[offset + 1] &= (mask & 0xFF) as u8; - self - } - - /// Disable an interrupt on all lines. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CfgIrq, Irq}; - /// - /// const IRQ_CFG: CfgIrq = CfgIrq::new() - /// .irq_enable_all(Irq::TxDone) - /// .irq_enable_all(Irq::Timeout) - /// .irq_disable_all(Irq::TxDone) - /// .irq_disable_all(Irq::Timeout); - /// # assert_eq!(IRQ_CFG, CfgIrq::new()); - /// ``` - #[must_use = "irq_disable_all returns a modified CfgIrq"] - pub const fn irq_disable_all(mut self, irq: Irq) -> CfgIrq { - let mask: [u8; 2] = (!irq.mask()).to_be_bytes(); - - self.buf[1] &= mask[0]; - self.buf[2] &= mask[1]; - self.buf[3] &= mask[0]; - self.buf[4] &= mask[1]; - self.buf[5] &= mask[0]; - self.buf[6] &= mask[1]; - self.buf[7] &= mask[0]; - self.buf[8] &= mask[1]; - - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CfgIrq, Irq}; - /// - /// const IRQ_CFG: CfgIrq = CfgIrq::new() - /// .irq_enable_all(Irq::TxDone) - /// .irq_enable_all(Irq::Timeout); - /// - /// assert_eq!( - /// IRQ_CFG.as_slice(), - /// &[0x08, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01] - /// ); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for CfgIrq { - fn default() -> Self { - Self::new() - } -} diff --git a/embassy-stm32/src/subghz/lora_sync_word.rs b/embassy-stm32/src/subghz/lora_sync_word.rs deleted file mode 100644 index 2c163104e..000000000 --- a/embassy-stm32/src/subghz/lora_sync_word.rs +++ /dev/null @@ -1,20 +0,0 @@ -/// LoRa synchronization word. -/// -/// Argument of [`set_lora_sync_word`][crate::subghz::SubGhz::set_lora_sync_word]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum LoRaSyncWord { - /// LoRa private network. - Private, - /// LoRa public network. - Public, -} - -impl LoRaSyncWord { - pub(crate) const fn bytes(self) -> [u8; 2] { - match self { - LoRaSyncWord::Private => [0x14, 0x24], - LoRaSyncWord::Public => [0x34, 0x44], - } - } -} diff --git a/embassy-stm32/src/subghz/mod.rs b/embassy-stm32/src/subghz/mod.rs deleted file mode 100644 index cd566ba24..000000000 --- a/embassy-stm32/src/subghz/mod.rs +++ /dev/null @@ -1,1004 +0,0 @@ -//! Sub-GHz radio operating in the 150 - 960 MHz ISM band -//! -//! The main radio type is [`SubGhz`]. -//! -//! ## LoRa user notice -//! -//! The Sub-GHz radio may have an undocumented erratum, see this ST community -//! post for more information: [link] -//! -//! [link]: https://community.st.com/s/question/0D53W00000hR8kpSAC/stm32wl55-erratum-clairification -//! -//! NOTE: This HAL is based on https://github.com/newAM/stm32wl-hal, but adopted for use with the stm32-metapac -//! and SPI HALs. - -mod bit_sync; -mod cad_params; -mod calibrate; -mod fallback_mode; -mod hse_trim; -mod irq; -mod lora_sync_word; -mod mod_params; -mod ocp; -mod op_error; -mod pa_config; -mod packet_params; -mod packet_status; -mod packet_type; -mod pkt_ctrl; -mod pmode; -mod pwr_ctrl; -mod reg_mode; -mod rf_frequency; -mod rx_timeout_stop; -mod sleep_cfg; -mod smps; -mod standby_clk; -mod stats; -mod status; -mod tcxo_mode; -mod timeout; -mod tx_params; -mod value_error; - -pub use bit_sync::BitSync; -pub use cad_params::{CadParams, ExitMode, NbCadSymbol}; -pub use calibrate::{Calibrate, CalibrateImage}; -use embassy_hal_common::ratio::Ratio; -pub use fallback_mode::FallbackMode; -pub use hse_trim::HseTrim; -pub use irq::{CfgIrq, Irq, IrqLine}; -pub use lora_sync_word::LoRaSyncWord; -pub use mod_params::{ - BpskModParams, CodingRate, FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape, LoRaBandwidth, - LoRaModParams, SpreadingFactor, -}; -pub use ocp::Ocp; -pub use op_error::OpError; -pub use pa_config::{PaConfig, PaSel}; -pub use packet_params::{ - AddrComp, BpskPacketParams, CrcType, GenericPacketParams, HeaderType, LoRaPacketParams, PreambleDetection, -}; -pub use packet_status::{FskPacketStatus, LoRaPacketStatus}; -pub use packet_type::PacketType; -pub use pkt_ctrl::{InfSeqSel, PktCtrl}; -pub use pmode::PMode; -pub use pwr_ctrl::{CurrentLim, PwrCtrl}; -pub use reg_mode::RegMode; -pub use rf_frequency::RfFreq; -pub use rx_timeout_stop::RxTimeoutStop; -pub use sleep_cfg::{SleepCfg, Startup}; -pub use smps::SmpsDrv; -pub use standby_clk::StandbyClk; -pub use stats::{FskStats, LoRaStats, Stats}; -pub use status::{CmdStatus, Status, StatusMode}; -pub use tcxo_mode::{TcxoMode, TcxoTrim}; -pub use timeout::Timeout; -pub use tx_params::{RampTime, TxParams}; -pub use value_error::ValueError; - -use crate::dma::NoDma; -use crate::peripherals::SUBGHZSPI; -use crate::rcc::sealed::RccPeripheral; -use crate::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; -use crate::time::Hertz; -use crate::{pac, Peripheral}; - -/// Passthrough for SPI errors (for now) -pub type Error = crate::spi::Error; - -struct Nss { - _priv: (), -} - -impl Nss { - #[inline(always)] - pub fn new() -> Nss { - Self::clear(); - Nss { _priv: () } - } - - /// Clear NSS, enabling SPI transactions - #[inline(always)] - fn clear() { - let pwr = pac::PWR; - unsafe { - pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW)); - } - } - - /// Set NSS, disabling SPI transactions - #[inline(always)] - fn set() { - let pwr = pac::PWR; - unsafe { - pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH)); - } - } -} - -impl Drop for Nss { - fn drop(&mut self) { - Self::set() - } -} - -/// Wakeup the radio from sleep mode. -/// -/// # Safety -/// -/// 1. This must not be called when the SubGHz radio is in use. -/// 2. This must not be called when the SubGHz SPI bus is in use. -/// -/// # Example -/// -/// See [`SubGhz::set_sleep`] -#[inline] -unsafe fn wakeup() { - Nss::clear(); - // RM0453 rev 2 page 171 section 5.7.2 "Sleep mode" - // on a firmware request via the sub-GHz radio SPI NSS signal - // (keeping sub-GHz radio SPI NSS low for at least 20 μs) - // - // I have found this to be a more reliable mechanism for ensuring NSS is - // pulled low for long enough to wake the radio. - while rfbusys() {} - Nss::set(); -} - -/// Returns `true` if the radio is busy. -/// -/// This may not be set immediately after NSS going low. -/// -/// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more -/// details. -#[inline] -fn rfbusys() -> bool { - // safety: atmoic read with no side-effects - //unsafe { (*pac::PWR::ptr()).sr2.read().rfbusys().is_busy() } - let pwr = pac::PWR; - unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } -} - -/* -/// Returns `true` if the radio is busy or NSS is low. -/// -/// See RM0461 Rev 4 section 5.3 page 181 "Radio busy management" for more -/// details. -#[inline] -fn rfbusyms() -> bool { - let pwr = pac::PWR; - unsafe { pwr.sr2().read().rfbusyms() == pac::pwr::vals::Rfbusyms::BUSY } -} -*/ - -/// Sub-GHz radio peripheral -pub struct SubGhz<'d, Tx, Rx> { - spi: Spi<'d, SUBGHZSPI, Tx, Rx>, -} - -impl<'d, Tx, Rx> SubGhz<'d, Tx, Rx> { - fn pulse_radio_reset() { - let rcc = pac::RCC; - unsafe { - rcc.csr().modify(|w| w.set_rfrst(true)); - rcc.csr().modify(|w| w.set_rfrst(false)); - } - } - - // TODO: This should be replaced with async handling based on IRQ - fn poll_not_busy(&self) { - let mut count: u32 = 1_000_000; - while rfbusys() { - count -= 1; - if count == 0 { - let pwr = pac::PWR; - unsafe { - panic!( - "rfbusys timeout pwr.sr2=0x{:X} pwr.subghzspicr=0x{:X} pwr.cr1=0x{:X}", - pwr.sr2().read().0, - pwr.subghzspicr().read().0, - pwr.cr1().read().0 - ); - } - } - } - } - - /// Create a new sub-GHz radio driver from a peripheral. - /// - /// This will reset the radio and the SPI bus, and enable the peripheral - /// clock. - pub fn new( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - ) -> Self { - Self::pulse_radio_reset(); - - // see RM0453 rev 1 section 7.2.13 page 291 - // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. - // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. - let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); - let mut config = SpiConfig::default(); - config.mode = MODE_0; - config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new_subghz(peri, txdma, rxdma, clk, config); - - unsafe { wakeup() }; - - SubGhz { spi } - } - - pub fn is_busy(&mut self) -> bool { - rfbusys() - } - - pub fn reset(&mut self) { - Self::pulse_radio_reset(); - } -} - -impl<'d> SubGhz<'d, NoDma, NoDma> { - fn read(&mut self, opcode: OpCode, data: &mut [u8]) -> Result<(), Error> { - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[opcode as u8])?; - self.spi.blocking_transfer_in_place(data)?; - } - self.poll_not_busy(); - Ok(()) - } - - /// Read one byte from the sub-Ghz radio. - fn read_1(&mut self, opcode: OpCode) -> Result { - let mut buf: [u8; 1] = [0; 1]; - self.read(opcode, &mut buf)?; - Ok(buf[0]) - } - - /// Read a fixed number of bytes from the sub-Ghz radio. - fn read_n(&mut self, opcode: OpCode) -> Result<[u8; N], Error> { - let mut buf: [u8; N] = [0; N]; - self.read(opcode, &mut buf)?; - Ok(buf) - } - - fn write(&mut self, data: &[u8]) -> Result<(), Error> { - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(data)?; - } - self.poll_not_busy(); - Ok(()) - } - - pub fn write_buffer(&mut self, offset: u8, data: &[u8]) -> Result<(), Error> { - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[OpCode::WriteBuffer as u8, offset])?; - self.spi.blocking_write(data)?; - } - self.poll_not_busy(); - - Ok(()) - } - - /// Read the radio buffer at the given offset. - /// - /// The offset and length of a received packet is provided by - /// [`rx_buffer_status`](Self::rx_buffer_status). - pub fn read_buffer(&mut self, offset: u8, buf: &mut [u8]) -> Result { - let mut status_buf: [u8; 1] = [0]; - - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[OpCode::ReadBuffer as u8, offset])?; - self.spi.blocking_transfer_in_place(&mut status_buf)?; - self.spi.blocking_transfer_in_place(buf)?; - } - self.poll_not_busy(); - - Ok(status_buf[0].into()) - } -} - -// helper to pack register writes into a single buffer to avoid multiple DMA -// transfers -macro_rules! wr_reg { - [$reg:ident, $($data:expr),+] => { - &[ - OpCode::WriteRegister as u8, - Register::$reg.address().to_be_bytes()[0], - Register::$reg.address().to_be_bytes()[1], - $($data),+ - ] - }; -} - -// 5.8.2 -/// Register access -impl<'d> SubGhz<'d, NoDma, NoDma> { - // register write with variable length data - fn write_register(&mut self, register: Register, data: &[u8]) -> Result<(), Error> { - let addr: [u8; 2] = register.address().to_be_bytes(); - - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi - .blocking_write(&[OpCode::WriteRegister as u8, addr[0], addr[1]])?; - self.spi.blocking_write(data)?; - } - self.poll_not_busy(); - - Ok(()) - } - - /// Set the LoRa bit synchronization. - pub fn set_bit_sync(&mut self, bs: BitSync) -> Result<(), Error> { - self.write(wr_reg![GBSYNC, bs.as_bits()]) - } - - /// Set the generic packet control register. - pub fn set_pkt_ctrl(&mut self, pkt_ctrl: PktCtrl) -> Result<(), Error> { - self.write(wr_reg![GPKTCTL1A, pkt_ctrl.as_bits()]) - } - - /// Set the initial value for generic packet whitening. - /// - /// This sets the first 8 bits, the 9th bit is set with - /// [`set_pkt_ctrl`](Self::set_pkt_ctrl). - pub fn set_init_whitening(&mut self, init: u8) -> Result<(), Error> { - self.write(wr_reg![GWHITEINIRL, init]) - } - - /// Set the initial value for generic packet CRC polynomial. - pub fn set_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> { - let bytes: [u8; 2] = polynomial.to_be_bytes(); - self.write(wr_reg![GCRCINIRH, bytes[0], bytes[1]]) - } - - /// Set the generic packet CRC polynomial. - pub fn set_initial_crc_polynomial(&mut self, polynomial: u16) -> Result<(), Error> { - let bytes: [u8; 2] = polynomial.to_be_bytes(); - self.write(wr_reg![GCRCPOLRH, bytes[0], bytes[1]]) - } - - /// Set the synchronization word registers. - pub fn set_sync_word(&mut self, sync_word: &[u8; 8]) -> Result<(), Error> { - self.write_register(Register::GSYNC7, sync_word) - } - - /// Set the LoRa synchronization word registers. - pub fn set_lora_sync_word(&mut self, sync_word: LoRaSyncWord) -> Result<(), Error> { - let bytes: [u8; 2] = sync_word.bytes(); - self.write(wr_reg![LSYNCH, bytes[0], bytes[1]]) - } - - /// Set the RX gain control. - pub fn set_rx_gain(&mut self, pmode: PMode) -> Result<(), Error> { - self.write(wr_reg![RXGAINC, pmode as u8]) - } - - /// Set the power amplifier over current protection. - pub fn set_pa_ocp(&mut self, ocp: Ocp) -> Result<(), Error> { - self.write(wr_reg![PAOCP, ocp as u8]) - } - - /// Restart the radio RTC. - /// - /// This is used to workaround an erratum for [`set_rx_duty_cycle`]. - /// - /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle - pub fn restart_rtc(&mut self) -> Result<(), Error> { - self.write(wr_reg![RTCCTLR, 0b1]) - } - - /// Set the radio real-time-clock period. - /// - /// This is used to workaround an erratum for [`set_rx_duty_cycle`]. - /// - /// [`set_rx_duty_cycle`]: crate::subghz::SubGhz::set_rx_duty_cycle - pub fn set_rtc_period(&mut self, period: Timeout) -> Result<(), Error> { - let tobits: u32 = period.into_bits(); - self.write(wr_reg![ - RTCPRDR2, - (tobits >> 16) as u8, - (tobits >> 8) as u8, - tobits as u8 - ]) - } - - /// Set the HSE32 crystal OSC_IN load capacitor trimming. - pub fn set_hse_in_trim(&mut self, trim: HseTrim) -> Result<(), Error> { - self.write(wr_reg![HSEINTRIM, trim.into()]) - } - - /// Set the HSE32 crystal OSC_OUT load capacitor trimming. - pub fn set_hse_out_trim(&mut self, trim: HseTrim) -> Result<(), Error> { - self.write(wr_reg![HSEOUTTRIM, trim.into()]) - } - - /// Set the SMPS clock detection enabled. - /// - /// SMPS clock detection must be enabled fore enabling the SMPS. - pub fn set_smps_clock_det_en(&mut self, en: bool) -> Result<(), Error> { - self.write(wr_reg![SMPSC0, (en as u8) << 6]) - } - - /// Set the power current limiting. - pub fn set_pwr_ctrl(&mut self, pwr_ctrl: PwrCtrl) -> Result<(), Error> { - self.write(wr_reg![PC, pwr_ctrl.as_bits()]) - } - - /// Set the maximum SMPS drive capability. - pub fn set_smps_drv(&mut self, drv: SmpsDrv) -> Result<(), Error> { - self.write(wr_reg![SMPSC2, (drv as u8) << 1]) - } - - /// Set the node address. - /// - /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based - /// on node address. - pub fn set_node_addr(&mut self, addr: u8) -> Result<(), Error> { - self.write(wr_reg![NODE, addr]) - } - - /// Set the broadcast address. - /// - /// Used with [`GenericPacketParams::set_addr_comp`] to filter packets based - /// on broadcast address. - pub fn set_broadcast_addr(&mut self, addr: u8) -> Result<(), Error> { - self.write(wr_reg![BROADCAST, addr]) - } - - /// Set both the broadcast address and node address. - /// - /// This is a combination of [`set_node_addr`] and [`set_broadcast_addr`] - /// in a single SPI transfer. - /// - /// [`set_node_addr`]: Self::set_node_addr - /// [`set_broadcast_addr`]: Self::set_broadcast_addr - pub fn set_addrs(&mut self, node: u8, broadcast: u8) -> Result<(), Error> { - self.write(wr_reg![NODE, node, broadcast]) - } -} - -// 5.8.3 -/// Operating mode commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Put the radio into sleep mode. - /// - /// This command is only accepted in standby mode. - /// The cfg argument allows some optional functions to be maintained - /// in sleep mode. - /// - /// # Safety - /// - /// 1. After the `set_sleep` command, the sub-GHz radio NSS must not go low - /// for 500 μs. - /// No reason is provided, the reference manual (RM0453 rev 2) simply - /// says "you must". - /// 2. The radio cannot be used while in sleep mode. - /// 3. The radio must be woken up with [`wakeup`] before resuming use. - /// - /// # Example - /// - /// Put the radio into sleep mode. - /// - /// ```no_run - /// # let dp = unsafe { embassy_stm32::pac::Peripherals::steal() }; - /// # let mut sg = embassy_stm32::subghz::SubGhz::new(p.SUBGHZSPI, ...); - /// use embassy_stm32::{ - /// subghz::{wakeup, SleepCfg, StandbyClk}, - /// }; - /// - /// sg.set_standby(StandbyClk::Rc)?; - /// unsafe { sg.set_sleep(SleepCfg::default())? }; - /// embassy_time::Timer::after(embassy_time::Duration::from_micros(500)).await; - /// unsafe { wakeup() }; - /// # Ok::<(), embassy_stm32::subghz::Error>(()) - /// ``` - pub unsafe fn set_sleep(&mut self, cfg: SleepCfg) -> Result<(), Error> { - // poll for busy before, but not after - // radio idles with busy high while in sleep mode - self.poll_not_busy(); - { - let _nss: Nss = Nss::new(); - self.spi.blocking_write(&[OpCode::SetSleep as u8, u8::from(cfg)])?; - } - Ok(()) - } - - /// Put the radio into standby mode. - pub fn set_standby(&mut self, standby_clk: StandbyClk) -> Result<(), Error> { - self.write(&[OpCode::SetStandby as u8, u8::from(standby_clk)]) - } - - /// Put the subghz radio into frequency synthesis mode. - /// - /// The RF-PLL frequency must be set with [`set_rf_frequency`] before using - /// this command. - /// - /// Check the datasheet for more information, this is a test command but - /// I honestly do not see any use for it. Please update this description - /// if you know more than I do. - /// - /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency - pub fn set_fs(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetFs.into()]) - } - - /// Setup the sub-GHz radio for TX. - pub fn set_tx(&mut self, timeout: Timeout) -> Result<(), Error> { - let tobits: u32 = timeout.into_bits(); - self.write(&[ - OpCode::SetTx.into(), - (tobits >> 16) as u8, - (tobits >> 8) as u8, - tobits as u8, - ]) - } - - /// Setup the sub-GHz radio for RX. - pub fn set_rx(&mut self, timeout: Timeout) -> Result<(), Error> { - let tobits: u32 = timeout.into_bits(); - self.write(&[ - OpCode::SetRx.into(), - (tobits >> 16) as u8, - (tobits >> 8) as u8, - tobits as u8, - ]) - } - - /// Allows selection of the receiver event which stops the RX timeout timer. - pub fn set_rx_timeout_stop(&mut self, rx_timeout_stop: RxTimeoutStop) -> Result<(), Error> { - self.write(&[OpCode::SetStopRxTimerOnPreamble.into(), rx_timeout_stop.into()]) - } - - /// Put the radio in non-continuous RX mode. - /// - /// This command must be sent in Standby mode. - /// This command is only functional with FSK and LoRa packet type. - /// - /// The following steps are performed: - /// 1. Save sub-GHz radio configuration. - /// 2. Enter Receive mode and listen for a preamble for the specified `rx_period`. - /// 3. Upon the detection of a preamble, the `rx_period` timeout is stopped - /// and restarted with the value 2 x `rx_period` + `sleep_period`. - /// During this new period, the sub-GHz radio looks for the detection of - /// a synchronization word when in (G)FSK modulation mode, - /// or a header when in LoRa modulation mode. - /// 4. If no packet is received during the listen period defined by - /// 2 x `rx_period` + `sleep_period`, the sleep mode is entered for a - /// duration of `sleep_period`. At the end of the receive period, - /// the sub-GHz radio takes some time to save the context before starting - /// the sleep period. - /// 5. After the sleep period, a new listening period is automatically - /// started. The sub-GHz radio restores the sub-GHz radio configuration - /// and continuous with step 2. - /// - /// The listening mode is terminated in one of the following cases: - /// * if a packet is received during the listening period: the sub-GHz radio - /// issues a [`RxDone`] interrupt and enters standby mode. - /// * if [`set_standby`] is sent during the listening period or after the - /// sub-GHz has been requested to exit sleep mode by sub-GHz radio SPI NSS - /// - /// # Erratum - /// - /// When a preamble is detected the radio should restart the RX timeout - /// with a value of 2 × `rx_period` + `sleep_period`. - /// Instead the radio erroneously uses `sleep_period`. - /// - /// To workaround this use [`restart_rtc`] and [`set_rtc_period`] to - /// reprogram the radio timeout to 2 × `rx_period` + `sleep_period`. - /// - /// Use code similar to this in the [`PreambleDetected`] interrupt handler. - /// - /// ```no_run - /// # let rx_period: Timeout = Timeout::from_millis_sat(100); - /// # let sleep_period: Timeout = Timeout::from_millis_sat(100); - /// # let mut sg = unsafe { stm32wlxx_hal::subghz::SubGhz::steal() }; - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// let period: Timeout = rx_period - /// .saturating_add(rx_period) - /// .saturating_add(sleep_period); - /// - /// sg.set_rtc_period(period)?; - /// sg.restart_rtc()?; - /// # Ok::<(), stm32wlxx_hal::subghz::Error>(()) - /// ``` - /// - /// Please read the erratum for more details. - /// - /// [`PreambleDetected`]: crate::subghz::Irq::PreambleDetected - /// [`restart_rtc`]: crate::subghz::SubGhz::restart_rtc - /// [`RxDone`]: crate::subghz::Irq::RxDone - /// [`set_rf_frequency`]: crate::subghz::SubGhz::set_rf_frequency - /// [`set_rtc_period`]: crate::subghz::SubGhz::set_rtc_period - /// [`set_standby`]: crate::subghz::SubGhz::set_standby - pub fn set_rx_duty_cycle(&mut self, rx_period: Timeout, sleep_period: Timeout) -> Result<(), Error> { - let rx_period_bits: u32 = rx_period.into_bits(); - let sleep_period_bits: u32 = sleep_period.into_bits(); - self.write(&[ - OpCode::SetRxDutyCycle.into(), - (rx_period_bits >> 16) as u8, - (rx_period_bits >> 8) as u8, - rx_period_bits as u8, - (sleep_period_bits >> 16) as u8, - (sleep_period_bits >> 8) as u8, - sleep_period_bits as u8, - ]) - } - - /// Channel Activity Detection (CAD) with LoRa packets. - /// - /// The channel activity detection (CAD) is a specific LoRa operation mode, - /// where the sub-GHz radio searches for a LoRa radio signal. - /// After the search is completed, the Standby mode is automatically - /// entered, CAD is done and IRQ is generated. - /// When a LoRa radio signal is detected, the CAD detected IRQ is also - /// generated. - /// - /// The length of the search must be configured with [`set_cad_params`] - /// prior to calling `set_cad`. - /// - /// [`set_cad_params`]: crate::subghz::SubGhz::set_cad_params - pub fn set_cad(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetCad.into()]) - } - - /// Generate a continuous transmit tone at the RF-PLL frequency. - /// - /// The sub-GHz radio remains in continuous transmit tone mode until a mode - /// configuration command is received. - pub fn set_tx_continuous_wave(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetTxContinuousWave as u8]) - } - - /// Generate an infinite preamble at the RF-PLL frequency. - /// - /// The preamble is an alternating 0s and 1s sequence in generic (G)FSK and - /// (G)MSK modulations. - /// The preamble is symbol 0 in LoRa modulation. - /// The sub-GHz radio remains in infinite preamble mode until a mode - /// configuration command is received. - pub fn set_tx_continuous_preamble(&mut self) -> Result<(), Error> { - self.write(&[OpCode::SetTxContinuousPreamble as u8]) - } -} - -// 5.8.4 -/// Radio configuration commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Set the packet type (modulation scheme). - pub fn set_packet_type(&mut self, packet_type: PacketType) -> Result<(), Error> { - self.write(&[OpCode::SetPacketType as u8, packet_type as u8]) - } - - /// Get the packet type. - pub fn packet_type(&mut self) -> Result, Error> { - let pkt_type: [u8; 2] = self.read_n(OpCode::GetPacketType)?; - Ok(PacketType::from_raw(pkt_type[1])) - } - - /// Set the radio carrier frequency. - pub fn set_rf_frequency(&mut self, freq: &RfFreq) -> Result<(), Error> { - self.write(freq.as_slice()) - } - - /// Set the transmit output power and the PA ramp-up time. - pub fn set_tx_params(&mut self, params: &TxParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Power amplifier configuration. - /// - /// Used to customize the maximum output power and efficiency. - pub fn set_pa_config(&mut self, pa_config: &PaConfig) -> Result<(), Error> { - self.write(pa_config.as_slice()) - } - - /// Operating mode to enter after a successful packet transmission or - /// packet reception. - pub fn set_tx_rx_fallback_mode(&mut self, fm: FallbackMode) -> Result<(), Error> { - self.write(&[OpCode::SetTxRxFallbackMode as u8, fm as u8]) - } - - /// Set channel activity detection (CAD) parameters. - pub fn set_cad_params(&mut self, params: &CadParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the data buffer base address for the packet handling in TX and RX. - /// - /// There is a single buffer for both TX and RX. - /// The buffer is not memory mapped, it is accessed via the - /// [`read_buffer`](SubGhz::read_buffer) and - /// [`write_buffer`](SubGhz::write_buffer) methods. - pub fn set_buffer_base_address(&mut self, tx: u8, rx: u8) -> Result<(), Error> { - self.write(&[OpCode::SetBufferBaseAddress as u8, tx, rx]) - } - - /// Set the (G)FSK modulation parameters. - pub fn set_fsk_mod_params(&mut self, params: &FskModParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the LoRa modulation parameters. - pub fn set_lora_mod_params(&mut self, params: &LoRaModParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the BPSK modulation parameters. - pub fn set_bpsk_mod_params(&mut self, params: &BpskModParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the generic (FSK) packet parameters. - pub fn set_packet_params(&mut self, params: &GenericPacketParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the BPSK packet parameters. - pub fn set_bpsk_packet_params(&mut self, params: &BpskPacketParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the LoRa packet parameters. - pub fn set_lora_packet_params(&mut self, params: &LoRaPacketParams) -> Result<(), Error> { - self.write(params.as_slice()) - } - - /// Set the number of LoRa symbols to be received before starting the - /// reception of a LoRa packet. - /// - /// Packet reception is started after `n` + 1 symbols are detected. - pub fn set_lora_symb_timeout(&mut self, n: u8) -> Result<(), Error> { - self.write(&[OpCode::SetLoRaSymbTimeout.into(), n]) - } -} - -// 5.8.5 -/// Communication status and information commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Get the radio status. - /// - /// The hardware (or documentation) appears to have many bugs where this - /// will return reserved values. - /// See this thread in the ST community for details: [link] - /// - /// [link]: https://community.st.com/s/question/0D53W00000hR9GQSA0/stm32wl55-getstatus-command-returns-reserved-cmdstatus - pub fn status(&mut self) -> Result { - Ok(self.read_1(OpCode::GetStatus)?.into()) - } - - /// Get the RX buffer status. - /// - /// The return tuple is (status, payload_length, buffer_pointer). - pub fn rx_buffer_status(&mut self) -> Result<(Status, u8, u8), Error> { - let data: [u8; 3] = self.read_n(OpCode::GetRxBufferStatus)?; - Ok((data[0].into(), data[1], data[2])) - } - - /// Returns information on the last received (G)FSK packet. - pub fn fsk_packet_status(&mut self) -> Result { - Ok(FskPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?)) - } - - /// Returns information on the last received LoRa packet. - pub fn lora_packet_status(&mut self) -> Result { - Ok(LoRaPacketStatus::from(self.read_n(OpCode::GetPacketStatus)?)) - } - - /// Get the instantaneous signal strength during packet reception. - /// - /// The units are in dbm. - pub fn rssi_inst(&mut self) -> Result<(Status, Ratio), Error> { - let data: [u8; 2] = self.read_n(OpCode::GetRssiInst)?; - let status: Status = data[0].into(); - let rssi: Ratio = Ratio::new_raw(i16::from(data[1]), -2); - - Ok((status, rssi)) - } - - /// (G)FSK packet stats. - pub fn fsk_stats(&mut self) -> Result, Error> { - let data: [u8; 7] = self.read_n(OpCode::GetStats)?; - Ok(Stats::from_raw_fsk(data)) - } - - /// LoRa packet stats. - pub fn lora_stats(&mut self) -> Result, Error> { - let data: [u8; 7] = self.read_n(OpCode::GetStats)?; - Ok(Stats::from_raw_lora(data)) - } - - /// Reset the stats as reported in [`lora_stats`](SubGhz::lora_stats) and - /// [`fsk_stats`](SubGhz::fsk_stats). - pub fn reset_stats(&mut self) -> Result<(), Error> { - const RESET_STATS: [u8; 7] = [0x00; 7]; - self.write(&RESET_STATS) - } -} - -// 5.8.6 -/// IRQ commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Set the interrupt configuration. - pub fn set_irq_cfg(&mut self, cfg: &CfgIrq) -> Result<(), Error> { - self.write(cfg.as_slice()) - } - - /// Get the IRQ status. - pub fn irq_status(&mut self) -> Result<(Status, u16), Error> { - let data: [u8; 3] = self.read_n(OpCode::GetIrqStatus)?; - let irq_status: u16 = u16::from_be_bytes([data[1], data[2]]); - Ok((data[0].into(), irq_status)) - } - - /// Clear the IRQ status. - pub fn clear_irq_status(&mut self, mask: u16) -> Result<(), Error> { - self.write(&[OpCode::ClrIrqStatus as u8, (mask >> 8) as u8, mask as u8]) - } -} - -// 5.8.7 -/// Miscellaneous commands -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Calibrate one or several blocks at any time when in standby mode. - pub fn calibrate(&mut self, cal: u8) -> Result<(), Error> { - // bit 7 is reserved and must be kept at reset value. - self.write(&[OpCode::Calibrate as u8, cal & 0x7F]) - } - - /// Calibrate the image at the given frequencies. - /// - /// Requires the radio to be in standby mode. - pub fn calibrate_image(&mut self, cal: CalibrateImage) -> Result<(), Error> { - self.write(&[OpCode::CalibrateImage as u8, cal.0, cal.1]) - } - - /// Set the radio power supply. - pub fn set_regulator_mode(&mut self, reg_mode: RegMode) -> Result<(), Error> { - self.write(&[OpCode::SetRegulatorMode as u8, reg_mode as u8]) - } - - /// Get the radio operational errors. - pub fn op_error(&mut self) -> Result<(Status, u16), Error> { - let data: [u8; 3] = self.read_n(OpCode::GetError)?; - Ok((data[0].into(), u16::from_be_bytes([data[1], data[2]]))) - } - - /// Clear all errors as reported by [`op_error`](SubGhz::op_error). - pub fn clear_error(&mut self) -> Result<(), Error> { - self.write(&[OpCode::ClrError as u8, 0x00]) - } -} - -// 5.8.8 -/// Set TCXO mode command -impl<'d> SubGhz<'d, NoDma, NoDma> { - /// Set the TCXO trim and HSE32 ready timeout. - pub fn set_tcxo_mode(&mut self, tcxo_mode: &TcxoMode) -> Result<(), Error> { - self.write(tcxo_mode.as_slice()) - } -} - -/// sub-GHz radio opcodes. -/// -/// See Table 41 "Sub-GHz radio SPI commands overview" -#[repr(u8)] -#[derive(Debug, Clone, Copy)] -#[allow(dead_code)] -pub(crate) enum OpCode { - Calibrate = 0x89, - CalibrateImage = 0x98, - CfgDioIrq = 0x08, - ClrError = 0x07, - ClrIrqStatus = 0x02, - GetError = 0x17, - GetIrqStatus = 0x12, - GetPacketStatus = 0x14, - GetPacketType = 0x11, - GetRssiInst = 0x15, - GetRxBufferStatus = 0x13, - GetStats = 0x10, - GetStatus = 0xC0, - ReadBuffer = 0x1E, - RegRegister = 0x1D, - ResetStats = 0x00, - SetBufferBaseAddress = 0x8F, - SetCad = 0xC5, - SetCadParams = 0x88, - SetFs = 0xC1, - SetLoRaSymbTimeout = 0xA0, - SetModulationParams = 0x8B, - SetPacketParams = 0x8C, - SetPacketType = 0x8A, - SetPaConfig = 0x95, - SetRegulatorMode = 0x96, - SetRfFrequency = 0x86, - SetRx = 0x82, - SetRxDutyCycle = 0x94, - SetSleep = 0x84, - SetStandby = 0x80, - SetStopRxTimerOnPreamble = 0x9F, - SetTcxoMode = 0x97, - SetTx = 0x83, - SetTxContinuousPreamble = 0xD2, - SetTxContinuousWave = 0xD1, - SetTxParams = 0x8E, - SetTxRxFallbackMode = 0x93, - WriteBuffer = 0x0E, - WriteRegister = 0x0D, -} - -impl From for u8 { - fn from(opcode: OpCode) -> Self { - opcode as u8 - } -} - -#[repr(u16)] -#[allow(clippy::upper_case_acronyms)] -pub(crate) enum Register { - /// Generic bit synchronization. - GBSYNC = 0x06AC, - /// Generic packet control. - GPKTCTL1A = 0x06B8, - /// Generic whitening. - GWHITEINIRL = 0x06B9, - /// Generic CRC initial. - GCRCINIRH = 0x06BC, - /// Generic CRC polynomial. - GCRCPOLRH = 0x06BE, - /// Generic synchronization word 7. - GSYNC7 = 0x06C0, - /// Node address. - NODE = 0x06CD, - /// Broadcast address. - BROADCAST = 0x06CE, - /// LoRa synchronization word MSB. - LSYNCH = 0x0740, - /// LoRa synchronization word LSB. - #[allow(dead_code)] - LSYNCL = 0x0741, - /// Receiver gain control. - RXGAINC = 0x08AC, - /// PA over current protection. - PAOCP = 0x08E7, - /// RTC control. - RTCCTLR = 0x0902, - /// RTC period MSB. - RTCPRDR2 = 0x0906, - /// RTC period mid-byte. - #[allow(dead_code)] - RTCPRDR1 = 0x0907, - /// RTC period LSB. - #[allow(dead_code)] - RTCPRDR0 = 0x0908, - /// HSE32 OSC_IN capacitor trim. - HSEINTRIM = 0x0911, - /// HSE32 OSC_OUT capacitor trim. - HSEOUTTRIM = 0x0912, - /// SMPS control 0. - SMPSC0 = 0x0916, - /// Power control. - PC = 0x091A, - /// SMPS control 2. - SMPSC2 = 0x0923, -} - -impl Register { - pub const fn address(self) -> u16 { - self as u16 - } -} diff --git a/embassy-stm32/src/subghz/mod_params.rs b/embassy-stm32/src/subghz/mod_params.rs deleted file mode 100644 index d997ae112..000000000 --- a/embassy-stm32/src/subghz/mod_params.rs +++ /dev/null @@ -1,1045 +0,0 @@ -/// Bandwidth options for [`FskModParams`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FskBandwidth { - /// 4.8 kHz double-sideband - Bw4 = 0x1F, - /// 5.8 kHz double-sideband - Bw5 = 0x17, - /// 7.3 kHz double-sideband - Bw7 = 0x0F, - /// 9.7 kHz double-sideband - Bw9 = 0x1E, - /// 11.7 kHz double-sideband - Bw11 = 0x16, - /// 14.6 kHz double-sideband - Bw14 = 0x0E, - /// 19.5 kHz double-sideband - Bw19 = 0x1D, - /// 23.4 kHz double-sideband - Bw23 = 0x15, - /// 29.3 kHz double-sideband - Bw29 = 0x0D, - /// 39.0 kHz double-sideband - Bw39 = 0x1C, - /// 46.9 kHz double-sideband - Bw46 = 0x14, - /// 58.6 kHz double-sideband - Bw58 = 0x0C, - /// 78.2 kHz double-sideband - Bw78 = 0x1B, - /// 93.8 kHz double-sideband - Bw93 = 0x13, - /// 117.3 kHz double-sideband - Bw117 = 0x0B, - /// 156.2 kHz double-sideband - Bw156 = 0x1A, - /// 187.2 kHz double-sideband - Bw187 = 0x12, - /// 234.3 kHz double-sideband - Bw234 = 0x0A, - /// 312.0 kHz double-sideband - Bw312 = 0x19, - /// 373.6 kHz double-sideband - Bw373 = 0x11, - /// 467.0 kHz double-sideband - Bw467 = 0x09, -} - -impl FskBandwidth { - /// Get the bandwidth in hertz. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBandwidth; - /// - /// assert_eq!(FskBandwidth::Bw4.hertz(), 4_800); - /// assert_eq!(FskBandwidth::Bw5.hertz(), 5_800); - /// assert_eq!(FskBandwidth::Bw7.hertz(), 7_300); - /// assert_eq!(FskBandwidth::Bw9.hertz(), 9_700); - /// assert_eq!(FskBandwidth::Bw11.hertz(), 11_700); - /// assert_eq!(FskBandwidth::Bw14.hertz(), 14_600); - /// assert_eq!(FskBandwidth::Bw19.hertz(), 19_500); - /// assert_eq!(FskBandwidth::Bw23.hertz(), 23_400); - /// assert_eq!(FskBandwidth::Bw29.hertz(), 29_300); - /// assert_eq!(FskBandwidth::Bw39.hertz(), 39_000); - /// assert_eq!(FskBandwidth::Bw46.hertz(), 46_900); - /// assert_eq!(FskBandwidth::Bw58.hertz(), 58_600); - /// assert_eq!(FskBandwidth::Bw78.hertz(), 78_200); - /// assert_eq!(FskBandwidth::Bw93.hertz(), 93_800); - /// assert_eq!(FskBandwidth::Bw117.hertz(), 117_300); - /// assert_eq!(FskBandwidth::Bw156.hertz(), 156_200); - /// assert_eq!(FskBandwidth::Bw187.hertz(), 187_200); - /// assert_eq!(FskBandwidth::Bw234.hertz(), 234_300); - /// assert_eq!(FskBandwidth::Bw312.hertz(), 312_000); - /// assert_eq!(FskBandwidth::Bw373.hertz(), 373_600); - /// assert_eq!(FskBandwidth::Bw467.hertz(), 467_000); - /// ``` - pub const fn hertz(&self) -> u32 { - match self { - FskBandwidth::Bw4 => 4_800, - FskBandwidth::Bw5 => 5_800, - FskBandwidth::Bw7 => 7_300, - FskBandwidth::Bw9 => 9_700, - FskBandwidth::Bw11 => 11_700, - FskBandwidth::Bw14 => 14_600, - FskBandwidth::Bw19 => 19_500, - FskBandwidth::Bw23 => 23_400, - FskBandwidth::Bw29 => 29_300, - FskBandwidth::Bw39 => 39_000, - FskBandwidth::Bw46 => 46_900, - FskBandwidth::Bw58 => 58_600, - FskBandwidth::Bw78 => 78_200, - FskBandwidth::Bw93 => 93_800, - FskBandwidth::Bw117 => 117_300, - FskBandwidth::Bw156 => 156_200, - FskBandwidth::Bw187 => 187_200, - FskBandwidth::Bw234 => 234_300, - FskBandwidth::Bw312 => 312_000, - FskBandwidth::Bw373 => 373_600, - FskBandwidth::Bw467 => 467_000, - } - } - - /// Convert from a raw bit value. - /// - /// Invalid values will be returned in the `Err` variant of the result. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBandwidth; - /// - /// assert_eq!(FskBandwidth::from_bits(0x1F), Ok(FskBandwidth::Bw4)); - /// assert_eq!(FskBandwidth::from_bits(0x17), Ok(FskBandwidth::Bw5)); - /// assert_eq!(FskBandwidth::from_bits(0x0F), Ok(FskBandwidth::Bw7)); - /// assert_eq!(FskBandwidth::from_bits(0x1E), Ok(FskBandwidth::Bw9)); - /// assert_eq!(FskBandwidth::from_bits(0x16), Ok(FskBandwidth::Bw11)); - /// assert_eq!(FskBandwidth::from_bits(0x0E), Ok(FskBandwidth::Bw14)); - /// assert_eq!(FskBandwidth::from_bits(0x1D), Ok(FskBandwidth::Bw19)); - /// assert_eq!(FskBandwidth::from_bits(0x15), Ok(FskBandwidth::Bw23)); - /// assert_eq!(FskBandwidth::from_bits(0x0D), Ok(FskBandwidth::Bw29)); - /// assert_eq!(FskBandwidth::from_bits(0x1C), Ok(FskBandwidth::Bw39)); - /// assert_eq!(FskBandwidth::from_bits(0x14), Ok(FskBandwidth::Bw46)); - /// assert_eq!(FskBandwidth::from_bits(0x0C), Ok(FskBandwidth::Bw58)); - /// assert_eq!(FskBandwidth::from_bits(0x1B), Ok(FskBandwidth::Bw78)); - /// assert_eq!(FskBandwidth::from_bits(0x13), Ok(FskBandwidth::Bw93)); - /// assert_eq!(FskBandwidth::from_bits(0x0B), Ok(FskBandwidth::Bw117)); - /// assert_eq!(FskBandwidth::from_bits(0x1A), Ok(FskBandwidth::Bw156)); - /// assert_eq!(FskBandwidth::from_bits(0x12), Ok(FskBandwidth::Bw187)); - /// assert_eq!(FskBandwidth::from_bits(0x0A), Ok(FskBandwidth::Bw234)); - /// assert_eq!(FskBandwidth::from_bits(0x19), Ok(FskBandwidth::Bw312)); - /// assert_eq!(FskBandwidth::from_bits(0x11), Ok(FskBandwidth::Bw373)); - /// assert_eq!(FskBandwidth::from_bits(0x09), Ok(FskBandwidth::Bw467)); - /// assert_eq!(FskBandwidth::from_bits(0x00), Err(0x00)); - /// ``` - pub const fn from_bits(bits: u8) -> Result { - match bits { - 0x1F => Ok(Self::Bw4), - 0x17 => Ok(Self::Bw5), - 0x0F => Ok(Self::Bw7), - 0x1E => Ok(Self::Bw9), - 0x16 => Ok(Self::Bw11), - 0x0E => Ok(Self::Bw14), - 0x1D => Ok(Self::Bw19), - 0x15 => Ok(Self::Bw23), - 0x0D => Ok(Self::Bw29), - 0x1C => Ok(Self::Bw39), - 0x14 => Ok(Self::Bw46), - 0x0C => Ok(Self::Bw58), - 0x1B => Ok(Self::Bw78), - 0x13 => Ok(Self::Bw93), - 0x0B => Ok(Self::Bw117), - 0x1A => Ok(Self::Bw156), - 0x12 => Ok(Self::Bw187), - 0x0A => Ok(Self::Bw234), - 0x19 => Ok(Self::Bw312), - 0x11 => Ok(Self::Bw373), - 0x09 => Ok(Self::Bw467), - x => Err(x), - } - } -} - -impl Ord for FskBandwidth { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.hertz().cmp(&other.hertz()) - } -} - -impl PartialOrd for FskBandwidth { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.hertz().cmp(&other.hertz())) - } -} - -/// Pulse shaping options for [`FskModParams`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FskPulseShape { - /// No filtering applied. - None = 0b00, - /// Gaussian BT 0.3 - Bt03 = 0x08, - /// Gaussian BT 0.5 - Bt05 = 0x09, - /// Gaussian BT 0.7 - Bt07 = 0x0A, - /// Gaussian BT 1.0 - Bt10 = 0x0B, -} - -/// Bitrate argument for [`FskModParams::set_bitrate`] and -/// [`BpskModParams::set_bitrate`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct FskBitrate { - bits: u32, -} - -impl FskBitrate { - /// Create a new `FskBitrate` from a bitrate in bits per second. - /// - /// This the resulting value will be rounded down, and will saturate if - /// `bps` is outside of the theoretical limits. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBitrate; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(9600); - /// assert_eq!(BITRATE.as_bps(), 9600); - /// ``` - pub const fn from_bps(bps: u32) -> Self { - const MAX: u32 = 0x00FF_FFFF; - if bps == 0 { - Self { bits: MAX } - } else { - let bits: u32 = 32 * 32_000_000 / bps; - if bits > MAX { - Self { bits: MAX } - } else { - Self { bits } - } - } - } - - /// Create a new `FskBitrate` from a raw bit value. - /// - /// bits = 32 × 32 MHz / bitrate - /// - /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` - /// argument will be masked. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBitrate; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_raw(0x7D00); - /// assert_eq!(BITRATE.as_bps(), 32_000); - /// ``` - pub const fn from_raw(bits: u32) -> Self { - Self { - bits: bits & 0x00FF_FFFF, - } - } - - /// Return the bitrate in bits per second, rounded down. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskBitrate; - /// - /// const BITS_PER_SEC: u32 = 9600; - /// const BITRATE: FskBitrate = FskBitrate::from_bps(BITS_PER_SEC); - /// assert_eq!(BITRATE.as_bps(), BITS_PER_SEC); - /// ``` - pub const fn as_bps(&self) -> u32 { - if self.bits == 0 { - 0 - } else { - 32 * 32_000_000 / self.bits - } - } - - pub(crate) const fn into_bits(self) -> u32 { - self.bits - } -} - -impl Ord for FskBitrate { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.as_bps().cmp(&other.as_bps()) - } -} - -impl PartialOrd for FskBitrate { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.as_bps().cmp(&other.as_bps())) - } -} - -/// Frequency deviation argument for [`FskModParams::set_fdev`] -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct FskFdev { - bits: u32, -} - -impl FskFdev { - /// Create a new `FskFdev` from a frequency deviation in hertz, rounded - /// down. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskFdev; - /// - /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); - /// assert_eq!(FDEV.as_hertz(), 31_250); - /// ``` - pub const fn from_hertz(hz: u32) -> Self { - Self { - bits: ((hz as u64) * (1 << 25) / 32_000_000) as u32 & 0x00FF_FFFF, - } - } - - /// Create a new `FskFdev` from a raw bit value. - /// - /// bits = fdev × 225 / 32 MHz - /// - /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` - /// argument will be masked. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskFdev; - /// - /// const FDEV: FskFdev = FskFdev::from_raw(0x8000); - /// assert_eq!(FDEV.as_hertz(), 31_250); - /// ``` - pub const fn from_raw(bits: u32) -> Self { - Self { - bits: bits & 0x00FF_FFFF, - } - } - - /// Return the frequency deviation in hertz, rounded down. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskFdev; - /// - /// const HERTZ: u32 = 31_250; - /// const FDEV: FskFdev = FskFdev::from_hertz(HERTZ); - /// assert_eq!(FDEV.as_hertz(), HERTZ); - /// ``` - pub const fn as_hertz(&self) -> u32 { - ((self.bits as u64) * 32_000_000 / (1 << 25)) as u32 - } - - pub(crate) const fn into_bits(self) -> u32 { - self.bits - } -} - -/// (G)FSK modulation parameters. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct FskModParams { - buf: [u8; 9], -} - -impl FskModParams { - /// Create a new `FskModParams` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::FskModParams; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new(); - /// ``` - pub const fn new() -> FskModParams { - FskModParams { - buf: [ - super::OpCode::SetModulationParams as u8, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - ], - } - .set_bitrate(FskBitrate::from_bps(50_000)) - .set_pulse_shape(FskPulseShape::None) - .set_bandwidth(FskBandwidth::Bw58) - .set_fdev(FskFdev::from_hertz(25_000)) - } - - /// Get the bitrate. - /// - /// # Example - /// - /// Setting the bitrate to 32,000 bits per second. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE); - /// assert_eq!(MOD_PARAMS.bitrate(), BITRATE); - /// ``` - pub const fn bitrate(&self) -> FskBitrate { - let raw: u32 = u32::from_be_bytes([0, self.buf[1], self.buf[2], self.buf[3]]); - FskBitrate::from_raw(raw) - } - - /// Set the bitrate. - /// - /// # Example - /// - /// Setting the bitrate to 32,000 bits per second. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBitrate, FskModParams}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE); - /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x00); - /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x7D); - /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0x00); - /// ``` - #[must_use = "set_bitrate returns a modified FskModParams"] - pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> FskModParams { - let bits: u32 = bitrate.into_bits(); - self.buf[1] = ((bits >> 16) & 0xFF) as u8; - self.buf[2] = ((bits >> 8) & 0xFF) as u8; - self.buf[3] = (bits & 0xFF) as u8; - self - } - - /// Set the pulse shaping. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskModParams, FskPulseShape}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_pulse_shape(FskPulseShape::Bt03); - /// # assert_eq!(MOD_PARAMS.as_slice()[4], 0x08); - /// ``` - #[must_use = "set_pulse_shape returns a modified FskModParams"] - pub const fn set_pulse_shape(mut self, shape: FskPulseShape) -> FskModParams { - self.buf[4] = shape as u8; - self - } - - /// Get the bandwidth. - /// - /// Values that do not correspond to a valid [`FskBandwidth`] will be - /// returned in the `Err` variant of the result. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9); - /// assert_eq!(MOD_PARAMS.bandwidth(), Ok(FskBandwidth::Bw9)); - /// ``` - pub const fn bandwidth(&self) -> Result { - FskBandwidth::from_bits(self.buf[5]) - } - - /// Set the bandwidth. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskModParams}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9); - /// # assert_eq!(MOD_PARAMS.as_slice()[5], 0x1E); - /// ``` - #[must_use = "set_pulse_shape returns a modified FskModParams"] - pub const fn set_bandwidth(mut self, bw: FskBandwidth) -> FskModParams { - self.buf[5] = bw as u8; - self - } - - /// Get the frequency deviation. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams}; - /// - /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV); - /// assert_eq!(MOD_PARAMS.fdev(), FDEV); - /// ``` - pub const fn fdev(&self) -> FskFdev { - let raw: u32 = u32::from_be_bytes([0, self.buf[6], self.buf[7], self.buf[8]]); - FskFdev::from_raw(raw) - } - - /// Set the frequency deviation. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskFdev, FskModParams}; - /// - /// const FDEV: FskFdev = FskFdev::from_hertz(31_250); - /// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV); - /// # assert_eq!(MOD_PARAMS.as_slice()[6], 0x00); - /// # assert_eq!(MOD_PARAMS.as_slice()[7], 0x80); - /// # assert_eq!(MOD_PARAMS.as_slice()[8], 0x00); - /// ``` - #[must_use = "set_fdev returns a modified FskModParams"] - pub const fn set_fdev(mut self, fdev: FskFdev) -> FskModParams { - let bits: u32 = fdev.into_bits(); - self.buf[6] = ((bits >> 16) & 0xFF) as u8; - self.buf[7] = ((bits >> 8) & 0xFF) as u8; - self.buf[8] = (bits & 0xFF) as u8; - self - } - /// Returns `true` if the modulation parameters are valid. - /// - /// The bandwidth must be chosen so that: - /// - /// [`FskBandwidth`] > [`FskBitrate`] + 2 × [`FskFdev`] + frequency error - /// - /// Where frequency error = 2 × HSE32FREQ error. - /// - /// The datasheet (DS13293 Rev 1) gives these requirements for the HSE32 - /// frequency tolerance: - /// - /// * Initial: ±10 ppm - /// * Over temperature (-20 to 70 °C): ±10 ppm - /// * Aging over 10 years: ±10 ppm - /// - /// # Example - /// - /// Checking valid parameters at compile-time - /// - /// ``` - /// extern crate static_assertions as sa; - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape}; - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new() - /// .set_bitrate(FskBitrate::from_bps(20_000)) - /// .set_pulse_shape(FskPulseShape::Bt03) - /// .set_bandwidth(FskBandwidth::Bw58) - /// .set_fdev(FskFdev::from_hertz(10_000)); - /// - /// // 30 PPM is wost case (if the HSE32 crystal meets requirements) - /// sa::const_assert!(MOD_PARAMS.is_valid(30)); - /// ``` - #[must_use = "the return value indicates if the modulation parameters are valid"] - pub const fn is_valid(&self, ppm: u8) -> bool { - let bw: u32 = match self.bandwidth() { - Ok(bw) => bw.hertz(), - Err(_) => return false, - }; - let br: u32 = self.bitrate().as_bps(); - let fdev: u32 = self.fdev().as_hertz(); - let hse_err: u32 = 32 * (ppm as u32); - let freq_err: u32 = 2 * hse_err; - - bw > br + 2 * fdev + freq_err - } - - /// Returns `true` if the modulation parameters are valid for a worst-case - /// crystal tolerance. - /// - /// This is equivalent to [`is_valid`](Self::is_valid) with a `ppm` argument - /// of 30. - #[must_use = "the return value indicates if the modulation parameters are valid"] - pub const fn is_valid_worst_case(&self) -> bool { - self.is_valid(30) - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(20_000); - /// const PULSE_SHAPE: FskPulseShape = FskPulseShape::Bt03; - /// const BW: FskBandwidth = FskBandwidth::Bw58; - /// const FDEV: FskFdev = FskFdev::from_hertz(10_000); - /// - /// const MOD_PARAMS: FskModParams = FskModParams::new() - /// .set_bitrate(BITRATE) - /// .set_pulse_shape(PULSE_SHAPE) - /// .set_bandwidth(BW) - /// .set_fdev(FDEV); - /// - /// assert_eq!( - /// MOD_PARAMS.as_slice(), - /// &[0x8B, 0x00, 0xC8, 0x00, 0x08, 0x0C, 0x00, 0x28, 0xF5] - /// ); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for FskModParams { - fn default() -> Self { - Self::new() - } -} - -/// LoRa spreading factor. -/// -/// Argument of [`LoRaModParams::set_sf`]. -/// -/// Higher spreading factors improve receiver sensitivity, but reduce bit rate -/// and increase power consumption. -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum SpreadingFactor { - /// Spreading factor 5. - Sf5 = 0x05, - /// Spreading factor 6. - Sf6 = 0x06, - /// Spreading factor 7. - Sf7 = 0x07, - /// Spreading factor 8. - Sf8 = 0x08, - /// Spreading factor 9. - Sf9 = 0x09, - /// Spreading factor 10. - Sf10 = 0x0A, - /// Spreading factor 11. - Sf11 = 0x0B, - /// Spreading factor 12. - Sf12 = 0x0C, -} - -impl From for u8 { - fn from(sf: SpreadingFactor) -> Self { - sf as u8 - } -} - -/// LoRa bandwidth. -/// -/// Argument of [`LoRaModParams::set_bw`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum LoRaBandwidth { - /// 7.81 kHz - Bw7 = 0x00, - /// 10.42 kHz - Bw10 = 0x08, - /// 15.63 kHz - Bw15 = 0x01, - /// 20.83 kHz - Bw20 = 0x09, - /// 31.25 kHz - Bw31 = 0x02, - /// 41.67 kHz - Bw41 = 0x0A, - /// 62.50 kHz - Bw62 = 0x03, - /// 125 kHz - Bw125 = 0x04, - /// 250 kHz - Bw250 = 0x05, - /// 500 kHz - Bw500 = 0x06, -} - -impl LoRaBandwidth { - /// Get the bandwidth in hertz. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaBandwidth; - /// - /// assert_eq!(LoRaBandwidth::Bw7.hertz(), 7_810); - /// assert_eq!(LoRaBandwidth::Bw10.hertz(), 10_420); - /// assert_eq!(LoRaBandwidth::Bw15.hertz(), 15_630); - /// assert_eq!(LoRaBandwidth::Bw20.hertz(), 20_830); - /// assert_eq!(LoRaBandwidth::Bw31.hertz(), 31_250); - /// assert_eq!(LoRaBandwidth::Bw41.hertz(), 41_670); - /// assert_eq!(LoRaBandwidth::Bw62.hertz(), 62_500); - /// assert_eq!(LoRaBandwidth::Bw125.hertz(), 125_000); - /// assert_eq!(LoRaBandwidth::Bw250.hertz(), 250_000); - /// assert_eq!(LoRaBandwidth::Bw500.hertz(), 500_000); - /// ``` - pub const fn hertz(&self) -> u32 { - match self { - LoRaBandwidth::Bw7 => 7_810, - LoRaBandwidth::Bw10 => 10_420, - LoRaBandwidth::Bw15 => 15_630, - LoRaBandwidth::Bw20 => 20_830, - LoRaBandwidth::Bw31 => 31_250, - LoRaBandwidth::Bw41 => 41_670, - LoRaBandwidth::Bw62 => 62_500, - LoRaBandwidth::Bw125 => 125_000, - LoRaBandwidth::Bw250 => 250_000, - LoRaBandwidth::Bw500 => 500_000, - } - } -} - -impl Ord for LoRaBandwidth { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.hertz().cmp(&other.hertz()) - } -} - -impl PartialOrd for LoRaBandwidth { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.hertz().cmp(&other.hertz())) - } -} - -/// LoRa forward error correction coding rate. -/// -/// Argument of [`LoRaModParams::set_cr`]. -/// -/// A higher coding rate provides better immunity to interference at the expense -/// of longer transmission time. -/// In normal conditions [`CodingRate::Cr45`] provides the best trade off. -/// In case of strong interference, a higher coding rate may be used. -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum CodingRate { - /// No forward error correction coding rate 4/4 - /// - /// Overhead ratio of 1 - Cr44 = 0x00, - /// Forward error correction coding rate 4/5 - /// - /// Overhead ratio of 1.25 - Cr45 = 0x1, - /// Forward error correction coding rate 4/6 - /// - /// Overhead ratio of 1.5 - Cr46 = 0x2, - /// Forward error correction coding rate 4/7 - /// - /// Overhead ratio of 1.75 - Cr47 = 0x3, - /// Forward error correction coding rate 4/8 - /// - /// Overhead ratio of 2 - Cr48 = 0x4, -} - -/// LoRa modulation parameters. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] - -pub struct LoRaModParams { - buf: [u8; 5], -} - -impl LoRaModParams { - /// Create a new `LoRaModParams` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaModParams; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new(); - /// assert_eq!(MOD_PARAMS, LoRaModParams::default()); - /// ``` - pub const fn new() -> LoRaModParams { - LoRaModParams { - buf: [ - super::OpCode::SetModulationParams as u8, - 0x05, // valid spreading factor - 0x00, - 0x00, - 0x00, - ], - } - } - - /// Set the spreading factor. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{LoRaModParams, SpreadingFactor}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_sf(SpreadingFactor::Sf7); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x00, 0x00, 0x00]); - /// ``` - #[must_use = "set_sf returns a modified LoRaModParams"] - pub const fn set_sf(mut self, sf: SpreadingFactor) -> Self { - self.buf[1] = sf as u8; - self - } - - /// Set the bandwidth. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{LoRaBandwidth, LoRaModParams}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_bw(LoRaBandwidth::Bw125); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x04, 0x00, 0x00]); - /// ``` - #[must_use = "set_bw returns a modified LoRaModParams"] - pub const fn set_bw(mut self, bw: LoRaBandwidth) -> Self { - self.buf[2] = bw as u8; - self - } - - /// Set the forward error correction coding rate. - /// - /// See [`CodingRate`] for more information. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CodingRate, LoRaModParams}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_cr(CodingRate::Cr45); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x01, 0x00]); - /// ``` - #[must_use = "set_cr returns a modified LoRaModParams"] - pub const fn set_cr(mut self, cr: CodingRate) -> Self { - self.buf[3] = cr as u8; - self - } - - /// Set low data rate optimization enable. - /// - /// For low data rates (typically high SF or low BW) and very long payloads - /// (may last several seconds), the low data rate optimization (LDRO) can be - /// enabled. - /// This reduces the number of bits per symbol to the given SF minus 2, - /// to allow the receiver to have a better tracking of the LoRa receive - /// signal. - /// Depending on the payload length, the low data rate optimization is - /// usually recommended when the LoRa symbol time is equal or above - /// 16.38 ms. - /// When using LoRa modulation, the total frequency drift over the packet - /// time must be kept lower than Freq_drift_max: - /// - /// Freq_drift_max = BW / (3 × 2SF) - /// - /// When possible, enabling the low data rate optimization, relaxes the - /// total frequency drift over the packet time by 16: - /// - /// Freq_drift_optimise_max = 16 × Freq_drift_max - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaModParams; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_ldro_en(true); - /// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x00, 0x01]); - /// ``` - #[must_use = "set_ldro_en returns a modified LoRaModParams"] - pub const fn set_ldro_en(mut self, en: bool) -> Self { - self.buf[4] = en as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CodingRate, LoRaBandwidth, LoRaModParams, SpreadingFactor}; - /// - /// const MOD_PARAMS: LoRaModParams = LoRaModParams::new() - /// .set_sf(SpreadingFactor::Sf7) - /// .set_bw(LoRaBandwidth::Bw125) - /// .set_cr(CodingRate::Cr45) - /// .set_ldro_en(false); - /// - /// assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x04, 0x01, 0x00]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for LoRaModParams { - fn default() -> Self { - Self::new() - } -} - -/// BPSK modulation parameters. -/// -/// **Note:** There is no method to set the pulse shape because there is only -/// one valid pulse shape (Gaussian BT 0.5). -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct BpskModParams { - buf: [u8; 5], -} - -impl BpskModParams { - /// Create a new `BpskModParams` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::BpskModParams; - /// - /// const MOD_PARAMS: BpskModParams = BpskModParams::new(); - /// assert_eq!(MOD_PARAMS, BpskModParams::default()); - /// ``` - pub const fn new() -> BpskModParams { - const OPCODE: u8 = super::OpCode::SetModulationParams as u8; - BpskModParams { - buf: [OPCODE, 0x1A, 0x0A, 0xAA, 0x16], - } - } - - /// Set the bitrate. - /// - /// # Example - /// - /// Setting the bitrate to 600 bits per second. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(600); - /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE); - /// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x1A); - /// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x0A); - /// # assert_eq!(MOD_PARAMS.as_slice()[3], 0xAA); - /// ``` - #[must_use = "set_bitrate returns a modified BpskModParams"] - pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> BpskModParams { - let bits: u32 = bitrate.into_bits(); - self.buf[1] = ((bits >> 16) & 0xFF) as u8; - self.buf[2] = ((bits >> 8) & 0xFF) as u8; - self.buf[3] = (bits & 0xFF) as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{BpskModParams, FskBitrate}; - /// - /// const BITRATE: FskBitrate = FskBitrate::from_bps(100); - /// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE); - /// assert_eq!(MOD_PARAMS.as_slice(), [0x8B, 0x9C, 0x40, 0x00, 0x16]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for BpskModParams { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod test { - use super::{FskBandwidth, FskBitrate, FskFdev, LoRaBandwidth}; - - #[test] - fn fsk_bw_ord() { - assert!((FskBandwidth::Bw4 as u8) > (FskBandwidth::Bw5 as u8)); - assert!(FskBandwidth::Bw4 < FskBandwidth::Bw5); - assert!(FskBandwidth::Bw5 > FskBandwidth::Bw4); - } - - #[test] - fn lora_bw_ord() { - assert!((LoRaBandwidth::Bw10 as u8) > (LoRaBandwidth::Bw15 as u8)); - assert!(LoRaBandwidth::Bw10 < LoRaBandwidth::Bw15); - assert!(LoRaBandwidth::Bw15 > LoRaBandwidth::Bw10); - } - - #[test] - fn fsk_bitrate_ord() { - assert!(FskBitrate::from_bps(9600) > FskBitrate::from_bps(4800)); - assert!(FskBitrate::from_bps(4800) < FskBitrate::from_bps(9600)); - } - - #[test] - fn fsk_bitrate_as_bps_limits() { - const ZERO: FskBitrate = FskBitrate::from_raw(0); - const ONE: FskBitrate = FskBitrate::from_raw(1); - const MAX: FskBitrate = FskBitrate::from_raw(u32::MAX); - - assert_eq!(ZERO.as_bps(), 0); - assert_eq!(ONE.as_bps(), 1_024_000_000); - assert_eq!(MAX.as_bps(), 61); - } - - #[test] - fn fsk_bitrate_from_bps_limits() { - const ZERO: FskBitrate = FskBitrate::from_bps(0); - const ONE: FskBitrate = FskBitrate::from_bps(1); - const MAX: FskBitrate = FskBitrate::from_bps(u32::MAX); - - assert_eq!(ZERO.as_bps(), 61); - assert_eq!(ONE.as_bps(), 61); - assert_eq!(MAX.as_bps(), 0); - } - - #[test] - fn fsk_fdev_ord() { - assert!(FskFdev::from_hertz(30_000) > FskFdev::from_hertz(20_000)); - assert!(FskFdev::from_hertz(20_000) < FskFdev::from_hertz(30_000)); - } - - #[test] - fn fsk_fdev_as_hertz_limits() { - const ZERO: FskFdev = FskFdev::from_raw(0); - const ONE: FskFdev = FskFdev::from_raw(1); - const MAX: FskFdev = FskFdev::from_raw(u32::MAX); - - assert_eq!(ZERO.as_hertz(), 0); - assert_eq!(ONE.as_hertz(), 0); - assert_eq!(MAX.as_hertz(), 15_999_999); - } - - #[test] - fn fsk_fdev_from_hertz_limits() { - const ZERO: FskFdev = FskFdev::from_hertz(0); - const ONE: FskFdev = FskFdev::from_hertz(1); - const MAX: FskFdev = FskFdev::from_hertz(u32::MAX); - - assert_eq!(ZERO.as_hertz(), 0); - assert_eq!(ONE.as_hertz(), 0); - assert_eq!(MAX.as_hertz(), 6_967_294); - } -} diff --git a/embassy-stm32/src/subghz/ocp.rs b/embassy-stm32/src/subghz/ocp.rs deleted file mode 100644 index 81e89c217..000000000 --- a/embassy-stm32/src/subghz/ocp.rs +++ /dev/null @@ -1,14 +0,0 @@ -/// Power amplifier over current protection. -/// -/// Used by [`set_pa_ocp`]. -/// -/// [`set_pa_ocp`]: super::SubGhz::set_pa_ocp -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum Ocp { - /// Maximum 60mA current for LP PA mode. - Max60m = 0x18, - /// Maximum 140mA for HP PA mode. - Max140m = 0x38, -} diff --git a/embassy-stm32/src/subghz/op_error.rs b/embassy-stm32/src/subghz/op_error.rs deleted file mode 100644 index b17b99205..000000000 --- a/embassy-stm32/src/subghz/op_error.rs +++ /dev/null @@ -1,48 +0,0 @@ -/// Operation Errors. -/// -/// Returned by [`op_error`]. -/// -/// [`op_error`]: super::SubGhz::op_error -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum OpError { - /// PA ramping failed - PaRampError = 8, - /// RF-PLL locking failed - PllLockError = 6, - /// HSE32 clock startup failed - XoscStartError = 5, - /// Image calibration failed - ImageCalibrationError = 4, - /// RF-ADC calibration failed - AdcCalibrationError = 3, - /// RF-PLL calibration failed - PllCalibrationError = 2, - /// Sub-GHz radio RC 13 MHz oscillator - RC13MCalibrationError = 1, - /// Sub-GHz radio RC 64 kHz oscillator - RC64KCalibrationError = 0, -} - -impl OpError { - /// Get the bitmask for the error. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::OpError; - /// - /// assert_eq!(OpError::PaRampError.mask(), 0b1_0000_0000); - /// assert_eq!(OpError::PllLockError.mask(), 0b0_0100_0000); - /// assert_eq!(OpError::XoscStartError.mask(), 0b0_0010_0000); - /// assert_eq!(OpError::ImageCalibrationError.mask(), 0b0_0001_0000); - /// assert_eq!(OpError::AdcCalibrationError.mask(), 0b0_0000_1000); - /// assert_eq!(OpError::PllCalibrationError.mask(), 0b0_0000_0100); - /// assert_eq!(OpError::RC13MCalibrationError.mask(), 0b0_0000_0010); - /// assert_eq!(OpError::RC64KCalibrationError.mask(), 0b0_0000_0001); - /// ``` - pub const fn mask(self) -> u16 { - 1 << (self as u8) - } -} diff --git a/embassy-stm32/src/subghz/pa_config.rs b/embassy-stm32/src/subghz/pa_config.rs deleted file mode 100644 index 875827bd4..000000000 --- a/embassy-stm32/src/subghz/pa_config.rs +++ /dev/null @@ -1,196 +0,0 @@ -/// Power amplifier configuration parameters. -/// -/// Argument of [`set_pa_config`]. -/// -/// [`set_pa_config`]: super::SubGhz::set_pa_config -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PaConfig { - buf: [u8; 5], -} - -impl PaConfig { - /// Optimal settings for +15dBm output power with the low-power PA. - /// - /// This must be used with [`TxParams::LP_15`](super::TxParams::LP_15). - pub const LP_15: PaConfig = PaConfig::new().set_pa_duty_cycle(0x6).set_hp_max(0x0).set_pa(PaSel::Lp); - - /// Optimal settings for +14dBm output power with the low-power PA. - /// - /// This must be used with [`TxParams::LP_14`](super::TxParams::LP_14). - pub const LP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x0).set_pa(PaSel::Lp); - - /// Optimal settings for +10dBm output power with the low-power PA. - /// - /// This must be used with [`TxParams::LP_10`](super::TxParams::LP_10). - pub const LP_10: PaConfig = PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp); - - /// Optimal settings for +22dBm output power with the high-power PA. - /// - /// This must be used with [`TxParams::HP`](super::TxParams::HP). - pub const HP_22: PaConfig = PaConfig::new().set_pa_duty_cycle(0x4).set_hp_max(0x7).set_pa(PaSel::Hp); - - /// Optimal settings for +20dBm output power with the high-power PA. - /// - /// This must be used with [`TxParams::HP`](super::TxParams::HP). - pub const HP_20: PaConfig = PaConfig::new().set_pa_duty_cycle(0x3).set_hp_max(0x5).set_pa(PaSel::Hp); - - /// Optimal settings for +17dBm output power with the high-power PA. - /// - /// This must be used with [`TxParams::HP`](super::TxParams::HP). - pub const HP_17: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x3).set_pa(PaSel::Hp); - - /// Optimal settings for +14dBm output power with the high-power PA. - /// - /// This must be used with [`TxParams::HP`](super::TxParams::HP). - pub const HP_14: PaConfig = PaConfig::new().set_pa_duty_cycle(0x2).set_hp_max(0x2).set_pa(PaSel::Hp); - - /// Create a new `PaConfig` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PaConfig; - /// - /// const PA_CONFIG: PaConfig = PaConfig::new(); - /// ``` - pub const fn new() -> PaConfig { - PaConfig { - buf: [super::OpCode::SetPaConfig as u8, 0x01, 0x00, 0x01, 0x01], - } - } - - /// Set the power amplifier duty cycle (conduit angle) control. - /// - /// **Note:** Only the first 3 bits of the `pa_duty_cycle` argument are used. - /// - /// Duty cycle = 0.2 + 0.04 × bits - /// - /// # Caution - /// - /// The following restrictions must be observed to avoid over-stress on the PA: - /// * LP PA mode with synthesis frequency > 400 MHz, `pa_duty_cycle` must be < 0x7. - /// * LP PA mode with synthesis frequency < 400 MHz, `pa_duty_cycle` must be < 0x4. - /// * HP PA mode, `pa_duty_cycle` must be < 0x4 - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; - /// - /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Lp).set_pa_duty_cycle(0x4); - /// # assert_eq!(PA_CONFIG.as_slice()[1], 0x04); - /// ``` - #[must_use = "set_pa_duty_cycle returns a modified PaConfig"] - pub const fn set_pa_duty_cycle(mut self, pa_duty_cycle: u8) -> PaConfig { - self.buf[1] = pa_duty_cycle & 0b111; - self - } - - /// Set the high power amplifier output power. - /// - /// **Note:** Only the first 3 bits of the `hp_max` argument are used. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; - /// - /// const PA_CONFIG: PaConfig = PaConfig::new().set_pa(PaSel::Hp).set_hp_max(0x2); - /// # assert_eq!(PA_CONFIG.as_slice()[2], 0x02); - /// ``` - #[must_use = "set_hp_max returns a modified PaConfig"] - pub const fn set_hp_max(mut self, hp_max: u8) -> PaConfig { - self.buf[2] = hp_max & 0b111; - self - } - - /// Set the power amplifier to use, low or high power. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; - /// - /// const PA_CONFIG_HP: PaConfig = PaConfig::new().set_pa(PaSel::Hp); - /// const PA_CONFIG_LP: PaConfig = PaConfig::new().set_pa(PaSel::Lp); - /// # assert_eq!(PA_CONFIG_HP.as_slice()[3], 0x00); - /// # assert_eq!(PA_CONFIG_LP.as_slice()[3], 0x01); - /// ``` - #[must_use = "set_pa returns a modified PaConfig"] - pub const fn set_pa(mut self, pa: PaSel) -> PaConfig { - self.buf[3] = pa as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{PaConfig, PaSel}; - /// - /// const PA_CONFIG: PaConfig = PaConfig::new() - /// .set_pa(PaSel::Hp) - /// .set_pa_duty_cycle(0x2) - /// .set_hp_max(0x3); - /// - /// assert_eq!(PA_CONFIG.as_slice(), &[0x95, 0x2, 0x03, 0x00, 0x01]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for PaConfig { - fn default() -> Self { - Self::new() - } -} - -/// Power amplifier selection. -/// -/// Argument of [`PaConfig::set_pa`]. -#[repr(u8)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum PaSel { - /// High power amplifier. - Hp = 0b0, - /// Low power amplifier. - Lp = 0b1, -} - -impl PartialOrd for PaSel { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for PaSel { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - match (self, other) { - (PaSel::Hp, PaSel::Hp) | (PaSel::Lp, PaSel::Lp) => core::cmp::Ordering::Equal, - (PaSel::Hp, PaSel::Lp) => core::cmp::Ordering::Greater, - (PaSel::Lp, PaSel::Hp) => core::cmp::Ordering::Less, - } - } -} - -impl Default for PaSel { - fn default() -> Self { - PaSel::Lp - } -} - -#[cfg(test)] -mod test { - use super::PaSel; - - #[test] - fn pa_sel_ord() { - assert!(PaSel::Lp < PaSel::Hp); - assert!(PaSel::Hp > PaSel::Lp); - } -} diff --git a/embassy-stm32/src/subghz/packet_params.rs b/embassy-stm32/src/subghz/packet_params.rs deleted file mode 100644 index db1fb88d9..000000000 --- a/embassy-stm32/src/subghz/packet_params.rs +++ /dev/null @@ -1,534 +0,0 @@ -/// Preamble detection length for [`GenericPacketParams`]. -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PreambleDetection { - /// Preamble detection disabled. - Disabled = 0x0, - /// 8-bit preamble detection. - Bit8 = 0x4, - /// 16-bit preamble detection. - Bit16 = 0x5, - /// 24-bit preamble detection. - Bit24 = 0x6, - /// 32-bit preamble detection. - Bit32 = 0x7, -} - -/// Address comparison/filtering for [`GenericPacketParams`]. -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AddrComp { - /// Address comparison/filtering disabled. - Disabled = 0x0, - /// Address comparison/filtering on node address. - Node = 0x1, - /// Address comparison/filtering on node and broadcast addresses. - Broadcast = 0x2, -} - -/// Packet header type. -/// -/// Argument of [`GenericPacketParams::set_header_type`] and -/// [`LoRaPacketParams::set_header_type`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum HeaderType { - /// Fixed; payload length and header field not added to packet. - Fixed, - /// Variable; payload length and header field added to packet. - Variable, -} - -impl HeaderType { - pub(crate) const fn to_bits_generic(self) -> u8 { - match self { - HeaderType::Fixed => 0, - HeaderType::Variable => 1, - } - } - - pub(crate) const fn to_bits_lora(self) -> u8 { - match self { - HeaderType::Fixed => 1, - HeaderType::Variable => 0, - } - } -} - -/// CRC type definition for [`GenericPacketParams`]. -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CrcType { - /// 1-byte CRC. - Byte1 = 0x0, - /// CRC disabled. - Disabled = 0x1, - /// 2-byte CRC. - Byte2 = 0x2, - /// 1-byte inverted CRC. - Byte1Inverted = 0x4, - /// 2-byte inverted CRC. - Byte2Inverted = 0x6, -} - -/// Packet parameters for [`set_packet_params`]. -/// -/// [`set_packet_params`]: super::SubGhz::set_packet_params -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct GenericPacketParams { - buf: [u8; 10], -} - -impl GenericPacketParams { - /// Create a new `GenericPacketParams`. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::GenericPacketParams; - /// - /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new(); - /// assert_eq!(PKT_PARAMS, GenericPacketParams::default()); - /// ``` - pub const fn new() -> GenericPacketParams { - const OPCODE: u8 = super::OpCode::SetPacketParams as u8; - // const variable ensure the compile always optimizes the methods - const NEW: GenericPacketParams = GenericPacketParams { - buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - } - .set_preamble_len(1) - .set_preamble_detection(PreambleDetection::Disabled) - .set_sync_word_len(0) - .set_addr_comp(AddrComp::Disabled) - .set_header_type(HeaderType::Fixed) - .set_payload_len(1); - - NEW - } - - /// Preamble length in number of symbols. - /// - /// Values of zero are invalid, and will automatically be set to 1. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::GenericPacketParams; - /// - /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_preamble_len(0x1234); - /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12); - /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34); - /// ``` - #[must_use = "preamble_length returns a modified GenericPacketParams"] - pub const fn set_preamble_len(mut self, mut len: u16) -> GenericPacketParams { - if len == 0 { - len = 1 - } - self.buf[1] = ((len >> 8) & 0xFF) as u8; - self.buf[2] = (len & 0xFF) as u8; - self - } - - /// Preamble detection length in number of bit symbols. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{GenericPacketParams, PreambleDetection}; - /// - /// const PKT_PARAMS: GenericPacketParams = - /// GenericPacketParams::new().set_preamble_detection(PreambleDetection::Bit8); - /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x4); - /// ``` - #[must_use = "set_preamble_detection returns a modified GenericPacketParams"] - pub const fn set_preamble_detection(mut self, pb_det: PreambleDetection) -> GenericPacketParams { - self.buf[3] = pb_det as u8; - self - } - - /// Sync word length in number of bit symbols. - /// - /// Valid values are `0x00` - `0x40` for 0 to 64-bits respectively. - /// Values that exceed the maximum will saturate at `0x40`. - /// - /// # Example - /// - /// Set the sync word length to 4 bytes (16 bits). - /// - /// ``` - /// use stm32wlxx_hal::subghz::GenericPacketParams; - /// - /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_sync_word_len(16); - /// # assert_eq!(PKT_PARAMS.as_slice()[4], 0x10); - /// ``` - #[must_use = "set_sync_word_len returns a modified GenericPacketParams"] - pub const fn set_sync_word_len(mut self, len: u8) -> GenericPacketParams { - const MAX: u8 = 0x40; - if len > MAX { - self.buf[4] = MAX; - } else { - self.buf[4] = len; - } - self - } - - /// Address comparison/filtering. - /// - /// # Example - /// - /// Enable address on the node address. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{AddrComp, GenericPacketParams}; - /// - /// const PKT_PARAMS: GenericPacketParams = - /// GenericPacketParams::new().set_addr_comp(AddrComp::Node); - /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x01); - /// ``` - #[must_use = "set_addr_comp returns a modified GenericPacketParams"] - pub const fn set_addr_comp(mut self, addr_comp: AddrComp) -> GenericPacketParams { - self.buf[5] = addr_comp as u8; - self - } - - /// Header type definition. - /// - /// **Note:** The reference manual calls this packet type, but that results - /// in a conflicting variable name for the modulation scheme, which the - /// reference manual also calls packet type. - /// - /// # Example - /// - /// Set the header type to a variable length. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{GenericPacketParams, HeaderType}; - /// - /// const PKT_PARAMS: GenericPacketParams = - /// GenericPacketParams::new().set_header_type(HeaderType::Variable); - /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x01); - /// ``` - #[must_use = "set_header_type returns a modified GenericPacketParams"] - pub const fn set_header_type(mut self, header_type: HeaderType) -> GenericPacketParams { - self.buf[6] = header_type.to_bits_generic(); - self - } - - /// Set the payload length in bytes. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::GenericPacketParams; - /// - /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_payload_len(12); - /// # assert_eq!(PKT_PARAMS.as_slice()[7], 12); - /// ``` - #[must_use = "set_payload_len returns a modified GenericPacketParams"] - pub const fn set_payload_len(mut self, len: u8) -> GenericPacketParams { - self.buf[7] = len; - self - } - - /// CRC type definition. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CrcType, GenericPacketParams}; - /// - /// const PKT_PARAMS: GenericPacketParams = - /// GenericPacketParams::new().set_crc_type(CrcType::Byte2Inverted); - /// # assert_eq!(PKT_PARAMS.as_slice()[8], 0x6); - /// ``` - #[must_use = "set_payload_len returns a modified GenericPacketParams"] - pub const fn set_crc_type(mut self, crc_type: CrcType) -> GenericPacketParams { - self.buf[8] = crc_type as u8; - self - } - - /// Whitening enable. - /// - /// # Example - /// - /// Enable whitening. - /// - /// ``` - /// use stm32wlxx_hal::subghz::GenericPacketParams; - /// - /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new().set_whitening_enable(true); - /// # assert_eq!(PKT_PARAMS.as_slice()[9], 1); - /// ``` - #[must_use = "set_whitening_enable returns a modified GenericPacketParams"] - pub const fn set_whitening_enable(mut self, en: bool) -> GenericPacketParams { - self.buf[9] = en as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{ - /// AddrComp, CrcType, GenericPacketParams, HeaderType, PreambleDetection, - /// }; - /// - /// const PKT_PARAMS: GenericPacketParams = GenericPacketParams::new() - /// .set_preamble_len(8) - /// .set_preamble_detection(PreambleDetection::Disabled) - /// .set_sync_word_len(2) - /// .set_addr_comp(AddrComp::Disabled) - /// .set_header_type(HeaderType::Fixed) - /// .set_payload_len(128) - /// .set_crc_type(CrcType::Byte2) - /// .set_whitening_enable(true); - /// - /// assert_eq!( - /// PKT_PARAMS.as_slice(), - /// &[0x8C, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x80, 0x02, 0x01] - /// ); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for GenericPacketParams { - fn default() -> Self { - Self::new() - } -} - -/// Packet parameters for [`set_lora_packet_params`]. -/// -/// [`set_lora_packet_params`]: super::SubGhz::set_lora_packet_params -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct LoRaPacketParams { - buf: [u8; 7], -} - -impl LoRaPacketParams { - /// Create a new `LoRaPacketParams`. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaPacketParams; - /// - /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new(); - /// assert_eq!(PKT_PARAMS, LoRaPacketParams::default()); - /// ``` - pub const fn new() -> LoRaPacketParams { - const OPCODE: u8 = super::OpCode::SetPacketParams as u8; - // const variable ensure the compile always optimizes the methods - const NEW: LoRaPacketParams = LoRaPacketParams { - buf: [OPCODE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - } - .set_preamble_len(1) - .set_header_type(HeaderType::Fixed) - .set_payload_len(1) - .set_crc_en(true) - .set_invert_iq(false); - - NEW - } - - /// Preamble length in number of symbols. - /// - /// Values of zero are invalid, and will automatically be set to 1. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaPacketParams; - /// - /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_preamble_len(0x1234); - /// # assert_eq!(PKT_PARAMS.as_slice()[1], 0x12); - /// # assert_eq!(PKT_PARAMS.as_slice()[2], 0x34); - /// ``` - #[must_use = "preamble_length returns a modified LoRaPacketParams"] - pub const fn set_preamble_len(mut self, mut len: u16) -> LoRaPacketParams { - if len == 0 { - len = 1 - } - self.buf[1] = ((len >> 8) & 0xFF) as u8; - self.buf[2] = (len & 0xFF) as u8; - self - } - - /// Header type (fixed or variable). - /// - /// # Example - /// - /// Set the payload type to a fixed length. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams}; - /// - /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_header_type(HeaderType::Fixed); - /// # assert_eq!(PKT_PARAMS.as_slice()[3], 0x01); - /// ``` - #[must_use = "set_header_type returns a modified LoRaPacketParams"] - pub const fn set_header_type(mut self, header_type: HeaderType) -> LoRaPacketParams { - self.buf[3] = header_type.to_bits_lora(); - self - } - - /// Set the payload length in bytes. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaPacketParams; - /// - /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_payload_len(12); - /// # assert_eq!(PKT_PARAMS.as_slice()[4], 12); - /// ``` - #[must_use = "set_payload_len returns a modified LoRaPacketParams"] - pub const fn set_payload_len(mut self, len: u8) -> LoRaPacketParams { - self.buf[4] = len; - self - } - - /// CRC enable. - /// - /// # Example - /// - /// Enable CRC. - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaPacketParams; - /// - /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_crc_en(true); - /// # assert_eq!(PKT_PARAMS.as_slice()[5], 0x1); - /// ``` - #[must_use = "set_crc_en returns a modified LoRaPacketParams"] - pub const fn set_crc_en(mut self, en: bool) -> LoRaPacketParams { - self.buf[5] = en as u8; - self - } - - /// IQ setup. - /// - /// # Example - /// - /// Use an inverted IQ setup. - /// - /// ``` - /// use stm32wlxx_hal::subghz::LoRaPacketParams; - /// - /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new().set_invert_iq(true); - /// # assert_eq!(PKT_PARAMS.as_slice()[6], 0x1); - /// ``` - #[must_use = "set_invert_iq returns a modified LoRaPacketParams"] - pub const fn set_invert_iq(mut self, invert: bool) -> LoRaPacketParams { - self.buf[6] = invert as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{HeaderType, LoRaPacketParams}; - /// - /// const PKT_PARAMS: LoRaPacketParams = LoRaPacketParams::new() - /// .set_preamble_len(5 * 8) - /// .set_header_type(HeaderType::Fixed) - /// .set_payload_len(64) - /// .set_crc_en(true) - /// .set_invert_iq(true); - /// - /// assert_eq!( - /// PKT_PARAMS.as_slice(), - /// &[0x8C, 0x00, 0x28, 0x01, 0x40, 0x01, 0x01] - /// ); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for LoRaPacketParams { - fn default() -> Self { - Self::new() - } -} - -/// Packet parameters for [`set_bpsk_packet_params`]. -/// -/// [`set_bpsk_packet_params`]: super::SubGhz::set_bpsk_packet_params -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct BpskPacketParams { - buf: [u8; 2], -} - -impl BpskPacketParams { - /// Create a new `BpskPacketParams`. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::BpskPacketParams; - /// - /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new(); - /// assert_eq!(PKT_PARAMS, BpskPacketParams::default()); - /// ``` - pub const fn new() -> BpskPacketParams { - BpskPacketParams { - buf: [super::OpCode::SetPacketParams as u8, 0x00], - } - } - - /// Set the payload length in bytes. - /// - /// The length includes preamble, sync word, device ID, and CRC. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::BpskPacketParams; - /// - /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(12); - /// # assert_eq!(PKT_PARAMS.as_slice()[1], 12); - /// ``` - #[must_use = "set_payload_len returns a modified BpskPacketParams"] - pub const fn set_payload_len(mut self, len: u8) -> BpskPacketParams { - self.buf[1] = len; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{BpskPacketParams, HeaderType}; - /// - /// const PKT_PARAMS: BpskPacketParams = BpskPacketParams::new().set_payload_len(24); - /// - /// assert_eq!(PKT_PARAMS.as_slice(), &[0x8C, 24]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for BpskPacketParams { - fn default() -> Self { - Self::new() - } -} diff --git a/embassy-stm32/src/subghz/packet_status.rs b/embassy-stm32/src/subghz/packet_status.rs deleted file mode 100644 index b3acd73ce..000000000 --- a/embassy-stm32/src/subghz/packet_status.rs +++ /dev/null @@ -1,282 +0,0 @@ -use super::{Ratio, Status}; - -/// (G)FSK packet status. -/// -/// Returned by [`fsk_packet_status`]. -/// -/// [`fsk_packet_status`]: super::SubGhz::fsk_packet_status -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct FskPacketStatus { - buf: [u8; 4], -} - -impl From<[u8; 4]> for FskPacketStatus { - fn from(buf: [u8; 4]) -> Self { - FskPacketStatus { buf } - } -} - -impl FskPacketStatus { - /// Get the status. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CmdStatus, FskPacketStatus, Status, StatusMode}; - /// - /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0]; - /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio); - /// let status: Status = pkt_status.status(); - /// assert_eq!(status.mode(), Ok(StatusMode::Rx)); - /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable)); - /// ``` - pub const fn status(&self) -> Status { - Status::from_raw(self.buf[0]) - } - - /// Returns `true` if a preamble error occurred. - pub const fn preamble_err(&self) -> bool { - (self.buf[1] & (1 << 7)) != 0 - } - - /// Returns `true` if a synchronization error occurred. - pub const fn sync_err(&self) -> bool { - (self.buf[1] & (1 << 6)) != 0 - } - - /// Returns `true` if an address error occurred. - pub const fn addr_err(&self) -> bool { - (self.buf[1] & (1 << 5)) != 0 - } - - /// Returns `true` if an CRC error occurred. - pub const fn crc_err(&self) -> bool { - (self.buf[1] & (1 << 4)) != 0 - } - - /// Returns `true` if a length error occurred. - pub const fn length_err(&self) -> bool { - (self.buf[1] & (1 << 3)) != 0 - } - - /// Returns `true` if an abort error occurred. - pub const fn abort_err(&self) -> bool { - (self.buf[1] & (1 << 2)) != 0 - } - - /// Returns `true` if a packet is received. - pub const fn pkt_received(&self) -> bool { - (self.buf[1] & (1 << 1)) != 0 - } - - /// Returns `true` when a packet has been sent. - pub const fn pkt_sent(&self) -> bool { - (self.buf[1] & 1) != 0 - } - - /// Returns `true` if any error occurred. - pub const fn any_err(&self) -> bool { - (self.buf[1] & 0xFC) != 0 - } - - /// RSSI level when the synchronization address is detected. - /// - /// Units are in dBm. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio}; - /// - /// let example_data_from_radio: [u8; 4] = [0, 0, 80, 0]; - /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio); - /// assert_eq!(pkt_status.rssi_sync().to_integer(), -40); - /// ``` - pub fn rssi_sync(&self) -> Ratio { - Ratio::new_raw(i16::from(self.buf[2]), -2) - } - - /// Return the RSSI level over the received packet. - /// - /// Units are in dBm. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::{subghz::FskPacketStatus, Ratio}; - /// - /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 100]; - /// let pkt_status: FskPacketStatus = FskPacketStatus::from(example_data_from_radio); - /// assert_eq!(pkt_status.rssi_avg().to_integer(), -50); - /// ``` - pub fn rssi_avg(&self) -> Ratio { - Ratio::new_raw(i16::from(self.buf[3]), -2) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for FskPacketStatus { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!( - fmt, - r#"FskPacketStatus {{ - status: {}, - preamble_err: {}, - sync_err: {}, - addr_err: {}, - crc_err: {}, - length_err: {}, - abort_err: {}, - pkt_received: {}, - pkt_sent: {}, - rssi_sync: {}, - rssi_avg: {}, -}}"#, - self.status(), - self.preamble_err(), - self.sync_err(), - self.addr_err(), - self.crc_err(), - self.length_err(), - self.abort_err(), - self.pkt_received(), - self.pkt_sent(), - self.rssi_sync().to_integer(), - self.rssi_avg().to_integer() - ) - } -} - -impl core::fmt::Debug for FskPacketStatus { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("FskPacketStatus") - .field("status", &self.status()) - .field("preamble_err", &self.preamble_err()) - .field("sync_err", &self.sync_err()) - .field("addr_err", &self.addr_err()) - .field("crc_err", &self.crc_err()) - .field("length_err", &self.length_err()) - .field("abort_err", &self.abort_err()) - .field("pkt_received", &self.pkt_received()) - .field("pkt_sent", &self.pkt_sent()) - .field("rssi_sync", &self.rssi_sync().to_integer()) - .field("rssi_avg", &self.rssi_avg().to_integer()) - .finish() - } -} - -/// (G)FSK packet status. -/// -/// Returned by [`lora_packet_status`]. -/// -/// [`lora_packet_status`]: super::SubGhz::lora_packet_status -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct LoRaPacketStatus { - buf: [u8; 4], -} - -impl From<[u8; 4]> for LoRaPacketStatus { - fn from(buf: [u8; 4]) -> Self { - LoRaPacketStatus { buf } - } -} - -impl LoRaPacketStatus { - /// Get the status. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CmdStatus, LoRaPacketStatus, Status, StatusMode}; - /// - /// let example_data_from_radio: [u8; 4] = [0x54, 0, 0, 0]; - /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); - /// let status: Status = pkt_status.status(); - /// assert_eq!(status.mode(), Ok(StatusMode::Rx)); - /// assert_eq!(status.cmd(), Ok(CmdStatus::Avaliable)); - /// ``` - pub const fn status(&self) -> Status { - Status::from_raw(self.buf[0]) - } - - /// Average RSSI level over the received packet. - /// - /// Units are in dBm. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio}; - /// - /// let example_data_from_radio: [u8; 4] = [0, 80, 0, 0]; - /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); - /// assert_eq!(pkt_status.rssi_pkt().to_integer(), -40); - /// ``` - pub fn rssi_pkt(&self) -> Ratio { - Ratio::new_raw(i16::from(self.buf[1]), -2) - } - - /// Estimation of SNR over the received packet. - /// - /// Units are in dB. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio}; - /// - /// let example_data_from_radio: [u8; 4] = [0, 0, 40, 0]; - /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); - /// assert_eq!(pkt_status.snr_pkt().to_integer(), 10); - /// ``` - pub fn snr_pkt(&self) -> Ratio { - Ratio::new_raw(i16::from(self.buf[2]), 4) - } - - /// Estimation of RSSI level of the LoRa signal after despreading. - /// - /// Units are in dBm. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::{subghz::LoRaPacketStatus, Ratio}; - /// - /// let example_data_from_radio: [u8; 4] = [0, 0, 0, 80]; - /// let pkt_status: LoRaPacketStatus = LoRaPacketStatus::from(example_data_from_radio); - /// assert_eq!(pkt_status.signal_rssi_pkt().to_integer(), -40); - /// ``` - pub fn signal_rssi_pkt(&self) -> Ratio { - Ratio::new_raw(i16::from(self.buf[3]), -2) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for LoRaPacketStatus { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!( - fmt, - r#"LoRaPacketStatus {{ - status: {}, - rssi_pkt: {}, - snr_pkt: {}, - signal_rssi_pkt: {}, -}}"#, - self.status(), - self.rssi_pkt().to_integer(), - self.snr_pkt().to_integer(), - self.signal_rssi_pkt().to_integer(), - ) - } -} - -impl core::fmt::Debug for LoRaPacketStatus { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("LoRaPacketStatus") - .field("status", &self.status()) - .field("rssi_pkt", &self.rssi_pkt().to_integer()) - .field("snr_pkt", &self.snr_pkt().to_integer()) - .field("signal_rssi_pkt", &self.signal_rssi_pkt().to_integer()) - .finish() - } -} diff --git a/embassy-stm32/src/subghz/packet_type.rs b/embassy-stm32/src/subghz/packet_type.rs deleted file mode 100644 index 88c62bb6a..000000000 --- a/embassy-stm32/src/subghz/packet_type.rs +++ /dev/null @@ -1,44 +0,0 @@ -/// Packet type definition. -/// -/// Argument of [`set_packet_type`] -/// -/// [`set_packet_type`]: super::SubGhz::set_packet_type -#[repr(u8)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PacketType { - /// FSK (frequency shift keying) generic packet type. - Fsk = 0, - /// LoRa (long range) packet type. - LoRa = 1, - /// BPSK (binary phase shift keying) packet type. - Bpsk = 2, - /// MSK (minimum shift keying) generic packet type. - Msk = 3, -} - -impl PacketType { - /// Create a new `PacketType` from bits. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PacketType; - /// - /// assert_eq!(PacketType::from_raw(0), Ok(PacketType::Fsk)); - /// assert_eq!(PacketType::from_raw(1), Ok(PacketType::LoRa)); - /// assert_eq!(PacketType::from_raw(2), Ok(PacketType::Bpsk)); - /// assert_eq!(PacketType::from_raw(3), Ok(PacketType::Msk)); - /// // Other values are reserved - /// assert_eq!(PacketType::from_raw(4), Err(4)); - /// ``` - pub const fn from_raw(bits: u8) -> Result { - match bits { - 0 => Ok(PacketType::Fsk), - 1 => Ok(PacketType::LoRa), - 2 => Ok(PacketType::Bpsk), - 3 => Ok(PacketType::Msk), - _ => Err(bits), - } - } -} diff --git a/embassy-stm32/src/subghz/pkt_ctrl.rs b/embassy-stm32/src/subghz/pkt_ctrl.rs deleted file mode 100644 index 265833e35..000000000 --- a/embassy-stm32/src/subghz/pkt_ctrl.rs +++ /dev/null @@ -1,247 +0,0 @@ -/// Generic packet infinite sequence selection. -/// -/// Argument of [`PktCtrl::set_inf_seq_sel`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InfSeqSel { - /// Preamble `0x5555`. - Five = 0b00, - /// Preamble `0x0000`. - Zero = 0b01, - /// Preamble `0xFFFF`. - One = 0b10, - /// PRBS9. - Prbs9 = 0b11, -} - -impl Default for InfSeqSel { - fn default() -> Self { - InfSeqSel::Five - } -} - -/// Generic packet control. -/// -/// Argument of [`set_pkt_ctrl`](super::SubGhz::set_pkt_ctrl). -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PktCtrl { - val: u8, -} - -impl PktCtrl { - /// Reset value of the packet control register. - pub const RESET: PktCtrl = PktCtrl { val: 0x21 }; - - /// Create a new [`PktCtrl`] structure from a raw value. - /// - /// Reserved bits will be masked. - pub const fn from_raw(raw: u8) -> Self { - Self { val: raw & 0x3F } - } - - /// Get the raw value of the [`PktCtrl`] register. - pub const fn as_bits(&self) -> u8 { - self.val - } - - /// Generic packet synchronization word detection enable. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_sync_det_en(true); - /// ``` - #[must_use = "set_sync_det_en returns a modified PktCtrl"] - pub const fn set_sync_det_en(mut self, en: bool) -> PktCtrl { - if en { - self.val |= 1 << 5; - } else { - self.val &= !(1 << 5); - } - self - } - - /// Returns `true` if generic packet synchronization word detection is - /// enabled. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// let pc: PktCtrl = PktCtrl::RESET; - /// assert_eq!(pc.sync_det_en(), true); - /// let pc: PktCtrl = pc.set_sync_det_en(false); - /// assert_eq!(pc.sync_det_en(), false); - /// let pc: PktCtrl = pc.set_sync_det_en(true); - /// assert_eq!(pc.sync_det_en(), true); - /// ``` - pub const fn sync_det_en(&self) -> bool { - self.val & (1 << 5) != 0 - } - - /// Generic packet continuous transmit enable. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_cont_tx_en(true); - /// ``` - #[must_use = "set_cont_tx_en returns a modified PktCtrl"] - pub const fn set_cont_tx_en(mut self, en: bool) -> PktCtrl { - if en { - self.val |= 1 << 4; - } else { - self.val &= !(1 << 4); - } - self - } - - /// Returns `true` if generic packet continuous transmit is enabled. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// let pc: PktCtrl = PktCtrl::RESET; - /// assert_eq!(pc.cont_tx_en(), false); - /// let pc: PktCtrl = pc.set_cont_tx_en(true); - /// assert_eq!(pc.cont_tx_en(), true); - /// let pc: PktCtrl = pc.set_cont_tx_en(false); - /// assert_eq!(pc.cont_tx_en(), false); - /// ``` - pub const fn cont_tx_en(&self) -> bool { - self.val & (1 << 4) != 0 - } - - /// Set the continuous sequence type. - #[must_use = "set_inf_seq_sel returns a modified PktCtrl"] - pub const fn set_inf_seq_sel(mut self, sel: InfSeqSel) -> PktCtrl { - self.val &= !(0b11 << 2); - self.val |= (sel as u8) << 2; - self - } - - /// Get the continuous sequence type. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{InfSeqSel, PktCtrl}; - /// - /// let pc: PktCtrl = PktCtrl::RESET; - /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five); - /// - /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Zero); - /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Zero); - /// - /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::One); - /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::One); - /// - /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Prbs9); - /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Prbs9); - /// - /// let pc: PktCtrl = pc.set_inf_seq_sel(InfSeqSel::Five); - /// assert_eq!(pc.inf_seq_sel(), InfSeqSel::Five); - /// ``` - pub const fn inf_seq_sel(&self) -> InfSeqSel { - match (self.val >> 2) & 0b11 { - 0b00 => InfSeqSel::Five, - 0b01 => InfSeqSel::Zero, - 0b10 => InfSeqSel::One, - _ => InfSeqSel::Prbs9, - } - } - - /// Enable infinite sequence generation. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_inf_seq_en(true); - /// ``` - #[must_use = "set_inf_seq_en returns a modified PktCtrl"] - pub const fn set_inf_seq_en(mut self, en: bool) -> PktCtrl { - if en { - self.val |= 1 << 1; - } else { - self.val &= !(1 << 1); - } - self - } - - /// Returns `true` if infinite sequence generation is enabled. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// let pc: PktCtrl = PktCtrl::RESET; - /// assert_eq!(pc.inf_seq_en(), false); - /// let pc: PktCtrl = pc.set_inf_seq_en(true); - /// assert_eq!(pc.inf_seq_en(), true); - /// let pc: PktCtrl = pc.set_inf_seq_en(false); - /// assert_eq!(pc.inf_seq_en(), false); - /// ``` - pub const fn inf_seq_en(&self) -> bool { - self.val & (1 << 1) != 0 - } - - /// Set the value of bit-8 (9th bit) for generic packet whitening. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// const PKT_CTRL: PktCtrl = PktCtrl::RESET.set_whitening_init(true); - /// ``` - #[must_use = "set_whitening_init returns a modified PktCtrl"] - pub const fn set_whitening_init(mut self, val: bool) -> PktCtrl { - if val { - self.val |= 1; - } else { - self.val &= !1; - } - self - } - - /// Returns `true` if bit-8 of the generic packet whitening is set. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PktCtrl; - /// - /// let pc: PktCtrl = PktCtrl::RESET; - /// assert_eq!(pc.whitening_init(), true); - /// let pc: PktCtrl = pc.set_whitening_init(false); - /// assert_eq!(pc.whitening_init(), false); - /// let pc: PktCtrl = pc.set_whitening_init(true); - /// assert_eq!(pc.whitening_init(), true); - /// ``` - pub const fn whitening_init(&self) -> bool { - self.val & 0b1 != 0 - } -} - -impl From for u8 { - fn from(pc: PktCtrl) -> Self { - pc.val - } -} - -impl Default for PktCtrl { - fn default() -> Self { - Self::RESET - } -} diff --git a/embassy-stm32/src/subghz/pmode.rs b/embassy-stm32/src/subghz/pmode.rs deleted file mode 100644 index 0c07f3195..000000000 --- a/embassy-stm32/src/subghz/pmode.rs +++ /dev/null @@ -1,27 +0,0 @@ -/// RX gain power modes. -/// -/// Argument of [`set_rx_gain`]. -/// -/// [`set_rx_gain`]: super::SubGhz::set_rx_gain -#[repr(u8)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PMode { - /// Power saving mode. - /// - /// Reduces sensitivity. - #[allow(clippy::identity_op)] - PowerSaving = (0x25 << 2) | 0b00, - /// Boost mode level 1. - /// - /// Improves sensitivity at detriment of power consumption. - Boost1 = (0x25 << 2) | 0b01, - /// Boost mode level 2. - /// - /// Improves a set further sensitivity at detriment of power consumption. - Boost2 = (0x25 << 2) | 0b10, - /// Boost mode. - /// - /// Best receiver sensitivity. - Boost = (0x25 << 2) | 0b11, -} diff --git a/embassy-stm32/src/subghz/pwr_ctrl.rs b/embassy-stm32/src/subghz/pwr_ctrl.rs deleted file mode 100644 index 974bddebb..000000000 --- a/embassy-stm32/src/subghz/pwr_ctrl.rs +++ /dev/null @@ -1,160 +0,0 @@ -/// Power-supply current limit. -/// -/// Argument of [`PwrCtrl::set_current_lim`]. -#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum CurrentLim { - /// 25 mA - Milli25 = 0x0, - /// 50 mA (default) - Milli50 = 0x1, - /// 100 mA - Milli100 = 0x2, - /// 200 mA - Milli200 = 0x3, -} - -impl CurrentLim { - /// Get the SMPS drive value as milliamps. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::CurrentLim; - /// - /// assert_eq!(CurrentLim::Milli25.as_milliamps(), 25); - /// assert_eq!(CurrentLim::Milli50.as_milliamps(), 50); - /// assert_eq!(CurrentLim::Milli100.as_milliamps(), 100); - /// assert_eq!(CurrentLim::Milli200.as_milliamps(), 200); - /// ``` - pub const fn as_milliamps(&self) -> u8 { - match self { - CurrentLim::Milli25 => 25, - CurrentLim::Milli50 => 50, - CurrentLim::Milli100 => 100, - CurrentLim::Milli200 => 200, - } - } -} - -impl Default for CurrentLim { - fn default() -> Self { - CurrentLim::Milli50 - } -} - -/// Power control. -/// -/// Argument of [`set_bit_sync`](super::SubGhz::set_bit_sync). -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PwrCtrl { - val: u8, -} - -impl PwrCtrl { - /// Power control register reset value. - pub const RESET: PwrCtrl = PwrCtrl { val: 0x50 }; - - /// Create a new [`PwrCtrl`] structure from a raw value. - /// - /// Reserved bits will be masked. - pub const fn from_raw(raw: u8) -> Self { - Self { val: raw & 0x70 } - } - - /// Get the raw value of the [`PwrCtrl`] register. - pub const fn as_bits(&self) -> u8 { - self.val - } - - /// Set the current limiter enable. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PwrCtrl; - /// - /// const PWR_CTRL: PwrCtrl = PwrCtrl::RESET.set_current_lim_en(true); - /// # assert_eq!(u8::from(PWR_CTRL), 0x50u8); - /// ``` - #[must_use = "set_current_lim_en returns a modified PwrCtrl"] - pub const fn set_current_lim_en(mut self, en: bool) -> PwrCtrl { - if en { - self.val |= 1 << 6; - } else { - self.val &= !(1 << 6); - } - self - } - - /// Returns `true` if current limiting is enabled - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::PwrCtrl; - /// - /// let pc: PwrCtrl = PwrCtrl::RESET; - /// assert_eq!(pc.current_limit_en(), true); - /// let pc: PwrCtrl = pc.set_current_lim_en(false); - /// assert_eq!(pc.current_limit_en(), false); - /// let pc: PwrCtrl = pc.set_current_lim_en(true); - /// assert_eq!(pc.current_limit_en(), true); - /// ``` - pub const fn current_limit_en(&self) -> bool { - self.val & (1 << 6) != 0 - } - - /// Set the current limit. - #[must_use = "set_current_lim returns a modified PwrCtrl"] - pub const fn set_current_lim(mut self, lim: CurrentLim) -> PwrCtrl { - self.val &= !(0x30); - self.val |= (lim as u8) << 4; - self - } - - /// Get the current limit. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CurrentLim, PwrCtrl}; - /// - /// let pc: PwrCtrl = PwrCtrl::RESET; - /// assert_eq!(pc.current_lim(), CurrentLim::Milli50); - /// - /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli25); - /// assert_eq!(pc.current_lim(), CurrentLim::Milli25); - /// - /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli50); - /// assert_eq!(pc.current_lim(), CurrentLim::Milli50); - /// - /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli100); - /// assert_eq!(pc.current_lim(), CurrentLim::Milli100); - /// - /// let pc: PwrCtrl = pc.set_current_lim(CurrentLim::Milli200); - /// assert_eq!(pc.current_lim(), CurrentLim::Milli200); - /// ``` - pub const fn current_lim(&self) -> CurrentLim { - match (self.val >> 4) & 0b11 { - 0x0 => CurrentLim::Milli25, - 0x1 => CurrentLim::Milli50, - 0x2 => CurrentLim::Milli100, - _ => CurrentLim::Milli200, - } - } -} - -impl From for u8 { - fn from(bs: PwrCtrl) -> Self { - bs.val - } -} - -impl Default for PwrCtrl { - fn default() -> Self { - Self::RESET - } -} diff --git a/embassy-stm32/src/subghz/reg_mode.rs b/embassy-stm32/src/subghz/reg_mode.rs deleted file mode 100644 index b83226954..000000000 --- a/embassy-stm32/src/subghz/reg_mode.rs +++ /dev/null @@ -1,18 +0,0 @@ -/// Radio power supply selection. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum RegMode { - /// Linear dropout regulator - Ldo = 0b0, - /// Switch mode power supply. - /// - /// Used in standby with HSE32, FS, RX, and TX modes. - Smps = 0b1, -} - -impl Default for RegMode { - fn default() -> Self { - RegMode::Ldo - } -} diff --git a/embassy-stm32/src/subghz/rf_frequency.rs b/embassy-stm32/src/subghz/rf_frequency.rs deleted file mode 100644 index 3de2f50c4..000000000 --- a/embassy-stm32/src/subghz/rf_frequency.rs +++ /dev/null @@ -1,135 +0,0 @@ -/// RF frequency structure. -/// -/// Argument of [`set_rf_frequency`]. -/// -/// [`set_rf_frequency`]: super::SubGhz::set_rf_frequency -#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct RfFreq { - buf: [u8; 5], -} - -impl RfFreq { - /// 915MHz, often used in Australia and North America. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::RfFreq; - /// - /// assert_eq!(RfFreq::F915.freq(), 915_000_000); - /// ``` - pub const F915: RfFreq = RfFreq::from_raw(0x39_30_00_00); - - /// 868MHz, often used in Europe. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::RfFreq; - /// - /// assert_eq!(RfFreq::F868.freq(), 868_000_000); - /// ``` - pub const F868: RfFreq = RfFreq::from_raw(0x36_40_00_00); - - /// 433MHz, often used in Europe. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::RfFreq; - /// - /// assert_eq!(RfFreq::F433.freq(), 433_000_000); - /// ``` - pub const F433: RfFreq = RfFreq::from_raw(0x1B_10_00_00); - - /// Create a new `RfFreq` from a raw bit value. - /// - /// The equation used to get the PLL frequency from the raw bits is: - /// - /// RFPLL = 32e6 × bits / 225 - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::RfFreq; - /// - /// const FREQ: RfFreq = RfFreq::from_raw(0x39300000); - /// assert_eq!(FREQ, RfFreq::F915); - /// ``` - pub const fn from_raw(bits: u32) -> RfFreq { - RfFreq { - buf: [ - super::OpCode::SetRfFrequency as u8, - ((bits >> 24) & 0xFF) as u8, - ((bits >> 16) & 0xFF) as u8, - ((bits >> 8) & 0xFF) as u8, - (bits & 0xFF) as u8, - ], - } - } - - /// Create a new `RfFreq` from a PLL frequency. - /// - /// The equation used to get the raw bits from the PLL frequency is: - /// - /// bits = RFPLL * 225 / 32e6 - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::RfFreq; - /// - /// const FREQ: RfFreq = RfFreq::from_frequency(915_000_000); - /// assert_eq!(FREQ, RfFreq::F915); - /// ``` - pub const fn from_frequency(freq: u32) -> RfFreq { - Self::from_raw((((freq as u64) * (1 << 25)) / 32_000_000) as u32) - } - - // Get the frequency bit value. - const fn as_bits(&self) -> u32 { - ((self.buf[1] as u32) << 24) | ((self.buf[2] as u32) << 16) | ((self.buf[3] as u32) << 8) | (self.buf[4] as u32) - } - - /// Get the actual frequency. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::RfFreq; - /// - /// assert_eq!(RfFreq::from_raw(0x39300000).freq(), 915_000_000); - /// ``` - pub fn freq(&self) -> u32 { - (32_000_000 * (self.as_bits() as u64) / (1 << 25)) as u32 - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::RfFreq; - /// - /// assert_eq!(RfFreq::F915.as_slice(), &[0x86, 0x39, 0x30, 0x00, 0x00]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -#[cfg(test)] -mod test { - use super::RfFreq; - - #[test] - fn max() { - assert_eq!(RfFreq::from_raw(u32::MAX).freq(), 4_095_999_999); - } - - #[test] - fn min() { - assert_eq!(RfFreq::from_raw(u32::MIN).freq(), 0); - } -} diff --git a/embassy-stm32/src/subghz/rx_timeout_stop.rs b/embassy-stm32/src/subghz/rx_timeout_stop.rs deleted file mode 100644 index 1d4aaecee..000000000 --- a/embassy-stm32/src/subghz/rx_timeout_stop.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// Receiver event which stops the RX timeout timer. -/// -/// Used by [`set_rx_timeout_stop`]. -/// -/// [`set_rx_timeout_stop`]: super::SubGhz::set_rx_timeout_stop -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum RxTimeoutStop { - /// Receive timeout stopped on synchronization word detection in generic - /// packet mode or header detection in LoRa packet mode. - Sync = 0b0, - /// Receive timeout stopped on preamble detection. - Preamble = 0b1, -} - -impl From for u8 { - fn from(rx_ts: RxTimeoutStop) -> Self { - rx_ts as u8 - } -} diff --git a/embassy-stm32/src/subghz/sleep_cfg.rs b/embassy-stm32/src/subghz/sleep_cfg.rs deleted file mode 100644 index 0a50e9704..000000000 --- a/embassy-stm32/src/subghz/sleep_cfg.rs +++ /dev/null @@ -1,107 +0,0 @@ -/// Startup configurations when exiting sleep mode. -/// -/// Argument of [`SleepCfg::set_startup`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum Startup { - /// Cold startup when exiting Sleep mode, configuration registers reset. - Cold = 0, - /// Warm startup when exiting Sleep mode, - /// configuration registers kept in retention. - /// - /// **Note:** Only the configuration of the activated modem, - /// before going to sleep mode, is retained. - /// The configuration of the other modes is lost and must be re-configured - /// when exiting sleep mode. - Warm = 1, -} - -impl Default for Startup { - fn default() -> Self { - Startup::Warm - } -} - -/// Sleep configuration. -/// -/// Argument of [`set_sleep`]. -/// -/// [`set_sleep`]: super::SubGhz::set_sleep -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SleepCfg(u8); - -impl SleepCfg { - /// Create a new `SleepCfg` structure. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// The defaults are a warm startup, with RTC wakeup enabled. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::SleepCfg; - /// - /// const SLEEP_CFG: SleepCfg = SleepCfg::new(); - /// assert_eq!(SLEEP_CFG, SleepCfg::default()); - /// # assert_eq!(u8::from(SLEEP_CFG), 0b101); - /// ``` - pub const fn new() -> SleepCfg { - SleepCfg(0).set_startup(Startup::Warm).set_rtc_wakeup_en(true) - } - - /// Set the startup mode. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{SleepCfg, Startup}; - /// - /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_startup(Startup::Cold); - /// # assert_eq!(u8::from(SLEEP_CFG), 0b001); - /// # assert_eq!(u8::from(SLEEP_CFG.set_startup(Startup::Warm)), 0b101); - /// ``` - pub const fn set_startup(mut self, startup: Startup) -> SleepCfg { - if startup as u8 == 1 { - self.0 |= 1 << 2 - } else { - self.0 &= !(1 << 2) - } - self - } - - /// Set the RTC wakeup enable. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::SleepCfg; - /// - /// const SLEEP_CFG: SleepCfg = SleepCfg::new().set_rtc_wakeup_en(false); - /// # assert_eq!(u8::from(SLEEP_CFG), 0b100); - /// # assert_eq!(u8::from(SLEEP_CFG.set_rtc_wakeup_en(true)), 0b101); - /// ``` - #[must_use = "set_rtc_wakeup_en returns a modified SleepCfg"] - pub const fn set_rtc_wakeup_en(mut self, en: bool) -> SleepCfg { - if en { - self.0 |= 0b1 - } else { - self.0 &= !0b1 - } - self - } -} - -impl From for u8 { - fn from(sc: SleepCfg) -> Self { - sc.0 - } -} - -impl Default for SleepCfg { - fn default() -> Self { - Self::new() - } -} diff --git a/embassy-stm32/src/subghz/smps.rs b/embassy-stm32/src/subghz/smps.rs deleted file mode 100644 index 81615ea7b..000000000 --- a/embassy-stm32/src/subghz/smps.rs +++ /dev/null @@ -1,45 +0,0 @@ -/// SMPS maximum drive capability. -/// -/// Argument of [`set_smps_drv`](super::SubGhz::set_smps_drv). -#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum SmpsDrv { - /// 20 mA - Milli20 = 0x0, - /// 40 mA - Milli40 = 0x1, - /// 60 mA - Milli60 = 0x2, - /// 100 mA (default) - Milli100 = 0x3, -} - -impl SmpsDrv { - /// Get the SMPS drive value as milliamps. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::SmpsDrv; - /// - /// assert_eq!(SmpsDrv::Milli20.as_milliamps(), 20); - /// assert_eq!(SmpsDrv::Milli40.as_milliamps(), 40); - /// assert_eq!(SmpsDrv::Milli60.as_milliamps(), 60); - /// assert_eq!(SmpsDrv::Milli100.as_milliamps(), 100); - /// ``` - pub const fn as_milliamps(&self) -> u8 { - match self { - SmpsDrv::Milli20 => 20, - SmpsDrv::Milli40 => 40, - SmpsDrv::Milli60 => 60, - SmpsDrv::Milli100 => 100, - } - } -} - -impl Default for SmpsDrv { - fn default() -> Self { - SmpsDrv::Milli100 - } -} diff --git a/embassy-stm32/src/subghz/standby_clk.rs b/embassy-stm32/src/subghz/standby_clk.rs deleted file mode 100644 index c130bbee4..000000000 --- a/embassy-stm32/src/subghz/standby_clk.rs +++ /dev/null @@ -1,20 +0,0 @@ -/// Clock in standby mode. -/// -/// Used by [`set_standby`]. -/// -/// [`set_standby`]: super::SubGhz::set_standby -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum StandbyClk { - /// RC 13 MHz used in standby mode. - Rc = 0b0, - /// HSE32 used in standby mode. - Hse = 0b1, -} - -impl From for u8 { - fn from(sc: StandbyClk) -> Self { - sc as u8 - } -} diff --git a/embassy-stm32/src/subghz/stats.rs b/embassy-stm32/src/subghz/stats.rs deleted file mode 100644 index 41b7a200f..000000000 --- a/embassy-stm32/src/subghz/stats.rs +++ /dev/null @@ -1,184 +0,0 @@ -use super::Status; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct LoRaStats; - -impl LoRaStats { - pub const fn new() -> Self { - Self {} - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct FskStats; - -impl FskStats { - pub const fn new() -> Self { - Self {} - } -} - -/// Packet statistics. -/// -/// Returned by [`fsk_stats`] and [`lora_stats`]. -/// -/// [`fsk_stats`]: super::SubGhz::fsk_stats -/// [`lora_stats`]: super::SubGhz::lora_stats -#[derive(Eq, PartialEq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Stats { - status: Status, - pkt_rx: u16, - pkt_crc: u16, - pkt_len_or_hdr_err: u16, - ty: ModType, -} - -impl Stats { - const fn from_buf(buf: [u8; 7], ty: ModType) -> Stats { - Stats { - status: Status::from_raw(buf[0]), - pkt_rx: u16::from_be_bytes([buf[1], buf[2]]), - pkt_crc: u16::from_be_bytes([buf[3], buf[4]]), - pkt_len_or_hdr_err: u16::from_be_bytes([buf[5], buf[6]]), - ty, - } - } - - /// Get the radio status returned with the packet statistics. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CmdStatus, FskStats, Stats, StatusMode}; - /// - /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0]; - /// let stats: Stats = Stats::from_raw_fsk(example_data_from_radio); - /// assert_eq!(stats.status().mode(), Ok(StatusMode::Rx)); - /// assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable)); - /// ``` - pub const fn status(&self) -> Status { - self.status - } - - /// Number of packets received. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskStats, Stats}; - /// - /// let example_data_from_radio: [u8; 7] = [0x54, 0, 3, 0, 0, 0, 0]; - /// let stats: Stats = Stats::from_raw_fsk(example_data_from_radio); - /// assert_eq!(stats.pkt_rx(), 3); - /// ``` - pub const fn pkt_rx(&self) -> u16 { - self.pkt_rx - } - - /// Number of packets received with a payload CRC error - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{LoRaStats, Stats}; - /// - /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 1, 0, 0]; - /// let stats: Stats = Stats::from_raw_lora(example_data_from_radio); - /// assert_eq!(stats.pkt_crc(), 1); - /// ``` - pub const fn pkt_crc(&self) -> u16 { - self.pkt_crc - } -} - -impl Stats { - /// Create a new FSK packet statistics structure from a raw buffer. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskStats, Stats}; - /// - /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0]; - /// let stats: Stats = Stats::from_raw_fsk(example_data_from_radio); - /// ``` - pub const fn from_raw_fsk(buf: [u8; 7]) -> Stats { - Self::from_buf(buf, FskStats::new()) - } - - /// Number of packets received with a payload length error. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{FskStats, Stats}; - /// - /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1]; - /// let stats: Stats = Stats::from_raw_fsk(example_data_from_radio); - /// assert_eq!(stats.pkt_len_err(), 1); - /// ``` - pub const fn pkt_len_err(&self) -> u16 { - self.pkt_len_or_hdr_err - } -} - -impl Stats { - /// Create a new LoRa packet statistics structure from a raw buffer. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{LoRaStats, Stats}; - /// - /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 0]; - /// let stats: Stats = Stats::from_raw_lora(example_data_from_radio); - /// ``` - pub const fn from_raw_lora(buf: [u8; 7]) -> Stats { - Self::from_buf(buf, LoRaStats::new()) - } - - /// Number of packets received with a header CRC error. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{LoRaStats, Stats}; - /// - /// let example_data_from_radio: [u8; 7] = [0x54, 0, 0, 0, 0, 0, 1]; - /// let stats: Stats = Stats::from_raw_lora(example_data_from_radio); - /// assert_eq!(stats.pkt_hdr_err(), 1); - /// ``` - pub const fn pkt_hdr_err(&self) -> u16 { - self.pkt_len_or_hdr_err - } -} - -impl core::fmt::Debug for Stats { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Stats") - .field("status", &self.status()) - .field("pkt_rx", &self.pkt_rx()) - .field("pkt_crc", &self.pkt_crc()) - .field("pkt_len_err", &self.pkt_len_err()) - .finish() - } -} - -#[cfg(test)] -mod test { - use super::super::{CmdStatus, LoRaStats, Stats, StatusMode}; - - #[test] - fn mixed() { - let example_data_from_radio: [u8; 7] = [0x54, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]; - let stats: Stats = Stats::from_raw_lora(example_data_from_radio); - assert_eq!(stats.status().mode(), Ok(StatusMode::Rx)); - assert_eq!(stats.status().cmd(), Ok(CmdStatus::Avaliable)); - assert_eq!(stats.pkt_rx(), 0x0102); - assert_eq!(stats.pkt_crc(), 0x0304); - assert_eq!(stats.pkt_hdr_err(), 0x0506); - } -} diff --git a/embassy-stm32/src/subghz/status.rs b/embassy-stm32/src/subghz/status.rs deleted file mode 100644 index b84034f68..000000000 --- a/embassy-stm32/src/subghz/status.rs +++ /dev/null @@ -1,197 +0,0 @@ -/// sub-GHz radio operating mode. -/// -/// See `Get_Status` under section 5.8.5 "Communication status information commands" -/// in the reference manual. -/// -/// This is returned by [`Status::mode`]. -#[repr(u8)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum StatusMode { - /// Standby mode with RC 13MHz. - StandbyRc = 0x2, - /// Standby mode with HSE32. - StandbyHse = 0x3, - /// Frequency Synthesis mode. - Fs = 0x4, - /// Receive mode. - Rx = 0x5, - /// Transmit mode. - Tx = 0x6, -} - -impl StatusMode { - /// Create a new `StatusMode` from bits. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::StatusMode; - /// - /// assert_eq!(StatusMode::from_raw(0x2), Ok(StatusMode::StandbyRc)); - /// assert_eq!(StatusMode::from_raw(0x3), Ok(StatusMode::StandbyHse)); - /// assert_eq!(StatusMode::from_raw(0x4), Ok(StatusMode::Fs)); - /// assert_eq!(StatusMode::from_raw(0x5), Ok(StatusMode::Rx)); - /// assert_eq!(StatusMode::from_raw(0x6), Ok(StatusMode::Tx)); - /// // Other values are reserved - /// assert_eq!(StatusMode::from_raw(0), Err(0)); - /// ``` - pub const fn from_raw(bits: u8) -> Result { - match bits { - 0x2 => Ok(StatusMode::StandbyRc), - 0x3 => Ok(StatusMode::StandbyHse), - 0x4 => Ok(StatusMode::Fs), - 0x5 => Ok(StatusMode::Rx), - 0x6 => Ok(StatusMode::Tx), - _ => Err(bits), - } - } -} - -/// Command status. -/// -/// See `Get_Status` under section 5.8.5 "Communication status information commands" -/// in the reference manual. -/// -/// This is returned by [`Status::cmd`]. -#[repr(u8)] -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum CmdStatus { - /// Data available to host. - /// - /// Packet received successfully and data can be retrieved. - Avaliable = 0x2, - /// Command time out. - /// - /// Command took too long to complete triggering a sub-GHz radio watchdog - /// timeout. - Timeout = 0x3, - /// Command processing error. - /// - /// Invalid opcode or incorrect number of parameters. - ProcessingError = 0x4, - /// Command execution failure. - /// - /// Command successfully received but cannot be executed at this time, - /// requested operating mode cannot be entered or requested data cannot be - /// sent. - ExecutionFailure = 0x5, - /// Transmit command completed. - /// - /// Current packet transmission completed. - Complete = 0x6, -} - -impl CmdStatus { - /// Create a new `CmdStatus` from bits. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::CmdStatus; - /// - /// assert_eq!(CmdStatus::from_raw(0x2), Ok(CmdStatus::Avaliable)); - /// assert_eq!(CmdStatus::from_raw(0x3), Ok(CmdStatus::Timeout)); - /// assert_eq!(CmdStatus::from_raw(0x4), Ok(CmdStatus::ProcessingError)); - /// assert_eq!(CmdStatus::from_raw(0x5), Ok(CmdStatus::ExecutionFailure)); - /// assert_eq!(CmdStatus::from_raw(0x6), Ok(CmdStatus::Complete)); - /// // Other values are reserved - /// assert_eq!(CmdStatus::from_raw(0), Err(0)); - /// ``` - pub const fn from_raw(bits: u8) -> Result { - match bits { - 0x2 => Ok(CmdStatus::Avaliable), - 0x3 => Ok(CmdStatus::Timeout), - 0x4 => Ok(CmdStatus::ProcessingError), - 0x5 => Ok(CmdStatus::ExecutionFailure), - 0x6 => Ok(CmdStatus::Complete), - _ => Err(bits), - } - } -} - -/// Radio status. -/// -/// This is returned by [`status`]. -/// -/// [`status`]: super::SubGhz::status -#[derive(PartialEq, Eq, Clone, Copy)] -pub struct Status(u8); - -impl From for Status { - fn from(x: u8) -> Self { - Status(x) - } -} -impl From for u8 { - fn from(x: Status) -> Self { - x.0 - } -} - -impl Status { - /// Create a new `Status` from a raw `u8` value. - /// - /// This is the same as `Status::from(u8)`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CmdStatus, Status, StatusMode}; - /// - /// const STATUS: Status = Status::from_raw(0x54_u8); - /// assert_eq!(STATUS.mode(), Ok(StatusMode::Rx)); - /// assert_eq!(STATUS.cmd(), Ok(CmdStatus::Avaliable)); - /// ``` - pub const fn from_raw(value: u8) -> Status { - Status(value) - } - - /// sub-GHz radio operating mode. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{Status, StatusMode}; - /// - /// let status: Status = 0xACu8.into(); - /// assert_eq!(status.mode(), Ok(StatusMode::StandbyRc)); - /// ``` - pub const fn mode(&self) -> Result { - StatusMode::from_raw((self.0 >> 4) & 0b111) - } - - /// Command status. - /// - /// This method frequently returns reserved values such as `Err(1)`. - /// ST support has confirmed that this is normal and should be ignored. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{CmdStatus, Status}; - /// - /// let status: Status = 0xACu8.into(); - /// assert_eq!(status.cmd(), Ok(CmdStatus::Complete)); - /// ``` - pub const fn cmd(&self) -> Result { - CmdStatus::from_raw((self.0 >> 1) & 0b111) - } -} - -impl core::fmt::Debug for Status { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Status") - .field("mode", &self.mode()) - .field("cmd", &self.cmd()) - .finish() - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for Status { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "Status {{ mode: {}, cmd: {} }}", self.mode(), self.cmd()) - } -} diff --git a/embassy-stm32/src/subghz/tcxo_mode.rs b/embassy-stm32/src/subghz/tcxo_mode.rs deleted file mode 100644 index 698dee0a6..000000000 --- a/embassy-stm32/src/subghz/tcxo_mode.rs +++ /dev/null @@ -1,170 +0,0 @@ -use super::Timeout; - -/// TCXO trim. -/// -/// **Note:** To use VDDTCXO, the VDDRF supply must be at -/// least + 200 mV higher than the selected `TcxoTrim` voltage level. -/// -/// Used by [`TcxoMode`]. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum TcxoTrim { - /// 1.6V - Volts1pt6 = 0x0, - /// 1.7V - Volts1pt7 = 0x1, - /// 1.8V - Volts1pt8 = 0x2, - /// 2.2V - Volts2pt2 = 0x3, - /// 2.4V - Volts2pt4 = 0x4, - /// 2.7V - Volts2pt7 = 0x5, - /// 3.0V - Volts3pt0 = 0x6, - /// 3.3V - Volts3pt3 = 0x7, -} - -impl core::fmt::Display for TcxoTrim { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - TcxoTrim::Volts1pt6 => write!(f, "1.6V"), - TcxoTrim::Volts1pt7 => write!(f, "1.7V"), - TcxoTrim::Volts1pt8 => write!(f, "1.8V"), - TcxoTrim::Volts2pt2 => write!(f, "2.2V"), - TcxoTrim::Volts2pt4 => write!(f, "2.4V"), - TcxoTrim::Volts2pt7 => write!(f, "2.7V"), - TcxoTrim::Volts3pt0 => write!(f, "3.0V"), - TcxoTrim::Volts3pt3 => write!(f, "3.3V"), - } - } -} - -impl TcxoTrim { - /// Get the value of the TXCO trim in millivolts. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::TcxoTrim; - /// - /// assert_eq!(TcxoTrim::Volts1pt6.as_millivolts(), 1600); - /// assert_eq!(TcxoTrim::Volts1pt7.as_millivolts(), 1700); - /// assert_eq!(TcxoTrim::Volts1pt8.as_millivolts(), 1800); - /// assert_eq!(TcxoTrim::Volts2pt2.as_millivolts(), 2200); - /// assert_eq!(TcxoTrim::Volts2pt4.as_millivolts(), 2400); - /// assert_eq!(TcxoTrim::Volts2pt7.as_millivolts(), 2700); - /// assert_eq!(TcxoTrim::Volts3pt0.as_millivolts(), 3000); - /// assert_eq!(TcxoTrim::Volts3pt3.as_millivolts(), 3300); - /// ``` - pub const fn as_millivolts(&self) -> u16 { - match self { - TcxoTrim::Volts1pt6 => 1600, - TcxoTrim::Volts1pt7 => 1700, - TcxoTrim::Volts1pt8 => 1800, - TcxoTrim::Volts2pt2 => 2200, - TcxoTrim::Volts2pt4 => 2400, - TcxoTrim::Volts2pt7 => 2700, - TcxoTrim::Volts3pt0 => 3000, - TcxoTrim::Volts3pt3 => 3300, - } - } -} - -/// TCXO trim and HSE32 ready timeout. -/// -/// Argument of [`set_tcxo_mode`]. -/// -/// [`set_tcxo_mode`]: super::SubGhz::set_tcxo_mode -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TcxoMode { - buf: [u8; 5], -} - -impl TcxoMode { - /// Create a new `TcxoMode` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::TcxoMode; - /// - /// const TCXO_MODE: TcxoMode = TcxoMode::new(); - /// ``` - pub const fn new() -> TcxoMode { - TcxoMode { - buf: [super::OpCode::SetTcxoMode as u8, 0x00, 0x00, 0x00, 0x00], - } - } - - /// Set the TCXO trim. - /// - /// **Note:** To use VDDTCXO, the VDDRF supply must be - /// at least + 200 mV higher than the selected `TcxoTrim` voltage level. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim}; - /// - /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_txco_trim(TcxoTrim::Volts1pt6); - /// # assert_eq!(TCXO_MODE.as_slice()[1], 0x00); - /// ``` - #[must_use = "set_txco_trim returns a modified TcxoMode"] - pub const fn set_txco_trim(mut self, tcxo_trim: TcxoTrim) -> TcxoMode { - self.buf[1] = tcxo_trim as u8; - self - } - - /// Set the ready timeout duration. - /// - /// # Example - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::{TcxoMode, Timeout}; - /// - /// // 15.625 ms timeout - /// const TIMEOUT: Timeout = Timeout::from_duration_sat(Duration::from_millis(15_625)); - /// const TCXO_MODE: TcxoMode = TcxoMode::new().set_timeout(TIMEOUT); - /// # assert_eq!(TCXO_MODE.as_slice()[2], 0x0F); - /// # assert_eq!(TCXO_MODE.as_slice()[3], 0x42); - /// # assert_eq!(TCXO_MODE.as_slice()[4], 0x40); - /// ``` - #[must_use = "set_timeout returns a modified TcxoMode"] - pub const fn set_timeout(mut self, timeout: Timeout) -> TcxoMode { - let timeout_bits: u32 = timeout.into_bits(); - self.buf[2] = ((timeout_bits >> 16) & 0xFF) as u8; - self.buf[3] = ((timeout_bits >> 8) & 0xFF) as u8; - self.buf[4] = (timeout_bits & 0xFF) as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{TcxoMode, TcxoTrim, Timeout}; - /// - /// const TCXO_MODE: TcxoMode = TcxoMode::new() - /// .set_txco_trim(TcxoTrim::Volts1pt7) - /// .set_timeout(Timeout::from_raw(0x123456)); - /// assert_eq!(TCXO_MODE.as_slice(), &[0x97, 0x1, 0x12, 0x34, 0x56]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for TcxoMode { - fn default() -> Self { - Self::new() - } -} diff --git a/embassy-stm32/src/subghz/timeout.rs b/embassy-stm32/src/subghz/timeout.rs deleted file mode 100644 index 0ae49dd90..000000000 --- a/embassy-stm32/src/subghz/timeout.rs +++ /dev/null @@ -1,492 +0,0 @@ -use core::time::Duration; - -use super::ValueError; - -const fn abs_diff(a: u64, b: u64) -> u64 { - if a > b { - a - b - } else { - b - a - } -} - -/// Timeout argument. -/// -/// This is used by: -/// * [`set_rx`] -/// * [`set_tx`] -/// * [`TcxoMode`] -/// -/// Each timeout has 3 bytes, with a resolution of 15.625µs per bit, giving a -/// range of 0s to 262.143984375s. -/// -/// [`set_rx`]: super::SubGhz::set_rx -/// [`set_tx`]: super::SubGhz::set_tx -/// [`TcxoMode`]: super::TcxoMode -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Timeout { - bits: u32, -} - -impl Timeout { - const BITS_PER_MILLI: u32 = 64; // 1e-3 / 15.625e-6 - const BITS_PER_SEC: u32 = 64_000; // 1 / 15.625e-6 - - /// Disable the timeout (0s timeout). - /// - /// # Example - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// const TIMEOUT: Timeout = Timeout::DISABLED; - /// assert_eq!(TIMEOUT.as_duration(), Duration::from_secs(0)); - /// ``` - pub const DISABLED: Timeout = Timeout { bits: 0x0 }; - - /// Minimum timeout, 15.625µs. - /// - /// # Example - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// const TIMEOUT: Timeout = Timeout::MIN; - /// assert_eq!(TIMEOUT.into_bits(), 1); - /// ``` - pub const MIN: Timeout = Timeout { bits: 1 }; - - /// Maximum timeout, 262.143984375s. - /// - /// # Example - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// const TIMEOUT: Timeout = Timeout::MAX; - /// assert_eq!(TIMEOUT.as_duration(), Duration::from_nanos(262_143_984_375)); - /// ``` - pub const MAX: Timeout = Timeout { bits: 0x00FF_FFFF }; - - /// Timeout resolution in nanoseconds, 15.625µs. - pub const RESOLUTION_NANOS: u16 = 15_625; - - /// Timeout resolution, 15.625µs. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!( - /// Timeout::RESOLUTION.as_nanos(), - /// Timeout::RESOLUTION_NANOS as u128 - /// ); - /// ``` - pub const RESOLUTION: Duration = Duration::from_nanos(Self::RESOLUTION_NANOS as u64); - - /// Create a new timeout from a [`Duration`]. - /// - /// This will return the nearest timeout value possible, or a - /// [`ValueError`] if the value is out of bounds. - /// - /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout - /// construction. - /// This is not _that_ useful right now, it is simply future proofing for a - /// time when `Result::unwrap` is available for `const fn`. - /// - /// # Example - /// - /// Value within bounds: - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::{Timeout, ValueError}; - /// - /// const MIN: Duration = Timeout::RESOLUTION; - /// assert_eq!(Timeout::from_duration(MIN).unwrap(), Timeout::MIN); - /// ``` - /// - /// Value too low: - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::{Timeout, ValueError}; - /// - /// const LOWER_LIMIT_NANOS: u128 = 7813; - /// const TOO_LOW_NANOS: u128 = LOWER_LIMIT_NANOS - 1; - /// const TOO_LOW_DURATION: Duration = Duration::from_nanos(TOO_LOW_NANOS as u64); - /// assert_eq!( - /// Timeout::from_duration(TOO_LOW_DURATION), - /// Err(ValueError::too_low(TOO_LOW_NANOS, LOWER_LIMIT_NANOS)) - /// ); - /// ``` - /// - /// Value too high: - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::{Timeout, ValueError}; - /// - /// const UPPER_LIMIT_NANOS: u128 = Timeout::MAX.as_nanos() as u128 + 7812; - /// const TOO_HIGH_NANOS: u128 = UPPER_LIMIT_NANOS + 1; - /// const TOO_HIGH_DURATION: Duration = Duration::from_nanos(TOO_HIGH_NANOS as u64); - /// assert_eq!( - /// Timeout::from_duration(TOO_HIGH_DURATION), - /// Err(ValueError::too_high(TOO_HIGH_NANOS, UPPER_LIMIT_NANOS)) - /// ); - /// ``` - pub const fn from_duration(duration: Duration) -> Result> { - // at the time of development many methods in - // `core::Duration` were not `const fn`, which leads to the hacks - // you see here. - let nanos: u128 = duration.as_nanos(); - const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128 + (Timeout::RESOLUTION_NANOS as u128) / 2; - const LOWER_LIMIT: u128 = (((Timeout::RESOLUTION_NANOS as u128) + 1) / 2) as u128; - - if nanos > UPPER_LIMIT { - Err(ValueError::too_high(nanos, UPPER_LIMIT)) - } else if nanos < LOWER_LIMIT { - Err(ValueError::too_low(nanos, LOWER_LIMIT)) - } else { - // safe to truncate here because of previous bounds check. - let duration_nanos: u64 = nanos as u64; - - let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64); - let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64); - - let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32); - let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32); - - let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos); - let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos); - - if error_ceil < error_floor { - Ok(timeout_ceil) - } else { - Ok(timeout_floor) - } - } - } - - /// Create a new timeout from a [`Duration`]. - /// - /// This will return the nearest timeout value possible, saturating at the - /// limits. - /// - /// This is an expensive function to call outside of `const` contexts. - /// Use [`from_millis_sat`](Self::from_millis_sat) for runtime timeout - /// construction. - /// - /// # Example - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// const DURATION_MAX_NS: u64 = 262_143_984_376; - /// - /// assert_eq!( - /// Timeout::from_duration_sat(Duration::from_millis(0)), - /// Timeout::MIN - /// ); - /// assert_eq!( - /// Timeout::from_duration_sat(Duration::from_nanos(DURATION_MAX_NS)), - /// Timeout::MAX - /// ); - /// assert_eq!( - /// Timeout::from_duration_sat(Timeout::RESOLUTION).into_bits(), - /// 1 - /// ); - /// ``` - pub const fn from_duration_sat(duration: Duration) -> Timeout { - // at the time of development many methods in - // `core::Duration` were not `const fn`, which leads to the hacks - // you see here. - let nanos: u128 = duration.as_nanos(); - const UPPER_LIMIT: u128 = Timeout::MAX.as_nanos() as u128; - - if nanos > UPPER_LIMIT { - Timeout::MAX - } else if nanos < (Timeout::RESOLUTION_NANOS as u128) { - Timeout::from_raw(1) - } else { - // safe to truncate here because of previous bounds check. - let duration_nanos: u64 = duration.as_nanos() as u64; - - let div_floor: u64 = duration_nanos / (Self::RESOLUTION_NANOS as u64); - let div_ceil: u64 = 1 + (duration_nanos - 1) / (Self::RESOLUTION_NANOS as u64); - - let timeout_ceil: Timeout = Timeout::from_raw(div_ceil as u32); - let timeout_floor: Timeout = Timeout::from_raw(div_floor as u32); - - let error_ceil: u64 = abs_diff(timeout_ceil.as_nanos(), duration_nanos); - let error_floor: u64 = abs_diff(timeout_floor.as_nanos(), duration_nanos); - - if error_ceil < error_floor { - timeout_ceil - } else { - timeout_floor - } - } - } - - /// Create a new timeout from a milliseconds value. - /// - /// This will round towards zero and saturate at the limits. - /// - /// This is the preferred method to call when you need to generate a - /// timeout value at runtime. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::from_millis_sat(0), Timeout::MIN); - /// assert_eq!(Timeout::from_millis_sat(262_144), Timeout::MAX); - /// assert_eq!(Timeout::from_millis_sat(1).into_bits(), 64); - /// ``` - pub const fn from_millis_sat(millis: u32) -> Timeout { - if millis == 0 { - Timeout::MIN - } else if millis >= 262_144 { - Timeout::MAX - } else { - Timeout::from_raw(millis * Self::BITS_PER_MILLI) - } - } - - /// Create a timeout from raw bits, where each bit has the resolution of - /// [`Timeout::RESOLUTION`]. - /// - /// **Note:** Only the first 24 bits of the `u32` are used, the `bits` - /// argument will be masked. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::from_raw(u32::MAX), Timeout::MAX); - /// assert_eq!(Timeout::from_raw(0x00_FF_FF_FF), Timeout::MAX); - /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION); - /// assert_eq!(Timeout::from_raw(0), Timeout::DISABLED); - /// ``` - pub const fn from_raw(bits: u32) -> Timeout { - Timeout { - bits: bits & 0x00FF_FFFF, - } - } - - /// Get the timeout as nanoseconds. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::MAX.as_nanos(), 262_143_984_375); - /// assert_eq!(Timeout::DISABLED.as_nanos(), 0); - /// assert_eq!(Timeout::from_raw(1).as_nanos(), 15_625); - /// assert_eq!(Timeout::from_raw(64_000).as_nanos(), 1_000_000_000); - /// ``` - pub const fn as_nanos(&self) -> u64 { - (self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64) - } - - /// Get the timeout as microseconds, rounding towards zero. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::MAX.as_micros(), 262_143_984); - /// assert_eq!(Timeout::DISABLED.as_micros(), 0); - /// assert_eq!(Timeout::from_raw(1).as_micros(), 15); - /// assert_eq!(Timeout::from_raw(64_000).as_micros(), 1_000_000); - /// ``` - pub const fn as_micros(&self) -> u32 { - (self.as_nanos() / 1_000) as u32 - } - - /// Get the timeout as milliseconds, rounding towards zero. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::MAX.as_millis(), 262_143); - /// assert_eq!(Timeout::DISABLED.as_millis(), 0); - /// assert_eq!(Timeout::from_raw(1).as_millis(), 0); - /// assert_eq!(Timeout::from_raw(64_000).as_millis(), 1_000); - /// ``` - pub const fn as_millis(&self) -> u32 { - self.into_bits() / Self::BITS_PER_MILLI - } - - /// Get the timeout as seconds, rounding towards zero. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::MAX.as_secs(), 262); - /// assert_eq!(Timeout::DISABLED.as_secs(), 0); - /// assert_eq!(Timeout::from_raw(1).as_secs(), 0); - /// assert_eq!(Timeout::from_raw(64_000).as_secs(), 1); - /// ``` - pub const fn as_secs(&self) -> u16 { - (self.into_bits() / Self::BITS_PER_SEC) as u16 - } - - /// Get the timeout as a [`Duration`]. - /// - /// # Example - /// - /// ``` - /// use core::time::Duration; - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!( - /// Timeout::MAX.as_duration(), - /// Duration::from_nanos(262_143_984_375) - /// ); - /// assert_eq!(Timeout::DISABLED.as_duration(), Duration::from_nanos(0)); - /// assert_eq!(Timeout::from_raw(1).as_duration(), Timeout::RESOLUTION); - /// ``` - pub const fn as_duration(&self) -> Duration { - Duration::from_nanos((self.bits as u64) * (Timeout::RESOLUTION_NANOS as u64)) - } - - /// Get the bit value for the timeout. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::from_raw(u32::MAX).into_bits(), 0x00FF_FFFF); - /// assert_eq!(Timeout::from_raw(1).into_bits(), 1); - /// ``` - pub const fn into_bits(self) -> u32 { - self.bits - } - - /// Get the byte value for the timeout. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!(Timeout::from_raw(u32::MAX).as_bytes(), [0xFF, 0xFF, 0xFF]); - /// assert_eq!(Timeout::from_raw(1).as_bytes(), [0, 0, 1]); - /// ``` - pub const fn as_bytes(self) -> [u8; 3] { - [ - ((self.bits >> 16) & 0xFF) as u8, - ((self.bits >> 8) & 0xFF) as u8, - (self.bits & 0xFF) as u8, - ] - } - - /// Saturating timeout addition. Computes `self + rhs`, saturating at the - /// numeric bounds instead of overflowing. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::Timeout; - /// - /// assert_eq!( - /// Timeout::from_raw(0xFF_FF_F0).saturating_add(Timeout::from_raw(0xFF)), - /// Timeout::from_raw(0xFF_FF_FF) - /// ); - /// assert_eq!( - /// Timeout::from_raw(100).saturating_add(Timeout::from_raw(23)), - /// Timeout::from_raw(123) - /// ); - /// ``` - #[must_use = "saturating_add returns a new Timeout"] - pub const fn saturating_add(self, rhs: Self) -> Self { - // TODO: use core::cmp::min when it is const - let bits: u32 = self.bits.saturating_add(rhs.bits); - if bits > Self::MAX.bits { - Self::MAX - } else { - Self { bits } - } - } -} - -impl From for Duration { - fn from(to: Timeout) -> Self { - to.as_duration() - } -} - -impl From for [u8; 3] { - fn from(to: Timeout) -> Self { - to.as_bytes() - } -} - -#[cfg(feature = "time")] -impl From for embassy_time::Duration { - fn from(to: Timeout) -> Self { - embassy_time::Duration::from_micros(to.as_micros().into()) - } -} - -#[cfg(test)] -mod tests { - use core::time::Duration; - - use super::{Timeout, ValueError}; - - #[test] - fn saturate() { - assert_eq!(Timeout::from_duration_sat(Duration::from_secs(u64::MAX)), Timeout::MAX); - } - - #[test] - fn rounding() { - const NANO1: Duration = Duration::from_nanos(1); - let res_sub_1_ns: Duration = Timeout::RESOLUTION - NANO1; - let res_add_1_ns: Duration = Timeout::RESOLUTION + NANO1; - assert_eq!(Timeout::from_duration_sat(res_sub_1_ns).into_bits(), 1); - assert_eq!(Timeout::from_duration_sat(res_add_1_ns).into_bits(), 1); - } - - #[test] - fn lower_limit() { - let low: Duration = (Timeout::RESOLUTION + Duration::from_nanos(1)) / 2; - assert_eq!(Timeout::from_duration(low), Ok(Timeout::from_raw(1))); - - let too_low: Duration = low - Duration::from_nanos(1); - assert_eq!( - Timeout::from_duration(too_low), - Err(ValueError::too_low(too_low.as_nanos(), low.as_nanos())) - ); - } - - #[test] - fn upper_limit() { - let high: Duration = Timeout::MAX.as_duration() + Timeout::RESOLUTION / 2; - assert_eq!(Timeout::from_duration(high), Ok(Timeout::from_raw(0xFFFFFF))); - - let too_high: Duration = high + Duration::from_nanos(1); - assert_eq!( - Timeout::from_duration(too_high), - Err(ValueError::too_high(too_high.as_nanos(), high.as_nanos())) - ); - } -} diff --git a/embassy-stm32/src/subghz/tx_params.rs b/embassy-stm32/src/subghz/tx_params.rs deleted file mode 100644 index 03bdb1ea8..000000000 --- a/embassy-stm32/src/subghz/tx_params.rs +++ /dev/null @@ -1,192 +0,0 @@ -/// Power amplifier ramp time for FSK, MSK, and LoRa modulation. -/// -/// Argument of [`set_ramp_time`][`super::TxParams::set_ramp_time`]. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u8)] -pub enum RampTime { - /// 10µs - Micros10 = 0x00, - /// 20µs - Micros20 = 0x01, - /// 40µs - Micros40 = 0x02, - /// 80µs - Micros80 = 0x03, - /// 200µs - Micros200 = 0x04, - /// 800µs - Micros800 = 0x05, - /// 1.7ms - Micros1700 = 0x06, - /// 3.4ms - Micros3400 = 0x07, -} - -impl From for u8 { - fn from(rt: RampTime) -> Self { - rt as u8 - } -} - -impl From for core::time::Duration { - fn from(rt: RampTime) -> Self { - match rt { - RampTime::Micros10 => core::time::Duration::from_micros(10), - RampTime::Micros20 => core::time::Duration::from_micros(20), - RampTime::Micros40 => core::time::Duration::from_micros(40), - RampTime::Micros80 => core::time::Duration::from_micros(80), - RampTime::Micros200 => core::time::Duration::from_micros(200), - RampTime::Micros800 => core::time::Duration::from_micros(800), - RampTime::Micros1700 => core::time::Duration::from_micros(1700), - RampTime::Micros3400 => core::time::Duration::from_micros(3400), - } - } -} - -#[cfg(feature = "time")] -impl From for embassy_time::Duration { - fn from(rt: RampTime) -> Self { - match rt { - RampTime::Micros10 => embassy_time::Duration::from_micros(10), - RampTime::Micros20 => embassy_time::Duration::from_micros(20), - RampTime::Micros40 => embassy_time::Duration::from_micros(40), - RampTime::Micros80 => embassy_time::Duration::from_micros(80), - RampTime::Micros200 => embassy_time::Duration::from_micros(200), - RampTime::Micros800 => embassy_time::Duration::from_micros(800), - RampTime::Micros1700 => embassy_time::Duration::from_micros(1700), - RampTime::Micros3400 => embassy_time::Duration::from_micros(3400), - } - } -} -/// Transmit parameters, output power and power amplifier ramp up time. -/// -/// Argument of [`set_tx_params`][`super::SubGhz::set_tx_params`]. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TxParams { - buf: [u8; 3], -} - -impl TxParams { - /// Optimal power setting for +15dBm output power with the low-power PA. - /// - /// This must be used with [`PaConfig::LP_15`](super::PaConfig::LP_15). - pub const LP_15: TxParams = TxParams::new().set_power(0x0E); - - /// Optimal power setting for +14dBm output power with the low-power PA. - /// - /// This must be used with [`PaConfig::LP_14`](super::PaConfig::LP_14). - pub const LP_14: TxParams = TxParams::new().set_power(0x0E); - - /// Optimal power setting for +10dBm output power with the low-power PA. - /// - /// This must be used with [`PaConfig::LP_10`](super::PaConfig::LP_10). - pub const LP_10: TxParams = TxParams::new().set_power(0x0D); - - /// Optimal power setting for the high-power PA. - /// - /// This must be used with one of: - /// - /// * [`PaConfig::HP_22`](super::PaConfig::HP_22) - /// * [`PaConfig::HP_20`](super::PaConfig::HP_20) - /// * [`PaConfig::HP_17`](super::PaConfig::HP_17) - /// * [`PaConfig::HP_14`](super::PaConfig::HP_14) - pub const HP: TxParams = TxParams::new().set_power(0x16); - - /// Create a new `TxParams` struct. - /// - /// This is the same as `default`, but in a `const` function. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::TxParams; - /// - /// const TX_PARAMS: TxParams = TxParams::new(); - /// assert_eq!(TX_PARAMS, TxParams::default()); - /// ``` - pub const fn new() -> TxParams { - TxParams { - buf: [super::OpCode::SetTxParams as u8, 0x00, 0x00], - } - } - - /// Set the output power. - /// - /// For low power selected in [`set_pa_config`]: - /// - /// * 0x0E: +14 dB - /// * ... - /// * 0x00: 0 dB - /// * ... - /// * 0xEF: -17 dB - /// * Others: reserved - /// - /// For high power selected in [`set_pa_config`]: - /// - /// * 0x16: +22 dB - /// * ... - /// * 0x00: 0 dB - /// * ... - /// * 0xF7: -9 dB - /// * Others: reserved - /// - /// # Example - /// - /// Set the output power to 0 dB. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{RampTime, TxParams}; - /// - /// const TX_PARAMS: TxParams = TxParams::new().set_power(0x00); - /// # assert_eq!(TX_PARAMS.as_slice()[1], 0x00); - /// ``` - /// - /// [`set_pa_config`]: super::SubGhz::set_pa_config - #[must_use = "set_power returns a modified TxParams"] - pub const fn set_power(mut self, power: u8) -> TxParams { - self.buf[1] = power; - self - } - - /// Set the Power amplifier ramp time for FSK, MSK, and LoRa modulation. - /// - /// # Example - /// - /// Set the ramp time to 200 microseconds. - /// - /// ``` - /// use stm32wlxx_hal::subghz::{RampTime, TxParams}; - /// - /// const TX_PARAMS: TxParams = TxParams::new().set_ramp_time(RampTime::Micros200); - /// # assert_eq!(TX_PARAMS.as_slice()[2], 0x04); - /// ``` - #[must_use = "set_ramp_time returns a modified TxParams"] - pub const fn set_ramp_time(mut self, rt: RampTime) -> TxParams { - self.buf[2] = rt as u8; - self - } - - /// Extracts a slice containing the packet. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::{RampTime, TxParams}; - /// - /// const TX_PARAMS: TxParams = TxParams::new() - /// .set_ramp_time(RampTime::Micros80) - /// .set_power(0x0E); - /// assert_eq!(TX_PARAMS.as_slice(), &[0x8E, 0x0E, 0x03]); - /// ``` - pub const fn as_slice(&self) -> &[u8] { - &self.buf - } -} - -impl Default for TxParams { - fn default() -> Self { - Self::new() - } -} diff --git a/embassy-stm32/src/subghz/value_error.rs b/embassy-stm32/src/subghz/value_error.rs deleted file mode 100644 index 6a0b489a8..000000000 --- a/embassy-stm32/src/subghz/value_error.rs +++ /dev/null @@ -1,129 +0,0 @@ -/// Error for a value that is out-of-bounds. -/// -/// Used by [`Timeout::from_duration`]. -/// -/// [`Timeout::from_duration`]: super::Timeout::from_duration -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ValueError { - value: T, - limit: T, - over: bool, -} - -impl ValueError { - /// Create a new `ValueError` for a value that exceeded an upper bound. - /// - /// Unfortunately panic is not available in `const fn`, so there are no - /// guarantees on the value being greater than the limit. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::ValueError; - /// - /// const ERROR: ValueError = ValueError::too_high(101u8, 100u8); - /// assert!(ERROR.over()); - /// assert!(!ERROR.under()); - /// ``` - pub const fn too_high(value: T, limit: T) -> ValueError { - ValueError { - value, - limit, - over: true, - } - } - - /// Create a new `ValueError` for a value that exceeded a lower bound. - /// - /// Unfortunately panic is not available in `const fn`, so there are no - /// guarantees on the value being less than the limit. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::ValueError; - /// - /// const ERROR: ValueError = ValueError::too_low(200u8, 201u8); - /// assert!(ERROR.under()); - /// assert!(!ERROR.over()); - /// ``` - pub const fn too_low(value: T, limit: T) -> ValueError { - ValueError { - value, - limit, - over: false, - } - } - - /// Get the value that caused the error. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::ValueError; - /// - /// const ERROR: ValueError = ValueError::too_high(101u8, 100u8); - /// assert_eq!(ERROR.value(), &101u8); - /// ``` - pub const fn value(&self) -> &T { - &self.value - } - - /// Get the limit for the value. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::ValueError; - /// - /// const ERROR: ValueError = ValueError::too_high(101u8, 100u8); - /// assert_eq!(ERROR.limit(), &100u8); - /// ``` - pub const fn limit(&self) -> &T { - &self.limit - } - - /// Returns `true` if the value was over the limit. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::ValueError; - /// - /// const ERROR: ValueError = ValueError::too_high(101u8, 100u8); - /// assert!(ERROR.over()); - /// assert!(!ERROR.under()); - /// ``` - pub const fn over(&self) -> bool { - self.over - } - - /// Returns `true` if the value was under the limit. - /// - /// # Example - /// - /// ``` - /// use stm32wlxx_hal::subghz::ValueError; - /// - /// const ERROR: ValueError = ValueError::too_low(200u8, 201u8); - /// assert!(ERROR.under()); - /// assert!(!ERROR.over()); - /// ``` - pub const fn under(&self) -> bool { - !self.over - } -} - -impl core::fmt::Display for ValueError -where - T: core::fmt::Display, -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - if self.over { - write!(f, "Value is too high {} > {}", self.value, self.limit) - } else { - write!(f, "Value is too low {} < {}", self.value, self.limit) - } - } -} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 59f30a9be..77be46ffd 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt", "external-lora-phy"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index ca022e254..e9d2127c5 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -14,7 +14,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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 = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt", "external-lora-phy"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } From 91cddd50f6a2deb3ae88007dfae50972ab7852d1 Mon Sep 17 00:00:00 2001 From: OueslatiGhaith Date: Thu, 27 Apr 2023 18:26:19 +0100 Subject: [PATCH 0963/1575] reversed changes in Cargo.toml --- embassy-stm32/Cargo.toml | 2729 +++++++++++++++++++------------------- 1 file changed, 1348 insertions(+), 1381 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8015a9354..f27cf6a51 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -8,15 +8,7 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" -features = [ - "nightly", - "defmt", - "unstable-pac", - "unstable-traits", - "exti", - "time-driver-any", - "time", -] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, @@ -43,20 +35,16 @@ embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = [ - "prio-bits-4", -] } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } -embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} +embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } -embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver", optional = true } +embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } -embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ - "unproven", -] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true } -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } -embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true } +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} embedded-storage = "0.3.0" @@ -64,9 +52,7 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -futures = { version = "0.3.17", default-features = false, features = [ - "async-await", -] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand_core = "0.6.3" sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } @@ -80,7 +66,7 @@ stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } -chrono = { version = "^0.4", default-features = false, optional = true } +chrono = { version = "^0.4", default-features = false, optional = true} [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -88,23 +74,11 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "6", default-features = false, features = [ - "metadata", -] } +stm32-metapac = { version = "6", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] -defmt = [ - "dep:defmt", - "bxcan/unstable-defmt", - "embassy-sync/defmt", - "embassy-executor/defmt", - "embassy-embedded-hal/defmt", - "embassy-hal-common/defmt", - "embedded-io?/defmt", - "embassy-usb-driver?/defmt", - "embassy-net-driver/defmt", -] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] exti = [] @@ -123,14 +97,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = [ - "embassy-executor/nightly", - "embedded-hal-1", - "embedded-hal-async", - "dep:embedded-io", - "dep:embassy-usb-driver", - "embassy-embedded-hal/nightly", -] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. @@ -143,1337 +110,1337 @@ unstable-pac = [] unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # Chip-selection features -stm32c011d6 = ["stm32-metapac/stm32c011d6"] -stm32c011f4 = ["stm32-metapac/stm32c011f4"] -stm32c011f6 = ["stm32-metapac/stm32c011f6"] -stm32c011j4 = ["stm32-metapac/stm32c011j4"] -stm32c011j6 = ["stm32-metapac/stm32c011j6"] -stm32c031c4 = ["stm32-metapac/stm32c031c4"] -stm32c031c6 = ["stm32-metapac/stm32c031c6"] -stm32c031f4 = ["stm32-metapac/stm32c031f4"] -stm32c031f6 = ["stm32-metapac/stm32c031f6"] -stm32c031g4 = ["stm32-metapac/stm32c031g4"] -stm32c031g6 = ["stm32-metapac/stm32c031g6"] -stm32c031k4 = ["stm32-metapac/stm32c031k4"] -stm32c031k6 = ["stm32-metapac/stm32c031k6"] -stm32f030c6 = ["stm32-metapac/stm32f030c6"] -stm32f030c8 = ["stm32-metapac/stm32f030c8"] -stm32f030cc = ["stm32-metapac/stm32f030cc"] -stm32f030f4 = ["stm32-metapac/stm32f030f4"] -stm32f030k6 = ["stm32-metapac/stm32f030k6"] -stm32f030r8 = ["stm32-metapac/stm32f030r8"] -stm32f030rc = ["stm32-metapac/stm32f030rc"] -stm32f031c4 = ["stm32-metapac/stm32f031c4"] -stm32f031c6 = ["stm32-metapac/stm32f031c6"] -stm32f031e6 = ["stm32-metapac/stm32f031e6"] -stm32f031f4 = ["stm32-metapac/stm32f031f4"] -stm32f031f6 = ["stm32-metapac/stm32f031f6"] -stm32f031g4 = ["stm32-metapac/stm32f031g4"] -stm32f031g6 = ["stm32-metapac/stm32f031g6"] -stm32f031k4 = ["stm32-metapac/stm32f031k4"] -stm32f031k6 = ["stm32-metapac/stm32f031k6"] -stm32f038c6 = ["stm32-metapac/stm32f038c6"] -stm32f038e6 = ["stm32-metapac/stm32f038e6"] -stm32f038f6 = ["stm32-metapac/stm32f038f6"] -stm32f038g6 = ["stm32-metapac/stm32f038g6"] -stm32f038k6 = ["stm32-metapac/stm32f038k6"] -stm32f042c4 = ["stm32-metapac/stm32f042c4"] -stm32f042c6 = ["stm32-metapac/stm32f042c6"] -stm32f042f4 = ["stm32-metapac/stm32f042f4"] -stm32f042f6 = ["stm32-metapac/stm32f042f6"] -stm32f042g4 = ["stm32-metapac/stm32f042g4"] -stm32f042g6 = ["stm32-metapac/stm32f042g6"] -stm32f042k4 = ["stm32-metapac/stm32f042k4"] -stm32f042k6 = ["stm32-metapac/stm32f042k6"] -stm32f042t6 = ["stm32-metapac/stm32f042t6"] -stm32f048c6 = ["stm32-metapac/stm32f048c6"] -stm32f048g6 = ["stm32-metapac/stm32f048g6"] -stm32f048t6 = ["stm32-metapac/stm32f048t6"] -stm32f051c4 = ["stm32-metapac/stm32f051c4"] -stm32f051c6 = ["stm32-metapac/stm32f051c6"] -stm32f051c8 = ["stm32-metapac/stm32f051c8"] -stm32f051k4 = ["stm32-metapac/stm32f051k4"] -stm32f051k6 = ["stm32-metapac/stm32f051k6"] -stm32f051k8 = ["stm32-metapac/stm32f051k8"] -stm32f051r4 = ["stm32-metapac/stm32f051r4"] -stm32f051r6 = ["stm32-metapac/stm32f051r6"] -stm32f051r8 = ["stm32-metapac/stm32f051r8"] -stm32f051t8 = ["stm32-metapac/stm32f051t8"] -stm32f058c8 = ["stm32-metapac/stm32f058c8"] -stm32f058r8 = ["stm32-metapac/stm32f058r8"] -stm32f058t8 = ["stm32-metapac/stm32f058t8"] -stm32f070c6 = ["stm32-metapac/stm32f070c6"] -stm32f070cb = ["stm32-metapac/stm32f070cb"] -stm32f070f6 = ["stm32-metapac/stm32f070f6"] -stm32f070rb = ["stm32-metapac/stm32f070rb"] -stm32f071c8 = ["stm32-metapac/stm32f071c8"] -stm32f071cb = ["stm32-metapac/stm32f071cb"] -stm32f071rb = ["stm32-metapac/stm32f071rb"] -stm32f071v8 = ["stm32-metapac/stm32f071v8"] -stm32f071vb = ["stm32-metapac/stm32f071vb"] -stm32f072c8 = ["stm32-metapac/stm32f072c8"] -stm32f072cb = ["stm32-metapac/stm32f072cb"] -stm32f072r8 = ["stm32-metapac/stm32f072r8"] -stm32f072rb = ["stm32-metapac/stm32f072rb"] -stm32f072v8 = ["stm32-metapac/stm32f072v8"] -stm32f072vb = ["stm32-metapac/stm32f072vb"] -stm32f078cb = ["stm32-metapac/stm32f078cb"] -stm32f078rb = ["stm32-metapac/stm32f078rb"] -stm32f078vb = ["stm32-metapac/stm32f078vb"] -stm32f091cb = ["stm32-metapac/stm32f091cb"] -stm32f091cc = ["stm32-metapac/stm32f091cc"] -stm32f091rb = ["stm32-metapac/stm32f091rb"] -stm32f091rc = ["stm32-metapac/stm32f091rc"] -stm32f091vb = ["stm32-metapac/stm32f091vb"] -stm32f091vc = ["stm32-metapac/stm32f091vc"] -stm32f098cc = ["stm32-metapac/stm32f098cc"] -stm32f098rc = ["stm32-metapac/stm32f098rc"] -stm32f098vc = ["stm32-metapac/stm32f098vc"] -stm32f100c4 = ["stm32-metapac/stm32f100c4"] -stm32f100c6 = ["stm32-metapac/stm32f100c6"] -stm32f100c8 = ["stm32-metapac/stm32f100c8"] -stm32f100cb = ["stm32-metapac/stm32f100cb"] -stm32f100r4 = ["stm32-metapac/stm32f100r4"] -stm32f100r6 = ["stm32-metapac/stm32f100r6"] -stm32f100r8 = ["stm32-metapac/stm32f100r8"] -stm32f100rb = ["stm32-metapac/stm32f100rb"] -stm32f100rc = ["stm32-metapac/stm32f100rc"] -stm32f100rd = ["stm32-metapac/stm32f100rd"] -stm32f100re = ["stm32-metapac/stm32f100re"] -stm32f100v8 = ["stm32-metapac/stm32f100v8"] -stm32f100vb = ["stm32-metapac/stm32f100vb"] -stm32f100vc = ["stm32-metapac/stm32f100vc"] -stm32f100vd = ["stm32-metapac/stm32f100vd"] -stm32f100ve = ["stm32-metapac/stm32f100ve"] -stm32f100zc = ["stm32-metapac/stm32f100zc"] -stm32f100zd = ["stm32-metapac/stm32f100zd"] -stm32f100ze = ["stm32-metapac/stm32f100ze"] -stm32f101c4 = ["stm32-metapac/stm32f101c4"] -stm32f101c6 = ["stm32-metapac/stm32f101c6"] -stm32f101c8 = ["stm32-metapac/stm32f101c8"] -stm32f101cb = ["stm32-metapac/stm32f101cb"] -stm32f101r4 = ["stm32-metapac/stm32f101r4"] -stm32f101r6 = ["stm32-metapac/stm32f101r6"] -stm32f101r8 = ["stm32-metapac/stm32f101r8"] -stm32f101rb = ["stm32-metapac/stm32f101rb"] -stm32f101rc = ["stm32-metapac/stm32f101rc"] -stm32f101rd = ["stm32-metapac/stm32f101rd"] -stm32f101re = ["stm32-metapac/stm32f101re"] -stm32f101rf = ["stm32-metapac/stm32f101rf"] -stm32f101rg = ["stm32-metapac/stm32f101rg"] -stm32f101t4 = ["stm32-metapac/stm32f101t4"] -stm32f101t6 = ["stm32-metapac/stm32f101t6"] -stm32f101t8 = ["stm32-metapac/stm32f101t8"] -stm32f101tb = ["stm32-metapac/stm32f101tb"] -stm32f101v8 = ["stm32-metapac/stm32f101v8"] -stm32f101vb = ["stm32-metapac/stm32f101vb"] -stm32f101vc = ["stm32-metapac/stm32f101vc"] -stm32f101vd = ["stm32-metapac/stm32f101vd"] -stm32f101ve = ["stm32-metapac/stm32f101ve"] -stm32f101vf = ["stm32-metapac/stm32f101vf"] -stm32f101vg = ["stm32-metapac/stm32f101vg"] -stm32f101zc = ["stm32-metapac/stm32f101zc"] -stm32f101zd = ["stm32-metapac/stm32f101zd"] -stm32f101ze = ["stm32-metapac/stm32f101ze"] -stm32f101zf = ["stm32-metapac/stm32f101zf"] -stm32f101zg = ["stm32-metapac/stm32f101zg"] -stm32f102c4 = ["stm32-metapac/stm32f102c4"] -stm32f102c6 = ["stm32-metapac/stm32f102c6"] -stm32f102c8 = ["stm32-metapac/stm32f102c8"] -stm32f102cb = ["stm32-metapac/stm32f102cb"] -stm32f102r4 = ["stm32-metapac/stm32f102r4"] -stm32f102r6 = ["stm32-metapac/stm32f102r6"] -stm32f102r8 = ["stm32-metapac/stm32f102r8"] -stm32f102rb = ["stm32-metapac/stm32f102rb"] -stm32f103c4 = ["stm32-metapac/stm32f103c4"] -stm32f103c6 = ["stm32-metapac/stm32f103c6"] -stm32f103c8 = ["stm32-metapac/stm32f103c8"] -stm32f103cb = ["stm32-metapac/stm32f103cb"] -stm32f103r4 = ["stm32-metapac/stm32f103r4"] -stm32f103r6 = ["stm32-metapac/stm32f103r6"] -stm32f103r8 = ["stm32-metapac/stm32f103r8"] -stm32f103rb = ["stm32-metapac/stm32f103rb"] -stm32f103rc = ["stm32-metapac/stm32f103rc"] -stm32f103rd = ["stm32-metapac/stm32f103rd"] -stm32f103re = ["stm32-metapac/stm32f103re"] -stm32f103rf = ["stm32-metapac/stm32f103rf"] -stm32f103rg = ["stm32-metapac/stm32f103rg"] -stm32f103t4 = ["stm32-metapac/stm32f103t4"] -stm32f103t6 = ["stm32-metapac/stm32f103t6"] -stm32f103t8 = ["stm32-metapac/stm32f103t8"] -stm32f103tb = ["stm32-metapac/stm32f103tb"] -stm32f103v8 = ["stm32-metapac/stm32f103v8"] -stm32f103vb = ["stm32-metapac/stm32f103vb"] -stm32f103vc = ["stm32-metapac/stm32f103vc"] -stm32f103vd = ["stm32-metapac/stm32f103vd"] -stm32f103ve = ["stm32-metapac/stm32f103ve"] -stm32f103vf = ["stm32-metapac/stm32f103vf"] -stm32f103vg = ["stm32-metapac/stm32f103vg"] -stm32f103zc = ["stm32-metapac/stm32f103zc"] -stm32f103zd = ["stm32-metapac/stm32f103zd"] -stm32f103ze = ["stm32-metapac/stm32f103ze"] -stm32f103zf = ["stm32-metapac/stm32f103zf"] -stm32f103zg = ["stm32-metapac/stm32f103zg"] -stm32f105r8 = ["stm32-metapac/stm32f105r8"] -stm32f105rb = ["stm32-metapac/stm32f105rb"] -stm32f105rc = ["stm32-metapac/stm32f105rc"] -stm32f105v8 = ["stm32-metapac/stm32f105v8"] -stm32f105vb = ["stm32-metapac/stm32f105vb"] -stm32f105vc = ["stm32-metapac/stm32f105vc"] -stm32f107rb = ["stm32-metapac/stm32f107rb"] -stm32f107rc = ["stm32-metapac/stm32f107rc"] -stm32f107vb = ["stm32-metapac/stm32f107vb"] -stm32f107vc = ["stm32-metapac/stm32f107vc"] -stm32f205rb = ["stm32-metapac/stm32f205rb"] -stm32f205rc = ["stm32-metapac/stm32f205rc"] -stm32f205re = ["stm32-metapac/stm32f205re"] -stm32f205rf = ["stm32-metapac/stm32f205rf"] -stm32f205rg = ["stm32-metapac/stm32f205rg"] -stm32f205vb = ["stm32-metapac/stm32f205vb"] -stm32f205vc = ["stm32-metapac/stm32f205vc"] -stm32f205ve = ["stm32-metapac/stm32f205ve"] -stm32f205vf = ["stm32-metapac/stm32f205vf"] -stm32f205vg = ["stm32-metapac/stm32f205vg"] -stm32f205zc = ["stm32-metapac/stm32f205zc"] -stm32f205ze = ["stm32-metapac/stm32f205ze"] -stm32f205zf = ["stm32-metapac/stm32f205zf"] -stm32f205zg = ["stm32-metapac/stm32f205zg"] -stm32f207ic = ["stm32-metapac/stm32f207ic"] -stm32f207ie = ["stm32-metapac/stm32f207ie"] -stm32f207if = ["stm32-metapac/stm32f207if"] -stm32f207ig = ["stm32-metapac/stm32f207ig"] -stm32f207vc = ["stm32-metapac/stm32f207vc"] -stm32f207ve = ["stm32-metapac/stm32f207ve"] -stm32f207vf = ["stm32-metapac/stm32f207vf"] -stm32f207vg = ["stm32-metapac/stm32f207vg"] -stm32f207zc = ["stm32-metapac/stm32f207zc"] -stm32f207ze = ["stm32-metapac/stm32f207ze"] -stm32f207zf = ["stm32-metapac/stm32f207zf"] -stm32f207zg = ["stm32-metapac/stm32f207zg"] -stm32f215re = ["stm32-metapac/stm32f215re"] -stm32f215rg = ["stm32-metapac/stm32f215rg"] -stm32f215ve = ["stm32-metapac/stm32f215ve"] -stm32f215vg = ["stm32-metapac/stm32f215vg"] -stm32f215ze = ["stm32-metapac/stm32f215ze"] -stm32f215zg = ["stm32-metapac/stm32f215zg"] -stm32f217ie = ["stm32-metapac/stm32f217ie"] -stm32f217ig = ["stm32-metapac/stm32f217ig"] -stm32f217ve = ["stm32-metapac/stm32f217ve"] -stm32f217vg = ["stm32-metapac/stm32f217vg"] -stm32f217ze = ["stm32-metapac/stm32f217ze"] -stm32f217zg = ["stm32-metapac/stm32f217zg"] -stm32f301c6 = ["stm32-metapac/stm32f301c6"] -stm32f301c8 = ["stm32-metapac/stm32f301c8"] -stm32f301k6 = ["stm32-metapac/stm32f301k6"] -stm32f301k8 = ["stm32-metapac/stm32f301k8"] -stm32f301r6 = ["stm32-metapac/stm32f301r6"] -stm32f301r8 = ["stm32-metapac/stm32f301r8"] -stm32f302c6 = ["stm32-metapac/stm32f302c6"] -stm32f302c8 = ["stm32-metapac/stm32f302c8"] -stm32f302cb = ["stm32-metapac/stm32f302cb"] -stm32f302cc = ["stm32-metapac/stm32f302cc"] -stm32f302k6 = ["stm32-metapac/stm32f302k6"] -stm32f302k8 = ["stm32-metapac/stm32f302k8"] -stm32f302r6 = ["stm32-metapac/stm32f302r6"] -stm32f302r8 = ["stm32-metapac/stm32f302r8"] -stm32f302rb = ["stm32-metapac/stm32f302rb"] -stm32f302rc = ["stm32-metapac/stm32f302rc"] -stm32f302rd = ["stm32-metapac/stm32f302rd"] -stm32f302re = ["stm32-metapac/stm32f302re"] -stm32f302vb = ["stm32-metapac/stm32f302vb"] -stm32f302vc = ["stm32-metapac/stm32f302vc"] -stm32f302vd = ["stm32-metapac/stm32f302vd"] -stm32f302ve = ["stm32-metapac/stm32f302ve"] -stm32f302zd = ["stm32-metapac/stm32f302zd"] -stm32f302ze = ["stm32-metapac/stm32f302ze"] -stm32f303c6 = ["stm32-metapac/stm32f303c6"] -stm32f303c8 = ["stm32-metapac/stm32f303c8"] -stm32f303cb = ["stm32-metapac/stm32f303cb"] -stm32f303cc = ["stm32-metapac/stm32f303cc"] -stm32f303k6 = ["stm32-metapac/stm32f303k6"] -stm32f303k8 = ["stm32-metapac/stm32f303k8"] -stm32f303r6 = ["stm32-metapac/stm32f303r6"] -stm32f303r8 = ["stm32-metapac/stm32f303r8"] -stm32f303rb = ["stm32-metapac/stm32f303rb"] -stm32f303rc = ["stm32-metapac/stm32f303rc"] -stm32f303rd = ["stm32-metapac/stm32f303rd"] -stm32f303re = ["stm32-metapac/stm32f303re"] -stm32f303vb = ["stm32-metapac/stm32f303vb"] -stm32f303vc = ["stm32-metapac/stm32f303vc"] -stm32f303vd = ["stm32-metapac/stm32f303vd"] -stm32f303ve = ["stm32-metapac/stm32f303ve"] -stm32f303zd = ["stm32-metapac/stm32f303zd"] -stm32f303ze = ["stm32-metapac/stm32f303ze"] -stm32f318c8 = ["stm32-metapac/stm32f318c8"] -stm32f318k8 = ["stm32-metapac/stm32f318k8"] -stm32f328c8 = ["stm32-metapac/stm32f328c8"] -stm32f334c4 = ["stm32-metapac/stm32f334c4"] -stm32f334c6 = ["stm32-metapac/stm32f334c6"] -stm32f334c8 = ["stm32-metapac/stm32f334c8"] -stm32f334k4 = ["stm32-metapac/stm32f334k4"] -stm32f334k6 = ["stm32-metapac/stm32f334k6"] -stm32f334k8 = ["stm32-metapac/stm32f334k8"] -stm32f334r6 = ["stm32-metapac/stm32f334r6"] -stm32f334r8 = ["stm32-metapac/stm32f334r8"] -stm32f358cc = ["stm32-metapac/stm32f358cc"] -stm32f358rc = ["stm32-metapac/stm32f358rc"] -stm32f358vc = ["stm32-metapac/stm32f358vc"] -stm32f373c8 = ["stm32-metapac/stm32f373c8"] -stm32f373cb = ["stm32-metapac/stm32f373cb"] -stm32f373cc = ["stm32-metapac/stm32f373cc"] -stm32f373r8 = ["stm32-metapac/stm32f373r8"] -stm32f373rb = ["stm32-metapac/stm32f373rb"] -stm32f373rc = ["stm32-metapac/stm32f373rc"] -stm32f373v8 = ["stm32-metapac/stm32f373v8"] -stm32f373vb = ["stm32-metapac/stm32f373vb"] -stm32f373vc = ["stm32-metapac/stm32f373vc"] -stm32f378cc = ["stm32-metapac/stm32f378cc"] -stm32f378rc = ["stm32-metapac/stm32f378rc"] -stm32f378vc = ["stm32-metapac/stm32f378vc"] -stm32f398ve = ["stm32-metapac/stm32f398ve"] -stm32f401cb = ["stm32-metapac/stm32f401cb"] -stm32f401cc = ["stm32-metapac/stm32f401cc"] -stm32f401cd = ["stm32-metapac/stm32f401cd"] -stm32f401ce = ["stm32-metapac/stm32f401ce"] -stm32f401rb = ["stm32-metapac/stm32f401rb"] -stm32f401rc = ["stm32-metapac/stm32f401rc"] -stm32f401rd = ["stm32-metapac/stm32f401rd"] -stm32f401re = ["stm32-metapac/stm32f401re"] -stm32f401vb = ["stm32-metapac/stm32f401vb"] -stm32f401vc = ["stm32-metapac/stm32f401vc"] -stm32f401vd = ["stm32-metapac/stm32f401vd"] -stm32f401ve = ["stm32-metapac/stm32f401ve"] -stm32f405oe = ["stm32-metapac/stm32f405oe"] -stm32f405og = ["stm32-metapac/stm32f405og"] -stm32f405rg = ["stm32-metapac/stm32f405rg"] -stm32f405vg = ["stm32-metapac/stm32f405vg"] -stm32f405zg = ["stm32-metapac/stm32f405zg"] -stm32f407ie = ["stm32-metapac/stm32f407ie"] -stm32f407ig = ["stm32-metapac/stm32f407ig"] -stm32f407ve = ["stm32-metapac/stm32f407ve"] -stm32f407vg = ["stm32-metapac/stm32f407vg"] -stm32f407ze = ["stm32-metapac/stm32f407ze"] -stm32f407zg = ["stm32-metapac/stm32f407zg"] -stm32f410c8 = ["stm32-metapac/stm32f410c8"] -stm32f410cb = ["stm32-metapac/stm32f410cb"] -stm32f410r8 = ["stm32-metapac/stm32f410r8"] -stm32f410rb = ["stm32-metapac/stm32f410rb"] -stm32f410t8 = ["stm32-metapac/stm32f410t8"] -stm32f410tb = ["stm32-metapac/stm32f410tb"] -stm32f411cc = ["stm32-metapac/stm32f411cc"] -stm32f411ce = ["stm32-metapac/stm32f411ce"] -stm32f411rc = ["stm32-metapac/stm32f411rc"] -stm32f411re = ["stm32-metapac/stm32f411re"] -stm32f411vc = ["stm32-metapac/stm32f411vc"] -stm32f411ve = ["stm32-metapac/stm32f411ve"] -stm32f412ce = ["stm32-metapac/stm32f412ce"] -stm32f412cg = ["stm32-metapac/stm32f412cg"] -stm32f412re = ["stm32-metapac/stm32f412re"] -stm32f412rg = ["stm32-metapac/stm32f412rg"] -stm32f412ve = ["stm32-metapac/stm32f412ve"] -stm32f412vg = ["stm32-metapac/stm32f412vg"] -stm32f412ze = ["stm32-metapac/stm32f412ze"] -stm32f412zg = ["stm32-metapac/stm32f412zg"] -stm32f413cg = ["stm32-metapac/stm32f413cg"] -stm32f413ch = ["stm32-metapac/stm32f413ch"] -stm32f413mg = ["stm32-metapac/stm32f413mg"] -stm32f413mh = ["stm32-metapac/stm32f413mh"] -stm32f413rg = ["stm32-metapac/stm32f413rg"] -stm32f413rh = ["stm32-metapac/stm32f413rh"] -stm32f413vg = ["stm32-metapac/stm32f413vg"] -stm32f413vh = ["stm32-metapac/stm32f413vh"] -stm32f413zg = ["stm32-metapac/stm32f413zg"] -stm32f413zh = ["stm32-metapac/stm32f413zh"] -stm32f415og = ["stm32-metapac/stm32f415og"] -stm32f415rg = ["stm32-metapac/stm32f415rg"] -stm32f415vg = ["stm32-metapac/stm32f415vg"] -stm32f415zg = ["stm32-metapac/stm32f415zg"] -stm32f417ie = ["stm32-metapac/stm32f417ie"] -stm32f417ig = ["stm32-metapac/stm32f417ig"] -stm32f417ve = ["stm32-metapac/stm32f417ve"] -stm32f417vg = ["stm32-metapac/stm32f417vg"] -stm32f417ze = ["stm32-metapac/stm32f417ze"] -stm32f417zg = ["stm32-metapac/stm32f417zg"] -stm32f423ch = ["stm32-metapac/stm32f423ch"] -stm32f423mh = ["stm32-metapac/stm32f423mh"] -stm32f423rh = ["stm32-metapac/stm32f423rh"] -stm32f423vh = ["stm32-metapac/stm32f423vh"] -stm32f423zh = ["stm32-metapac/stm32f423zh"] -stm32f427ag = ["stm32-metapac/stm32f427ag"] -stm32f427ai = ["stm32-metapac/stm32f427ai"] -stm32f427ig = ["stm32-metapac/stm32f427ig"] -stm32f427ii = ["stm32-metapac/stm32f427ii"] -stm32f427vg = ["stm32-metapac/stm32f427vg"] -stm32f427vi = ["stm32-metapac/stm32f427vi"] -stm32f427zg = ["stm32-metapac/stm32f427zg"] -stm32f427zi = ["stm32-metapac/stm32f427zi"] -stm32f429ag = ["stm32-metapac/stm32f429ag"] -stm32f429ai = ["stm32-metapac/stm32f429ai"] -stm32f429be = ["stm32-metapac/stm32f429be"] -stm32f429bg = ["stm32-metapac/stm32f429bg"] -stm32f429bi = ["stm32-metapac/stm32f429bi"] -stm32f429ie = ["stm32-metapac/stm32f429ie"] -stm32f429ig = ["stm32-metapac/stm32f429ig"] -stm32f429ii = ["stm32-metapac/stm32f429ii"] -stm32f429ne = ["stm32-metapac/stm32f429ne"] -stm32f429ng = ["stm32-metapac/stm32f429ng"] -stm32f429ni = ["stm32-metapac/stm32f429ni"] -stm32f429ve = ["stm32-metapac/stm32f429ve"] -stm32f429vg = ["stm32-metapac/stm32f429vg"] -stm32f429vi = ["stm32-metapac/stm32f429vi"] -stm32f429ze = ["stm32-metapac/stm32f429ze"] -stm32f429zg = ["stm32-metapac/stm32f429zg"] -stm32f429zi = ["stm32-metapac/stm32f429zi"] -stm32f437ai = ["stm32-metapac/stm32f437ai"] -stm32f437ig = ["stm32-metapac/stm32f437ig"] -stm32f437ii = ["stm32-metapac/stm32f437ii"] -stm32f437vg = ["stm32-metapac/stm32f437vg"] -stm32f437vi = ["stm32-metapac/stm32f437vi"] -stm32f437zg = ["stm32-metapac/stm32f437zg"] -stm32f437zi = ["stm32-metapac/stm32f437zi"] -stm32f439ai = ["stm32-metapac/stm32f439ai"] -stm32f439bg = ["stm32-metapac/stm32f439bg"] -stm32f439bi = ["stm32-metapac/stm32f439bi"] -stm32f439ig = ["stm32-metapac/stm32f439ig"] -stm32f439ii = ["stm32-metapac/stm32f439ii"] -stm32f439ng = ["stm32-metapac/stm32f439ng"] -stm32f439ni = ["stm32-metapac/stm32f439ni"] -stm32f439vg = ["stm32-metapac/stm32f439vg"] -stm32f439vi = ["stm32-metapac/stm32f439vi"] -stm32f439zg = ["stm32-metapac/stm32f439zg"] -stm32f439zi = ["stm32-metapac/stm32f439zi"] -stm32f446mc = ["stm32-metapac/stm32f446mc"] -stm32f446me = ["stm32-metapac/stm32f446me"] -stm32f446rc = ["stm32-metapac/stm32f446rc"] -stm32f446re = ["stm32-metapac/stm32f446re"] -stm32f446vc = ["stm32-metapac/stm32f446vc"] -stm32f446ve = ["stm32-metapac/stm32f446ve"] -stm32f446zc = ["stm32-metapac/stm32f446zc"] -stm32f446ze = ["stm32-metapac/stm32f446ze"] -stm32f469ae = ["stm32-metapac/stm32f469ae"] -stm32f469ag = ["stm32-metapac/stm32f469ag"] -stm32f469ai = ["stm32-metapac/stm32f469ai"] -stm32f469be = ["stm32-metapac/stm32f469be"] -stm32f469bg = ["stm32-metapac/stm32f469bg"] -stm32f469bi = ["stm32-metapac/stm32f469bi"] -stm32f469ie = ["stm32-metapac/stm32f469ie"] -stm32f469ig = ["stm32-metapac/stm32f469ig"] -stm32f469ii = ["stm32-metapac/stm32f469ii"] -stm32f469ne = ["stm32-metapac/stm32f469ne"] -stm32f469ng = ["stm32-metapac/stm32f469ng"] -stm32f469ni = ["stm32-metapac/stm32f469ni"] -stm32f469ve = ["stm32-metapac/stm32f469ve"] -stm32f469vg = ["stm32-metapac/stm32f469vg"] -stm32f469vi = ["stm32-metapac/stm32f469vi"] -stm32f469ze = ["stm32-metapac/stm32f469ze"] -stm32f469zg = ["stm32-metapac/stm32f469zg"] -stm32f469zi = ["stm32-metapac/stm32f469zi"] -stm32f479ag = ["stm32-metapac/stm32f479ag"] -stm32f479ai = ["stm32-metapac/stm32f479ai"] -stm32f479bg = ["stm32-metapac/stm32f479bg"] -stm32f479bi = ["stm32-metapac/stm32f479bi"] -stm32f479ig = ["stm32-metapac/stm32f479ig"] -stm32f479ii = ["stm32-metapac/stm32f479ii"] -stm32f479ng = ["stm32-metapac/stm32f479ng"] -stm32f479ni = ["stm32-metapac/stm32f479ni"] -stm32f479vg = ["stm32-metapac/stm32f479vg"] -stm32f479vi = ["stm32-metapac/stm32f479vi"] -stm32f479zg = ["stm32-metapac/stm32f479zg"] -stm32f479zi = ["stm32-metapac/stm32f479zi"] -stm32f722ic = ["stm32-metapac/stm32f722ic"] -stm32f722ie = ["stm32-metapac/stm32f722ie"] -stm32f722rc = ["stm32-metapac/stm32f722rc"] -stm32f722re = ["stm32-metapac/stm32f722re"] -stm32f722vc = ["stm32-metapac/stm32f722vc"] -stm32f722ve = ["stm32-metapac/stm32f722ve"] -stm32f722zc = ["stm32-metapac/stm32f722zc"] -stm32f722ze = ["stm32-metapac/stm32f722ze"] -stm32f723ic = ["stm32-metapac/stm32f723ic"] -stm32f723ie = ["stm32-metapac/stm32f723ie"] -stm32f723vc = ["stm32-metapac/stm32f723vc"] -stm32f723ve = ["stm32-metapac/stm32f723ve"] -stm32f723zc = ["stm32-metapac/stm32f723zc"] -stm32f723ze = ["stm32-metapac/stm32f723ze"] -stm32f730i8 = ["stm32-metapac/stm32f730i8"] -stm32f730r8 = ["stm32-metapac/stm32f730r8"] -stm32f730v8 = ["stm32-metapac/stm32f730v8"] -stm32f730z8 = ["stm32-metapac/stm32f730z8"] -stm32f732ie = ["stm32-metapac/stm32f732ie"] -stm32f732re = ["stm32-metapac/stm32f732re"] -stm32f732ve = ["stm32-metapac/stm32f732ve"] -stm32f732ze = ["stm32-metapac/stm32f732ze"] -stm32f733ie = ["stm32-metapac/stm32f733ie"] -stm32f733ve = ["stm32-metapac/stm32f733ve"] -stm32f733ze = ["stm32-metapac/stm32f733ze"] -stm32f745ie = ["stm32-metapac/stm32f745ie"] -stm32f745ig = ["stm32-metapac/stm32f745ig"] -stm32f745ve = ["stm32-metapac/stm32f745ve"] -stm32f745vg = ["stm32-metapac/stm32f745vg"] -stm32f745ze = ["stm32-metapac/stm32f745ze"] -stm32f745zg = ["stm32-metapac/stm32f745zg"] -stm32f746be = ["stm32-metapac/stm32f746be"] -stm32f746bg = ["stm32-metapac/stm32f746bg"] -stm32f746ie = ["stm32-metapac/stm32f746ie"] -stm32f746ig = ["stm32-metapac/stm32f746ig"] -stm32f746ne = ["stm32-metapac/stm32f746ne"] -stm32f746ng = ["stm32-metapac/stm32f746ng"] -stm32f746ve = ["stm32-metapac/stm32f746ve"] -stm32f746vg = ["stm32-metapac/stm32f746vg"] -stm32f746ze = ["stm32-metapac/stm32f746ze"] -stm32f746zg = ["stm32-metapac/stm32f746zg"] -stm32f750n8 = ["stm32-metapac/stm32f750n8"] -stm32f750v8 = ["stm32-metapac/stm32f750v8"] -stm32f750z8 = ["stm32-metapac/stm32f750z8"] -stm32f756bg = ["stm32-metapac/stm32f756bg"] -stm32f756ig = ["stm32-metapac/stm32f756ig"] -stm32f756ng = ["stm32-metapac/stm32f756ng"] -stm32f756vg = ["stm32-metapac/stm32f756vg"] -stm32f756zg = ["stm32-metapac/stm32f756zg"] -stm32f765bg = ["stm32-metapac/stm32f765bg"] -stm32f765bi = ["stm32-metapac/stm32f765bi"] -stm32f765ig = ["stm32-metapac/stm32f765ig"] -stm32f765ii = ["stm32-metapac/stm32f765ii"] -stm32f765ng = ["stm32-metapac/stm32f765ng"] -stm32f765ni = ["stm32-metapac/stm32f765ni"] -stm32f765vg = ["stm32-metapac/stm32f765vg"] -stm32f765vi = ["stm32-metapac/stm32f765vi"] -stm32f765zg = ["stm32-metapac/stm32f765zg"] -stm32f765zi = ["stm32-metapac/stm32f765zi"] -stm32f767bg = ["stm32-metapac/stm32f767bg"] -stm32f767bi = ["stm32-metapac/stm32f767bi"] -stm32f767ig = ["stm32-metapac/stm32f767ig"] -stm32f767ii = ["stm32-metapac/stm32f767ii"] -stm32f767ng = ["stm32-metapac/stm32f767ng"] -stm32f767ni = ["stm32-metapac/stm32f767ni"] -stm32f767vg = ["stm32-metapac/stm32f767vg"] -stm32f767vi = ["stm32-metapac/stm32f767vi"] -stm32f767zg = ["stm32-metapac/stm32f767zg"] -stm32f767zi = ["stm32-metapac/stm32f767zi"] -stm32f768ai = ["stm32-metapac/stm32f768ai"] -stm32f769ag = ["stm32-metapac/stm32f769ag"] -stm32f769ai = ["stm32-metapac/stm32f769ai"] -stm32f769bg = ["stm32-metapac/stm32f769bg"] -stm32f769bi = ["stm32-metapac/stm32f769bi"] -stm32f769ig = ["stm32-metapac/stm32f769ig"] -stm32f769ii = ["stm32-metapac/stm32f769ii"] -stm32f769ng = ["stm32-metapac/stm32f769ng"] -stm32f769ni = ["stm32-metapac/stm32f769ni"] -stm32f777bi = ["stm32-metapac/stm32f777bi"] -stm32f777ii = ["stm32-metapac/stm32f777ii"] -stm32f777ni = ["stm32-metapac/stm32f777ni"] -stm32f777vi = ["stm32-metapac/stm32f777vi"] -stm32f777zi = ["stm32-metapac/stm32f777zi"] -stm32f778ai = ["stm32-metapac/stm32f778ai"] -stm32f779ai = ["stm32-metapac/stm32f779ai"] -stm32f779bi = ["stm32-metapac/stm32f779bi"] -stm32f779ii = ["stm32-metapac/stm32f779ii"] -stm32f779ni = ["stm32-metapac/stm32f779ni"] -stm32g030c6 = ["stm32-metapac/stm32g030c6"] -stm32g030c8 = ["stm32-metapac/stm32g030c8"] -stm32g030f6 = ["stm32-metapac/stm32g030f6"] -stm32g030j6 = ["stm32-metapac/stm32g030j6"] -stm32g030k6 = ["stm32-metapac/stm32g030k6"] -stm32g030k8 = ["stm32-metapac/stm32g030k8"] -stm32g031c4 = ["stm32-metapac/stm32g031c4"] -stm32g031c6 = ["stm32-metapac/stm32g031c6"] -stm32g031c8 = ["stm32-metapac/stm32g031c8"] -stm32g031f4 = ["stm32-metapac/stm32g031f4"] -stm32g031f6 = ["stm32-metapac/stm32g031f6"] -stm32g031f8 = ["stm32-metapac/stm32g031f8"] -stm32g031g4 = ["stm32-metapac/stm32g031g4"] -stm32g031g6 = ["stm32-metapac/stm32g031g6"] -stm32g031g8 = ["stm32-metapac/stm32g031g8"] -stm32g031j4 = ["stm32-metapac/stm32g031j4"] -stm32g031j6 = ["stm32-metapac/stm32g031j6"] -stm32g031k4 = ["stm32-metapac/stm32g031k4"] -stm32g031k6 = ["stm32-metapac/stm32g031k6"] -stm32g031k8 = ["stm32-metapac/stm32g031k8"] -stm32g031y8 = ["stm32-metapac/stm32g031y8"] -stm32g041c6 = ["stm32-metapac/stm32g041c6"] -stm32g041c8 = ["stm32-metapac/stm32g041c8"] -stm32g041f6 = ["stm32-metapac/stm32g041f6"] -stm32g041f8 = ["stm32-metapac/stm32g041f8"] -stm32g041g6 = ["stm32-metapac/stm32g041g6"] -stm32g041g8 = ["stm32-metapac/stm32g041g8"] -stm32g041j6 = ["stm32-metapac/stm32g041j6"] -stm32g041k6 = ["stm32-metapac/stm32g041k6"] -stm32g041k8 = ["stm32-metapac/stm32g041k8"] -stm32g041y8 = ["stm32-metapac/stm32g041y8"] -stm32g050c6 = ["stm32-metapac/stm32g050c6"] -stm32g050c8 = ["stm32-metapac/stm32g050c8"] -stm32g050f6 = ["stm32-metapac/stm32g050f6"] -stm32g050k6 = ["stm32-metapac/stm32g050k6"] -stm32g050k8 = ["stm32-metapac/stm32g050k8"] -stm32g051c6 = ["stm32-metapac/stm32g051c6"] -stm32g051c8 = ["stm32-metapac/stm32g051c8"] -stm32g051f6 = ["stm32-metapac/stm32g051f6"] -stm32g051f8 = ["stm32-metapac/stm32g051f8"] -stm32g051g6 = ["stm32-metapac/stm32g051g6"] -stm32g051g8 = ["stm32-metapac/stm32g051g8"] -stm32g051k6 = ["stm32-metapac/stm32g051k6"] -stm32g051k8 = ["stm32-metapac/stm32g051k8"] -stm32g061c6 = ["stm32-metapac/stm32g061c6"] -stm32g061c8 = ["stm32-metapac/stm32g061c8"] -stm32g061f6 = ["stm32-metapac/stm32g061f6"] -stm32g061f8 = ["stm32-metapac/stm32g061f8"] -stm32g061g6 = ["stm32-metapac/stm32g061g6"] -stm32g061g8 = ["stm32-metapac/stm32g061g8"] -stm32g061k6 = ["stm32-metapac/stm32g061k6"] -stm32g061k8 = ["stm32-metapac/stm32g061k8"] -stm32g070cb = ["stm32-metapac/stm32g070cb"] -stm32g070kb = ["stm32-metapac/stm32g070kb"] -stm32g070rb = ["stm32-metapac/stm32g070rb"] -stm32g071c6 = ["stm32-metapac/stm32g071c6"] -stm32g071c8 = ["stm32-metapac/stm32g071c8"] -stm32g071cb = ["stm32-metapac/stm32g071cb"] -stm32g071eb = ["stm32-metapac/stm32g071eb"] -stm32g071g6 = ["stm32-metapac/stm32g071g6"] -stm32g071g8 = ["stm32-metapac/stm32g071g8"] -stm32g071gb = ["stm32-metapac/stm32g071gb"] -stm32g071k6 = ["stm32-metapac/stm32g071k6"] -stm32g071k8 = ["stm32-metapac/stm32g071k8"] -stm32g071kb = ["stm32-metapac/stm32g071kb"] -stm32g071r6 = ["stm32-metapac/stm32g071r6"] -stm32g071r8 = ["stm32-metapac/stm32g071r8"] -stm32g071rb = ["stm32-metapac/stm32g071rb"] -stm32g081cb = ["stm32-metapac/stm32g081cb"] -stm32g081eb = ["stm32-metapac/stm32g081eb"] -stm32g081gb = ["stm32-metapac/stm32g081gb"] -stm32g081kb = ["stm32-metapac/stm32g081kb"] -stm32g081rb = ["stm32-metapac/stm32g081rb"] -stm32g0b0ce = ["stm32-metapac/stm32g0b0ce"] -stm32g0b0ke = ["stm32-metapac/stm32g0b0ke"] -stm32g0b0re = ["stm32-metapac/stm32g0b0re"] -stm32g0b0ve = ["stm32-metapac/stm32g0b0ve"] -stm32g0b1cb = ["stm32-metapac/stm32g0b1cb"] -stm32g0b1cc = ["stm32-metapac/stm32g0b1cc"] -stm32g0b1ce = ["stm32-metapac/stm32g0b1ce"] -stm32g0b1kb = ["stm32-metapac/stm32g0b1kb"] -stm32g0b1kc = ["stm32-metapac/stm32g0b1kc"] -stm32g0b1ke = ["stm32-metapac/stm32g0b1ke"] -stm32g0b1mb = ["stm32-metapac/stm32g0b1mb"] -stm32g0b1mc = ["stm32-metapac/stm32g0b1mc"] -stm32g0b1me = ["stm32-metapac/stm32g0b1me"] -stm32g0b1ne = ["stm32-metapac/stm32g0b1ne"] -stm32g0b1rb = ["stm32-metapac/stm32g0b1rb"] -stm32g0b1rc = ["stm32-metapac/stm32g0b1rc"] -stm32g0b1re = ["stm32-metapac/stm32g0b1re"] -stm32g0b1vb = ["stm32-metapac/stm32g0b1vb"] -stm32g0b1vc = ["stm32-metapac/stm32g0b1vc"] -stm32g0b1ve = ["stm32-metapac/stm32g0b1ve"] -stm32g0c1cc = ["stm32-metapac/stm32g0c1cc"] -stm32g0c1ce = ["stm32-metapac/stm32g0c1ce"] -stm32g0c1kc = ["stm32-metapac/stm32g0c1kc"] -stm32g0c1ke = ["stm32-metapac/stm32g0c1ke"] -stm32g0c1mc = ["stm32-metapac/stm32g0c1mc"] -stm32g0c1me = ["stm32-metapac/stm32g0c1me"] -stm32g0c1ne = ["stm32-metapac/stm32g0c1ne"] -stm32g0c1rc = ["stm32-metapac/stm32g0c1rc"] -stm32g0c1re = ["stm32-metapac/stm32g0c1re"] -stm32g0c1vc = ["stm32-metapac/stm32g0c1vc"] -stm32g0c1ve = ["stm32-metapac/stm32g0c1ve"] -stm32g431c6 = ["stm32-metapac/stm32g431c6"] -stm32g431c8 = ["stm32-metapac/stm32g431c8"] -stm32g431cb = ["stm32-metapac/stm32g431cb"] -stm32g431k6 = ["stm32-metapac/stm32g431k6"] -stm32g431k8 = ["stm32-metapac/stm32g431k8"] -stm32g431kb = ["stm32-metapac/stm32g431kb"] -stm32g431m6 = ["stm32-metapac/stm32g431m6"] -stm32g431m8 = ["stm32-metapac/stm32g431m8"] -stm32g431mb = ["stm32-metapac/stm32g431mb"] -stm32g431r6 = ["stm32-metapac/stm32g431r6"] -stm32g431r8 = ["stm32-metapac/stm32g431r8"] -stm32g431rb = ["stm32-metapac/stm32g431rb"] -stm32g431v6 = ["stm32-metapac/stm32g431v6"] -stm32g431v8 = ["stm32-metapac/stm32g431v8"] -stm32g431vb = ["stm32-metapac/stm32g431vb"] -stm32g441cb = ["stm32-metapac/stm32g441cb"] -stm32g441kb = ["stm32-metapac/stm32g441kb"] -stm32g441mb = ["stm32-metapac/stm32g441mb"] -stm32g441rb = ["stm32-metapac/stm32g441rb"] -stm32g441vb = ["stm32-metapac/stm32g441vb"] -stm32g471cc = ["stm32-metapac/stm32g471cc"] -stm32g471ce = ["stm32-metapac/stm32g471ce"] -stm32g471mc = ["stm32-metapac/stm32g471mc"] -stm32g471me = ["stm32-metapac/stm32g471me"] -stm32g471qc = ["stm32-metapac/stm32g471qc"] -stm32g471qe = ["stm32-metapac/stm32g471qe"] -stm32g471rc = ["stm32-metapac/stm32g471rc"] -stm32g471re = ["stm32-metapac/stm32g471re"] -stm32g471vc = ["stm32-metapac/stm32g471vc"] -stm32g471ve = ["stm32-metapac/stm32g471ve"] -stm32g473cb = ["stm32-metapac/stm32g473cb"] -stm32g473cc = ["stm32-metapac/stm32g473cc"] -stm32g473ce = ["stm32-metapac/stm32g473ce"] -stm32g473mb = ["stm32-metapac/stm32g473mb"] -stm32g473mc = ["stm32-metapac/stm32g473mc"] -stm32g473me = ["stm32-metapac/stm32g473me"] -stm32g473pb = ["stm32-metapac/stm32g473pb"] -stm32g473pc = ["stm32-metapac/stm32g473pc"] -stm32g473pe = ["stm32-metapac/stm32g473pe"] -stm32g473qb = ["stm32-metapac/stm32g473qb"] -stm32g473qc = ["stm32-metapac/stm32g473qc"] -stm32g473qe = ["stm32-metapac/stm32g473qe"] -stm32g473rb = ["stm32-metapac/stm32g473rb"] -stm32g473rc = ["stm32-metapac/stm32g473rc"] -stm32g473re = ["stm32-metapac/stm32g473re"] -stm32g473vb = ["stm32-metapac/stm32g473vb"] -stm32g473vc = ["stm32-metapac/stm32g473vc"] -stm32g473ve = ["stm32-metapac/stm32g473ve"] -stm32g474cb = ["stm32-metapac/stm32g474cb"] -stm32g474cc = ["stm32-metapac/stm32g474cc"] -stm32g474ce = ["stm32-metapac/stm32g474ce"] -stm32g474mb = ["stm32-metapac/stm32g474mb"] -stm32g474mc = ["stm32-metapac/stm32g474mc"] -stm32g474me = ["stm32-metapac/stm32g474me"] -stm32g474pb = ["stm32-metapac/stm32g474pb"] -stm32g474pc = ["stm32-metapac/stm32g474pc"] -stm32g474pe = ["stm32-metapac/stm32g474pe"] -stm32g474qb = ["stm32-metapac/stm32g474qb"] -stm32g474qc = ["stm32-metapac/stm32g474qc"] -stm32g474qe = ["stm32-metapac/stm32g474qe"] -stm32g474rb = ["stm32-metapac/stm32g474rb"] -stm32g474rc = ["stm32-metapac/stm32g474rc"] -stm32g474re = ["stm32-metapac/stm32g474re"] -stm32g474vb = ["stm32-metapac/stm32g474vb"] -stm32g474vc = ["stm32-metapac/stm32g474vc"] -stm32g474ve = ["stm32-metapac/stm32g474ve"] -stm32g483ce = ["stm32-metapac/stm32g483ce"] -stm32g483me = ["stm32-metapac/stm32g483me"] -stm32g483pe = ["stm32-metapac/stm32g483pe"] -stm32g483qe = ["stm32-metapac/stm32g483qe"] -stm32g483re = ["stm32-metapac/stm32g483re"] -stm32g483ve = ["stm32-metapac/stm32g483ve"] -stm32g484ce = ["stm32-metapac/stm32g484ce"] -stm32g484me = ["stm32-metapac/stm32g484me"] -stm32g484pe = ["stm32-metapac/stm32g484pe"] -stm32g484qe = ["stm32-metapac/stm32g484qe"] -stm32g484re = ["stm32-metapac/stm32g484re"] -stm32g484ve = ["stm32-metapac/stm32g484ve"] -stm32g491cc = ["stm32-metapac/stm32g491cc"] -stm32g491ce = ["stm32-metapac/stm32g491ce"] -stm32g491kc = ["stm32-metapac/stm32g491kc"] -stm32g491ke = ["stm32-metapac/stm32g491ke"] -stm32g491mc = ["stm32-metapac/stm32g491mc"] -stm32g491me = ["stm32-metapac/stm32g491me"] -stm32g491rc = ["stm32-metapac/stm32g491rc"] -stm32g491re = ["stm32-metapac/stm32g491re"] -stm32g491vc = ["stm32-metapac/stm32g491vc"] -stm32g491ve = ["stm32-metapac/stm32g491ve"] -stm32g4a1ce = ["stm32-metapac/stm32g4a1ce"] -stm32g4a1ke = ["stm32-metapac/stm32g4a1ke"] -stm32g4a1me = ["stm32-metapac/stm32g4a1me"] -stm32g4a1re = ["stm32-metapac/stm32g4a1re"] -stm32g4a1ve = ["stm32-metapac/stm32g4a1ve"] -stm32h503cb = ["stm32-metapac/stm32h503cb"] -stm32h503eb = ["stm32-metapac/stm32h503eb"] -stm32h503kb = ["stm32-metapac/stm32h503kb"] -stm32h503rb = ["stm32-metapac/stm32h503rb"] -stm32h562ag = ["stm32-metapac/stm32h562ag"] -stm32h562ai = ["stm32-metapac/stm32h562ai"] -stm32h562ig = ["stm32-metapac/stm32h562ig"] -stm32h562ii = ["stm32-metapac/stm32h562ii"] -stm32h562rg = ["stm32-metapac/stm32h562rg"] -stm32h562ri = ["stm32-metapac/stm32h562ri"] -stm32h562vg = ["stm32-metapac/stm32h562vg"] -stm32h562vi = ["stm32-metapac/stm32h562vi"] -stm32h562zg = ["stm32-metapac/stm32h562zg"] -stm32h562zi = ["stm32-metapac/stm32h562zi"] -stm32h563ag = ["stm32-metapac/stm32h563ag"] -stm32h563ai = ["stm32-metapac/stm32h563ai"] -stm32h563ig = ["stm32-metapac/stm32h563ig"] -stm32h563ii = ["stm32-metapac/stm32h563ii"] -stm32h563mi = ["stm32-metapac/stm32h563mi"] -stm32h563rg = ["stm32-metapac/stm32h563rg"] -stm32h563ri = ["stm32-metapac/stm32h563ri"] -stm32h563vg = ["stm32-metapac/stm32h563vg"] -stm32h563vi = ["stm32-metapac/stm32h563vi"] -stm32h563zg = ["stm32-metapac/stm32h563zg"] -stm32h563zi = ["stm32-metapac/stm32h563zi"] -stm32h573ai = ["stm32-metapac/stm32h573ai"] -stm32h573ii = ["stm32-metapac/stm32h573ii"] -stm32h573mi = ["stm32-metapac/stm32h573mi"] -stm32h573ri = ["stm32-metapac/stm32h573ri"] -stm32h573vi = ["stm32-metapac/stm32h573vi"] -stm32h573zi = ["stm32-metapac/stm32h573zi"] -stm32h723ve = ["stm32-metapac/stm32h723ve"] -stm32h723vg = ["stm32-metapac/stm32h723vg"] -stm32h723ze = ["stm32-metapac/stm32h723ze"] -stm32h723zg = ["stm32-metapac/stm32h723zg"] -stm32h725ae = ["stm32-metapac/stm32h725ae"] -stm32h725ag = ["stm32-metapac/stm32h725ag"] -stm32h725ie = ["stm32-metapac/stm32h725ie"] -stm32h725ig = ["stm32-metapac/stm32h725ig"] -stm32h725re = ["stm32-metapac/stm32h725re"] -stm32h725rg = ["stm32-metapac/stm32h725rg"] -stm32h725ve = ["stm32-metapac/stm32h725ve"] -stm32h725vg = ["stm32-metapac/stm32h725vg"] -stm32h725ze = ["stm32-metapac/stm32h725ze"] -stm32h725zg = ["stm32-metapac/stm32h725zg"] -stm32h730ab = ["stm32-metapac/stm32h730ab"] -stm32h730ib = ["stm32-metapac/stm32h730ib"] -stm32h730vb = ["stm32-metapac/stm32h730vb"] -stm32h730zb = ["stm32-metapac/stm32h730zb"] -stm32h733vg = ["stm32-metapac/stm32h733vg"] -stm32h733zg = ["stm32-metapac/stm32h733zg"] -stm32h735ag = ["stm32-metapac/stm32h735ag"] -stm32h735ig = ["stm32-metapac/stm32h735ig"] -stm32h735rg = ["stm32-metapac/stm32h735rg"] -stm32h735vg = ["stm32-metapac/stm32h735vg"] -stm32h735zg = ["stm32-metapac/stm32h735zg"] -stm32h742ag = ["stm32-metapac/stm32h742ag"] -stm32h742ai = ["stm32-metapac/stm32h742ai"] -stm32h742bg = ["stm32-metapac/stm32h742bg"] -stm32h742bi = ["stm32-metapac/stm32h742bi"] -stm32h742ig = ["stm32-metapac/stm32h742ig"] -stm32h742ii = ["stm32-metapac/stm32h742ii"] -stm32h742vg = ["stm32-metapac/stm32h742vg"] -stm32h742vi = ["stm32-metapac/stm32h742vi"] -stm32h742xg = ["stm32-metapac/stm32h742xg"] -stm32h742xi = ["stm32-metapac/stm32h742xi"] -stm32h742zg = ["stm32-metapac/stm32h742zg"] -stm32h742zi = ["stm32-metapac/stm32h742zi"] -stm32h743ag = ["stm32-metapac/stm32h743ag"] -stm32h743ai = ["stm32-metapac/stm32h743ai"] -stm32h743bg = ["stm32-metapac/stm32h743bg"] -stm32h743bi = ["stm32-metapac/stm32h743bi"] -stm32h743ig = ["stm32-metapac/stm32h743ig"] -stm32h743ii = ["stm32-metapac/stm32h743ii"] -stm32h743vg = ["stm32-metapac/stm32h743vg"] -stm32h743vi = ["stm32-metapac/stm32h743vi"] -stm32h743xg = ["stm32-metapac/stm32h743xg"] -stm32h743xi = ["stm32-metapac/stm32h743xi"] -stm32h743zg = ["stm32-metapac/stm32h743zg"] -stm32h743zi = ["stm32-metapac/stm32h743zi"] -stm32h745bg-cm7 = ["stm32-metapac/stm32h745bg-cm7"] -stm32h745bg-cm4 = ["stm32-metapac/stm32h745bg-cm4"] -stm32h745bi-cm7 = ["stm32-metapac/stm32h745bi-cm7"] -stm32h745bi-cm4 = ["stm32-metapac/stm32h745bi-cm4"] -stm32h745ig-cm7 = ["stm32-metapac/stm32h745ig-cm7"] -stm32h745ig-cm4 = ["stm32-metapac/stm32h745ig-cm4"] -stm32h745ii-cm7 = ["stm32-metapac/stm32h745ii-cm7"] -stm32h745ii-cm4 = ["stm32-metapac/stm32h745ii-cm4"] -stm32h745xg-cm7 = ["stm32-metapac/stm32h745xg-cm7"] -stm32h745xg-cm4 = ["stm32-metapac/stm32h745xg-cm4"] -stm32h745xi-cm7 = ["stm32-metapac/stm32h745xi-cm7"] -stm32h745xi-cm4 = ["stm32-metapac/stm32h745xi-cm4"] -stm32h745zg-cm7 = ["stm32-metapac/stm32h745zg-cm7"] -stm32h745zg-cm4 = ["stm32-metapac/stm32h745zg-cm4"] -stm32h745zi-cm7 = ["stm32-metapac/stm32h745zi-cm7"] -stm32h745zi-cm4 = ["stm32-metapac/stm32h745zi-cm4"] -stm32h747ag-cm7 = ["stm32-metapac/stm32h747ag-cm7"] -stm32h747ag-cm4 = ["stm32-metapac/stm32h747ag-cm4"] -stm32h747ai-cm7 = ["stm32-metapac/stm32h747ai-cm7"] -stm32h747ai-cm4 = ["stm32-metapac/stm32h747ai-cm4"] -stm32h747bg-cm7 = ["stm32-metapac/stm32h747bg-cm7"] -stm32h747bg-cm4 = ["stm32-metapac/stm32h747bg-cm4"] -stm32h747bi-cm7 = ["stm32-metapac/stm32h747bi-cm7"] -stm32h747bi-cm4 = ["stm32-metapac/stm32h747bi-cm4"] -stm32h747ig-cm7 = ["stm32-metapac/stm32h747ig-cm7"] -stm32h747ig-cm4 = ["stm32-metapac/stm32h747ig-cm4"] -stm32h747ii-cm7 = ["stm32-metapac/stm32h747ii-cm7"] -stm32h747ii-cm4 = ["stm32-metapac/stm32h747ii-cm4"] -stm32h747xg-cm7 = ["stm32-metapac/stm32h747xg-cm7"] -stm32h747xg-cm4 = ["stm32-metapac/stm32h747xg-cm4"] -stm32h747xi-cm7 = ["stm32-metapac/stm32h747xi-cm7"] -stm32h747xi-cm4 = ["stm32-metapac/stm32h747xi-cm4"] -stm32h747zi-cm7 = ["stm32-metapac/stm32h747zi-cm7"] -stm32h747zi-cm4 = ["stm32-metapac/stm32h747zi-cm4"] -stm32h750ib = ["stm32-metapac/stm32h750ib"] -stm32h750vb = ["stm32-metapac/stm32h750vb"] -stm32h750xb = ["stm32-metapac/stm32h750xb"] -stm32h750zb = ["stm32-metapac/stm32h750zb"] -stm32h753ai = ["stm32-metapac/stm32h753ai"] -stm32h753bi = ["stm32-metapac/stm32h753bi"] -stm32h753ii = ["stm32-metapac/stm32h753ii"] -stm32h753vi = ["stm32-metapac/stm32h753vi"] -stm32h753xi = ["stm32-metapac/stm32h753xi"] -stm32h753zi = ["stm32-metapac/stm32h753zi"] -stm32h755bi-cm7 = ["stm32-metapac/stm32h755bi-cm7"] -stm32h755bi-cm4 = ["stm32-metapac/stm32h755bi-cm4"] -stm32h755ii-cm7 = ["stm32-metapac/stm32h755ii-cm7"] -stm32h755ii-cm4 = ["stm32-metapac/stm32h755ii-cm4"] -stm32h755xi-cm7 = ["stm32-metapac/stm32h755xi-cm7"] -stm32h755xi-cm4 = ["stm32-metapac/stm32h755xi-cm4"] -stm32h755zi-cm7 = ["stm32-metapac/stm32h755zi-cm7"] -stm32h755zi-cm4 = ["stm32-metapac/stm32h755zi-cm4"] -stm32h757ai-cm7 = ["stm32-metapac/stm32h757ai-cm7"] -stm32h757ai-cm4 = ["stm32-metapac/stm32h757ai-cm4"] -stm32h757bi-cm7 = ["stm32-metapac/stm32h757bi-cm7"] -stm32h757bi-cm4 = ["stm32-metapac/stm32h757bi-cm4"] -stm32h757ii-cm7 = ["stm32-metapac/stm32h757ii-cm7"] -stm32h757ii-cm4 = ["stm32-metapac/stm32h757ii-cm4"] -stm32h757xi-cm7 = ["stm32-metapac/stm32h757xi-cm7"] -stm32h757xi-cm4 = ["stm32-metapac/stm32h757xi-cm4"] -stm32h757zi-cm7 = ["stm32-metapac/stm32h757zi-cm7"] -stm32h757zi-cm4 = ["stm32-metapac/stm32h757zi-cm4"] -stm32h7a3ag = ["stm32-metapac/stm32h7a3ag"] -stm32h7a3ai = ["stm32-metapac/stm32h7a3ai"] -stm32h7a3ig = ["stm32-metapac/stm32h7a3ig"] -stm32h7a3ii = ["stm32-metapac/stm32h7a3ii"] -stm32h7a3lg = ["stm32-metapac/stm32h7a3lg"] -stm32h7a3li = ["stm32-metapac/stm32h7a3li"] -stm32h7a3ng = ["stm32-metapac/stm32h7a3ng"] -stm32h7a3ni = ["stm32-metapac/stm32h7a3ni"] -stm32h7a3qi = ["stm32-metapac/stm32h7a3qi"] -stm32h7a3rg = ["stm32-metapac/stm32h7a3rg"] -stm32h7a3ri = ["stm32-metapac/stm32h7a3ri"] -stm32h7a3vg = ["stm32-metapac/stm32h7a3vg"] -stm32h7a3vi = ["stm32-metapac/stm32h7a3vi"] -stm32h7a3zg = ["stm32-metapac/stm32h7a3zg"] -stm32h7a3zi = ["stm32-metapac/stm32h7a3zi"] -stm32h7b0ab = ["stm32-metapac/stm32h7b0ab"] -stm32h7b0ib = ["stm32-metapac/stm32h7b0ib"] -stm32h7b0rb = ["stm32-metapac/stm32h7b0rb"] -stm32h7b0vb = ["stm32-metapac/stm32h7b0vb"] -stm32h7b0zb = ["stm32-metapac/stm32h7b0zb"] -stm32h7b3ai = ["stm32-metapac/stm32h7b3ai"] -stm32h7b3ii = ["stm32-metapac/stm32h7b3ii"] -stm32h7b3li = ["stm32-metapac/stm32h7b3li"] -stm32h7b3ni = ["stm32-metapac/stm32h7b3ni"] -stm32h7b3qi = ["stm32-metapac/stm32h7b3qi"] -stm32h7b3ri = ["stm32-metapac/stm32h7b3ri"] -stm32h7b3vi = ["stm32-metapac/stm32h7b3vi"] -stm32h7b3zi = ["stm32-metapac/stm32h7b3zi"] -stm32l010c6 = ["stm32-metapac/stm32l010c6"] -stm32l010f4 = ["stm32-metapac/stm32l010f4"] -stm32l010k4 = ["stm32-metapac/stm32l010k4"] -stm32l010k8 = ["stm32-metapac/stm32l010k8"] -stm32l010r8 = ["stm32-metapac/stm32l010r8"] -stm32l010rb = ["stm32-metapac/stm32l010rb"] -stm32l011d3 = ["stm32-metapac/stm32l011d3"] -stm32l011d4 = ["stm32-metapac/stm32l011d4"] -stm32l011e3 = ["stm32-metapac/stm32l011e3"] -stm32l011e4 = ["stm32-metapac/stm32l011e4"] -stm32l011f3 = ["stm32-metapac/stm32l011f3"] -stm32l011f4 = ["stm32-metapac/stm32l011f4"] -stm32l011g3 = ["stm32-metapac/stm32l011g3"] -stm32l011g4 = ["stm32-metapac/stm32l011g4"] -stm32l011k3 = ["stm32-metapac/stm32l011k3"] -stm32l011k4 = ["stm32-metapac/stm32l011k4"] -stm32l021d4 = ["stm32-metapac/stm32l021d4"] -stm32l021f4 = ["stm32-metapac/stm32l021f4"] -stm32l021g4 = ["stm32-metapac/stm32l021g4"] -stm32l021k4 = ["stm32-metapac/stm32l021k4"] -stm32l031c4 = ["stm32-metapac/stm32l031c4"] -stm32l031c6 = ["stm32-metapac/stm32l031c6"] -stm32l031e4 = ["stm32-metapac/stm32l031e4"] -stm32l031e6 = ["stm32-metapac/stm32l031e6"] -stm32l031f4 = ["stm32-metapac/stm32l031f4"] -stm32l031f6 = ["stm32-metapac/stm32l031f6"] -stm32l031g4 = ["stm32-metapac/stm32l031g4"] -stm32l031g6 = ["stm32-metapac/stm32l031g6"] -stm32l031k4 = ["stm32-metapac/stm32l031k4"] -stm32l031k6 = ["stm32-metapac/stm32l031k6"] -stm32l041c4 = ["stm32-metapac/stm32l041c4"] -stm32l041c6 = ["stm32-metapac/stm32l041c6"] -stm32l041e6 = ["stm32-metapac/stm32l041e6"] -stm32l041f6 = ["stm32-metapac/stm32l041f6"] -stm32l041g6 = ["stm32-metapac/stm32l041g6"] -stm32l041k6 = ["stm32-metapac/stm32l041k6"] -stm32l051c6 = ["stm32-metapac/stm32l051c6"] -stm32l051c8 = ["stm32-metapac/stm32l051c8"] -stm32l051k6 = ["stm32-metapac/stm32l051k6"] -stm32l051k8 = ["stm32-metapac/stm32l051k8"] -stm32l051r6 = ["stm32-metapac/stm32l051r6"] -stm32l051r8 = ["stm32-metapac/stm32l051r8"] -stm32l051t6 = ["stm32-metapac/stm32l051t6"] -stm32l051t8 = ["stm32-metapac/stm32l051t8"] -stm32l052c6 = ["stm32-metapac/stm32l052c6"] -stm32l052c8 = ["stm32-metapac/stm32l052c8"] -stm32l052k6 = ["stm32-metapac/stm32l052k6"] -stm32l052k8 = ["stm32-metapac/stm32l052k8"] -stm32l052r6 = ["stm32-metapac/stm32l052r6"] -stm32l052r8 = ["stm32-metapac/stm32l052r8"] -stm32l052t6 = ["stm32-metapac/stm32l052t6"] -stm32l052t8 = ["stm32-metapac/stm32l052t8"] -stm32l053c6 = ["stm32-metapac/stm32l053c6"] -stm32l053c8 = ["stm32-metapac/stm32l053c8"] -stm32l053r6 = ["stm32-metapac/stm32l053r6"] -stm32l053r8 = ["stm32-metapac/stm32l053r8"] -stm32l062c8 = ["stm32-metapac/stm32l062c8"] -stm32l062k8 = ["stm32-metapac/stm32l062k8"] -stm32l063c8 = ["stm32-metapac/stm32l063c8"] -stm32l063r8 = ["stm32-metapac/stm32l063r8"] -stm32l071c8 = ["stm32-metapac/stm32l071c8"] -stm32l071cb = ["stm32-metapac/stm32l071cb"] -stm32l071cz = ["stm32-metapac/stm32l071cz"] -stm32l071k8 = ["stm32-metapac/stm32l071k8"] -stm32l071kb = ["stm32-metapac/stm32l071kb"] -stm32l071kz = ["stm32-metapac/stm32l071kz"] -stm32l071rb = ["stm32-metapac/stm32l071rb"] -stm32l071rz = ["stm32-metapac/stm32l071rz"] -stm32l071v8 = ["stm32-metapac/stm32l071v8"] -stm32l071vb = ["stm32-metapac/stm32l071vb"] -stm32l071vz = ["stm32-metapac/stm32l071vz"] -stm32l072cb = ["stm32-metapac/stm32l072cb"] -stm32l072cz = ["stm32-metapac/stm32l072cz"] -stm32l072kb = ["stm32-metapac/stm32l072kb"] -stm32l072kz = ["stm32-metapac/stm32l072kz"] -stm32l072rb = ["stm32-metapac/stm32l072rb"] -stm32l072rz = ["stm32-metapac/stm32l072rz"] -stm32l072v8 = ["stm32-metapac/stm32l072v8"] -stm32l072vb = ["stm32-metapac/stm32l072vb"] -stm32l072vz = ["stm32-metapac/stm32l072vz"] -stm32l073cb = ["stm32-metapac/stm32l073cb"] -stm32l073cz = ["stm32-metapac/stm32l073cz"] -stm32l073rb = ["stm32-metapac/stm32l073rb"] -stm32l073rz = ["stm32-metapac/stm32l073rz"] -stm32l073v8 = ["stm32-metapac/stm32l073v8"] -stm32l073vb = ["stm32-metapac/stm32l073vb"] -stm32l073vz = ["stm32-metapac/stm32l073vz"] -stm32l081cb = ["stm32-metapac/stm32l081cb"] -stm32l081cz = ["stm32-metapac/stm32l081cz"] -stm32l081kz = ["stm32-metapac/stm32l081kz"] -stm32l082cz = ["stm32-metapac/stm32l082cz"] -stm32l082kb = ["stm32-metapac/stm32l082kb"] -stm32l082kz = ["stm32-metapac/stm32l082kz"] -stm32l083cb = ["stm32-metapac/stm32l083cb"] -stm32l083cz = ["stm32-metapac/stm32l083cz"] -stm32l083rb = ["stm32-metapac/stm32l083rb"] -stm32l083rz = ["stm32-metapac/stm32l083rz"] -stm32l083v8 = ["stm32-metapac/stm32l083v8"] -stm32l083vb = ["stm32-metapac/stm32l083vb"] -stm32l083vz = ["stm32-metapac/stm32l083vz"] -stm32l100c6 = ["stm32-metapac/stm32l100c6"] -stm32l100c6-a = ["stm32-metapac/stm32l100c6-a"] -stm32l100r8 = ["stm32-metapac/stm32l100r8"] -stm32l100r8-a = ["stm32-metapac/stm32l100r8-a"] -stm32l100rb = ["stm32-metapac/stm32l100rb"] -stm32l100rb-a = ["stm32-metapac/stm32l100rb-a"] -stm32l100rc = ["stm32-metapac/stm32l100rc"] -stm32l151c6 = ["stm32-metapac/stm32l151c6"] -stm32l151c6-a = ["stm32-metapac/stm32l151c6-a"] -stm32l151c8 = ["stm32-metapac/stm32l151c8"] -stm32l151c8-a = ["stm32-metapac/stm32l151c8-a"] -stm32l151cb = ["stm32-metapac/stm32l151cb"] -stm32l151cb-a = ["stm32-metapac/stm32l151cb-a"] -stm32l151cc = ["stm32-metapac/stm32l151cc"] -stm32l151qc = ["stm32-metapac/stm32l151qc"] -stm32l151qd = ["stm32-metapac/stm32l151qd"] -stm32l151qe = ["stm32-metapac/stm32l151qe"] -stm32l151r6 = ["stm32-metapac/stm32l151r6"] -stm32l151r6-a = ["stm32-metapac/stm32l151r6-a"] -stm32l151r8 = ["stm32-metapac/stm32l151r8"] -stm32l151r8-a = ["stm32-metapac/stm32l151r8-a"] -stm32l151rb = ["stm32-metapac/stm32l151rb"] -stm32l151rb-a = ["stm32-metapac/stm32l151rb-a"] -stm32l151rc = ["stm32-metapac/stm32l151rc"] -stm32l151rc-a = ["stm32-metapac/stm32l151rc-a"] -stm32l151rd = ["stm32-metapac/stm32l151rd"] -stm32l151re = ["stm32-metapac/stm32l151re"] -stm32l151uc = ["stm32-metapac/stm32l151uc"] -stm32l151v8 = ["stm32-metapac/stm32l151v8"] -stm32l151v8-a = ["stm32-metapac/stm32l151v8-a"] -stm32l151vb = ["stm32-metapac/stm32l151vb"] -stm32l151vb-a = ["stm32-metapac/stm32l151vb-a"] -stm32l151vc = ["stm32-metapac/stm32l151vc"] -stm32l151vc-a = ["stm32-metapac/stm32l151vc-a"] -stm32l151vd = ["stm32-metapac/stm32l151vd"] -stm32l151vd-x = ["stm32-metapac/stm32l151vd-x"] -stm32l151ve = ["stm32-metapac/stm32l151ve"] -stm32l151zc = ["stm32-metapac/stm32l151zc"] -stm32l151zd = ["stm32-metapac/stm32l151zd"] -stm32l151ze = ["stm32-metapac/stm32l151ze"] -stm32l152c6 = ["stm32-metapac/stm32l152c6"] -stm32l152c6-a = ["stm32-metapac/stm32l152c6-a"] -stm32l152c8 = ["stm32-metapac/stm32l152c8"] -stm32l152c8-a = ["stm32-metapac/stm32l152c8-a"] -stm32l152cb = ["stm32-metapac/stm32l152cb"] -stm32l152cb-a = ["stm32-metapac/stm32l152cb-a"] -stm32l152cc = ["stm32-metapac/stm32l152cc"] -stm32l152qc = ["stm32-metapac/stm32l152qc"] -stm32l152qd = ["stm32-metapac/stm32l152qd"] -stm32l152qe = ["stm32-metapac/stm32l152qe"] -stm32l152r6 = ["stm32-metapac/stm32l152r6"] -stm32l152r6-a = ["stm32-metapac/stm32l152r6-a"] -stm32l152r8 = ["stm32-metapac/stm32l152r8"] -stm32l152r8-a = ["stm32-metapac/stm32l152r8-a"] -stm32l152rb = ["stm32-metapac/stm32l152rb"] -stm32l152rb-a = ["stm32-metapac/stm32l152rb-a"] -stm32l152rc = ["stm32-metapac/stm32l152rc"] -stm32l152rc-a = ["stm32-metapac/stm32l152rc-a"] -stm32l152rd = ["stm32-metapac/stm32l152rd"] -stm32l152re = ["stm32-metapac/stm32l152re"] -stm32l152uc = ["stm32-metapac/stm32l152uc"] -stm32l152v8 = ["stm32-metapac/stm32l152v8"] -stm32l152v8-a = ["stm32-metapac/stm32l152v8-a"] -stm32l152vb = ["stm32-metapac/stm32l152vb"] -stm32l152vb-a = ["stm32-metapac/stm32l152vb-a"] -stm32l152vc = ["stm32-metapac/stm32l152vc"] -stm32l152vc-a = ["stm32-metapac/stm32l152vc-a"] -stm32l152vd = ["stm32-metapac/stm32l152vd"] -stm32l152vd-x = ["stm32-metapac/stm32l152vd-x"] -stm32l152ve = ["stm32-metapac/stm32l152ve"] -stm32l152zc = ["stm32-metapac/stm32l152zc"] -stm32l152zd = ["stm32-metapac/stm32l152zd"] -stm32l152ze = ["stm32-metapac/stm32l152ze"] -stm32l162qc = ["stm32-metapac/stm32l162qc"] -stm32l162qd = ["stm32-metapac/stm32l162qd"] -stm32l162rc = ["stm32-metapac/stm32l162rc"] -stm32l162rc-a = ["stm32-metapac/stm32l162rc-a"] -stm32l162rd = ["stm32-metapac/stm32l162rd"] -stm32l162re = ["stm32-metapac/stm32l162re"] -stm32l162vc = ["stm32-metapac/stm32l162vc"] -stm32l162vc-a = ["stm32-metapac/stm32l162vc-a"] -stm32l162vd = ["stm32-metapac/stm32l162vd"] -stm32l162vd-x = ["stm32-metapac/stm32l162vd-x"] -stm32l162ve = ["stm32-metapac/stm32l162ve"] -stm32l162zc = ["stm32-metapac/stm32l162zc"] -stm32l162zd = ["stm32-metapac/stm32l162zd"] -stm32l162ze = ["stm32-metapac/stm32l162ze"] -stm32l412c8 = ["stm32-metapac/stm32l412c8"] -stm32l412cb = ["stm32-metapac/stm32l412cb"] -stm32l412k8 = ["stm32-metapac/stm32l412k8"] -stm32l412kb = ["stm32-metapac/stm32l412kb"] -stm32l412r8 = ["stm32-metapac/stm32l412r8"] -stm32l412rb = ["stm32-metapac/stm32l412rb"] -stm32l412t8 = ["stm32-metapac/stm32l412t8"] -stm32l412tb = ["stm32-metapac/stm32l412tb"] -stm32l422cb = ["stm32-metapac/stm32l422cb"] -stm32l422kb = ["stm32-metapac/stm32l422kb"] -stm32l422rb = ["stm32-metapac/stm32l422rb"] -stm32l422tb = ["stm32-metapac/stm32l422tb"] -stm32l431cb = ["stm32-metapac/stm32l431cb"] -stm32l431cc = ["stm32-metapac/stm32l431cc"] -stm32l431kb = ["stm32-metapac/stm32l431kb"] -stm32l431kc = ["stm32-metapac/stm32l431kc"] -stm32l431rb = ["stm32-metapac/stm32l431rb"] -stm32l431rc = ["stm32-metapac/stm32l431rc"] -stm32l431vc = ["stm32-metapac/stm32l431vc"] -stm32l432kb = ["stm32-metapac/stm32l432kb"] -stm32l432kc = ["stm32-metapac/stm32l432kc"] -stm32l433cb = ["stm32-metapac/stm32l433cb"] -stm32l433cc = ["stm32-metapac/stm32l433cc"] -stm32l433rb = ["stm32-metapac/stm32l433rb"] -stm32l433rc = ["stm32-metapac/stm32l433rc"] -stm32l433vc = ["stm32-metapac/stm32l433vc"] -stm32l442kc = ["stm32-metapac/stm32l442kc"] -stm32l443cc = ["stm32-metapac/stm32l443cc"] -stm32l443rc = ["stm32-metapac/stm32l443rc"] -stm32l443vc = ["stm32-metapac/stm32l443vc"] -stm32l451cc = ["stm32-metapac/stm32l451cc"] -stm32l451ce = ["stm32-metapac/stm32l451ce"] -stm32l451rc = ["stm32-metapac/stm32l451rc"] -stm32l451re = ["stm32-metapac/stm32l451re"] -stm32l451vc = ["stm32-metapac/stm32l451vc"] -stm32l451ve = ["stm32-metapac/stm32l451ve"] -stm32l452cc = ["stm32-metapac/stm32l452cc"] -stm32l452ce = ["stm32-metapac/stm32l452ce"] -stm32l452rc = ["stm32-metapac/stm32l452rc"] -stm32l452re = ["stm32-metapac/stm32l452re"] -stm32l452vc = ["stm32-metapac/stm32l452vc"] -stm32l452ve = ["stm32-metapac/stm32l452ve"] -stm32l462ce = ["stm32-metapac/stm32l462ce"] -stm32l462re = ["stm32-metapac/stm32l462re"] -stm32l462ve = ["stm32-metapac/stm32l462ve"] -stm32l471qe = ["stm32-metapac/stm32l471qe"] -stm32l471qg = ["stm32-metapac/stm32l471qg"] -stm32l471re = ["stm32-metapac/stm32l471re"] -stm32l471rg = ["stm32-metapac/stm32l471rg"] -stm32l471ve = ["stm32-metapac/stm32l471ve"] -stm32l471vg = ["stm32-metapac/stm32l471vg"] -stm32l471ze = ["stm32-metapac/stm32l471ze"] -stm32l471zg = ["stm32-metapac/stm32l471zg"] -stm32l475rc = ["stm32-metapac/stm32l475rc"] -stm32l475re = ["stm32-metapac/stm32l475re"] -stm32l475rg = ["stm32-metapac/stm32l475rg"] -stm32l475vc = ["stm32-metapac/stm32l475vc"] -stm32l475ve = ["stm32-metapac/stm32l475ve"] -stm32l475vg = ["stm32-metapac/stm32l475vg"] -stm32l476je = ["stm32-metapac/stm32l476je"] -stm32l476jg = ["stm32-metapac/stm32l476jg"] -stm32l476me = ["stm32-metapac/stm32l476me"] -stm32l476mg = ["stm32-metapac/stm32l476mg"] -stm32l476qe = ["stm32-metapac/stm32l476qe"] -stm32l476qg = ["stm32-metapac/stm32l476qg"] -stm32l476rc = ["stm32-metapac/stm32l476rc"] -stm32l476re = ["stm32-metapac/stm32l476re"] -stm32l476rg = ["stm32-metapac/stm32l476rg"] -stm32l476vc = ["stm32-metapac/stm32l476vc"] -stm32l476ve = ["stm32-metapac/stm32l476ve"] -stm32l476vg = ["stm32-metapac/stm32l476vg"] -stm32l476ze = ["stm32-metapac/stm32l476ze"] -stm32l476zg = ["stm32-metapac/stm32l476zg"] -stm32l486jg = ["stm32-metapac/stm32l486jg"] -stm32l486qg = ["stm32-metapac/stm32l486qg"] -stm32l486rg = ["stm32-metapac/stm32l486rg"] -stm32l486vg = ["stm32-metapac/stm32l486vg"] -stm32l486zg = ["stm32-metapac/stm32l486zg"] -stm32l496ae = ["stm32-metapac/stm32l496ae"] -stm32l496ag = ["stm32-metapac/stm32l496ag"] -stm32l496qe = ["stm32-metapac/stm32l496qe"] -stm32l496qg = ["stm32-metapac/stm32l496qg"] -stm32l496re = ["stm32-metapac/stm32l496re"] -stm32l496rg = ["stm32-metapac/stm32l496rg"] -stm32l496ve = ["stm32-metapac/stm32l496ve"] -stm32l496vg = ["stm32-metapac/stm32l496vg"] -stm32l496wg = ["stm32-metapac/stm32l496wg"] -stm32l496ze = ["stm32-metapac/stm32l496ze"] -stm32l496zg = ["stm32-metapac/stm32l496zg"] -stm32l4a6ag = ["stm32-metapac/stm32l4a6ag"] -stm32l4a6qg = ["stm32-metapac/stm32l4a6qg"] -stm32l4a6rg = ["stm32-metapac/stm32l4a6rg"] -stm32l4a6vg = ["stm32-metapac/stm32l4a6vg"] -stm32l4a6zg = ["stm32-metapac/stm32l4a6zg"] -stm32l4p5ae = ["stm32-metapac/stm32l4p5ae"] -stm32l4p5ag = ["stm32-metapac/stm32l4p5ag"] -stm32l4p5ce = ["stm32-metapac/stm32l4p5ce"] -stm32l4p5cg = ["stm32-metapac/stm32l4p5cg"] -stm32l4p5qe = ["stm32-metapac/stm32l4p5qe"] -stm32l4p5qg = ["stm32-metapac/stm32l4p5qg"] -stm32l4p5re = ["stm32-metapac/stm32l4p5re"] -stm32l4p5rg = ["stm32-metapac/stm32l4p5rg"] -stm32l4p5ve = ["stm32-metapac/stm32l4p5ve"] -stm32l4p5vg = ["stm32-metapac/stm32l4p5vg"] -stm32l4p5ze = ["stm32-metapac/stm32l4p5ze"] -stm32l4p5zg = ["stm32-metapac/stm32l4p5zg"] -stm32l4q5ag = ["stm32-metapac/stm32l4q5ag"] -stm32l4q5cg = ["stm32-metapac/stm32l4q5cg"] -stm32l4q5qg = ["stm32-metapac/stm32l4q5qg"] -stm32l4q5rg = ["stm32-metapac/stm32l4q5rg"] -stm32l4q5vg = ["stm32-metapac/stm32l4q5vg"] -stm32l4q5zg = ["stm32-metapac/stm32l4q5zg"] -stm32l4r5ag = ["stm32-metapac/stm32l4r5ag"] -stm32l4r5ai = ["stm32-metapac/stm32l4r5ai"] -stm32l4r5qg = ["stm32-metapac/stm32l4r5qg"] -stm32l4r5qi = ["stm32-metapac/stm32l4r5qi"] -stm32l4r5vg = ["stm32-metapac/stm32l4r5vg"] -stm32l4r5vi = ["stm32-metapac/stm32l4r5vi"] -stm32l4r5zg = ["stm32-metapac/stm32l4r5zg"] -stm32l4r5zi = ["stm32-metapac/stm32l4r5zi"] -stm32l4r7ai = ["stm32-metapac/stm32l4r7ai"] -stm32l4r7vi = ["stm32-metapac/stm32l4r7vi"] -stm32l4r7zi = ["stm32-metapac/stm32l4r7zi"] -stm32l4r9ag = ["stm32-metapac/stm32l4r9ag"] -stm32l4r9ai = ["stm32-metapac/stm32l4r9ai"] -stm32l4r9vg = ["stm32-metapac/stm32l4r9vg"] -stm32l4r9vi = ["stm32-metapac/stm32l4r9vi"] -stm32l4r9zg = ["stm32-metapac/stm32l4r9zg"] -stm32l4r9zi = ["stm32-metapac/stm32l4r9zi"] -stm32l4s5ai = ["stm32-metapac/stm32l4s5ai"] -stm32l4s5qi = ["stm32-metapac/stm32l4s5qi"] -stm32l4s5vi = ["stm32-metapac/stm32l4s5vi"] -stm32l4s5zi = ["stm32-metapac/stm32l4s5zi"] -stm32l4s7ai = ["stm32-metapac/stm32l4s7ai"] -stm32l4s7vi = ["stm32-metapac/stm32l4s7vi"] -stm32l4s7zi = ["stm32-metapac/stm32l4s7zi"] -stm32l4s9ai = ["stm32-metapac/stm32l4s9ai"] -stm32l4s9vi = ["stm32-metapac/stm32l4s9vi"] -stm32l4s9zi = ["stm32-metapac/stm32l4s9zi"] -stm32l552cc = ["stm32-metapac/stm32l552cc"] -stm32l552ce = ["stm32-metapac/stm32l552ce"] -stm32l552me = ["stm32-metapac/stm32l552me"] -stm32l552qc = ["stm32-metapac/stm32l552qc"] -stm32l552qe = ["stm32-metapac/stm32l552qe"] -stm32l552rc = ["stm32-metapac/stm32l552rc"] -stm32l552re = ["stm32-metapac/stm32l552re"] -stm32l552vc = ["stm32-metapac/stm32l552vc"] -stm32l552ve = ["stm32-metapac/stm32l552ve"] -stm32l552zc = ["stm32-metapac/stm32l552zc"] -stm32l552ze = ["stm32-metapac/stm32l552ze"] -stm32l562ce = ["stm32-metapac/stm32l562ce"] -stm32l562me = ["stm32-metapac/stm32l562me"] -stm32l562qe = ["stm32-metapac/stm32l562qe"] -stm32l562re = ["stm32-metapac/stm32l562re"] -stm32l562ve = ["stm32-metapac/stm32l562ve"] -stm32l562ze = ["stm32-metapac/stm32l562ze"] -stm32u535cb = ["stm32-metapac/stm32u535cb"] -stm32u535cc = ["stm32-metapac/stm32u535cc"] -stm32u535ce = ["stm32-metapac/stm32u535ce"] -stm32u535je = ["stm32-metapac/stm32u535je"] -stm32u535nc = ["stm32-metapac/stm32u535nc"] -stm32u535ne = ["stm32-metapac/stm32u535ne"] -stm32u535rb = ["stm32-metapac/stm32u535rb"] -stm32u535rc = ["stm32-metapac/stm32u535rc"] -stm32u535re = ["stm32-metapac/stm32u535re"] -stm32u535vc = ["stm32-metapac/stm32u535vc"] -stm32u535ve = ["stm32-metapac/stm32u535ve"] -stm32u545ce = ["stm32-metapac/stm32u545ce"] -stm32u545je = ["stm32-metapac/stm32u545je"] -stm32u545ne = ["stm32-metapac/stm32u545ne"] -stm32u545re = ["stm32-metapac/stm32u545re"] -stm32u545ve = ["stm32-metapac/stm32u545ve"] -stm32u575ag = ["stm32-metapac/stm32u575ag"] -stm32u575ai = ["stm32-metapac/stm32u575ai"] -stm32u575cg = ["stm32-metapac/stm32u575cg"] -stm32u575ci = ["stm32-metapac/stm32u575ci"] -stm32u575og = ["stm32-metapac/stm32u575og"] -stm32u575oi = ["stm32-metapac/stm32u575oi"] -stm32u575qg = ["stm32-metapac/stm32u575qg"] -stm32u575qi = ["stm32-metapac/stm32u575qi"] -stm32u575rg = ["stm32-metapac/stm32u575rg"] -stm32u575ri = ["stm32-metapac/stm32u575ri"] -stm32u575vg = ["stm32-metapac/stm32u575vg"] -stm32u575vi = ["stm32-metapac/stm32u575vi"] -stm32u575zg = ["stm32-metapac/stm32u575zg"] -stm32u575zi = ["stm32-metapac/stm32u575zi"] -stm32u585ai = ["stm32-metapac/stm32u585ai"] -stm32u585ci = ["stm32-metapac/stm32u585ci"] -stm32u585oi = ["stm32-metapac/stm32u585oi"] -stm32u585qi = ["stm32-metapac/stm32u585qi"] -stm32u585ri = ["stm32-metapac/stm32u585ri"] -stm32u585vi = ["stm32-metapac/stm32u585vi"] -stm32u585zi = ["stm32-metapac/stm32u585zi"] -stm32u595ai = ["stm32-metapac/stm32u595ai"] -stm32u595aj = ["stm32-metapac/stm32u595aj"] -stm32u595qi = ["stm32-metapac/stm32u595qi"] -stm32u595qj = ["stm32-metapac/stm32u595qj"] -stm32u595ri = ["stm32-metapac/stm32u595ri"] -stm32u595rj = ["stm32-metapac/stm32u595rj"] -stm32u595vi = ["stm32-metapac/stm32u595vi"] -stm32u595vj = ["stm32-metapac/stm32u595vj"] -stm32u595zi = ["stm32-metapac/stm32u595zi"] -stm32u595zj = ["stm32-metapac/stm32u595zj"] -stm32u599bj = ["stm32-metapac/stm32u599bj"] -stm32u599ni = ["stm32-metapac/stm32u599ni"] -stm32u599nj = ["stm32-metapac/stm32u599nj"] -stm32u599vi = ["stm32-metapac/stm32u599vi"] -stm32u599vj = ["stm32-metapac/stm32u599vj"] -stm32u599zi = ["stm32-metapac/stm32u599zi"] -stm32u599zj = ["stm32-metapac/stm32u599zj"] -stm32u5a5aj = ["stm32-metapac/stm32u5a5aj"] -stm32u5a5qj = ["stm32-metapac/stm32u5a5qj"] -stm32u5a5rj = ["stm32-metapac/stm32u5a5rj"] -stm32u5a5vj = ["stm32-metapac/stm32u5a5vj"] -stm32u5a5zj = ["stm32-metapac/stm32u5a5zj"] -stm32u5a9bj = ["stm32-metapac/stm32u5a9bj"] -stm32u5a9nj = ["stm32-metapac/stm32u5a9nj"] -stm32u5a9vj = ["stm32-metapac/stm32u5a9vj"] -stm32u5a9zj = ["stm32-metapac/stm32u5a9zj"] -stm32wb10cc = ["stm32-metapac/stm32wb10cc"] -stm32wb15cc = ["stm32-metapac/stm32wb15cc"] -stm32wb30ce = ["stm32-metapac/stm32wb30ce"] -stm32wb35cc = ["stm32-metapac/stm32wb35cc"] -stm32wb35ce = ["stm32-metapac/stm32wb35ce"] -stm32wb50cg = ["stm32-metapac/stm32wb50cg"] -stm32wb55cc = ["stm32-metapac/stm32wb55cc"] -stm32wb55ce = ["stm32-metapac/stm32wb55ce"] -stm32wb55cg = ["stm32-metapac/stm32wb55cg"] -stm32wb55rc = ["stm32-metapac/stm32wb55rc"] -stm32wb55re = ["stm32-metapac/stm32wb55re"] -stm32wb55rg = ["stm32-metapac/stm32wb55rg"] -stm32wb55vc = ["stm32-metapac/stm32wb55vc"] -stm32wb55ve = ["stm32-metapac/stm32wb55ve"] -stm32wb55vg = ["stm32-metapac/stm32wb55vg"] -stm32wb55vy = ["stm32-metapac/stm32wb55vy"] -stm32wl54cc-cm4 = ["stm32-metapac/stm32wl54cc-cm4"] -stm32wl54cc-cm0p = ["stm32-metapac/stm32wl54cc-cm0p"] -stm32wl54jc-cm4 = ["stm32-metapac/stm32wl54jc-cm4"] -stm32wl54jc-cm0p = ["stm32-metapac/stm32wl54jc-cm0p"] -stm32wl55cc-cm4 = ["stm32-metapac/stm32wl55cc-cm4"] -stm32wl55cc-cm0p = ["stm32-metapac/stm32wl55cc-cm0p"] -stm32wl55jc-cm4 = ["stm32-metapac/stm32wl55jc-cm4"] -stm32wl55jc-cm0p = ["stm32-metapac/stm32wl55jc-cm0p"] -stm32wle4c8 = ["stm32-metapac/stm32wle4c8"] -stm32wle4cb = ["stm32-metapac/stm32wle4cb"] -stm32wle4cc = ["stm32-metapac/stm32wle4cc"] -stm32wle4j8 = ["stm32-metapac/stm32wle4j8"] -stm32wle4jb = ["stm32-metapac/stm32wle4jb"] -stm32wle4jc = ["stm32-metapac/stm32wle4jc"] -stm32wle5c8 = ["stm32-metapac/stm32wle5c8"] -stm32wle5cb = ["stm32-metapac/stm32wle5cb"] -stm32wle5cc = ["stm32-metapac/stm32wle5cc"] -stm32wle5j8 = ["stm32-metapac/stm32wle5j8"] -stm32wle5jb = ["stm32-metapac/stm32wle5jb"] -stm32wle5jc = ["stm32-metapac/stm32wle5jc"] +stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] +stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] +stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] +stm32c011j4 = [ "stm32-metapac/stm32c011j4" ] +stm32c011j6 = [ "stm32-metapac/stm32c011j6" ] +stm32c031c4 = [ "stm32-metapac/stm32c031c4" ] +stm32c031c6 = [ "stm32-metapac/stm32c031c6" ] +stm32c031f4 = [ "stm32-metapac/stm32c031f4" ] +stm32c031f6 = [ "stm32-metapac/stm32c031f6" ] +stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] +stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] +stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] +stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] +stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] +stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] +stm32f030cc = [ "stm32-metapac/stm32f030cc" ] +stm32f030f4 = [ "stm32-metapac/stm32f030f4" ] +stm32f030k6 = [ "stm32-metapac/stm32f030k6" ] +stm32f030r8 = [ "stm32-metapac/stm32f030r8" ] +stm32f030rc = [ "stm32-metapac/stm32f030rc" ] +stm32f031c4 = [ "stm32-metapac/stm32f031c4" ] +stm32f031c6 = [ "stm32-metapac/stm32f031c6" ] +stm32f031e6 = [ "stm32-metapac/stm32f031e6" ] +stm32f031f4 = [ "stm32-metapac/stm32f031f4" ] +stm32f031f6 = [ "stm32-metapac/stm32f031f6" ] +stm32f031g4 = [ "stm32-metapac/stm32f031g4" ] +stm32f031g6 = [ "stm32-metapac/stm32f031g6" ] +stm32f031k4 = [ "stm32-metapac/stm32f031k4" ] +stm32f031k6 = [ "stm32-metapac/stm32f031k6" ] +stm32f038c6 = [ "stm32-metapac/stm32f038c6" ] +stm32f038e6 = [ "stm32-metapac/stm32f038e6" ] +stm32f038f6 = [ "stm32-metapac/stm32f038f6" ] +stm32f038g6 = [ "stm32-metapac/stm32f038g6" ] +stm32f038k6 = [ "stm32-metapac/stm32f038k6" ] +stm32f042c4 = [ "stm32-metapac/stm32f042c4" ] +stm32f042c6 = [ "stm32-metapac/stm32f042c6" ] +stm32f042f4 = [ "stm32-metapac/stm32f042f4" ] +stm32f042f6 = [ "stm32-metapac/stm32f042f6" ] +stm32f042g4 = [ "stm32-metapac/stm32f042g4" ] +stm32f042g6 = [ "stm32-metapac/stm32f042g6" ] +stm32f042k4 = [ "stm32-metapac/stm32f042k4" ] +stm32f042k6 = [ "stm32-metapac/stm32f042k6" ] +stm32f042t6 = [ "stm32-metapac/stm32f042t6" ] +stm32f048c6 = [ "stm32-metapac/stm32f048c6" ] +stm32f048g6 = [ "stm32-metapac/stm32f048g6" ] +stm32f048t6 = [ "stm32-metapac/stm32f048t6" ] +stm32f051c4 = [ "stm32-metapac/stm32f051c4" ] +stm32f051c6 = [ "stm32-metapac/stm32f051c6" ] +stm32f051c8 = [ "stm32-metapac/stm32f051c8" ] +stm32f051k4 = [ "stm32-metapac/stm32f051k4" ] +stm32f051k6 = [ "stm32-metapac/stm32f051k6" ] +stm32f051k8 = [ "stm32-metapac/stm32f051k8" ] +stm32f051r4 = [ "stm32-metapac/stm32f051r4" ] +stm32f051r6 = [ "stm32-metapac/stm32f051r6" ] +stm32f051r8 = [ "stm32-metapac/stm32f051r8" ] +stm32f051t8 = [ "stm32-metapac/stm32f051t8" ] +stm32f058c8 = [ "stm32-metapac/stm32f058c8" ] +stm32f058r8 = [ "stm32-metapac/stm32f058r8" ] +stm32f058t8 = [ "stm32-metapac/stm32f058t8" ] +stm32f070c6 = [ "stm32-metapac/stm32f070c6" ] +stm32f070cb = [ "stm32-metapac/stm32f070cb" ] +stm32f070f6 = [ "stm32-metapac/stm32f070f6" ] +stm32f070rb = [ "stm32-metapac/stm32f070rb" ] +stm32f071c8 = [ "stm32-metapac/stm32f071c8" ] +stm32f071cb = [ "stm32-metapac/stm32f071cb" ] +stm32f071rb = [ "stm32-metapac/stm32f071rb" ] +stm32f071v8 = [ "stm32-metapac/stm32f071v8" ] +stm32f071vb = [ "stm32-metapac/stm32f071vb" ] +stm32f072c8 = [ "stm32-metapac/stm32f072c8" ] +stm32f072cb = [ "stm32-metapac/stm32f072cb" ] +stm32f072r8 = [ "stm32-metapac/stm32f072r8" ] +stm32f072rb = [ "stm32-metapac/stm32f072rb" ] +stm32f072v8 = [ "stm32-metapac/stm32f072v8" ] +stm32f072vb = [ "stm32-metapac/stm32f072vb" ] +stm32f078cb = [ "stm32-metapac/stm32f078cb" ] +stm32f078rb = [ "stm32-metapac/stm32f078rb" ] +stm32f078vb = [ "stm32-metapac/stm32f078vb" ] +stm32f091cb = [ "stm32-metapac/stm32f091cb" ] +stm32f091cc = [ "stm32-metapac/stm32f091cc" ] +stm32f091rb = [ "stm32-metapac/stm32f091rb" ] +stm32f091rc = [ "stm32-metapac/stm32f091rc" ] +stm32f091vb = [ "stm32-metapac/stm32f091vb" ] +stm32f091vc = [ "stm32-metapac/stm32f091vc" ] +stm32f098cc = [ "stm32-metapac/stm32f098cc" ] +stm32f098rc = [ "stm32-metapac/stm32f098rc" ] +stm32f098vc = [ "stm32-metapac/stm32f098vc" ] +stm32f100c4 = [ "stm32-metapac/stm32f100c4" ] +stm32f100c6 = [ "stm32-metapac/stm32f100c6" ] +stm32f100c8 = [ "stm32-metapac/stm32f100c8" ] +stm32f100cb = [ "stm32-metapac/stm32f100cb" ] +stm32f100r4 = [ "stm32-metapac/stm32f100r4" ] +stm32f100r6 = [ "stm32-metapac/stm32f100r6" ] +stm32f100r8 = [ "stm32-metapac/stm32f100r8" ] +stm32f100rb = [ "stm32-metapac/stm32f100rb" ] +stm32f100rc = [ "stm32-metapac/stm32f100rc" ] +stm32f100rd = [ "stm32-metapac/stm32f100rd" ] +stm32f100re = [ "stm32-metapac/stm32f100re" ] +stm32f100v8 = [ "stm32-metapac/stm32f100v8" ] +stm32f100vb = [ "stm32-metapac/stm32f100vb" ] +stm32f100vc = [ "stm32-metapac/stm32f100vc" ] +stm32f100vd = [ "stm32-metapac/stm32f100vd" ] +stm32f100ve = [ "stm32-metapac/stm32f100ve" ] +stm32f100zc = [ "stm32-metapac/stm32f100zc" ] +stm32f100zd = [ "stm32-metapac/stm32f100zd" ] +stm32f100ze = [ "stm32-metapac/stm32f100ze" ] +stm32f101c4 = [ "stm32-metapac/stm32f101c4" ] +stm32f101c6 = [ "stm32-metapac/stm32f101c6" ] +stm32f101c8 = [ "stm32-metapac/stm32f101c8" ] +stm32f101cb = [ "stm32-metapac/stm32f101cb" ] +stm32f101r4 = [ "stm32-metapac/stm32f101r4" ] +stm32f101r6 = [ "stm32-metapac/stm32f101r6" ] +stm32f101r8 = [ "stm32-metapac/stm32f101r8" ] +stm32f101rb = [ "stm32-metapac/stm32f101rb" ] +stm32f101rc = [ "stm32-metapac/stm32f101rc" ] +stm32f101rd = [ "stm32-metapac/stm32f101rd" ] +stm32f101re = [ "stm32-metapac/stm32f101re" ] +stm32f101rf = [ "stm32-metapac/stm32f101rf" ] +stm32f101rg = [ "stm32-metapac/stm32f101rg" ] +stm32f101t4 = [ "stm32-metapac/stm32f101t4" ] +stm32f101t6 = [ "stm32-metapac/stm32f101t6" ] +stm32f101t8 = [ "stm32-metapac/stm32f101t8" ] +stm32f101tb = [ "stm32-metapac/stm32f101tb" ] +stm32f101v8 = [ "stm32-metapac/stm32f101v8" ] +stm32f101vb = [ "stm32-metapac/stm32f101vb" ] +stm32f101vc = [ "stm32-metapac/stm32f101vc" ] +stm32f101vd = [ "stm32-metapac/stm32f101vd" ] +stm32f101ve = [ "stm32-metapac/stm32f101ve" ] +stm32f101vf = [ "stm32-metapac/stm32f101vf" ] +stm32f101vg = [ "stm32-metapac/stm32f101vg" ] +stm32f101zc = [ "stm32-metapac/stm32f101zc" ] +stm32f101zd = [ "stm32-metapac/stm32f101zd" ] +stm32f101ze = [ "stm32-metapac/stm32f101ze" ] +stm32f101zf = [ "stm32-metapac/stm32f101zf" ] +stm32f101zg = [ "stm32-metapac/stm32f101zg" ] +stm32f102c4 = [ "stm32-metapac/stm32f102c4" ] +stm32f102c6 = [ "stm32-metapac/stm32f102c6" ] +stm32f102c8 = [ "stm32-metapac/stm32f102c8" ] +stm32f102cb = [ "stm32-metapac/stm32f102cb" ] +stm32f102r4 = [ "stm32-metapac/stm32f102r4" ] +stm32f102r6 = [ "stm32-metapac/stm32f102r6" ] +stm32f102r8 = [ "stm32-metapac/stm32f102r8" ] +stm32f102rb = [ "stm32-metapac/stm32f102rb" ] +stm32f103c4 = [ "stm32-metapac/stm32f103c4" ] +stm32f103c6 = [ "stm32-metapac/stm32f103c6" ] +stm32f103c8 = [ "stm32-metapac/stm32f103c8" ] +stm32f103cb = [ "stm32-metapac/stm32f103cb" ] +stm32f103r4 = [ "stm32-metapac/stm32f103r4" ] +stm32f103r6 = [ "stm32-metapac/stm32f103r6" ] +stm32f103r8 = [ "stm32-metapac/stm32f103r8" ] +stm32f103rb = [ "stm32-metapac/stm32f103rb" ] +stm32f103rc = [ "stm32-metapac/stm32f103rc" ] +stm32f103rd = [ "stm32-metapac/stm32f103rd" ] +stm32f103re = [ "stm32-metapac/stm32f103re" ] +stm32f103rf = [ "stm32-metapac/stm32f103rf" ] +stm32f103rg = [ "stm32-metapac/stm32f103rg" ] +stm32f103t4 = [ "stm32-metapac/stm32f103t4" ] +stm32f103t6 = [ "stm32-metapac/stm32f103t6" ] +stm32f103t8 = [ "stm32-metapac/stm32f103t8" ] +stm32f103tb = [ "stm32-metapac/stm32f103tb" ] +stm32f103v8 = [ "stm32-metapac/stm32f103v8" ] +stm32f103vb = [ "stm32-metapac/stm32f103vb" ] +stm32f103vc = [ "stm32-metapac/stm32f103vc" ] +stm32f103vd = [ "stm32-metapac/stm32f103vd" ] +stm32f103ve = [ "stm32-metapac/stm32f103ve" ] +stm32f103vf = [ "stm32-metapac/stm32f103vf" ] +stm32f103vg = [ "stm32-metapac/stm32f103vg" ] +stm32f103zc = [ "stm32-metapac/stm32f103zc" ] +stm32f103zd = [ "stm32-metapac/stm32f103zd" ] +stm32f103ze = [ "stm32-metapac/stm32f103ze" ] +stm32f103zf = [ "stm32-metapac/stm32f103zf" ] +stm32f103zg = [ "stm32-metapac/stm32f103zg" ] +stm32f105r8 = [ "stm32-metapac/stm32f105r8" ] +stm32f105rb = [ "stm32-metapac/stm32f105rb" ] +stm32f105rc = [ "stm32-metapac/stm32f105rc" ] +stm32f105v8 = [ "stm32-metapac/stm32f105v8" ] +stm32f105vb = [ "stm32-metapac/stm32f105vb" ] +stm32f105vc = [ "stm32-metapac/stm32f105vc" ] +stm32f107rb = [ "stm32-metapac/stm32f107rb" ] +stm32f107rc = [ "stm32-metapac/stm32f107rc" ] +stm32f107vb = [ "stm32-metapac/stm32f107vb" ] +stm32f107vc = [ "stm32-metapac/stm32f107vc" ] +stm32f205rb = [ "stm32-metapac/stm32f205rb" ] +stm32f205rc = [ "stm32-metapac/stm32f205rc" ] +stm32f205re = [ "stm32-metapac/stm32f205re" ] +stm32f205rf = [ "stm32-metapac/stm32f205rf" ] +stm32f205rg = [ "stm32-metapac/stm32f205rg" ] +stm32f205vb = [ "stm32-metapac/stm32f205vb" ] +stm32f205vc = [ "stm32-metapac/stm32f205vc" ] +stm32f205ve = [ "stm32-metapac/stm32f205ve" ] +stm32f205vf = [ "stm32-metapac/stm32f205vf" ] +stm32f205vg = [ "stm32-metapac/stm32f205vg" ] +stm32f205zc = [ "stm32-metapac/stm32f205zc" ] +stm32f205ze = [ "stm32-metapac/stm32f205ze" ] +stm32f205zf = [ "stm32-metapac/stm32f205zf" ] +stm32f205zg = [ "stm32-metapac/stm32f205zg" ] +stm32f207ic = [ "stm32-metapac/stm32f207ic" ] +stm32f207ie = [ "stm32-metapac/stm32f207ie" ] +stm32f207if = [ "stm32-metapac/stm32f207if" ] +stm32f207ig = [ "stm32-metapac/stm32f207ig" ] +stm32f207vc = [ "stm32-metapac/stm32f207vc" ] +stm32f207ve = [ "stm32-metapac/stm32f207ve" ] +stm32f207vf = [ "stm32-metapac/stm32f207vf" ] +stm32f207vg = [ "stm32-metapac/stm32f207vg" ] +stm32f207zc = [ "stm32-metapac/stm32f207zc" ] +stm32f207ze = [ "stm32-metapac/stm32f207ze" ] +stm32f207zf = [ "stm32-metapac/stm32f207zf" ] +stm32f207zg = [ "stm32-metapac/stm32f207zg" ] +stm32f215re = [ "stm32-metapac/stm32f215re" ] +stm32f215rg = [ "stm32-metapac/stm32f215rg" ] +stm32f215ve = [ "stm32-metapac/stm32f215ve" ] +stm32f215vg = [ "stm32-metapac/stm32f215vg" ] +stm32f215ze = [ "stm32-metapac/stm32f215ze" ] +stm32f215zg = [ "stm32-metapac/stm32f215zg" ] +stm32f217ie = [ "stm32-metapac/stm32f217ie" ] +stm32f217ig = [ "stm32-metapac/stm32f217ig" ] +stm32f217ve = [ "stm32-metapac/stm32f217ve" ] +stm32f217vg = [ "stm32-metapac/stm32f217vg" ] +stm32f217ze = [ "stm32-metapac/stm32f217ze" ] +stm32f217zg = [ "stm32-metapac/stm32f217zg" ] +stm32f301c6 = [ "stm32-metapac/stm32f301c6" ] +stm32f301c8 = [ "stm32-metapac/stm32f301c8" ] +stm32f301k6 = [ "stm32-metapac/stm32f301k6" ] +stm32f301k8 = [ "stm32-metapac/stm32f301k8" ] +stm32f301r6 = [ "stm32-metapac/stm32f301r6" ] +stm32f301r8 = [ "stm32-metapac/stm32f301r8" ] +stm32f302c6 = [ "stm32-metapac/stm32f302c6" ] +stm32f302c8 = [ "stm32-metapac/stm32f302c8" ] +stm32f302cb = [ "stm32-metapac/stm32f302cb" ] +stm32f302cc = [ "stm32-metapac/stm32f302cc" ] +stm32f302k6 = [ "stm32-metapac/stm32f302k6" ] +stm32f302k8 = [ "stm32-metapac/stm32f302k8" ] +stm32f302r6 = [ "stm32-metapac/stm32f302r6" ] +stm32f302r8 = [ "stm32-metapac/stm32f302r8" ] +stm32f302rb = [ "stm32-metapac/stm32f302rb" ] +stm32f302rc = [ "stm32-metapac/stm32f302rc" ] +stm32f302rd = [ "stm32-metapac/stm32f302rd" ] +stm32f302re = [ "stm32-metapac/stm32f302re" ] +stm32f302vb = [ "stm32-metapac/stm32f302vb" ] +stm32f302vc = [ "stm32-metapac/stm32f302vc" ] +stm32f302vd = [ "stm32-metapac/stm32f302vd" ] +stm32f302ve = [ "stm32-metapac/stm32f302ve" ] +stm32f302zd = [ "stm32-metapac/stm32f302zd" ] +stm32f302ze = [ "stm32-metapac/stm32f302ze" ] +stm32f303c6 = [ "stm32-metapac/stm32f303c6" ] +stm32f303c8 = [ "stm32-metapac/stm32f303c8" ] +stm32f303cb = [ "stm32-metapac/stm32f303cb" ] +stm32f303cc = [ "stm32-metapac/stm32f303cc" ] +stm32f303k6 = [ "stm32-metapac/stm32f303k6" ] +stm32f303k8 = [ "stm32-metapac/stm32f303k8" ] +stm32f303r6 = [ "stm32-metapac/stm32f303r6" ] +stm32f303r8 = [ "stm32-metapac/stm32f303r8" ] +stm32f303rb = [ "stm32-metapac/stm32f303rb" ] +stm32f303rc = [ "stm32-metapac/stm32f303rc" ] +stm32f303rd = [ "stm32-metapac/stm32f303rd" ] +stm32f303re = [ "stm32-metapac/stm32f303re" ] +stm32f303vb = [ "stm32-metapac/stm32f303vb" ] +stm32f303vc = [ "stm32-metapac/stm32f303vc" ] +stm32f303vd = [ "stm32-metapac/stm32f303vd" ] +stm32f303ve = [ "stm32-metapac/stm32f303ve" ] +stm32f303zd = [ "stm32-metapac/stm32f303zd" ] +stm32f303ze = [ "stm32-metapac/stm32f303ze" ] +stm32f318c8 = [ "stm32-metapac/stm32f318c8" ] +stm32f318k8 = [ "stm32-metapac/stm32f318k8" ] +stm32f328c8 = [ "stm32-metapac/stm32f328c8" ] +stm32f334c4 = [ "stm32-metapac/stm32f334c4" ] +stm32f334c6 = [ "stm32-metapac/stm32f334c6" ] +stm32f334c8 = [ "stm32-metapac/stm32f334c8" ] +stm32f334k4 = [ "stm32-metapac/stm32f334k4" ] +stm32f334k6 = [ "stm32-metapac/stm32f334k6" ] +stm32f334k8 = [ "stm32-metapac/stm32f334k8" ] +stm32f334r6 = [ "stm32-metapac/stm32f334r6" ] +stm32f334r8 = [ "stm32-metapac/stm32f334r8" ] +stm32f358cc = [ "stm32-metapac/stm32f358cc" ] +stm32f358rc = [ "stm32-metapac/stm32f358rc" ] +stm32f358vc = [ "stm32-metapac/stm32f358vc" ] +stm32f373c8 = [ "stm32-metapac/stm32f373c8" ] +stm32f373cb = [ "stm32-metapac/stm32f373cb" ] +stm32f373cc = [ "stm32-metapac/stm32f373cc" ] +stm32f373r8 = [ "stm32-metapac/stm32f373r8" ] +stm32f373rb = [ "stm32-metapac/stm32f373rb" ] +stm32f373rc = [ "stm32-metapac/stm32f373rc" ] +stm32f373v8 = [ "stm32-metapac/stm32f373v8" ] +stm32f373vb = [ "stm32-metapac/stm32f373vb" ] +stm32f373vc = [ "stm32-metapac/stm32f373vc" ] +stm32f378cc = [ "stm32-metapac/stm32f378cc" ] +stm32f378rc = [ "stm32-metapac/stm32f378rc" ] +stm32f378vc = [ "stm32-metapac/stm32f378vc" ] +stm32f398ve = [ "stm32-metapac/stm32f398ve" ] +stm32f401cb = [ "stm32-metapac/stm32f401cb" ] +stm32f401cc = [ "stm32-metapac/stm32f401cc" ] +stm32f401cd = [ "stm32-metapac/stm32f401cd" ] +stm32f401ce = [ "stm32-metapac/stm32f401ce" ] +stm32f401rb = [ "stm32-metapac/stm32f401rb" ] +stm32f401rc = [ "stm32-metapac/stm32f401rc" ] +stm32f401rd = [ "stm32-metapac/stm32f401rd" ] +stm32f401re = [ "stm32-metapac/stm32f401re" ] +stm32f401vb = [ "stm32-metapac/stm32f401vb" ] +stm32f401vc = [ "stm32-metapac/stm32f401vc" ] +stm32f401vd = [ "stm32-metapac/stm32f401vd" ] +stm32f401ve = [ "stm32-metapac/stm32f401ve" ] +stm32f405oe = [ "stm32-metapac/stm32f405oe" ] +stm32f405og = [ "stm32-metapac/stm32f405og" ] +stm32f405rg = [ "stm32-metapac/stm32f405rg" ] +stm32f405vg = [ "stm32-metapac/stm32f405vg" ] +stm32f405zg = [ "stm32-metapac/stm32f405zg" ] +stm32f407ie = [ "stm32-metapac/stm32f407ie" ] +stm32f407ig = [ "stm32-metapac/stm32f407ig" ] +stm32f407ve = [ "stm32-metapac/stm32f407ve" ] +stm32f407vg = [ "stm32-metapac/stm32f407vg" ] +stm32f407ze = [ "stm32-metapac/stm32f407ze" ] +stm32f407zg = [ "stm32-metapac/stm32f407zg" ] +stm32f410c8 = [ "stm32-metapac/stm32f410c8" ] +stm32f410cb = [ "stm32-metapac/stm32f410cb" ] +stm32f410r8 = [ "stm32-metapac/stm32f410r8" ] +stm32f410rb = [ "stm32-metapac/stm32f410rb" ] +stm32f410t8 = [ "stm32-metapac/stm32f410t8" ] +stm32f410tb = [ "stm32-metapac/stm32f410tb" ] +stm32f411cc = [ "stm32-metapac/stm32f411cc" ] +stm32f411ce = [ "stm32-metapac/stm32f411ce" ] +stm32f411rc = [ "stm32-metapac/stm32f411rc" ] +stm32f411re = [ "stm32-metapac/stm32f411re" ] +stm32f411vc = [ "stm32-metapac/stm32f411vc" ] +stm32f411ve = [ "stm32-metapac/stm32f411ve" ] +stm32f412ce = [ "stm32-metapac/stm32f412ce" ] +stm32f412cg = [ "stm32-metapac/stm32f412cg" ] +stm32f412re = [ "stm32-metapac/stm32f412re" ] +stm32f412rg = [ "stm32-metapac/stm32f412rg" ] +stm32f412ve = [ "stm32-metapac/stm32f412ve" ] +stm32f412vg = [ "stm32-metapac/stm32f412vg" ] +stm32f412ze = [ "stm32-metapac/stm32f412ze" ] +stm32f412zg = [ "stm32-metapac/stm32f412zg" ] +stm32f413cg = [ "stm32-metapac/stm32f413cg" ] +stm32f413ch = [ "stm32-metapac/stm32f413ch" ] +stm32f413mg = [ "stm32-metapac/stm32f413mg" ] +stm32f413mh = [ "stm32-metapac/stm32f413mh" ] +stm32f413rg = [ "stm32-metapac/stm32f413rg" ] +stm32f413rh = [ "stm32-metapac/stm32f413rh" ] +stm32f413vg = [ "stm32-metapac/stm32f413vg" ] +stm32f413vh = [ "stm32-metapac/stm32f413vh" ] +stm32f413zg = [ "stm32-metapac/stm32f413zg" ] +stm32f413zh = [ "stm32-metapac/stm32f413zh" ] +stm32f415og = [ "stm32-metapac/stm32f415og" ] +stm32f415rg = [ "stm32-metapac/stm32f415rg" ] +stm32f415vg = [ "stm32-metapac/stm32f415vg" ] +stm32f415zg = [ "stm32-metapac/stm32f415zg" ] +stm32f417ie = [ "stm32-metapac/stm32f417ie" ] +stm32f417ig = [ "stm32-metapac/stm32f417ig" ] +stm32f417ve = [ "stm32-metapac/stm32f417ve" ] +stm32f417vg = [ "stm32-metapac/stm32f417vg" ] +stm32f417ze = [ "stm32-metapac/stm32f417ze" ] +stm32f417zg = [ "stm32-metapac/stm32f417zg" ] +stm32f423ch = [ "stm32-metapac/stm32f423ch" ] +stm32f423mh = [ "stm32-metapac/stm32f423mh" ] +stm32f423rh = [ "stm32-metapac/stm32f423rh" ] +stm32f423vh = [ "stm32-metapac/stm32f423vh" ] +stm32f423zh = [ "stm32-metapac/stm32f423zh" ] +stm32f427ag = [ "stm32-metapac/stm32f427ag" ] +stm32f427ai = [ "stm32-metapac/stm32f427ai" ] +stm32f427ig = [ "stm32-metapac/stm32f427ig" ] +stm32f427ii = [ "stm32-metapac/stm32f427ii" ] +stm32f427vg = [ "stm32-metapac/stm32f427vg" ] +stm32f427vi = [ "stm32-metapac/stm32f427vi" ] +stm32f427zg = [ "stm32-metapac/stm32f427zg" ] +stm32f427zi = [ "stm32-metapac/stm32f427zi" ] +stm32f429ag = [ "stm32-metapac/stm32f429ag" ] +stm32f429ai = [ "stm32-metapac/stm32f429ai" ] +stm32f429be = [ "stm32-metapac/stm32f429be" ] +stm32f429bg = [ "stm32-metapac/stm32f429bg" ] +stm32f429bi = [ "stm32-metapac/stm32f429bi" ] +stm32f429ie = [ "stm32-metapac/stm32f429ie" ] +stm32f429ig = [ "stm32-metapac/stm32f429ig" ] +stm32f429ii = [ "stm32-metapac/stm32f429ii" ] +stm32f429ne = [ "stm32-metapac/stm32f429ne" ] +stm32f429ng = [ "stm32-metapac/stm32f429ng" ] +stm32f429ni = [ "stm32-metapac/stm32f429ni" ] +stm32f429ve = [ "stm32-metapac/stm32f429ve" ] +stm32f429vg = [ "stm32-metapac/stm32f429vg" ] +stm32f429vi = [ "stm32-metapac/stm32f429vi" ] +stm32f429ze = [ "stm32-metapac/stm32f429ze" ] +stm32f429zg = [ "stm32-metapac/stm32f429zg" ] +stm32f429zi = [ "stm32-metapac/stm32f429zi" ] +stm32f437ai = [ "stm32-metapac/stm32f437ai" ] +stm32f437ig = [ "stm32-metapac/stm32f437ig" ] +stm32f437ii = [ "stm32-metapac/stm32f437ii" ] +stm32f437vg = [ "stm32-metapac/stm32f437vg" ] +stm32f437vi = [ "stm32-metapac/stm32f437vi" ] +stm32f437zg = [ "stm32-metapac/stm32f437zg" ] +stm32f437zi = [ "stm32-metapac/stm32f437zi" ] +stm32f439ai = [ "stm32-metapac/stm32f439ai" ] +stm32f439bg = [ "stm32-metapac/stm32f439bg" ] +stm32f439bi = [ "stm32-metapac/stm32f439bi" ] +stm32f439ig = [ "stm32-metapac/stm32f439ig" ] +stm32f439ii = [ "stm32-metapac/stm32f439ii" ] +stm32f439ng = [ "stm32-metapac/stm32f439ng" ] +stm32f439ni = [ "stm32-metapac/stm32f439ni" ] +stm32f439vg = [ "stm32-metapac/stm32f439vg" ] +stm32f439vi = [ "stm32-metapac/stm32f439vi" ] +stm32f439zg = [ "stm32-metapac/stm32f439zg" ] +stm32f439zi = [ "stm32-metapac/stm32f439zi" ] +stm32f446mc = [ "stm32-metapac/stm32f446mc" ] +stm32f446me = [ "stm32-metapac/stm32f446me" ] +stm32f446rc = [ "stm32-metapac/stm32f446rc" ] +stm32f446re = [ "stm32-metapac/stm32f446re" ] +stm32f446vc = [ "stm32-metapac/stm32f446vc" ] +stm32f446ve = [ "stm32-metapac/stm32f446ve" ] +stm32f446zc = [ "stm32-metapac/stm32f446zc" ] +stm32f446ze = [ "stm32-metapac/stm32f446ze" ] +stm32f469ae = [ "stm32-metapac/stm32f469ae" ] +stm32f469ag = [ "stm32-metapac/stm32f469ag" ] +stm32f469ai = [ "stm32-metapac/stm32f469ai" ] +stm32f469be = [ "stm32-metapac/stm32f469be" ] +stm32f469bg = [ "stm32-metapac/stm32f469bg" ] +stm32f469bi = [ "stm32-metapac/stm32f469bi" ] +stm32f469ie = [ "stm32-metapac/stm32f469ie" ] +stm32f469ig = [ "stm32-metapac/stm32f469ig" ] +stm32f469ii = [ "stm32-metapac/stm32f469ii" ] +stm32f469ne = [ "stm32-metapac/stm32f469ne" ] +stm32f469ng = [ "stm32-metapac/stm32f469ng" ] +stm32f469ni = [ "stm32-metapac/stm32f469ni" ] +stm32f469ve = [ "stm32-metapac/stm32f469ve" ] +stm32f469vg = [ "stm32-metapac/stm32f469vg" ] +stm32f469vi = [ "stm32-metapac/stm32f469vi" ] +stm32f469ze = [ "stm32-metapac/stm32f469ze" ] +stm32f469zg = [ "stm32-metapac/stm32f469zg" ] +stm32f469zi = [ "stm32-metapac/stm32f469zi" ] +stm32f479ag = [ "stm32-metapac/stm32f479ag" ] +stm32f479ai = [ "stm32-metapac/stm32f479ai" ] +stm32f479bg = [ "stm32-metapac/stm32f479bg" ] +stm32f479bi = [ "stm32-metapac/stm32f479bi" ] +stm32f479ig = [ "stm32-metapac/stm32f479ig" ] +stm32f479ii = [ "stm32-metapac/stm32f479ii" ] +stm32f479ng = [ "stm32-metapac/stm32f479ng" ] +stm32f479ni = [ "stm32-metapac/stm32f479ni" ] +stm32f479vg = [ "stm32-metapac/stm32f479vg" ] +stm32f479vi = [ "stm32-metapac/stm32f479vi" ] +stm32f479zg = [ "stm32-metapac/stm32f479zg" ] +stm32f479zi = [ "stm32-metapac/stm32f479zi" ] +stm32f722ic = [ "stm32-metapac/stm32f722ic" ] +stm32f722ie = [ "stm32-metapac/stm32f722ie" ] +stm32f722rc = [ "stm32-metapac/stm32f722rc" ] +stm32f722re = [ "stm32-metapac/stm32f722re" ] +stm32f722vc = [ "stm32-metapac/stm32f722vc" ] +stm32f722ve = [ "stm32-metapac/stm32f722ve" ] +stm32f722zc = [ "stm32-metapac/stm32f722zc" ] +stm32f722ze = [ "stm32-metapac/stm32f722ze" ] +stm32f723ic = [ "stm32-metapac/stm32f723ic" ] +stm32f723ie = [ "stm32-metapac/stm32f723ie" ] +stm32f723vc = [ "stm32-metapac/stm32f723vc" ] +stm32f723ve = [ "stm32-metapac/stm32f723ve" ] +stm32f723zc = [ "stm32-metapac/stm32f723zc" ] +stm32f723ze = [ "stm32-metapac/stm32f723ze" ] +stm32f730i8 = [ "stm32-metapac/stm32f730i8" ] +stm32f730r8 = [ "stm32-metapac/stm32f730r8" ] +stm32f730v8 = [ "stm32-metapac/stm32f730v8" ] +stm32f730z8 = [ "stm32-metapac/stm32f730z8" ] +stm32f732ie = [ "stm32-metapac/stm32f732ie" ] +stm32f732re = [ "stm32-metapac/stm32f732re" ] +stm32f732ve = [ "stm32-metapac/stm32f732ve" ] +stm32f732ze = [ "stm32-metapac/stm32f732ze" ] +stm32f733ie = [ "stm32-metapac/stm32f733ie" ] +stm32f733ve = [ "stm32-metapac/stm32f733ve" ] +stm32f733ze = [ "stm32-metapac/stm32f733ze" ] +stm32f745ie = [ "stm32-metapac/stm32f745ie" ] +stm32f745ig = [ "stm32-metapac/stm32f745ig" ] +stm32f745ve = [ "stm32-metapac/stm32f745ve" ] +stm32f745vg = [ "stm32-metapac/stm32f745vg" ] +stm32f745ze = [ "stm32-metapac/stm32f745ze" ] +stm32f745zg = [ "stm32-metapac/stm32f745zg" ] +stm32f746be = [ "stm32-metapac/stm32f746be" ] +stm32f746bg = [ "stm32-metapac/stm32f746bg" ] +stm32f746ie = [ "stm32-metapac/stm32f746ie" ] +stm32f746ig = [ "stm32-metapac/stm32f746ig" ] +stm32f746ne = [ "stm32-metapac/stm32f746ne" ] +stm32f746ng = [ "stm32-metapac/stm32f746ng" ] +stm32f746ve = [ "stm32-metapac/stm32f746ve" ] +stm32f746vg = [ "stm32-metapac/stm32f746vg" ] +stm32f746ze = [ "stm32-metapac/stm32f746ze" ] +stm32f746zg = [ "stm32-metapac/stm32f746zg" ] +stm32f750n8 = [ "stm32-metapac/stm32f750n8" ] +stm32f750v8 = [ "stm32-metapac/stm32f750v8" ] +stm32f750z8 = [ "stm32-metapac/stm32f750z8" ] +stm32f756bg = [ "stm32-metapac/stm32f756bg" ] +stm32f756ig = [ "stm32-metapac/stm32f756ig" ] +stm32f756ng = [ "stm32-metapac/stm32f756ng" ] +stm32f756vg = [ "stm32-metapac/stm32f756vg" ] +stm32f756zg = [ "stm32-metapac/stm32f756zg" ] +stm32f765bg = [ "stm32-metapac/stm32f765bg" ] +stm32f765bi = [ "stm32-metapac/stm32f765bi" ] +stm32f765ig = [ "stm32-metapac/stm32f765ig" ] +stm32f765ii = [ "stm32-metapac/stm32f765ii" ] +stm32f765ng = [ "stm32-metapac/stm32f765ng" ] +stm32f765ni = [ "stm32-metapac/stm32f765ni" ] +stm32f765vg = [ "stm32-metapac/stm32f765vg" ] +stm32f765vi = [ "stm32-metapac/stm32f765vi" ] +stm32f765zg = [ "stm32-metapac/stm32f765zg" ] +stm32f765zi = [ "stm32-metapac/stm32f765zi" ] +stm32f767bg = [ "stm32-metapac/stm32f767bg" ] +stm32f767bi = [ "stm32-metapac/stm32f767bi" ] +stm32f767ig = [ "stm32-metapac/stm32f767ig" ] +stm32f767ii = [ "stm32-metapac/stm32f767ii" ] +stm32f767ng = [ "stm32-metapac/stm32f767ng" ] +stm32f767ni = [ "stm32-metapac/stm32f767ni" ] +stm32f767vg = [ "stm32-metapac/stm32f767vg" ] +stm32f767vi = [ "stm32-metapac/stm32f767vi" ] +stm32f767zg = [ "stm32-metapac/stm32f767zg" ] +stm32f767zi = [ "stm32-metapac/stm32f767zi" ] +stm32f768ai = [ "stm32-metapac/stm32f768ai" ] +stm32f769ag = [ "stm32-metapac/stm32f769ag" ] +stm32f769ai = [ "stm32-metapac/stm32f769ai" ] +stm32f769bg = [ "stm32-metapac/stm32f769bg" ] +stm32f769bi = [ "stm32-metapac/stm32f769bi" ] +stm32f769ig = [ "stm32-metapac/stm32f769ig" ] +stm32f769ii = [ "stm32-metapac/stm32f769ii" ] +stm32f769ng = [ "stm32-metapac/stm32f769ng" ] +stm32f769ni = [ "stm32-metapac/stm32f769ni" ] +stm32f777bi = [ "stm32-metapac/stm32f777bi" ] +stm32f777ii = [ "stm32-metapac/stm32f777ii" ] +stm32f777ni = [ "stm32-metapac/stm32f777ni" ] +stm32f777vi = [ "stm32-metapac/stm32f777vi" ] +stm32f777zi = [ "stm32-metapac/stm32f777zi" ] +stm32f778ai = [ "stm32-metapac/stm32f778ai" ] +stm32f779ai = [ "stm32-metapac/stm32f779ai" ] +stm32f779bi = [ "stm32-metapac/stm32f779bi" ] +stm32f779ii = [ "stm32-metapac/stm32f779ii" ] +stm32f779ni = [ "stm32-metapac/stm32f779ni" ] +stm32g030c6 = [ "stm32-metapac/stm32g030c6" ] +stm32g030c8 = [ "stm32-metapac/stm32g030c8" ] +stm32g030f6 = [ "stm32-metapac/stm32g030f6" ] +stm32g030j6 = [ "stm32-metapac/stm32g030j6" ] +stm32g030k6 = [ "stm32-metapac/stm32g030k6" ] +stm32g030k8 = [ "stm32-metapac/stm32g030k8" ] +stm32g031c4 = [ "stm32-metapac/stm32g031c4" ] +stm32g031c6 = [ "stm32-metapac/stm32g031c6" ] +stm32g031c8 = [ "stm32-metapac/stm32g031c8" ] +stm32g031f4 = [ "stm32-metapac/stm32g031f4" ] +stm32g031f6 = [ "stm32-metapac/stm32g031f6" ] +stm32g031f8 = [ "stm32-metapac/stm32g031f8" ] +stm32g031g4 = [ "stm32-metapac/stm32g031g4" ] +stm32g031g6 = [ "stm32-metapac/stm32g031g6" ] +stm32g031g8 = [ "stm32-metapac/stm32g031g8" ] +stm32g031j4 = [ "stm32-metapac/stm32g031j4" ] +stm32g031j6 = [ "stm32-metapac/stm32g031j6" ] +stm32g031k4 = [ "stm32-metapac/stm32g031k4" ] +stm32g031k6 = [ "stm32-metapac/stm32g031k6" ] +stm32g031k8 = [ "stm32-metapac/stm32g031k8" ] +stm32g031y8 = [ "stm32-metapac/stm32g031y8" ] +stm32g041c6 = [ "stm32-metapac/stm32g041c6" ] +stm32g041c8 = [ "stm32-metapac/stm32g041c8" ] +stm32g041f6 = [ "stm32-metapac/stm32g041f6" ] +stm32g041f8 = [ "stm32-metapac/stm32g041f8" ] +stm32g041g6 = [ "stm32-metapac/stm32g041g6" ] +stm32g041g8 = [ "stm32-metapac/stm32g041g8" ] +stm32g041j6 = [ "stm32-metapac/stm32g041j6" ] +stm32g041k6 = [ "stm32-metapac/stm32g041k6" ] +stm32g041k8 = [ "stm32-metapac/stm32g041k8" ] +stm32g041y8 = [ "stm32-metapac/stm32g041y8" ] +stm32g050c6 = [ "stm32-metapac/stm32g050c6" ] +stm32g050c8 = [ "stm32-metapac/stm32g050c8" ] +stm32g050f6 = [ "stm32-metapac/stm32g050f6" ] +stm32g050k6 = [ "stm32-metapac/stm32g050k6" ] +stm32g050k8 = [ "stm32-metapac/stm32g050k8" ] +stm32g051c6 = [ "stm32-metapac/stm32g051c6" ] +stm32g051c8 = [ "stm32-metapac/stm32g051c8" ] +stm32g051f6 = [ "stm32-metapac/stm32g051f6" ] +stm32g051f8 = [ "stm32-metapac/stm32g051f8" ] +stm32g051g6 = [ "stm32-metapac/stm32g051g6" ] +stm32g051g8 = [ "stm32-metapac/stm32g051g8" ] +stm32g051k6 = [ "stm32-metapac/stm32g051k6" ] +stm32g051k8 = [ "stm32-metapac/stm32g051k8" ] +stm32g061c6 = [ "stm32-metapac/stm32g061c6" ] +stm32g061c8 = [ "stm32-metapac/stm32g061c8" ] +stm32g061f6 = [ "stm32-metapac/stm32g061f6" ] +stm32g061f8 = [ "stm32-metapac/stm32g061f8" ] +stm32g061g6 = [ "stm32-metapac/stm32g061g6" ] +stm32g061g8 = [ "stm32-metapac/stm32g061g8" ] +stm32g061k6 = [ "stm32-metapac/stm32g061k6" ] +stm32g061k8 = [ "stm32-metapac/stm32g061k8" ] +stm32g070cb = [ "stm32-metapac/stm32g070cb" ] +stm32g070kb = [ "stm32-metapac/stm32g070kb" ] +stm32g070rb = [ "stm32-metapac/stm32g070rb" ] +stm32g071c6 = [ "stm32-metapac/stm32g071c6" ] +stm32g071c8 = [ "stm32-metapac/stm32g071c8" ] +stm32g071cb = [ "stm32-metapac/stm32g071cb" ] +stm32g071eb = [ "stm32-metapac/stm32g071eb" ] +stm32g071g6 = [ "stm32-metapac/stm32g071g6" ] +stm32g071g8 = [ "stm32-metapac/stm32g071g8" ] +stm32g071gb = [ "stm32-metapac/stm32g071gb" ] +stm32g071k6 = [ "stm32-metapac/stm32g071k6" ] +stm32g071k8 = [ "stm32-metapac/stm32g071k8" ] +stm32g071kb = [ "stm32-metapac/stm32g071kb" ] +stm32g071r6 = [ "stm32-metapac/stm32g071r6" ] +stm32g071r8 = [ "stm32-metapac/stm32g071r8" ] +stm32g071rb = [ "stm32-metapac/stm32g071rb" ] +stm32g081cb = [ "stm32-metapac/stm32g081cb" ] +stm32g081eb = [ "stm32-metapac/stm32g081eb" ] +stm32g081gb = [ "stm32-metapac/stm32g081gb" ] +stm32g081kb = [ "stm32-metapac/stm32g081kb" ] +stm32g081rb = [ "stm32-metapac/stm32g081rb" ] +stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce" ] +stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke" ] +stm32g0b0re = [ "stm32-metapac/stm32g0b0re" ] +stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve" ] +stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb" ] +stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc" ] +stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce" ] +stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb" ] +stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc" ] +stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke" ] +stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb" ] +stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc" ] +stm32g0b1me = [ "stm32-metapac/stm32g0b1me" ] +stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne" ] +stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb" ] +stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc" ] +stm32g0b1re = [ "stm32-metapac/stm32g0b1re" ] +stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb" ] +stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc" ] +stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve" ] +stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc" ] +stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce" ] +stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc" ] +stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke" ] +stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc" ] +stm32g0c1me = [ "stm32-metapac/stm32g0c1me" ] +stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne" ] +stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc" ] +stm32g0c1re = [ "stm32-metapac/stm32g0c1re" ] +stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc" ] +stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve" ] +stm32g431c6 = [ "stm32-metapac/stm32g431c6" ] +stm32g431c8 = [ "stm32-metapac/stm32g431c8" ] +stm32g431cb = [ "stm32-metapac/stm32g431cb" ] +stm32g431k6 = [ "stm32-metapac/stm32g431k6" ] +stm32g431k8 = [ "stm32-metapac/stm32g431k8" ] +stm32g431kb = [ "stm32-metapac/stm32g431kb" ] +stm32g431m6 = [ "stm32-metapac/stm32g431m6" ] +stm32g431m8 = [ "stm32-metapac/stm32g431m8" ] +stm32g431mb = [ "stm32-metapac/stm32g431mb" ] +stm32g431r6 = [ "stm32-metapac/stm32g431r6" ] +stm32g431r8 = [ "stm32-metapac/stm32g431r8" ] +stm32g431rb = [ "stm32-metapac/stm32g431rb" ] +stm32g431v6 = [ "stm32-metapac/stm32g431v6" ] +stm32g431v8 = [ "stm32-metapac/stm32g431v8" ] +stm32g431vb = [ "stm32-metapac/stm32g431vb" ] +stm32g441cb = [ "stm32-metapac/stm32g441cb" ] +stm32g441kb = [ "stm32-metapac/stm32g441kb" ] +stm32g441mb = [ "stm32-metapac/stm32g441mb" ] +stm32g441rb = [ "stm32-metapac/stm32g441rb" ] +stm32g441vb = [ "stm32-metapac/stm32g441vb" ] +stm32g471cc = [ "stm32-metapac/stm32g471cc" ] +stm32g471ce = [ "stm32-metapac/stm32g471ce" ] +stm32g471mc = [ "stm32-metapac/stm32g471mc" ] +stm32g471me = [ "stm32-metapac/stm32g471me" ] +stm32g471qc = [ "stm32-metapac/stm32g471qc" ] +stm32g471qe = [ "stm32-metapac/stm32g471qe" ] +stm32g471rc = [ "stm32-metapac/stm32g471rc" ] +stm32g471re = [ "stm32-metapac/stm32g471re" ] +stm32g471vc = [ "stm32-metapac/stm32g471vc" ] +stm32g471ve = [ "stm32-metapac/stm32g471ve" ] +stm32g473cb = [ "stm32-metapac/stm32g473cb" ] +stm32g473cc = [ "stm32-metapac/stm32g473cc" ] +stm32g473ce = [ "stm32-metapac/stm32g473ce" ] +stm32g473mb = [ "stm32-metapac/stm32g473mb" ] +stm32g473mc = [ "stm32-metapac/stm32g473mc" ] +stm32g473me = [ "stm32-metapac/stm32g473me" ] +stm32g473pb = [ "stm32-metapac/stm32g473pb" ] +stm32g473pc = [ "stm32-metapac/stm32g473pc" ] +stm32g473pe = [ "stm32-metapac/stm32g473pe" ] +stm32g473qb = [ "stm32-metapac/stm32g473qb" ] +stm32g473qc = [ "stm32-metapac/stm32g473qc" ] +stm32g473qe = [ "stm32-metapac/stm32g473qe" ] +stm32g473rb = [ "stm32-metapac/stm32g473rb" ] +stm32g473rc = [ "stm32-metapac/stm32g473rc" ] +stm32g473re = [ "stm32-metapac/stm32g473re" ] +stm32g473vb = [ "stm32-metapac/stm32g473vb" ] +stm32g473vc = [ "stm32-metapac/stm32g473vc" ] +stm32g473ve = [ "stm32-metapac/stm32g473ve" ] +stm32g474cb = [ "stm32-metapac/stm32g474cb" ] +stm32g474cc = [ "stm32-metapac/stm32g474cc" ] +stm32g474ce = [ "stm32-metapac/stm32g474ce" ] +stm32g474mb = [ "stm32-metapac/stm32g474mb" ] +stm32g474mc = [ "stm32-metapac/stm32g474mc" ] +stm32g474me = [ "stm32-metapac/stm32g474me" ] +stm32g474pb = [ "stm32-metapac/stm32g474pb" ] +stm32g474pc = [ "stm32-metapac/stm32g474pc" ] +stm32g474pe = [ "stm32-metapac/stm32g474pe" ] +stm32g474qb = [ "stm32-metapac/stm32g474qb" ] +stm32g474qc = [ "stm32-metapac/stm32g474qc" ] +stm32g474qe = [ "stm32-metapac/stm32g474qe" ] +stm32g474rb = [ "stm32-metapac/stm32g474rb" ] +stm32g474rc = [ "stm32-metapac/stm32g474rc" ] +stm32g474re = [ "stm32-metapac/stm32g474re" ] +stm32g474vb = [ "stm32-metapac/stm32g474vb" ] +stm32g474vc = [ "stm32-metapac/stm32g474vc" ] +stm32g474ve = [ "stm32-metapac/stm32g474ve" ] +stm32g483ce = [ "stm32-metapac/stm32g483ce" ] +stm32g483me = [ "stm32-metapac/stm32g483me" ] +stm32g483pe = [ "stm32-metapac/stm32g483pe" ] +stm32g483qe = [ "stm32-metapac/stm32g483qe" ] +stm32g483re = [ "stm32-metapac/stm32g483re" ] +stm32g483ve = [ "stm32-metapac/stm32g483ve" ] +stm32g484ce = [ "stm32-metapac/stm32g484ce" ] +stm32g484me = [ "stm32-metapac/stm32g484me" ] +stm32g484pe = [ "stm32-metapac/stm32g484pe" ] +stm32g484qe = [ "stm32-metapac/stm32g484qe" ] +stm32g484re = [ "stm32-metapac/stm32g484re" ] +stm32g484ve = [ "stm32-metapac/stm32g484ve" ] +stm32g491cc = [ "stm32-metapac/stm32g491cc" ] +stm32g491ce = [ "stm32-metapac/stm32g491ce" ] +stm32g491kc = [ "stm32-metapac/stm32g491kc" ] +stm32g491ke = [ "stm32-metapac/stm32g491ke" ] +stm32g491mc = [ "stm32-metapac/stm32g491mc" ] +stm32g491me = [ "stm32-metapac/stm32g491me" ] +stm32g491rc = [ "stm32-metapac/stm32g491rc" ] +stm32g491re = [ "stm32-metapac/stm32g491re" ] +stm32g491vc = [ "stm32-metapac/stm32g491vc" ] +stm32g491ve = [ "stm32-metapac/stm32g491ve" ] +stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce" ] +stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] +stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] +stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] +stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] +stm32h503cb = [ "stm32-metapac/stm32h503cb" ] +stm32h503eb = [ "stm32-metapac/stm32h503eb" ] +stm32h503kb = [ "stm32-metapac/stm32h503kb" ] +stm32h503rb = [ "stm32-metapac/stm32h503rb" ] +stm32h562ag = [ "stm32-metapac/stm32h562ag" ] +stm32h562ai = [ "stm32-metapac/stm32h562ai" ] +stm32h562ig = [ "stm32-metapac/stm32h562ig" ] +stm32h562ii = [ "stm32-metapac/stm32h562ii" ] +stm32h562rg = [ "stm32-metapac/stm32h562rg" ] +stm32h562ri = [ "stm32-metapac/stm32h562ri" ] +stm32h562vg = [ "stm32-metapac/stm32h562vg" ] +stm32h562vi = [ "stm32-metapac/stm32h562vi" ] +stm32h562zg = [ "stm32-metapac/stm32h562zg" ] +stm32h562zi = [ "stm32-metapac/stm32h562zi" ] +stm32h563ag = [ "stm32-metapac/stm32h563ag" ] +stm32h563ai = [ "stm32-metapac/stm32h563ai" ] +stm32h563ig = [ "stm32-metapac/stm32h563ig" ] +stm32h563ii = [ "stm32-metapac/stm32h563ii" ] +stm32h563mi = [ "stm32-metapac/stm32h563mi" ] +stm32h563rg = [ "stm32-metapac/stm32h563rg" ] +stm32h563ri = [ "stm32-metapac/stm32h563ri" ] +stm32h563vg = [ "stm32-metapac/stm32h563vg" ] +stm32h563vi = [ "stm32-metapac/stm32h563vi" ] +stm32h563zg = [ "stm32-metapac/stm32h563zg" ] +stm32h563zi = [ "stm32-metapac/stm32h563zi" ] +stm32h573ai = [ "stm32-metapac/stm32h573ai" ] +stm32h573ii = [ "stm32-metapac/stm32h573ii" ] +stm32h573mi = [ "stm32-metapac/stm32h573mi" ] +stm32h573ri = [ "stm32-metapac/stm32h573ri" ] +stm32h573vi = [ "stm32-metapac/stm32h573vi" ] +stm32h573zi = [ "stm32-metapac/stm32h573zi" ] +stm32h723ve = [ "stm32-metapac/stm32h723ve" ] +stm32h723vg = [ "stm32-metapac/stm32h723vg" ] +stm32h723ze = [ "stm32-metapac/stm32h723ze" ] +stm32h723zg = [ "stm32-metapac/stm32h723zg" ] +stm32h725ae = [ "stm32-metapac/stm32h725ae" ] +stm32h725ag = [ "stm32-metapac/stm32h725ag" ] +stm32h725ie = [ "stm32-metapac/stm32h725ie" ] +stm32h725ig = [ "stm32-metapac/stm32h725ig" ] +stm32h725re = [ "stm32-metapac/stm32h725re" ] +stm32h725rg = [ "stm32-metapac/stm32h725rg" ] +stm32h725ve = [ "stm32-metapac/stm32h725ve" ] +stm32h725vg = [ "stm32-metapac/stm32h725vg" ] +stm32h725ze = [ "stm32-metapac/stm32h725ze" ] +stm32h725zg = [ "stm32-metapac/stm32h725zg" ] +stm32h730ab = [ "stm32-metapac/stm32h730ab" ] +stm32h730ib = [ "stm32-metapac/stm32h730ib" ] +stm32h730vb = [ "stm32-metapac/stm32h730vb" ] +stm32h730zb = [ "stm32-metapac/stm32h730zb" ] +stm32h733vg = [ "stm32-metapac/stm32h733vg" ] +stm32h733zg = [ "stm32-metapac/stm32h733zg" ] +stm32h735ag = [ "stm32-metapac/stm32h735ag" ] +stm32h735ig = [ "stm32-metapac/stm32h735ig" ] +stm32h735rg = [ "stm32-metapac/stm32h735rg" ] +stm32h735vg = [ "stm32-metapac/stm32h735vg" ] +stm32h735zg = [ "stm32-metapac/stm32h735zg" ] +stm32h742ag = [ "stm32-metapac/stm32h742ag" ] +stm32h742ai = [ "stm32-metapac/stm32h742ai" ] +stm32h742bg = [ "stm32-metapac/stm32h742bg" ] +stm32h742bi = [ "stm32-metapac/stm32h742bi" ] +stm32h742ig = [ "stm32-metapac/stm32h742ig" ] +stm32h742ii = [ "stm32-metapac/stm32h742ii" ] +stm32h742vg = [ "stm32-metapac/stm32h742vg" ] +stm32h742vi = [ "stm32-metapac/stm32h742vi" ] +stm32h742xg = [ "stm32-metapac/stm32h742xg" ] +stm32h742xi = [ "stm32-metapac/stm32h742xi" ] +stm32h742zg = [ "stm32-metapac/stm32h742zg" ] +stm32h742zi = [ "stm32-metapac/stm32h742zi" ] +stm32h743ag = [ "stm32-metapac/stm32h743ag" ] +stm32h743ai = [ "stm32-metapac/stm32h743ai" ] +stm32h743bg = [ "stm32-metapac/stm32h743bg" ] +stm32h743bi = [ "stm32-metapac/stm32h743bi" ] +stm32h743ig = [ "stm32-metapac/stm32h743ig" ] +stm32h743ii = [ "stm32-metapac/stm32h743ii" ] +stm32h743vg = [ "stm32-metapac/stm32h743vg" ] +stm32h743vi = [ "stm32-metapac/stm32h743vi" ] +stm32h743xg = [ "stm32-metapac/stm32h743xg" ] +stm32h743xi = [ "stm32-metapac/stm32h743xi" ] +stm32h743zg = [ "stm32-metapac/stm32h743zg" ] +stm32h743zi = [ "stm32-metapac/stm32h743zi" ] +stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7" ] +stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4" ] +stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7" ] +stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4" ] +stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7" ] +stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4" ] +stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7" ] +stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4" ] +stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7" ] +stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4" ] +stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7" ] +stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4" ] +stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7" ] +stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4" ] +stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7" ] +stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4" ] +stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7" ] +stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4" ] +stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7" ] +stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4" ] +stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7" ] +stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4" ] +stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7" ] +stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4" ] +stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7" ] +stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4" ] +stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7" ] +stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4" ] +stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7" ] +stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4" ] +stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7" ] +stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4" ] +stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7" ] +stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4" ] +stm32h750ib = [ "stm32-metapac/stm32h750ib" ] +stm32h750vb = [ "stm32-metapac/stm32h750vb" ] +stm32h750xb = [ "stm32-metapac/stm32h750xb" ] +stm32h750zb = [ "stm32-metapac/stm32h750zb" ] +stm32h753ai = [ "stm32-metapac/stm32h753ai" ] +stm32h753bi = [ "stm32-metapac/stm32h753bi" ] +stm32h753ii = [ "stm32-metapac/stm32h753ii" ] +stm32h753vi = [ "stm32-metapac/stm32h753vi" ] +stm32h753xi = [ "stm32-metapac/stm32h753xi" ] +stm32h753zi = [ "stm32-metapac/stm32h753zi" ] +stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7" ] +stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4" ] +stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7" ] +stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4" ] +stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7" ] +stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4" ] +stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7" ] +stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4" ] +stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7" ] +stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4" ] +stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7" ] +stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4" ] +stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7" ] +stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4" ] +stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7" ] +stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4" ] +stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7" ] +stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4" ] +stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] +stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] +stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] +stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii" ] +stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg" ] +stm32h7a3li = [ "stm32-metapac/stm32h7a3li" ] +stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng" ] +stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni" ] +stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi" ] +stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg" ] +stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri" ] +stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg" ] +stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi" ] +stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg" ] +stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi" ] +stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab" ] +stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib" ] +stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb" ] +stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb" ] +stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb" ] +stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai" ] +stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii" ] +stm32h7b3li = [ "stm32-metapac/stm32h7b3li" ] +stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni" ] +stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi" ] +stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri" ] +stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi" ] +stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi" ] +stm32l010c6 = [ "stm32-metapac/stm32l010c6" ] +stm32l010f4 = [ "stm32-metapac/stm32l010f4" ] +stm32l010k4 = [ "stm32-metapac/stm32l010k4" ] +stm32l010k8 = [ "stm32-metapac/stm32l010k8" ] +stm32l010r8 = [ "stm32-metapac/stm32l010r8" ] +stm32l010rb = [ "stm32-metapac/stm32l010rb" ] +stm32l011d3 = [ "stm32-metapac/stm32l011d3" ] +stm32l011d4 = [ "stm32-metapac/stm32l011d4" ] +stm32l011e3 = [ "stm32-metapac/stm32l011e3" ] +stm32l011e4 = [ "stm32-metapac/stm32l011e4" ] +stm32l011f3 = [ "stm32-metapac/stm32l011f3" ] +stm32l011f4 = [ "stm32-metapac/stm32l011f4" ] +stm32l011g3 = [ "stm32-metapac/stm32l011g3" ] +stm32l011g4 = [ "stm32-metapac/stm32l011g4" ] +stm32l011k3 = [ "stm32-metapac/stm32l011k3" ] +stm32l011k4 = [ "stm32-metapac/stm32l011k4" ] +stm32l021d4 = [ "stm32-metapac/stm32l021d4" ] +stm32l021f4 = [ "stm32-metapac/stm32l021f4" ] +stm32l021g4 = [ "stm32-metapac/stm32l021g4" ] +stm32l021k4 = [ "stm32-metapac/stm32l021k4" ] +stm32l031c4 = [ "stm32-metapac/stm32l031c4" ] +stm32l031c6 = [ "stm32-metapac/stm32l031c6" ] +stm32l031e4 = [ "stm32-metapac/stm32l031e4" ] +stm32l031e6 = [ "stm32-metapac/stm32l031e6" ] +stm32l031f4 = [ "stm32-metapac/stm32l031f4" ] +stm32l031f6 = [ "stm32-metapac/stm32l031f6" ] +stm32l031g4 = [ "stm32-metapac/stm32l031g4" ] +stm32l031g6 = [ "stm32-metapac/stm32l031g6" ] +stm32l031k4 = [ "stm32-metapac/stm32l031k4" ] +stm32l031k6 = [ "stm32-metapac/stm32l031k6" ] +stm32l041c4 = [ "stm32-metapac/stm32l041c4" ] +stm32l041c6 = [ "stm32-metapac/stm32l041c6" ] +stm32l041e6 = [ "stm32-metapac/stm32l041e6" ] +stm32l041f6 = [ "stm32-metapac/stm32l041f6" ] +stm32l041g6 = [ "stm32-metapac/stm32l041g6" ] +stm32l041k6 = [ "stm32-metapac/stm32l041k6" ] +stm32l051c6 = [ "stm32-metapac/stm32l051c6" ] +stm32l051c8 = [ "stm32-metapac/stm32l051c8" ] +stm32l051k6 = [ "stm32-metapac/stm32l051k6" ] +stm32l051k8 = [ "stm32-metapac/stm32l051k8" ] +stm32l051r6 = [ "stm32-metapac/stm32l051r6" ] +stm32l051r8 = [ "stm32-metapac/stm32l051r8" ] +stm32l051t6 = [ "stm32-metapac/stm32l051t6" ] +stm32l051t8 = [ "stm32-metapac/stm32l051t8" ] +stm32l052c6 = [ "stm32-metapac/stm32l052c6" ] +stm32l052c8 = [ "stm32-metapac/stm32l052c8" ] +stm32l052k6 = [ "stm32-metapac/stm32l052k6" ] +stm32l052k8 = [ "stm32-metapac/stm32l052k8" ] +stm32l052r6 = [ "stm32-metapac/stm32l052r6" ] +stm32l052r8 = [ "stm32-metapac/stm32l052r8" ] +stm32l052t6 = [ "stm32-metapac/stm32l052t6" ] +stm32l052t8 = [ "stm32-metapac/stm32l052t8" ] +stm32l053c6 = [ "stm32-metapac/stm32l053c6" ] +stm32l053c8 = [ "stm32-metapac/stm32l053c8" ] +stm32l053r6 = [ "stm32-metapac/stm32l053r6" ] +stm32l053r8 = [ "stm32-metapac/stm32l053r8" ] +stm32l062c8 = [ "stm32-metapac/stm32l062c8" ] +stm32l062k8 = [ "stm32-metapac/stm32l062k8" ] +stm32l063c8 = [ "stm32-metapac/stm32l063c8" ] +stm32l063r8 = [ "stm32-metapac/stm32l063r8" ] +stm32l071c8 = [ "stm32-metapac/stm32l071c8" ] +stm32l071cb = [ "stm32-metapac/stm32l071cb" ] +stm32l071cz = [ "stm32-metapac/stm32l071cz" ] +stm32l071k8 = [ "stm32-metapac/stm32l071k8" ] +stm32l071kb = [ "stm32-metapac/stm32l071kb" ] +stm32l071kz = [ "stm32-metapac/stm32l071kz" ] +stm32l071rb = [ "stm32-metapac/stm32l071rb" ] +stm32l071rz = [ "stm32-metapac/stm32l071rz" ] +stm32l071v8 = [ "stm32-metapac/stm32l071v8" ] +stm32l071vb = [ "stm32-metapac/stm32l071vb" ] +stm32l071vz = [ "stm32-metapac/stm32l071vz" ] +stm32l072cb = [ "stm32-metapac/stm32l072cb" ] +stm32l072cz = [ "stm32-metapac/stm32l072cz" ] +stm32l072kb = [ "stm32-metapac/stm32l072kb" ] +stm32l072kz = [ "stm32-metapac/stm32l072kz" ] +stm32l072rb = [ "stm32-metapac/stm32l072rb" ] +stm32l072rz = [ "stm32-metapac/stm32l072rz" ] +stm32l072v8 = [ "stm32-metapac/stm32l072v8" ] +stm32l072vb = [ "stm32-metapac/stm32l072vb" ] +stm32l072vz = [ "stm32-metapac/stm32l072vz" ] +stm32l073cb = [ "stm32-metapac/stm32l073cb" ] +stm32l073cz = [ "stm32-metapac/stm32l073cz" ] +stm32l073rb = [ "stm32-metapac/stm32l073rb" ] +stm32l073rz = [ "stm32-metapac/stm32l073rz" ] +stm32l073v8 = [ "stm32-metapac/stm32l073v8" ] +stm32l073vb = [ "stm32-metapac/stm32l073vb" ] +stm32l073vz = [ "stm32-metapac/stm32l073vz" ] +stm32l081cb = [ "stm32-metapac/stm32l081cb" ] +stm32l081cz = [ "stm32-metapac/stm32l081cz" ] +stm32l081kz = [ "stm32-metapac/stm32l081kz" ] +stm32l082cz = [ "stm32-metapac/stm32l082cz" ] +stm32l082kb = [ "stm32-metapac/stm32l082kb" ] +stm32l082kz = [ "stm32-metapac/stm32l082kz" ] +stm32l083cb = [ "stm32-metapac/stm32l083cb" ] +stm32l083cz = [ "stm32-metapac/stm32l083cz" ] +stm32l083rb = [ "stm32-metapac/stm32l083rb" ] +stm32l083rz = [ "stm32-metapac/stm32l083rz" ] +stm32l083v8 = [ "stm32-metapac/stm32l083v8" ] +stm32l083vb = [ "stm32-metapac/stm32l083vb" ] +stm32l083vz = [ "stm32-metapac/stm32l083vz" ] +stm32l100c6 = [ "stm32-metapac/stm32l100c6" ] +stm32l100c6-a = [ "stm32-metapac/stm32l100c6-a" ] +stm32l100r8 = [ "stm32-metapac/stm32l100r8" ] +stm32l100r8-a = [ "stm32-metapac/stm32l100r8-a" ] +stm32l100rb = [ "stm32-metapac/stm32l100rb" ] +stm32l100rb-a = [ "stm32-metapac/stm32l100rb-a" ] +stm32l100rc = [ "stm32-metapac/stm32l100rc" ] +stm32l151c6 = [ "stm32-metapac/stm32l151c6" ] +stm32l151c6-a = [ "stm32-metapac/stm32l151c6-a" ] +stm32l151c8 = [ "stm32-metapac/stm32l151c8" ] +stm32l151c8-a = [ "stm32-metapac/stm32l151c8-a" ] +stm32l151cb = [ "stm32-metapac/stm32l151cb" ] +stm32l151cb-a = [ "stm32-metapac/stm32l151cb-a" ] +stm32l151cc = [ "stm32-metapac/stm32l151cc" ] +stm32l151qc = [ "stm32-metapac/stm32l151qc" ] +stm32l151qd = [ "stm32-metapac/stm32l151qd" ] +stm32l151qe = [ "stm32-metapac/stm32l151qe" ] +stm32l151r6 = [ "stm32-metapac/stm32l151r6" ] +stm32l151r6-a = [ "stm32-metapac/stm32l151r6-a" ] +stm32l151r8 = [ "stm32-metapac/stm32l151r8" ] +stm32l151r8-a = [ "stm32-metapac/stm32l151r8-a" ] +stm32l151rb = [ "stm32-metapac/stm32l151rb" ] +stm32l151rb-a = [ "stm32-metapac/stm32l151rb-a" ] +stm32l151rc = [ "stm32-metapac/stm32l151rc" ] +stm32l151rc-a = [ "stm32-metapac/stm32l151rc-a" ] +stm32l151rd = [ "stm32-metapac/stm32l151rd" ] +stm32l151re = [ "stm32-metapac/stm32l151re" ] +stm32l151uc = [ "stm32-metapac/stm32l151uc" ] +stm32l151v8 = [ "stm32-metapac/stm32l151v8" ] +stm32l151v8-a = [ "stm32-metapac/stm32l151v8-a" ] +stm32l151vb = [ "stm32-metapac/stm32l151vb" ] +stm32l151vb-a = [ "stm32-metapac/stm32l151vb-a" ] +stm32l151vc = [ "stm32-metapac/stm32l151vc" ] +stm32l151vc-a = [ "stm32-metapac/stm32l151vc-a" ] +stm32l151vd = [ "stm32-metapac/stm32l151vd" ] +stm32l151vd-x = [ "stm32-metapac/stm32l151vd-x" ] +stm32l151ve = [ "stm32-metapac/stm32l151ve" ] +stm32l151zc = [ "stm32-metapac/stm32l151zc" ] +stm32l151zd = [ "stm32-metapac/stm32l151zd" ] +stm32l151ze = [ "stm32-metapac/stm32l151ze" ] +stm32l152c6 = [ "stm32-metapac/stm32l152c6" ] +stm32l152c6-a = [ "stm32-metapac/stm32l152c6-a" ] +stm32l152c8 = [ "stm32-metapac/stm32l152c8" ] +stm32l152c8-a = [ "stm32-metapac/stm32l152c8-a" ] +stm32l152cb = [ "stm32-metapac/stm32l152cb" ] +stm32l152cb-a = [ "stm32-metapac/stm32l152cb-a" ] +stm32l152cc = [ "stm32-metapac/stm32l152cc" ] +stm32l152qc = [ "stm32-metapac/stm32l152qc" ] +stm32l152qd = [ "stm32-metapac/stm32l152qd" ] +stm32l152qe = [ "stm32-metapac/stm32l152qe" ] +stm32l152r6 = [ "stm32-metapac/stm32l152r6" ] +stm32l152r6-a = [ "stm32-metapac/stm32l152r6-a" ] +stm32l152r8 = [ "stm32-metapac/stm32l152r8" ] +stm32l152r8-a = [ "stm32-metapac/stm32l152r8-a" ] +stm32l152rb = [ "stm32-metapac/stm32l152rb" ] +stm32l152rb-a = [ "stm32-metapac/stm32l152rb-a" ] +stm32l152rc = [ "stm32-metapac/stm32l152rc" ] +stm32l152rc-a = [ "stm32-metapac/stm32l152rc-a" ] +stm32l152rd = [ "stm32-metapac/stm32l152rd" ] +stm32l152re = [ "stm32-metapac/stm32l152re" ] +stm32l152uc = [ "stm32-metapac/stm32l152uc" ] +stm32l152v8 = [ "stm32-metapac/stm32l152v8" ] +stm32l152v8-a = [ "stm32-metapac/stm32l152v8-a" ] +stm32l152vb = [ "stm32-metapac/stm32l152vb" ] +stm32l152vb-a = [ "stm32-metapac/stm32l152vb-a" ] +stm32l152vc = [ "stm32-metapac/stm32l152vc" ] +stm32l152vc-a = [ "stm32-metapac/stm32l152vc-a" ] +stm32l152vd = [ "stm32-metapac/stm32l152vd" ] +stm32l152vd-x = [ "stm32-metapac/stm32l152vd-x" ] +stm32l152ve = [ "stm32-metapac/stm32l152ve" ] +stm32l152zc = [ "stm32-metapac/stm32l152zc" ] +stm32l152zd = [ "stm32-metapac/stm32l152zd" ] +stm32l152ze = [ "stm32-metapac/stm32l152ze" ] +stm32l162qc = [ "stm32-metapac/stm32l162qc" ] +stm32l162qd = [ "stm32-metapac/stm32l162qd" ] +stm32l162rc = [ "stm32-metapac/stm32l162rc" ] +stm32l162rc-a = [ "stm32-metapac/stm32l162rc-a" ] +stm32l162rd = [ "stm32-metapac/stm32l162rd" ] +stm32l162re = [ "stm32-metapac/stm32l162re" ] +stm32l162vc = [ "stm32-metapac/stm32l162vc" ] +stm32l162vc-a = [ "stm32-metapac/stm32l162vc-a" ] +stm32l162vd = [ "stm32-metapac/stm32l162vd" ] +stm32l162vd-x = [ "stm32-metapac/stm32l162vd-x" ] +stm32l162ve = [ "stm32-metapac/stm32l162ve" ] +stm32l162zc = [ "stm32-metapac/stm32l162zc" ] +stm32l162zd = [ "stm32-metapac/stm32l162zd" ] +stm32l162ze = [ "stm32-metapac/stm32l162ze" ] +stm32l412c8 = [ "stm32-metapac/stm32l412c8" ] +stm32l412cb = [ "stm32-metapac/stm32l412cb" ] +stm32l412k8 = [ "stm32-metapac/stm32l412k8" ] +stm32l412kb = [ "stm32-metapac/stm32l412kb" ] +stm32l412r8 = [ "stm32-metapac/stm32l412r8" ] +stm32l412rb = [ "stm32-metapac/stm32l412rb" ] +stm32l412t8 = [ "stm32-metapac/stm32l412t8" ] +stm32l412tb = [ "stm32-metapac/stm32l412tb" ] +stm32l422cb = [ "stm32-metapac/stm32l422cb" ] +stm32l422kb = [ "stm32-metapac/stm32l422kb" ] +stm32l422rb = [ "stm32-metapac/stm32l422rb" ] +stm32l422tb = [ "stm32-metapac/stm32l422tb" ] +stm32l431cb = [ "stm32-metapac/stm32l431cb" ] +stm32l431cc = [ "stm32-metapac/stm32l431cc" ] +stm32l431kb = [ "stm32-metapac/stm32l431kb" ] +stm32l431kc = [ "stm32-metapac/stm32l431kc" ] +stm32l431rb = [ "stm32-metapac/stm32l431rb" ] +stm32l431rc = [ "stm32-metapac/stm32l431rc" ] +stm32l431vc = [ "stm32-metapac/stm32l431vc" ] +stm32l432kb = [ "stm32-metapac/stm32l432kb" ] +stm32l432kc = [ "stm32-metapac/stm32l432kc" ] +stm32l433cb = [ "stm32-metapac/stm32l433cb" ] +stm32l433cc = [ "stm32-metapac/stm32l433cc" ] +stm32l433rb = [ "stm32-metapac/stm32l433rb" ] +stm32l433rc = [ "stm32-metapac/stm32l433rc" ] +stm32l433vc = [ "stm32-metapac/stm32l433vc" ] +stm32l442kc = [ "stm32-metapac/stm32l442kc" ] +stm32l443cc = [ "stm32-metapac/stm32l443cc" ] +stm32l443rc = [ "stm32-metapac/stm32l443rc" ] +stm32l443vc = [ "stm32-metapac/stm32l443vc" ] +stm32l451cc = [ "stm32-metapac/stm32l451cc" ] +stm32l451ce = [ "stm32-metapac/stm32l451ce" ] +stm32l451rc = [ "stm32-metapac/stm32l451rc" ] +stm32l451re = [ "stm32-metapac/stm32l451re" ] +stm32l451vc = [ "stm32-metapac/stm32l451vc" ] +stm32l451ve = [ "stm32-metapac/stm32l451ve" ] +stm32l452cc = [ "stm32-metapac/stm32l452cc" ] +stm32l452ce = [ "stm32-metapac/stm32l452ce" ] +stm32l452rc = [ "stm32-metapac/stm32l452rc" ] +stm32l452re = [ "stm32-metapac/stm32l452re" ] +stm32l452vc = [ "stm32-metapac/stm32l452vc" ] +stm32l452ve = [ "stm32-metapac/stm32l452ve" ] +stm32l462ce = [ "stm32-metapac/stm32l462ce" ] +stm32l462re = [ "stm32-metapac/stm32l462re" ] +stm32l462ve = [ "stm32-metapac/stm32l462ve" ] +stm32l471qe = [ "stm32-metapac/stm32l471qe" ] +stm32l471qg = [ "stm32-metapac/stm32l471qg" ] +stm32l471re = [ "stm32-metapac/stm32l471re" ] +stm32l471rg = [ "stm32-metapac/stm32l471rg" ] +stm32l471ve = [ "stm32-metapac/stm32l471ve" ] +stm32l471vg = [ "stm32-metapac/stm32l471vg" ] +stm32l471ze = [ "stm32-metapac/stm32l471ze" ] +stm32l471zg = [ "stm32-metapac/stm32l471zg" ] +stm32l475rc = [ "stm32-metapac/stm32l475rc" ] +stm32l475re = [ "stm32-metapac/stm32l475re" ] +stm32l475rg = [ "stm32-metapac/stm32l475rg" ] +stm32l475vc = [ "stm32-metapac/stm32l475vc" ] +stm32l475ve = [ "stm32-metapac/stm32l475ve" ] +stm32l475vg = [ "stm32-metapac/stm32l475vg" ] +stm32l476je = [ "stm32-metapac/stm32l476je" ] +stm32l476jg = [ "stm32-metapac/stm32l476jg" ] +stm32l476me = [ "stm32-metapac/stm32l476me" ] +stm32l476mg = [ "stm32-metapac/stm32l476mg" ] +stm32l476qe = [ "stm32-metapac/stm32l476qe" ] +stm32l476qg = [ "stm32-metapac/stm32l476qg" ] +stm32l476rc = [ "stm32-metapac/stm32l476rc" ] +stm32l476re = [ "stm32-metapac/stm32l476re" ] +stm32l476rg = [ "stm32-metapac/stm32l476rg" ] +stm32l476vc = [ "stm32-metapac/stm32l476vc" ] +stm32l476ve = [ "stm32-metapac/stm32l476ve" ] +stm32l476vg = [ "stm32-metapac/stm32l476vg" ] +stm32l476ze = [ "stm32-metapac/stm32l476ze" ] +stm32l476zg = [ "stm32-metapac/stm32l476zg" ] +stm32l486jg = [ "stm32-metapac/stm32l486jg" ] +stm32l486qg = [ "stm32-metapac/stm32l486qg" ] +stm32l486rg = [ "stm32-metapac/stm32l486rg" ] +stm32l486vg = [ "stm32-metapac/stm32l486vg" ] +stm32l486zg = [ "stm32-metapac/stm32l486zg" ] +stm32l496ae = [ "stm32-metapac/stm32l496ae" ] +stm32l496ag = [ "stm32-metapac/stm32l496ag" ] +stm32l496qe = [ "stm32-metapac/stm32l496qe" ] +stm32l496qg = [ "stm32-metapac/stm32l496qg" ] +stm32l496re = [ "stm32-metapac/stm32l496re" ] +stm32l496rg = [ "stm32-metapac/stm32l496rg" ] +stm32l496ve = [ "stm32-metapac/stm32l496ve" ] +stm32l496vg = [ "stm32-metapac/stm32l496vg" ] +stm32l496wg = [ "stm32-metapac/stm32l496wg" ] +stm32l496ze = [ "stm32-metapac/stm32l496ze" ] +stm32l496zg = [ "stm32-metapac/stm32l496zg" ] +stm32l4a6ag = [ "stm32-metapac/stm32l4a6ag" ] +stm32l4a6qg = [ "stm32-metapac/stm32l4a6qg" ] +stm32l4a6rg = [ "stm32-metapac/stm32l4a6rg" ] +stm32l4a6vg = [ "stm32-metapac/stm32l4a6vg" ] +stm32l4a6zg = [ "stm32-metapac/stm32l4a6zg" ] +stm32l4p5ae = [ "stm32-metapac/stm32l4p5ae" ] +stm32l4p5ag = [ "stm32-metapac/stm32l4p5ag" ] +stm32l4p5ce = [ "stm32-metapac/stm32l4p5ce" ] +stm32l4p5cg = [ "stm32-metapac/stm32l4p5cg" ] +stm32l4p5qe = [ "stm32-metapac/stm32l4p5qe" ] +stm32l4p5qg = [ "stm32-metapac/stm32l4p5qg" ] +stm32l4p5re = [ "stm32-metapac/stm32l4p5re" ] +stm32l4p5rg = [ "stm32-metapac/stm32l4p5rg" ] +stm32l4p5ve = [ "stm32-metapac/stm32l4p5ve" ] +stm32l4p5vg = [ "stm32-metapac/stm32l4p5vg" ] +stm32l4p5ze = [ "stm32-metapac/stm32l4p5ze" ] +stm32l4p5zg = [ "stm32-metapac/stm32l4p5zg" ] +stm32l4q5ag = [ "stm32-metapac/stm32l4q5ag" ] +stm32l4q5cg = [ "stm32-metapac/stm32l4q5cg" ] +stm32l4q5qg = [ "stm32-metapac/stm32l4q5qg" ] +stm32l4q5rg = [ "stm32-metapac/stm32l4q5rg" ] +stm32l4q5vg = [ "stm32-metapac/stm32l4q5vg" ] +stm32l4q5zg = [ "stm32-metapac/stm32l4q5zg" ] +stm32l4r5ag = [ "stm32-metapac/stm32l4r5ag" ] +stm32l4r5ai = [ "stm32-metapac/stm32l4r5ai" ] +stm32l4r5qg = [ "stm32-metapac/stm32l4r5qg" ] +stm32l4r5qi = [ "stm32-metapac/stm32l4r5qi" ] +stm32l4r5vg = [ "stm32-metapac/stm32l4r5vg" ] +stm32l4r5vi = [ "stm32-metapac/stm32l4r5vi" ] +stm32l4r5zg = [ "stm32-metapac/stm32l4r5zg" ] +stm32l4r5zi = [ "stm32-metapac/stm32l4r5zi" ] +stm32l4r7ai = [ "stm32-metapac/stm32l4r7ai" ] +stm32l4r7vi = [ "stm32-metapac/stm32l4r7vi" ] +stm32l4r7zi = [ "stm32-metapac/stm32l4r7zi" ] +stm32l4r9ag = [ "stm32-metapac/stm32l4r9ag" ] +stm32l4r9ai = [ "stm32-metapac/stm32l4r9ai" ] +stm32l4r9vg = [ "stm32-metapac/stm32l4r9vg" ] +stm32l4r9vi = [ "stm32-metapac/stm32l4r9vi" ] +stm32l4r9zg = [ "stm32-metapac/stm32l4r9zg" ] +stm32l4r9zi = [ "stm32-metapac/stm32l4r9zi" ] +stm32l4s5ai = [ "stm32-metapac/stm32l4s5ai" ] +stm32l4s5qi = [ "stm32-metapac/stm32l4s5qi" ] +stm32l4s5vi = [ "stm32-metapac/stm32l4s5vi" ] +stm32l4s5zi = [ "stm32-metapac/stm32l4s5zi" ] +stm32l4s7ai = [ "stm32-metapac/stm32l4s7ai" ] +stm32l4s7vi = [ "stm32-metapac/stm32l4s7vi" ] +stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ] +stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] +stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] +stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] +stm32l552cc = [ "stm32-metapac/stm32l552cc" ] +stm32l552ce = [ "stm32-metapac/stm32l552ce" ] +stm32l552me = [ "stm32-metapac/stm32l552me" ] +stm32l552qc = [ "stm32-metapac/stm32l552qc" ] +stm32l552qe = [ "stm32-metapac/stm32l552qe" ] +stm32l552rc = [ "stm32-metapac/stm32l552rc" ] +stm32l552re = [ "stm32-metapac/stm32l552re" ] +stm32l552vc = [ "stm32-metapac/stm32l552vc" ] +stm32l552ve = [ "stm32-metapac/stm32l552ve" ] +stm32l552zc = [ "stm32-metapac/stm32l552zc" ] +stm32l552ze = [ "stm32-metapac/stm32l552ze" ] +stm32l562ce = [ "stm32-metapac/stm32l562ce" ] +stm32l562me = [ "stm32-metapac/stm32l562me" ] +stm32l562qe = [ "stm32-metapac/stm32l562qe" ] +stm32l562re = [ "stm32-metapac/stm32l562re" ] +stm32l562ve = [ "stm32-metapac/stm32l562ve" ] +stm32l562ze = [ "stm32-metapac/stm32l562ze" ] +stm32u535cb = [ "stm32-metapac/stm32u535cb" ] +stm32u535cc = [ "stm32-metapac/stm32u535cc" ] +stm32u535ce = [ "stm32-metapac/stm32u535ce" ] +stm32u535je = [ "stm32-metapac/stm32u535je" ] +stm32u535nc = [ "stm32-metapac/stm32u535nc" ] +stm32u535ne = [ "stm32-metapac/stm32u535ne" ] +stm32u535rb = [ "stm32-metapac/stm32u535rb" ] +stm32u535rc = [ "stm32-metapac/stm32u535rc" ] +stm32u535re = [ "stm32-metapac/stm32u535re" ] +stm32u535vc = [ "stm32-metapac/stm32u535vc" ] +stm32u535ve = [ "stm32-metapac/stm32u535ve" ] +stm32u545ce = [ "stm32-metapac/stm32u545ce" ] +stm32u545je = [ "stm32-metapac/stm32u545je" ] +stm32u545ne = [ "stm32-metapac/stm32u545ne" ] +stm32u545re = [ "stm32-metapac/stm32u545re" ] +stm32u545ve = [ "stm32-metapac/stm32u545ve" ] +stm32u575ag = [ "stm32-metapac/stm32u575ag" ] +stm32u575ai = [ "stm32-metapac/stm32u575ai" ] +stm32u575cg = [ "stm32-metapac/stm32u575cg" ] +stm32u575ci = [ "stm32-metapac/stm32u575ci" ] +stm32u575og = [ "stm32-metapac/stm32u575og" ] +stm32u575oi = [ "stm32-metapac/stm32u575oi" ] +stm32u575qg = [ "stm32-metapac/stm32u575qg" ] +stm32u575qi = [ "stm32-metapac/stm32u575qi" ] +stm32u575rg = [ "stm32-metapac/stm32u575rg" ] +stm32u575ri = [ "stm32-metapac/stm32u575ri" ] +stm32u575vg = [ "stm32-metapac/stm32u575vg" ] +stm32u575vi = [ "stm32-metapac/stm32u575vi" ] +stm32u575zg = [ "stm32-metapac/stm32u575zg" ] +stm32u575zi = [ "stm32-metapac/stm32u575zi" ] +stm32u585ai = [ "stm32-metapac/stm32u585ai" ] +stm32u585ci = [ "stm32-metapac/stm32u585ci" ] +stm32u585oi = [ "stm32-metapac/stm32u585oi" ] +stm32u585qi = [ "stm32-metapac/stm32u585qi" ] +stm32u585ri = [ "stm32-metapac/stm32u585ri" ] +stm32u585vi = [ "stm32-metapac/stm32u585vi" ] +stm32u585zi = [ "stm32-metapac/stm32u585zi" ] +stm32u595ai = [ "stm32-metapac/stm32u595ai" ] +stm32u595aj = [ "stm32-metapac/stm32u595aj" ] +stm32u595qi = [ "stm32-metapac/stm32u595qi" ] +stm32u595qj = [ "stm32-metapac/stm32u595qj" ] +stm32u595ri = [ "stm32-metapac/stm32u595ri" ] +stm32u595rj = [ "stm32-metapac/stm32u595rj" ] +stm32u595vi = [ "stm32-metapac/stm32u595vi" ] +stm32u595vj = [ "stm32-metapac/stm32u595vj" ] +stm32u595zi = [ "stm32-metapac/stm32u595zi" ] +stm32u595zj = [ "stm32-metapac/stm32u595zj" ] +stm32u599bj = [ "stm32-metapac/stm32u599bj" ] +stm32u599ni = [ "stm32-metapac/stm32u599ni" ] +stm32u599nj = [ "stm32-metapac/stm32u599nj" ] +stm32u599vi = [ "stm32-metapac/stm32u599vi" ] +stm32u599vj = [ "stm32-metapac/stm32u599vj" ] +stm32u599zi = [ "stm32-metapac/stm32u599zi" ] +stm32u599zj = [ "stm32-metapac/stm32u599zj" ] +stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] +stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] +stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] +stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] +stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] +stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] +stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] +stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] +stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] +stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] +stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] +stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] +stm32wb35cc = [ "stm32-metapac/stm32wb35cc" ] +stm32wb35ce = [ "stm32-metapac/stm32wb35ce" ] +stm32wb50cg = [ "stm32-metapac/stm32wb50cg" ] +stm32wb55cc = [ "stm32-metapac/stm32wb55cc" ] +stm32wb55ce = [ "stm32-metapac/stm32wb55ce" ] +stm32wb55cg = [ "stm32-metapac/stm32wb55cg" ] +stm32wb55rc = [ "stm32-metapac/stm32wb55rc" ] +stm32wb55re = [ "stm32-metapac/stm32wb55re" ] +stm32wb55rg = [ "stm32-metapac/stm32wb55rg" ] +stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ] +stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ] +stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ] +stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ] +stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ] +stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ] +stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ] +stm32wl54jc-cm0p = [ "stm32-metapac/stm32wl54jc-cm0p" ] +stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4" ] +stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p" ] +stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4" ] +stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p" ] +stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] +stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] +stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] +stm32wle4j8 = [ "stm32-metapac/stm32wle4j8" ] +stm32wle4jb = [ "stm32-metapac/stm32wle4jb" ] +stm32wle4jc = [ "stm32-metapac/stm32wle4jc" ] +stm32wle5c8 = [ "stm32-metapac/stm32wle5c8" ] +stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] +stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] +stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] +stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] +stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] From 4d551a586589b9fd8b5f92d2f98b90be4144154e Mon Sep 17 00:00:00 2001 From: kalkyl Date: Thu, 27 Apr 2023 19:37:19 +0200 Subject: [PATCH 0964/1575] Update embassy --- Cargo.toml | 12 ++++++------ cyw43-pio/Cargo.toml | 2 +- examples/rpi-pico-w/Cargo.toml | 18 +++++++++--------- rust-toolchain.toml | 2 +- src/ioctl.rs | 10 +++++++++- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ea5d8b6f..c4872f423 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ atomic-polyfill = "0.1.5" defmt = { version = "0.3", optional = true } log = { version = "0.4.17", optional = true } -cortex-m = "0.7.3" +cortex-m = "0.7.6" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } @@ -28,11 +28,11 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } num_enum = { version = "0.5.7", default-features = false } [patch.crates-io] -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } [workspace] members = ["cyw43-pio"] diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 2fc6b7591..4ca227d3d 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] cyw43 = { path = "../" } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } pio-proc = "0.2" pio = "0.2.1" defmt = "0.3" \ No newline at end of file diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index dca796e3d..970db089d 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -9,7 +9,7 @@ cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio" } embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } atomic-polyfill = "0.1.5" static_cell = "1.0" @@ -27,14 +27,14 @@ heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "047ea9066f0d946fd4d706577b21df38fd3b1647" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } [profile.dev] debug = 2 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 885199792..2582e88f5 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-2023-04-02" +channel = "nightly-2023-04-18" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", diff --git a/src/ioctl.rs b/src/ioctl.rs index 89b20a2d6..0fee1ad19 100644 --- a/src/ioctl.rs +++ b/src/ioctl.rs @@ -25,12 +25,20 @@ enum IoctlStateInner { Done { resp_len: usize }, } -#[derive(Default)] struct Wakers { control: WakerRegistration, runner: WakerRegistration, } +impl Default for Wakers { + fn default() -> Self { + Self { + control: WakerRegistration::new(), + runner: WakerRegistration::new(), + } + } +} + pub struct IoctlState { state: Cell, wakers: RefCell, From 28a34548464d3d8646fc8dbe65d10e52c02cc56c Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 27 Apr 2023 20:19:07 +0200 Subject: [PATCH 0965/1575] add changelog --- embassy-executor/CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 embassy-executor/CHANGELOG.md diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md new file mode 100644 index 000000000..4fd3dccf7 --- /dev/null +++ b/embassy-executor/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.2.0 - 2023-04-27 + +- Replace unnecessary atomics in runqueue +- add Pender, rework Cargo features. +- add support for turbo-wakers. +- Allow TaskStorage to auto-implement `Sync` +- Use AtomicPtr for signal_ctx, removes 1 unsafe. +- Replace unsound critical sections with atomics + +## 0.1.1 - 2022-11-23 + +- Fix features for documentation + +## 0.1.0 - 2022-11-23 + +- First release From 29cc661dca449e404fa37ba4d8aff3580eb6fe78 Mon Sep 17 00:00:00 2001 From: OueslatiGhaith Date: Fri, 28 Apr 2023 10:17:01 +0100 Subject: [PATCH 0966/1575] removed constrain method --- embassy-stm32/src/ipcc.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index a54cf8faf..903aeca30 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -170,16 +170,6 @@ impl sealed::Instance for crate::peripherals::IPCC { } } -/// extension trait that constrains the [`Ipcc`] peripheral -pub trait IpccExt<'d> { - fn constrain(self) -> Ipcc<'d>; -} -impl<'d> IpccExt<'d> for IPCC { - fn constrain(self) -> Ipcc<'d> { - Ipcc { _peri: self.into_ref() } - } -} - unsafe fn _configure_pwr() { let rcc = crate::pac::RCC; From 49ecd8d7c554334dc265840723b8548a1b8b8e98 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Fri, 28 Apr 2023 13:33:20 -0500 Subject: [PATCH 0967/1575] Remove external-lora-phy feature. --- embassy-lora/Cargo.toml | 11 +++-------- embassy-lora/src/lib.rs | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- 6 files changed, 8 insertions(+), 13 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index aa0d2911d..e62ff431f 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -7,18 +7,13 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" -features = ["time", "defmt"] -flavors = [ - { name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/time-driver-any"] }, -] - -[lib] +features = ["stm32wl", "time", "defmt"] +target = "thumbv7em-none-eabi" [features] stm32wl = ["dep:embassy-stm32"] time = [] defmt = ["dep:defmt", "lorawan-device/defmt"] -external-lora-phy = ["dep:lora-phy"] [dependencies] @@ -35,5 +30,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lora-phy = { version = "1", optional = true } +lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index c391a8029..3facee6f3 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -4,7 +4,7 @@ //! embassy-lora holds LoRa-specific functionality. pub(crate) mod fmt; -#[cfg(feature = "external-lora-phy")] + /// interface variants required by the external lora physical layer crate (lora-phy) pub mod iv; diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 77be46ffd..7c701c157 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -18,7 +18,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embedded-io = "0.4.0" -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 7f65a00d7..fd4cdcf05 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -15,7 +15,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"] } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index e9d2127c5..ce8dfffc5 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -14,7 +14,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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 = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt", "external-lora-phy"], optional = true } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 0eb24bc44..759008596 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } -embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt", "external-lora-phy"] } +embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } From 2119b8e1ca34c4771fae88cf6684713e66d70ba4 Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Fri, 28 Apr 2023 21:23:32 +0200 Subject: [PATCH 0968/1575] Add `Transactional` trait to rp's i2c impl --- embassy-rp/src/i2c.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 40e85c66f..d1df671ed 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -617,6 +617,22 @@ mod eh02 { self.blocking_write_read(address, bytes, buffer) } } + + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> { + type Error = Error; + + fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<(), Self::Error> { + Self::setup(address.into())?; + for i in 0..operations.len() { + let last = i == operations.len() - 1; + match &mut operations[i] { + embedded_hal_02::blocking::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, + embedded_hal_02::blocking::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, + } + } + Ok(()) + } + } } #[cfg(feature = "unstable-traits")] From 2c5d94493c25792435102680fe8e659cc7dad9df Mon Sep 17 00:00:00 2001 From: kbleeke Date: Thu, 30 Mar 2023 17:05:29 +0200 Subject: [PATCH 0969/1575] wifi scan ioctl --- examples/rpi-pico-w/src/main.rs | 1 - src/control.rs | 67 +++++++++++++++++++++++++-- src/events.rs | 11 +++-- src/lib.rs | 1 + src/runner.rs | 14 +++++- src/structs.rs | 81 +++++++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 10 deletions(-) diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/main.rs index d075aec2a..944beaac0 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/main.rs @@ -143,4 +143,3 @@ async fn main(spawner: Spawner) { } } } - diff --git a/src/control.rs b/src/control.rs index 0c06009b9..bcb449375 100644 --- a/src/control.rs +++ b/src/control.rs @@ -6,11 +6,11 @@ use embassy_time::{Duration, Timer}; pub use crate::bus::SpiBusCyw43; use crate::consts::*; -use crate::events::{Event, Events}; +use crate::events::{Event, EventSubscriber, Events}; use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType}; use crate::structs::*; -use crate::{countries, PowerManagementMode}; +use crate::{countries, events, PowerManagementMode}; pub struct Control<'a> { state_ch: ch::StateRunner<'a>, @@ -245,9 +245,13 @@ impl<'a> Control<'a> { } async fn set_iovar(&mut self, name: &str, val: &[u8]) { + self.set_iovar_v::<64>(name, val).await + } + + async fn set_iovar_v(&mut self, name: &str, val: &[u8]) { info!("set {} = {:02x}", name, Bytes(val)); - let mut buf = [0; 64]; + let mut buf = [0; BUFSIZE]; buf[..name.len()].copy_from_slice(name.as_bytes()); buf[name.len()] = 0; buf[name.len() + 1..][..val.len()].copy_from_slice(val); @@ -304,4 +308,61 @@ impl<'a> Control<'a> { resp_len } + + pub async fn scan(&mut self) -> Scanner<'_> { + const SCANTYPE_PASSIVE: u8 = 1; + + let scan_params = ScanParams { + version: 1, + action: 1, + sync_id: 1, + ssid_len: 0, + ssid: [0; 32], + bssid: [0xff; 6], + bss_type: 2, + scan_type: SCANTYPE_PASSIVE, + nprobes: !0, + active_time: !0, + passive_time: !0, + home_time: !0, + channel_num: 0, + channel_list: [0; 1], + }; + + self.events.mask.enable(&[Event::ESCAN_RESULT]); + let subscriber = self.events.queue.subscriber().unwrap(); + self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await; + + Scanner { + subscriber, + events: &self.events, + } + } +} + +pub struct Scanner<'a> { + subscriber: EventSubscriber<'a>, + events: &'a Events, +} + +impl Scanner<'_> { + pub async fn next(&mut self) -> Option { + let event = self.subscriber.next_message_pure().await; + if event.header.status != EStatus::PARTIAL { + self.events.mask.disable_all(); + return None; + } + + if let events::Payload::BssInfo(bss) = event.payload { + Some(bss) + } else { + None + } + } +} + +impl Drop for Scanner<'_> { + fn drop(&mut self) { + self.events.mask.disable_all(); + } } diff --git a/src/events.rs b/src/events.rs index d6f114ed9..a94c49a0c 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,10 +1,12 @@ -#![allow(unused)] +#![allow(dead_code)] #![allow(non_camel_case_types)] use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; +use embassy_sync::pubsub::{PubSubChannel, Subscriber}; + +use crate::structs::BssInfo; #[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -286,7 +288,6 @@ pub enum Event { // TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. pub type EventQueue = PubSubChannel; -pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, Message, 2, 1, 1>; pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; pub struct Events { @@ -313,6 +314,7 @@ pub struct Status { #[derive(Clone, Copy)] pub enum Payload { None, + BssInfo(BssInfo), } #[derive(Clone, Copy)] @@ -344,7 +346,7 @@ impl EventMask { let word = n / u32::BITS; let bit = n % u32::BITS; - self.mask[word as usize] |= (1 << bit); + self.mask[word as usize] |= 1 << bit; } fn disable(&mut self, event: Event) { @@ -378,6 +380,7 @@ impl SharedEventMask { } } + #[allow(dead_code)] pub fn disable(&self, events: &[Event]) { let mut mask = self.mask.borrow_mut(); for event in events { diff --git a/src/lib.rs b/src/lib.rs index f9244bddb..d437a882e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; pub use crate::control::Control; pub use crate::runner::Runner; +pub use crate::structs::BssInfo; const MTU: usize = 1514; diff --git a/src/runner.rs b/src/runner.rs index 806ddfc49..9b99e174f 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -7,7 +7,7 @@ use embedded_hal_1::digital::OutputPin; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; use crate::consts::*; -use crate::events::{Events, Status}; +use crate::events::{Event, Events, Status}; use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; use crate::nvram::NVRAM; @@ -351,6 +351,8 @@ where panic!("IOCTL error {}", cdc_header.status as i32); } + info!("IOCTL Response: {:02x}", Bytes(response)); + self.ioctl_state.ioctl_done(response); } } @@ -404,7 +406,15 @@ where if self.events.mask.is_enabled(evt_type) { let status = event_packet.msg.status; - let event_payload = events::Payload::None; + let event_payload = match evt_type { + Event::ESCAN_RESULT if status == EStatus::PARTIAL => { + let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return }; + let Some(bss_info) = BssInfo::parse(bss_info) else { return }; + events::Payload::BssInfo(*bss_info) + } + Event::ESCAN_RESULT => events::Payload::None, + _ => events::Payload::None, + }; // this intentionally uses the non-blocking publish immediate // publish() is a deadlock risk in the current design as awaiting here prevents ioctls diff --git a/src/structs.rs b/src/structs.rs index f54ec7fcf..d01d5a65c 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -404,3 +404,84 @@ impl EventMask { self.events[evt / 8] &= !(1 << (evt % 8)); } } + +/// Parameters for a wifi scan +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct ScanParams { + pub version: u32, + pub action: u16, + pub sync_id: u16, + pub ssid_len: u32, + pub ssid: [u8; 32], + pub bssid: [u8; 6], + pub bss_type: u8, + pub scan_type: u8, + pub nprobes: u32, + pub active_time: u32, + pub passive_time: u32, + pub home_time: u32, + pub channel_num: u32, + pub channel_list: [u16; 1], +} +impl_bytes!(ScanParams); + +/// Wifi Scan Results Header, followed by `bss_count` `BssInfo` +#[derive(Clone, Copy)] +// #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C, packed(2))] +pub struct ScanResults { + pub buflen: u32, + pub version: u32, + pub sync_id: u16, + pub bss_count: u16, +} +impl_bytes!(ScanResults); + +impl ScanResults { + pub fn parse(packet: &mut [u8]) -> Option<(&mut ScanResults, &mut [u8])> { + if packet.len() < ScanResults::SIZE { + return None; + } + + let (scan_results, bssinfo) = packet.split_at_mut(ScanResults::SIZE); + let scan_results = ScanResults::from_bytes_mut(scan_results.try_into().unwrap()); + + if scan_results.bss_count > 0 && bssinfo.len() < BssInfo::SIZE { + warn!("Scan result, incomplete BssInfo"); + return None; + } + + Some((scan_results, bssinfo)) + } +} + +/// Wifi Scan Result +#[derive(Clone, Copy)] +// #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C, packed(2))] +#[non_exhaustive] +pub struct BssInfo { + pub version: u32, + pub length: u32, + pub bssid: [u8; 6], + pub beacon_period: u16, + pub capability: u16, + pub ssid_len: u8, + pub ssid: [u8; 32], + // there will be more stuff here +} +impl_bytes!(BssInfo); + +impl BssInfo { + pub fn parse(packet: &mut [u8]) -> Option<&mut Self> { + if packet.len() < BssInfo::SIZE { + return None; + } + + Some(BssInfo::from_bytes_mut( + packet[..BssInfo::SIZE].as_mut().try_into().unwrap(), + )) + } +} From 76b967a966677e570cc0a2942ed3ccd29b2d1017 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Fri, 28 Apr 2023 21:17:13 +0200 Subject: [PATCH 0970/1575] comment wifi scanning items --- src/control.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/control.rs b/src/control.rs index bcb449375..934bade23 100644 --- a/src/control.rs +++ b/src/control.rs @@ -309,6 +309,13 @@ impl<'a> Control<'a> { resp_len } + /// Start a wifi scan + /// + /// Returns a `Stream` of networks found by the device + /// + /// # Note + /// Device events are currently implemented using a bounded queue. + /// To not miss any events, you should make sure to always await the stream. pub async fn scan(&mut self) -> Scanner<'_> { const SCANTYPE_PASSIVE: u8 = 1; @@ -346,6 +353,7 @@ pub struct Scanner<'a> { } impl Scanner<'_> { + /// wait for the next found network pub async fn next(&mut self) -> Option { let event = self.subscriber.next_message_pure().await; if event.header.status != EStatus::PARTIAL { From ba886b45b8a78d4fd3f2442607d515fc024eff1b Mon Sep 17 00:00:00 2001 From: xoviat <49173759+xoviat@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:46:32 -0500 Subject: [PATCH 0971/1575] rustfmt --- embassy-rp/src/i2c.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d1df671ed..cd5364393 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -617,16 +617,22 @@ mod eh02 { self.blocking_write_read(address, bytes, buffer) } } - + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, T, M> { type Error = Error; - - fn exec(&mut self, address: u8, operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>]) -> Result<(), Self::Error> { + + fn exec( + &mut self, + address: u8, + operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { Self::setup(address.into())?; for i in 0..operations.len() { let last = i == operations.len() - 1; match &mut operations[i] { - embedded_hal_02::blocking::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?, + embedded_hal_02::blocking::i2c::Operation::Read(buf) => { + self.read_blocking_internal(buf, false, last)? + } embedded_hal_02::blocking::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, } } From b77794c9a70d35ea027454a678ac24e215a62c13 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Apr 2023 21:43:03 -0500 Subject: [PATCH 0972/1575] stm32/uart: abort on error --- embassy-stm32/src/usart/mod.rs | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index b8656b586..266561659 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -497,28 +497,24 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { unreachable!(); } - if !enable_idle_line_detection { - transfer.await; + if enable_idle_line_detection { + // clear idle flag + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); - return Ok(ReadCompletionEvent::DmaCompleted); + // enable idle interrupt + r.cr1().modify(|w| { + w.set_idleie(true); + }); } - - // clear idle flag - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); - - // enable idle interrupt - r.cr1().modify(|w| { - w.set_idleie(true); - }); } compiler_fence(Ordering::SeqCst); - // future which completes when idle line is detected - let idle = poll_fn(move |cx| { + // future which completes when idle line or error is detected + let abort = poll_fn(move |cx| { let s = T::state(); s.rx_waker.register(cx.waker()); @@ -554,7 +550,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } - if sr.idle() { + if enable_idle_line_detection && sr.idle() { // Idle line detected return Poll::Ready(Ok(())); } @@ -565,7 +561,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // wait for the first of DMA request or idle line detected to completes // select consumes its arguments // when transfer is dropped, it will stop the DMA request - let r = match select(transfer, idle).await { + let r = match select(transfer, abort).await { // DMA transfer completed first Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted), From 099ec7443bed2183397005b4e8ebfcd2492e2b4c Mon Sep 17 00:00:00 2001 From: Satoshi Tanaka Date: Mon, 1 May 2023 04:30:21 +0900 Subject: [PATCH 0973/1575] Add AP mode (unencrypted) --- src/consts.rs | 3 +++ src/control.rs | 37 +++++++++++++++++++++++++++++++++++++ src/structs.rs | 9 +++++++++ 3 files changed, 49 insertions(+) diff --git a/src/consts.rs b/src/consts.rs index 18502bd1a..ade3cb2c5 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -93,8 +93,11 @@ pub(crate) const IRQ_F2_INTR: u16 = 0x4000; pub(crate) const IRQ_F3_INTR: u16 = 0x8000; pub(crate) const IOCTL_CMD_UP: u32 = 2; +pub(crate) const IOCTL_CMD_DOWN: u32 = 3; pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; +pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30; pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64; +pub(crate) const IOCTL_CMD_SET_AP: u32 = 118; pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262; pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; diff --git a/src/control.rs b/src/control.rs index 934bade23..440246498 100644 --- a/src/control.rs +++ b/src/control.rs @@ -226,6 +226,43 @@ impl<'a> Control<'a> { .await } + pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) { + // Temporarily set wifi down + self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; + + // Turn off APSTA mode + self.set_iovar_u32("apsta", 0).await; + + // Set wifi up again + self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + + // Turn on AP mode + self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; + + // Set SSID + let mut i = SsidInfoWithIndex { + index: 0, + ssid_info: SsidInfo { + len: ssid.as_bytes().len() as _, + ssid: [0; 32], + }, + }; + i.ssid_info.ssid[..ssid.as_bytes().len()].copy_from_slice(ssid.as_bytes()); + self.set_iovar("bsscfg:ssid", &i.to_bytes()).await; + + // Set channel number + self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await; + + // Set security + self.set_iovar_u32x2("bsscfg:wsec", 0, 0).await; // wsec = open + + // Change mutlicast rate from 1 Mbps to 11 Mbps + self.set_iovar_u32("2g_mrate", 11000000 / 500000).await; + + // Start AP + self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP + } + async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { let mut buf = [0; 8]; buf[0..4].copy_from_slice(&val1.to_le_bytes()); diff --git a/src/structs.rs b/src/structs.rs index d01d5a65c..3b646e1a8 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -389,6 +389,15 @@ pub struct PassphraseInfo { } impl_bytes!(PassphraseInfo); +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C)] +pub struct SsidInfoWithIndex { + pub index: u32, + pub ssid_info: SsidInfo, +} +impl_bytes!(SsidInfoWithIndex); + #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] From af368676ef50317c1b97b5f2134dd86503b01124 Mon Sep 17 00:00:00 2001 From: Daniel Larsen <44644910+daniel-larsen@users.noreply.github.com> Date: Sun, 30 Apr 2023 18:02:44 -0300 Subject: [PATCH 0974/1575] Removed defmt --- cyw43-pio/Cargo.toml | 5 +++-- cyw43-pio/src/lib.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 4ca227d3d..dbb94f612 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -7,7 +7,8 @@ edition = "2021" [dependencies] cyw43 = { path = "../" } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } +embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] } +# embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } pio-proc = "0.2" pio = "0.2.1" -defmt = "0.3" \ No newline at end of file +# defmt = "0.3" \ No newline at end of file diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index 9c425cbaa..f67a55a08 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -129,7 +129,7 @@ where let write_bits = write.len() * 32 - 1; let read_bits = 31; - defmt::trace!("write={} read={}", write_bits, read_bits); + // defmt::trace!("write={} read={}", write_bits, read_bits); let mut dma = Peripheral::into_ref(&mut self.dma); pio_instr_util::set_x(&mut self.sm, write_bits as u32); @@ -151,7 +151,7 @@ where let write_bits = 31; let read_bits = read.len() * 32 + 32 - 1; - defmt::trace!("write={} read={}", write_bits, read_bits); + // defmt::trace!("write={} read={}", write_bits, read_bits); let mut dma = Peripheral::into_ref(&mut self.dma); pio_instr_util::set_y(&mut self.sm, read_bits as u32); From a186694fddd00beb4c3b45349cd79ca1959b4d17 Mon Sep 17 00:00:00 2001 From: Satoshi Tanaka Date: Mon, 1 May 2023 06:54:26 +0900 Subject: [PATCH 0975/1575] Implement WPA2 AP mode --- src/consts.rs | 15 +++++++++++++++ src/control.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/consts.rs b/src/consts.rs index ade3cb2c5..1f6551589 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -112,6 +112,21 @@ pub(crate) const READ: bool = false; pub(crate) const INC_ADDR: bool = true; pub(crate) const FIXED_ADDR: bool = false; +pub(crate) const AES_ENABLED: u32 = 0x0004; +pub(crate) const WPA2_SECURITY: u32 = 0x00400000; + +pub(crate) const MIN_PSK_LEN: usize = 8; +pub(crate) const MAX_PSK_LEN: usize = 64; + +// Security type (authentication and encryption types are combined using bit mask) +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, PartialEq)] +#[repr(u32)] +pub(crate) enum Security { + OPEN = 0, + WPA2_AES_PSK = WPA2_SECURITY | AES_ENABLED, +} + #[allow(non_camel_case_types)] #[derive(Copy, Clone)] #[repr(u8)] diff --git a/src/control.rs b/src/control.rs index 440246498..e1ad06e6b 100644 --- a/src/control.rs +++ b/src/control.rs @@ -227,6 +227,20 @@ impl<'a> Control<'a> { } pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) { + self.start_ap(ssid, "", Security::OPEN, channel).await; + } + + pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) { + self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await; + } + + async fn start_ap(&mut self, ssid: &str, passphrase: &str, security: Security, channel: u8) { + if security != Security::OPEN + && (passphrase.as_bytes().len() < MIN_PSK_LEN || passphrase.as_bytes().len() > MAX_PSK_LEN) + { + panic!("Passphrase is too short or too long"); + } + // Temporarily set wifi down self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; @@ -254,7 +268,23 @@ impl<'a> Control<'a> { self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await; // Set security - self.set_iovar_u32x2("bsscfg:wsec", 0, 0).await; // wsec = open + self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await; + + if security != Security::OPEN { + self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK + + Timer::after(Duration::from_millis(100)).await; + + // Set passphrase + let mut pfi = PassphraseInfo { + len: passphrase.as_bytes().len() as _, + flags: 1, // WSEC_PASSPHRASE + passphrase: [0; 64], + }; + pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) + .await; + } // Change mutlicast rate from 1 Mbps to 11 Mbps self.set_iovar_u32("2g_mrate", 11000000 / 500000).await; From c70a66fe815cc3926a0b8ae73066a8ed2c1583e1 Mon Sep 17 00:00:00 2001 From: Daniel Larsen <44644910+daniel-larsen@users.noreply.github.com> Date: Sun, 30 Apr 2023 18:55:19 -0300 Subject: [PATCH 0976/1575] Make defmt optional --- cyw43-pio/Cargo.toml | 3 +-- cyw43-pio/src/lib.rs | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index dbb94f612..c5632d5fd 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] cyw43 = { path = "../" } embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] } -# embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } pio-proc = "0.2" pio = "0.2.1" -# defmt = "0.3" \ No newline at end of file +defmt = { version = "0.3", optional = true } \ No newline at end of file diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index f67a55a08..c468435f1 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -129,7 +129,8 @@ where let write_bits = write.len() * 32 - 1; let read_bits = 31; - // defmt::trace!("write={} read={}", write_bits, read_bits); + #[cfg(feature = "defmt")] + defmt::trace!("write={} read={}", write_bits, read_bits); let mut dma = Peripheral::into_ref(&mut self.dma); pio_instr_util::set_x(&mut self.sm, write_bits as u32); @@ -151,7 +152,8 @@ where let write_bits = 31; let read_bits = read.len() * 32 + 32 - 1; - // defmt::trace!("write={} read={}", write_bits, read_bits); + #[cfg(feature = "defmt")] + defmt::trace!("write={} read={}", write_bits, read_bits); let mut dma = Peripheral::into_ref(&mut self.dma); pio_instr_util::set_y(&mut self.sm, read_bits as u32); From bc34f3c60f83d4f1762864e3a070a501a94fc4e2 Mon Sep 17 00:00:00 2001 From: Daniel Larsen <44644910+daniel-larsen@users.noreply.github.com> Date: Sun, 30 Apr 2023 23:19:53 -0300 Subject: [PATCH 0977/1575] updated example --- examples/rpi-pico-w/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 970db089d..8df65e188 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } -cyw43-pio = { path = "../../cyw43-pio" } +cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] } embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } From a10850a6dacc0672f998069457bf104eb970c746 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 02:27:56 +0200 Subject: [PATCH 0978/1575] rp/pio: handle all pio irqs in one handler dma does this too, also with 12 bits to check. this decreases code size significantly (increasing speed when the cache is cold), frees up an interrupt handler, and avoids read-modify-write cycles (which makes each processed flag cheaper). due to more iterations per handler invocation the actual runtime of the handler body remains roughly the same (slightly faster at O2, slightly slower at Oz). notably wakers are now kept in one large array indexed by the irq register bit number instead of three different arrays, this allows for machine code-level optimizations of waker lookups. --- embassy-rp/src/pio.rs | 134 ++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 90 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 3c7abea25..e3b6ca97c 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -12,14 +12,29 @@ use crate::dma::{self, Channel, Transfer}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{Drive, Pin, Pull, SlewRate}; use crate::pac::dma::vals::{DataSize, TreqSel}; -use crate::{interrupt, pac, peripherals}; +use crate::{interrupt, pac, peripherals, RegExt}; + +struct Wakers([AtomicWaker; 12]); + +impl Wakers { + #[inline(always)] + fn fifo_in(&self) -> &[AtomicWaker] { + &self.0[0..4] + } + #[inline(always)] + fn fifo_out(&self) -> &[AtomicWaker] { + &self.0[4..8] + } + #[inline(always)] + fn irq(&self) -> &[AtomicWaker] { + &self.0[8..12] + } +} const PIOS: [&pac::pio::Pio; 2] = [&pac::PIO0, &pac::PIO1]; const NEW_AW: AtomicWaker = AtomicWaker::new(); -const PIO_WAKERS_INIT: [AtomicWaker; 4] = [NEW_AW; 4]; -static FIFO_OUT_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; -static FIFO_IN_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; -static IRQ_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; +const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); +static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; pub enum FifoJoin { /// Both TX and RX fifo is enabled @@ -40,83 +55,28 @@ const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; -#[interrupt] -unsafe fn PIO0_IRQ_1() { - use crate::pac; - let ints = pac::PIO0.irqs(1).ints().read().0; - let inte = pac::PIO0.irqs(1).inte(); - for i in 0..4 { - // Check RXNEMPTY - if ints & (RXNEMPTY_MASK << i) != 0 { - inte.modify(|m| { - m.0 &= !(RXNEMPTY_MASK << i); - }); - FIFO_IN_WAKERS[0][i].wake(); - } - // Check IRQ flgs - if ints & (SMIRQ_MASK << i) != 0 { - inte.modify(|m| { - m.0 &= !(SMIRQ_MASK << i); - }); - IRQ_WAKERS[0][i].wake(); - } - } -} - -#[interrupt] -unsafe fn PIO1_IRQ_1() { - use crate::pac; - let ints = pac::PIO1.irqs(1).ints().read().0; - let inte = pac::PIO1.irqs(1).inte(); - for i in 0..4 { - // Check all RXNEMPTY - if ints & (RXNEMPTY_MASK << i) != 0 { - inte.modify(|m| { - m.0 &= !(RXNEMPTY_MASK << i); - }); - FIFO_IN_WAKERS[1][i].wake(); - } - // Check IRQ flgs - if ints & (SMIRQ_MASK << i) != 0 { - inte.modify(|m| { - m.0 &= !(SMIRQ_MASK << i); - }); - IRQ_WAKERS[1][i].wake(); - } - } -} - #[interrupt] unsafe fn PIO0_IRQ_0() { use crate::pac; let ints = pac::PIO0.irqs(0).ints().read().0; - let inte = pac::PIO0.irqs(0).inte(); - //debug!("!{:04x}",ints); - // Check all TXNFULL - for i in 0..4 { - if ints & (TXNFULL_MASK << i) != 0 { - inte.modify(|m| { - m.0 &= !(TXNFULL_MASK << i); - }); - FIFO_OUT_WAKERS[0][i].wake(); + for bit in 0..12 { + if ints & (1 << bit) != 0 { + WAKERS[0].0[bit].wake(); } } + pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints); } #[interrupt] unsafe fn PIO1_IRQ_0() { use crate::pac; let ints = pac::PIO1.irqs(0).ints().read().0; - let inte = pac::PIO1.irqs(0).inte(); - // Check all TXNFULL - for i in 0..4 { - if ints & (TXNFULL_MASK << i) != 0 { - inte.modify(|m| { - m.0 &= !(TXNFULL_MASK << i); - }); - FIFO_OUT_WAKERS[1][i].wake(); + for bit in 0..12 { + if ints & (1 << bit) != 0 { + WAKERS[1].0[bit].wake(); } } + pac::PIO1.irqs(0).inte().write_clear(|m| m.0 = ints); } /// Future that waits for TX-FIFO to become writable @@ -131,7 +91,7 @@ impl<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> FifoOutFuture<'a, PIO, S pub fn new(sm: &'a mut SM, value: u32) -> Self { unsafe { critical_section::with(|_| { - let irq = PIO::IrqOut::steal(); + let irq = PIO::Irq::steal(); irq.set_priority(interrupt::Priority::P3); irq.enable(); @@ -153,9 +113,9 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture if self.get_mut().sm.try_push_tx(value) { Poll::Ready(()) } else { - FIFO_OUT_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::Sm::SM_NO as usize].register(cx.waker()); unsafe { - let irq = PIO::IrqOut::steal(); + let irq = PIO::Irq::steal(); irq.disable(); critical_section::with(|_| { PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { @@ -193,7 +153,7 @@ impl<'a, PIO: PioInstance, SM: PioStateMachine> FifoInFuture<'a, PIO, SM> { pub fn new(sm: &'a mut SM) -> Self { unsafe { critical_section::with(|_| { - let irq = PIO::IrqIn::steal(); + let irq = PIO::Irq::steal(); irq.set_priority(interrupt::Priority::P3); irq.enable(); @@ -213,12 +173,12 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, if let Some(v) = self.sm.try_pull_rx() { Poll::Ready(v) } else { - FIFO_IN_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::Sm::SM_NO as usize].register(cx.waker()); unsafe { - let irq = PIO::IrqIn::steal(); + let irq = PIO::Irq::steal(); irq.disable(); critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { m.0 |= RXNEMPTY_MASK << SM::Sm::SM_NO; }); }); @@ -234,7 +194,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, S fn drop(&mut self) { unsafe { critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { m.0 &= !(RXNEMPTY_MASK << SM::Sm::SM_NO); }); }); @@ -253,7 +213,7 @@ impl<'a, PIO: PioInstance> IrqFuture { pub fn new(irq_no: u8) -> Self { unsafe { critical_section::with(|_| { - let irq = PIO::IrqSm::steal(); + let irq = PIO::Irq::steal(); irq.set_priority(interrupt::Priority::P3); irq.enable(); @@ -286,12 +246,12 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { return Poll::Ready(()); } - IRQ_WAKERS[PIO::PIO_NO as usize][self.irq_no as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker()); unsafe { - let irq = PIO::IrqSm::steal(); + let irq = PIO::Irq::steal(); irq.disable(); critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { m.0 |= SMIRQ_MASK << self.irq_no; }); }); @@ -305,7 +265,7 @@ impl<'d, PIO: PioInstance> Drop for IrqFuture { fn drop(&mut self) { unsafe { critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { + PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { m.0 &= !(SMIRQ_MASK << self.irq_no); }); }); @@ -1223,23 +1183,17 @@ pub struct PioInstanceBase {} pub trait PioInstance: Unpin { const PIO_NO: u8; - type IrqOut: Interrupt; - type IrqIn: Interrupt; - type IrqSm: Interrupt; + type Irq: Interrupt; } impl PioInstance for PioInstanceBase<0> { const PIO_NO: u8 = 0; - type IrqOut = interrupt::PIO0_IRQ_0; - type IrqIn = interrupt::PIO0_IRQ_1; - type IrqSm = interrupt::PIO0_IRQ_1; + type Irq = interrupt::PIO0_IRQ_0; } impl PioInstance for PioInstanceBase<1> { const PIO_NO: u8 = 1; - type IrqOut = interrupt::PIO1_IRQ_0; - type IrqIn = interrupt::PIO1_IRQ_1; - type IrqSm = interrupt::PIO1_IRQ_1; + type Irq = interrupt::PIO1_IRQ_0; } pub type Pio0 = PioInstanceBase<0>; From f2469776f4f229dff23eb7f0bf59c00290620d9c Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 18:44:02 +0200 Subject: [PATCH 0979/1575] rp/pio: use atomic accesses, not critical sections atomic accesses are not only faster but also can't conflict with other critical sections. --- embassy-rp/src/pio.rs | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index e3b6ca97c..f0d550d95 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -117,10 +117,8 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture unsafe { let irq = PIO::Irq::steal(); irq.disable(); - critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { - m.0 |= TXNFULL_MASK << SM::Sm::SM_NO; - }); + PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { + m.0 = TXNFULL_MASK << SM::Sm::SM_NO; }); irq.enable(); } @@ -133,10 +131,8 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { fn drop(&mut self) { unsafe { - critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { - m.0 &= !(TXNFULL_MASK << SM::Sm::SM_NO); - }); + PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_clear(|m| { + m.0 = TXNFULL_MASK << SM::Sm::SM_NO; }); } } @@ -177,10 +173,8 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, unsafe { let irq = PIO::Irq::steal(); irq.disable(); - critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { - m.0 |= RXNEMPTY_MASK << SM::Sm::SM_NO; - }); + PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { + m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; }); irq.enable(); } @@ -193,10 +187,8 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, SM> { fn drop(&mut self) { unsafe { - critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { - m.0 &= !(RXNEMPTY_MASK << SM::Sm::SM_NO); - }); + PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_clear(|m| { + m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; }); } } @@ -250,10 +242,8 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { unsafe { let irq = PIO::Irq::steal(); irq.disable(); - critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { - m.0 |= SMIRQ_MASK << self.irq_no; - }); + PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { + m.0 = SMIRQ_MASK << self.irq_no; }); irq.enable(); } @@ -264,10 +254,8 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { impl<'d, PIO: PioInstance> Drop for IrqFuture { fn drop(&mut self) { unsafe { - critical_section::with(|_| { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { - m.0 &= !(SMIRQ_MASK << self.irq_no); - }); + PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_clear(|m| { + m.0 = SMIRQ_MASK << self.irq_no; }); } } From a9074fd09bb2fa6265808f32e5cf303285fc0ec6 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 18:47:51 +0200 Subject: [PATCH 0980/1575] rp/pio: enable pio interrupts only once since we never actually *disable* these interrupts for any length of time we can simply enable them globally. we also initialize all pio interrupt flags to not cause system interrupts since state machine irqa are not necessarily meant to cause a system interrupt when set. the fifo interrupts are sticky and can likewise only be cleared inside the handler by disabling them. --- embassy-rp/src/lib.rs | 1 + embassy-rp/src/pio.rs | 50 ++++++++++++------------------------------- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 697f4308b..d69d12a30 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -156,6 +156,7 @@ pub fn init(_config: config::Config) -> Peripherals { #[cfg(feature = "time-driver")] timer::init(); dma::init(); + pio::init(); } peripherals diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index f0d550d95..00a7aa372 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -79,6 +79,20 @@ unsafe fn PIO1_IRQ_0() { pac::PIO1.irqs(0).inte().write_clear(|m| m.0 = ints); } +pub(crate) unsafe fn init() { + let irq = interrupt::PIO0_IRQ_0::steal(); + irq.disable(); + irq.set_priority(interrupt::Priority::P3); + pac::PIO0.irqs(0).inte().write(|m| m.0 = 0); + irq.enable(); + + let irq = interrupt::PIO1_IRQ_0::steal(); + irq.disable(); + irq.set_priority(interrupt::Priority::P3); + pac::PIO1.irqs(0).inte().write(|m| m.0 = 0); + irq.enable(); +} + /// Future that waits for TX-FIFO to become writable #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { @@ -89,14 +103,6 @@ pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { impl<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> FifoOutFuture<'a, PIO, SM> { pub fn new(sm: &'a mut SM, value: u32) -> Self { - unsafe { - critical_section::with(|_| { - let irq = PIO::Irq::steal(); - irq.set_priority(interrupt::Priority::P3); - - irq.enable(); - }); - } FifoOutFuture { sm, pio: PhantomData::default(), @@ -115,12 +121,9 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture } else { WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::Sm::SM_NO as usize].register(cx.waker()); unsafe { - let irq = PIO::Irq::steal(); - irq.disable(); PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { m.0 = TXNFULL_MASK << SM::Sm::SM_NO; }); - irq.enable(); } // debug!("Pending"); Poll::Pending @@ -147,14 +150,6 @@ pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachine> { impl<'a, PIO: PioInstance, SM: PioStateMachine> FifoInFuture<'a, PIO, SM> { pub fn new(sm: &'a mut SM) -> Self { - unsafe { - critical_section::with(|_| { - let irq = PIO::Irq::steal(); - irq.set_priority(interrupt::Priority::P3); - - irq.enable(); - }); - } FifoInFuture { sm, pio: PhantomData::default(), @@ -171,12 +166,9 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, } else { WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::Sm::SM_NO as usize].register(cx.waker()); unsafe { - let irq = PIO::Irq::steal(); - irq.disable(); PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; }); - irq.enable(); } //debug!("Pending"); Poll::Pending @@ -203,14 +195,6 @@ pub struct IrqFuture { impl<'a, PIO: PioInstance> IrqFuture { pub fn new(irq_no: u8) -> Self { - unsafe { - critical_section::with(|_| { - let irq = PIO::Irq::steal(); - irq.set_priority(interrupt::Priority::P3); - - irq.enable(); - }); - } IrqFuture { pio: PhantomData::default(), irq_no, @@ -240,12 +224,9 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker()); unsafe { - let irq = PIO::Irq::steal(); - irq.disable(); PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { m.0 = SMIRQ_MASK << self.irq_no; }); - irq.enable(); } Poll::Pending } @@ -1171,17 +1152,14 @@ pub struct PioInstanceBase {} pub trait PioInstance: Unpin { const PIO_NO: u8; - type Irq: Interrupt; } impl PioInstance for PioInstanceBase<0> { const PIO_NO: u8 = 0; - type Irq = interrupt::PIO0_IRQ_0; } impl PioInstance for PioInstanceBase<1> { const PIO_NO: u8 = 1; - type Irq = interrupt::PIO1_IRQ_0; } pub type Pio0 = PioInstanceBase<0>; From db16b6ff3f94e48f50c4212c164d018741b61b42 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 18:48:36 +0200 Subject: [PATCH 0981/1575] rp/pio: don't call dma::init so much this is already done during platform init. it wasn't even sound in the original implementation because futures would meddle with the nvic in critical sections, while another (interrupt) executor could meddle with the nvic without critical sections here. it is only accidentally sound now and only if irq1 of both pios isn't used by user code. luckily the worst we can expect to happen is interrupt priorities being set wrong, but wrong is wrong is wrong. --- embassy-rp/src/pio.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 00a7aa372..3775060f0 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -8,7 +8,7 @@ use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::PeripheralRef; use embassy_sync::waitqueue::AtomicWaker; -use crate::dma::{self, Channel, Transfer}; +use crate::dma::{Channel, Transfer}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{Drive, Pin, Pull, SlewRate}; use crate::pac::dma::vals::{DataSize, TreqSel}; @@ -949,7 +949,6 @@ pub trait PioStateMachine: Sized + Unpin { fn dma_push<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [u32]) -> Transfer<'a, C> { unsafe { - dma::init(); let pio_no = Self::Pio::PIO_NO; let sm_no = Self::Sm::SM_NO; let p = ch.regs(); @@ -973,7 +972,6 @@ pub trait PioStateMachine: Sized + Unpin { fn dma_pull<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [u32]) -> Transfer<'a, C> { unsafe { - dma::init(); let pio_no = Self::Pio::PIO_NO; let sm_no = Self::Sm::SM_NO; let p = ch.regs(); From 4618b79b22bee36ef9485a2b0e8562121b84c54b Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 19:28:01 +0200 Subject: [PATCH 0982/1575] rp/pio: seal PioInstance, SmInstance seems prudent to hide access to the internals. --- embassy-rp/src/pio.rs | 61 +++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 3775060f0..756f0e928 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -12,6 +12,7 @@ use crate::dma::{Channel, Transfer}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{Drive, Pin, Pull, SlewRate}; use crate::pac::dma::vals::{DataSize, TreqSel}; +use crate::pio::sealed::{PioInstance as _, SmInstance as _}; use crate::{interrupt, pac, peripherals, RegExt}; struct Wakers([AtomicWaker; 12]); @@ -320,14 +321,13 @@ pub struct PioStateMachineInstance { sm: PhantomData, } -impl PioStateMachine for PioStateMachineInstance { +impl sealed::PioStateMachine for PioStateMachineInstance { type Pio = PIO; type Sm = SM; } +impl PioStateMachine for PioStateMachineInstance {} -pub trait PioStateMachine: Sized + Unpin { - type Pio: PioInstance; - type Sm: SmInstance; +pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn pio_no(&self) -> u8 { let _ = self; Self::Pio::PIO_NO @@ -1027,9 +1027,10 @@ pub struct PioCommonInstance { pio: PhantomData, } -impl PioCommon for PioCommonInstance { +impl sealed::PioCommon for PioCommonInstance { type Pio = PIO; } +impl PioCommon for PioCommonInstance {} fn write_instr(pio_no: u8, start: usize, instrs: I, mem_user: u32) where @@ -1051,9 +1052,7 @@ where } } -pub trait PioCommon: Sized { - type Pio: PioInstance; - +pub trait PioCommon: sealed::PioCommon + Sized { fn write_instr(&mut self, start: usize, instrs: I) where I: Iterator, @@ -1096,16 +1095,14 @@ pub trait PioCommon: Sized { // Identifies a specific state machine inside a PIO device pub struct SmInstanceBase {} -pub trait SmInstance: Unpin { - const SM_NO: u8; -} +pub trait SmInstance: sealed::SmInstance + Unpin {} -impl SmInstance for SmInstanceBase { +impl sealed::SmInstance for SmInstanceBase { const SM_NO: u8 = SM_NO; } +impl SmInstance for SmInstanceBase {} -pub trait PioPeripheral: Sized { - type Pio: PioInstance; +pub trait PioPeripheral: sealed::PioPeripheral + Sized { fn pio(&self) -> u8 { let _ = self; Self::Pio::PIO_NO @@ -1145,20 +1142,43 @@ pub trait PioPeripheral: Sized { } } +mod sealed { + pub trait PioInstance { + const PIO_NO: u8; + } + + pub trait PioCommon { + type Pio: super::PioInstance; + } + + pub trait PioStateMachine { + type Pio: super::PioInstance; + type Sm: super::SmInstance; + } + + pub trait SmInstance { + const SM_NO: u8; + } + + pub trait PioPeripheral { + type Pio: super::PioInstance; + } +} + // Identifies a specific PIO device pub struct PioInstanceBase {} -pub trait PioInstance: Unpin { - const PIO_NO: u8; -} +pub trait PioInstance: sealed::PioInstance + Unpin {} -impl PioInstance for PioInstanceBase<0> { +impl sealed::PioInstance for PioInstanceBase<0> { const PIO_NO: u8 = 0; } +impl PioInstance for PioInstanceBase<0> {} -impl PioInstance for PioInstanceBase<1> { +impl sealed::PioInstance for PioInstanceBase<1> { const PIO_NO: u8 = 1; } +impl PioInstance for PioInstanceBase<1> {} pub type Pio0 = PioInstanceBase<0>; pub type Pio1 = PioInstanceBase<1>; @@ -1170,9 +1190,10 @@ pub type Sm3 = SmInstanceBase<3>; macro_rules! impl_pio_sm { ($name:ident, $pio:expr) => { - impl PioPeripheral for peripherals::$name { + impl sealed::PioPeripheral for peripherals::$name { type Pio = PioInstanceBase<$pio>; } + impl PioPeripheral for peripherals::$name {} }; } From 4cd5ed81aa7931b8b0ff3e09c3604cf5d65f2107 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 20:02:52 +0200 Subject: [PATCH 0983/1575] rp/pio: remove top-level PIOS array this mainly removes the need for explicit indexing to get the pac object. runtime effect is zero, but arguably things are a bit easier to read with less indexing. --- embassy-rp/src/pio.rs | 252 +++++++++++++++--------------------------- 1 file changed, 90 insertions(+), 162 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 756f0e928..088944b66 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -32,7 +32,6 @@ impl Wakers { } } -const PIOS: [&pac::pio::Pio; 2] = [&pac::PIO0, &pac::PIO1]; const NEW_AW: AtomicWaker = AtomicWaker::new(); const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; @@ -122,7 +121,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture } else { WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::Sm::SM_NO as usize].register(cx.waker()); unsafe { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { + PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = TXNFULL_MASK << SM::Sm::SM_NO; }); } @@ -135,7 +134,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { fn drop(&mut self) { unsafe { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_clear(|m| { + PIO::PIO.irqs(0).inte().write_clear(|m| { m.0 = TXNFULL_MASK << SM::Sm::SM_NO; }); } @@ -167,7 +166,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, } else { WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::Sm::SM_NO as usize].register(cx.waker()); unsafe { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { + PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; }); } @@ -180,7 +179,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, SM> { fn drop(&mut self) { unsafe { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_clear(|m| { + PIO::PIO.irqs(0).inte().write_clear(|m| { m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; }); } @@ -210,7 +209,7 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { // Check if IRQ flag is already set if critical_section::with(|_| unsafe { - let irq_flags = PIOS[PIO::PIO_NO as usize].irq(); + let irq_flags = PIO::PIO.irq(); if irq_flags.read().0 & (1 << self.irq_no) != 0 { irq_flags.write(|m| { m.0 = 1 << self.irq_no; @@ -225,7 +224,7 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker()); unsafe { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_set(|m| { + PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = SMIRQ_MASK << self.irq_no; }); } @@ -236,7 +235,7 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { impl<'d, PIO: PioInstance> Drop for IrqFuture { fn drop(&mut self) { unsafe { - PIOS[PIO::PIO_NO as usize].irqs(0).inte().write_clear(|m| { + PIO::PIO.irqs(0).inte().write_clear(|m| { m.0 = SMIRQ_MASK << self.irq_no; }); } @@ -299,7 +298,7 @@ impl PioPin { pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { let mask = 1 << self.pin(); unsafe { - PIOS[PIO::PIO_NO as usize] + PIO::PIO .input_sync_bypass() .modify(|w| *w = if bypass { *w & !mask } else { *w | mask }); } @@ -340,7 +339,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn restart(&mut self) { let _ = self; unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .ctrl() .modify(|w| w.set_sm_restart(1u8 << Self::Sm::SM_NO)); } @@ -349,7 +348,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { let _ = self; let mask = 1u8 << Self::Sm::SM_NO; unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .ctrl() .modify(|w| w.set_sm_enable((w.sm_enable() & !mask) | (if enable { mask } else { 0 }))); } @@ -357,46 +356,44 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn is_enabled(&self) -> bool { let _ = self; - unsafe { PIOS[Self::Pio::PIO_NO as usize].ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_tx_empty(&self) -> bool { let _ = self; - unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_tx_full(&self) -> bool { let _ = self; - unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_rx_empty(&self) -> bool { let _ = self; - unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_rx_full(&self) -> bool { let _ = self; - unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } } fn tx_level(&self) -> u8 { unsafe { - let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; + let flevel = Self::Pio::PIO.flevel().read().0; (flevel >> (Self::Sm::SM_NO * 8)) as u8 & 0x0f } } fn rx_level(&self) -> u8 { unsafe { - let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; + let flevel = Self::Pio::PIO.flevel().read().0; (flevel >> (Self::Sm::SM_NO * 8 + 4)) as u8 & 0x0f } } fn push_tx(&mut self, v: u32) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .txf(Self::Sm::SM_NO as usize) - .write_value(v); + Self::Pio::PIO.txf(Self::Sm::SM_NO as usize).write_value(v); } } @@ -409,7 +406,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn pull_rx(&mut self) -> u32 { - unsafe { PIOS[Self::Pio::PIO_NO as usize].rxf(Self::Sm::SM_NO as usize).read() } + unsafe { Self::Pio::PIO.rxf(Self::Sm::SM_NO as usize).read() } } fn try_pull_rx(&mut self) -> Option { @@ -421,7 +418,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_clkdiv(&mut self, div_x_256: u32) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .clkdiv() .write(|w| w.0 = div_x_256 << 8); @@ -429,20 +426,13 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_clkdiv(&self) -> u32 { - unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .clkdiv() - .read() - .0 - >> 8 - } + unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).clkdiv().read().0 >> 8 } } fn clkdiv_restart(&mut self) { let _ = self; unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .ctrl() .modify(|w| w.set_clkdiv_restart(1u8 << Self::Sm::SM_NO)); } @@ -450,7 +440,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_side_enable(&self, enable: bool) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .execctrl() .modify(|w| w.set_side_en(enable)); @@ -458,18 +448,12 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn is_side_enabled(&self) -> bool { - unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .read() - .side_en() - } + unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read().side_en() } } fn set_side_pindir(&mut self, pindir: bool) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .execctrl() .modify(|w| w.set_side_pindir(pindir)); @@ -478,7 +462,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn is_side_pindir(&self) -> bool { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .execctrl() .read() @@ -488,7 +472,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_jmp_pin(&mut self, pin: u8) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .execctrl() .modify(|w| w.set_jmp_pin(pin)); @@ -496,34 +480,22 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_jmp_pin(&mut self) -> u8 { - unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .read() - .jmp_pin() - } + unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read().jmp_pin() } } fn set_wrap(&self, source: u8, target: u8) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .modify(|w| { - w.set_wrap_top(source); - w.set_wrap_bottom(target) - }); + Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().modify(|w| { + w.set_wrap_top(source); + w.set_wrap_bottom(target) + }); } } /// Get wrapping addresses. Returns (source, target). fn get_wrap(&self) -> (u8, u8) { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read(); (r.wrap_top(), r.wrap_bottom()) } } @@ -535,21 +507,15 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { FifoJoin::TxOnly => (false, true), }; unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| { - w.set_fjoin_rx(rx); - w.set_fjoin_tx(tx) - }); + Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().modify(|w| { + w.set_fjoin_rx(rx); + w.set_fjoin_tx(tx) + }); } } fn get_fifo_join(&self) -> FifoJoin { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); // Ignores the invalid state when both bits are set if r.fjoin_rx() { FifoJoin::RxOnly @@ -564,9 +530,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs unsafe { - let shiftctrl = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .shiftctrl(); + let shiftctrl = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl(); shiftctrl.modify(|w| { w.set_fjoin_rx(!w.fjoin_rx()); }); @@ -578,7 +542,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_pull_threshold(&mut self, threshold: u8) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .modify(|w| w.set_pull_thresh(threshold)); @@ -587,16 +551,13 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn get_pull_threshold(&self) -> u8 { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); r.pull_thresh() } } fn set_push_threshold(&mut self, threshold: u8) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .modify(|w| w.set_push_thresh(threshold)); @@ -605,17 +566,14 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn get_push_threshold(&self) -> u8 { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); r.push_thresh() } } fn set_out_shift_dir(&mut self, dir: ShiftDirection) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); @@ -623,7 +581,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_out_shiftdir(&self) -> ShiftDirection { unsafe { - if PIOS[Self::Pio::PIO_NO as usize] + if Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .read() @@ -638,7 +596,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_in_shift_dir(&mut self, dir: ShiftDirection) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); @@ -646,7 +604,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_in_shiftdir(&self) -> ShiftDirection { unsafe { - if PIOS[Self::Pio::PIO_NO as usize] + if Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .read() @@ -661,7 +619,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_autopull(&mut self, auto: bool) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .modify(|w| w.set_autopull(auto)); @@ -670,7 +628,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn is_autopull(&self) -> bool { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .read() @@ -680,7 +638,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_autopush(&mut self, auto: bool) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .modify(|w| w.set_autopush(auto)); @@ -689,7 +647,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn is_autopush(&self) -> bool { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .shiftctrl() .read() @@ -699,16 +657,13 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn get_addr(&self) -> u8 { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .addr() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).addr().read(); r.addr() } } fn set_sideset_count(&mut self, count: u8) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .pinctrl() .modify(|w| w.set_sideset_count(count)); @@ -717,10 +672,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn get_sideset_count(&self) -> u8 { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); r.sideset_count() } } @@ -747,7 +699,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_sideset_base_pin(&mut self, base_pin: &PioPin) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .pinctrl() .modify(|w| w.set_sideset_base(base_pin.pin())); @@ -756,10 +708,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn get_sideset_base(&self) -> u8 { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); r.sideset_base() } } @@ -768,30 +717,24 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_set_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| { - w.set_set_base(base); - w.set_set_count(count) - }); + Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().modify(|w| { + w.set_set_base(base); + w.set_set_count(count) + }); } } /// Get the range of out pins affected by a set instruction. Returns (base, count). fn get_set_range(&self) -> (u8, u8) { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); (r.set_base(), r.set_count()) } } fn set_in_base_pin(&mut self, base: &PioPin) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .pinctrl() .modify(|w| w.set_in_base(base.pin())); @@ -800,10 +743,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn get_in_base(&self) -> u8 { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); r.in_base() } } @@ -811,23 +751,17 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_out_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| { - w.set_out_base(base); - w.set_out_count(count) - }); + Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().modify(|w| { + w.set_out_base(base); + w.set_out_count(count) + }); } } /// Get the range of out pins affected by a set instruction. Returns (base, count). fn get_out_range(&self) -> (u8, u8) { unsafe { - let r = PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .read(); + let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); (r.out_base(), r.out_count()) } } @@ -855,18 +789,12 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_current_instr() -> u32 { - unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .sm(Self::Sm::SM_NO as usize) - .instr() - .read() - .0 - } + unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).instr().read().0 } } fn exec_instr(&mut self, instr: u16) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .sm(Self::Sm::SM_NO as usize) .instr() .write(|w| w.set_instr(instr)); @@ -879,6 +807,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { { let _ = self; write_instr( + Self::Pio::PIO, Self::Pio::PIO_NO, start, instrs, @@ -889,14 +818,14 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn is_irq_set(&self, irq_no: u8) -> bool { assert!(irq_no < 8); unsafe { - let irq_flags = PIOS[Self::Pio::PIO_NO as usize].irq(); + let irq_flags = Self::Pio::PIO.irq(); irq_flags.read().0 & (1 << irq_no) != 0 } } fn clear_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); - unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } + unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } } fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, Self::Pio, Self> { @@ -913,7 +842,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_tx_stalled(&self) -> bool { unsafe { - let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let fdebug = Self::Pio::PIO.fdebug(); let ret = fdebug.read().txstall() & (1 << Self::Sm::SM_NO) != 0; fdebug.write(|w| w.set_txstall(1 << Self::Sm::SM_NO)); ret @@ -922,7 +851,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_tx_overflowed(&self) -> bool { unsafe { - let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let fdebug = Self::Pio::PIO.fdebug(); let ret = fdebug.read().txover() & (1 << Self::Sm::SM_NO) != 0; fdebug.write(|w| w.set_txover(1 << Self::Sm::SM_NO)); ret @@ -931,7 +860,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_rx_stalled(&self) -> bool { unsafe { - let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let fdebug = Self::Pio::PIO.fdebug(); let ret = fdebug.read().rxstall() & (1 << Self::Sm::SM_NO) != 0; fdebug.write(|w| w.set_rxstall(1 << Self::Sm::SM_NO)); ret @@ -940,7 +869,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_rx_underflowed(&self) -> bool { unsafe { - let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); + let fdebug = Self::Pio::PIO.fdebug(); let ret = fdebug.read().rxunder() & (1 << Self::Sm::SM_NO) != 0; fdebug.write(|w| w.set_rxunder(1 << Self::Sm::SM_NO)); ret @@ -954,7 +883,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { let p = ch.regs(); p.read_addr().write_value(data.as_ptr() as u32); p.write_addr() - .write_value(PIOS[pio_no as usize].txf(sm_no as usize).ptr() as u32); + .write_value(Self::Pio::PIO.txf(sm_no as usize).ptr() as u32); p.trans_count().write_value(data.len() as u32); p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine @@ -977,7 +906,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { let p = ch.regs(); p.write_addr().write_value(data.as_ptr() as u32); p.read_addr() - .write_value(PIOS[pio_no as usize].rxf(sm_no as usize).ptr() as u32); + .write_value(Self::Pio::PIO.rxf(sm_no as usize).ptr() as u32); p.trans_count().write_value(data.len() as u32); p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine @@ -1032,7 +961,7 @@ impl sealed::PioCommon for PioCommonInstance { } impl PioCommon for PioCommonInstance {} -fn write_instr(pio_no: u8, start: usize, instrs: I, mem_user: u32) +fn write_instr(pio: &pac::pio::Pio, pio_no: u8, start: usize, instrs: I, mem_user: u32) where I: Iterator, { @@ -1044,7 +973,7 @@ where addr ); unsafe { - PIOS[pio_no as usize].instr_mem(addr as usize).write(|w| { + pio.instr_mem(addr as usize).write(|w| { w.set_instr_mem(instr); }); instr_mem_set_status(pio_no, addr, mem_user); @@ -1058,37 +987,33 @@ pub trait PioCommon: sealed::PioCommon + Sized { I: Iterator, { let _ = self; - write_instr(Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); + write_instr(Self::Pio::PIO, Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); } fn clear_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); - unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } + unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } } fn clear_irqs(&mut self, mask: u8) { - unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(mask)) } + unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(mask)) } } fn force_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); - unsafe { - PIOS[Self::Pio::PIO_NO as usize] - .irq_force() - .write(|w| w.set_irq_force(1 << irq_no)) - } + unsafe { Self::Pio::PIO.irq_force().write(|w| w.set_irq_force(1 << irq_no)) } } fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { unsafe { - PIOS[Self::Pio::PIO_NO as usize] + Self::Pio::PIO .input_sync_bypass() .modify(|w| *w = (*w & !mask) | (bypass & mask)); } } fn get_input_sync_bypass(&self) -> u32 { - unsafe { PIOS[Self::Pio::PIO_NO as usize].input_sync_bypass().read() } + unsafe { Self::Pio::PIO.input_sync_bypass().read() } } } @@ -1145,6 +1070,7 @@ pub trait PioPeripheral: sealed::PioPeripheral + Sized { mod sealed { pub trait PioInstance { const PIO_NO: u8; + const PIO: &'static crate::pac::pio::Pio; } pub trait PioCommon { @@ -1172,11 +1098,13 @@ pub trait PioInstance: sealed::PioInstance + Unpin {} impl sealed::PioInstance for PioInstanceBase<0> { const PIO_NO: u8 = 0; + const PIO: &'static pac::pio::Pio = &pac::PIO0; } impl PioInstance for PioInstanceBase<0> {} impl sealed::PioInstance for PioInstanceBase<1> { const PIO_NO: u8 = 1; + const PIO: &'static pac::pio::Pio = &pac::PIO1; } impl PioInstance for PioInstanceBase<1> {} From 58e727d3b9aa9d54f7c20f618fada176dcd533a9 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 20:16:27 +0200 Subject: [PATCH 0984/1575] rp/pio: move non-sm-specific methods to PioCommon pin and irq operations affect the entire pio block. with pins this is not very problematic since pins themselves are resources, but irqs are not treated like that and can thus interfere across state machines. the ability to wait for an irq on a state machine is kept to make synchronization with user code easier, and since we can't inspect loaded programs at build time we wouldn't gain much from disallowing waits from state machines anyway. --- embassy-rp/src/pio.rs | 61 ++++++++++++++----------------- examples/rp/src/bin/pio_async.rs | 20 ++++++---- examples/rp/src/bin/ws2812-pio.rs | 11 +++--- 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 088944b66..b43244da2 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -677,26 +677,6 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } - fn make_pio_pin(&self, pin: impl Pin) -> PioPin { - unsafe { - pin.io().ctrl().write(|w| { - w.set_funcsel( - if Self::Pio::PIO_NO == 1 { - pac::io::vals::Gpio0ctrlFuncsel::PIO1_0 - } else { - // PIO == 0 - pac::io::vals::Gpio0ctrlFuncsel::PIO0_0 - } - .0, - ); - }); - } - PioPin { - pin_bank: pin.pin_bank(), - pio: PhantomData::default(), - } - } - fn set_sideset_base_pin(&mut self, base_pin: &PioPin) { unsafe { Self::Pio::PIO @@ -815,19 +795,6 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { ); } - fn is_irq_set(&self, irq_no: u8) -> bool { - assert!(irq_no < 8); - unsafe { - let irq_flags = Self::Pio::PIO.irq(); - irq_flags.read().0 & (1 << irq_no) != 0 - } - } - - fn clear_irq(&mut self, irq_no: usize) { - assert!(irq_no < 8); - unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } - } - fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, Self::Pio, Self> { FifoOutFuture::new(self, value) } @@ -990,6 +957,14 @@ pub trait PioCommon: sealed::PioCommon + Sized { write_instr(Self::Pio::PIO, Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); } + fn is_irq_set(&self, irq_no: u8) -> bool { + assert!(irq_no < 8); + unsafe { + let irq_flags = Self::Pio::PIO.irq(); + irq_flags.read().0 & (1 << irq_no) != 0 + } + } + fn clear_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } @@ -1015,6 +990,26 @@ pub trait PioCommon: sealed::PioCommon + Sized { fn get_input_sync_bypass(&self) -> u32 { unsafe { Self::Pio::PIO.input_sync_bypass().read() } } + + fn make_pio_pin(&self, pin: impl Pin) -> PioPin { + unsafe { + pin.io().ctrl().write(|w| { + w.set_funcsel( + if Self::Pio::PIO_NO == 1 { + pac::io::vals::Gpio0ctrlFuncsel::PIO1_0 + } else { + // PIO == 0 + pac::io::vals::Gpio0ctrlFuncsel::PIO0_0 + } + .0, + ); + }); + } + PioPin { + pin_bank: pin.pin_bank(), + pio: PhantomData::default(), + } + } } // Identifies a specific state machine inside a PIO device diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index e616d8c5a..3cfeec71f 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,13 +4,15 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; -use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; +use embassy_rp::pio::{ + Pio0, PioCommon, PioCommonInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, + Sm1, Sm2, +}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -#[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance, pin: AnyPin) { // Setup sm0 // Send data serially to pin @@ -23,11 +25,11 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance, pin: AnyPin) { ); let relocated = RelocatedProgram::new(&prg.program); - let out_pin = sm.make_pio_pin(pin); + let out_pin = pio.make_pio_pin(pin); let pio_pins = [&out_pin]; sm.set_out_pins(&pio_pins); sm.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(&mut sm, relocated.origin()); + pio_instr_util::exec_jmp(sm, relocated.origin()); sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); sm.set_set_range(0, 1); let pio::Wrap { source, target } = relocated.wrap(); @@ -35,7 +37,10 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance, pin: AnyPin) { sm.set_autopull(true); sm.set_out_shift_dir(ShiftDirection::Left); +} +#[embassy_executor::task] +async fn pio_task_sm0(mut sm: PioStateMachineInstance) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -104,9 +109,10 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let pio = p.PIO0; - let (_, sm0, sm1, sm2, ..) = pio.split(); + let (mut pio0, mut sm0, sm1, sm2, ..) = pio.split(); - spawner.spawn(pio_task_sm0(sm0, p.PIN_0.degrade())).unwrap(); + setup_pio_task_sm0(&mut pio0, &mut sm0, p.PIN_0.degrade()); + spawner.spawn(pio_task_sm0(sm0)).unwrap(); spawner.spawn(pio_task_sm1(sm1)).unwrap(); spawner.spawn(pio_task_sm2(sm2)).unwrap(); } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 5f8a3baee..211c60c49 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,7 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, PioInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, + FifoJoin, PioCommon, PioCommonInstance, PioInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, + ShiftDirection, SmInstance, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; @@ -18,7 +19,7 @@ pub struct Ws2812 { } impl Ws2812 { - pub fn new(mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { + pub fn new(pio: PioCommonInstance

, mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { // Setup sm0 // prepare the PIO program @@ -53,7 +54,7 @@ impl Ws2812 { pio_instr_util::exec_jmp(&mut sm, relocated.origin()); // Pin config - let out_pin = sm.make_pio_pin(pin); + let out_pin = pio.make_pio_pin(pin); sm.set_set_pins(&[&out_pin]); sm.set_sideset_base_pin(&out_pin); sm.set_sideset_count(1); @@ -115,7 +116,7 @@ async fn main(_spawner: Spawner) { info!("Start"); let p = embassy_rp::init(Default::default()); - let (_pio0, sm0, _sm1, _sm2, _sm3) = p.PIO0.split(); + let (pio0, sm0, _sm1, _sm2, _sm3) = p.PIO0.split(); // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit // feather boards for the 2040 both have one built in. @@ -124,7 +125,7 @@ async fn main(_spawner: Spawner) { // For the thing plus, use pin 8 // For the feather, use pin 16 - let mut ws2812 = Ws2812::new(sm0, p.PIN_8.degrade()); + let mut ws2812 = Ws2812::new(pio0, sm0, p.PIN_8.degrade()); // Loop forever making RGB values and pushing them out to the WS2812. loop { From fa1ec29ae61df78a74be4bffb47f400fa35e49f1 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 20:18:22 +0200 Subject: [PATCH 0985/1575] rp/pio: remove a bunch of unnecessary let _ = self --- embassy-rp/src/pio.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index b43244da2..8492ca31d 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -328,7 +328,6 @@ impl PioStateMachine for PioStateMachineInstan pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn pio_no(&self) -> u8 { - let _ = self; Self::Pio::PIO_NO } @@ -337,7 +336,6 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn restart(&mut self) { - let _ = self; unsafe { Self::Pio::PIO .ctrl() @@ -345,7 +343,6 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } fn set_enable(&mut self, enable: bool) { - let _ = self; let mask = 1u8 << Self::Sm::SM_NO; unsafe { Self::Pio::PIO @@ -355,25 +352,20 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn is_enabled(&self) -> bool { - let _ = self; unsafe { Self::Pio::PIO.ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_tx_empty(&self) -> bool { - let _ = self; unsafe { Self::Pio::PIO.fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_tx_full(&self) -> bool { - let _ = self; unsafe { Self::Pio::PIO.fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_rx_empty(&self) -> bool { - let _ = self; unsafe { Self::Pio::PIO.fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } } fn is_rx_full(&self) -> bool { - let _ = self; unsafe { Self::Pio::PIO.fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } } @@ -430,7 +422,6 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn clkdiv_restart(&mut self) { - let _ = self; unsafe { Self::Pio::PIO .ctrl() @@ -785,7 +776,6 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { where I: Iterator, { - let _ = self; write_instr( Self::Pio::PIO, Self::Pio::PIO_NO, @@ -953,7 +943,6 @@ pub trait PioCommon: sealed::PioCommon + Sized { where I: Iterator, { - let _ = self; write_instr(Self::Pio::PIO, Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); } @@ -1024,7 +1013,6 @@ impl SmInstance for SmInstanceBase {} pub trait PioPeripheral: sealed::PioPeripheral + Sized { fn pio(&self) -> u8 { - let _ = self; Self::Pio::PIO_NO } @@ -1037,7 +1025,6 @@ pub trait PioPeripheral: sealed::PioPeripheral + Sized { PioStateMachineInstance>, PioStateMachineInstance>, ) { - let _ = self; ( PioCommonInstance { pio: PhantomData::default(), From f4ade6af8bb2571ce2de0531d9c9715a7b8b941c Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 20:40:18 +0200 Subject: [PATCH 0986/1575] rp/pio: write instr memory only from common instruction memory is a shared resource. writing it only from PioCommon clarifies this, and perhaps makes it more obvious that multiple state machines can share the same instructions. this also allows *freeing* of instruction memory to reprogram the system, although this interface is not entirely safe yet. it's safe in the sense rusts understands things, but state machines may misbehave if their instruction memory is freed and rewritten while they are running. fixing this is out of scope for now since it requires some larger changes to how state machines are handled. the interface provided currently is already unsafe in that it lets people execute instruction memory that has never been written, so this isn't much of a drawback for now. --- embassy-rp/src/pio.rs | 106 ++++++++++++------------------ examples/rp/src/bin/pio_async.rs | 28 +++++--- examples/rp/src/bin/pio_dma.rs | 6 +- examples/rp/src/bin/ws2812-pio.rs | 4 +- 4 files changed, 64 insertions(+), 80 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 8492ca31d..7faec10b5 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -772,19 +772,6 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } - fn write_instr(&mut self, start: usize, instrs: I) - where - I: Iterator, - { - write_instr( - Self::Pio::PIO, - Self::Pio::PIO_NO, - start, - instrs, - MEM_USED_BY_STATEMACHINE | Self::Sm::SM_NO as u32, - ); - } - fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, Self::Pio, Self> { FifoOutFuture::new(self, value) } @@ -880,71 +867,59 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } -/* -This is a bit array containing 4 bits for every word in the PIO instruction memory. -*/ -// Bit 3-2 -//const MEM_USE_MASK: u32 = 0b1100; -const MEM_NOT_USED: u32 = 0b0000; -const MEM_USED_BY_STATEMACHINE: u32 = 0b0100; -const MEM_USED_BY_COMMON: u32 = 0b1000; - -// Bit 1-0 is the number of the state machine -//const MEM_STATE_MASK: u32 = 0b0011; - -// Should use mutex if running on multiple cores -static mut INSTR_MEM_STATUS: &'static mut [[u32; 4]; 2] = &mut [[0; 4]; 2]; - -fn instr_mem_get_status(pio_no: u8, addr: u8) -> u32 { - ((unsafe { INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }) >> ((addr & 0x07) * 4)) & 0xf -} - -fn instr_mem_set_status(pio_no: u8, addr: u8, status: u32) { - let w = unsafe { &mut INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }; - let shift = (addr & 0x07) * 4; - *w = (*w & !(0xf << shift)) | (status << shift); -} - -fn instr_mem_is_free(pio_no: u8, addr: u8) -> bool { - instr_mem_get_status(pio_no, addr) == MEM_NOT_USED -} - pub struct PioCommonInstance { + instructions_used: u32, + pio: PhantomData, +} + +pub struct PioInstanceMemory { + used_mask: u32, pio: PhantomData, } impl sealed::PioCommon for PioCommonInstance { type Pio = PIO; } -impl PioCommon for PioCommonInstance {} - -fn write_instr(pio: &pac::pio::Pio, pio_no: u8, start: usize, instrs: I, mem_user: u32) -where - I: Iterator, -{ - for (i, instr) in instrs.enumerate() { - let addr = (i + start) as u8; - assert!( - instr_mem_is_free(pio_no, addr), - "Trying to write already used PIO instruction memory at {}", - addr - ); - unsafe { - pio.instr_mem(addr as usize).write(|w| { - w.set_instr_mem(instr); - }); - instr_mem_set_status(pio_no, addr, mem_user); +impl PioCommon for PioCommonInstance { + fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory + where + I: Iterator, + { + let mut used_mask = 0; + for (i, instr) in instrs.enumerate() { + let addr = (i + start) as u8; + let mask = 1 << (addr as usize); + assert!( + self.instructions_used & mask == 0, + "Trying to write already used PIO instruction memory at {}", + addr + ); + unsafe { + PIO::PIO.instr_mem(addr as usize).write(|w| { + w.set_instr_mem(instr); + }); + } + used_mask |= mask; } + self.instructions_used |= used_mask; + PioInstanceMemory { + used_mask, + pio: PhantomData, + } + } + + fn free_instr(&mut self, instrs: PioInstanceMemory) { + self.instructions_used &= !instrs.used_mask; } } pub trait PioCommon: sealed::PioCommon + Sized { - fn write_instr(&mut self, start: usize, instrs: I) + fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory where - I: Iterator, - { - write_instr(Self::Pio::PIO, Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); - } + I: Iterator; + + // TODO make instruction memory that is currently in use unfreeable + fn free_instr(&mut self, instrs: PioInstanceMemory); fn is_irq_set(&self, irq_no: u8) -> bool { assert!(irq_no < 8); @@ -1027,6 +1002,7 @@ pub trait PioPeripheral: sealed::PioPeripheral + Sized { ) { ( PioCommonInstance { + instructions_used: 0, pio: PhantomData::default(), }, PioStateMachineInstance { diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 3cfeec71f..1b075b8fd 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -28,7 +28,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachin let out_pin = pio.make_pio_pin(pin); let pio_pins = [&out_pin]; sm.set_out_pins(&pio_pins); - sm.write_instr(relocated.origin() as usize, relocated.code()); + pio.write_instr(relocated.origin() as usize, relocated.code()); pio_instr_util::exec_jmp(sm, relocated.origin()); sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); sm.set_set_range(0, 1); @@ -51,16 +51,15 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance) { } } -#[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); let relocated = RelocatedProgram::new(&prg.program); - sm.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(&mut sm, relocated.origin()); + pio.write_instr(relocated.origin() as usize, relocated.code()); + pio_instr_util::exec_jmp(sm, relocated.origin()); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); sm.set_set_range(0, 0); let pio::Wrap { source, target } = relocated.wrap(); @@ -68,6 +67,10 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance) { sm.set_autopush(true); sm.set_in_shift_dir(ShiftDirection::Right); +} + +#[embassy_executor::task] +async fn pio_task_sm1(mut sm: PioStateMachineInstance) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -75,8 +78,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance) { } } -#[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -90,13 +92,17 @@ async fn pio_task_sm2(mut sm: PioStateMachineInstance) { ".wrap", ); let relocated = RelocatedProgram::new(&prg.program); - sm.write_instr(relocated.origin() as usize, relocated.code()); + pio.write_instr(relocated.origin() as usize, relocated.code()); let pio::Wrap { source, target } = relocated.wrap(); sm.set_wrap(source, target); - pio_instr_util::exec_jmp(&mut sm, relocated.origin()); + pio_instr_util::exec_jmp(sm, relocated.origin()); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); +} + +#[embassy_executor::task] +async fn pio_task_sm2(mut sm: PioStateMachineInstance) { sm.set_enable(true); loop { sm.wait_irq(3).await; @@ -109,9 +115,11 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let pio = p.PIO0; - let (mut pio0, mut sm0, sm1, sm2, ..) = pio.split(); + let (mut pio0, mut sm0, mut sm1, mut sm2, ..) = pio.split(); setup_pio_task_sm0(&mut pio0, &mut sm0, p.PIN_0.degrade()); + setup_pio_task_sm1(&mut pio0, &mut sm1); + setup_pio_task_sm2(&mut pio0, &mut sm2); spawner.spawn(pio_task_sm0(sm0)).unwrap(); spawner.spawn(pio_task_sm1(sm1)).unwrap(); spawner.spawn(pio_task_sm2(sm2)).unwrap(); diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 145e4a656..7d4919f75 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{PioPeripheral, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{PioCommon, PioPeripheral, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +19,7 @@ fn swap_nibbles(v: u32) -> u32 { async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let pio = p.PIO0; - let (_, mut sm, ..) = pio.split(); + let (mut pio0, mut sm, ..) = pio.split(); let prg = pio_proc::pio_asm!( ".origin 0", @@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - sm.write_instr(relocated.origin() as usize, relocated.code()); + pio0.write_instr(relocated.origin() as usize, relocated.code()); pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); let pio::Wrap { source, target } = relocated.wrap(); diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 211c60c49..041e8ae11 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -19,7 +19,7 @@ pub struct Ws2812 { } impl Ws2812 { - pub fn new(pio: PioCommonInstance

, mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { + pub fn new(mut pio: PioCommonInstance

, mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { // Setup sm0 // prepare the PIO program @@ -50,7 +50,7 @@ impl Ws2812 { let prg = a.assemble_with_wrap(wrap_source, wrap_target); let relocated = RelocatedProgram::new(&prg); - sm.write_instr(relocated.origin() as usize, relocated.code()); + pio.write_instr(relocated.origin() as usize, relocated.code()); pio_instr_util::exec_jmp(&mut sm, relocated.origin()); // Pin config From bcbe3040a1eb9d96feae8f6d665bbbcc5ea41c4e Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 04:14:07 +0200 Subject: [PATCH 0987/1575] tests/rp: fix buffered uart test the rp uart receive fifo is 32 entries deep, so the 31 byte test data fits into it without needing any buffering. extend to 48 bytes to fill the entire fifo and the 16 byte test buffer. --- tests/rp/src/bin/uart_buffered.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index bea9283e7..a1f0e2bf3 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -26,13 +26,13 @@ async fn main(_spawner: Spawner) { // bufferedUart. let data = [ - 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, + 1, 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, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, ]; uart.write_all(&data).await.unwrap(); info!("Done writing"); - let mut buf = [0; 31]; + let mut buf = [0; 48]; uart.read_exact(&mut buf).await.unwrap(); assert_eq!(buf, data); From 7336b8cd88daf5299ee7f5b329028bbb64051fc6 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 08:26:57 +0200 Subject: [PATCH 0988/1575] rp/uart: add UartRx::new_blocking --- embassy-rp/src/uart/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index dedc390f0..f4a93f637 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -229,6 +229,16 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { } impl<'d, T: Instance> UartRx<'d, T, Blocking> { + pub fn new_blocking( + _uart: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + into_ref!(rx); + Uart::::init(None, Some(rx.map_into()), None, None, config); + Self::new_inner(None) + } + #[cfg(feature = "nightly")] pub fn into_buffered( self, From 1d2f6667df1bb0299c4e9b4e1660ee729ce2b463 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 08:04:21 +0200 Subject: [PATCH 0989/1575] rp/uart: add set-break functions sending break conditions is necessary to implement some protocols, and the hardware supports this natively. we do have to make sure that we don't assert a break condition while the uart is busy though, otherwise the break may be inserted before the last character in the tx fifo. --- embassy-rp/src/uart/buffered.rs | 47 +++++++++++++++++++++++++++++++ embassy-rp/src/uart/mod.rs | 49 ++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index cb0461930..b87345ee9 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -5,8 +5,10 @@ use core::task::Poll; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; +use embassy_time::{Duration, Timer}; use super::*; +use crate::clocks::clk_peri_freq; use crate::RegExt; pub struct State { @@ -136,6 +138,14 @@ impl<'d, T: Instance> BufferedUart<'d, T> { self.rx.blocking_read(buffer) } + pub fn busy(&self) -> bool { + self.tx.busy() + } + + pub async fn send_break(&mut self, bits: u32) { + self.tx.send_break(bits).await + } + pub fn split(self) -> (BufferedUartRx<'d, T>, BufferedUartTx<'d, T>) { (self.rx, self.tx) } @@ -371,6 +381,43 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } } } + + pub fn busy(&self) -> bool { + unsafe { T::regs().uartfr().read().busy() } + } + + /// Assert a break condition after waiting for the transmit buffers to empty, + /// for the specified number of bit times. This condition must be asserted + /// for at least two frame times to be effective, `bits` will adjusted + /// according to frame size, parity, and stop bit settings to ensure this. + /// + /// This method may block for a long amount of time since it has to wait + /// for the transmit fifo to empty, which may take a while on slow links. + pub async fn send_break(&mut self, bits: u32) { + let regs = T::regs(); + let bits = bits.max(unsafe { + let lcr = regs.uartlcr_h().read(); + let width = lcr.wlen() as u32 + 5; + let parity = lcr.pen() as u32; + let stops = 1 + lcr.stp2() as u32; + 2 * (1 + width + parity + stops) + }); + let divx64 = unsafe { + ((regs.uartibrd().read().baud_divint() as u32) << 6) + regs.uartfbrd().read().baud_divfrac() as u32 + } as u64; + let div_clk = clk_peri_freq() as u64 * 64; + let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; + + Self::flush().await.unwrap(); + while self.busy() {} + unsafe { + regs.uartlcr_h().write_set(|w| w.set_brk(true)); + } + Timer::after(Duration::from_micros(wait_usecs)).await; + unsafe { + regs.uartlcr_h().write_clear(|w| w.set_brk(true)); + } + } } impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f4a93f637..dd4a1cce2 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1,12 +1,14 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_time::{Duration, Timer}; +use crate::clocks::clk_peri_freq; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::pac::io::vals::{Inover, Outover}; -use crate::{pac, peripherals, Peripheral}; +use crate::{pac, peripherals, Peripheral, RegExt}; #[cfg(feature = "nightly")] mod buffered; @@ -146,6 +148,43 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { unsafe { while !r.uartfr().read().txfe() {} } Ok(()) } + + pub fn busy(&self) -> bool { + unsafe { T::regs().uartfr().read().busy() } + } + + /// Assert a break condition after waiting for the transmit buffers to empty, + /// for the specified number of bit times. This condition must be asserted + /// for at least two frame times to be effective, `bits` will adjusted + /// according to frame size, parity, and stop bit settings to ensure this. + /// + /// This method may block for a long amount of time since it has to wait + /// for the transmit fifo to empty, which may take a while on slow links. + pub async fn send_break(&mut self, bits: u32) { + let regs = T::regs(); + let bits = bits.max(unsafe { + let lcr = regs.uartlcr_h().read(); + let width = lcr.wlen() as u32 + 5; + let parity = lcr.pen() as u32; + let stops = 1 + lcr.stp2() as u32; + 2 * (1 + width + parity + stops) + }); + let divx64 = unsafe { + ((regs.uartibrd().read().baud_divint() as u32) << 6) + regs.uartfbrd().read().baud_divfrac() as u32 + } as u64; + let div_clk = clk_peri_freq() as u64 * 64; + let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; + + self.blocking_flush().unwrap(); + while self.busy() {} + unsafe { + regs.uartlcr_h().write_set(|w| w.set_brk(true)); + } + Timer::after(Duration::from_micros(wait_usecs)).await; + unsafe { + regs.uartlcr_h().write_clear(|w| w.set_brk(true)); + } + } } impl<'d, T: Instance> UartTx<'d, T, Blocking> { @@ -526,6 +565,14 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { self.rx.blocking_read(buffer) } + pub fn busy(&self) -> bool { + self.tx.busy() + } + + pub async fn send_break(&mut self, bits: u32) { + self.tx.send_break(bits).await + } + /// Split the Uart into a transmitter and receiver, which is particuarly /// useful when having two tasks correlating to transmitting and receiving. pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { From 19588a9e6fb4474a2b3bdb64a97db9798027c700 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 1 May 2023 15:22:39 +0200 Subject: [PATCH 0990/1575] rp/uart: rename state to buffered_state we'll add a dma state soon as well. --- embassy-rp/src/uart/buffered.rs | 24 ++++++++++++------------ embassy-rp/src/uart/mod.rs | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index b87345ee9..f9fae4c50 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -47,7 +47,7 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], ) { - let state = T::state(); + let state = T::buffered_state(); let len = tx_buffer.len(); unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; let len = rx_buffer.len(); @@ -189,7 +189,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { return Poll::Ready(Ok(0)); } - let state = T::state(); + let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { let n = data.len().min(buf.len()); @@ -221,7 +221,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { } loop { - let state = T::state(); + let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let n = rx_reader.pop(|data| { let n = data.len().min(buf.len()); @@ -247,7 +247,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { fn fill_buf<'a>() -> impl Future> { poll_fn(move |cx| { - let state = T::state(); + let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; let (p, n) = rx_reader.pop_buf(); if n == 0 { @@ -261,7 +261,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { } fn consume(amt: usize) { - let state = T::state(); + let state = T::buffered_state(); let mut rx_reader = unsafe { state.rx_buf.reader() }; rx_reader.pop_done(amt); @@ -315,7 +315,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { return Poll::Ready(Ok(0)); } - let state = T::state(); + let state = T::buffered_state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; let n = tx_writer.push(|data| { let n = data.len().min(buf.len()); @@ -338,7 +338,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { fn flush() -> impl Future> { poll_fn(move |cx| { - let state = T::state(); + let state = T::buffered_state(); if !state.tx_buf.is_empty() { state.tx_waker.register(cx.waker()); return Poll::Pending; @@ -354,7 +354,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } loop { - let state = T::state(); + let state = T::buffered_state(); let mut tx_writer = unsafe { state.tx_buf.writer() }; let n = tx_writer.push(|data| { let n = data.len().min(buf.len()); @@ -375,7 +375,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { pub fn blocking_flush(&mut self) -> Result<(), Error> { loop { - let state = T::state(); + let state = T::buffered_state(); if state.tx_buf.is_empty() { return Ok(()); } @@ -422,7 +422,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { fn drop(&mut self) { - let state = T::state(); + let state = T::buffered_state(); unsafe { state.rx_buf.deinit(); @@ -437,7 +437,7 @@ impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { fn drop(&mut self) { - let state = T::state(); + let state = T::buffered_state(); unsafe { state.tx_buf.deinit(); @@ -452,7 +452,7 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { pub(crate) unsafe fn on_interrupt(_: *mut ()) { let r = T::regs(); - let s = T::state(); + let s = T::buffered_state(); unsafe { // Clear TX and error interrupt flags diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index dd4a1cce2..0b323978d 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -763,7 +763,7 @@ mod sealed { fn regs() -> pac::uart::Uart; #[cfg(feature = "nightly")] - fn state() -> &'static buffered::State; + fn buffered_state() -> &'static buffered::State; } pub trait TxPin {} pub trait RxPin {} @@ -801,7 +801,7 @@ macro_rules! impl_instance { } #[cfg(feature = "nightly")] - fn state() -> &'static buffered::State { + fn buffered_state() -> &'static buffered::State { static STATE: buffered::State = buffered::State::new(); &STATE } From 1c8492bab2534bd04389f055affe12c4de0e3857 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 08:27:19 +0200 Subject: [PATCH 0991/1575] tests/rp: test error conditions for uart --- tests/rp/src/bin/uart.rs | 159 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 11 deletions(-) diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs index 92f61464e..80c18c02e 100644 --- a/tests/rp/src/bin/uart.rs +++ b/tests/rp/src/bin/uart.rs @@ -4,28 +4,165 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_rp::uart::{Config, Uart}; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::uart::{Blocking, Config, Error, Instance, Parity, Uart, UartRx}; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +fn read(uart: &mut Uart<'_, impl Instance, Blocking>) -> Result<[u8; N], Error> { + let mut buf = [255; N]; + uart.blocking_read(&mut buf)?; + Ok(buf) +} + +fn read1(uart: &mut UartRx<'_, impl Instance, Blocking>) -> Result<[u8; N], Error> { + let mut buf = [255; N]; + uart.blocking_read(&mut buf)?; + Ok(buf) +} + +async fn send(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: Option) { + pin.set_low(); + Timer::after(Duration::from_millis(1)).await; + for i in 0..8 { + if v & (1 << i) == 0 { + pin.set_low(); + } else { + pin.set_high(); + } + Timer::after(Duration::from_millis(1)).await; + } + if let Some(b) = parity { + if b { + pin.set_high(); + } else { + pin.set_low(); + } + Timer::after(Duration::from_millis(1)).await; + } + pin.set_high(); + Timer::after(Duration::from_millis(1)).await; +} + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); - let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); - let config = Config::default(); - let mut uart = Uart::new_blocking(uart, tx, rx, config); + { + let config = Config::default(); + let mut uart = Uart::new_blocking(&mut uart, &mut tx, &mut rx, config); - // We can't send too many bytes, they have to fit in the FIFO. - // This is because we aren't sending+receiving at the same time. + // We can't send too many bytes, they have to fit in the FIFO. + // This is because we aren't sending+receiving at the same time. - let data = [0xC0, 0xDE]; - uart.blocking_write(&data).unwrap(); + let data = [0xC0, 0xDE]; + uart.blocking_write(&data).unwrap(); + assert_eq!(read(&mut uart).unwrap(), data); + } - let mut buf = [0; 2]; - uart.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, data); + info!("test overflow detection"); + { + let config = Config::default(); + let mut uart = Uart::new_blocking(&mut uart, &mut tx, &mut rx, config); + + let data = [ + 1, 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, + ]; + let overflow = [ + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + ]; + uart.blocking_write(&data).unwrap(); + uart.blocking_write(&overflow).unwrap(); + while uart.busy() {} + + // prefix in fifo is valid + assert_eq!(read(&mut uart).unwrap(), data); + // next received character causes overrun error and is discarded + uart.blocking_write(&[1, 2, 3]).unwrap(); + assert_eq!(read::<1>(&mut uart).unwrap_err(), Error::Overrun); + assert_eq!(read(&mut uart).unwrap(), [2, 3]); + } + + info!("test break detection"); + { + let config = Config::default(); + let mut uart = Uart::new_blocking(&mut uart, &mut tx, &mut rx, config); + + // break on empty fifo + uart.send_break(20).await; + uart.blocking_write(&[64]).unwrap(); + assert_eq!(read::<1>(&mut uart).unwrap_err(), Error::Break); + assert_eq!(read(&mut uart).unwrap(), [64]); + + // break on partially filled fifo + uart.blocking_write(&[65; 2]).unwrap(); + uart.send_break(20).await; + uart.blocking_write(&[66]).unwrap(); + assert_eq!(read(&mut uart).unwrap(), [65; 2]); + assert_eq!(read::<1>(&mut uart).unwrap_err(), Error::Break); + assert_eq!(read(&mut uart).unwrap(), [66]); + } + + // parity detection. here we bitbang to not require two uarts. + info!("test parity error detection"); + { + let mut pin = Output::new(&mut tx, Level::High); + let mut config = Config::default(); + config.baudrate = 1000; + config.parity = Parity::ParityEven; + let mut uart = UartRx::new_blocking(&mut uart, &mut rx, config); + + async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u8) { + send(pin, v, Some(parity != 0)).await; + } + + // first check that we can send correctly + chr(&mut pin, 64, 1).await; + assert_eq!(read1(&mut uart).unwrap(), [64]); + + // all good, check real errors + chr(&mut pin, 2, 1).await; + chr(&mut pin, 3, 0).await; + chr(&mut pin, 4, 0).await; + chr(&mut pin, 5, 0).await; + assert_eq!(read1(&mut uart).unwrap(), [2, 3]); + assert_eq!(read1::<1>(&mut uart).unwrap_err(), Error::Parity); + assert_eq!(read1(&mut uart).unwrap(), [5]); + } + + // framing error detection. here we bitbang because there's no other way. + info!("test framing error detection"); + { + let mut pin = Output::new(&mut tx, Level::High); + let mut config = Config::default(); + config.baudrate = 1000; + let mut uart = UartRx::new_blocking(&mut uart, &mut rx, config); + + async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) { + if good { + send(pin, v, None).await; + } else { + send(pin, v, Some(false)).await; + } + } + + // first check that we can send correctly + chr(&mut pin, 64, true).await; + assert_eq!(read1(&mut uart).unwrap(), [64]); + + // all good, check real errors + chr(&mut pin, 2, true).await; + chr(&mut pin, 3, true).await; + chr(&mut pin, 4, false).await; + chr(&mut pin, 5, true).await; + assert_eq!(read1(&mut uart).unwrap(), [2, 3]); + assert_eq!(read1::<1>(&mut uart).unwrap_err(), Error::Framing); + assert_eq!(read1(&mut uart).unwrap(), [5]); + } info!("Test OK"); cortex_m::asm::bkpt(); From 7ab9fe0522de81583592bd09c5c57910bed1c181 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 07:05:42 +0200 Subject: [PATCH 0992/1575] rp/uart: extract common code from async and blocking buffered reads once we add error propagation the common code will become even larger, so it makes sense to move it out. --- embassy-rp/src/uart/buffered.rs | 86 +++++++++++++++------------------ 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index f9fae4c50..3fe0c8c4e 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -183,64 +183,56 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { Self { phantom: PhantomData } } - fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a { + fn read<'a>(buf: &'a mut [u8]) -> impl Future> + 'a + where + T: 'd, + { poll_fn(move |cx| { - if buf.is_empty() { - return Poll::Ready(Ok(0)); + if let Poll::Ready(r) = Self::try_read(buf) { + return Poll::Ready(r); } - - let state = T::buffered_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; - } - - // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. - let regs = T::regs(); - unsafe { - regs.uartimsc().write_set(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); - } - - Poll::Ready(Ok(n)) + T::buffered_state().rx_waker.register(cx.waker()); + Poll::Pending }) } - pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result { + fn try_read(buf: &mut [u8]) -> Poll> { if buf.is_empty() { - return Ok(0); + return Poll::Ready(Ok(0)); } - loop { - let state = T::buffered_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 + let state = T::buffered_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 + }); + + let result = if n == 0 { + return Poll::Pending; + } else { + Ok(n) + }; + + // (Re-)Enable the interrupt to receive more data in case it was + // disabled because the buffer was full. + let regs = T::regs(); + unsafe { + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); }); + } - if n > 0 { - // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. - let regs = T::regs(); - unsafe { - regs.uartimsc().write_set(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); - } + Poll::Ready(result) + } - return Ok(n); + pub fn blocking_read(&mut self, buf: &mut [u8]) -> Result { + loop { + match Self::try_read(buf) { + Poll::Ready(res) => return res, + Poll::Pending => continue, } } } From 861f49cfd4380d89aa13d507234a97b30a84473d Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 04:21:11 +0200 Subject: [PATCH 0993/1575] rp/uart: report errors from buffered uart this reports errors at the same location the blocking uart would, which works out to being mostly exact (except in the case of overruns, where one extra character is dropped). this is actually easier than going nuclear in the case of errors and nuking both the buffer contents and the rx fifo, both of which are things we'd have to do in addition to what's added here, and neither are needed for correctness. --- embassy-rp/src/uart/buffered.rs | 86 +++++++++-- tests/rp/src/bin/uart_buffered.rs | 249 +++++++++++++++++++++++++++--- 2 files changed, 301 insertions(+), 34 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 3fe0c8c4e..58fede765 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -2,6 +2,7 @@ use core::future::{poll_fn, Future}; use core::slice; use core::task::Poll; +use atomic_polyfill::{AtomicU8, Ordering}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; @@ -16,8 +17,15 @@ pub struct State { tx_buf: RingBuffer, rx_waker: AtomicWaker, rx_buf: RingBuffer, + rx_error: AtomicU8, } +// these must match bits 8..11 in UARTDR +const RXE_OVERRUN: u8 = 8; +const RXE_BREAK: u8 = 4; +const RXE_PARITY: u8 = 2; +const RXE_FRAMING: u8 = 1; + impl State { pub const fn new() -> Self { Self { @@ -25,6 +33,7 @@ impl State { tx_buf: RingBuffer::new(), rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), + rx_error: AtomicU8::new(0), } } } @@ -196,7 +205,25 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }) } - fn try_read(buf: &mut [u8]) -> Poll> { + fn get_rx_error() -> Option { + let errs = T::buffered_state().rx_error.swap(0, Ordering::Relaxed); + if errs & RXE_OVERRUN != 0 { + Some(Error::Overrun) + } else if errs & RXE_BREAK != 0 { + Some(Error::Break) + } else if errs & RXE_PARITY != 0 { + Some(Error::Parity) + } else if errs & RXE_FRAMING != 0 { + Some(Error::Framing) + } else { + None + } + } + + fn try_read(buf: &mut [u8]) -> Poll> + where + T: 'd, + { if buf.is_empty() { return Poll::Ready(Ok(0)); } @@ -210,13 +237,16 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { }); let result = if n == 0 { - return Poll::Pending; + match Self::get_rx_error() { + None => return Poll::Pending, + Some(e) => Err(e), + } } else { Ok(n) }; // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. + // disabled because the buffer was full or errors were detected. let regs = T::regs(); unsafe { regs.uartimsc().write_set(|w| { @@ -237,18 +267,28 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { } } - fn fill_buf<'a>() -> impl Future> { + fn fill_buf<'a>() -> impl Future> + where + T: 'd, + { poll_fn(move |cx| { let state = T::buffered_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 result = if n == 0 { + match Self::get_rx_error() { + None => { + state.rx_waker.register(cx.waker()); + return Poll::Pending; + } + Some(e) => Err(e), + } + } else { + let buf = unsafe { slice::from_raw_parts(p, n) }; + Ok(buf) + }; - let buf = unsafe { slice::from_raw_parts(p, n) }; - Poll::Ready(Ok(buf)) + Poll::Ready(result) }) } @@ -258,7 +298,7 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { rx_reader.pop_done(amt); // (Re-)Enable the interrupt to receive more data in case it was - // disabled because the buffer was full. + // disabled because the buffer was full or errors were detected. let regs = T::regs(); unsafe { regs.uartimsc().write_set(|w| { @@ -478,19 +518,37 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { let mut rx_writer = s.rx_buf.writer(); let rx_buf = rx_writer.push_slice(); let mut n_read = 0; + let mut error = false; for rx_byte in rx_buf { if r.uartfr().read().rxfe() { break; } - *rx_byte = r.uartdr().read().data(); + let dr = r.uartdr().read(); + if (dr.0 >> 8) != 0 { + s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); + error = true; + // only fill the buffer with valid characters. the current character is fine + // if the error is an overrun, but if we add it to the buffer we'll report + // the overrun one character too late. drop it instead and pretend we were + // a bit slower at draining the rx fifo than we actually were. + // this is consistent with blocking uart error reporting. + break; + } + *rx_byte = dr.data(); n_read += 1; } if n_read > 0 { rx_writer.push_done(n_read); s.rx_waker.wake(); + } else if error { + s.rx_waker.wake(); } - // Disable any further RX interrupts when the buffer becomes full. - if s.rx_buf.is_full() { + // Disable any further RX interrupts when the buffer becomes full or + // errors have occured. this lets us buffer additional errors in the + // fifo without needing more error storage locations, and most applications + // will want to do a full reset of their uart state anyway once an error + // has happened. + if s.rx_buf.is_full() || error { r.uartimsc().write_clear(|w| { w.set_rxim(true); w.set_rtim(true); diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index a1f0e2bf3..1deb22ce6 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -2,39 +2,248 @@ #![no_main] #![feature(type_alias_impl_trait)] -use defmt::{assert_eq, *}; +use defmt::{assert_eq, panic, *}; use embassy_executor::Spawner; +use embassy_rp::gpio::{Level, Output}; use embassy_rp::interrupt; -use embassy_rp::uart::{BufferedUart, Config}; -use embedded_io::asynch::{Read, Write}; +use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::{Read, ReadExactError, Write}; use {defmt_rtt as _, panic_probe as _}; +async fn read(uart: &mut BufferedUart<'_, impl Instance>) -> Result<[u8; N], Error> { + let mut buf = [255; N]; + match uart.read_exact(&mut buf).await { + Ok(()) => Ok(buf), + // we should not ever produce an Eof condition + Err(ReadExactError::UnexpectedEof) => panic!(), + Err(ReadExactError::Other(e)) => Err(e), + } +} + +async fn read1(uart: &mut BufferedUartRx<'_, impl Instance>) -> Result<[u8; N], Error> { + let mut buf = [255; N]; + match uart.read_exact(&mut buf).await { + Ok(()) => Ok(buf), + // we should not ever produce an Eof condition + Err(ReadExactError::UnexpectedEof) => panic!(), + Err(ReadExactError::Other(e)) => Err(e), + } +} + +async fn send(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: Option) { + pin.set_low(); + Timer::after(Duration::from_millis(1)).await; + for i in 0..8 { + if v & (1 << i) == 0 { + pin.set_low(); + } else { + pin.set_high(); + } + Timer::after(Duration::from_millis(1)).await; + } + if let Some(b) = parity { + if b { + pin.set_high(); + } else { + pin.set_low(); + } + Timer::after(Duration::from_millis(1)).await; + } + pin.set_high(); + Timer::after(Duration::from_millis(1)).await; +} + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Hello World!"); - let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); + let mut irq = interrupt::take!(UART0_IRQ); - let config = Config::default(); - let irq = interrupt::take!(UART0_IRQ); - let tx_buf = &mut [0u8; 16]; - let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUart::new(uart, irq, tx, rx, tx_buf, rx_buf, config); + { + let config = Config::default(); + let tx_buf = &mut [0u8; 16]; + let rx_buf = &mut [0u8; 16]; + let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); - // Make sure we send more bytes than fits in the FIFO, to test the actual - // bufferedUart. + // Make sure we send more bytes than fits in the FIFO, to test the actual + // bufferedUart. - let data = [ - 1, 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, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - ]; - uart.write_all(&data).await.unwrap(); - info!("Done writing"); + let data = [ + 1, 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, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + ]; + uart.write_all(&data).await.unwrap(); + info!("Done writing"); - let mut buf = [0; 48]; - uart.read_exact(&mut buf).await.unwrap(); - assert_eq!(buf, data); + assert_eq!(read(&mut uart).await.unwrap(), data); + } + + info!("test overflow detection"); + { + let config = Config::default(); + let tx_buf = &mut [0u8; 16]; + let rx_buf = &mut [0u8; 16]; + let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut 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, 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, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + ]; + let overflow = [ + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + ]; + // give each block time to settle into the fifo. we want the overrun to occur at a well-defined point. + uart.write_all(&data).await.unwrap(); + uart.blocking_flush().unwrap(); + while uart.busy() {} + uart.write_all(&overflow).await.unwrap(); + uart.blocking_flush().unwrap(); + while uart.busy() {} + + // already buffered/fifod prefix is valid + assert_eq!(read(&mut uart).await.unwrap(), data); + // next received character causes overrun error and is discarded + uart.write_all(&[1, 2, 3]).await.unwrap(); + uart.blocking_flush().unwrap(); + assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Overrun); + assert_eq!(read(&mut uart).await.unwrap(), [2, 3]); + } + + info!("test break detection"); + { + let mut config = Config::default(); + config.baudrate = 1000; + let tx_buf = &mut [0u8; 16]; + let rx_buf = &mut [0u8; 16]; + let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); + + // break on empty buffer + uart.send_break(20).await; + assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break); + uart.write_all(&[64]).await.unwrap(); + assert_eq!(read(&mut uart).await.unwrap(), [64]); + + // break on partially filled buffer + uart.write_all(&[65; 2]).await.unwrap(); + uart.send_break(20).await; + uart.write_all(&[66]).await.unwrap(); + assert_eq!(read(&mut uart).await.unwrap(), [65; 2]); + assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break); + assert_eq!(read(&mut uart).await.unwrap(), [66]); + + // break on full buffer + uart.write_all(&[64; 16]).await.unwrap(); + uart.send_break(20).await; + uart.write_all(&[65]).await.unwrap(); + assert_eq!(read(&mut uart).await.unwrap(), [64; 16]); + assert_eq!(read::<1>(&mut uart).await.unwrap_err(), Error::Break); + assert_eq!(read(&mut uart).await.unwrap(), [65]); + } + + // parity detection. here we bitbang to not require two uarts. + info!("test parity error detection"); + { + let mut pin = Output::new(&mut tx, Level::High); + // choose a very slow baud rate to make tests reliable even with O0 + let mut config = Config::default(); + config.baudrate = 1000; + config.parity = Parity::ParityEven; + let rx_buf = &mut [0u8; 16]; + let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config); + + async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u32) { + send(pin, v, Some(parity != 0)).await; + } + + // first check that we can send correctly + chr(&mut pin, 64, 1).await; + assert_eq!(read1(&mut uart).await.unwrap(), [64]); + + // parity on empty buffer + chr(&mut pin, 64, 0).await; + chr(&mut pin, 4, 1).await; + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity); + assert_eq!(read1(&mut uart).await.unwrap(), [4]); + + // parity on partially filled buffer + chr(&mut pin, 64, 1).await; + chr(&mut pin, 32, 1).await; + chr(&mut pin, 64, 0).await; + chr(&mut pin, 65, 0).await; + assert_eq!(read1(&mut uart).await.unwrap(), [64, 32]); + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity); + assert_eq!(read1(&mut uart).await.unwrap(), [65]); + + // parity on full buffer + for i in 0..16 { + chr(&mut pin, i, i.count_ones() % 2).await; + } + chr(&mut pin, 64, 0).await; + chr(&mut pin, 65, 0).await; + assert_eq!( + read1(&mut uart).await.unwrap(), + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + ); + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity); + assert_eq!(read1(&mut uart).await.unwrap(), [65]); + } + + // framing error detection. here we bitbang because there's no other way. + info!("test framing error detection"); + { + let mut pin = Output::new(&mut tx, Level::High); + // choose a very slow baud rate to make tests reliable even with O0 + let mut config = Config::default(); + config.baudrate = 1000; + let rx_buf = &mut [0u8; 16]; + let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config); + + async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) { + if good { + send(pin, v, None).await; + } else { + send(pin, v, Some(false)).await; + } + } + + // first check that we can send correctly + chr(&mut pin, 64, true).await; + assert_eq!(read1(&mut uart).await.unwrap(), [64]); + + // framing on empty buffer + chr(&mut pin, 64, false).await; + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing); + chr(&mut pin, 65, true).await; + assert_eq!(read1(&mut uart).await.unwrap(), [65]); + + // framing on partially filled buffer + chr(&mut pin, 64, true).await; + chr(&mut pin, 32, true).await; + chr(&mut pin, 64, false).await; + chr(&mut pin, 65, true).await; + assert_eq!(read1(&mut uart).await.unwrap(), [64, 32]); + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing); + assert_eq!(read1(&mut uart).await.unwrap(), [65]); + + // framing on full buffer + for i in 0..16 { + chr(&mut pin, i, true).await; + } + chr(&mut pin, 64, false).await; + chr(&mut pin, 65, true).await; + assert_eq!( + read1(&mut uart).await.unwrap(), + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] + ); + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing); + assert_eq!(read1(&mut uart).await.unwrap(), [65]); + } info!("Test OK"); cortex_m::asm::bkpt(); From be66e0f7cec742d580a224f12a79b2ab2ca78e37 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 09:30:10 +0200 Subject: [PATCH 0994/1575] rp/uart: make dma multicore-safe running rx and tx on different cores could lead to hangs if the dmacr register modifys run concurrently. this is bad. --- embassy-rp/src/uart/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 0b323978d..f198afe2f 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -206,7 +206,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { let ch = self.tx_dma.as_mut().unwrap(); let transfer = unsafe { - T::regs().uartdmacr().modify(|reg| { + T::regs().uartdmacr().write_set(|reg| { reg.set_txdmae(true); }); // If we don't assign future to a variable, the data register pointer @@ -296,7 +296,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { let ch = self.rx_dma.as_mut().unwrap(); let transfer = unsafe { - T::regs().uartdmacr().modify(|reg| { + T::regs().uartdmacr().write_set(|reg| { reg.set_rxdmae(true); }); // If we don't assign future to a variable, the data register pointer From 1d5adb8974964203e47c47cb590b906b6a46ba82 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Apr 2023 11:30:55 +0200 Subject: [PATCH 0995/1575] rp/uart: extract fifo draining from blocking_read this will also be needed for dma operations. --- embassy-rp/src/uart/mod.rs | 52 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f198afe2f..8612e9cda 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -238,33 +238,37 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { } } - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let r = T::regs(); - unsafe { - for b in buffer { - *b = loop { - if r.uartfr().read().rxfe() { - continue; - } - - let dr = r.uartdr().read(); - - if dr.oe() { - return Err(Error::Overrun); - } else if dr.be() { - return Err(Error::Break); - } else if dr.pe() { - return Err(Error::Parity); - } else if dr.fe() { - return Err(Error::Framing); - } else { - break dr.data(); - } - }; - } + pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { + while buffer.len() > 0 { + let received = self.drain_fifo(buffer)?; + buffer = &mut buffer[received..]; } Ok(()) } + + fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result { + let r = T::regs(); + for (i, b) in buffer.iter_mut().enumerate() { + if unsafe { r.uartfr().read().rxfe() } { + return Ok(i); + } + + let dr = unsafe { r.uartdr().read() }; + + if dr.oe() { + return Err(Error::Overrun); + } else if dr.be() { + return Err(Error::Break); + } else if dr.pe() { + return Err(Error::Parity); + } else if dr.fe() { + return Err(Error::Framing); + } else { + *b = dr.data(); + } + } + Ok(buffer.len()) + } } impl<'d, T: Instance> UartRx<'d, T, Blocking> { From b58b9ff390fb885f0cca2ad15fc89d537f3a9818 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 29 Apr 2023 10:30:04 +0200 Subject: [PATCH 0996/1575] rp/uart: report errors from dma receive --- embassy-rp/src/uart/buffered.rs | 2 +- embassy-rp/src/uart/mod.rs | 151 ++++++++++++++++-- examples/rp/src/bin/uart_unidir.rs | 9 +- tests/rp/src/bin/uart_dma.rs | 238 +++++++++++++++++++++++++++-- 4 files changed, 375 insertions(+), 25 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 58fede765..59fec8f1b 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -74,7 +74,7 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( // to pend the ISR when we want data transmission to start. let regs = T::regs(); unsafe { - regs.uartimsc().write_set(|w| { + regs.uartimsc().write(|w| { w.set_rxim(true); w.set_rtim(true); w.set_txim(true); diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 8612e9cda..ebe32cc66 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1,7 +1,14 @@ +use core::future::poll_fn; use core::marker::PhantomData; +use core::task::Poll; +use atomic_polyfill::{AtomicU16, Ordering}; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_futures::select::{select, Either}; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; +use pac::uart::regs::Uartris; use crate::clocks::clk_peri_freq; use crate::dma::{AnyChannel, Channel}; @@ -97,6 +104,11 @@ pub enum Error { Framing, } +pub struct DmaState { + rx_err_waker: AtomicWaker, + rx_errs: AtomicU16, +} + pub struct Uart<'d, T: Instance, M: Mode> { tx: UartTx<'d, T, M>, rx: UartRx<'d, T, M>, @@ -223,15 +235,26 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { pub fn new( _uart: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(rx, rx_dma); + into_ref!(rx, irq, rx_dma); Uart::::init(None, Some(rx.map_into()), None, None, config); - Self::new_inner(Some(rx_dma.map_into())) + Self::new_inner(Some(irq), Some(rx_dma.map_into())) } - fn new_inner(rx_dma: Option>) -> Self { + fn new_inner(irq: Option>, rx_dma: Option>) -> Self { + debug_assert_eq!(irq.is_some(), rx_dma.is_some()); + if let Some(irq) = irq { + unsafe { + // disable all error interrupts initially + T::regs().uartimsc().write(|w| w.0 = 0); + } + irq.set_handler(on_interrupt::); + irq.unpend(); + irq.enable(); + } Self { rx_dma, phantom: PhantomData, @@ -271,6 +294,16 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { } } +impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { + fn drop(&mut self) { + if let Some(_) = self.rx_dma { + unsafe { + T::Interrupt::steal().disable(); + } + } + } +} + impl<'d, T: Instance> UartRx<'d, T, Blocking> { pub fn new_blocking( _uart: impl Peripheral

+ 'd, @@ -279,7 +312,7 @@ impl<'d, T: Instance> UartRx<'d, T, Blocking> { ) -> Self { into_ref!(rx); Uart::::init(None, Some(rx.map_into()), None, None, config); - Self::new_inner(None) + Self::new_inner(None, None) } #[cfg(feature = "nightly")] @@ -296,19 +329,93 @@ impl<'d, T: Instance> UartRx<'d, T, Blocking> { } } +unsafe fn on_interrupt(_: *mut ()) { + let uart = T::regs(); + let state = T::dma_state(); + let errs = uart.uartris().read(); + state.rx_errs.store(errs.0 as u16, Ordering::Relaxed); + state.rx_err_waker.wake(); + // disable the error interrupts instead of clearing the flags. clearing the + // flags would allow the dma transfer to continue, potentially signaling + // completion before we can check for errors that happened *during* the transfer. + uart.uartimsc().write_clear(|w| w.0 = errs.0); +} + impl<'d, T: Instance> UartRx<'d, T, Async> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + // clear error flags before we drain the fifo. errors that have accumulated + // in the flags will also be present in the fifo. + T::dma_state().rx_errs.store(0, Ordering::Relaxed); + unsafe { + T::regs().uarticr().write(|w| { + w.set_oeic(true); + w.set_beic(true); + w.set_peic(true); + w.set_feic(true); + }); + } + + // then drain the fifo. we need to read at most 32 bytes. errors that apply + // to fifo bytes will be reported directly. + let buffer = match { + let limit = buffer.len().min(32); + self.drain_fifo(&mut buffer[0..limit]) + } { + Ok(len) if len < buffer.len() => &mut buffer[len..], + Ok(_) => return Ok(()), + Err(e) => return Err(e), + }; + + // start a dma transfer. if errors have happened in the interim some error + // interrupt flags will have been raised, and those will be picked up immediately + // by the interrupt handler. let ch = self.rx_dma.as_mut().unwrap(); let transfer = unsafe { + T::regs().uartimsc().write_set(|w| { + w.set_oeim(true); + w.set_beim(true); + w.set_peim(true); + w.set_feim(true); + }); T::regs().uartdmacr().write_set(|reg| { reg.set_rxdmae(true); + reg.set_dmaonerr(true); }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer, T::RX_DREQ) }; - transfer.await; - Ok(()) + + // wait for either the transfer to complete or an error to happen. + let transfer_result = select( + transfer, + poll_fn(|cx| { + T::dma_state().rx_err_waker.register(cx.waker()); + match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { + 0 => Poll::Pending, + e => Poll::Ready(Uartris(e as u32)), + } + }), + ) + .await; + + let errors = match transfer_result { + Either::First(()) => return Ok(()), + Either::Second(e) => e, + }; + + if errors.0 == 0 { + return Ok(()); + } else if errors.oeris() { + return Err(Error::Overrun); + } else if errors.beris() { + return Err(Error::Break); + } else if errors.peris() { + return Err(Error::Parity); + } else if errors.feris() { + return Err(Error::Framing); + } + unreachable!("unrecognized rx error"); } } @@ -321,7 +428,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(tx, rx); - Self::new_inner(uart, tx.map_into(), rx.map_into(), None, None, None, None, config) + Self::new_inner(uart, tx.map_into(), rx.map_into(), None, None, None, None, None, config) } /// Create a new UART with hardware flow control (RTS/CTS) @@ -342,6 +449,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { Some(cts.map_into()), None, None, + None, config, ) } @@ -370,17 +478,19 @@ impl<'d, T: Instance> Uart<'d, T, Async> { uart: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(tx, rx, tx_dma, rx_dma); + into_ref!(tx, rx, irq, tx_dma, rx_dma); Self::new_inner( uart, tx.map_into(), rx.map_into(), None, None, + Some(irq), Some(tx_dma.map_into()), Some(rx_dma.map_into()), config, @@ -394,17 +504,19 @@ impl<'d, T: Instance> Uart<'d, T, Async> { rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, + irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(tx, rx, cts, rts, tx_dma, rx_dma); + into_ref!(tx, rx, cts, rts, irq, tx_dma, rx_dma); Self::new_inner( uart, tx.map_into(), rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), + Some(irq), Some(tx_dma.map_into()), Some(rx_dma.map_into()), config, @@ -419,6 +531,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { mut rx: PeripheralRef<'d, AnyPin>, mut rts: Option>, mut cts: Option>, + irq: Option>, tx_dma: Option>, rx_dma: Option>, config: Config, @@ -433,7 +546,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { Self { tx: UartTx::new_inner(tx_dma), - rx: UartRx::new_inner(rx_dma), + rx: UartRx::new_inner(irq, rx_dma), } } @@ -761,6 +874,7 @@ mod sealed { pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; + const ID: usize; type Interrupt: crate::interrupt::Interrupt; @@ -768,6 +882,8 @@ mod sealed { #[cfg(feature = "nightly")] fn buffered_state() -> &'static buffered::State; + + fn dma_state() -> &'static DmaState; } pub trait TxPin {} pub trait RxPin {} @@ -793,10 +909,11 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { + ($inst:ident, $irq:ident, $id:expr, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + const ID: usize = $id; type Interrupt = crate::interrupt::$irq; @@ -809,13 +926,21 @@ macro_rules! impl_instance { static STATE: buffered::State = buffered::State::new(); &STATE } + + fn dma_state() -> &'static DmaState { + static STATE: DmaState = DmaState { + rx_err_waker: AtomicWaker::new(), + rx_errs: AtomicU16::new(0), + }; + &STATE + } } impl Instance for peripherals::$inst {} }; } -impl_instance!(UART0, UART0_IRQ, 20, 21); -impl_instance!(UART1, UART1_IRQ, 22, 23); +impl_instance!(UART0, UART0_IRQ, 0, 20, 21); +impl_instance!(UART1, UART1_IRQ, 1, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index f56e7009f..4119a309f 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs @@ -7,6 +7,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::interrupt; use embassy_rp::peripherals::UART1; use embassy_rp::uart::{Async, Config, UartRx, UartTx}; use embassy_time::{Duration, Timer}; @@ -17,7 +18,13 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); - let uart_rx = UartRx::new(p.UART1, p.PIN_5, p.DMA_CH1, Config::default()); + let uart_rx = UartRx::new( + p.UART1, + p.PIN_5, + interrupt::take!(UART1_IRQ), + p.DMA_CH1, + Config::default(), + ); unwrap!(spawner.spawn(reader(uart_rx))); diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index 963c22707..92aa205c9 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -4,28 +4,246 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_rp::uart::{Config, Uart}; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::interrupt; +use embassy_rp::uart::{Async, Config, Error, Instance, Parity, Uart, UartRx}; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +async fn read(uart: &mut Uart<'_, impl Instance, Async>) -> Result<[u8; N], Error> { + let mut buf = [255; N]; + uart.read(&mut buf).await?; + Ok(buf) +} + +async fn read1(uart: &mut UartRx<'_, impl Instance, Async>) -> Result<[u8; N], Error> { + let mut buf = [255; N]; + uart.read(&mut buf).await?; + Ok(buf) +} + +async fn send(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: Option) { + pin.set_low(); + Timer::after(Duration::from_millis(1)).await; + for i in 0..8 { + if v & (1 << i) == 0 { + pin.set_low(); + } else { + pin.set_high(); + } + Timer::after(Duration::from_millis(1)).await; + } + if let Some(b) = parity { + if b { + pin.set_high(); + } else { + pin.set_low(); + } + Timer::after(Duration::from_millis(1)).await; + } + pin.set_high(); + Timer::after(Duration::from_millis(1)).await; +} + #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_rp::init(Default::default()); + let mut p = embassy_rp::init(Default::default()); info!("Hello World!"); - let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); + let mut irq = interrupt::take!(UART0_IRQ); - let config = Config::default(); - let mut uart = Uart::new(uart, tx, rx, p.DMA_CH0, p.DMA_CH1, config); + // TODO + // nuclear error reporting. just abort the entire transfer and invalidate the + // dma buffer, buffered buffer, fifo etc. // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. + { + let config = Config::default(); + let mut uart = Uart::new( + &mut uart, + &mut tx, + &mut rx, + &mut irq, + &mut p.DMA_CH0, + &mut p.DMA_CH1, + config, + ); - let data = [0xC0, 0xDE]; - uart.write(&data).await.unwrap(); + let data = [0xC0, 0xDE]; + uart.write(&data).await.unwrap(); - let mut buf = [0; 2]; - uart.read(&mut buf).await.unwrap(); - assert_eq!(buf, data); + let mut buf = [0; 2]; + uart.read(&mut buf).await.unwrap(); + assert_eq!(buf, data); + } + + info!("test overflow detection"); + { + let config = Config::default(); + let mut uart = Uart::new( + &mut uart, + &mut tx, + &mut rx, + &mut irq, + &mut p.DMA_CH0, + &mut p.DMA_CH1, + config, + ); + + uart.blocking_write(&[42; 32]).unwrap(); + uart.blocking_write(&[1, 2, 3]).unwrap(); + uart.blocking_flush().unwrap(); + + // can receive regular fifo contents + assert_eq!(read(&mut uart).await, Ok([42; 16])); + assert_eq!(read(&mut uart).await, Ok([42; 16])); + // receiving the rest fails with overrun + assert_eq!(read::<16>(&mut uart).await, Err(Error::Overrun)); + // new data is accepted, latest overrunning byte first + assert_eq!(read(&mut uart).await, Ok([3])); + uart.blocking_write(&[8, 9]).unwrap(); + Timer::after(Duration::from_millis(1)).await; + assert_eq!(read(&mut uart).await, Ok([8, 9])); + } + + info!("test break detection"); + { + let config = Config::default(); + let (mut tx, mut rx) = Uart::new( + &mut uart, + &mut tx, + &mut rx, + &mut irq, + &mut p.DMA_CH0, + &mut p.DMA_CH1, + config, + ) + .split(); + + // break before read + tx.send_break(20).await; + tx.write(&[64]).await.unwrap(); + assert_eq!(read1::<1>(&mut rx).await.unwrap_err(), Error::Break); + assert_eq!(read1(&mut rx).await.unwrap(), [64]); + + // break during read + { + let r = read1::<2>(&mut rx); + tx.write(&[2]).await.unwrap(); + tx.send_break(20).await; + tx.write(&[3]).await.unwrap(); + assert_eq!(r.await.unwrap_err(), Error::Break); + assert_eq!(read1(&mut rx).await.unwrap(), [3]); + } + + // break after read + { + let r = read1(&mut rx); + tx.write(&[2]).await.unwrap(); + tx.send_break(20).await; + tx.write(&[3]).await.unwrap(); + assert_eq!(r.await.unwrap(), [2]); + assert_eq!(read1::<1>(&mut rx).await.unwrap_err(), Error::Break); + assert_eq!(read1(&mut rx).await.unwrap(), [3]); + } + } + + // parity detection. here we bitbang to not require two uarts. + info!("test parity error detection"); + { + let mut pin = Output::new(&mut tx, Level::High); + // choose a very slow baud rate to make tests reliable even with O0 + let mut config = Config::default(); + config.baudrate = 1000; + config.parity = Parity::ParityEven; + let mut uart = UartRx::new(&mut uart, &mut rx, &mut irq, &mut p.DMA_CH0, config); + + async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u32) { + send(pin, v, Some(parity != 0)).await; + } + + // first check that we can send correctly + chr(&mut pin, 32, 1).await; + assert_eq!(read1(&mut uart).await.unwrap(), [32]); + + // parity error before read + chr(&mut pin, 32, 0).await; + chr(&mut pin, 31, 1).await; + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity); + assert_eq!(read1(&mut uart).await.unwrap(), [31]); + + // parity error during read + { + let r = read1::<2>(&mut uart); + chr(&mut pin, 2, 1).await; + chr(&mut pin, 32, 0).await; + chr(&mut pin, 3, 0).await; + assert_eq!(r.await.unwrap_err(), Error::Parity); + assert_eq!(read1(&mut uart).await.unwrap(), [3]); + } + + // parity error after read + { + let r = read1(&mut uart); + chr(&mut pin, 2, 1).await; + chr(&mut pin, 32, 0).await; + chr(&mut pin, 3, 0).await; + assert_eq!(r.await.unwrap(), [2]); + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Parity); + assert_eq!(read1(&mut uart).await.unwrap(), [3]); + } + } + + // framing error detection. here we bitbang because there's no other way. + info!("test framing error detection"); + { + let mut pin = Output::new(&mut tx, Level::High); + // choose a very slow baud rate to make tests reliable even with O0 + let mut config = Config::default(); + config.baudrate = 1000; + let mut uart = UartRx::new(&mut uart, &mut rx, &mut irq, &mut p.DMA_CH0, config); + + async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) { + if good { + send(pin, v, None).await; + } else { + send(pin, v, Some(false)).await; + } + } + + // first check that we can send correctly + chr(&mut pin, 32, true).await; + assert_eq!(read1(&mut uart).await.unwrap(), [32]); + + // parity error before read + chr(&mut pin, 32, false).await; + chr(&mut pin, 31, true).await; + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing); + assert_eq!(read1(&mut uart).await.unwrap(), [31]); + + // parity error during read + { + let r = read1::<2>(&mut uart); + chr(&mut pin, 2, true).await; + chr(&mut pin, 32, false).await; + chr(&mut pin, 3, true).await; + assert_eq!(r.await.unwrap_err(), Error::Framing); + assert_eq!(read1(&mut uart).await.unwrap(), [3]); + } + + // parity error after read + { + let r = read1(&mut uart); + chr(&mut pin, 2, true).await; + chr(&mut pin, 32, false).await; + chr(&mut pin, 3, true).await; + assert_eq!(r.await.unwrap(), [2]); + assert_eq!(read1::<1>(&mut uart).await.unwrap_err(), Error::Framing); + assert_eq!(read1(&mut uart).await.unwrap(), [3]); + } + } info!("Test OK"); cortex_m::asm::bkpt(); From b612976cc7e9fcce7b547b348adaaabe73c487d0 Mon Sep 17 00:00:00 2001 From: kbleeke Date: Fri, 28 Apr 2023 20:53:09 +0200 Subject: [PATCH 0997/1575] add wifi scan example --- .../src/{main.rs => bin/tcp_server.rs} | 5 +- examples/rpi-pico-w/src/bin/wifi_scan.rs | 81 +++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) rename examples/rpi-pico-w/src/{main.rs => bin/tcp_server.rs} (95%) create mode 100644 examples/rpi-pico-w/src/bin/wifi_scan.rs diff --git a/examples/rpi-pico-w/src/main.rs b/examples/rpi-pico-w/src/bin/tcp_server.rs similarity index 95% rename from examples/rpi-pico-w/src/main.rs rename to examples/rpi-pico-w/src/bin/tcp_server.rs index 944beaac0..036f79308 100644 --- a/examples/rpi-pico-w/src/main.rs +++ b/examples/rpi-pico-w/src/bin/tcp_server.rs @@ -48,9 +48,8 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); - // Include the WiFi firmware and Country Locale Matrix (CLM) blobs. - let fw = include_bytes!("../../../firmware/43439A0.bin"); - let clm = include_bytes!("../../../firmware/43439A0_clm.bin"); + let fw = include_bytes!("../../../../firmware/43439A0.bin"); + let clm = include_bytes!("../../../../firmware/43439A0_clm.bin"); // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: diff --git a/examples/rpi-pico-w/src/bin/wifi_scan.rs b/examples/rpi-pico-w/src/bin/wifi_scan.rs new file mode 100644 index 000000000..da8fadfd8 --- /dev/null +++ b/examples/rpi-pico-w/src/bin/wifi_scan.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +use core::str; + +use cyw43_pio::PioSpi; +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::Stack; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25}; +use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachineInstance, Sm0}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +#[embassy_executor::task] +async fn wifi_task( + runner: cyw43::Runner< + 'static, + Output<'static, PIN_23>, + PioSpi, DMA_CH0>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_rp::init(Default::default()); + + let fw = include_bytes!("../../../../firmware/43439A0.bin"); + let clm = include_bytes!("../../../../firmware/43439A0_clm.bin"); + + // To make flashing faster for development, you may want to flash the firmwares independently + // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: + // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + + let pwr = Output::new(p.PIN_23, Level::Low); + let cs = Output::new(p.PIN_25, Level::High); + + let (_, sm, _, _, _) = p.PIO0.split(); + let dma = p.DMA_CH0; + let spi = PioSpi::new(sm, cs, p.PIN_24, p.PIN_29, dma); + + let state = singleton!(cyw43::State::new()); + let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; + unwrap!(spawner.spawn(wifi_task(runner))); + + control.init(clm).await; + control + .set_power_management(cyw43::PowerManagementMode::PowerSave) + .await; + + let mut scanner = control.scan().await; + while let Some(bss) = scanner.next().await { + if let Ok(ssid_str) = str::from_utf8(&bss.ssid) { + info!("scanned {} == {:x}", ssid_str, bss.bssid); + } + } +} From 534cf7c618b0e93881b9757b5608a7ad67606fce Mon Sep 17 00:00:00 2001 From: Satoshi Tanaka Date: Tue, 2 May 2023 01:30:08 +0900 Subject: [PATCH 0998/1575] Add AP mode example --- examples/rpi-pico-w/src/bin/tcp_server_ap.rs | 144 +++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 examples/rpi-pico-w/src/bin/tcp_server_ap.rs diff --git a/examples/rpi-pico-w/src/bin/tcp_server_ap.rs b/examples/rpi-pico-w/src/bin/tcp_server_ap.rs new file mode 100644 index 000000000..e43412625 --- /dev/null +++ b/examples/rpi-pico-w/src/bin/tcp_server_ap.rs @@ -0,0 +1,144 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +use core::str::from_utf8; + +use cyw43_pio::PioSpi; +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Config, Stack, StackResources}; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25}; +use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachineInstance, Sm0}; +use embedded_io::asynch::Write; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +#[embassy_executor::task] +async fn wifi_task( + runner: cyw43::Runner< + 'static, + Output<'static, PIN_23>, + PioSpi, DMA_CH0>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_rp::init(Default::default()); + + let fw = include_bytes!("../../../../firmware/43439A0.bin"); + let clm = include_bytes!("../../../../firmware/43439A0_clm.bin"); + + // To make flashing faster for development, you may want to flash the firmwares independently + // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: + // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + + let pwr = Output::new(p.PIN_23, Level::Low); + let cs = Output::new(p.PIN_25, Level::High); + + let (_, sm, _, _, _) = p.PIO0.split(); + let dma = p.DMA_CH0; + let spi = PioSpi::new(sm, cs, p.PIN_24, p.PIN_29, dma); + + let state = singleton!(cyw43::State::new()); + let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; + unwrap!(spawner.spawn(wifi_task(runner))); + + control.init(clm).await; + control + .set_power_management(cyw43::PowerManagementMode::PowerSave) + .await; + + // Use a link-local address for communication without DHCP server + let config = Config::Static(embassy_net::StaticConfig { + address: embassy_net::Ipv4Cidr::new(embassy_net::Ipv4Address::new(169, 254, 1, 1), 16), + dns_servers: heapless::Vec::new(), + gateway: None, + }); + + // Generate random seed + let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. + + // Init network stack + let stack = &*singleton!(Stack::new( + net_device, + config, + singleton!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + //control.start_ap_open("cyw43", 5).await; + control.start_ap_wpa2("cyw43", "password", 5).await; + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + control.gpio_set(0, false).await; + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + control.gpio_set(0, true).await; + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {}", from_utf8(&buf[..n]).unwrap()); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} From 6ee45f5ec01208bdcb38f23bf46dcdac141ff6e7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 18:47:09 +0200 Subject: [PATCH 0999/1575] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d0920aae2..fe8d5d93b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implemen Working: - Station mode (joining an AP). +- AP mode (creating an AP) +- Scanning - Sending and receiving Ethernet frames. - Using the default MAC address. - [`embassy-net`](https://embassy.dev) integration. @@ -16,10 +18,7 @@ Working: TODO: -- AP mode (creating an AP) -- Scanning - Setting a custom MAC address. -- Investigate why can [this](https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/pico_cyw43_driver) use higher PIO speed. - Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) ## Running the example From 49455792cb5406f3e2df0f3850c0d650965e6b2b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 26 Apr 2023 10:51:23 +0200 Subject: [PATCH 1000/1575] Ring-buffered uart rx with one-period overrun detection --- embassy-stm32/src/dma/dma.rs | 186 +++++++- embassy-stm32/src/dma/mod.rs | 1 + embassy-stm32/src/dma/ringbuffer.rs | 433 +++++++++++++++++++ embassy-stm32/src/usart/mod.rs | 43 +- embassy-stm32/src/usart/rx_ringbuffered.rs | 286 ++++++++++++ tests/stm32/Cargo.toml | 2 + tests/stm32/src/bin/usart_rx_ringbuffered.rs | 188 ++++++++ tests/utils/Cargo.toml | 10 + tests/utils/src/bin/saturate_serial.rs | 52 +++ 9 files changed, 1177 insertions(+), 24 deletions(-) create mode 100644 embassy-stm32/src/dma/ringbuffer.rs create mode 100644 embassy-stm32/src/usart/rx_ringbuffered.rs create mode 100644 tests/stm32/src/bin/usart_rx_ringbuffered.rs create mode 100644 tests/utils/Cargo.toml create mode 100644 tests/utils/src/bin/saturate_serial.rs diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index ef1d27573..17d82fe2d 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -9,6 +9,7 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::regs; +use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; @@ -445,7 +446,6 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { // ================================== -#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct DoubleBuffered<'a, C: Channel, W: Word> { channel: PeripheralRef<'a, C>, _phantom: PhantomData, @@ -578,15 +578,6 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { let ch = self.channel.regs().st(self.channel.num()); unsafe { ch.ndtr().read() }.ndt() } - - pub fn blocking_wait(mut self) { - while self.is_running() {} - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::SeqCst); - - core::mem::forget(self); - } } impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { @@ -598,3 +589,178 @@ impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { fence(Ordering::SeqCst); } } + +// ============================== + +impl DmaCtrl for C { + fn tcif(&self) -> bool { + let channel_number = self.num(); + let dma = self.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + unsafe { dma.isr(isrn).read() }.tcif(isrbit) + } + + fn clear_tcif(&mut self) { + let channel_number = self.num(); + let dma = self.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + unsafe { + dma.ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + }) + } + } + + fn ndtr(&self) -> usize { + let ch = self.regs().st(self.num()); + unsafe { ch.ndtr().read() }.ndt() as usize + } +} + +pub struct RingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: DmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::PeripheralToMemory; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + let mut w = regs::Cr(0); + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); + w.set_pl(vals::Pl::VERYHIGH); + w.set_minc(vals::Inc::INCREMENTED); + w.set_pinc(vals::Inc::FIXED); + w.set_teie(true); + w.set_tcie(false); + w.set_circ(vals::Circ::ENABLED); + #[cfg(dma_v1)] + w.set_trbuff(true); + #[cfg(dma_v2)] + w.set_chsel(_request); + w.set_pburst(options.pburst.into()); + w.set_mburst(options.mburst.into()); + w.set_pfctrl(options.flow_ctrl.into()); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: DmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.st(channel_number); + ch.par().write_value(peri_addr as u32); + ch.m0ar().write_value(buffer_ptr as u32); + ch.ndtr().write_value(regs::Ndtr(len as _)); + ch.fcr().write(|w| { + if let Some(fth) = options.fifo_threshold { + // FIFO mode + w.set_dmdis(vals::Dmdis::DISABLED); + w.set_fth(fth.into()); + } else { + // Direct mode + w.set_dmdis(vals::Dmdis::ENABLED); + } + }); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().write_value(self.cr) } + } + + pub fn clear(&mut self) { + self.ringbuf.clear(); + } + + /// Read bytes from the ring buffer + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + pub fn read(&mut self, buf: &mut [W]) -> Result { + self.ringbuf.read(&mut *self.channel, buf) + } + + fn clear_irqs(&mut self) { + let channel_number = self.channel.num(); + let dma = self.channel.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + unsafe { + dma.ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }) + } + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }) + } + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().read() }.en() + } + + /// Gets the total remaining transfers for the channel + /// Note: this will be zero for transfers that completed without cancellation. + pub fn get_remaining_transfers(&self) -> usize { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.ndtr().read() }.ndt() as usize + } + + pub fn set_ndtr(&mut self, ndtr: usize) { + self.ringbuf.ndtr = ndtr; + } +} + +impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 3312ca752..3ac0d1b3d 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -21,6 +21,7 @@ pub use gpdma::*; #[cfg(dmamux)] mod dmamux; +pub(crate) mod ringbuffer; pub mod word; use core::mem; diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs new file mode 100644 index 000000000..f9ace6018 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -0,0 +1,433 @@ +use core::ops::Range; +use core::sync::atomic::{compiler_fence, Ordering}; + +use super::word::Word; + +/// A "read-only" ring-buffer to be used together with the DMA controller which +/// writes in a circular way, "uncontrolled" to the buffer. +/// +/// A snapshot of the ring buffer state can be attained by setting the `ndtr` field +/// to the current register value. `ndtr` describes the current position of the DMA +/// write. +/// +/// # Safety +/// +/// The ring buffer controls the TCIF (transfer completed interrupt flag) to +/// detect buffer overruns, hence this interrupt must be disabled. +/// The buffer can detect overruns up to one period, that is, for a X byte buffer, +/// overruns can be detected if they happen from byte X+1 up to 2X. After this +/// point, overrunds may or may not be detected. +/// +/// # Buffer layout +/// +/// ```text +/// Without wraparound: With wraparound: +/// +/// + buf +--- NDTR ---+ + buf +---------- NDTR ----------+ +/// | | | | | | +/// v v v v v v +/// +-----------------------------------------+ +-----------------------------------------+ +/// |oooooooooooXXXXXXXXXXXXXXXXoooooooooooooo| |XXXXXXXXXXXXXooooooooooooXXXXXXXXXXXXXXXX| +/// +-----------------------------------------+ +-----------------------------------------+ +/// ^ ^ ^ ^ ^ ^ +/// | | | | | | +/// +- first --+ | +- end ------+ | +/// | | | | +/// +- end --------------------+ +- first ----------------+ +/// ``` +pub struct DmaRingBuffer<'a, W: Word> { + pub(crate) dma_buf: &'a mut [W], + first: usize, + pub ndtr: usize, + expect_next_read_to_wrap: bool, +} + +#[derive(Debug, PartialEq)] +pub struct OverrunError; + +pub trait DmaCtrl { + /// Get the NDTR register value, i.e. the space left in the underlying + /// buffer until the dma writer wraps. + fn ndtr(&self) -> usize; + + /// Read the transfer completed interrupt flag + /// This flag is set by the dma controller when NDTR is reloaded, + /// i.e. when the writing wraps. + fn tcif(&self) -> bool; + + /// Clear the transfer completed interrupt flag + fn clear_tcif(&mut self); +} + +impl<'a, W: Word> DmaRingBuffer<'a, W> { + pub fn new(dma_buf: &'a mut [W]) -> Self { + let ndtr = dma_buf.len(); + Self { + dma_buf, + first: 0, + ndtr, + expect_next_read_to_wrap: false, + } + } + + /// Reset the ring buffer to its initial state + pub fn clear(&mut self) { + self.first = 0; + self.ndtr = self.dma_buf.len(); + self.expect_next_read_to_wrap = false; + } + + /// The buffer end position + fn end(&self) -> usize { + self.dma_buf.len() - self.ndtr + } + + /// Returns whether the buffer is empty + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.first == self.end() + } + + /// The current number of bytes in the buffer + /// This may change at any time if dma is currently active + #[allow(dead_code)] + pub fn len(&self) -> usize { + // Read out a stable end (the dma periheral can change it at anytime) + let end = self.end(); + if self.first <= end { + // No wrap + end - self.first + } else { + self.dma_buf.len() - self.first + end + } + } + + /// Read bytes from the ring buffer + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result { + let end = self.end(); + + compiler_fence(Ordering::SeqCst); + + if self.first == end { + // The buffer is currently empty + + if dma.tcif() { + // The dma controller has written such that the ring buffer now wraps + // This is the special case where exactly n*dma_buf.len(), n = 1,2,..., bytes was written, + // but where additional bytes are now written causing the ring buffer to wrap. + // This is only an error if the writing has passed the current unread region. + self.ndtr = dma.ndtr(); + if self.end() > self.first { + dma.clear_tcif(); + return Err(OverrunError); + } + } + + self.expect_next_read_to_wrap = false; + Ok(0) + } else if self.first < end { + // The available, unread portion in the ring buffer DOES NOT wrap + + if self.expect_next_read_to_wrap { + // The read was expected to wrap but it did not + + dma.clear_tcif(); + return Err(OverrunError); + } + + // Copy out the bytes from the dma buffer + let len = self.copy_to(buf, self.first..end); + + compiler_fence(Ordering::SeqCst); + + if dma.tcif() { + // The dma controller has written such that the ring buffer now wraps + + self.ndtr = dma.ndtr(); + if self.end() > self.first { + // The bytes that we have copied out have overflowed + // as the writer has now both wrapped and is currently writing + // within the region that we have just copied out + + // Clear transfer completed interrupt flag + dma.clear_tcif(); + return Err(OverrunError); + } + } + + self.first = (self.first + len) % self.dma_buf.len(); + self.expect_next_read_to_wrap = false; + Ok(len) + } else { + // The available, unread portion in the ring buffer DOES wrap + // The dma controller has wrapped since we last read and is currently + // writing (or the next byte added will be) in the beginning of the ring buffer. + + // If the unread portion wraps then the writer must also have wrapped, + // or it has wrapped and we already cleared the TCIF flag + assert!(dma.tcif() || self.expect_next_read_to_wrap); + + // Clear transfer completed interrupt flag + dma.clear_tcif(); + + if self.first + buf.len() < self.dma_buf.len() { + // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. + + // Copy out from the dma buffer + let len = self.copy_to(buf, self.first..self.dma_buf.len()); + + compiler_fence(Ordering::SeqCst); + + // We have now copied out the data from dma_buf + // Make sure that the just read part was not overwritten during the copy + self.ndtr = dma.ndtr(); + if self.end() > self.first || dma.tcif() { + // The writer has entered the data that we have just read since we read out `end` in the beginning and until now. + return Err(OverrunError); + } + + self.first = (self.first + len) % self.dma_buf.len(); + self.expect_next_read_to_wrap = true; + Ok(len) + } else { + // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, + // so the next read will not have any unread tail bytes in the ring buffer. + + // Copy out from the dma buffer + let tail = self.copy_to(buf, self.first..self.dma_buf.len()); + let head = self.copy_to(&mut buf[tail..], 0..end); + + compiler_fence(Ordering::SeqCst); + + // We have now copied out the data from dma_buf + // Make sure that the just read part was not overwritten during the copy + self.ndtr = dma.ndtr(); + if self.end() > self.first || dma.tcif() { + return Err(OverrunError); + } + + self.first = head; + self.expect_next_read_to_wrap = false; + Ok(tail + head) + } + } + } + + /// Copy from the dma buffer at `data_range` into `buf` + fn copy_to(&mut self, buf: &mut [W], data_range: Range) -> usize { + // Limit the number of bytes that can be copied + let length = usize::min(data_range.len(), buf.len()); + + // Copy from dma buffer into read buffer + // We need to do it like this instead of a simple copy_from_slice() because + // reading from a part of memory that may be simultaneously written to is unsafe + unsafe { + let dma_buf = self.dma_buf.as_ptr(); + + for i in 0..length { + buf[i] = core::ptr::read_volatile(dma_buf.offset((data_range.start + i) as isize)); + } + } + + length + } +} + +#[cfg(test)] +mod tests { + use core::array; + use core::cell::RefCell; + + use super::*; + + struct TestCtrl { + next_ndtr: RefCell>, + tcif: bool, + } + + impl TestCtrl { + pub const fn new() -> Self { + Self { + next_ndtr: RefCell::new(None), + tcif: false, + } + } + + pub fn set_next_ndtr(&mut self, ndtr: usize) { + self.next_ndtr.borrow_mut().replace(ndtr); + } + } + + impl DmaCtrl for TestCtrl { + fn ndtr(&self) -> usize { + self.next_ndtr.borrow_mut().unwrap() + } + + fn tcif(&self) -> bool { + self.tcif + } + + fn clear_tcif(&mut self) { + self.tcif = false; + } + } + + #[test] + fn empty() { + let mut dma_buf = [0u8; 16]; + let ringbuf = DmaRingBuffer::new(&mut dma_buf); + + assert!(ringbuf.is_empty()); + assert_eq!(0, ringbuf.len()); + } + + #[test] + fn can_read() { + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 + let mut ctrl = TestCtrl::new(); + let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + ringbuf.ndtr = 6; + + assert!(!ringbuf.is_empty()); + assert_eq!(10, ringbuf.len()); + + let mut buf = [0; 2]; + assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!([0, 1], buf); + assert_eq!(8, ringbuf.len()); + + let mut buf = [0; 2]; + assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!([2, 3], buf); + assert_eq!(6, ringbuf.len()); + + let mut buf = [0; 8]; + assert_eq!(6, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!([4, 5, 6, 7, 8, 9], buf[..6]); + assert_eq!(0, ringbuf.len()); + + let mut buf = [0; 2]; + assert_eq!(0, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + } + + #[test] + fn can_read_with_wrap() { + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 + let mut ctrl = TestCtrl::new(); + let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + ringbuf.first = 12; + ringbuf.ndtr = 10; + + // The dma controller has written 4 + 6 bytes and has reloaded NDTR + ctrl.tcif = true; + ctrl.set_next_ndtr(10); + + assert!(!ringbuf.is_empty()); + assert_eq!(6 + 4, ringbuf.len()); + + let mut buf = [0; 2]; + assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!([12, 13], buf); + assert_eq!(6 + 2, ringbuf.len()); + + let mut buf = [0; 4]; + assert_eq!(4, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!([14, 15, 0, 1], buf); + assert_eq!(4, ringbuf.len()); + } + + #[test] + fn can_read_when_dma_writer_is_wrapped_and_read_does_not_wrap() { + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 + let mut ctrl = TestCtrl::new(); + let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + ringbuf.first = 2; + ringbuf.ndtr = 6; + + // The dma controller has written 6 + 2 bytes and has reloaded NDTR + ctrl.tcif = true; + ctrl.set_next_ndtr(14); + + let mut buf = [0; 2]; + assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!([2, 3], buf); + + assert_eq!(true, ctrl.tcif); // The interrupt flag IS NOT cleared + } + + #[test] + fn can_read_when_dma_writer_is_wrapped_and_read_wraps() { + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 + let mut ctrl = TestCtrl::new(); + let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + ringbuf.first = 12; + ringbuf.ndtr = 10; + + // The dma controller has written 6 + 2 bytes and has reloaded NDTR + ctrl.tcif = true; + ctrl.set_next_ndtr(14); + + let mut buf = [0; 10]; + assert_eq!(10, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!([12, 13, 14, 15, 0, 1, 2, 3, 4, 5], buf); + + assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + } + + #[test] + fn cannot_read_when_dma_writer_wraps_with_same_ndtr() { + let mut dma_buf = [0u8; 16]; + let mut ctrl = TestCtrl::new(); + let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + ringbuf.first = 6; + ringbuf.ndtr = 10; + ctrl.set_next_ndtr(9); + + assert!(ringbuf.is_empty()); // The ring buffer thinks that it is empty + + // The dma controller has written exactly 16 bytes + ctrl.tcif = true; + + let mut buf = [0; 2]; + assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); + + assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + } + + #[test] + fn cannot_read_when_dma_writer_overwrites_during_not_wrapping_read() { + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 + let mut ctrl = TestCtrl::new(); + let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + ringbuf.first = 2; + ringbuf.ndtr = 6; + + // The dma controller has written 6 + 3 bytes and has reloaded NDTR + ctrl.tcif = true; + ctrl.set_next_ndtr(13); + + let mut buf = [0; 2]; + assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); + + assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + } + + #[test] + fn cannot_read_when_dma_writer_overwrites_during_wrapping_read() { + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 + let mut ctrl = TestCtrl::new(); + let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + ringbuf.first = 12; + ringbuf.ndtr = 10; + + // The dma controller has written 6 + 13 bytes and has reloaded NDTR + ctrl.tcif = true; + ctrl.set_next_ndtr(3); + + let mut buf = [0; 2]; + assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); + + assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + } +} diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 266561659..fea0c5f11 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -283,8 +283,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; + let mut wake = false; let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); - if has_errors { // clear all interrupts and DMA Rx Request unsafe { @@ -304,22 +304,35 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { }); } - compiler_fence(Ordering::SeqCst); + wake = true; + } else { + if cr1.idleie() && sr.idle() { + // IDLE detected: no more data will come + unsafe { + r.cr1().modify(|w| { + // disable idle line detection + w.set_idleie(false); + }); - s.rx_waker.wake(); - } else if cr1.idleie() && sr.idle() { - // IDLE detected: no more data will come - unsafe { - r.cr1().modify(|w| { - // disable idle line detection - w.set_idleie(false); - }); + r.cr3().modify(|w| { + // disable DMA Rx Request + w.set_dmar(false); + }); + } - r.cr3().modify(|w| { - // disable DMA Rx Request - w.set_dmar(false); - }); + wake = true; } + + if cr1.rxneie() { + // We cannot check the RXNE flag as it is auto-cleared by the DMA controller + + // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection + + wake = true; + } + } + + if wake { compiler_fence(Ordering::SeqCst); s.rx_waker.wake(); @@ -972,6 +985,8 @@ mod eio { pub use buffered::*; #[cfg(feature = "nightly")] mod buffered; +mod rx_ringbuffered; +pub use rx_ringbuffered::RingBufferedUartRx; #[cfg(usart_v1)] fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { diff --git a/embassy-stm32/src/usart/rx_ringbuffered.rs b/embassy-stm32/src/usart/rx_ringbuffered.rs new file mode 100644 index 000000000..0dc90ece7 --- /dev/null +++ b/embassy-stm32/src/usart/rx_ringbuffered.rs @@ -0,0 +1,286 @@ +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::PeripheralRef; + +use super::{rdr, sr, BasicInstance, Error, UartRx}; +use crate::dma::ringbuffer::OverrunError; +use crate::dma::RingBuffer; + +pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma> { + _peri: PeripheralRef<'d, T>, + ring_buf: RingBuffer<'d, RxDma, u8>, +} + +impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { + /// Turn the `UartRx` into a buffered uart which can continously receive in the background + /// without the possibility of loosing bytes. The `dma_buf` is a buffer registered to the + /// DMA controller, and must be sufficiently large, such that it will not overflow. + pub fn into_ring_buffered(self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T, RxDma> { + assert!(dma_buf.len() > 0 && dma_buf.len() <= 0xFFFF); + + let request = self.rx_dma.request(); + let opts = Default::default(); + let ring_buf = unsafe { RingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; + RingBufferedUartRx { + _peri: self._peri, + ring_buf, + } + } +} + +impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxDma> { + pub fn start(&mut self) -> Result<(), Error> { + // Clear the ring buffer so that it is ready to receive data + self.ring_buf.clear(); + + self.setup_uart(); + + Ok(()) + } + + /// Start uart background receive + fn setup_uart(&mut self) { + // fence before starting DMA. + compiler_fence(Ordering::SeqCst); + + self.ring_buf.start(); + + let r = T::regs(); + // clear all interrupts and DMA Rx Request + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // enable parity interrupt if not ParityNone + w.set_peie(w.pce()); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + // enable DMA Rx Request + w.set_dmar(true); + }); + } + } + + /// Stop uart background receive + fn teardown_uart(&mut self) { + let r = T::regs(); + // clear all interrupts and DMA Rx Request + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + compiler_fence(Ordering::SeqCst); + + self.ring_buf.request_stop(); + while self.ring_buf.is_running() {} + } + + /// Read bytes that are readily available in the ring buffer. + /// If no bytes are currently available in the buffer the call waits until data are received. + /// + /// Background receive is started if `start()` has not been previously called. + /// + /// Receive in the background is terminated if an error is returned. + /// It must then manually be started again by calling `start()` or by re-calling `read()`. + pub async fn read(&mut self, buf: &mut [u8]) -> Result { + let r = T::regs(); + + // SAFETY: read only + let is_started = unsafe { r.cr3().read().dmar() }; + + // Start background receive if it was not already started + if !is_started { + self.start()?; + } + + // SAFETY: read only and we only use Rx related flags + let s = unsafe { sr(r).read() }; + let has_errors = s.pe() || s.fe() || s.ne() || s.ore(); + if has_errors { + self.teardown_uart(); + + if s.pe() { + return Err(Error::Parity); + } else if s.fe() { + return Err(Error::Framing); + } else if s.ne() { + return Err(Error::Noise); + } else { + return Err(Error::Overrun); + } + } + + let ndtr = self.ring_buf.get_remaining_transfers(); + self.ring_buf.set_ndtr(ndtr); + match self.ring_buf.read(buf) { + Ok(len) if len == 0 => {} + Ok(len) => { + assert!(len > 0); + return Ok(len); + } + Err(OverrunError) => { + // Stop any transfer from now on + // The user must re-start to receive any more data + self.teardown_uart(); + return Err(Error::Overrun); + } + } + + // Wait for any data since `ndtr` + self.wait_for_data(ndtr).await?; + + // ndtr is now different than the value provided to `wait_for_data()` + // Re-sample ndtr now when it has changed. + self.ring_buf.set_ndtr(self.ring_buf.get_remaining_transfers()); + let len = self.ring_buf.read(buf).map_err(|_err| Error::Overrun)?; + assert!(len > 0); + Ok(len) + } + + /// Wait for uart data + async fn wait_for_data(&mut self, old_ndtr: usize) -> Result<(), Error> { + let r = T::regs(); + + // make sure USART state is restored to neutral state when this future is dropped + let _drop = OnDrop::new(move || { + // SAFETY: only clears Rx related flags + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + }); + } + }); + + // SAFETY: only sets Rx related flags + unsafe { + r.cr1().modify(|w| { + // enable RXNE interrupt + w.set_rxneie(true); + }); + } + + // future which completes when RX "not empty" is detected, + // i.e. when there is data in uart rx register + let rxne = poll_fn(|cx| { + let s = T::state(); + + // Register waker to be awaken when RXNE interrupt is received + s.rx_waker.register(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + // SAFETY: read only and we only use Rx related flags + let s = unsafe { sr(r).read() }; + let has_errors = s.pe() || s.fe() || s.ne() || s.ore(); + if has_errors { + if s.pe() { + return Poll::Ready(Err(Error::Parity)); + } else if s.fe() { + return Poll::Ready(Err(Error::Framing)); + } else if s.ne() { + return Poll::Ready(Err(Error::Noise)); + } else { + return Poll::Ready(Err(Error::Overrun)); + } + } + + // Re-sample ndtr and determine if it has changed since we started + // waiting for data. + let new_ndtr = self.ring_buf.get_remaining_transfers(); + if new_ndtr != old_ndtr { + // Some data was received as NDTR has changed + Poll::Ready(Ok(())) + } else { + // It may be that the DMA controller is currently busy consuming the + // RX data register. We therefore wait register to become empty. + while unsafe { sr(r).read().rxne() } {} + + compiler_fence(Ordering::SeqCst); + + // Re-get again: This time we know that the DMA controller has consumed + // the current read register if it was busy doing so + let new_ndtr = self.ring_buf.get_remaining_transfers(); + if new_ndtr != old_ndtr { + // Some data was received as NDTR has changed + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } + }); + + compiler_fence(Ordering::SeqCst); + + let new_ndtr = self.ring_buf.get_remaining_transfers(); + if new_ndtr != old_ndtr { + // Fast path - NDTR has already changed, no reason to poll + Ok(()) + } else { + // NDTR has not changed since we first read from the ring buffer + // Wait for RXNE interrupt... + match rxne.await { + Ok(()) => Ok(()), + Err(e) => { + self.teardown_uart(); + Err(e) + } + } + } + } +} + +impl> Drop for RingBufferedUartRx<'_, T, RxDma> { + fn drop(&mut self) { + self.teardown_uart(); + } +} + +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eio { + use embedded_io::asynch::Read; + use embedded_io::Io; + + use super::RingBufferedUartRx; + use crate::usart::{BasicInstance, Error, RxDma}; + + impl Io for RingBufferedUartRx<'_, T, Rx> + where + T: BasicInstance, + Rx: RxDma, + { + type Error = Error; + } + + impl Read for RingBufferedUartRx<'_, T, Rx> + where + T: BasicInstance, + Rx: RxDma, + { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf).await + } + } +} diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index d10d01e29..240fad522 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -33,6 +33,8 @@ embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } +rand_core = { version = "0.6", default-features = false } +rand_chacha = { version = "0.3", default-features = false } chrono = { version = "^0.4", default-features = false, optional = true} diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs new file mode 100644 index 000000000..3ea8bfb7b --- /dev/null +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -0,0 +1,188 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use embassy_executor::Spawner; +use embassy_stm32::interrupt; +use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; +use embassy_time::{Duration, Timer}; +use example_common::*; +use rand_chacha::ChaCha8Rng; +use rand_core::{RngCore, SeedableRng}; + +#[cfg(feature = "stm32f103c8")] +mod board { + pub type Uart = embassy_stm32::peripherals::USART1; + pub type TxDma = embassy_stm32::peripherals::DMA1_CH4; + pub type RxDma = embassy_stm32::peripherals::DMA1_CH5; +} +#[cfg(feature = "stm32g491re")] +mod board { + pub type Uart = embassy_stm32::peripherals::USART1; + pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; + pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; +} +#[cfg(feature = "stm32g071rb")] +mod board { + pub type Uart = embassy_stm32::peripherals::USART1; + pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; + pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; +} +#[cfg(feature = "stm32f429zi")] +mod board { + pub type Uart = embassy_stm32::peripherals::USART2; + pub type TxDma = embassy_stm32::peripherals::DMA1_CH6; + pub type RxDma = embassy_stm32::peripherals::DMA1_CH5; +} +#[cfg(feature = "stm32wb55rg")] +mod board { + pub type Uart = embassy_stm32::peripherals::LPUART1; + pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; + pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; +} +#[cfg(feature = "stm32h755zi")] +mod board { + pub type Uart = embassy_stm32::peripherals::USART1; + pub type TxDma = embassy_stm32::peripherals::DMA1_CH0; + pub type RxDma = embassy_stm32::peripherals::DMA1_CH1; +} +#[cfg(feature = "stm32u585ai")] +mod board { + pub type Uart = embassy_stm32::peripherals::USART3; + pub type TxDma = embassy_stm32::peripherals::GPDMA1_CH0; + pub type RxDma = embassy_stm32::peripherals::GPDMA1_CH1; +} + +const ONE_BYTE_DURATION_US: u32 = 9_000_000 / 115200; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(config()); + info!("Hello World!"); + + // Arduino pins D0 and D1 + // They're connected together with a 1K resistor. + #[cfg(feature = "stm32f103c8")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA9, + p.PA10, + p.USART1, + interrupt::take!(USART1), + p.DMA1_CH4, + p.DMA1_CH5, + ); + #[cfg(feature = "stm32g491re")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + #[cfg(feature = "stm32g071rb")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + #[cfg(feature = "stm32f429zi")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PA2, p.PA3, p.USART2, interrupt::take!(USART2), p.DMA1_CH6, p.DMA1_CH5); + #[cfg(feature = "stm32wb55rg")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PA2, + p.PA3, + p.LPUART1, + interrupt::take!(LPUART1), + p.DMA1_CH1, + p.DMA1_CH2, + ); + #[cfg(feature = "stm32h755zi")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH0, p.DMA1_CH1); + #[cfg(feature = "stm32u585ai")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PD8, + p.PD9, + p.USART3, + interrupt::take!(USART3), + p.GPDMA1_CH0, + p.GPDMA1_CH1, + ); + + // To run this test, use the saturating_serial test utility to saturate the serial port + + let mut config = Config::default(); + config.baudrate = 115200; + config.data_bits = DataBits::DataBits8; + config.stop_bits = StopBits::STOP1; + config.parity = Parity::ParityNone; + + let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); + let (tx, rx) = usart.split(); + static mut DMA_BUF: [u8; 64] = [0; 64]; + let dma_buf = unsafe { DMA_BUF.as_mut() }; + let rx = rx.into_ring_buffered(dma_buf); + + info!("Spawning tasks"); + spawner.spawn(transmit_task(tx)).unwrap(); + spawner.spawn(receive_task(rx)).unwrap(); +} + +#[embassy_executor::task] +async fn transmit_task(mut tx: UartTx<'static, board::Uart, board::TxDma>) { + let mut rng = ChaCha8Rng::seed_from_u64(1337); + + info!("Starting random transmissions into void..."); + + let mut i: u8 = 0; + loop { + let mut buf = [0; 32]; + let len = 1 + (rng.next_u32() as usize % (buf.len() - 1)); + for b in &mut buf[..len] { + *b = i; + i = i.wrapping_add(1); + } + + tx.write(&buf[..len]).await.unwrap(); + Timer::after(Duration::from_micros((rng.next_u32() % 10000) as _)).await; + + //i += 1; + //if i % 1000 == 0 { + // trace!("Wrote {} times", i); + //} + } +} + +#[embassy_executor::task] +async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::RxDma>) { + info!("Ready to receive..."); + + let mut rng = ChaCha8Rng::seed_from_u64(1337); + + let mut i = 0; + let mut expected: Option = None; + loop { + let mut buf = [0; 100]; + let max_len = 1 + (rng.next_u32() as usize % (buf.len() - 1)); + let received = rx.read(&mut buf[..max_len]).await.unwrap(); + + if expected.is_none() { + info!("Test started"); + expected = Some(buf[0]); + } + + for byte in &buf[..received] { + if byte != &expected.unwrap() { + error!("Test fail! received {}, expected {}", *byte, expected.unwrap()); + cortex_m::asm::bkpt(); + return; + } + expected = Some(expected.unwrap().wrapping_add(1)); + } + + if received < max_len { + let byte_count = rng.next_u32() % 64; + Timer::after(Duration::from_micros((byte_count * ONE_BYTE_DURATION_US) as _)).await; + } + + i += 1; + if i % 1000 == 0 { + trace!("Read {} times", i); + } + } +} diff --git a/tests/utils/Cargo.toml b/tests/utils/Cargo.toml new file mode 100644 index 000000000..7d66fd586 --- /dev/null +++ b/tests/utils/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "test-utils" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8" +serial = "0.4" diff --git a/tests/utils/src/bin/saturate_serial.rs b/tests/utils/src/bin/saturate_serial.rs new file mode 100644 index 000000000..28480516d --- /dev/null +++ b/tests/utils/src/bin/saturate_serial.rs @@ -0,0 +1,52 @@ +use std::path::Path; +use std::time::Duration; +use std::{env, io, thread}; + +use rand::random; +use serial::SerialPort; + +pub fn main() { + if let Some(port_name) = env::args().nth(1) { + let sleep = env::args().position(|x| x == "--sleep").is_some(); + + println!("Saturating port {:?} with 115200 8N1", port_name); + println!("Sleep: {}", sleep); + let mut port = serial::open(&port_name).unwrap(); + if saturate(&mut port, sleep).is_err() { + eprintln!("Unable to saturate port"); + } + } else { + let path = env::args().next().unwrap(); + let basepath = Path::new(&path).with_extension(""); + let basename = basepath.file_name().unwrap(); + eprintln!("USAGE: {} ", basename.to_string_lossy()); + } +} + +fn saturate(port: &mut T, sleep: bool) -> io::Result<()> { + port.reconfigure(&|settings| { + settings.set_baud_rate(serial::Baud115200)?; + settings.set_char_size(serial::Bits8); + settings.set_parity(serial::ParityNone); + settings.set_stop_bits(serial::Stop1); + Ok(()) + })?; + + let mut written = 0; + loop { + let len = random::() % 0x1000; + let buf: Vec = (written..written + len).map(|x| x as u8).collect(); + + port.write_all(&buf)?; + + if sleep { + let micros = (random::() % 1000) as u64; + println!("Sleeping {}us", micros); + port.flush().unwrap(); + thread::sleep(Duration::from_micros(micros)); + } + + written += len; + println!("Written: {}", written); + } +} \ No newline at end of file From 4ea6662e55f32ff90b564c1c611f5ac4e0d8ab1c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 27 Apr 2023 10:43:52 +0200 Subject: [PATCH 1001/1575] Do not disable dma request when idle line is detected --- embassy-stm32/src/usart/mod.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index fea0c5f11..f3dea3391 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -313,11 +313,6 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // disable idle line detection w.set_idleie(false); }); - - r.cr3().modify(|w| { - // disable DMA Rx Request - w.set_dmar(false); - }); } wake = true; From fc268df6f56661d5f43450c7a03850044ae8e136 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 27 Apr 2023 10:48:38 +0200 Subject: [PATCH 1002/1575] Support overflow detection for more than one ring-period --- embassy-stm32/src/dma/dma.rs | 115 ++++++++++++----- embassy-stm32/src/dma/ringbuffer.rs | 127 ++++++++----------- embassy-stm32/src/usart/rx_ringbuffered.rs | 121 +++++++++--------- tests/stm32/src/bin/usart_rx_ringbuffered.rs | 19 ++- tests/utils/src/bin/saturate_serial.rs | 15 ++- 5 files changed, 216 insertions(+), 181 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 17d82fe2d..10ae20f5c 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -4,6 +4,7 @@ use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; +use atomic_polyfill::AtomicUsize; use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -129,13 +130,16 @@ impl From for vals::Fth { struct State { ch_wakers: [AtomicWaker; DMA_CHANNEL_COUNT], + complete_count: [AtomicUsize; DMA_CHANNEL_COUNT], } impl State { const fn new() -> Self { + const ZERO: AtomicUsize = AtomicUsize::new(0); const AW: AtomicWaker = AtomicWaker::new(); Self { ch_wakers: [AW; DMA_CHANNEL_COUNT], + complete_count: [ZERO; DMA_CHANNEL_COUNT], } } } @@ -184,13 +188,43 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); } - if isr.tcif(channel_num % 4) && cr.read().tcie() { - /* acknowledge transfer complete interrupt */ - dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); + let mut wake = false; + + if isr.htif(channel_num % 4) && cr.read().htie() { + // Acknowledge half transfer complete interrupt + dma.ifcr(channel_num / 4).write(|w| w.set_htif(channel_num % 4, true)); + wake = true; + } + + wake |= process_tcif(dma, channel_num, index); + + if wake { STATE.ch_wakers[index].wake(); } } +unsafe fn process_tcif(dma: pac::dma::Dma, channel_num: usize, index: usize) -> bool { + let isr_reg = dma.isr(channel_num / 4); + let cr_reg = dma.st(channel_num).cr(); + + // First, figure out if tcif is set without a cs. + if isr_reg.read().tcif(channel_num % 4) && cr_reg.read().tcie() { + // Make tcif test again within a cs to avoid race when incrementing complete_count. + critical_section::with(|_| { + if isr_reg.read().tcif(channel_num % 4) && cr_reg.read().tcie() { + // Acknowledge transfer complete interrupt + dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); + STATE.complete_count[index].fetch_add(1, Ordering::Release); + true + } else { + false + } + }) + } else { + false + } +} + #[cfg(any(dma_v2, dmamux))] pub type Request = u8; #[cfg(not(any(dma_v2, dmamux)))] @@ -530,6 +564,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { unsafe { dma.ifcr(isrn).write(|w| { + w.set_htif(isrbit, true); w.set_tcif(isrbit, true); w.set_teif(isrbit, true); }) @@ -593,32 +628,28 @@ impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { // ============================== impl DmaCtrl for C { - fn tcif(&self) -> bool { - let channel_number = self.num(); - let dma = self.regs(); - let isrn = channel_number / 4; - let isrbit = channel_number % 4; - - unsafe { dma.isr(isrn).read() }.tcif(isrbit) - } - - fn clear_tcif(&mut self) { - let channel_number = self.num(); - let dma = self.regs(); - let isrn = channel_number / 4; - let isrbit = channel_number % 4; - - unsafe { - dma.ifcr(isrn).write(|w| { - w.set_tcif(isrbit, true); - }) - } - } - fn ndtr(&self) -> usize { let ch = self.regs().st(self.num()); unsafe { ch.ndtr().read() }.ndt() as usize } + + fn get_complete_count(&self) -> usize { + let dma = self.regs(); + let channel_num = self.num(); + let index = self.index(); + // Manually process tcif in case transfer was completed and we are in a higher priority task. + unsafe { process_tcif(dma, channel_num, index) }; + STATE.complete_count[index].load(Ordering::Acquire) + } + + fn reset_complete_count(&mut self) -> usize { + let dma = self.regs(); + let channel_num = self.num(); + let index = self.index(); + // Manually process tcif in case transfer was completed and we are in a higher priority task. + unsafe { process_tcif(dma, channel_num, index) }; + STATE.complete_count[index].swap(0, Ordering::AcqRel) + } } pub struct RingBuffer<'a, C: Channel, W: Word> { @@ -657,7 +688,8 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { w.set_minc(vals::Inc::INCREMENTED); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_tcie(false); + w.set_htie(true); + w.set_tcie(true); w.set_circ(vals::Circ::ENABLED); #[cfg(dma_v1)] w.set_trbuff(true); @@ -703,7 +735,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(); + self.ringbuf.clear(&mut *self.channel); } /// Read bytes from the ring buffer @@ -712,6 +744,22 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { self.ringbuf.read(&mut *self.channel, buf) } + pub fn is_empty(&self) -> bool { + self.ringbuf.is_empty() + } + + pub fn len(&self) -> usize { + self.ringbuf.len() + } + + pub fn capacity(&self) -> usize { + self.ringbuf.dma_buf.len() + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + fn clear_irqs(&mut self) { let channel_number = self.channel.num(); let dma = self.channel.regs(); @@ -720,6 +768,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { unsafe { dma.ifcr(isrn).write(|w| { + w.set_htif(isrbit, true); w.set_tcif(isrbit, true); w.set_teif(isrbit, true); }) @@ -733,6 +782,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { unsafe { ch.cr().write(|w| { w.set_teie(true); + w.set_htie(true); w.set_tcie(true); }) } @@ -743,15 +793,10 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { unsafe { ch.cr().read() }.en() } - /// Gets the total remaining transfers for the channel - /// Note: this will be zero for transfers that completed without cancellation. - pub fn get_remaining_transfers(&self) -> usize { + /// Synchronize the position of the ring buffer to the actual DMA controller position + pub fn reload_position(&mut self) { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.ndtr().read() }.ndt() as usize - } - - pub fn set_ndtr(&mut self, ndtr: usize) { - self.ringbuf.ndtr = ndtr; + self.ringbuf.ndtr = unsafe { ch.ndtr().read() }.ndt() as usize; } } diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index f9ace6018..544ec9461 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -10,14 +10,6 @@ use super::word::Word; /// to the current register value. `ndtr` describes the current position of the DMA /// write. /// -/// # Safety -/// -/// The ring buffer controls the TCIF (transfer completed interrupt flag) to -/// detect buffer overruns, hence this interrupt must be disabled. -/// The buffer can detect overruns up to one period, that is, for a X byte buffer, -/// overruns can be detected if they happen from byte X+1 up to 2X. After this -/// point, overrunds may or may not be detected. -/// /// # Buffer layout /// /// ```text @@ -39,7 +31,6 @@ pub struct DmaRingBuffer<'a, W: Word> { pub(crate) dma_buf: &'a mut [W], first: usize, pub ndtr: usize, - expect_next_read_to_wrap: bool, } #[derive(Debug, PartialEq)] @@ -50,13 +41,13 @@ pub trait DmaCtrl { /// buffer until the dma writer wraps. fn ndtr(&self) -> usize; - /// Read the transfer completed interrupt flag - /// This flag is set by the dma controller when NDTR is reloaded, + /// Get the transfer completed counter. + /// This counter is incremented by the dma controller when NDTR is reloaded, /// i.e. when the writing wraps. - fn tcif(&self) -> bool; + fn get_complete_count(&self) -> usize; - /// Clear the transfer completed interrupt flag - fn clear_tcif(&mut self); + /// Reset the transfer completed counter to 0 and return the value just prior to the reset. + fn reset_complete_count(&mut self) -> usize; } impl<'a, W: Word> DmaRingBuffer<'a, W> { @@ -66,15 +57,14 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { dma_buf, first: 0, ndtr, - expect_next_read_to_wrap: false, } } /// Reset the ring buffer to its initial state - pub fn clear(&mut self) { + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { self.first = 0; self.ndtr = self.dma_buf.len(); - self.expect_next_read_to_wrap = false; + dma.reset_complete_count(); } /// The buffer end position @@ -83,14 +73,12 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { } /// Returns whether the buffer is empty - #[allow(dead_code)] pub fn is_empty(&self) -> bool { self.first == self.end() } /// The current number of bytes in the buffer /// This may change at any time if dma is currently active - #[allow(dead_code)] pub fn len(&self) -> usize { // Read out a stable end (the dma periheral can change it at anytime) let end = self.end(); @@ -112,27 +100,19 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { if self.first == end { // The buffer is currently empty - if dma.tcif() { - // The dma controller has written such that the ring buffer now wraps - // This is the special case where exactly n*dma_buf.len(), n = 1,2,..., bytes was written, - // but where additional bytes are now written causing the ring buffer to wrap. - // This is only an error if the writing has passed the current unread region. + if dma.get_complete_count() > 0 { + // The DMA has written such that the ring buffer wraps at least once self.ndtr = dma.ndtr(); - if self.end() > self.first { - dma.clear_tcif(); + if self.end() > self.first || dma.get_complete_count() > 1 { return Err(OverrunError); } } - self.expect_next_read_to_wrap = false; Ok(0) } else if self.first < end { // The available, unread portion in the ring buffer DOES NOT wrap - if self.expect_next_read_to_wrap { - // The read was expected to wrap but it did not - - dma.clear_tcif(); + if dma.get_complete_count() > 1 { return Err(OverrunError); } @@ -141,35 +121,39 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); - if dma.tcif() { - // The dma controller has written such that the ring buffer now wraps - - self.ndtr = dma.ndtr(); - if self.end() > self.first { - // The bytes that we have copied out have overflowed - // as the writer has now both wrapped and is currently writing - // within the region that we have just copied out - - // Clear transfer completed interrupt flag - dma.clear_tcif(); + match dma.get_complete_count() { + 0 => { + // The DMA writer has not wrapped before nor after the copy + } + 1 => { + // The DMA writer has written such that the ring buffer now wraps + self.ndtr = dma.ndtr(); + if self.end() > self.first || dma.get_complete_count() > 1 { + // The bytes that we have copied out have overflowed + // as the writer has now both wrapped and is currently writing + // within the region that we have just copied out + return Err(OverrunError); + } + } + _ => { return Err(OverrunError); } } self.first = (self.first + len) % self.dma_buf.len(); - self.expect_next_read_to_wrap = false; Ok(len) } else { // The available, unread portion in the ring buffer DOES wrap - // The dma controller has wrapped since we last read and is currently + // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - // If the unread portion wraps then the writer must also have wrapped, - // or it has wrapped and we already cleared the TCIF flag - assert!(dma.tcif() || self.expect_next_read_to_wrap); + let complete_count = dma.get_complete_count(); + if complete_count > 1 { + return Err(OverrunError); + } - // Clear transfer completed interrupt flag - dma.clear_tcif(); + // If the unread portion wraps then the writer must also have wrapped + assert!(complete_count == 1); if self.first + buf.len() < self.dma_buf.len() { // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. @@ -182,13 +166,12 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { // We have now copied out the data from dma_buf // Make sure that the just read part was not overwritten during the copy self.ndtr = dma.ndtr(); - if self.end() > self.first || dma.tcif() { + if self.end() > self.first || dma.get_complete_count() > 1 { // The writer has entered the data that we have just read since we read out `end` in the beginning and until now. return Err(OverrunError); } self.first = (self.first + len) % self.dma_buf.len(); - self.expect_next_read_to_wrap = true; Ok(len) } else { // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, @@ -201,14 +184,14 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // We have now copied out the data from dma_buf - // Make sure that the just read part was not overwritten during the copy + // Reset complete counter and make sure that the just read part was not overwritten during the copy self.ndtr = dma.ndtr(); - if self.end() > self.first || dma.tcif() { + let complete_count = dma.reset_complete_count(); + if self.end() > self.first || complete_count > 1 { return Err(OverrunError); } self.first = head; - self.expect_next_read_to_wrap = false; Ok(tail + head) } } @@ -243,14 +226,14 @@ mod tests { struct TestCtrl { next_ndtr: RefCell>, - tcif: bool, + complete_count: usize, } impl TestCtrl { pub const fn new() -> Self { Self { next_ndtr: RefCell::new(None), - tcif: false, + complete_count: 0, } } @@ -264,12 +247,14 @@ mod tests { self.next_ndtr.borrow_mut().unwrap() } - fn tcif(&self) -> bool { - self.tcif + fn get_complete_count(&self) -> usize { + self.complete_count } - fn clear_tcif(&mut self) { - self.tcif = false; + fn reset_complete_count(&mut self) -> usize { + let old = self.complete_count; + self.complete_count = 0; + old } } @@ -320,7 +305,7 @@ mod tests { ringbuf.ndtr = 10; // The dma controller has written 4 + 6 bytes and has reloaded NDTR - ctrl.tcif = true; + ctrl.complete_count = 1; ctrl.set_next_ndtr(10); assert!(!ringbuf.is_empty()); @@ -346,14 +331,14 @@ mod tests { ringbuf.ndtr = 6; // The dma controller has written 6 + 2 bytes and has reloaded NDTR - ctrl.tcif = true; + ctrl.complete_count = 1; ctrl.set_next_ndtr(14); let mut buf = [0; 2]; assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([2, 3], buf); - assert_eq!(true, ctrl.tcif); // The interrupt flag IS NOT cleared + assert_eq!(1, ctrl.complete_count); // The interrupt flag IS NOT cleared } #[test] @@ -365,14 +350,14 @@ mod tests { ringbuf.ndtr = 10; // The dma controller has written 6 + 2 bytes and has reloaded NDTR - ctrl.tcif = true; + ctrl.complete_count = 1; ctrl.set_next_ndtr(14); let mut buf = [0; 10]; assert_eq!(10, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!([12, 13, 14, 15, 0, 1, 2, 3, 4, 5], buf); - assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + assert_eq!(0, ctrl.complete_count); // The interrupt flag IS cleared } #[test] @@ -387,12 +372,12 @@ mod tests { assert!(ringbuf.is_empty()); // The ring buffer thinks that it is empty // The dma controller has written exactly 16 bytes - ctrl.tcif = true; + ctrl.complete_count = 1; let mut buf = [0; 2]; assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); - assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + assert_eq!(1, ctrl.complete_count); // The complete counter is not reset } #[test] @@ -404,13 +389,13 @@ mod tests { ringbuf.ndtr = 6; // The dma controller has written 6 + 3 bytes and has reloaded NDTR - ctrl.tcif = true; + ctrl.complete_count = 1; ctrl.set_next_ndtr(13); let mut buf = [0; 2]; assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); - assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + assert_eq!(1, ctrl.complete_count); // The complete counter is not reset } #[test] @@ -422,12 +407,12 @@ mod tests { ringbuf.ndtr = 10; // The dma controller has written 6 + 13 bytes and has reloaded NDTR - ctrl.tcif = true; + ctrl.complete_count = 1; ctrl.set_next_ndtr(3); let mut buf = [0; 2]; assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); - assert_eq!(false, ctrl.tcif); // The interrupt flag IS cleared + assert_eq!(1, ctrl.complete_count); // The complete counter is not reset } } diff --git a/embassy-stm32/src/usart/rx_ringbuffered.rs b/embassy-stm32/src/usart/rx_ringbuffered.rs index 0dc90ece7..dc21f557b 100644 --- a/embassy-stm32/src/usart/rx_ringbuffered.rs +++ b/embassy-stm32/src/usart/rx_ringbuffered.rs @@ -4,8 +4,9 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::PeripheralRef; +use futures::future::{select, Either}; -use super::{rdr, sr, BasicInstance, Error, UartRx}; +use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; use crate::dma::ringbuffer::OverrunError; use crate::dma::RingBuffer; @@ -98,7 +99,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD } /// Read bytes that are readily available in the ring buffer. - /// If no bytes are currently available in the buffer the call waits until data are received. + /// If no bytes are currently available in the buffer the call waits until the some + /// bytes are available (at least one byte and at most half the buffer size) /// /// Background receive is started if `start()` has not been previously called. /// @@ -107,10 +109,9 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD pub async fn read(&mut self, buf: &mut [u8]) -> Result { let r = T::regs(); + // Start background receive if it was not already started // SAFETY: read only let is_started = unsafe { r.cr3().read().dmar() }; - - // Start background receive if it was not already started if !is_started { self.start()?; } @@ -132,8 +133,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD } } - let ndtr = self.ring_buf.get_remaining_transfers(); - self.ring_buf.set_ndtr(ndtr); + self.ring_buf.reload_position(); match self.ring_buf.read(buf) { Ok(len) if len == 0 => {} Ok(len) => { @@ -148,28 +148,32 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD } } - // Wait for any data since `ndtr` - self.wait_for_data(ndtr).await?; + loop { + self.wait_for_data_or_idle().await?; + + self.ring_buf.reload_position(); + if !self.ring_buf.is_empty() { + break; + } + } - // ndtr is now different than the value provided to `wait_for_data()` - // Re-sample ndtr now when it has changed. - self.ring_buf.set_ndtr(self.ring_buf.get_remaining_transfers()); let len = self.ring_buf.read(buf).map_err(|_err| Error::Overrun)?; assert!(len > 0); + Ok(len) } - /// Wait for uart data - async fn wait_for_data(&mut self, old_ndtr: usize) -> Result<(), Error> { + /// Wait for uart idle or dma half-full or full + async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { let r = T::regs(); - // make sure USART state is restored to neutral state when this future is dropped - let _drop = OnDrop::new(move || { + // make sure USART state is restored to neutral state + let _on_drop = OnDrop::new(move || { // SAFETY: only clears Rx related flags unsafe { r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); + // disable idle line interrupt + w.set_idleie(false); }); } }); @@ -177,76 +181,65 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD // SAFETY: only sets Rx related flags unsafe { r.cr1().modify(|w| { - // enable RXNE interrupt - w.set_rxneie(true); + // enable idle line interrupt + w.set_idleie(true); }); } - // future which completes when RX "not empty" is detected, - // i.e. when there is data in uart rx register - let rxne = poll_fn(|cx| { - let s = T::state(); + compiler_fence(Ordering::SeqCst); - // Register waker to be awaken when RXNE interrupt is received + // Future which completes when there is dma is half full or full + let dma = poll_fn(|cx| { + self.ring_buf.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + self.ring_buf.reload_position(); + if !self.ring_buf.is_empty() { + // Some data is now available + Poll::Ready(()) + } else { + Poll::Pending + } + }); + + // Future which completes when idle line is detected + let uart = poll_fn(|cx| { + let s = T::state(); s.rx_waker.register(cx.waker()); compiler_fence(Ordering::SeqCst); // SAFETY: read only and we only use Rx related flags - let s = unsafe { sr(r).read() }; - let has_errors = s.pe() || s.fe() || s.ne() || s.ore(); + let sr = unsafe { sr(r).read() }; + + let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); if has_errors { - if s.pe() { + if sr.pe() { return Poll::Ready(Err(Error::Parity)); - } else if s.fe() { + } else if sr.fe() { return Poll::Ready(Err(Error::Framing)); - } else if s.ne() { + } else if sr.ne() { return Poll::Ready(Err(Error::Noise)); } else { return Poll::Ready(Err(Error::Overrun)); } } - // Re-sample ndtr and determine if it has changed since we started - // waiting for data. - let new_ndtr = self.ring_buf.get_remaining_transfers(); - if new_ndtr != old_ndtr { - // Some data was received as NDTR has changed + if sr.idle() { + // Idle line is detected Poll::Ready(Ok(())) } else { - // It may be that the DMA controller is currently busy consuming the - // RX data register. We therefore wait register to become empty. - while unsafe { sr(r).read().rxne() } {} - - compiler_fence(Ordering::SeqCst); - - // Re-get again: This time we know that the DMA controller has consumed - // the current read register if it was busy doing so - let new_ndtr = self.ring_buf.get_remaining_transfers(); - if new_ndtr != old_ndtr { - // Some data was received as NDTR has changed - Poll::Ready(Ok(())) - } else { - Poll::Pending - } + Poll::Pending } }); - compiler_fence(Ordering::SeqCst); - - let new_ndtr = self.ring_buf.get_remaining_transfers(); - if new_ndtr != old_ndtr { - // Fast path - NDTR has already changed, no reason to poll - Ok(()) - } else { - // NDTR has not changed since we first read from the ring buffer - // Wait for RXNE interrupt... - match rxne.await { - Ok(()) => Ok(()), - Err(e) => { - self.teardown_uart(); - Err(e) - } + match select(dma, uart).await { + Either::Left(((), _)) => Ok(()), + Either::Right((Ok(()), _)) => Ok(()), + Either::Right((Err(e), _)) => { + self.teardown_uart(); + Err(e) } } } diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 3ea8bfb7b..48dc25b0e 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -56,6 +56,7 @@ mod board { } const ONE_BYTE_DURATION_US: u32 = 9_000_000 / 115200; +const DMA_BUF_SIZE: usize = 64; #[embassy_executor::main] async fn main(spawner: Spawner) { @@ -114,7 +115,7 @@ async fn main(spawner: Spawner) { let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); let (tx, rx) = usart.split(); - static mut DMA_BUF: [u8; 64] = [0; 64]; + static mut DMA_BUF: [u8; DMA_BUF_SIZE] = [0; DMA_BUF_SIZE]; let dma_buf = unsafe { DMA_BUF.as_mut() }; let rx = rx.into_ring_buffered(dma_buf); @@ -159,7 +160,14 @@ async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::Rx loop { let mut buf = [0; 100]; let max_len = 1 + (rng.next_u32() as usize % (buf.len() - 1)); - let received = rx.read(&mut buf[..max_len]).await.unwrap(); + let received = match rx.read(&mut buf[..max_len]).await { + Ok(r) => r, + Err(e) => { + error!("Test fail! read error: {:?}", e); + cortex_m::asm::bkpt(); + return; + } + }; if expected.is_none() { info!("Test started"); @@ -176,8 +184,11 @@ async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::Rx } if received < max_len { - let byte_count = rng.next_u32() % 64; - Timer::after(Duration::from_micros((byte_count * ONE_BYTE_DURATION_US) as _)).await; + let byte_count = rng.next_u32() % (DMA_BUF_SIZE as u32); + let random_delay_us = (byte_count * ONE_BYTE_DURATION_US) as u64; + if random_delay_us > 200 { + Timer::after(Duration::from_micros(random_delay_us - 200)).await; + } } i += 1; diff --git a/tests/utils/src/bin/saturate_serial.rs b/tests/utils/src/bin/saturate_serial.rs index 28480516d..18ca12fb7 100644 --- a/tests/utils/src/bin/saturate_serial.rs +++ b/tests/utils/src/bin/saturate_serial.rs @@ -1,18 +1,19 @@ use std::path::Path; use std::time::Duration; -use std::{env, io, thread}; +use std::{env, io, process, thread}; use rand::random; use serial::SerialPort; pub fn main() { if let Some(port_name) = env::args().nth(1) { - let sleep = env::args().position(|x| x == "--sleep").is_some(); + let idles = env::args().position(|x| x == "--idles").is_some(); println!("Saturating port {:?} with 115200 8N1", port_name); - println!("Sleep: {}", sleep); + println!("Idles: {}", idles); + println!("Process ID: {}", process::id()); let mut port = serial::open(&port_name).unwrap(); - if saturate(&mut port, sleep).is_err() { + if saturate(&mut port, idles).is_err() { eprintln!("Unable to saturate port"); } } else { @@ -23,7 +24,7 @@ pub fn main() { } } -fn saturate(port: &mut T, sleep: bool) -> io::Result<()> { +fn saturate(port: &mut T, idles: bool) -> io::Result<()> { port.reconfigure(&|settings| { settings.set_baud_rate(serial::Baud115200)?; settings.set_char_size(serial::Bits8); @@ -39,7 +40,7 @@ fn saturate(port: &mut T, sleep: bool) -> io::Result<()> { port.write_all(&buf)?; - if sleep { + if idles { let micros = (random::() % 1000) as u64; println!("Sleeping {}us", micros); port.flush().unwrap(); @@ -49,4 +50,4 @@ fn saturate(port: &mut T, sleep: bool) -> io::Result<()> { written += len; println!("Written: {}", written); } -} \ No newline at end of file +} From 775740590839904a6b16a8afaac9e7861226bf92 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 27 Apr 2023 10:58:41 +0200 Subject: [PATCH 1003/1575] Remove unused import --- embassy-stm32/src/usart/rx_ringbuffered.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/rx_ringbuffered.rs b/embassy-stm32/src/usart/rx_ringbuffered.rs index dc21f557b..8d3848269 100644 --- a/embassy-stm32/src/usart/rx_ringbuffered.rs +++ b/embassy-stm32/src/usart/rx_ringbuffered.rs @@ -6,7 +6,7 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::PeripheralRef; use futures::future::{select, Either}; -use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; +use super::{rdr, sr, BasicInstance, Error, UartRx}; use crate::dma::ringbuffer::OverrunError; use crate::dma::RingBuffer; From 45843034ec7c53f3f619bf1dcc6c145c428b0601 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 27 Apr 2023 11:59:51 +0200 Subject: [PATCH 1004/1575] Actually clear idle flag --- embassy-stm32/src/usart/rx_ringbuffered.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/rx_ringbuffered.rs b/embassy-stm32/src/usart/rx_ringbuffered.rs index 8d3848269..33b750497 100644 --- a/embassy-stm32/src/usart/rx_ringbuffered.rs +++ b/embassy-stm32/src/usart/rx_ringbuffered.rs @@ -6,7 +6,7 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::PeripheralRef; use futures::future::{select, Either}; -use super::{rdr, sr, BasicInstance, Error, UartRx}; +use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; use crate::dma::ringbuffer::OverrunError; use crate::dma::RingBuffer; @@ -213,6 +213,13 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD // SAFETY: read only and we only use Rx related flags let sr = unsafe { sr(r).read() }; + // SAFETY: only clears Rx related flags + unsafe { + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + } + let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); if has_errors { if sr.pe() { From 14e0090cb1e60ce477ae4f080d0deb724dbc3b9e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 18:14:34 +0200 Subject: [PATCH 1005/1575] stm32/dma: remove separate process_tcif. --- embassy-stm32/src/dma/dma.rs | 46 +++++++----------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 10ae20f5c..69a5ec4e4 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -8,14 +8,13 @@ use atomic_polyfill::AtomicUsize; use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use pac::dma::regs; use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; -use crate::pac::dma::vals; +use crate::pac::dma::{regs, vals}; use crate::{interrupt, pac}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -196,35 +195,18 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: wake = true; } - wake |= process_tcif(dma, channel_num, index); + if isr.tcif(channel_num % 4) && cr.read().tcie() { + // Acknowledge transfer complete interrupt + dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); + STATE.complete_count[index].fetch_add(1, Ordering::Release); + wake = true; + } if wake { STATE.ch_wakers[index].wake(); } } -unsafe fn process_tcif(dma: pac::dma::Dma, channel_num: usize, index: usize) -> bool { - let isr_reg = dma.isr(channel_num / 4); - let cr_reg = dma.st(channel_num).cr(); - - // First, figure out if tcif is set without a cs. - if isr_reg.read().tcif(channel_num % 4) && cr_reg.read().tcie() { - // Make tcif test again within a cs to avoid race when incrementing complete_count. - critical_section::with(|_| { - if isr_reg.read().tcif(channel_num % 4) && cr_reg.read().tcie() { - // Acknowledge transfer complete interrupt - dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); - STATE.complete_count[index].fetch_add(1, Ordering::Release); - true - } else { - false - } - }) - } else { - false - } -} - #[cfg(any(dma_v2, dmamux))] pub type Request = u8; #[cfg(not(any(dma_v2, dmamux)))] @@ -634,21 +616,11 @@ impl DmaCtrl for C { } fn get_complete_count(&self) -> usize { - let dma = self.regs(); - let channel_num = self.num(); - let index = self.index(); - // Manually process tcif in case transfer was completed and we are in a higher priority task. - unsafe { process_tcif(dma, channel_num, index) }; - STATE.complete_count[index].load(Ordering::Acquire) + STATE.complete_count[self.index()].load(Ordering::Acquire) } fn reset_complete_count(&mut self) -> usize { - let dma = self.regs(); - let channel_num = self.num(); - let index = self.index(); - // Manually process tcif in case transfer was completed and we are in a higher priority task. - unsafe { process_tcif(dma, channel_num, index) }; - STATE.complete_count[index].swap(0, Ordering::AcqRel) + STATE.complete_count[self.index()].swap(0, Ordering::AcqRel) } } From 25864ae4dc3e5a765f6a3e2bb52bceb4df2e0199 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 18:14:53 +0200 Subject: [PATCH 1006/1575] stm32/bdma: add ringbuffer support. --- embassy-stm32/src/dma/bdma.rs | 187 +++++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index a23bb8cd7..88df76ba7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -3,18 +3,20 @@ use core::future::Future; use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; -use core::task::{Context, Poll}; +use core::task::{Context, Poll, Waker}; +use atomic_polyfill::AtomicUsize; use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac; -use crate::pac::bdma::vals; +use crate::pac::bdma::{regs, vals}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -48,13 +50,16 @@ impl From

for vals::Dir { struct State { ch_wakers: [AtomicWaker; BDMA_CHANNEL_COUNT], + complete_count: [AtomicUsize; BDMA_CHANNEL_COUNT], } impl State { const fn new() -> Self { + const ZERO: AtomicUsize = AtomicUsize::new(0); const AW: AtomicWaker = AtomicWaker::new(); Self { ch_wakers: [AW; BDMA_CHANNEL_COUNT], + complete_count: [ZERO; BDMA_CHANNEL_COUNT], } } } @@ -105,8 +110,23 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index if isr.teif(channel_num) { panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); } + + let mut wake = false; + + if isr.htif(channel_num) && cr.read().htie() { + // Acknowledge half transfer complete interrupt + dma.ifcr().write(|w| w.set_htif(channel_num, true)); + wake = true; + } + if isr.tcif(channel_num) && cr.read().tcie() { - cr.write(|_| ()); // Disable channel interrupts with the default value. + // Acknowledge transfer complete interrupt + dma.ifcr().write(|w| w.set_tcif(channel_num, true)); + STATE.complete_count[index].fetch_add(1, Ordering::Release); + wake = true; + } + + if wake { STATE.ch_wakers[index].wake(); } } @@ -252,6 +272,7 @@ impl<'a, C: Channel> Transfer<'a, C> { let mut this = Self { channel }; this.clear_irqs(); + STATE.complete_count[this.channel.index()].store(0, Ordering::Release); #[cfg(dmamux)] super::dmamux::configure_dmamux(&mut *this.channel, _request); @@ -299,7 +320,9 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); - unsafe { ch.cr().read() }.en() + let en = unsafe { ch.cr().read() }.en(); + let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; + en && !tcif } /// Gets the total remaining transfers for the channel @@ -342,3 +365,159 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { } } } + +// ============================== + +impl DmaCtrl for C { + fn ndtr(&self) -> usize { + let ch = self.regs().ch(self.num()); + unsafe { ch.ndtr().read() }.ndt() as usize + } + + fn get_complete_count(&self) -> usize { + STATE.complete_count[self.index()].load(Ordering::Acquire) + } + + fn reset_complete_count(&mut self) -> usize { + STATE.complete_count[self.index()].swap(0, Ordering::AcqRel) + } +} + +pub struct RingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: DmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + _options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::PeripheralToMemory; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + #[cfg(bdma_v2)] + critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request))); + + let mut w = regs::Cr(0); + w.set_psize(data_size.into()); + w.set_msize(data_size.into()); + w.set_minc(vals::Inc::ENABLED); + w.set_dir(dir.into()); + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + w.set_circ(vals::Circ::ENABLED); + w.set_pl(vals::Pl::VERYHIGH); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: DmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.ch(channel_number); + ch.par().write_value(peri_addr as u32); + ch.mar().write_value(buffer_ptr as u32); + ch.ndtr().write(|w| w.set_ndt(len as u16)); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.cr().write_value(self.cr) } + } + + pub fn clear(&mut self) { + self.ringbuf.clear(&mut *self.channel); + } + + /// Read bytes from the ring buffer + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + pub fn read(&mut self, buf: &mut [W]) -> Result { + self.ringbuf.read(&mut *self.channel, buf) + } + + pub fn is_empty(&self) -> bool { + self.ringbuf.is_empty() + } + + pub fn len(&self) -> usize { + self.ringbuf.len() + } + + pub fn capacity(&self) -> usize { + self.ringbuf.dma_buf.len() + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + + fn clear_irqs(&mut self) { + let dma = self.channel.regs(); + unsafe { + dma.ifcr().write(|w| { + w.set_htif(self.channel.num(), true); + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }) + } + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }) + } + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.cr().read() }.en() + } + + /// Synchronize the position of the ring buffer to the actual DMA controller position + pub fn reload_position(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + self.ringbuf.ndtr = unsafe { ch.ndtr().read() }.ndt() as usize; + } +} + +impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} From 96e8a7ddb9b768a2827dffa5c72723b1075381ad Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 18:15:46 +0200 Subject: [PATCH 1007/1575] stm32/uart: feature-gate ringbuffer out when using gpdma, not supported yet. --- embassy-stm32/src/dma/ringbuffer.rs | 2 ++ embassy-stm32/src/usart/mod.rs | 3 +++ tests/stm32/Cargo.toml | 20 +++++++++++++------- tests/stm32/src/bin/usart_rx_ringbuffered.rs | 2 ++ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 544ec9461..02964eb62 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -1,3 +1,5 @@ +#![cfg_attr(gpdma, allow(unused))] + use core::ops::Range; use core::sync::atomic::{compiler_fence, Ordering}; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index f3dea3391..ad450f2b3 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -980,7 +980,10 @@ mod eio { pub use buffered::*; #[cfg(feature = "nightly")] mod buffered; + +#[cfg(not(gpdma))] mod rx_ringbuffered; +#[cfg(not(gpdma))] pub use rx_ringbuffered::RingBufferedUartRx; #[cfg(usart_v1)] diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 240fad522..eca470358 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -5,18 +5,19 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [features] -stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono"] # Nucleo -stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo -stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo -stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo +stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill +stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono", "not-gpdma"] # Nucleo +stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo +stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo +stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] chrono = ["embassy-stm32/chrono", "dep:chrono"] +not-gpdma = [] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } @@ -80,6 +81,11 @@ name = "usart_dma" path = "src/bin/usart_dma.rs" required-features = [] +[[bin]] +name = "usart_rx_ringbuffered" +path = "src/bin/usart_rx_ringbuffered.rs" +required-features = [ "not-gpdma",] + # END TESTS [profile.dev] diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 48dc25b0e..86bcfab8d 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -1,3 +1,5 @@ +// required-features: not-gpdma + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] From 00cde67abe23aa299f94fb6e1aa6cb2c3de69788 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 19:10:00 +0200 Subject: [PATCH 1008/1575] stm32/dma: solve overlapping impl on DmaCtrl on stm32h7 --- embassy-stm32/src/dma/bdma.rs | 14 ++++++++------ embassy-stm32/src/dma/dma.rs | 14 ++++++++------ embassy-stm32/src/dma/ringbuffer.rs | 6 +++--- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 88df76ba7..0202ec379 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -368,18 +368,20 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { // ============================== -impl DmaCtrl for C { +struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); + +impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn ndtr(&self) -> usize { - let ch = self.regs().ch(self.num()); + let ch = self.0.regs().ch(self.0.num()); unsafe { ch.ndtr().read() }.ndt() as usize } fn get_complete_count(&self) -> usize { - STATE.complete_count[self.index()].load(Ordering::Acquire) + STATE.complete_count[self.0.index()].load(Ordering::Acquire) } fn reset_complete_count(&mut self) -> usize { - STATE.complete_count[self.index()].swap(0, Ordering::AcqRel) + STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } } @@ -451,13 +453,13 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(&mut *self.channel); + self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } /// Read bytes from the ring buffer /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result { - self.ringbuf.read(&mut *self.channel, buf) + self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } pub fn is_empty(&self) -> bool { diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 69a5ec4e4..7b17d9e49 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -609,18 +609,20 @@ impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { // ============================== -impl DmaCtrl for C { +struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); + +impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn ndtr(&self) -> usize { - let ch = self.regs().st(self.num()); + let ch = self.0.regs().st(self.0.num()); unsafe { ch.ndtr().read() }.ndt() as usize } fn get_complete_count(&self) -> usize { - STATE.complete_count[self.index()].load(Ordering::Acquire) + STATE.complete_count[self.0.index()].load(Ordering::Acquire) } fn reset_complete_count(&mut self) -> usize { - STATE.complete_count[self.index()].swap(0, Ordering::AcqRel) + STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } } @@ -707,13 +709,13 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(&mut *self.channel); + self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } /// Read bytes from the ring buffer /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result { - self.ringbuf.read(&mut *self.channel, buf) + self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } pub fn is_empty(&self) -> bool { diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 02964eb62..38cc87ae9 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -63,7 +63,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { } /// Reset the ring buffer to its initial state - pub fn clear(&mut self, dma: &mut impl DmaCtrl) { + pub fn clear(&mut self, mut dma: impl DmaCtrl) { self.first = 0; self.ndtr = self.dma_buf.len(); dma.reset_complete_count(); @@ -94,7 +94,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { /// Read bytes from the ring buffer /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result { + pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result { let end = self.end(); compiler_fence(Ordering::SeqCst); @@ -244,7 +244,7 @@ mod tests { } } - impl DmaCtrl for TestCtrl { + impl DmaCtrl for &mut TestCtrl { fn ndtr(&self) -> usize { self.next_ndtr.borrow_mut().unwrap() } From 1806422763be4e9e47201f741a03e5968fc8dedb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 18:16:29 +0200 Subject: [PATCH 1009/1575] stm32/test: add real defmt timestamp --- tests/stm32/Cargo.toml | 3 ++- tests/stm32/src/example_common.rs | 11 ----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index eca470358..5cd949661 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -22,8 +22,9 @@ not-gpdma = [] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" defmt-rtt = "0.4" diff --git a/tests/stm32/src/example_common.rs b/tests/stm32/src/example_common.rs index c47ed75c4..a4f8668c7 100644 --- a/tests/stm32/src/example_common.rs +++ b/tests/stm32/src/example_common.rs @@ -1,22 +1,11 @@ #![macro_use] -use core::sync::atomic::{AtomicUsize, Ordering}; - pub use defmt::*; #[allow(unused)] use embassy_stm32::time::Hertz; use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; -defmt::timestamp! {"{=u64}", { - static COUNT: AtomicUsize = AtomicUsize::new(0); - // NOTE(no-CAS) `timestamps` runs with interrupts disabled - let n = COUNT.load(Ordering::Relaxed); - COUNT.store(n + 1, Ordering::Relaxed); - n as u64 - } -} - pub fn config() -> Config { #[allow(unused_mut)] let mut config = Config::default(); From 76017796933f0335bcdadbd6b765721833fdacec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 18:17:02 +0200 Subject: [PATCH 1010/1575] stm32/test: cleanup ringbuffer test, exit on success (transferring 100kb) --- tests/stm32/src/bin/usart_rx_ringbuffered.rs | 77 ++++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 86bcfab8d..2c4a8fdf4 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -6,6 +6,7 @@ #[path = "../example_common.rs"] mod example_common; +use defmt::{assert_eq, panic}; use embassy_executor::Spawner; use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; @@ -34,9 +35,9 @@ mod board { } #[cfg(feature = "stm32f429zi")] mod board { - pub type Uart = embassy_stm32::peripherals::USART2; - pub type TxDma = embassy_stm32::peripherals::DMA1_CH6; - pub type RxDma = embassy_stm32::peripherals::DMA1_CH5; + pub type Uart = embassy_stm32::peripherals::USART6; + pub type TxDma = embassy_stm32::peripherals::DMA2_CH6; + pub type RxDma = embassy_stm32::peripherals::DMA2_CH1; } #[cfg(feature = "stm32wb55rg")] mod board { @@ -56,9 +57,14 @@ mod board { pub type TxDma = embassy_stm32::peripherals::GPDMA1_CH0; pub type RxDma = embassy_stm32::peripherals::GPDMA1_CH1; } +#[cfg(feature = "stm32c031c6")] +mod board { + pub type Uart = embassy_stm32::peripherals::USART1; + pub type TxDma = embassy_stm32::peripherals::DMA1_CH1; + pub type RxDma = embassy_stm32::peripherals::DMA1_CH2; +} -const ONE_BYTE_DURATION_US: u32 = 9_000_000 / 115200; -const DMA_BUF_SIZE: usize = 64; +const DMA_BUF_SIZE: usize = 256; #[embassy_executor::main] async fn main(spawner: Spawner) { @@ -83,8 +89,14 @@ async fn main(spawner: Spawner) { let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PA2, p.PA3, p.USART2, interrupt::take!(USART2), p.DMA1_CH6, p.DMA1_CH5); + let (tx, rx, usart, irq, tx_dma, rx_dma) = ( + p.PG14, + p.PG9, + p.USART6, + interrupt::take!(USART6), + p.DMA2_CH6, + p.DMA2_CH1, + ); #[cfg(feature = "stm32wb55rg")] let (tx, rx, usart, irq, tx_dma, rx_dma) = ( p.PA2, @@ -106,11 +118,16 @@ async fn main(spawner: Spawner) { p.GPDMA1_CH0, p.GPDMA1_CH1, ); + #[cfg(feature = "stm32c031c6")] + let (tx, rx, usart, irq, tx_dma, rx_dma) = + (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); // To run this test, use the saturating_serial test utility to saturate the serial port let mut config = Config::default(); - config.baudrate = 115200; + // this is the fastest we can go without tuning RCC + // some chips have default pclk=8mhz, and uart can run at max pclk/16 + config.baudrate = 500_000; config.data_bits = DataBits::DataBits8; config.stop_bits = StopBits::STOP1; config.parity = Parity::ParityNone; @@ -135,19 +152,14 @@ async fn transmit_task(mut tx: UartTx<'static, board::Uart, board::TxDma>) { let mut i: u8 = 0; loop { let mut buf = [0; 32]; - let len = 1 + (rng.next_u32() as usize % (buf.len() - 1)); + let len = 1 + (rng.next_u32() as usize % buf.len()); for b in &mut buf[..len] { *b = i; i = i.wrapping_add(1); } tx.write(&buf[..len]).await.unwrap(); - Timer::after(Duration::from_micros((rng.next_u32() % 10000) as _)).await; - - //i += 1; - //if i % 1000 == 0 { - // trace!("Wrote {} times", i); - //} + Timer::after(Duration::from_micros((rng.next_u32() % 1000) as _)).await; } } @@ -158,44 +170,31 @@ async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::Rx let mut rng = ChaCha8Rng::seed_from_u64(1337); let mut i = 0; - let mut expected: Option = None; + let mut expected = 0; loop { let mut buf = [0; 100]; - let max_len = 1 + (rng.next_u32() as usize % (buf.len() - 1)); + let max_len = 1 + (rng.next_u32() as usize % buf.len()); let received = match rx.read(&mut buf[..max_len]).await { Ok(r) => r, Err(e) => { - error!("Test fail! read error: {:?}", e); - cortex_m::asm::bkpt(); - return; + panic!("Test fail! read error: {:?}", e); } }; - if expected.is_none() { - info!("Test started"); - expected = Some(buf[0]); - } - for byte in &buf[..received] { - if byte != &expected.unwrap() { - error!("Test fail! received {}, expected {}", *byte, expected.unwrap()); - cortex_m::asm::bkpt(); - return; - } - expected = Some(expected.unwrap().wrapping_add(1)); + assert_eq!(*byte, expected); + expected = expected.wrapping_add(1); } if received < max_len { - let byte_count = rng.next_u32() % (DMA_BUF_SIZE as u32); - let random_delay_us = (byte_count * ONE_BYTE_DURATION_US) as u64; - if random_delay_us > 200 { - Timer::after(Duration::from_micros(random_delay_us - 200)).await; - } + Timer::after(Duration::from_micros((rng.next_u32() % 1000) as _)).await; } - i += 1; - if i % 1000 == 0 { - trace!("Read {} times", i); + i += received; + + if i > 100000 { + info!("Test OK!"); + cortex_m::asm::bkpt(); } } } From a1d45303c336434929eb8eb7e55629c504a95b0e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 1 May 2023 18:17:29 +0200 Subject: [PATCH 1011/1575] stm32/test: fix race condition in uart_dma. --- tests/stm32/src/bin/usart_dma.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index d673df0f3..de6cd41d1 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -6,6 +6,7 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; use example_common::*; @@ -76,18 +77,26 @@ async fn main(_spawner: Spawner) { (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); + let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); - // We can't send too many bytes, they have to fit in the FIFO. - // This is because we aren't sending+receiving at the same time. - // For whatever reason, blocking works with 2 bytes but DMA only with 1?? + const LEN: usize = 128; + let mut tx_buf = [0; LEN]; + let mut rx_buf = [0; LEN]; + for i in 0..LEN { + tx_buf[i] = i as u8; + } - let data = [0x42]; - usart.write(&data).await.unwrap(); + let (mut tx, mut rx) = usart.split(); - let mut buf = [0; 1]; - usart.read(&mut buf).await.unwrap(); - assert_eq!(buf, data); + let tx_fut = async { + tx.write(&tx_buf).await.unwrap(); + }; + let rx_fut = async { + rx.read(&mut rx_buf).await.unwrap(); + }; + join(rx_fut, tx_fut).await; + + assert_eq!(tx_buf, rx_buf); info!("Test OK"); cortex_m::asm::bkpt(); From cd88e39f5fed0ed128f57d2e166f68a488e37698 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 1 May 2023 16:42:03 -0500 Subject: [PATCH 1012/1575] stm32/pwm: improve dead-time api --- embassy-stm32/src/pwm/complementary_pwm.rs | 146 +++++++++++++++++- examples/stm32f4/src/bin/pwm_complementary.rs | 31 +--- 2 files changed, 143 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index 13edfbaa3..3f8b43cfa 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -pub use stm32_metapac::timer::vals::Ckd; +use stm32_metapac::timer::vals::Ckd; use super::simple_pwm::*; use super::*; @@ -114,11 +114,145 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { unsafe { self.inner.set_compare_value(channel, duty) } } - pub fn set_dead_time_clock_division(&mut self, value: Ckd) { - unsafe { self.inner.set_dead_time_clock_division(value) } - } + /// Set the dead time as a proportion of max_duty + pub fn set_dead_time(&mut self, value: u16) { + let (ckd, value) = compute_dead_time_value(value); - pub fn set_dead_time_value(&mut self, value: u8) { - unsafe { self.inner.set_dead_time_value(value) } + unsafe { + self.inner.set_dead_time_clock_division(ckd); + self.inner.set_dead_time_value(value); + } + } +} + +fn compute_dead_time_value(value: u16) -> (Ckd, u8) { + /* + Dead-time = T_clk * T_dts * T_dtg + + T_dts: + This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the + dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters + (ETR, TIx), + 00: tDTS=tCK_INT + 01: tDTS=2*tCK_INT + 10: tDTS=4*tCK_INT + + T_dtg: + This bit-field defines the duration of the dead-time inserted between the complementary + outputs. DT correspond to this duration. + DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS. + DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS. + DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS. + DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS. + Example if TDTS=125ns (8MHz), dead-time possible values are: + 0 to 15875 ns by 125 ns steps, + 16 us to 31750 ns by 250 ns steps, + 32 us to 63us by 1 us steps, + 64 us to 126 us by 2 us steps + */ + + let mut error = u16::MAX; + let mut ckd = Ckd::DIV1; + let mut bits = 0u8; + + for this_ckd in [Ckd::DIV1, Ckd::DIV2, Ckd::DIV4] { + let outdiv = match this_ckd { + Ckd::DIV1 => 1, + Ckd::DIV2 => 2, + Ckd::DIV4 => 4, + _ => unreachable!(), + }; + + // 127 + // 128 + // .. + // 254 + // 256 + // .. + // 504 + // 512 + // .. + // 1008 + + let target = value / outdiv; + let (these_bits, result) = if target < 128 { + (target as u8, target) + } else if target < 255 { + (64 + (target / 2) as u8, (target - target % 2)) + } else if target < 508 { + (32 + (target / 8) as u8, (target - target % 8)) + } else if target < 1008 { + (32 + (target / 16) as u8, (target - target % 16)) + } else { + (u8::MAX, 1008) + }; + + let this_error = value.abs_diff(result * outdiv); + if error > this_error { + ckd = this_ckd; + bits = these_bits; + error = this_error; + } + + match error { + 0 => break, + _ => {} + } + } + + (ckd, bits) +} + +#[cfg(test)] +mod tests { + use super::{compute_dead_time_value, Ckd}; + + #[test] + fn test_compute_dead_time_value() { + struct test_run { + value: u16, + ckd: Ckd, + bits: u8, + } + + let fn_results = [ + test_run { + value: 1, + ckd: Ckd::DIV1, + bits: 1, + }, + test_run { + value: 125, + ckd: Ckd::DIV1, + bits: 125, + }, + test_run { + value: 245, + ckd: Ckd::DIV1, + bits: 64 + 245 / 2, + }, + test_run { + value: 255, + ckd: Ckd::DIV2, + bits: 127, + }, + test_run { + value: 400, + ckd: Ckd::DIV1, + bits: 32 + (400u16 / 8) as u8, + }, + test_run { + value: 600, + ckd: Ckd::DIV4, + bits: 64 + (600u16 / 8) as u8, + }, + ]; + + for test_run in fn_results { + let (ckd, bits) = compute_dead_time_value(test_run.value); + + assert_eq!(ckd.0, test_run.ckd.0); + assert_eq!(bits, test_run.bits); + } } } diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 6e17f3fd3..a8a68ed6e 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::complementary_pwm::{Ckd, ComplementaryPwm, ComplementaryPwmPin}; +use embassy_stm32::pwm::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; use embassy_stm32::pwm::simple_pwm::PwmPin; use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; @@ -31,34 +31,9 @@ async fn main(_spawner: Spawner) { khz(10), ); - /* - Dead-time = T_clk * T_dts * T_dtg - - T_dts: - This bit-field indicates the division ratio between the timer clock (CK_INT) frequency and the - dead-time and sampling clock (tDTS)used by the dead-time generators and the digital filters - (ETR, TIx), - 00: tDTS=tCK_INT - 01: tDTS=2*tCK_INT - 10: tDTS=4*tCK_INT - - T_dtg: - This bit-field defines the duration of the dead-time inserted between the complementary - outputs. DT correspond to this duration. - DTG[7:5]=0xx => DT=DTG[7:0]x tdtg with tdtg=tDTS. - DTG[7:5]=10x => DT=(64+DTG[5:0])xtdtg with Tdtg=2xtDTS. - DTG[7:5]=110 => DT=(32+DTG[4:0])xtdtg with Tdtg=8xtDTS. - DTG[7:5]=111 => DT=(32+DTG[4:0])xtdtg with Tdtg=16xtDTS. - Example if TDTS=125ns (8MHz), dead-time possible values are: - 0 to 15875 ns by 125 ns steps, - 16 us to 31750 ns by 250 ns steps, - 32 us to 63us by 1 us steps, - 64 us to 126 us by 2 us steps - */ - pwm.set_dead_time_clock_division(Ckd::DIV1); - pwm.set_dead_time_value(0); - let max = pwm.get_max_duty(); + pwm.set_dead_time(max / 1024); + pwm.enable(Channel::Ch1); info!("PWM initialized"); From 3c31236c1050f98c870ec03b98bc746e6e2ab1b5 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 07:40:12 +0200 Subject: [PATCH 1013/1575] rp: remove leftovers from #1414 forgot to remove these when they were no longer necessary or useful. oops. --- embassy-rp/src/uart/mod.rs | 8 +++----- tests/rp/src/bin/uart_dma.rs | 4 ---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index ebe32cc66..4084c158a 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -874,7 +874,6 @@ mod sealed { pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; - const ID: usize; type Interrupt: crate::interrupt::Interrupt; @@ -909,11 +908,10 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($inst:ident, $irq:ident, $id:expr, $tx_dreq:expr, $rx_dreq:expr) => { + ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - const ID: usize = $id; type Interrupt = crate::interrupt::$irq; @@ -939,8 +937,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0_IRQ, 0, 20, 21); -impl_instance!(UART1, UART1_IRQ, 1, 22, 23); +impl_instance!(UART0, UART0_IRQ, 20, 21); +impl_instance!(UART1, UART1_IRQ, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index 92aa205c9..52f42e582 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -53,10 +53,6 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); let mut irq = interrupt::take!(UART0_IRQ); - // TODO - // nuclear error reporting. just abort the entire transfer and invalidate the - // dma buffer, buffered buffer, fifo etc. - // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. { From c6424fdc112776b5ceeef4a01c56b1479c2901c5 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 08:29:32 +0200 Subject: [PATCH 1014/1575] gp/gpio: fix InputFuture edge waits InputFuture did not use and check edge interrupts correctly. InterruptTrigger should've checked for not 1,2,3,4 but 1,2,4,8 since the inte fields are bitmasks, and not clearing INTR would have repeatedly triggered edge interrupts early. --- embassy-rp/src/gpio.rs | 126 ++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 83 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 98e182868..7a86418aa 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -136,18 +136,6 @@ pub enum InterruptTrigger { AnyEdge, } -impl InterruptTrigger { - fn from_u32(value: u32) -> Option { - match value { - 1 => Some(InterruptTrigger::LevelLow), - 2 => Some(InterruptTrigger::LevelHigh), - 3 => Some(InterruptTrigger::EdgeLow), - 4 => Some(InterruptTrigger::EdgeHigh), - _ => None, - } - } -} - #[interrupt] unsafe fn IO_IRQ_BANK0() { let cpu = SIO.cpuid().read() as usize; @@ -166,26 +154,16 @@ unsafe fn IO_IRQ_BANK0() { let pin_group = (pin % 8) as usize; let event = (intsx.read().0 >> pin_group * 4) & 0xf as u32; - if let Some(trigger) = InterruptTrigger::from_u32(event) { + // no more than one event can be awaited per pin at any given time, so + // we can just clear all interrupt enables for that pin without having + // to check which event was signalled. + if event != 0 { critical_section::with(|_| { - proc_intx.inte(pin / 8).modify(|w| match trigger { - InterruptTrigger::AnyEdge => { - w.set_edge_high(pin_group, false); - w.set_edge_low(pin_group, false); - } - InterruptTrigger::LevelHigh => { - trace!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); - w.set_level_high(pin_group, false); - } - InterruptTrigger::LevelLow => { - w.set_level_low(pin_group, false); - } - InterruptTrigger::EdgeHigh => { - w.set_edge_high(pin_group, false); - } - InterruptTrigger::EdgeLow => { - w.set_edge_low(pin_group, false); - } + proc_intx.inte(pin / 8).modify(|w| { + w.set_edge_high(pin_group, false); + w.set_edge_low(pin_group, false); + w.set_level_high(pin_group, false); + w.set_level_low(pin_group, false); }); }); INTERRUPT_WAKERS[pin as usize].wake(); @@ -207,10 +185,23 @@ impl<'d, T: Pin> InputFuture<'d, T> { irq.disable(); irq.set_priority(interrupt::Priority::P3); + let pin_group = (pin.pin() % 8) as usize; + // first, clear the INTR register bits. without this INTR will still + // contain reports of previous edges, causing the IRQ to fire early + // on stale state. clearing these means that we can only detect edges + // that occur *after* the clear happened, but since both this and the + // alternative are fundamentally racy it's probably fine. + // (the alternative being checking the current level and waiting for + // its inverse, but that requires reading the current level and thus + // missing anything that happened before the level was read.) + pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); + }); + // Each INTR register is divided into 8 groups, one group for each // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, // and EGDE_HIGH. - let pin_group = (pin.pin() % 8) as usize; critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { @@ -227,7 +218,8 @@ impl<'d, T: Pin> InputFuture<'d, T> { w.set_edge_low(pin_group, true); } InterruptTrigger::AnyEdge => { - // noop + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); } }); }); @@ -257,47 +249,21 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin. let pin_group = (self.pin.pin() % 8) as usize; - // This should check the the level of the interrupt trigger level of - // the pin and if it has been disabled that means it was done by the - // interrupt service routine, so we then know that the event/trigger - // happened and Poll::Ready will be returned. - trace!("{:?} for pin {}", self.level, self.pin.pin()); - match self.level { - InterruptTrigger::AnyEdge => { - if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::LevelHigh => { - if !inte.level_high(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::LevelLow => { - if !inte.level_low(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::EdgeHigh => { - if !inte.edge_high(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::EdgeLow => { - if !inte.edge_low(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } + // since the interrupt handler clears all INTE flags we'll check that + // all have been cleared and unconditionally return Ready(()) if so. + // we don't need further handshaking since only a single event wait + // is possible for any given pin at any given time. + if !inte.edge_high(pin_group) + && !inte.edge_low(pin_group) + && !inte.level_high(pin_group) + && !inte.level_low(pin_group) + { + trace!( + "{:?} for pin {} was cleared, return Poll::Ready", + self.level, + self.pin.pin() + ); + return Poll::Ready(()); } trace!("InputFuture::poll return Poll::Pending"); Poll::Pending @@ -644,23 +610,17 @@ impl<'d, T: Pin> Flex<'d, T> { #[inline] pub async fn wait_for_rising_edge(&mut self) { - self.wait_for_low().await; - self.wait_for_high().await; + InputFuture::new(&mut self.pin, InterruptTrigger::EdgeHigh).await; } #[inline] pub async fn wait_for_falling_edge(&mut self) { - self.wait_for_high().await; - self.wait_for_low().await; + InputFuture::new(&mut self.pin, InterruptTrigger::EdgeLow).await; } #[inline] pub async fn wait_for_any_edge(&mut self) { - if self.is_high() { - self.wait_for_low().await; - } else { - self.wait_for_high().await; - } + InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; } } From 8fc92fdf6285190a1ba5ddf356958d49ed4225a3 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 08:34:36 +0200 Subject: [PATCH 1015/1575] rp/gpio: drop critical_section use we don't need critical sections if we just use atomic access aliases. --- embassy-rp/src/gpio.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 7a86418aa..66b9af04b 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::pac::common::{Reg, RW}; use crate::pac::SIO; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; const PIN_COUNT: usize = 30; const NEW_AW: AtomicWaker = AtomicWaker::new(); @@ -158,13 +158,11 @@ unsafe fn IO_IRQ_BANK0() { // we can just clear all interrupt enables for that pin without having // to check which event was signalled. if event != 0 { - critical_section::with(|_| { - proc_intx.inte(pin / 8).modify(|w| { - w.set_edge_high(pin_group, false); - w.set_edge_low(pin_group, false); - w.set_level_high(pin_group, false); - w.set_level_low(pin_group, false); - }); + proc_intx.inte(pin / 8).write_clear(|w| { + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); + w.set_level_high(pin_group, true); + w.set_level_low(pin_group, true); }); INTERRUPT_WAKERS[pin as usize].wake(); } @@ -202,8 +200,9 @@ impl<'d, T: Pin> InputFuture<'d, T> { // Each INTR register is divided into 8 groups, one group for each // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, // and EGDE_HIGH. - critical_section::with(|_| { - pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { + pin.int_proc() + .inte((pin.pin() / 8) as usize) + .write_set(|w| match level { InterruptTrigger::LevelHigh => { trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); @@ -222,7 +221,6 @@ impl<'d, T: Pin> InputFuture<'d, T> { w.set_edge_low(pin_group, true); } }); - }); irq.enable(); } From 54e695b1b2105c1b2484584d100564afc0679596 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 10:50:06 +0200 Subject: [PATCH 1016/1575] rp/pio: fix dma fixing the dma word size to 32 makes it impossible to implement any peripheral that takes its data in smaller chunks, eg uart, spi, i2c, ws2812, the list goes on. compiler barriers were also not set correctly; we need a SeqCst barrier before starting a transfer as well to avoid reordering of accesses into a buffer after dma has started. --- embassy-rp/src/pio.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 7faec10b5..cab57f765 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -8,10 +8,10 @@ use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::PeripheralRef; use embassy_sync::waitqueue::AtomicWaker; -use crate::dma::{Channel, Transfer}; +use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{Drive, Pin, Pull, SlewRate}; -use crate::pac::dma::vals::{DataSize, TreqSel}; +use crate::pac::dma::vals::TreqSel; use crate::pio::sealed::{PioInstance as _, SmInstance as _}; use crate::{interrupt, pac, peripherals, RegExt}; @@ -820,7 +820,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } - fn dma_push<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [u32]) -> Transfer<'a, C> { + fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; let sm_no = Self::Sm::SM_NO; @@ -829,10 +829,11 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { p.write_addr() .write_value(Self::Pio::PIO.txf(sm_no as usize).ptr() as u32); p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine w.set_treq_sel(TreqSel(pio_no * 8 + sm_no)); - w.set_data_size(DataSize::SIZE_WORD); + w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(true); w.set_incr_write(false); @@ -843,7 +844,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { Transfer::new(ch) } - fn dma_pull<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [u32]) -> Transfer<'a, C> { + fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; let sm_no = Self::Sm::SM_NO; @@ -852,10 +853,11 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { p.read_addr() .write_value(Self::Pio::PIO.rxf(sm_no as usize).ptr() as u32); p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { - // Set TX DREQ for this statemachine + // Set RX DREQ for this statemachine w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); - w.set_data_size(DataSize::SIZE_WORD); + w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(false); w.set_incr_write(true); From bab30a7e876e0c7f98e1c1f39a7d2494de5daece Mon Sep 17 00:00:00 2001 From: goueslati Date: Tue, 2 May 2023 12:16:48 +0100 Subject: [PATCH 1017/1575] added TL Mailbox initialization for STM32WB --- embassy-stm32/Cargo.toml | 2669 +++++++++-------- embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/tl_mbox/ble.rs | 26 + embassy-stm32/src/tl_mbox/channels.rs | 104 + embassy-stm32/src/tl_mbox/cmd.rs | 49 + embassy-stm32/src/tl_mbox/evt.rs | 8 + embassy-stm32/src/tl_mbox/mm.rs | 30 + embassy-stm32/src/tl_mbox/mod.rs | 318 ++ embassy-stm32/src/tl_mbox/sys.rs | 24 + .../src/tl_mbox/unsafe_linked_list.rs | 123 + examples/stm32wb/src/bin/tl_mbox.rs | 44 + 11 files changed, 2063 insertions(+), 1334 deletions(-) create mode 100644 embassy-stm32/src/tl_mbox/ble.rs create mode 100644 embassy-stm32/src/tl_mbox/channels.rs create mode 100644 embassy-stm32/src/tl_mbox/cmd.rs create mode 100644 embassy-stm32/src/tl_mbox/evt.rs create mode 100644 embassy-stm32/src/tl_mbox/mm.rs create mode 100644 embassy-stm32/src/tl_mbox/mod.rs create mode 100644 embassy-stm32/src/tl_mbox/sys.rs create mode 100644 embassy-stm32/src/tl_mbox/unsafe_linked_list.rs create mode 100644 examples/stm32wb/src/bin/tl_mbox.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9686b10ce..6810aca98 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -67,6 +67,7 @@ seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} +bit_field = "0.10.2" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -110,1337 +111,1337 @@ unstable-pac = [] unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # Chip-selection features -stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] -stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] -stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] -stm32c011j4 = [ "stm32-metapac/stm32c011j4" ] -stm32c011j6 = [ "stm32-metapac/stm32c011j6" ] -stm32c031c4 = [ "stm32-metapac/stm32c031c4" ] -stm32c031c6 = [ "stm32-metapac/stm32c031c6" ] -stm32c031f4 = [ "stm32-metapac/stm32c031f4" ] -stm32c031f6 = [ "stm32-metapac/stm32c031f6" ] -stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] -stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] -stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] -stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] -stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] -stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] -stm32f030cc = [ "stm32-metapac/stm32f030cc" ] -stm32f030f4 = [ "stm32-metapac/stm32f030f4" ] -stm32f030k6 = [ "stm32-metapac/stm32f030k6" ] -stm32f030r8 = [ "stm32-metapac/stm32f030r8" ] -stm32f030rc = [ "stm32-metapac/stm32f030rc" ] -stm32f031c4 = [ "stm32-metapac/stm32f031c4" ] -stm32f031c6 = [ "stm32-metapac/stm32f031c6" ] -stm32f031e6 = [ "stm32-metapac/stm32f031e6" ] -stm32f031f4 = [ "stm32-metapac/stm32f031f4" ] -stm32f031f6 = [ "stm32-metapac/stm32f031f6" ] -stm32f031g4 = [ "stm32-metapac/stm32f031g4" ] -stm32f031g6 = [ "stm32-metapac/stm32f031g6" ] -stm32f031k4 = [ "stm32-metapac/stm32f031k4" ] -stm32f031k6 = [ "stm32-metapac/stm32f031k6" ] -stm32f038c6 = [ "stm32-metapac/stm32f038c6" ] -stm32f038e6 = [ "stm32-metapac/stm32f038e6" ] -stm32f038f6 = [ "stm32-metapac/stm32f038f6" ] -stm32f038g6 = [ "stm32-metapac/stm32f038g6" ] -stm32f038k6 = [ "stm32-metapac/stm32f038k6" ] -stm32f042c4 = [ "stm32-metapac/stm32f042c4" ] -stm32f042c6 = [ "stm32-metapac/stm32f042c6" ] -stm32f042f4 = [ "stm32-metapac/stm32f042f4" ] -stm32f042f6 = [ "stm32-metapac/stm32f042f6" ] -stm32f042g4 = [ "stm32-metapac/stm32f042g4" ] -stm32f042g6 = [ "stm32-metapac/stm32f042g6" ] -stm32f042k4 = [ "stm32-metapac/stm32f042k4" ] -stm32f042k6 = [ "stm32-metapac/stm32f042k6" ] -stm32f042t6 = [ "stm32-metapac/stm32f042t6" ] -stm32f048c6 = [ "stm32-metapac/stm32f048c6" ] -stm32f048g6 = [ "stm32-metapac/stm32f048g6" ] -stm32f048t6 = [ "stm32-metapac/stm32f048t6" ] -stm32f051c4 = [ "stm32-metapac/stm32f051c4" ] -stm32f051c6 = [ "stm32-metapac/stm32f051c6" ] -stm32f051c8 = [ "stm32-metapac/stm32f051c8" ] -stm32f051k4 = [ "stm32-metapac/stm32f051k4" ] -stm32f051k6 = [ "stm32-metapac/stm32f051k6" ] -stm32f051k8 = [ "stm32-metapac/stm32f051k8" ] -stm32f051r4 = [ "stm32-metapac/stm32f051r4" ] -stm32f051r6 = [ "stm32-metapac/stm32f051r6" ] -stm32f051r8 = [ "stm32-metapac/stm32f051r8" ] -stm32f051t8 = [ "stm32-metapac/stm32f051t8" ] -stm32f058c8 = [ "stm32-metapac/stm32f058c8" ] -stm32f058r8 = [ "stm32-metapac/stm32f058r8" ] -stm32f058t8 = [ "stm32-metapac/stm32f058t8" ] -stm32f070c6 = [ "stm32-metapac/stm32f070c6" ] -stm32f070cb = [ "stm32-metapac/stm32f070cb" ] -stm32f070f6 = [ "stm32-metapac/stm32f070f6" ] -stm32f070rb = [ "stm32-metapac/stm32f070rb" ] -stm32f071c8 = [ "stm32-metapac/stm32f071c8" ] -stm32f071cb = [ "stm32-metapac/stm32f071cb" ] -stm32f071rb = [ "stm32-metapac/stm32f071rb" ] -stm32f071v8 = [ "stm32-metapac/stm32f071v8" ] -stm32f071vb = [ "stm32-metapac/stm32f071vb" ] -stm32f072c8 = [ "stm32-metapac/stm32f072c8" ] -stm32f072cb = [ "stm32-metapac/stm32f072cb" ] -stm32f072r8 = [ "stm32-metapac/stm32f072r8" ] -stm32f072rb = [ "stm32-metapac/stm32f072rb" ] -stm32f072v8 = [ "stm32-metapac/stm32f072v8" ] -stm32f072vb = [ "stm32-metapac/stm32f072vb" ] -stm32f078cb = [ "stm32-metapac/stm32f078cb" ] -stm32f078rb = [ "stm32-metapac/stm32f078rb" ] -stm32f078vb = [ "stm32-metapac/stm32f078vb" ] -stm32f091cb = [ "stm32-metapac/stm32f091cb" ] -stm32f091cc = [ "stm32-metapac/stm32f091cc" ] -stm32f091rb = [ "stm32-metapac/stm32f091rb" ] -stm32f091rc = [ "stm32-metapac/stm32f091rc" ] -stm32f091vb = [ "stm32-metapac/stm32f091vb" ] -stm32f091vc = [ "stm32-metapac/stm32f091vc" ] -stm32f098cc = [ "stm32-metapac/stm32f098cc" ] -stm32f098rc = [ "stm32-metapac/stm32f098rc" ] -stm32f098vc = [ "stm32-metapac/stm32f098vc" ] -stm32f100c4 = [ "stm32-metapac/stm32f100c4" ] -stm32f100c6 = [ "stm32-metapac/stm32f100c6" ] -stm32f100c8 = [ "stm32-metapac/stm32f100c8" ] -stm32f100cb = [ "stm32-metapac/stm32f100cb" ] -stm32f100r4 = [ "stm32-metapac/stm32f100r4" ] -stm32f100r6 = [ "stm32-metapac/stm32f100r6" ] -stm32f100r8 = [ "stm32-metapac/stm32f100r8" ] -stm32f100rb = [ "stm32-metapac/stm32f100rb" ] -stm32f100rc = [ "stm32-metapac/stm32f100rc" ] -stm32f100rd = [ "stm32-metapac/stm32f100rd" ] -stm32f100re = [ "stm32-metapac/stm32f100re" ] -stm32f100v8 = [ "stm32-metapac/stm32f100v8" ] -stm32f100vb = [ "stm32-metapac/stm32f100vb" ] -stm32f100vc = [ "stm32-metapac/stm32f100vc" ] -stm32f100vd = [ "stm32-metapac/stm32f100vd" ] -stm32f100ve = [ "stm32-metapac/stm32f100ve" ] -stm32f100zc = [ "stm32-metapac/stm32f100zc" ] -stm32f100zd = [ "stm32-metapac/stm32f100zd" ] -stm32f100ze = [ "stm32-metapac/stm32f100ze" ] -stm32f101c4 = [ "stm32-metapac/stm32f101c4" ] -stm32f101c6 = [ "stm32-metapac/stm32f101c6" ] -stm32f101c8 = [ "stm32-metapac/stm32f101c8" ] -stm32f101cb = [ "stm32-metapac/stm32f101cb" ] -stm32f101r4 = [ "stm32-metapac/stm32f101r4" ] -stm32f101r6 = [ "stm32-metapac/stm32f101r6" ] -stm32f101r8 = [ "stm32-metapac/stm32f101r8" ] -stm32f101rb = [ "stm32-metapac/stm32f101rb" ] -stm32f101rc = [ "stm32-metapac/stm32f101rc" ] -stm32f101rd = [ "stm32-metapac/stm32f101rd" ] -stm32f101re = [ "stm32-metapac/stm32f101re" ] -stm32f101rf = [ "stm32-metapac/stm32f101rf" ] -stm32f101rg = [ "stm32-metapac/stm32f101rg" ] -stm32f101t4 = [ "stm32-metapac/stm32f101t4" ] -stm32f101t6 = [ "stm32-metapac/stm32f101t6" ] -stm32f101t8 = [ "stm32-metapac/stm32f101t8" ] -stm32f101tb = [ "stm32-metapac/stm32f101tb" ] -stm32f101v8 = [ "stm32-metapac/stm32f101v8" ] -stm32f101vb = [ "stm32-metapac/stm32f101vb" ] -stm32f101vc = [ "stm32-metapac/stm32f101vc" ] -stm32f101vd = [ "stm32-metapac/stm32f101vd" ] -stm32f101ve = [ "stm32-metapac/stm32f101ve" ] -stm32f101vf = [ "stm32-metapac/stm32f101vf" ] -stm32f101vg = [ "stm32-metapac/stm32f101vg" ] -stm32f101zc = [ "stm32-metapac/stm32f101zc" ] -stm32f101zd = [ "stm32-metapac/stm32f101zd" ] -stm32f101ze = [ "stm32-metapac/stm32f101ze" ] -stm32f101zf = [ "stm32-metapac/stm32f101zf" ] -stm32f101zg = [ "stm32-metapac/stm32f101zg" ] -stm32f102c4 = [ "stm32-metapac/stm32f102c4" ] -stm32f102c6 = [ "stm32-metapac/stm32f102c6" ] -stm32f102c8 = [ "stm32-metapac/stm32f102c8" ] -stm32f102cb = [ "stm32-metapac/stm32f102cb" ] -stm32f102r4 = [ "stm32-metapac/stm32f102r4" ] -stm32f102r6 = [ "stm32-metapac/stm32f102r6" ] -stm32f102r8 = [ "stm32-metapac/stm32f102r8" ] -stm32f102rb = [ "stm32-metapac/stm32f102rb" ] -stm32f103c4 = [ "stm32-metapac/stm32f103c4" ] -stm32f103c6 = [ "stm32-metapac/stm32f103c6" ] -stm32f103c8 = [ "stm32-metapac/stm32f103c8" ] -stm32f103cb = [ "stm32-metapac/stm32f103cb" ] -stm32f103r4 = [ "stm32-metapac/stm32f103r4" ] -stm32f103r6 = [ "stm32-metapac/stm32f103r6" ] -stm32f103r8 = [ "stm32-metapac/stm32f103r8" ] -stm32f103rb = [ "stm32-metapac/stm32f103rb" ] -stm32f103rc = [ "stm32-metapac/stm32f103rc" ] -stm32f103rd = [ "stm32-metapac/stm32f103rd" ] -stm32f103re = [ "stm32-metapac/stm32f103re" ] -stm32f103rf = [ "stm32-metapac/stm32f103rf" ] -stm32f103rg = [ "stm32-metapac/stm32f103rg" ] -stm32f103t4 = [ "stm32-metapac/stm32f103t4" ] -stm32f103t6 = [ "stm32-metapac/stm32f103t6" ] -stm32f103t8 = [ "stm32-metapac/stm32f103t8" ] -stm32f103tb = [ "stm32-metapac/stm32f103tb" ] -stm32f103v8 = [ "stm32-metapac/stm32f103v8" ] -stm32f103vb = [ "stm32-metapac/stm32f103vb" ] -stm32f103vc = [ "stm32-metapac/stm32f103vc" ] -stm32f103vd = [ "stm32-metapac/stm32f103vd" ] -stm32f103ve = [ "stm32-metapac/stm32f103ve" ] -stm32f103vf = [ "stm32-metapac/stm32f103vf" ] -stm32f103vg = [ "stm32-metapac/stm32f103vg" ] -stm32f103zc = [ "stm32-metapac/stm32f103zc" ] -stm32f103zd = [ "stm32-metapac/stm32f103zd" ] -stm32f103ze = [ "stm32-metapac/stm32f103ze" ] -stm32f103zf = [ "stm32-metapac/stm32f103zf" ] -stm32f103zg = [ "stm32-metapac/stm32f103zg" ] -stm32f105r8 = [ "stm32-metapac/stm32f105r8" ] -stm32f105rb = [ "stm32-metapac/stm32f105rb" ] -stm32f105rc = [ "stm32-metapac/stm32f105rc" ] -stm32f105v8 = [ "stm32-metapac/stm32f105v8" ] -stm32f105vb = [ "stm32-metapac/stm32f105vb" ] -stm32f105vc = [ "stm32-metapac/stm32f105vc" ] -stm32f107rb = [ "stm32-metapac/stm32f107rb" ] -stm32f107rc = [ "stm32-metapac/stm32f107rc" ] -stm32f107vb = [ "stm32-metapac/stm32f107vb" ] -stm32f107vc = [ "stm32-metapac/stm32f107vc" ] -stm32f205rb = [ "stm32-metapac/stm32f205rb" ] -stm32f205rc = [ "stm32-metapac/stm32f205rc" ] -stm32f205re = [ "stm32-metapac/stm32f205re" ] -stm32f205rf = [ "stm32-metapac/stm32f205rf" ] -stm32f205rg = [ "stm32-metapac/stm32f205rg" ] -stm32f205vb = [ "stm32-metapac/stm32f205vb" ] -stm32f205vc = [ "stm32-metapac/stm32f205vc" ] -stm32f205ve = [ "stm32-metapac/stm32f205ve" ] -stm32f205vf = [ "stm32-metapac/stm32f205vf" ] -stm32f205vg = [ "stm32-metapac/stm32f205vg" ] -stm32f205zc = [ "stm32-metapac/stm32f205zc" ] -stm32f205ze = [ "stm32-metapac/stm32f205ze" ] -stm32f205zf = [ "stm32-metapac/stm32f205zf" ] -stm32f205zg = [ "stm32-metapac/stm32f205zg" ] -stm32f207ic = [ "stm32-metapac/stm32f207ic" ] -stm32f207ie = [ "stm32-metapac/stm32f207ie" ] -stm32f207if = [ "stm32-metapac/stm32f207if" ] -stm32f207ig = [ "stm32-metapac/stm32f207ig" ] -stm32f207vc = [ "stm32-metapac/stm32f207vc" ] -stm32f207ve = [ "stm32-metapac/stm32f207ve" ] -stm32f207vf = [ "stm32-metapac/stm32f207vf" ] -stm32f207vg = [ "stm32-metapac/stm32f207vg" ] -stm32f207zc = [ "stm32-metapac/stm32f207zc" ] -stm32f207ze = [ "stm32-metapac/stm32f207ze" ] -stm32f207zf = [ "stm32-metapac/stm32f207zf" ] -stm32f207zg = [ "stm32-metapac/stm32f207zg" ] -stm32f215re = [ "stm32-metapac/stm32f215re" ] -stm32f215rg = [ "stm32-metapac/stm32f215rg" ] -stm32f215ve = [ "stm32-metapac/stm32f215ve" ] -stm32f215vg = [ "stm32-metapac/stm32f215vg" ] -stm32f215ze = [ "stm32-metapac/stm32f215ze" ] -stm32f215zg = [ "stm32-metapac/stm32f215zg" ] -stm32f217ie = [ "stm32-metapac/stm32f217ie" ] -stm32f217ig = [ "stm32-metapac/stm32f217ig" ] -stm32f217ve = [ "stm32-metapac/stm32f217ve" ] -stm32f217vg = [ "stm32-metapac/stm32f217vg" ] -stm32f217ze = [ "stm32-metapac/stm32f217ze" ] -stm32f217zg = [ "stm32-metapac/stm32f217zg" ] -stm32f301c6 = [ "stm32-metapac/stm32f301c6" ] -stm32f301c8 = [ "stm32-metapac/stm32f301c8" ] -stm32f301k6 = [ "stm32-metapac/stm32f301k6" ] -stm32f301k8 = [ "stm32-metapac/stm32f301k8" ] -stm32f301r6 = [ "stm32-metapac/stm32f301r6" ] -stm32f301r8 = [ "stm32-metapac/stm32f301r8" ] -stm32f302c6 = [ "stm32-metapac/stm32f302c6" ] -stm32f302c8 = [ "stm32-metapac/stm32f302c8" ] -stm32f302cb = [ "stm32-metapac/stm32f302cb" ] -stm32f302cc = [ "stm32-metapac/stm32f302cc" ] -stm32f302k6 = [ "stm32-metapac/stm32f302k6" ] -stm32f302k8 = [ "stm32-metapac/stm32f302k8" ] -stm32f302r6 = [ "stm32-metapac/stm32f302r6" ] -stm32f302r8 = [ "stm32-metapac/stm32f302r8" ] -stm32f302rb = [ "stm32-metapac/stm32f302rb" ] -stm32f302rc = [ "stm32-metapac/stm32f302rc" ] -stm32f302rd = [ "stm32-metapac/stm32f302rd" ] -stm32f302re = [ "stm32-metapac/stm32f302re" ] -stm32f302vb = [ "stm32-metapac/stm32f302vb" ] -stm32f302vc = [ "stm32-metapac/stm32f302vc" ] -stm32f302vd = [ "stm32-metapac/stm32f302vd" ] -stm32f302ve = [ "stm32-metapac/stm32f302ve" ] -stm32f302zd = [ "stm32-metapac/stm32f302zd" ] -stm32f302ze = [ "stm32-metapac/stm32f302ze" ] -stm32f303c6 = [ "stm32-metapac/stm32f303c6" ] -stm32f303c8 = [ "stm32-metapac/stm32f303c8" ] -stm32f303cb = [ "stm32-metapac/stm32f303cb" ] -stm32f303cc = [ "stm32-metapac/stm32f303cc" ] -stm32f303k6 = [ "stm32-metapac/stm32f303k6" ] -stm32f303k8 = [ "stm32-metapac/stm32f303k8" ] -stm32f303r6 = [ "stm32-metapac/stm32f303r6" ] -stm32f303r8 = [ "stm32-metapac/stm32f303r8" ] -stm32f303rb = [ "stm32-metapac/stm32f303rb" ] -stm32f303rc = [ "stm32-metapac/stm32f303rc" ] -stm32f303rd = [ "stm32-metapac/stm32f303rd" ] -stm32f303re = [ "stm32-metapac/stm32f303re" ] -stm32f303vb = [ "stm32-metapac/stm32f303vb" ] -stm32f303vc = [ "stm32-metapac/stm32f303vc" ] -stm32f303vd = [ "stm32-metapac/stm32f303vd" ] -stm32f303ve = [ "stm32-metapac/stm32f303ve" ] -stm32f303zd = [ "stm32-metapac/stm32f303zd" ] -stm32f303ze = [ "stm32-metapac/stm32f303ze" ] -stm32f318c8 = [ "stm32-metapac/stm32f318c8" ] -stm32f318k8 = [ "stm32-metapac/stm32f318k8" ] -stm32f328c8 = [ "stm32-metapac/stm32f328c8" ] -stm32f334c4 = [ "stm32-metapac/stm32f334c4" ] -stm32f334c6 = [ "stm32-metapac/stm32f334c6" ] -stm32f334c8 = [ "stm32-metapac/stm32f334c8" ] -stm32f334k4 = [ "stm32-metapac/stm32f334k4" ] -stm32f334k6 = [ "stm32-metapac/stm32f334k6" ] -stm32f334k8 = [ "stm32-metapac/stm32f334k8" ] -stm32f334r6 = [ "stm32-metapac/stm32f334r6" ] -stm32f334r8 = [ "stm32-metapac/stm32f334r8" ] -stm32f358cc = [ "stm32-metapac/stm32f358cc" ] -stm32f358rc = [ "stm32-metapac/stm32f358rc" ] -stm32f358vc = [ "stm32-metapac/stm32f358vc" ] -stm32f373c8 = [ "stm32-metapac/stm32f373c8" ] -stm32f373cb = [ "stm32-metapac/stm32f373cb" ] -stm32f373cc = [ "stm32-metapac/stm32f373cc" ] -stm32f373r8 = [ "stm32-metapac/stm32f373r8" ] -stm32f373rb = [ "stm32-metapac/stm32f373rb" ] -stm32f373rc = [ "stm32-metapac/stm32f373rc" ] -stm32f373v8 = [ "stm32-metapac/stm32f373v8" ] -stm32f373vb = [ "stm32-metapac/stm32f373vb" ] -stm32f373vc = [ "stm32-metapac/stm32f373vc" ] -stm32f378cc = [ "stm32-metapac/stm32f378cc" ] -stm32f378rc = [ "stm32-metapac/stm32f378rc" ] -stm32f378vc = [ "stm32-metapac/stm32f378vc" ] -stm32f398ve = [ "stm32-metapac/stm32f398ve" ] -stm32f401cb = [ "stm32-metapac/stm32f401cb" ] -stm32f401cc = [ "stm32-metapac/stm32f401cc" ] -stm32f401cd = [ "stm32-metapac/stm32f401cd" ] -stm32f401ce = [ "stm32-metapac/stm32f401ce" ] -stm32f401rb = [ "stm32-metapac/stm32f401rb" ] -stm32f401rc = [ "stm32-metapac/stm32f401rc" ] -stm32f401rd = [ "stm32-metapac/stm32f401rd" ] -stm32f401re = [ "stm32-metapac/stm32f401re" ] -stm32f401vb = [ "stm32-metapac/stm32f401vb" ] -stm32f401vc = [ "stm32-metapac/stm32f401vc" ] -stm32f401vd = [ "stm32-metapac/stm32f401vd" ] -stm32f401ve = [ "stm32-metapac/stm32f401ve" ] -stm32f405oe = [ "stm32-metapac/stm32f405oe" ] -stm32f405og = [ "stm32-metapac/stm32f405og" ] -stm32f405rg = [ "stm32-metapac/stm32f405rg" ] -stm32f405vg = [ "stm32-metapac/stm32f405vg" ] -stm32f405zg = [ "stm32-metapac/stm32f405zg" ] -stm32f407ie = [ "stm32-metapac/stm32f407ie" ] -stm32f407ig = [ "stm32-metapac/stm32f407ig" ] -stm32f407ve = [ "stm32-metapac/stm32f407ve" ] -stm32f407vg = [ "stm32-metapac/stm32f407vg" ] -stm32f407ze = [ "stm32-metapac/stm32f407ze" ] -stm32f407zg = [ "stm32-metapac/stm32f407zg" ] -stm32f410c8 = [ "stm32-metapac/stm32f410c8" ] -stm32f410cb = [ "stm32-metapac/stm32f410cb" ] -stm32f410r8 = [ "stm32-metapac/stm32f410r8" ] -stm32f410rb = [ "stm32-metapac/stm32f410rb" ] -stm32f410t8 = [ "stm32-metapac/stm32f410t8" ] -stm32f410tb = [ "stm32-metapac/stm32f410tb" ] -stm32f411cc = [ "stm32-metapac/stm32f411cc" ] -stm32f411ce = [ "stm32-metapac/stm32f411ce" ] -stm32f411rc = [ "stm32-metapac/stm32f411rc" ] -stm32f411re = [ "stm32-metapac/stm32f411re" ] -stm32f411vc = [ "stm32-metapac/stm32f411vc" ] -stm32f411ve = [ "stm32-metapac/stm32f411ve" ] -stm32f412ce = [ "stm32-metapac/stm32f412ce" ] -stm32f412cg = [ "stm32-metapac/stm32f412cg" ] -stm32f412re = [ "stm32-metapac/stm32f412re" ] -stm32f412rg = [ "stm32-metapac/stm32f412rg" ] -stm32f412ve = [ "stm32-metapac/stm32f412ve" ] -stm32f412vg = [ "stm32-metapac/stm32f412vg" ] -stm32f412ze = [ "stm32-metapac/stm32f412ze" ] -stm32f412zg = [ "stm32-metapac/stm32f412zg" ] -stm32f413cg = [ "stm32-metapac/stm32f413cg" ] -stm32f413ch = [ "stm32-metapac/stm32f413ch" ] -stm32f413mg = [ "stm32-metapac/stm32f413mg" ] -stm32f413mh = [ "stm32-metapac/stm32f413mh" ] -stm32f413rg = [ "stm32-metapac/stm32f413rg" ] -stm32f413rh = [ "stm32-metapac/stm32f413rh" ] -stm32f413vg = [ "stm32-metapac/stm32f413vg" ] -stm32f413vh = [ "stm32-metapac/stm32f413vh" ] -stm32f413zg = [ "stm32-metapac/stm32f413zg" ] -stm32f413zh = [ "stm32-metapac/stm32f413zh" ] -stm32f415og = [ "stm32-metapac/stm32f415og" ] -stm32f415rg = [ "stm32-metapac/stm32f415rg" ] -stm32f415vg = [ "stm32-metapac/stm32f415vg" ] -stm32f415zg = [ "stm32-metapac/stm32f415zg" ] -stm32f417ie = [ "stm32-metapac/stm32f417ie" ] -stm32f417ig = [ "stm32-metapac/stm32f417ig" ] -stm32f417ve = [ "stm32-metapac/stm32f417ve" ] -stm32f417vg = [ "stm32-metapac/stm32f417vg" ] -stm32f417ze = [ "stm32-metapac/stm32f417ze" ] -stm32f417zg = [ "stm32-metapac/stm32f417zg" ] -stm32f423ch = [ "stm32-metapac/stm32f423ch" ] -stm32f423mh = [ "stm32-metapac/stm32f423mh" ] -stm32f423rh = [ "stm32-metapac/stm32f423rh" ] -stm32f423vh = [ "stm32-metapac/stm32f423vh" ] -stm32f423zh = [ "stm32-metapac/stm32f423zh" ] -stm32f427ag = [ "stm32-metapac/stm32f427ag" ] -stm32f427ai = [ "stm32-metapac/stm32f427ai" ] -stm32f427ig = [ "stm32-metapac/stm32f427ig" ] -stm32f427ii = [ "stm32-metapac/stm32f427ii" ] -stm32f427vg = [ "stm32-metapac/stm32f427vg" ] -stm32f427vi = [ "stm32-metapac/stm32f427vi" ] -stm32f427zg = [ "stm32-metapac/stm32f427zg" ] -stm32f427zi = [ "stm32-metapac/stm32f427zi" ] -stm32f429ag = [ "stm32-metapac/stm32f429ag" ] -stm32f429ai = [ "stm32-metapac/stm32f429ai" ] -stm32f429be = [ "stm32-metapac/stm32f429be" ] -stm32f429bg = [ "stm32-metapac/stm32f429bg" ] -stm32f429bi = [ "stm32-metapac/stm32f429bi" ] -stm32f429ie = [ "stm32-metapac/stm32f429ie" ] -stm32f429ig = [ "stm32-metapac/stm32f429ig" ] -stm32f429ii = [ "stm32-metapac/stm32f429ii" ] -stm32f429ne = [ "stm32-metapac/stm32f429ne" ] -stm32f429ng = [ "stm32-metapac/stm32f429ng" ] -stm32f429ni = [ "stm32-metapac/stm32f429ni" ] -stm32f429ve = [ "stm32-metapac/stm32f429ve" ] -stm32f429vg = [ "stm32-metapac/stm32f429vg" ] -stm32f429vi = [ "stm32-metapac/stm32f429vi" ] -stm32f429ze = [ "stm32-metapac/stm32f429ze" ] -stm32f429zg = [ "stm32-metapac/stm32f429zg" ] -stm32f429zi = [ "stm32-metapac/stm32f429zi" ] -stm32f437ai = [ "stm32-metapac/stm32f437ai" ] -stm32f437ig = [ "stm32-metapac/stm32f437ig" ] -stm32f437ii = [ "stm32-metapac/stm32f437ii" ] -stm32f437vg = [ "stm32-metapac/stm32f437vg" ] -stm32f437vi = [ "stm32-metapac/stm32f437vi" ] -stm32f437zg = [ "stm32-metapac/stm32f437zg" ] -stm32f437zi = [ "stm32-metapac/stm32f437zi" ] -stm32f439ai = [ "stm32-metapac/stm32f439ai" ] -stm32f439bg = [ "stm32-metapac/stm32f439bg" ] -stm32f439bi = [ "stm32-metapac/stm32f439bi" ] -stm32f439ig = [ "stm32-metapac/stm32f439ig" ] -stm32f439ii = [ "stm32-metapac/stm32f439ii" ] -stm32f439ng = [ "stm32-metapac/stm32f439ng" ] -stm32f439ni = [ "stm32-metapac/stm32f439ni" ] -stm32f439vg = [ "stm32-metapac/stm32f439vg" ] -stm32f439vi = [ "stm32-metapac/stm32f439vi" ] -stm32f439zg = [ "stm32-metapac/stm32f439zg" ] -stm32f439zi = [ "stm32-metapac/stm32f439zi" ] -stm32f446mc = [ "stm32-metapac/stm32f446mc" ] -stm32f446me = [ "stm32-metapac/stm32f446me" ] -stm32f446rc = [ "stm32-metapac/stm32f446rc" ] -stm32f446re = [ "stm32-metapac/stm32f446re" ] -stm32f446vc = [ "stm32-metapac/stm32f446vc" ] -stm32f446ve = [ "stm32-metapac/stm32f446ve" ] -stm32f446zc = [ "stm32-metapac/stm32f446zc" ] -stm32f446ze = [ "stm32-metapac/stm32f446ze" ] -stm32f469ae = [ "stm32-metapac/stm32f469ae" ] -stm32f469ag = [ "stm32-metapac/stm32f469ag" ] -stm32f469ai = [ "stm32-metapac/stm32f469ai" ] -stm32f469be = [ "stm32-metapac/stm32f469be" ] -stm32f469bg = [ "stm32-metapac/stm32f469bg" ] -stm32f469bi = [ "stm32-metapac/stm32f469bi" ] -stm32f469ie = [ "stm32-metapac/stm32f469ie" ] -stm32f469ig = [ "stm32-metapac/stm32f469ig" ] -stm32f469ii = [ "stm32-metapac/stm32f469ii" ] -stm32f469ne = [ "stm32-metapac/stm32f469ne" ] -stm32f469ng = [ "stm32-metapac/stm32f469ng" ] -stm32f469ni = [ "stm32-metapac/stm32f469ni" ] -stm32f469ve = [ "stm32-metapac/stm32f469ve" ] -stm32f469vg = [ "stm32-metapac/stm32f469vg" ] -stm32f469vi = [ "stm32-metapac/stm32f469vi" ] -stm32f469ze = [ "stm32-metapac/stm32f469ze" ] -stm32f469zg = [ "stm32-metapac/stm32f469zg" ] -stm32f469zi = [ "stm32-metapac/stm32f469zi" ] -stm32f479ag = [ "stm32-metapac/stm32f479ag" ] -stm32f479ai = [ "stm32-metapac/stm32f479ai" ] -stm32f479bg = [ "stm32-metapac/stm32f479bg" ] -stm32f479bi = [ "stm32-metapac/stm32f479bi" ] -stm32f479ig = [ "stm32-metapac/stm32f479ig" ] -stm32f479ii = [ "stm32-metapac/stm32f479ii" ] -stm32f479ng = [ "stm32-metapac/stm32f479ng" ] -stm32f479ni = [ "stm32-metapac/stm32f479ni" ] -stm32f479vg = [ "stm32-metapac/stm32f479vg" ] -stm32f479vi = [ "stm32-metapac/stm32f479vi" ] -stm32f479zg = [ "stm32-metapac/stm32f479zg" ] -stm32f479zi = [ "stm32-metapac/stm32f479zi" ] -stm32f722ic = [ "stm32-metapac/stm32f722ic" ] -stm32f722ie = [ "stm32-metapac/stm32f722ie" ] -stm32f722rc = [ "stm32-metapac/stm32f722rc" ] -stm32f722re = [ "stm32-metapac/stm32f722re" ] -stm32f722vc = [ "stm32-metapac/stm32f722vc" ] -stm32f722ve = [ "stm32-metapac/stm32f722ve" ] -stm32f722zc = [ "stm32-metapac/stm32f722zc" ] -stm32f722ze = [ "stm32-metapac/stm32f722ze" ] -stm32f723ic = [ "stm32-metapac/stm32f723ic" ] -stm32f723ie = [ "stm32-metapac/stm32f723ie" ] -stm32f723vc = [ "stm32-metapac/stm32f723vc" ] -stm32f723ve = [ "stm32-metapac/stm32f723ve" ] -stm32f723zc = [ "stm32-metapac/stm32f723zc" ] -stm32f723ze = [ "stm32-metapac/stm32f723ze" ] -stm32f730i8 = [ "stm32-metapac/stm32f730i8" ] -stm32f730r8 = [ "stm32-metapac/stm32f730r8" ] -stm32f730v8 = [ "stm32-metapac/stm32f730v8" ] -stm32f730z8 = [ "stm32-metapac/stm32f730z8" ] -stm32f732ie = [ "stm32-metapac/stm32f732ie" ] -stm32f732re = [ "stm32-metapac/stm32f732re" ] -stm32f732ve = [ "stm32-metapac/stm32f732ve" ] -stm32f732ze = [ "stm32-metapac/stm32f732ze" ] -stm32f733ie = [ "stm32-metapac/stm32f733ie" ] -stm32f733ve = [ "stm32-metapac/stm32f733ve" ] -stm32f733ze = [ "stm32-metapac/stm32f733ze" ] -stm32f745ie = [ "stm32-metapac/stm32f745ie" ] -stm32f745ig = [ "stm32-metapac/stm32f745ig" ] -stm32f745ve = [ "stm32-metapac/stm32f745ve" ] -stm32f745vg = [ "stm32-metapac/stm32f745vg" ] -stm32f745ze = [ "stm32-metapac/stm32f745ze" ] -stm32f745zg = [ "stm32-metapac/stm32f745zg" ] -stm32f746be = [ "stm32-metapac/stm32f746be" ] -stm32f746bg = [ "stm32-metapac/stm32f746bg" ] -stm32f746ie = [ "stm32-metapac/stm32f746ie" ] -stm32f746ig = [ "stm32-metapac/stm32f746ig" ] -stm32f746ne = [ "stm32-metapac/stm32f746ne" ] -stm32f746ng = [ "stm32-metapac/stm32f746ng" ] -stm32f746ve = [ "stm32-metapac/stm32f746ve" ] -stm32f746vg = [ "stm32-metapac/stm32f746vg" ] -stm32f746ze = [ "stm32-metapac/stm32f746ze" ] -stm32f746zg = [ "stm32-metapac/stm32f746zg" ] -stm32f750n8 = [ "stm32-metapac/stm32f750n8" ] -stm32f750v8 = [ "stm32-metapac/stm32f750v8" ] -stm32f750z8 = [ "stm32-metapac/stm32f750z8" ] -stm32f756bg = [ "stm32-metapac/stm32f756bg" ] -stm32f756ig = [ "stm32-metapac/stm32f756ig" ] -stm32f756ng = [ "stm32-metapac/stm32f756ng" ] -stm32f756vg = [ "stm32-metapac/stm32f756vg" ] -stm32f756zg = [ "stm32-metapac/stm32f756zg" ] -stm32f765bg = [ "stm32-metapac/stm32f765bg" ] -stm32f765bi = [ "stm32-metapac/stm32f765bi" ] -stm32f765ig = [ "stm32-metapac/stm32f765ig" ] -stm32f765ii = [ "stm32-metapac/stm32f765ii" ] -stm32f765ng = [ "stm32-metapac/stm32f765ng" ] -stm32f765ni = [ "stm32-metapac/stm32f765ni" ] -stm32f765vg = [ "stm32-metapac/stm32f765vg" ] -stm32f765vi = [ "stm32-metapac/stm32f765vi" ] -stm32f765zg = [ "stm32-metapac/stm32f765zg" ] -stm32f765zi = [ "stm32-metapac/stm32f765zi" ] -stm32f767bg = [ "stm32-metapac/stm32f767bg" ] -stm32f767bi = [ "stm32-metapac/stm32f767bi" ] -stm32f767ig = [ "stm32-metapac/stm32f767ig" ] -stm32f767ii = [ "stm32-metapac/stm32f767ii" ] -stm32f767ng = [ "stm32-metapac/stm32f767ng" ] -stm32f767ni = [ "stm32-metapac/stm32f767ni" ] -stm32f767vg = [ "stm32-metapac/stm32f767vg" ] -stm32f767vi = [ "stm32-metapac/stm32f767vi" ] -stm32f767zg = [ "stm32-metapac/stm32f767zg" ] -stm32f767zi = [ "stm32-metapac/stm32f767zi" ] -stm32f768ai = [ "stm32-metapac/stm32f768ai" ] -stm32f769ag = [ "stm32-metapac/stm32f769ag" ] -stm32f769ai = [ "stm32-metapac/stm32f769ai" ] -stm32f769bg = [ "stm32-metapac/stm32f769bg" ] -stm32f769bi = [ "stm32-metapac/stm32f769bi" ] -stm32f769ig = [ "stm32-metapac/stm32f769ig" ] -stm32f769ii = [ "stm32-metapac/stm32f769ii" ] -stm32f769ng = [ "stm32-metapac/stm32f769ng" ] -stm32f769ni = [ "stm32-metapac/stm32f769ni" ] -stm32f777bi = [ "stm32-metapac/stm32f777bi" ] -stm32f777ii = [ "stm32-metapac/stm32f777ii" ] -stm32f777ni = [ "stm32-metapac/stm32f777ni" ] -stm32f777vi = [ "stm32-metapac/stm32f777vi" ] -stm32f777zi = [ "stm32-metapac/stm32f777zi" ] -stm32f778ai = [ "stm32-metapac/stm32f778ai" ] -stm32f779ai = [ "stm32-metapac/stm32f779ai" ] -stm32f779bi = [ "stm32-metapac/stm32f779bi" ] -stm32f779ii = [ "stm32-metapac/stm32f779ii" ] -stm32f779ni = [ "stm32-metapac/stm32f779ni" ] -stm32g030c6 = [ "stm32-metapac/stm32g030c6" ] -stm32g030c8 = [ "stm32-metapac/stm32g030c8" ] -stm32g030f6 = [ "stm32-metapac/stm32g030f6" ] -stm32g030j6 = [ "stm32-metapac/stm32g030j6" ] -stm32g030k6 = [ "stm32-metapac/stm32g030k6" ] -stm32g030k8 = [ "stm32-metapac/stm32g030k8" ] -stm32g031c4 = [ "stm32-metapac/stm32g031c4" ] -stm32g031c6 = [ "stm32-metapac/stm32g031c6" ] -stm32g031c8 = [ "stm32-metapac/stm32g031c8" ] -stm32g031f4 = [ "stm32-metapac/stm32g031f4" ] -stm32g031f6 = [ "stm32-metapac/stm32g031f6" ] -stm32g031f8 = [ "stm32-metapac/stm32g031f8" ] -stm32g031g4 = [ "stm32-metapac/stm32g031g4" ] -stm32g031g6 = [ "stm32-metapac/stm32g031g6" ] -stm32g031g8 = [ "stm32-metapac/stm32g031g8" ] -stm32g031j4 = [ "stm32-metapac/stm32g031j4" ] -stm32g031j6 = [ "stm32-metapac/stm32g031j6" ] -stm32g031k4 = [ "stm32-metapac/stm32g031k4" ] -stm32g031k6 = [ "stm32-metapac/stm32g031k6" ] -stm32g031k8 = [ "stm32-metapac/stm32g031k8" ] -stm32g031y8 = [ "stm32-metapac/stm32g031y8" ] -stm32g041c6 = [ "stm32-metapac/stm32g041c6" ] -stm32g041c8 = [ "stm32-metapac/stm32g041c8" ] -stm32g041f6 = [ "stm32-metapac/stm32g041f6" ] -stm32g041f8 = [ "stm32-metapac/stm32g041f8" ] -stm32g041g6 = [ "stm32-metapac/stm32g041g6" ] -stm32g041g8 = [ "stm32-metapac/stm32g041g8" ] -stm32g041j6 = [ "stm32-metapac/stm32g041j6" ] -stm32g041k6 = [ "stm32-metapac/stm32g041k6" ] -stm32g041k8 = [ "stm32-metapac/stm32g041k8" ] -stm32g041y8 = [ "stm32-metapac/stm32g041y8" ] -stm32g050c6 = [ "stm32-metapac/stm32g050c6" ] -stm32g050c8 = [ "stm32-metapac/stm32g050c8" ] -stm32g050f6 = [ "stm32-metapac/stm32g050f6" ] -stm32g050k6 = [ "stm32-metapac/stm32g050k6" ] -stm32g050k8 = [ "stm32-metapac/stm32g050k8" ] -stm32g051c6 = [ "stm32-metapac/stm32g051c6" ] -stm32g051c8 = [ "stm32-metapac/stm32g051c8" ] -stm32g051f6 = [ "stm32-metapac/stm32g051f6" ] -stm32g051f8 = [ "stm32-metapac/stm32g051f8" ] -stm32g051g6 = [ "stm32-metapac/stm32g051g6" ] -stm32g051g8 = [ "stm32-metapac/stm32g051g8" ] -stm32g051k6 = [ "stm32-metapac/stm32g051k6" ] -stm32g051k8 = [ "stm32-metapac/stm32g051k8" ] -stm32g061c6 = [ "stm32-metapac/stm32g061c6" ] -stm32g061c8 = [ "stm32-metapac/stm32g061c8" ] -stm32g061f6 = [ "stm32-metapac/stm32g061f6" ] -stm32g061f8 = [ "stm32-metapac/stm32g061f8" ] -stm32g061g6 = [ "stm32-metapac/stm32g061g6" ] -stm32g061g8 = [ "stm32-metapac/stm32g061g8" ] -stm32g061k6 = [ "stm32-metapac/stm32g061k6" ] -stm32g061k8 = [ "stm32-metapac/stm32g061k8" ] -stm32g070cb = [ "stm32-metapac/stm32g070cb" ] -stm32g070kb = [ "stm32-metapac/stm32g070kb" ] -stm32g070rb = [ "stm32-metapac/stm32g070rb" ] -stm32g071c6 = [ "stm32-metapac/stm32g071c6" ] -stm32g071c8 = [ "stm32-metapac/stm32g071c8" ] -stm32g071cb = [ "stm32-metapac/stm32g071cb" ] -stm32g071eb = [ "stm32-metapac/stm32g071eb" ] -stm32g071g6 = [ "stm32-metapac/stm32g071g6" ] -stm32g071g8 = [ "stm32-metapac/stm32g071g8" ] -stm32g071gb = [ "stm32-metapac/stm32g071gb" ] -stm32g071k6 = [ "stm32-metapac/stm32g071k6" ] -stm32g071k8 = [ "stm32-metapac/stm32g071k8" ] -stm32g071kb = [ "stm32-metapac/stm32g071kb" ] -stm32g071r6 = [ "stm32-metapac/stm32g071r6" ] -stm32g071r8 = [ "stm32-metapac/stm32g071r8" ] -stm32g071rb = [ "stm32-metapac/stm32g071rb" ] -stm32g081cb = [ "stm32-metapac/stm32g081cb" ] -stm32g081eb = [ "stm32-metapac/stm32g081eb" ] -stm32g081gb = [ "stm32-metapac/stm32g081gb" ] -stm32g081kb = [ "stm32-metapac/stm32g081kb" ] -stm32g081rb = [ "stm32-metapac/stm32g081rb" ] -stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce" ] -stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke" ] -stm32g0b0re = [ "stm32-metapac/stm32g0b0re" ] -stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve" ] -stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb" ] -stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc" ] -stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce" ] -stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb" ] -stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc" ] -stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke" ] -stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb" ] -stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc" ] -stm32g0b1me = [ "stm32-metapac/stm32g0b1me" ] -stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne" ] -stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb" ] -stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc" ] -stm32g0b1re = [ "stm32-metapac/stm32g0b1re" ] -stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb" ] -stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc" ] -stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve" ] -stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc" ] -stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce" ] -stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc" ] -stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke" ] -stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc" ] -stm32g0c1me = [ "stm32-metapac/stm32g0c1me" ] -stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne" ] -stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc" ] -stm32g0c1re = [ "stm32-metapac/stm32g0c1re" ] -stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc" ] -stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve" ] -stm32g431c6 = [ "stm32-metapac/stm32g431c6" ] -stm32g431c8 = [ "stm32-metapac/stm32g431c8" ] -stm32g431cb = [ "stm32-metapac/stm32g431cb" ] -stm32g431k6 = [ "stm32-metapac/stm32g431k6" ] -stm32g431k8 = [ "stm32-metapac/stm32g431k8" ] -stm32g431kb = [ "stm32-metapac/stm32g431kb" ] -stm32g431m6 = [ "stm32-metapac/stm32g431m6" ] -stm32g431m8 = [ "stm32-metapac/stm32g431m8" ] -stm32g431mb = [ "stm32-metapac/stm32g431mb" ] -stm32g431r6 = [ "stm32-metapac/stm32g431r6" ] -stm32g431r8 = [ "stm32-metapac/stm32g431r8" ] -stm32g431rb = [ "stm32-metapac/stm32g431rb" ] -stm32g431v6 = [ "stm32-metapac/stm32g431v6" ] -stm32g431v8 = [ "stm32-metapac/stm32g431v8" ] -stm32g431vb = [ "stm32-metapac/stm32g431vb" ] -stm32g441cb = [ "stm32-metapac/stm32g441cb" ] -stm32g441kb = [ "stm32-metapac/stm32g441kb" ] -stm32g441mb = [ "stm32-metapac/stm32g441mb" ] -stm32g441rb = [ "stm32-metapac/stm32g441rb" ] -stm32g441vb = [ "stm32-metapac/stm32g441vb" ] -stm32g471cc = [ "stm32-metapac/stm32g471cc" ] -stm32g471ce = [ "stm32-metapac/stm32g471ce" ] -stm32g471mc = [ "stm32-metapac/stm32g471mc" ] -stm32g471me = [ "stm32-metapac/stm32g471me" ] -stm32g471qc = [ "stm32-metapac/stm32g471qc" ] -stm32g471qe = [ "stm32-metapac/stm32g471qe" ] -stm32g471rc = [ "stm32-metapac/stm32g471rc" ] -stm32g471re = [ "stm32-metapac/stm32g471re" ] -stm32g471vc = [ "stm32-metapac/stm32g471vc" ] -stm32g471ve = [ "stm32-metapac/stm32g471ve" ] -stm32g473cb = [ "stm32-metapac/stm32g473cb" ] -stm32g473cc = [ "stm32-metapac/stm32g473cc" ] -stm32g473ce = [ "stm32-metapac/stm32g473ce" ] -stm32g473mb = [ "stm32-metapac/stm32g473mb" ] -stm32g473mc = [ "stm32-metapac/stm32g473mc" ] -stm32g473me = [ "stm32-metapac/stm32g473me" ] -stm32g473pb = [ "stm32-metapac/stm32g473pb" ] -stm32g473pc = [ "stm32-metapac/stm32g473pc" ] -stm32g473pe = [ "stm32-metapac/stm32g473pe" ] -stm32g473qb = [ "stm32-metapac/stm32g473qb" ] -stm32g473qc = [ "stm32-metapac/stm32g473qc" ] -stm32g473qe = [ "stm32-metapac/stm32g473qe" ] -stm32g473rb = [ "stm32-metapac/stm32g473rb" ] -stm32g473rc = [ "stm32-metapac/stm32g473rc" ] -stm32g473re = [ "stm32-metapac/stm32g473re" ] -stm32g473vb = [ "stm32-metapac/stm32g473vb" ] -stm32g473vc = [ "stm32-metapac/stm32g473vc" ] -stm32g473ve = [ "stm32-metapac/stm32g473ve" ] -stm32g474cb = [ "stm32-metapac/stm32g474cb" ] -stm32g474cc = [ "stm32-metapac/stm32g474cc" ] -stm32g474ce = [ "stm32-metapac/stm32g474ce" ] -stm32g474mb = [ "stm32-metapac/stm32g474mb" ] -stm32g474mc = [ "stm32-metapac/stm32g474mc" ] -stm32g474me = [ "stm32-metapac/stm32g474me" ] -stm32g474pb = [ "stm32-metapac/stm32g474pb" ] -stm32g474pc = [ "stm32-metapac/stm32g474pc" ] -stm32g474pe = [ "stm32-metapac/stm32g474pe" ] -stm32g474qb = [ "stm32-metapac/stm32g474qb" ] -stm32g474qc = [ "stm32-metapac/stm32g474qc" ] -stm32g474qe = [ "stm32-metapac/stm32g474qe" ] -stm32g474rb = [ "stm32-metapac/stm32g474rb" ] -stm32g474rc = [ "stm32-metapac/stm32g474rc" ] -stm32g474re = [ "stm32-metapac/stm32g474re" ] -stm32g474vb = [ "stm32-metapac/stm32g474vb" ] -stm32g474vc = [ "stm32-metapac/stm32g474vc" ] -stm32g474ve = [ "stm32-metapac/stm32g474ve" ] -stm32g483ce = [ "stm32-metapac/stm32g483ce" ] -stm32g483me = [ "stm32-metapac/stm32g483me" ] -stm32g483pe = [ "stm32-metapac/stm32g483pe" ] -stm32g483qe = [ "stm32-metapac/stm32g483qe" ] -stm32g483re = [ "stm32-metapac/stm32g483re" ] -stm32g483ve = [ "stm32-metapac/stm32g483ve" ] -stm32g484ce = [ "stm32-metapac/stm32g484ce" ] -stm32g484me = [ "stm32-metapac/stm32g484me" ] -stm32g484pe = [ "stm32-metapac/stm32g484pe" ] -stm32g484qe = [ "stm32-metapac/stm32g484qe" ] -stm32g484re = [ "stm32-metapac/stm32g484re" ] -stm32g484ve = [ "stm32-metapac/stm32g484ve" ] -stm32g491cc = [ "stm32-metapac/stm32g491cc" ] -stm32g491ce = [ "stm32-metapac/stm32g491ce" ] -stm32g491kc = [ "stm32-metapac/stm32g491kc" ] -stm32g491ke = [ "stm32-metapac/stm32g491ke" ] -stm32g491mc = [ "stm32-metapac/stm32g491mc" ] -stm32g491me = [ "stm32-metapac/stm32g491me" ] -stm32g491rc = [ "stm32-metapac/stm32g491rc" ] -stm32g491re = [ "stm32-metapac/stm32g491re" ] -stm32g491vc = [ "stm32-metapac/stm32g491vc" ] -stm32g491ve = [ "stm32-metapac/stm32g491ve" ] -stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce" ] -stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] -stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] -stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] -stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] -stm32h503cb = [ "stm32-metapac/stm32h503cb" ] -stm32h503eb = [ "stm32-metapac/stm32h503eb" ] -stm32h503kb = [ "stm32-metapac/stm32h503kb" ] -stm32h503rb = [ "stm32-metapac/stm32h503rb" ] -stm32h562ag = [ "stm32-metapac/stm32h562ag" ] -stm32h562ai = [ "stm32-metapac/stm32h562ai" ] -stm32h562ig = [ "stm32-metapac/stm32h562ig" ] -stm32h562ii = [ "stm32-metapac/stm32h562ii" ] -stm32h562rg = [ "stm32-metapac/stm32h562rg" ] -stm32h562ri = [ "stm32-metapac/stm32h562ri" ] -stm32h562vg = [ "stm32-metapac/stm32h562vg" ] -stm32h562vi = [ "stm32-metapac/stm32h562vi" ] -stm32h562zg = [ "stm32-metapac/stm32h562zg" ] -stm32h562zi = [ "stm32-metapac/stm32h562zi" ] -stm32h563ag = [ "stm32-metapac/stm32h563ag" ] -stm32h563ai = [ "stm32-metapac/stm32h563ai" ] -stm32h563ig = [ "stm32-metapac/stm32h563ig" ] -stm32h563ii = [ "stm32-metapac/stm32h563ii" ] -stm32h563mi = [ "stm32-metapac/stm32h563mi" ] -stm32h563rg = [ "stm32-metapac/stm32h563rg" ] -stm32h563ri = [ "stm32-metapac/stm32h563ri" ] -stm32h563vg = [ "stm32-metapac/stm32h563vg" ] -stm32h563vi = [ "stm32-metapac/stm32h563vi" ] -stm32h563zg = [ "stm32-metapac/stm32h563zg" ] -stm32h563zi = [ "stm32-metapac/stm32h563zi" ] -stm32h573ai = [ "stm32-metapac/stm32h573ai" ] -stm32h573ii = [ "stm32-metapac/stm32h573ii" ] -stm32h573mi = [ "stm32-metapac/stm32h573mi" ] -stm32h573ri = [ "stm32-metapac/stm32h573ri" ] -stm32h573vi = [ "stm32-metapac/stm32h573vi" ] -stm32h573zi = [ "stm32-metapac/stm32h573zi" ] -stm32h723ve = [ "stm32-metapac/stm32h723ve" ] -stm32h723vg = [ "stm32-metapac/stm32h723vg" ] -stm32h723ze = [ "stm32-metapac/stm32h723ze" ] -stm32h723zg = [ "stm32-metapac/stm32h723zg" ] -stm32h725ae = [ "stm32-metapac/stm32h725ae" ] -stm32h725ag = [ "stm32-metapac/stm32h725ag" ] -stm32h725ie = [ "stm32-metapac/stm32h725ie" ] -stm32h725ig = [ "stm32-metapac/stm32h725ig" ] -stm32h725re = [ "stm32-metapac/stm32h725re" ] -stm32h725rg = [ "stm32-metapac/stm32h725rg" ] -stm32h725ve = [ "stm32-metapac/stm32h725ve" ] -stm32h725vg = [ "stm32-metapac/stm32h725vg" ] -stm32h725ze = [ "stm32-metapac/stm32h725ze" ] -stm32h725zg = [ "stm32-metapac/stm32h725zg" ] -stm32h730ab = [ "stm32-metapac/stm32h730ab" ] -stm32h730ib = [ "stm32-metapac/stm32h730ib" ] -stm32h730vb = [ "stm32-metapac/stm32h730vb" ] -stm32h730zb = [ "stm32-metapac/stm32h730zb" ] -stm32h733vg = [ "stm32-metapac/stm32h733vg" ] -stm32h733zg = [ "stm32-metapac/stm32h733zg" ] -stm32h735ag = [ "stm32-metapac/stm32h735ag" ] -stm32h735ig = [ "stm32-metapac/stm32h735ig" ] -stm32h735rg = [ "stm32-metapac/stm32h735rg" ] -stm32h735vg = [ "stm32-metapac/stm32h735vg" ] -stm32h735zg = [ "stm32-metapac/stm32h735zg" ] -stm32h742ag = [ "stm32-metapac/stm32h742ag" ] -stm32h742ai = [ "stm32-metapac/stm32h742ai" ] -stm32h742bg = [ "stm32-metapac/stm32h742bg" ] -stm32h742bi = [ "stm32-metapac/stm32h742bi" ] -stm32h742ig = [ "stm32-metapac/stm32h742ig" ] -stm32h742ii = [ "stm32-metapac/stm32h742ii" ] -stm32h742vg = [ "stm32-metapac/stm32h742vg" ] -stm32h742vi = [ "stm32-metapac/stm32h742vi" ] -stm32h742xg = [ "stm32-metapac/stm32h742xg" ] -stm32h742xi = [ "stm32-metapac/stm32h742xi" ] -stm32h742zg = [ "stm32-metapac/stm32h742zg" ] -stm32h742zi = [ "stm32-metapac/stm32h742zi" ] -stm32h743ag = [ "stm32-metapac/stm32h743ag" ] -stm32h743ai = [ "stm32-metapac/stm32h743ai" ] -stm32h743bg = [ "stm32-metapac/stm32h743bg" ] -stm32h743bi = [ "stm32-metapac/stm32h743bi" ] -stm32h743ig = [ "stm32-metapac/stm32h743ig" ] -stm32h743ii = [ "stm32-metapac/stm32h743ii" ] -stm32h743vg = [ "stm32-metapac/stm32h743vg" ] -stm32h743vi = [ "stm32-metapac/stm32h743vi" ] -stm32h743xg = [ "stm32-metapac/stm32h743xg" ] -stm32h743xi = [ "stm32-metapac/stm32h743xi" ] -stm32h743zg = [ "stm32-metapac/stm32h743zg" ] -stm32h743zi = [ "stm32-metapac/stm32h743zi" ] -stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7" ] -stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4" ] -stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7" ] -stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4" ] -stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7" ] -stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4" ] -stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7" ] -stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4" ] -stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7" ] -stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4" ] -stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7" ] -stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4" ] -stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7" ] -stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4" ] -stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7" ] -stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4" ] -stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7" ] -stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4" ] -stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7" ] -stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4" ] -stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7" ] -stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4" ] -stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7" ] -stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4" ] -stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7" ] -stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4" ] -stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7" ] -stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4" ] -stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7" ] -stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4" ] -stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7" ] -stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4" ] -stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7" ] -stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4" ] -stm32h750ib = [ "stm32-metapac/stm32h750ib" ] -stm32h750vb = [ "stm32-metapac/stm32h750vb" ] -stm32h750xb = [ "stm32-metapac/stm32h750xb" ] -stm32h750zb = [ "stm32-metapac/stm32h750zb" ] -stm32h753ai = [ "stm32-metapac/stm32h753ai" ] -stm32h753bi = [ "stm32-metapac/stm32h753bi" ] -stm32h753ii = [ "stm32-metapac/stm32h753ii" ] -stm32h753vi = [ "stm32-metapac/stm32h753vi" ] -stm32h753xi = [ "stm32-metapac/stm32h753xi" ] -stm32h753zi = [ "stm32-metapac/stm32h753zi" ] -stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7" ] -stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4" ] -stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7" ] -stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4" ] -stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7" ] -stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4" ] -stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7" ] -stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4" ] -stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7" ] -stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4" ] -stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7" ] -stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4" ] -stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7" ] -stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4" ] -stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7" ] -stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4" ] -stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7" ] -stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4" ] -stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] -stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] -stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] -stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii" ] -stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg" ] -stm32h7a3li = [ "stm32-metapac/stm32h7a3li" ] -stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng" ] -stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni" ] -stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi" ] -stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg" ] -stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri" ] -stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg" ] -stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi" ] -stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg" ] -stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi" ] -stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab" ] -stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib" ] -stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb" ] -stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb" ] -stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb" ] -stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai" ] -stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii" ] -stm32h7b3li = [ "stm32-metapac/stm32h7b3li" ] -stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni" ] -stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi" ] -stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri" ] -stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi" ] -stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi" ] -stm32l010c6 = [ "stm32-metapac/stm32l010c6" ] -stm32l010f4 = [ "stm32-metapac/stm32l010f4" ] -stm32l010k4 = [ "stm32-metapac/stm32l010k4" ] -stm32l010k8 = [ "stm32-metapac/stm32l010k8" ] -stm32l010r8 = [ "stm32-metapac/stm32l010r8" ] -stm32l010rb = [ "stm32-metapac/stm32l010rb" ] -stm32l011d3 = [ "stm32-metapac/stm32l011d3" ] -stm32l011d4 = [ "stm32-metapac/stm32l011d4" ] -stm32l011e3 = [ "stm32-metapac/stm32l011e3" ] -stm32l011e4 = [ "stm32-metapac/stm32l011e4" ] -stm32l011f3 = [ "stm32-metapac/stm32l011f3" ] -stm32l011f4 = [ "stm32-metapac/stm32l011f4" ] -stm32l011g3 = [ "stm32-metapac/stm32l011g3" ] -stm32l011g4 = [ "stm32-metapac/stm32l011g4" ] -stm32l011k3 = [ "stm32-metapac/stm32l011k3" ] -stm32l011k4 = [ "stm32-metapac/stm32l011k4" ] -stm32l021d4 = [ "stm32-metapac/stm32l021d4" ] -stm32l021f4 = [ "stm32-metapac/stm32l021f4" ] -stm32l021g4 = [ "stm32-metapac/stm32l021g4" ] -stm32l021k4 = [ "stm32-metapac/stm32l021k4" ] -stm32l031c4 = [ "stm32-metapac/stm32l031c4" ] -stm32l031c6 = [ "stm32-metapac/stm32l031c6" ] -stm32l031e4 = [ "stm32-metapac/stm32l031e4" ] -stm32l031e6 = [ "stm32-metapac/stm32l031e6" ] -stm32l031f4 = [ "stm32-metapac/stm32l031f4" ] -stm32l031f6 = [ "stm32-metapac/stm32l031f6" ] -stm32l031g4 = [ "stm32-metapac/stm32l031g4" ] -stm32l031g6 = [ "stm32-metapac/stm32l031g6" ] -stm32l031k4 = [ "stm32-metapac/stm32l031k4" ] -stm32l031k6 = [ "stm32-metapac/stm32l031k6" ] -stm32l041c4 = [ "stm32-metapac/stm32l041c4" ] -stm32l041c6 = [ "stm32-metapac/stm32l041c6" ] -stm32l041e6 = [ "stm32-metapac/stm32l041e6" ] -stm32l041f6 = [ "stm32-metapac/stm32l041f6" ] -stm32l041g6 = [ "stm32-metapac/stm32l041g6" ] -stm32l041k6 = [ "stm32-metapac/stm32l041k6" ] -stm32l051c6 = [ "stm32-metapac/stm32l051c6" ] -stm32l051c8 = [ "stm32-metapac/stm32l051c8" ] -stm32l051k6 = [ "stm32-metapac/stm32l051k6" ] -stm32l051k8 = [ "stm32-metapac/stm32l051k8" ] -stm32l051r6 = [ "stm32-metapac/stm32l051r6" ] -stm32l051r8 = [ "stm32-metapac/stm32l051r8" ] -stm32l051t6 = [ "stm32-metapac/stm32l051t6" ] -stm32l051t8 = [ "stm32-metapac/stm32l051t8" ] -stm32l052c6 = [ "stm32-metapac/stm32l052c6" ] -stm32l052c8 = [ "stm32-metapac/stm32l052c8" ] -stm32l052k6 = [ "stm32-metapac/stm32l052k6" ] -stm32l052k8 = [ "stm32-metapac/stm32l052k8" ] -stm32l052r6 = [ "stm32-metapac/stm32l052r6" ] -stm32l052r8 = [ "stm32-metapac/stm32l052r8" ] -stm32l052t6 = [ "stm32-metapac/stm32l052t6" ] -stm32l052t8 = [ "stm32-metapac/stm32l052t8" ] -stm32l053c6 = [ "stm32-metapac/stm32l053c6" ] -stm32l053c8 = [ "stm32-metapac/stm32l053c8" ] -stm32l053r6 = [ "stm32-metapac/stm32l053r6" ] -stm32l053r8 = [ "stm32-metapac/stm32l053r8" ] -stm32l062c8 = [ "stm32-metapac/stm32l062c8" ] -stm32l062k8 = [ "stm32-metapac/stm32l062k8" ] -stm32l063c8 = [ "stm32-metapac/stm32l063c8" ] -stm32l063r8 = [ "stm32-metapac/stm32l063r8" ] -stm32l071c8 = [ "stm32-metapac/stm32l071c8" ] -stm32l071cb = [ "stm32-metapac/stm32l071cb" ] -stm32l071cz = [ "stm32-metapac/stm32l071cz" ] -stm32l071k8 = [ "stm32-metapac/stm32l071k8" ] -stm32l071kb = [ "stm32-metapac/stm32l071kb" ] -stm32l071kz = [ "stm32-metapac/stm32l071kz" ] -stm32l071rb = [ "stm32-metapac/stm32l071rb" ] -stm32l071rz = [ "stm32-metapac/stm32l071rz" ] -stm32l071v8 = [ "stm32-metapac/stm32l071v8" ] -stm32l071vb = [ "stm32-metapac/stm32l071vb" ] -stm32l071vz = [ "stm32-metapac/stm32l071vz" ] -stm32l072cb = [ "stm32-metapac/stm32l072cb" ] -stm32l072cz = [ "stm32-metapac/stm32l072cz" ] -stm32l072kb = [ "stm32-metapac/stm32l072kb" ] -stm32l072kz = [ "stm32-metapac/stm32l072kz" ] -stm32l072rb = [ "stm32-metapac/stm32l072rb" ] -stm32l072rz = [ "stm32-metapac/stm32l072rz" ] -stm32l072v8 = [ "stm32-metapac/stm32l072v8" ] -stm32l072vb = [ "stm32-metapac/stm32l072vb" ] -stm32l072vz = [ "stm32-metapac/stm32l072vz" ] -stm32l073cb = [ "stm32-metapac/stm32l073cb" ] -stm32l073cz = [ "stm32-metapac/stm32l073cz" ] -stm32l073rb = [ "stm32-metapac/stm32l073rb" ] -stm32l073rz = [ "stm32-metapac/stm32l073rz" ] -stm32l073v8 = [ "stm32-metapac/stm32l073v8" ] -stm32l073vb = [ "stm32-metapac/stm32l073vb" ] -stm32l073vz = [ "stm32-metapac/stm32l073vz" ] -stm32l081cb = [ "stm32-metapac/stm32l081cb" ] -stm32l081cz = [ "stm32-metapac/stm32l081cz" ] -stm32l081kz = [ "stm32-metapac/stm32l081kz" ] -stm32l082cz = [ "stm32-metapac/stm32l082cz" ] -stm32l082kb = [ "stm32-metapac/stm32l082kb" ] -stm32l082kz = [ "stm32-metapac/stm32l082kz" ] -stm32l083cb = [ "stm32-metapac/stm32l083cb" ] -stm32l083cz = [ "stm32-metapac/stm32l083cz" ] -stm32l083rb = [ "stm32-metapac/stm32l083rb" ] -stm32l083rz = [ "stm32-metapac/stm32l083rz" ] -stm32l083v8 = [ "stm32-metapac/stm32l083v8" ] -stm32l083vb = [ "stm32-metapac/stm32l083vb" ] -stm32l083vz = [ "stm32-metapac/stm32l083vz" ] -stm32l100c6 = [ "stm32-metapac/stm32l100c6" ] -stm32l100c6-a = [ "stm32-metapac/stm32l100c6-a" ] -stm32l100r8 = [ "stm32-metapac/stm32l100r8" ] -stm32l100r8-a = [ "stm32-metapac/stm32l100r8-a" ] -stm32l100rb = [ "stm32-metapac/stm32l100rb" ] -stm32l100rb-a = [ "stm32-metapac/stm32l100rb-a" ] -stm32l100rc = [ "stm32-metapac/stm32l100rc" ] -stm32l151c6 = [ "stm32-metapac/stm32l151c6" ] -stm32l151c6-a = [ "stm32-metapac/stm32l151c6-a" ] -stm32l151c8 = [ "stm32-metapac/stm32l151c8" ] -stm32l151c8-a = [ "stm32-metapac/stm32l151c8-a" ] -stm32l151cb = [ "stm32-metapac/stm32l151cb" ] -stm32l151cb-a = [ "stm32-metapac/stm32l151cb-a" ] -stm32l151cc = [ "stm32-metapac/stm32l151cc" ] -stm32l151qc = [ "stm32-metapac/stm32l151qc" ] -stm32l151qd = [ "stm32-metapac/stm32l151qd" ] -stm32l151qe = [ "stm32-metapac/stm32l151qe" ] -stm32l151r6 = [ "stm32-metapac/stm32l151r6" ] -stm32l151r6-a = [ "stm32-metapac/stm32l151r6-a" ] -stm32l151r8 = [ "stm32-metapac/stm32l151r8" ] -stm32l151r8-a = [ "stm32-metapac/stm32l151r8-a" ] -stm32l151rb = [ "stm32-metapac/stm32l151rb" ] -stm32l151rb-a = [ "stm32-metapac/stm32l151rb-a" ] -stm32l151rc = [ "stm32-metapac/stm32l151rc" ] -stm32l151rc-a = [ "stm32-metapac/stm32l151rc-a" ] -stm32l151rd = [ "stm32-metapac/stm32l151rd" ] -stm32l151re = [ "stm32-metapac/stm32l151re" ] -stm32l151uc = [ "stm32-metapac/stm32l151uc" ] -stm32l151v8 = [ "stm32-metapac/stm32l151v8" ] -stm32l151v8-a = [ "stm32-metapac/stm32l151v8-a" ] -stm32l151vb = [ "stm32-metapac/stm32l151vb" ] -stm32l151vb-a = [ "stm32-metapac/stm32l151vb-a" ] -stm32l151vc = [ "stm32-metapac/stm32l151vc" ] -stm32l151vc-a = [ "stm32-metapac/stm32l151vc-a" ] -stm32l151vd = [ "stm32-metapac/stm32l151vd" ] -stm32l151vd-x = [ "stm32-metapac/stm32l151vd-x" ] -stm32l151ve = [ "stm32-metapac/stm32l151ve" ] -stm32l151zc = [ "stm32-metapac/stm32l151zc" ] -stm32l151zd = [ "stm32-metapac/stm32l151zd" ] -stm32l151ze = [ "stm32-metapac/stm32l151ze" ] -stm32l152c6 = [ "stm32-metapac/stm32l152c6" ] -stm32l152c6-a = [ "stm32-metapac/stm32l152c6-a" ] -stm32l152c8 = [ "stm32-metapac/stm32l152c8" ] -stm32l152c8-a = [ "stm32-metapac/stm32l152c8-a" ] -stm32l152cb = [ "stm32-metapac/stm32l152cb" ] -stm32l152cb-a = [ "stm32-metapac/stm32l152cb-a" ] -stm32l152cc = [ "stm32-metapac/stm32l152cc" ] -stm32l152qc = [ "stm32-metapac/stm32l152qc" ] -stm32l152qd = [ "stm32-metapac/stm32l152qd" ] -stm32l152qe = [ "stm32-metapac/stm32l152qe" ] -stm32l152r6 = [ "stm32-metapac/stm32l152r6" ] -stm32l152r6-a = [ "stm32-metapac/stm32l152r6-a" ] -stm32l152r8 = [ "stm32-metapac/stm32l152r8" ] -stm32l152r8-a = [ "stm32-metapac/stm32l152r8-a" ] -stm32l152rb = [ "stm32-metapac/stm32l152rb" ] -stm32l152rb-a = [ "stm32-metapac/stm32l152rb-a" ] -stm32l152rc = [ "stm32-metapac/stm32l152rc" ] -stm32l152rc-a = [ "stm32-metapac/stm32l152rc-a" ] -stm32l152rd = [ "stm32-metapac/stm32l152rd" ] -stm32l152re = [ "stm32-metapac/stm32l152re" ] -stm32l152uc = [ "stm32-metapac/stm32l152uc" ] -stm32l152v8 = [ "stm32-metapac/stm32l152v8" ] -stm32l152v8-a = [ "stm32-metapac/stm32l152v8-a" ] -stm32l152vb = [ "stm32-metapac/stm32l152vb" ] -stm32l152vb-a = [ "stm32-metapac/stm32l152vb-a" ] -stm32l152vc = [ "stm32-metapac/stm32l152vc" ] -stm32l152vc-a = [ "stm32-metapac/stm32l152vc-a" ] -stm32l152vd = [ "stm32-metapac/stm32l152vd" ] -stm32l152vd-x = [ "stm32-metapac/stm32l152vd-x" ] -stm32l152ve = [ "stm32-metapac/stm32l152ve" ] -stm32l152zc = [ "stm32-metapac/stm32l152zc" ] -stm32l152zd = [ "stm32-metapac/stm32l152zd" ] -stm32l152ze = [ "stm32-metapac/stm32l152ze" ] -stm32l162qc = [ "stm32-metapac/stm32l162qc" ] -stm32l162qd = [ "stm32-metapac/stm32l162qd" ] -stm32l162rc = [ "stm32-metapac/stm32l162rc" ] -stm32l162rc-a = [ "stm32-metapac/stm32l162rc-a" ] -stm32l162rd = [ "stm32-metapac/stm32l162rd" ] -stm32l162re = [ "stm32-metapac/stm32l162re" ] -stm32l162vc = [ "stm32-metapac/stm32l162vc" ] -stm32l162vc-a = [ "stm32-metapac/stm32l162vc-a" ] -stm32l162vd = [ "stm32-metapac/stm32l162vd" ] -stm32l162vd-x = [ "stm32-metapac/stm32l162vd-x" ] -stm32l162ve = [ "stm32-metapac/stm32l162ve" ] -stm32l162zc = [ "stm32-metapac/stm32l162zc" ] -stm32l162zd = [ "stm32-metapac/stm32l162zd" ] -stm32l162ze = [ "stm32-metapac/stm32l162ze" ] -stm32l412c8 = [ "stm32-metapac/stm32l412c8" ] -stm32l412cb = [ "stm32-metapac/stm32l412cb" ] -stm32l412k8 = [ "stm32-metapac/stm32l412k8" ] -stm32l412kb = [ "stm32-metapac/stm32l412kb" ] -stm32l412r8 = [ "stm32-metapac/stm32l412r8" ] -stm32l412rb = [ "stm32-metapac/stm32l412rb" ] -stm32l412t8 = [ "stm32-metapac/stm32l412t8" ] -stm32l412tb = [ "stm32-metapac/stm32l412tb" ] -stm32l422cb = [ "stm32-metapac/stm32l422cb" ] -stm32l422kb = [ "stm32-metapac/stm32l422kb" ] -stm32l422rb = [ "stm32-metapac/stm32l422rb" ] -stm32l422tb = [ "stm32-metapac/stm32l422tb" ] -stm32l431cb = [ "stm32-metapac/stm32l431cb" ] -stm32l431cc = [ "stm32-metapac/stm32l431cc" ] -stm32l431kb = [ "stm32-metapac/stm32l431kb" ] -stm32l431kc = [ "stm32-metapac/stm32l431kc" ] -stm32l431rb = [ "stm32-metapac/stm32l431rb" ] -stm32l431rc = [ "stm32-metapac/stm32l431rc" ] -stm32l431vc = [ "stm32-metapac/stm32l431vc" ] -stm32l432kb = [ "stm32-metapac/stm32l432kb" ] -stm32l432kc = [ "stm32-metapac/stm32l432kc" ] -stm32l433cb = [ "stm32-metapac/stm32l433cb" ] -stm32l433cc = [ "stm32-metapac/stm32l433cc" ] -stm32l433rb = [ "stm32-metapac/stm32l433rb" ] -stm32l433rc = [ "stm32-metapac/stm32l433rc" ] -stm32l433vc = [ "stm32-metapac/stm32l433vc" ] -stm32l442kc = [ "stm32-metapac/stm32l442kc" ] -stm32l443cc = [ "stm32-metapac/stm32l443cc" ] -stm32l443rc = [ "stm32-metapac/stm32l443rc" ] -stm32l443vc = [ "stm32-metapac/stm32l443vc" ] -stm32l451cc = [ "stm32-metapac/stm32l451cc" ] -stm32l451ce = [ "stm32-metapac/stm32l451ce" ] -stm32l451rc = [ "stm32-metapac/stm32l451rc" ] -stm32l451re = [ "stm32-metapac/stm32l451re" ] -stm32l451vc = [ "stm32-metapac/stm32l451vc" ] -stm32l451ve = [ "stm32-metapac/stm32l451ve" ] -stm32l452cc = [ "stm32-metapac/stm32l452cc" ] -stm32l452ce = [ "stm32-metapac/stm32l452ce" ] -stm32l452rc = [ "stm32-metapac/stm32l452rc" ] -stm32l452re = [ "stm32-metapac/stm32l452re" ] -stm32l452vc = [ "stm32-metapac/stm32l452vc" ] -stm32l452ve = [ "stm32-metapac/stm32l452ve" ] -stm32l462ce = [ "stm32-metapac/stm32l462ce" ] -stm32l462re = [ "stm32-metapac/stm32l462re" ] -stm32l462ve = [ "stm32-metapac/stm32l462ve" ] -stm32l471qe = [ "stm32-metapac/stm32l471qe" ] -stm32l471qg = [ "stm32-metapac/stm32l471qg" ] -stm32l471re = [ "stm32-metapac/stm32l471re" ] -stm32l471rg = [ "stm32-metapac/stm32l471rg" ] -stm32l471ve = [ "stm32-metapac/stm32l471ve" ] -stm32l471vg = [ "stm32-metapac/stm32l471vg" ] -stm32l471ze = [ "stm32-metapac/stm32l471ze" ] -stm32l471zg = [ "stm32-metapac/stm32l471zg" ] -stm32l475rc = [ "stm32-metapac/stm32l475rc" ] -stm32l475re = [ "stm32-metapac/stm32l475re" ] -stm32l475rg = [ "stm32-metapac/stm32l475rg" ] -stm32l475vc = [ "stm32-metapac/stm32l475vc" ] -stm32l475ve = [ "stm32-metapac/stm32l475ve" ] -stm32l475vg = [ "stm32-metapac/stm32l475vg" ] -stm32l476je = [ "stm32-metapac/stm32l476je" ] -stm32l476jg = [ "stm32-metapac/stm32l476jg" ] -stm32l476me = [ "stm32-metapac/stm32l476me" ] -stm32l476mg = [ "stm32-metapac/stm32l476mg" ] -stm32l476qe = [ "stm32-metapac/stm32l476qe" ] -stm32l476qg = [ "stm32-metapac/stm32l476qg" ] -stm32l476rc = [ "stm32-metapac/stm32l476rc" ] -stm32l476re = [ "stm32-metapac/stm32l476re" ] -stm32l476rg = [ "stm32-metapac/stm32l476rg" ] -stm32l476vc = [ "stm32-metapac/stm32l476vc" ] -stm32l476ve = [ "stm32-metapac/stm32l476ve" ] -stm32l476vg = [ "stm32-metapac/stm32l476vg" ] -stm32l476ze = [ "stm32-metapac/stm32l476ze" ] -stm32l476zg = [ "stm32-metapac/stm32l476zg" ] -stm32l486jg = [ "stm32-metapac/stm32l486jg" ] -stm32l486qg = [ "stm32-metapac/stm32l486qg" ] -stm32l486rg = [ "stm32-metapac/stm32l486rg" ] -stm32l486vg = [ "stm32-metapac/stm32l486vg" ] -stm32l486zg = [ "stm32-metapac/stm32l486zg" ] -stm32l496ae = [ "stm32-metapac/stm32l496ae" ] -stm32l496ag = [ "stm32-metapac/stm32l496ag" ] -stm32l496qe = [ "stm32-metapac/stm32l496qe" ] -stm32l496qg = [ "stm32-metapac/stm32l496qg" ] -stm32l496re = [ "stm32-metapac/stm32l496re" ] -stm32l496rg = [ "stm32-metapac/stm32l496rg" ] -stm32l496ve = [ "stm32-metapac/stm32l496ve" ] -stm32l496vg = [ "stm32-metapac/stm32l496vg" ] -stm32l496wg = [ "stm32-metapac/stm32l496wg" ] -stm32l496ze = [ "stm32-metapac/stm32l496ze" ] -stm32l496zg = [ "stm32-metapac/stm32l496zg" ] -stm32l4a6ag = [ "stm32-metapac/stm32l4a6ag" ] -stm32l4a6qg = [ "stm32-metapac/stm32l4a6qg" ] -stm32l4a6rg = [ "stm32-metapac/stm32l4a6rg" ] -stm32l4a6vg = [ "stm32-metapac/stm32l4a6vg" ] -stm32l4a6zg = [ "stm32-metapac/stm32l4a6zg" ] -stm32l4p5ae = [ "stm32-metapac/stm32l4p5ae" ] -stm32l4p5ag = [ "stm32-metapac/stm32l4p5ag" ] -stm32l4p5ce = [ "stm32-metapac/stm32l4p5ce" ] -stm32l4p5cg = [ "stm32-metapac/stm32l4p5cg" ] -stm32l4p5qe = [ "stm32-metapac/stm32l4p5qe" ] -stm32l4p5qg = [ "stm32-metapac/stm32l4p5qg" ] -stm32l4p5re = [ "stm32-metapac/stm32l4p5re" ] -stm32l4p5rg = [ "stm32-metapac/stm32l4p5rg" ] -stm32l4p5ve = [ "stm32-metapac/stm32l4p5ve" ] -stm32l4p5vg = [ "stm32-metapac/stm32l4p5vg" ] -stm32l4p5ze = [ "stm32-metapac/stm32l4p5ze" ] -stm32l4p5zg = [ "stm32-metapac/stm32l4p5zg" ] -stm32l4q5ag = [ "stm32-metapac/stm32l4q5ag" ] -stm32l4q5cg = [ "stm32-metapac/stm32l4q5cg" ] -stm32l4q5qg = [ "stm32-metapac/stm32l4q5qg" ] -stm32l4q5rg = [ "stm32-metapac/stm32l4q5rg" ] -stm32l4q5vg = [ "stm32-metapac/stm32l4q5vg" ] -stm32l4q5zg = [ "stm32-metapac/stm32l4q5zg" ] -stm32l4r5ag = [ "stm32-metapac/stm32l4r5ag" ] -stm32l4r5ai = [ "stm32-metapac/stm32l4r5ai" ] -stm32l4r5qg = [ "stm32-metapac/stm32l4r5qg" ] -stm32l4r5qi = [ "stm32-metapac/stm32l4r5qi" ] -stm32l4r5vg = [ "stm32-metapac/stm32l4r5vg" ] -stm32l4r5vi = [ "stm32-metapac/stm32l4r5vi" ] -stm32l4r5zg = [ "stm32-metapac/stm32l4r5zg" ] -stm32l4r5zi = [ "stm32-metapac/stm32l4r5zi" ] -stm32l4r7ai = [ "stm32-metapac/stm32l4r7ai" ] -stm32l4r7vi = [ "stm32-metapac/stm32l4r7vi" ] -stm32l4r7zi = [ "stm32-metapac/stm32l4r7zi" ] -stm32l4r9ag = [ "stm32-metapac/stm32l4r9ag" ] -stm32l4r9ai = [ "stm32-metapac/stm32l4r9ai" ] -stm32l4r9vg = [ "stm32-metapac/stm32l4r9vg" ] -stm32l4r9vi = [ "stm32-metapac/stm32l4r9vi" ] -stm32l4r9zg = [ "stm32-metapac/stm32l4r9zg" ] -stm32l4r9zi = [ "stm32-metapac/stm32l4r9zi" ] -stm32l4s5ai = [ "stm32-metapac/stm32l4s5ai" ] -stm32l4s5qi = [ "stm32-metapac/stm32l4s5qi" ] -stm32l4s5vi = [ "stm32-metapac/stm32l4s5vi" ] -stm32l4s5zi = [ "stm32-metapac/stm32l4s5zi" ] -stm32l4s7ai = [ "stm32-metapac/stm32l4s7ai" ] -stm32l4s7vi = [ "stm32-metapac/stm32l4s7vi" ] -stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ] -stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] -stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] -stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] -stm32l552cc = [ "stm32-metapac/stm32l552cc" ] -stm32l552ce = [ "stm32-metapac/stm32l552ce" ] -stm32l552me = [ "stm32-metapac/stm32l552me" ] -stm32l552qc = [ "stm32-metapac/stm32l552qc" ] -stm32l552qe = [ "stm32-metapac/stm32l552qe" ] -stm32l552rc = [ "stm32-metapac/stm32l552rc" ] -stm32l552re = [ "stm32-metapac/stm32l552re" ] -stm32l552vc = [ "stm32-metapac/stm32l552vc" ] -stm32l552ve = [ "stm32-metapac/stm32l552ve" ] -stm32l552zc = [ "stm32-metapac/stm32l552zc" ] -stm32l552ze = [ "stm32-metapac/stm32l552ze" ] -stm32l562ce = [ "stm32-metapac/stm32l562ce" ] -stm32l562me = [ "stm32-metapac/stm32l562me" ] -stm32l562qe = [ "stm32-metapac/stm32l562qe" ] -stm32l562re = [ "stm32-metapac/stm32l562re" ] -stm32l562ve = [ "stm32-metapac/stm32l562ve" ] -stm32l562ze = [ "stm32-metapac/stm32l562ze" ] -stm32u535cb = [ "stm32-metapac/stm32u535cb" ] -stm32u535cc = [ "stm32-metapac/stm32u535cc" ] -stm32u535ce = [ "stm32-metapac/stm32u535ce" ] -stm32u535je = [ "stm32-metapac/stm32u535je" ] -stm32u535nc = [ "stm32-metapac/stm32u535nc" ] -stm32u535ne = [ "stm32-metapac/stm32u535ne" ] -stm32u535rb = [ "stm32-metapac/stm32u535rb" ] -stm32u535rc = [ "stm32-metapac/stm32u535rc" ] -stm32u535re = [ "stm32-metapac/stm32u535re" ] -stm32u535vc = [ "stm32-metapac/stm32u535vc" ] -stm32u535ve = [ "stm32-metapac/stm32u535ve" ] -stm32u545ce = [ "stm32-metapac/stm32u545ce" ] -stm32u545je = [ "stm32-metapac/stm32u545je" ] -stm32u545ne = [ "stm32-metapac/stm32u545ne" ] -stm32u545re = [ "stm32-metapac/stm32u545re" ] -stm32u545ve = [ "stm32-metapac/stm32u545ve" ] -stm32u575ag = [ "stm32-metapac/stm32u575ag" ] -stm32u575ai = [ "stm32-metapac/stm32u575ai" ] -stm32u575cg = [ "stm32-metapac/stm32u575cg" ] -stm32u575ci = [ "stm32-metapac/stm32u575ci" ] -stm32u575og = [ "stm32-metapac/stm32u575og" ] -stm32u575oi = [ "stm32-metapac/stm32u575oi" ] -stm32u575qg = [ "stm32-metapac/stm32u575qg" ] -stm32u575qi = [ "stm32-metapac/stm32u575qi" ] -stm32u575rg = [ "stm32-metapac/stm32u575rg" ] -stm32u575ri = [ "stm32-metapac/stm32u575ri" ] -stm32u575vg = [ "stm32-metapac/stm32u575vg" ] -stm32u575vi = [ "stm32-metapac/stm32u575vi" ] -stm32u575zg = [ "stm32-metapac/stm32u575zg" ] -stm32u575zi = [ "stm32-metapac/stm32u575zi" ] -stm32u585ai = [ "stm32-metapac/stm32u585ai" ] -stm32u585ci = [ "stm32-metapac/stm32u585ci" ] -stm32u585oi = [ "stm32-metapac/stm32u585oi" ] -stm32u585qi = [ "stm32-metapac/stm32u585qi" ] -stm32u585ri = [ "stm32-metapac/stm32u585ri" ] -stm32u585vi = [ "stm32-metapac/stm32u585vi" ] -stm32u585zi = [ "stm32-metapac/stm32u585zi" ] -stm32u595ai = [ "stm32-metapac/stm32u595ai" ] -stm32u595aj = [ "stm32-metapac/stm32u595aj" ] -stm32u595qi = [ "stm32-metapac/stm32u595qi" ] -stm32u595qj = [ "stm32-metapac/stm32u595qj" ] -stm32u595ri = [ "stm32-metapac/stm32u595ri" ] -stm32u595rj = [ "stm32-metapac/stm32u595rj" ] -stm32u595vi = [ "stm32-metapac/stm32u595vi" ] -stm32u595vj = [ "stm32-metapac/stm32u595vj" ] -stm32u595zi = [ "stm32-metapac/stm32u595zi" ] -stm32u595zj = [ "stm32-metapac/stm32u595zj" ] -stm32u599bj = [ "stm32-metapac/stm32u599bj" ] -stm32u599ni = [ "stm32-metapac/stm32u599ni" ] -stm32u599nj = [ "stm32-metapac/stm32u599nj" ] -stm32u599vi = [ "stm32-metapac/stm32u599vi" ] -stm32u599vj = [ "stm32-metapac/stm32u599vj" ] -stm32u599zi = [ "stm32-metapac/stm32u599zi" ] -stm32u599zj = [ "stm32-metapac/stm32u599zj" ] -stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] -stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] -stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] -stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] -stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] -stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] -stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] -stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] -stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] -stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] -stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] -stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] -stm32wb35cc = [ "stm32-metapac/stm32wb35cc" ] -stm32wb35ce = [ "stm32-metapac/stm32wb35ce" ] -stm32wb50cg = [ "stm32-metapac/stm32wb50cg" ] -stm32wb55cc = [ "stm32-metapac/stm32wb55cc" ] -stm32wb55ce = [ "stm32-metapac/stm32wb55ce" ] -stm32wb55cg = [ "stm32-metapac/stm32wb55cg" ] -stm32wb55rc = [ "stm32-metapac/stm32wb55rc" ] -stm32wb55re = [ "stm32-metapac/stm32wb55re" ] -stm32wb55rg = [ "stm32-metapac/stm32wb55rg" ] -stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ] -stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ] -stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ] -stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ] -stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ] -stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ] -stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ] -stm32wl54jc-cm0p = [ "stm32-metapac/stm32wl54jc-cm0p" ] -stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4" ] -stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p" ] -stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4" ] -stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p" ] -stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] -stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] -stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] -stm32wle4j8 = [ "stm32-metapac/stm32wle4j8" ] -stm32wle4jb = [ "stm32-metapac/stm32wle4jb" ] -stm32wle4jc = [ "stm32-metapac/stm32wle4jc" ] -stm32wle5c8 = [ "stm32-metapac/stm32wle5c8" ] -stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] -stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] -stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] -stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] -stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] +stm32c011d6 = ["stm32-metapac/stm32c011d6"] +stm32c011f4 = ["stm32-metapac/stm32c011f4"] +stm32c011f6 = ["stm32-metapac/stm32c011f6"] +stm32c011j4 = ["stm32-metapac/stm32c011j4"] +stm32c011j6 = ["stm32-metapac/stm32c011j6"] +stm32c031c4 = ["stm32-metapac/stm32c031c4"] +stm32c031c6 = ["stm32-metapac/stm32c031c6"] +stm32c031f4 = ["stm32-metapac/stm32c031f4"] +stm32c031f6 = ["stm32-metapac/stm32c031f6"] +stm32c031g4 = ["stm32-metapac/stm32c031g4"] +stm32c031g6 = ["stm32-metapac/stm32c031g6"] +stm32c031k4 = ["stm32-metapac/stm32c031k4"] +stm32c031k6 = ["stm32-metapac/stm32c031k6"] +stm32f030c6 = ["stm32-metapac/stm32f030c6"] +stm32f030c8 = ["stm32-metapac/stm32f030c8"] +stm32f030cc = ["stm32-metapac/stm32f030cc"] +stm32f030f4 = ["stm32-metapac/stm32f030f4"] +stm32f030k6 = ["stm32-metapac/stm32f030k6"] +stm32f030r8 = ["stm32-metapac/stm32f030r8"] +stm32f030rc = ["stm32-metapac/stm32f030rc"] +stm32f031c4 = ["stm32-metapac/stm32f031c4"] +stm32f031c6 = ["stm32-metapac/stm32f031c6"] +stm32f031e6 = ["stm32-metapac/stm32f031e6"] +stm32f031f4 = ["stm32-metapac/stm32f031f4"] +stm32f031f6 = ["stm32-metapac/stm32f031f6"] +stm32f031g4 = ["stm32-metapac/stm32f031g4"] +stm32f031g6 = ["stm32-metapac/stm32f031g6"] +stm32f031k4 = ["stm32-metapac/stm32f031k4"] +stm32f031k6 = ["stm32-metapac/stm32f031k6"] +stm32f038c6 = ["stm32-metapac/stm32f038c6"] +stm32f038e6 = ["stm32-metapac/stm32f038e6"] +stm32f038f6 = ["stm32-metapac/stm32f038f6"] +stm32f038g6 = ["stm32-metapac/stm32f038g6"] +stm32f038k6 = ["stm32-metapac/stm32f038k6"] +stm32f042c4 = ["stm32-metapac/stm32f042c4"] +stm32f042c6 = ["stm32-metapac/stm32f042c6"] +stm32f042f4 = ["stm32-metapac/stm32f042f4"] +stm32f042f6 = ["stm32-metapac/stm32f042f6"] +stm32f042g4 = ["stm32-metapac/stm32f042g4"] +stm32f042g6 = ["stm32-metapac/stm32f042g6"] +stm32f042k4 = ["stm32-metapac/stm32f042k4"] +stm32f042k6 = ["stm32-metapac/stm32f042k6"] +stm32f042t6 = ["stm32-metapac/stm32f042t6"] +stm32f048c6 = ["stm32-metapac/stm32f048c6"] +stm32f048g6 = ["stm32-metapac/stm32f048g6"] +stm32f048t6 = ["stm32-metapac/stm32f048t6"] +stm32f051c4 = ["stm32-metapac/stm32f051c4"] +stm32f051c6 = ["stm32-metapac/stm32f051c6"] +stm32f051c8 = ["stm32-metapac/stm32f051c8"] +stm32f051k4 = ["stm32-metapac/stm32f051k4"] +stm32f051k6 = ["stm32-metapac/stm32f051k6"] +stm32f051k8 = ["stm32-metapac/stm32f051k8"] +stm32f051r4 = ["stm32-metapac/stm32f051r4"] +stm32f051r6 = ["stm32-metapac/stm32f051r6"] +stm32f051r8 = ["stm32-metapac/stm32f051r8"] +stm32f051t8 = ["stm32-metapac/stm32f051t8"] +stm32f058c8 = ["stm32-metapac/stm32f058c8"] +stm32f058r8 = ["stm32-metapac/stm32f058r8"] +stm32f058t8 = ["stm32-metapac/stm32f058t8"] +stm32f070c6 = ["stm32-metapac/stm32f070c6"] +stm32f070cb = ["stm32-metapac/stm32f070cb"] +stm32f070f6 = ["stm32-metapac/stm32f070f6"] +stm32f070rb = ["stm32-metapac/stm32f070rb"] +stm32f071c8 = ["stm32-metapac/stm32f071c8"] +stm32f071cb = ["stm32-metapac/stm32f071cb"] +stm32f071rb = ["stm32-metapac/stm32f071rb"] +stm32f071v8 = ["stm32-metapac/stm32f071v8"] +stm32f071vb = ["stm32-metapac/stm32f071vb"] +stm32f072c8 = ["stm32-metapac/stm32f072c8"] +stm32f072cb = ["stm32-metapac/stm32f072cb"] +stm32f072r8 = ["stm32-metapac/stm32f072r8"] +stm32f072rb = ["stm32-metapac/stm32f072rb"] +stm32f072v8 = ["stm32-metapac/stm32f072v8"] +stm32f072vb = ["stm32-metapac/stm32f072vb"] +stm32f078cb = ["stm32-metapac/stm32f078cb"] +stm32f078rb = ["stm32-metapac/stm32f078rb"] +stm32f078vb = ["stm32-metapac/stm32f078vb"] +stm32f091cb = ["stm32-metapac/stm32f091cb"] +stm32f091cc = ["stm32-metapac/stm32f091cc"] +stm32f091rb = ["stm32-metapac/stm32f091rb"] +stm32f091rc = ["stm32-metapac/stm32f091rc"] +stm32f091vb = ["stm32-metapac/stm32f091vb"] +stm32f091vc = ["stm32-metapac/stm32f091vc"] +stm32f098cc = ["stm32-metapac/stm32f098cc"] +stm32f098rc = ["stm32-metapac/stm32f098rc"] +stm32f098vc = ["stm32-metapac/stm32f098vc"] +stm32f100c4 = ["stm32-metapac/stm32f100c4"] +stm32f100c6 = ["stm32-metapac/stm32f100c6"] +stm32f100c8 = ["stm32-metapac/stm32f100c8"] +stm32f100cb = ["stm32-metapac/stm32f100cb"] +stm32f100r4 = ["stm32-metapac/stm32f100r4"] +stm32f100r6 = ["stm32-metapac/stm32f100r6"] +stm32f100r8 = ["stm32-metapac/stm32f100r8"] +stm32f100rb = ["stm32-metapac/stm32f100rb"] +stm32f100rc = ["stm32-metapac/stm32f100rc"] +stm32f100rd = ["stm32-metapac/stm32f100rd"] +stm32f100re = ["stm32-metapac/stm32f100re"] +stm32f100v8 = ["stm32-metapac/stm32f100v8"] +stm32f100vb = ["stm32-metapac/stm32f100vb"] +stm32f100vc = ["stm32-metapac/stm32f100vc"] +stm32f100vd = ["stm32-metapac/stm32f100vd"] +stm32f100ve = ["stm32-metapac/stm32f100ve"] +stm32f100zc = ["stm32-metapac/stm32f100zc"] +stm32f100zd = ["stm32-metapac/stm32f100zd"] +stm32f100ze = ["stm32-metapac/stm32f100ze"] +stm32f101c4 = ["stm32-metapac/stm32f101c4"] +stm32f101c6 = ["stm32-metapac/stm32f101c6"] +stm32f101c8 = ["stm32-metapac/stm32f101c8"] +stm32f101cb = ["stm32-metapac/stm32f101cb"] +stm32f101r4 = ["stm32-metapac/stm32f101r4"] +stm32f101r6 = ["stm32-metapac/stm32f101r6"] +stm32f101r8 = ["stm32-metapac/stm32f101r8"] +stm32f101rb = ["stm32-metapac/stm32f101rb"] +stm32f101rc = ["stm32-metapac/stm32f101rc"] +stm32f101rd = ["stm32-metapac/stm32f101rd"] +stm32f101re = ["stm32-metapac/stm32f101re"] +stm32f101rf = ["stm32-metapac/stm32f101rf"] +stm32f101rg = ["stm32-metapac/stm32f101rg"] +stm32f101t4 = ["stm32-metapac/stm32f101t4"] +stm32f101t6 = ["stm32-metapac/stm32f101t6"] +stm32f101t8 = ["stm32-metapac/stm32f101t8"] +stm32f101tb = ["stm32-metapac/stm32f101tb"] +stm32f101v8 = ["stm32-metapac/stm32f101v8"] +stm32f101vb = ["stm32-metapac/stm32f101vb"] +stm32f101vc = ["stm32-metapac/stm32f101vc"] +stm32f101vd = ["stm32-metapac/stm32f101vd"] +stm32f101ve = ["stm32-metapac/stm32f101ve"] +stm32f101vf = ["stm32-metapac/stm32f101vf"] +stm32f101vg = ["stm32-metapac/stm32f101vg"] +stm32f101zc = ["stm32-metapac/stm32f101zc"] +stm32f101zd = ["stm32-metapac/stm32f101zd"] +stm32f101ze = ["stm32-metapac/stm32f101ze"] +stm32f101zf = ["stm32-metapac/stm32f101zf"] +stm32f101zg = ["stm32-metapac/stm32f101zg"] +stm32f102c4 = ["stm32-metapac/stm32f102c4"] +stm32f102c6 = ["stm32-metapac/stm32f102c6"] +stm32f102c8 = ["stm32-metapac/stm32f102c8"] +stm32f102cb = ["stm32-metapac/stm32f102cb"] +stm32f102r4 = ["stm32-metapac/stm32f102r4"] +stm32f102r6 = ["stm32-metapac/stm32f102r6"] +stm32f102r8 = ["stm32-metapac/stm32f102r8"] +stm32f102rb = ["stm32-metapac/stm32f102rb"] +stm32f103c4 = ["stm32-metapac/stm32f103c4"] +stm32f103c6 = ["stm32-metapac/stm32f103c6"] +stm32f103c8 = ["stm32-metapac/stm32f103c8"] +stm32f103cb = ["stm32-metapac/stm32f103cb"] +stm32f103r4 = ["stm32-metapac/stm32f103r4"] +stm32f103r6 = ["stm32-metapac/stm32f103r6"] +stm32f103r8 = ["stm32-metapac/stm32f103r8"] +stm32f103rb = ["stm32-metapac/stm32f103rb"] +stm32f103rc = ["stm32-metapac/stm32f103rc"] +stm32f103rd = ["stm32-metapac/stm32f103rd"] +stm32f103re = ["stm32-metapac/stm32f103re"] +stm32f103rf = ["stm32-metapac/stm32f103rf"] +stm32f103rg = ["stm32-metapac/stm32f103rg"] +stm32f103t4 = ["stm32-metapac/stm32f103t4"] +stm32f103t6 = ["stm32-metapac/stm32f103t6"] +stm32f103t8 = ["stm32-metapac/stm32f103t8"] +stm32f103tb = ["stm32-metapac/stm32f103tb"] +stm32f103v8 = ["stm32-metapac/stm32f103v8"] +stm32f103vb = ["stm32-metapac/stm32f103vb"] +stm32f103vc = ["stm32-metapac/stm32f103vc"] +stm32f103vd = ["stm32-metapac/stm32f103vd"] +stm32f103ve = ["stm32-metapac/stm32f103ve"] +stm32f103vf = ["stm32-metapac/stm32f103vf"] +stm32f103vg = ["stm32-metapac/stm32f103vg"] +stm32f103zc = ["stm32-metapac/stm32f103zc"] +stm32f103zd = ["stm32-metapac/stm32f103zd"] +stm32f103ze = ["stm32-metapac/stm32f103ze"] +stm32f103zf = ["stm32-metapac/stm32f103zf"] +stm32f103zg = ["stm32-metapac/stm32f103zg"] +stm32f105r8 = ["stm32-metapac/stm32f105r8"] +stm32f105rb = ["stm32-metapac/stm32f105rb"] +stm32f105rc = ["stm32-metapac/stm32f105rc"] +stm32f105v8 = ["stm32-metapac/stm32f105v8"] +stm32f105vb = ["stm32-metapac/stm32f105vb"] +stm32f105vc = ["stm32-metapac/stm32f105vc"] +stm32f107rb = ["stm32-metapac/stm32f107rb"] +stm32f107rc = ["stm32-metapac/stm32f107rc"] +stm32f107vb = ["stm32-metapac/stm32f107vb"] +stm32f107vc = ["stm32-metapac/stm32f107vc"] +stm32f205rb = ["stm32-metapac/stm32f205rb"] +stm32f205rc = ["stm32-metapac/stm32f205rc"] +stm32f205re = ["stm32-metapac/stm32f205re"] +stm32f205rf = ["stm32-metapac/stm32f205rf"] +stm32f205rg = ["stm32-metapac/stm32f205rg"] +stm32f205vb = ["stm32-metapac/stm32f205vb"] +stm32f205vc = ["stm32-metapac/stm32f205vc"] +stm32f205ve = ["stm32-metapac/stm32f205ve"] +stm32f205vf = ["stm32-metapac/stm32f205vf"] +stm32f205vg = ["stm32-metapac/stm32f205vg"] +stm32f205zc = ["stm32-metapac/stm32f205zc"] +stm32f205ze = ["stm32-metapac/stm32f205ze"] +stm32f205zf = ["stm32-metapac/stm32f205zf"] +stm32f205zg = ["stm32-metapac/stm32f205zg"] +stm32f207ic = ["stm32-metapac/stm32f207ic"] +stm32f207ie = ["stm32-metapac/stm32f207ie"] +stm32f207if = ["stm32-metapac/stm32f207if"] +stm32f207ig = ["stm32-metapac/stm32f207ig"] +stm32f207vc = ["stm32-metapac/stm32f207vc"] +stm32f207ve = ["stm32-metapac/stm32f207ve"] +stm32f207vf = ["stm32-metapac/stm32f207vf"] +stm32f207vg = ["stm32-metapac/stm32f207vg"] +stm32f207zc = ["stm32-metapac/stm32f207zc"] +stm32f207ze = ["stm32-metapac/stm32f207ze"] +stm32f207zf = ["stm32-metapac/stm32f207zf"] +stm32f207zg = ["stm32-metapac/stm32f207zg"] +stm32f215re = ["stm32-metapac/stm32f215re"] +stm32f215rg = ["stm32-metapac/stm32f215rg"] +stm32f215ve = ["stm32-metapac/stm32f215ve"] +stm32f215vg = ["stm32-metapac/stm32f215vg"] +stm32f215ze = ["stm32-metapac/stm32f215ze"] +stm32f215zg = ["stm32-metapac/stm32f215zg"] +stm32f217ie = ["stm32-metapac/stm32f217ie"] +stm32f217ig = ["stm32-metapac/stm32f217ig"] +stm32f217ve = ["stm32-metapac/stm32f217ve"] +stm32f217vg = ["stm32-metapac/stm32f217vg"] +stm32f217ze = ["stm32-metapac/stm32f217ze"] +stm32f217zg = ["stm32-metapac/stm32f217zg"] +stm32f301c6 = ["stm32-metapac/stm32f301c6"] +stm32f301c8 = ["stm32-metapac/stm32f301c8"] +stm32f301k6 = ["stm32-metapac/stm32f301k6"] +stm32f301k8 = ["stm32-metapac/stm32f301k8"] +stm32f301r6 = ["stm32-metapac/stm32f301r6"] +stm32f301r8 = ["stm32-metapac/stm32f301r8"] +stm32f302c6 = ["stm32-metapac/stm32f302c6"] +stm32f302c8 = ["stm32-metapac/stm32f302c8"] +stm32f302cb = ["stm32-metapac/stm32f302cb"] +stm32f302cc = ["stm32-metapac/stm32f302cc"] +stm32f302k6 = ["stm32-metapac/stm32f302k6"] +stm32f302k8 = ["stm32-metapac/stm32f302k8"] +stm32f302r6 = ["stm32-metapac/stm32f302r6"] +stm32f302r8 = ["stm32-metapac/stm32f302r8"] +stm32f302rb = ["stm32-metapac/stm32f302rb"] +stm32f302rc = ["stm32-metapac/stm32f302rc"] +stm32f302rd = ["stm32-metapac/stm32f302rd"] +stm32f302re = ["stm32-metapac/stm32f302re"] +stm32f302vb = ["stm32-metapac/stm32f302vb"] +stm32f302vc = ["stm32-metapac/stm32f302vc"] +stm32f302vd = ["stm32-metapac/stm32f302vd"] +stm32f302ve = ["stm32-metapac/stm32f302ve"] +stm32f302zd = ["stm32-metapac/stm32f302zd"] +stm32f302ze = ["stm32-metapac/stm32f302ze"] +stm32f303c6 = ["stm32-metapac/stm32f303c6"] +stm32f303c8 = ["stm32-metapac/stm32f303c8"] +stm32f303cb = ["stm32-metapac/stm32f303cb"] +stm32f303cc = ["stm32-metapac/stm32f303cc"] +stm32f303k6 = ["stm32-metapac/stm32f303k6"] +stm32f303k8 = ["stm32-metapac/stm32f303k8"] +stm32f303r6 = ["stm32-metapac/stm32f303r6"] +stm32f303r8 = ["stm32-metapac/stm32f303r8"] +stm32f303rb = ["stm32-metapac/stm32f303rb"] +stm32f303rc = ["stm32-metapac/stm32f303rc"] +stm32f303rd = ["stm32-metapac/stm32f303rd"] +stm32f303re = ["stm32-metapac/stm32f303re"] +stm32f303vb = ["stm32-metapac/stm32f303vb"] +stm32f303vc = ["stm32-metapac/stm32f303vc"] +stm32f303vd = ["stm32-metapac/stm32f303vd"] +stm32f303ve = ["stm32-metapac/stm32f303ve"] +stm32f303zd = ["stm32-metapac/stm32f303zd"] +stm32f303ze = ["stm32-metapac/stm32f303ze"] +stm32f318c8 = ["stm32-metapac/stm32f318c8"] +stm32f318k8 = ["stm32-metapac/stm32f318k8"] +stm32f328c8 = ["stm32-metapac/stm32f328c8"] +stm32f334c4 = ["stm32-metapac/stm32f334c4"] +stm32f334c6 = ["stm32-metapac/stm32f334c6"] +stm32f334c8 = ["stm32-metapac/stm32f334c8"] +stm32f334k4 = ["stm32-metapac/stm32f334k4"] +stm32f334k6 = ["stm32-metapac/stm32f334k6"] +stm32f334k8 = ["stm32-metapac/stm32f334k8"] +stm32f334r6 = ["stm32-metapac/stm32f334r6"] +stm32f334r8 = ["stm32-metapac/stm32f334r8"] +stm32f358cc = ["stm32-metapac/stm32f358cc"] +stm32f358rc = ["stm32-metapac/stm32f358rc"] +stm32f358vc = ["stm32-metapac/stm32f358vc"] +stm32f373c8 = ["stm32-metapac/stm32f373c8"] +stm32f373cb = ["stm32-metapac/stm32f373cb"] +stm32f373cc = ["stm32-metapac/stm32f373cc"] +stm32f373r8 = ["stm32-metapac/stm32f373r8"] +stm32f373rb = ["stm32-metapac/stm32f373rb"] +stm32f373rc = ["stm32-metapac/stm32f373rc"] +stm32f373v8 = ["stm32-metapac/stm32f373v8"] +stm32f373vb = ["stm32-metapac/stm32f373vb"] +stm32f373vc = ["stm32-metapac/stm32f373vc"] +stm32f378cc = ["stm32-metapac/stm32f378cc"] +stm32f378rc = ["stm32-metapac/stm32f378rc"] +stm32f378vc = ["stm32-metapac/stm32f378vc"] +stm32f398ve = ["stm32-metapac/stm32f398ve"] +stm32f401cb = ["stm32-metapac/stm32f401cb"] +stm32f401cc = ["stm32-metapac/stm32f401cc"] +stm32f401cd = ["stm32-metapac/stm32f401cd"] +stm32f401ce = ["stm32-metapac/stm32f401ce"] +stm32f401rb = ["stm32-metapac/stm32f401rb"] +stm32f401rc = ["stm32-metapac/stm32f401rc"] +stm32f401rd = ["stm32-metapac/stm32f401rd"] +stm32f401re = ["stm32-metapac/stm32f401re"] +stm32f401vb = ["stm32-metapac/stm32f401vb"] +stm32f401vc = ["stm32-metapac/stm32f401vc"] +stm32f401vd = ["stm32-metapac/stm32f401vd"] +stm32f401ve = ["stm32-metapac/stm32f401ve"] +stm32f405oe = ["stm32-metapac/stm32f405oe"] +stm32f405og = ["stm32-metapac/stm32f405og"] +stm32f405rg = ["stm32-metapac/stm32f405rg"] +stm32f405vg = ["stm32-metapac/stm32f405vg"] +stm32f405zg = ["stm32-metapac/stm32f405zg"] +stm32f407ie = ["stm32-metapac/stm32f407ie"] +stm32f407ig = ["stm32-metapac/stm32f407ig"] +stm32f407ve = ["stm32-metapac/stm32f407ve"] +stm32f407vg = ["stm32-metapac/stm32f407vg"] +stm32f407ze = ["stm32-metapac/stm32f407ze"] +stm32f407zg = ["stm32-metapac/stm32f407zg"] +stm32f410c8 = ["stm32-metapac/stm32f410c8"] +stm32f410cb = ["stm32-metapac/stm32f410cb"] +stm32f410r8 = ["stm32-metapac/stm32f410r8"] +stm32f410rb = ["stm32-metapac/stm32f410rb"] +stm32f410t8 = ["stm32-metapac/stm32f410t8"] +stm32f410tb = ["stm32-metapac/stm32f410tb"] +stm32f411cc = ["stm32-metapac/stm32f411cc"] +stm32f411ce = ["stm32-metapac/stm32f411ce"] +stm32f411rc = ["stm32-metapac/stm32f411rc"] +stm32f411re = ["stm32-metapac/stm32f411re"] +stm32f411vc = ["stm32-metapac/stm32f411vc"] +stm32f411ve = ["stm32-metapac/stm32f411ve"] +stm32f412ce = ["stm32-metapac/stm32f412ce"] +stm32f412cg = ["stm32-metapac/stm32f412cg"] +stm32f412re = ["stm32-metapac/stm32f412re"] +stm32f412rg = ["stm32-metapac/stm32f412rg"] +stm32f412ve = ["stm32-metapac/stm32f412ve"] +stm32f412vg = ["stm32-metapac/stm32f412vg"] +stm32f412ze = ["stm32-metapac/stm32f412ze"] +stm32f412zg = ["stm32-metapac/stm32f412zg"] +stm32f413cg = ["stm32-metapac/stm32f413cg"] +stm32f413ch = ["stm32-metapac/stm32f413ch"] +stm32f413mg = ["stm32-metapac/stm32f413mg"] +stm32f413mh = ["stm32-metapac/stm32f413mh"] +stm32f413rg = ["stm32-metapac/stm32f413rg"] +stm32f413rh = ["stm32-metapac/stm32f413rh"] +stm32f413vg = ["stm32-metapac/stm32f413vg"] +stm32f413vh = ["stm32-metapac/stm32f413vh"] +stm32f413zg = ["stm32-metapac/stm32f413zg"] +stm32f413zh = ["stm32-metapac/stm32f413zh"] +stm32f415og = ["stm32-metapac/stm32f415og"] +stm32f415rg = ["stm32-metapac/stm32f415rg"] +stm32f415vg = ["stm32-metapac/stm32f415vg"] +stm32f415zg = ["stm32-metapac/stm32f415zg"] +stm32f417ie = ["stm32-metapac/stm32f417ie"] +stm32f417ig = ["stm32-metapac/stm32f417ig"] +stm32f417ve = ["stm32-metapac/stm32f417ve"] +stm32f417vg = ["stm32-metapac/stm32f417vg"] +stm32f417ze = ["stm32-metapac/stm32f417ze"] +stm32f417zg = ["stm32-metapac/stm32f417zg"] +stm32f423ch = ["stm32-metapac/stm32f423ch"] +stm32f423mh = ["stm32-metapac/stm32f423mh"] +stm32f423rh = ["stm32-metapac/stm32f423rh"] +stm32f423vh = ["stm32-metapac/stm32f423vh"] +stm32f423zh = ["stm32-metapac/stm32f423zh"] +stm32f427ag = ["stm32-metapac/stm32f427ag"] +stm32f427ai = ["stm32-metapac/stm32f427ai"] +stm32f427ig = ["stm32-metapac/stm32f427ig"] +stm32f427ii = ["stm32-metapac/stm32f427ii"] +stm32f427vg = ["stm32-metapac/stm32f427vg"] +stm32f427vi = ["stm32-metapac/stm32f427vi"] +stm32f427zg = ["stm32-metapac/stm32f427zg"] +stm32f427zi = ["stm32-metapac/stm32f427zi"] +stm32f429ag = ["stm32-metapac/stm32f429ag"] +stm32f429ai = ["stm32-metapac/stm32f429ai"] +stm32f429be = ["stm32-metapac/stm32f429be"] +stm32f429bg = ["stm32-metapac/stm32f429bg"] +stm32f429bi = ["stm32-metapac/stm32f429bi"] +stm32f429ie = ["stm32-metapac/stm32f429ie"] +stm32f429ig = ["stm32-metapac/stm32f429ig"] +stm32f429ii = ["stm32-metapac/stm32f429ii"] +stm32f429ne = ["stm32-metapac/stm32f429ne"] +stm32f429ng = ["stm32-metapac/stm32f429ng"] +stm32f429ni = ["stm32-metapac/stm32f429ni"] +stm32f429ve = ["stm32-metapac/stm32f429ve"] +stm32f429vg = ["stm32-metapac/stm32f429vg"] +stm32f429vi = ["stm32-metapac/stm32f429vi"] +stm32f429ze = ["stm32-metapac/stm32f429ze"] +stm32f429zg = ["stm32-metapac/stm32f429zg"] +stm32f429zi = ["stm32-metapac/stm32f429zi"] +stm32f437ai = ["stm32-metapac/stm32f437ai"] +stm32f437ig = ["stm32-metapac/stm32f437ig"] +stm32f437ii = ["stm32-metapac/stm32f437ii"] +stm32f437vg = ["stm32-metapac/stm32f437vg"] +stm32f437vi = ["stm32-metapac/stm32f437vi"] +stm32f437zg = ["stm32-metapac/stm32f437zg"] +stm32f437zi = ["stm32-metapac/stm32f437zi"] +stm32f439ai = ["stm32-metapac/stm32f439ai"] +stm32f439bg = ["stm32-metapac/stm32f439bg"] +stm32f439bi = ["stm32-metapac/stm32f439bi"] +stm32f439ig = ["stm32-metapac/stm32f439ig"] +stm32f439ii = ["stm32-metapac/stm32f439ii"] +stm32f439ng = ["stm32-metapac/stm32f439ng"] +stm32f439ni = ["stm32-metapac/stm32f439ni"] +stm32f439vg = ["stm32-metapac/stm32f439vg"] +stm32f439vi = ["stm32-metapac/stm32f439vi"] +stm32f439zg = ["stm32-metapac/stm32f439zg"] +stm32f439zi = ["stm32-metapac/stm32f439zi"] +stm32f446mc = ["stm32-metapac/stm32f446mc"] +stm32f446me = ["stm32-metapac/stm32f446me"] +stm32f446rc = ["stm32-metapac/stm32f446rc"] +stm32f446re = ["stm32-metapac/stm32f446re"] +stm32f446vc = ["stm32-metapac/stm32f446vc"] +stm32f446ve = ["stm32-metapac/stm32f446ve"] +stm32f446zc = ["stm32-metapac/stm32f446zc"] +stm32f446ze = ["stm32-metapac/stm32f446ze"] +stm32f469ae = ["stm32-metapac/stm32f469ae"] +stm32f469ag = ["stm32-metapac/stm32f469ag"] +stm32f469ai = ["stm32-metapac/stm32f469ai"] +stm32f469be = ["stm32-metapac/stm32f469be"] +stm32f469bg = ["stm32-metapac/stm32f469bg"] +stm32f469bi = ["stm32-metapac/stm32f469bi"] +stm32f469ie = ["stm32-metapac/stm32f469ie"] +stm32f469ig = ["stm32-metapac/stm32f469ig"] +stm32f469ii = ["stm32-metapac/stm32f469ii"] +stm32f469ne = ["stm32-metapac/stm32f469ne"] +stm32f469ng = ["stm32-metapac/stm32f469ng"] +stm32f469ni = ["stm32-metapac/stm32f469ni"] +stm32f469ve = ["stm32-metapac/stm32f469ve"] +stm32f469vg = ["stm32-metapac/stm32f469vg"] +stm32f469vi = ["stm32-metapac/stm32f469vi"] +stm32f469ze = ["stm32-metapac/stm32f469ze"] +stm32f469zg = ["stm32-metapac/stm32f469zg"] +stm32f469zi = ["stm32-metapac/stm32f469zi"] +stm32f479ag = ["stm32-metapac/stm32f479ag"] +stm32f479ai = ["stm32-metapac/stm32f479ai"] +stm32f479bg = ["stm32-metapac/stm32f479bg"] +stm32f479bi = ["stm32-metapac/stm32f479bi"] +stm32f479ig = ["stm32-metapac/stm32f479ig"] +stm32f479ii = ["stm32-metapac/stm32f479ii"] +stm32f479ng = ["stm32-metapac/stm32f479ng"] +stm32f479ni = ["stm32-metapac/stm32f479ni"] +stm32f479vg = ["stm32-metapac/stm32f479vg"] +stm32f479vi = ["stm32-metapac/stm32f479vi"] +stm32f479zg = ["stm32-metapac/stm32f479zg"] +stm32f479zi = ["stm32-metapac/stm32f479zi"] +stm32f722ic = ["stm32-metapac/stm32f722ic"] +stm32f722ie = ["stm32-metapac/stm32f722ie"] +stm32f722rc = ["stm32-metapac/stm32f722rc"] +stm32f722re = ["stm32-metapac/stm32f722re"] +stm32f722vc = ["stm32-metapac/stm32f722vc"] +stm32f722ve = ["stm32-metapac/stm32f722ve"] +stm32f722zc = ["stm32-metapac/stm32f722zc"] +stm32f722ze = ["stm32-metapac/stm32f722ze"] +stm32f723ic = ["stm32-metapac/stm32f723ic"] +stm32f723ie = ["stm32-metapac/stm32f723ie"] +stm32f723vc = ["stm32-metapac/stm32f723vc"] +stm32f723ve = ["stm32-metapac/stm32f723ve"] +stm32f723zc = ["stm32-metapac/stm32f723zc"] +stm32f723ze = ["stm32-metapac/stm32f723ze"] +stm32f730i8 = ["stm32-metapac/stm32f730i8"] +stm32f730r8 = ["stm32-metapac/stm32f730r8"] +stm32f730v8 = ["stm32-metapac/stm32f730v8"] +stm32f730z8 = ["stm32-metapac/stm32f730z8"] +stm32f732ie = ["stm32-metapac/stm32f732ie"] +stm32f732re = ["stm32-metapac/stm32f732re"] +stm32f732ve = ["stm32-metapac/stm32f732ve"] +stm32f732ze = ["stm32-metapac/stm32f732ze"] +stm32f733ie = ["stm32-metapac/stm32f733ie"] +stm32f733ve = ["stm32-metapac/stm32f733ve"] +stm32f733ze = ["stm32-metapac/stm32f733ze"] +stm32f745ie = ["stm32-metapac/stm32f745ie"] +stm32f745ig = ["stm32-metapac/stm32f745ig"] +stm32f745ve = ["stm32-metapac/stm32f745ve"] +stm32f745vg = ["stm32-metapac/stm32f745vg"] +stm32f745ze = ["stm32-metapac/stm32f745ze"] +stm32f745zg = ["stm32-metapac/stm32f745zg"] +stm32f746be = ["stm32-metapac/stm32f746be"] +stm32f746bg = ["stm32-metapac/stm32f746bg"] +stm32f746ie = ["stm32-metapac/stm32f746ie"] +stm32f746ig = ["stm32-metapac/stm32f746ig"] +stm32f746ne = ["stm32-metapac/stm32f746ne"] +stm32f746ng = ["stm32-metapac/stm32f746ng"] +stm32f746ve = ["stm32-metapac/stm32f746ve"] +stm32f746vg = ["stm32-metapac/stm32f746vg"] +stm32f746ze = ["stm32-metapac/stm32f746ze"] +stm32f746zg = ["stm32-metapac/stm32f746zg"] +stm32f750n8 = ["stm32-metapac/stm32f750n8"] +stm32f750v8 = ["stm32-metapac/stm32f750v8"] +stm32f750z8 = ["stm32-metapac/stm32f750z8"] +stm32f756bg = ["stm32-metapac/stm32f756bg"] +stm32f756ig = ["stm32-metapac/stm32f756ig"] +stm32f756ng = ["stm32-metapac/stm32f756ng"] +stm32f756vg = ["stm32-metapac/stm32f756vg"] +stm32f756zg = ["stm32-metapac/stm32f756zg"] +stm32f765bg = ["stm32-metapac/stm32f765bg"] +stm32f765bi = ["stm32-metapac/stm32f765bi"] +stm32f765ig = ["stm32-metapac/stm32f765ig"] +stm32f765ii = ["stm32-metapac/stm32f765ii"] +stm32f765ng = ["stm32-metapac/stm32f765ng"] +stm32f765ni = ["stm32-metapac/stm32f765ni"] +stm32f765vg = ["stm32-metapac/stm32f765vg"] +stm32f765vi = ["stm32-metapac/stm32f765vi"] +stm32f765zg = ["stm32-metapac/stm32f765zg"] +stm32f765zi = ["stm32-metapac/stm32f765zi"] +stm32f767bg = ["stm32-metapac/stm32f767bg"] +stm32f767bi = ["stm32-metapac/stm32f767bi"] +stm32f767ig = ["stm32-metapac/stm32f767ig"] +stm32f767ii = ["stm32-metapac/stm32f767ii"] +stm32f767ng = ["stm32-metapac/stm32f767ng"] +stm32f767ni = ["stm32-metapac/stm32f767ni"] +stm32f767vg = ["stm32-metapac/stm32f767vg"] +stm32f767vi = ["stm32-metapac/stm32f767vi"] +stm32f767zg = ["stm32-metapac/stm32f767zg"] +stm32f767zi = ["stm32-metapac/stm32f767zi"] +stm32f768ai = ["stm32-metapac/stm32f768ai"] +stm32f769ag = ["stm32-metapac/stm32f769ag"] +stm32f769ai = ["stm32-metapac/stm32f769ai"] +stm32f769bg = ["stm32-metapac/stm32f769bg"] +stm32f769bi = ["stm32-metapac/stm32f769bi"] +stm32f769ig = ["stm32-metapac/stm32f769ig"] +stm32f769ii = ["stm32-metapac/stm32f769ii"] +stm32f769ng = ["stm32-metapac/stm32f769ng"] +stm32f769ni = ["stm32-metapac/stm32f769ni"] +stm32f777bi = ["stm32-metapac/stm32f777bi"] +stm32f777ii = ["stm32-metapac/stm32f777ii"] +stm32f777ni = ["stm32-metapac/stm32f777ni"] +stm32f777vi = ["stm32-metapac/stm32f777vi"] +stm32f777zi = ["stm32-metapac/stm32f777zi"] +stm32f778ai = ["stm32-metapac/stm32f778ai"] +stm32f779ai = ["stm32-metapac/stm32f779ai"] +stm32f779bi = ["stm32-metapac/stm32f779bi"] +stm32f779ii = ["stm32-metapac/stm32f779ii"] +stm32f779ni = ["stm32-metapac/stm32f779ni"] +stm32g030c6 = ["stm32-metapac/stm32g030c6"] +stm32g030c8 = ["stm32-metapac/stm32g030c8"] +stm32g030f6 = ["stm32-metapac/stm32g030f6"] +stm32g030j6 = ["stm32-metapac/stm32g030j6"] +stm32g030k6 = ["stm32-metapac/stm32g030k6"] +stm32g030k8 = ["stm32-metapac/stm32g030k8"] +stm32g031c4 = ["stm32-metapac/stm32g031c4"] +stm32g031c6 = ["stm32-metapac/stm32g031c6"] +stm32g031c8 = ["stm32-metapac/stm32g031c8"] +stm32g031f4 = ["stm32-metapac/stm32g031f4"] +stm32g031f6 = ["stm32-metapac/stm32g031f6"] +stm32g031f8 = ["stm32-metapac/stm32g031f8"] +stm32g031g4 = ["stm32-metapac/stm32g031g4"] +stm32g031g6 = ["stm32-metapac/stm32g031g6"] +stm32g031g8 = ["stm32-metapac/stm32g031g8"] +stm32g031j4 = ["stm32-metapac/stm32g031j4"] +stm32g031j6 = ["stm32-metapac/stm32g031j6"] +stm32g031k4 = ["stm32-metapac/stm32g031k4"] +stm32g031k6 = ["stm32-metapac/stm32g031k6"] +stm32g031k8 = ["stm32-metapac/stm32g031k8"] +stm32g031y8 = ["stm32-metapac/stm32g031y8"] +stm32g041c6 = ["stm32-metapac/stm32g041c6"] +stm32g041c8 = ["stm32-metapac/stm32g041c8"] +stm32g041f6 = ["stm32-metapac/stm32g041f6"] +stm32g041f8 = ["stm32-metapac/stm32g041f8"] +stm32g041g6 = ["stm32-metapac/stm32g041g6"] +stm32g041g8 = ["stm32-metapac/stm32g041g8"] +stm32g041j6 = ["stm32-metapac/stm32g041j6"] +stm32g041k6 = ["stm32-metapac/stm32g041k6"] +stm32g041k8 = ["stm32-metapac/stm32g041k8"] +stm32g041y8 = ["stm32-metapac/stm32g041y8"] +stm32g050c6 = ["stm32-metapac/stm32g050c6"] +stm32g050c8 = ["stm32-metapac/stm32g050c8"] +stm32g050f6 = ["stm32-metapac/stm32g050f6"] +stm32g050k6 = ["stm32-metapac/stm32g050k6"] +stm32g050k8 = ["stm32-metapac/stm32g050k8"] +stm32g051c6 = ["stm32-metapac/stm32g051c6"] +stm32g051c8 = ["stm32-metapac/stm32g051c8"] +stm32g051f6 = ["stm32-metapac/stm32g051f6"] +stm32g051f8 = ["stm32-metapac/stm32g051f8"] +stm32g051g6 = ["stm32-metapac/stm32g051g6"] +stm32g051g8 = ["stm32-metapac/stm32g051g8"] +stm32g051k6 = ["stm32-metapac/stm32g051k6"] +stm32g051k8 = ["stm32-metapac/stm32g051k8"] +stm32g061c6 = ["stm32-metapac/stm32g061c6"] +stm32g061c8 = ["stm32-metapac/stm32g061c8"] +stm32g061f6 = ["stm32-metapac/stm32g061f6"] +stm32g061f8 = ["stm32-metapac/stm32g061f8"] +stm32g061g6 = ["stm32-metapac/stm32g061g6"] +stm32g061g8 = ["stm32-metapac/stm32g061g8"] +stm32g061k6 = ["stm32-metapac/stm32g061k6"] +stm32g061k8 = ["stm32-metapac/stm32g061k8"] +stm32g070cb = ["stm32-metapac/stm32g070cb"] +stm32g070kb = ["stm32-metapac/stm32g070kb"] +stm32g070rb = ["stm32-metapac/stm32g070rb"] +stm32g071c6 = ["stm32-metapac/stm32g071c6"] +stm32g071c8 = ["stm32-metapac/stm32g071c8"] +stm32g071cb = ["stm32-metapac/stm32g071cb"] +stm32g071eb = ["stm32-metapac/stm32g071eb"] +stm32g071g6 = ["stm32-metapac/stm32g071g6"] +stm32g071g8 = ["stm32-metapac/stm32g071g8"] +stm32g071gb = ["stm32-metapac/stm32g071gb"] +stm32g071k6 = ["stm32-metapac/stm32g071k6"] +stm32g071k8 = ["stm32-metapac/stm32g071k8"] +stm32g071kb = ["stm32-metapac/stm32g071kb"] +stm32g071r6 = ["stm32-metapac/stm32g071r6"] +stm32g071r8 = ["stm32-metapac/stm32g071r8"] +stm32g071rb = ["stm32-metapac/stm32g071rb"] +stm32g081cb = ["stm32-metapac/stm32g081cb"] +stm32g081eb = ["stm32-metapac/stm32g081eb"] +stm32g081gb = ["stm32-metapac/stm32g081gb"] +stm32g081kb = ["stm32-metapac/stm32g081kb"] +stm32g081rb = ["stm32-metapac/stm32g081rb"] +stm32g0b0ce = ["stm32-metapac/stm32g0b0ce"] +stm32g0b0ke = ["stm32-metapac/stm32g0b0ke"] +stm32g0b0re = ["stm32-metapac/stm32g0b0re"] +stm32g0b0ve = ["stm32-metapac/stm32g0b0ve"] +stm32g0b1cb = ["stm32-metapac/stm32g0b1cb"] +stm32g0b1cc = ["stm32-metapac/stm32g0b1cc"] +stm32g0b1ce = ["stm32-metapac/stm32g0b1ce"] +stm32g0b1kb = ["stm32-metapac/stm32g0b1kb"] +stm32g0b1kc = ["stm32-metapac/stm32g0b1kc"] +stm32g0b1ke = ["stm32-metapac/stm32g0b1ke"] +stm32g0b1mb = ["stm32-metapac/stm32g0b1mb"] +stm32g0b1mc = ["stm32-metapac/stm32g0b1mc"] +stm32g0b1me = ["stm32-metapac/stm32g0b1me"] +stm32g0b1ne = ["stm32-metapac/stm32g0b1ne"] +stm32g0b1rb = ["stm32-metapac/stm32g0b1rb"] +stm32g0b1rc = ["stm32-metapac/stm32g0b1rc"] +stm32g0b1re = ["stm32-metapac/stm32g0b1re"] +stm32g0b1vb = ["stm32-metapac/stm32g0b1vb"] +stm32g0b1vc = ["stm32-metapac/stm32g0b1vc"] +stm32g0b1ve = ["stm32-metapac/stm32g0b1ve"] +stm32g0c1cc = ["stm32-metapac/stm32g0c1cc"] +stm32g0c1ce = ["stm32-metapac/stm32g0c1ce"] +stm32g0c1kc = ["stm32-metapac/stm32g0c1kc"] +stm32g0c1ke = ["stm32-metapac/stm32g0c1ke"] +stm32g0c1mc = ["stm32-metapac/stm32g0c1mc"] +stm32g0c1me = ["stm32-metapac/stm32g0c1me"] +stm32g0c1ne = ["stm32-metapac/stm32g0c1ne"] +stm32g0c1rc = ["stm32-metapac/stm32g0c1rc"] +stm32g0c1re = ["stm32-metapac/stm32g0c1re"] +stm32g0c1vc = ["stm32-metapac/stm32g0c1vc"] +stm32g0c1ve = ["stm32-metapac/stm32g0c1ve"] +stm32g431c6 = ["stm32-metapac/stm32g431c6"] +stm32g431c8 = ["stm32-metapac/stm32g431c8"] +stm32g431cb = ["stm32-metapac/stm32g431cb"] +stm32g431k6 = ["stm32-metapac/stm32g431k6"] +stm32g431k8 = ["stm32-metapac/stm32g431k8"] +stm32g431kb = ["stm32-metapac/stm32g431kb"] +stm32g431m6 = ["stm32-metapac/stm32g431m6"] +stm32g431m8 = ["stm32-metapac/stm32g431m8"] +stm32g431mb = ["stm32-metapac/stm32g431mb"] +stm32g431r6 = ["stm32-metapac/stm32g431r6"] +stm32g431r8 = ["stm32-metapac/stm32g431r8"] +stm32g431rb = ["stm32-metapac/stm32g431rb"] +stm32g431v6 = ["stm32-metapac/stm32g431v6"] +stm32g431v8 = ["stm32-metapac/stm32g431v8"] +stm32g431vb = ["stm32-metapac/stm32g431vb"] +stm32g441cb = ["stm32-metapac/stm32g441cb"] +stm32g441kb = ["stm32-metapac/stm32g441kb"] +stm32g441mb = ["stm32-metapac/stm32g441mb"] +stm32g441rb = ["stm32-metapac/stm32g441rb"] +stm32g441vb = ["stm32-metapac/stm32g441vb"] +stm32g471cc = ["stm32-metapac/stm32g471cc"] +stm32g471ce = ["stm32-metapac/stm32g471ce"] +stm32g471mc = ["stm32-metapac/stm32g471mc"] +stm32g471me = ["stm32-metapac/stm32g471me"] +stm32g471qc = ["stm32-metapac/stm32g471qc"] +stm32g471qe = ["stm32-metapac/stm32g471qe"] +stm32g471rc = ["stm32-metapac/stm32g471rc"] +stm32g471re = ["stm32-metapac/stm32g471re"] +stm32g471vc = ["stm32-metapac/stm32g471vc"] +stm32g471ve = ["stm32-metapac/stm32g471ve"] +stm32g473cb = ["stm32-metapac/stm32g473cb"] +stm32g473cc = ["stm32-metapac/stm32g473cc"] +stm32g473ce = ["stm32-metapac/stm32g473ce"] +stm32g473mb = ["stm32-metapac/stm32g473mb"] +stm32g473mc = ["stm32-metapac/stm32g473mc"] +stm32g473me = ["stm32-metapac/stm32g473me"] +stm32g473pb = ["stm32-metapac/stm32g473pb"] +stm32g473pc = ["stm32-metapac/stm32g473pc"] +stm32g473pe = ["stm32-metapac/stm32g473pe"] +stm32g473qb = ["stm32-metapac/stm32g473qb"] +stm32g473qc = ["stm32-metapac/stm32g473qc"] +stm32g473qe = ["stm32-metapac/stm32g473qe"] +stm32g473rb = ["stm32-metapac/stm32g473rb"] +stm32g473rc = ["stm32-metapac/stm32g473rc"] +stm32g473re = ["stm32-metapac/stm32g473re"] +stm32g473vb = ["stm32-metapac/stm32g473vb"] +stm32g473vc = ["stm32-metapac/stm32g473vc"] +stm32g473ve = ["stm32-metapac/stm32g473ve"] +stm32g474cb = ["stm32-metapac/stm32g474cb"] +stm32g474cc = ["stm32-metapac/stm32g474cc"] +stm32g474ce = ["stm32-metapac/stm32g474ce"] +stm32g474mb = ["stm32-metapac/stm32g474mb"] +stm32g474mc = ["stm32-metapac/stm32g474mc"] +stm32g474me = ["stm32-metapac/stm32g474me"] +stm32g474pb = ["stm32-metapac/stm32g474pb"] +stm32g474pc = ["stm32-metapac/stm32g474pc"] +stm32g474pe = ["stm32-metapac/stm32g474pe"] +stm32g474qb = ["stm32-metapac/stm32g474qb"] +stm32g474qc = ["stm32-metapac/stm32g474qc"] +stm32g474qe = ["stm32-metapac/stm32g474qe"] +stm32g474rb = ["stm32-metapac/stm32g474rb"] +stm32g474rc = ["stm32-metapac/stm32g474rc"] +stm32g474re = ["stm32-metapac/stm32g474re"] +stm32g474vb = ["stm32-metapac/stm32g474vb"] +stm32g474vc = ["stm32-metapac/stm32g474vc"] +stm32g474ve = ["stm32-metapac/stm32g474ve"] +stm32g483ce = ["stm32-metapac/stm32g483ce"] +stm32g483me = ["stm32-metapac/stm32g483me"] +stm32g483pe = ["stm32-metapac/stm32g483pe"] +stm32g483qe = ["stm32-metapac/stm32g483qe"] +stm32g483re = ["stm32-metapac/stm32g483re"] +stm32g483ve = ["stm32-metapac/stm32g483ve"] +stm32g484ce = ["stm32-metapac/stm32g484ce"] +stm32g484me = ["stm32-metapac/stm32g484me"] +stm32g484pe = ["stm32-metapac/stm32g484pe"] +stm32g484qe = ["stm32-metapac/stm32g484qe"] +stm32g484re = ["stm32-metapac/stm32g484re"] +stm32g484ve = ["stm32-metapac/stm32g484ve"] +stm32g491cc = ["stm32-metapac/stm32g491cc"] +stm32g491ce = ["stm32-metapac/stm32g491ce"] +stm32g491kc = ["stm32-metapac/stm32g491kc"] +stm32g491ke = ["stm32-metapac/stm32g491ke"] +stm32g491mc = ["stm32-metapac/stm32g491mc"] +stm32g491me = ["stm32-metapac/stm32g491me"] +stm32g491rc = ["stm32-metapac/stm32g491rc"] +stm32g491re = ["stm32-metapac/stm32g491re"] +stm32g491vc = ["stm32-metapac/stm32g491vc"] +stm32g491ve = ["stm32-metapac/stm32g491ve"] +stm32g4a1ce = ["stm32-metapac/stm32g4a1ce"] +stm32g4a1ke = ["stm32-metapac/stm32g4a1ke"] +stm32g4a1me = ["stm32-metapac/stm32g4a1me"] +stm32g4a1re = ["stm32-metapac/stm32g4a1re"] +stm32g4a1ve = ["stm32-metapac/stm32g4a1ve"] +stm32h503cb = ["stm32-metapac/stm32h503cb"] +stm32h503eb = ["stm32-metapac/stm32h503eb"] +stm32h503kb = ["stm32-metapac/stm32h503kb"] +stm32h503rb = ["stm32-metapac/stm32h503rb"] +stm32h562ag = ["stm32-metapac/stm32h562ag"] +stm32h562ai = ["stm32-metapac/stm32h562ai"] +stm32h562ig = ["stm32-metapac/stm32h562ig"] +stm32h562ii = ["stm32-metapac/stm32h562ii"] +stm32h562rg = ["stm32-metapac/stm32h562rg"] +stm32h562ri = ["stm32-metapac/stm32h562ri"] +stm32h562vg = ["stm32-metapac/stm32h562vg"] +stm32h562vi = ["stm32-metapac/stm32h562vi"] +stm32h562zg = ["stm32-metapac/stm32h562zg"] +stm32h562zi = ["stm32-metapac/stm32h562zi"] +stm32h563ag = ["stm32-metapac/stm32h563ag"] +stm32h563ai = ["stm32-metapac/stm32h563ai"] +stm32h563ig = ["stm32-metapac/stm32h563ig"] +stm32h563ii = ["stm32-metapac/stm32h563ii"] +stm32h563mi = ["stm32-metapac/stm32h563mi"] +stm32h563rg = ["stm32-metapac/stm32h563rg"] +stm32h563ri = ["stm32-metapac/stm32h563ri"] +stm32h563vg = ["stm32-metapac/stm32h563vg"] +stm32h563vi = ["stm32-metapac/stm32h563vi"] +stm32h563zg = ["stm32-metapac/stm32h563zg"] +stm32h563zi = ["stm32-metapac/stm32h563zi"] +stm32h573ai = ["stm32-metapac/stm32h573ai"] +stm32h573ii = ["stm32-metapac/stm32h573ii"] +stm32h573mi = ["stm32-metapac/stm32h573mi"] +stm32h573ri = ["stm32-metapac/stm32h573ri"] +stm32h573vi = ["stm32-metapac/stm32h573vi"] +stm32h573zi = ["stm32-metapac/stm32h573zi"] +stm32h723ve = ["stm32-metapac/stm32h723ve"] +stm32h723vg = ["stm32-metapac/stm32h723vg"] +stm32h723ze = ["stm32-metapac/stm32h723ze"] +stm32h723zg = ["stm32-metapac/stm32h723zg"] +stm32h725ae = ["stm32-metapac/stm32h725ae"] +stm32h725ag = ["stm32-metapac/stm32h725ag"] +stm32h725ie = ["stm32-metapac/stm32h725ie"] +stm32h725ig = ["stm32-metapac/stm32h725ig"] +stm32h725re = ["stm32-metapac/stm32h725re"] +stm32h725rg = ["stm32-metapac/stm32h725rg"] +stm32h725ve = ["stm32-metapac/stm32h725ve"] +stm32h725vg = ["stm32-metapac/stm32h725vg"] +stm32h725ze = ["stm32-metapac/stm32h725ze"] +stm32h725zg = ["stm32-metapac/stm32h725zg"] +stm32h730ab = ["stm32-metapac/stm32h730ab"] +stm32h730ib = ["stm32-metapac/stm32h730ib"] +stm32h730vb = ["stm32-metapac/stm32h730vb"] +stm32h730zb = ["stm32-metapac/stm32h730zb"] +stm32h733vg = ["stm32-metapac/stm32h733vg"] +stm32h733zg = ["stm32-metapac/stm32h733zg"] +stm32h735ag = ["stm32-metapac/stm32h735ag"] +stm32h735ig = ["stm32-metapac/stm32h735ig"] +stm32h735rg = ["stm32-metapac/stm32h735rg"] +stm32h735vg = ["stm32-metapac/stm32h735vg"] +stm32h735zg = ["stm32-metapac/stm32h735zg"] +stm32h742ag = ["stm32-metapac/stm32h742ag"] +stm32h742ai = ["stm32-metapac/stm32h742ai"] +stm32h742bg = ["stm32-metapac/stm32h742bg"] +stm32h742bi = ["stm32-metapac/stm32h742bi"] +stm32h742ig = ["stm32-metapac/stm32h742ig"] +stm32h742ii = ["stm32-metapac/stm32h742ii"] +stm32h742vg = ["stm32-metapac/stm32h742vg"] +stm32h742vi = ["stm32-metapac/stm32h742vi"] +stm32h742xg = ["stm32-metapac/stm32h742xg"] +stm32h742xi = ["stm32-metapac/stm32h742xi"] +stm32h742zg = ["stm32-metapac/stm32h742zg"] +stm32h742zi = ["stm32-metapac/stm32h742zi"] +stm32h743ag = ["stm32-metapac/stm32h743ag"] +stm32h743ai = ["stm32-metapac/stm32h743ai"] +stm32h743bg = ["stm32-metapac/stm32h743bg"] +stm32h743bi = ["stm32-metapac/stm32h743bi"] +stm32h743ig = ["stm32-metapac/stm32h743ig"] +stm32h743ii = ["stm32-metapac/stm32h743ii"] +stm32h743vg = ["stm32-metapac/stm32h743vg"] +stm32h743vi = ["stm32-metapac/stm32h743vi"] +stm32h743xg = ["stm32-metapac/stm32h743xg"] +stm32h743xi = ["stm32-metapac/stm32h743xi"] +stm32h743zg = ["stm32-metapac/stm32h743zg"] +stm32h743zi = ["stm32-metapac/stm32h743zi"] +stm32h745bg-cm7 = ["stm32-metapac/stm32h745bg-cm7"] +stm32h745bg-cm4 = ["stm32-metapac/stm32h745bg-cm4"] +stm32h745bi-cm7 = ["stm32-metapac/stm32h745bi-cm7"] +stm32h745bi-cm4 = ["stm32-metapac/stm32h745bi-cm4"] +stm32h745ig-cm7 = ["stm32-metapac/stm32h745ig-cm7"] +stm32h745ig-cm4 = ["stm32-metapac/stm32h745ig-cm4"] +stm32h745ii-cm7 = ["stm32-metapac/stm32h745ii-cm7"] +stm32h745ii-cm4 = ["stm32-metapac/stm32h745ii-cm4"] +stm32h745xg-cm7 = ["stm32-metapac/stm32h745xg-cm7"] +stm32h745xg-cm4 = ["stm32-metapac/stm32h745xg-cm4"] +stm32h745xi-cm7 = ["stm32-metapac/stm32h745xi-cm7"] +stm32h745xi-cm4 = ["stm32-metapac/stm32h745xi-cm4"] +stm32h745zg-cm7 = ["stm32-metapac/stm32h745zg-cm7"] +stm32h745zg-cm4 = ["stm32-metapac/stm32h745zg-cm4"] +stm32h745zi-cm7 = ["stm32-metapac/stm32h745zi-cm7"] +stm32h745zi-cm4 = ["stm32-metapac/stm32h745zi-cm4"] +stm32h747ag-cm7 = ["stm32-metapac/stm32h747ag-cm7"] +stm32h747ag-cm4 = ["stm32-metapac/stm32h747ag-cm4"] +stm32h747ai-cm7 = ["stm32-metapac/stm32h747ai-cm7"] +stm32h747ai-cm4 = ["stm32-metapac/stm32h747ai-cm4"] +stm32h747bg-cm7 = ["stm32-metapac/stm32h747bg-cm7"] +stm32h747bg-cm4 = ["stm32-metapac/stm32h747bg-cm4"] +stm32h747bi-cm7 = ["stm32-metapac/stm32h747bi-cm7"] +stm32h747bi-cm4 = ["stm32-metapac/stm32h747bi-cm4"] +stm32h747ig-cm7 = ["stm32-metapac/stm32h747ig-cm7"] +stm32h747ig-cm4 = ["stm32-metapac/stm32h747ig-cm4"] +stm32h747ii-cm7 = ["stm32-metapac/stm32h747ii-cm7"] +stm32h747ii-cm4 = ["stm32-metapac/stm32h747ii-cm4"] +stm32h747xg-cm7 = ["stm32-metapac/stm32h747xg-cm7"] +stm32h747xg-cm4 = ["stm32-metapac/stm32h747xg-cm4"] +stm32h747xi-cm7 = ["stm32-metapac/stm32h747xi-cm7"] +stm32h747xi-cm4 = ["stm32-metapac/stm32h747xi-cm4"] +stm32h747zi-cm7 = ["stm32-metapac/stm32h747zi-cm7"] +stm32h747zi-cm4 = ["stm32-metapac/stm32h747zi-cm4"] +stm32h750ib = ["stm32-metapac/stm32h750ib"] +stm32h750vb = ["stm32-metapac/stm32h750vb"] +stm32h750xb = ["stm32-metapac/stm32h750xb"] +stm32h750zb = ["stm32-metapac/stm32h750zb"] +stm32h753ai = ["stm32-metapac/stm32h753ai"] +stm32h753bi = ["stm32-metapac/stm32h753bi"] +stm32h753ii = ["stm32-metapac/stm32h753ii"] +stm32h753vi = ["stm32-metapac/stm32h753vi"] +stm32h753xi = ["stm32-metapac/stm32h753xi"] +stm32h753zi = ["stm32-metapac/stm32h753zi"] +stm32h755bi-cm7 = ["stm32-metapac/stm32h755bi-cm7"] +stm32h755bi-cm4 = ["stm32-metapac/stm32h755bi-cm4"] +stm32h755ii-cm7 = ["stm32-metapac/stm32h755ii-cm7"] +stm32h755ii-cm4 = ["stm32-metapac/stm32h755ii-cm4"] +stm32h755xi-cm7 = ["stm32-metapac/stm32h755xi-cm7"] +stm32h755xi-cm4 = ["stm32-metapac/stm32h755xi-cm4"] +stm32h755zi-cm7 = ["stm32-metapac/stm32h755zi-cm7"] +stm32h755zi-cm4 = ["stm32-metapac/stm32h755zi-cm4"] +stm32h757ai-cm7 = ["stm32-metapac/stm32h757ai-cm7"] +stm32h757ai-cm4 = ["stm32-metapac/stm32h757ai-cm4"] +stm32h757bi-cm7 = ["stm32-metapac/stm32h757bi-cm7"] +stm32h757bi-cm4 = ["stm32-metapac/stm32h757bi-cm4"] +stm32h757ii-cm7 = ["stm32-metapac/stm32h757ii-cm7"] +stm32h757ii-cm4 = ["stm32-metapac/stm32h757ii-cm4"] +stm32h757xi-cm7 = ["stm32-metapac/stm32h757xi-cm7"] +stm32h757xi-cm4 = ["stm32-metapac/stm32h757xi-cm4"] +stm32h757zi-cm7 = ["stm32-metapac/stm32h757zi-cm7"] +stm32h757zi-cm4 = ["stm32-metapac/stm32h757zi-cm4"] +stm32h7a3ag = ["stm32-metapac/stm32h7a3ag"] +stm32h7a3ai = ["stm32-metapac/stm32h7a3ai"] +stm32h7a3ig = ["stm32-metapac/stm32h7a3ig"] +stm32h7a3ii = ["stm32-metapac/stm32h7a3ii"] +stm32h7a3lg = ["stm32-metapac/stm32h7a3lg"] +stm32h7a3li = ["stm32-metapac/stm32h7a3li"] +stm32h7a3ng = ["stm32-metapac/stm32h7a3ng"] +stm32h7a3ni = ["stm32-metapac/stm32h7a3ni"] +stm32h7a3qi = ["stm32-metapac/stm32h7a3qi"] +stm32h7a3rg = ["stm32-metapac/stm32h7a3rg"] +stm32h7a3ri = ["stm32-metapac/stm32h7a3ri"] +stm32h7a3vg = ["stm32-metapac/stm32h7a3vg"] +stm32h7a3vi = ["stm32-metapac/stm32h7a3vi"] +stm32h7a3zg = ["stm32-metapac/stm32h7a3zg"] +stm32h7a3zi = ["stm32-metapac/stm32h7a3zi"] +stm32h7b0ab = ["stm32-metapac/stm32h7b0ab"] +stm32h7b0ib = ["stm32-metapac/stm32h7b0ib"] +stm32h7b0rb = ["stm32-metapac/stm32h7b0rb"] +stm32h7b0vb = ["stm32-metapac/stm32h7b0vb"] +stm32h7b0zb = ["stm32-metapac/stm32h7b0zb"] +stm32h7b3ai = ["stm32-metapac/stm32h7b3ai"] +stm32h7b3ii = ["stm32-metapac/stm32h7b3ii"] +stm32h7b3li = ["stm32-metapac/stm32h7b3li"] +stm32h7b3ni = ["stm32-metapac/stm32h7b3ni"] +stm32h7b3qi = ["stm32-metapac/stm32h7b3qi"] +stm32h7b3ri = ["stm32-metapac/stm32h7b3ri"] +stm32h7b3vi = ["stm32-metapac/stm32h7b3vi"] +stm32h7b3zi = ["stm32-metapac/stm32h7b3zi"] +stm32l010c6 = ["stm32-metapac/stm32l010c6"] +stm32l010f4 = ["stm32-metapac/stm32l010f4"] +stm32l010k4 = ["stm32-metapac/stm32l010k4"] +stm32l010k8 = ["stm32-metapac/stm32l010k8"] +stm32l010r8 = ["stm32-metapac/stm32l010r8"] +stm32l010rb = ["stm32-metapac/stm32l010rb"] +stm32l011d3 = ["stm32-metapac/stm32l011d3"] +stm32l011d4 = ["stm32-metapac/stm32l011d4"] +stm32l011e3 = ["stm32-metapac/stm32l011e3"] +stm32l011e4 = ["stm32-metapac/stm32l011e4"] +stm32l011f3 = ["stm32-metapac/stm32l011f3"] +stm32l011f4 = ["stm32-metapac/stm32l011f4"] +stm32l011g3 = ["stm32-metapac/stm32l011g3"] +stm32l011g4 = ["stm32-metapac/stm32l011g4"] +stm32l011k3 = ["stm32-metapac/stm32l011k3"] +stm32l011k4 = ["stm32-metapac/stm32l011k4"] +stm32l021d4 = ["stm32-metapac/stm32l021d4"] +stm32l021f4 = ["stm32-metapac/stm32l021f4"] +stm32l021g4 = ["stm32-metapac/stm32l021g4"] +stm32l021k4 = ["stm32-metapac/stm32l021k4"] +stm32l031c4 = ["stm32-metapac/stm32l031c4"] +stm32l031c6 = ["stm32-metapac/stm32l031c6"] +stm32l031e4 = ["stm32-metapac/stm32l031e4"] +stm32l031e6 = ["stm32-metapac/stm32l031e6"] +stm32l031f4 = ["stm32-metapac/stm32l031f4"] +stm32l031f6 = ["stm32-metapac/stm32l031f6"] +stm32l031g4 = ["stm32-metapac/stm32l031g4"] +stm32l031g6 = ["stm32-metapac/stm32l031g6"] +stm32l031k4 = ["stm32-metapac/stm32l031k4"] +stm32l031k6 = ["stm32-metapac/stm32l031k6"] +stm32l041c4 = ["stm32-metapac/stm32l041c4"] +stm32l041c6 = ["stm32-metapac/stm32l041c6"] +stm32l041e6 = ["stm32-metapac/stm32l041e6"] +stm32l041f6 = ["stm32-metapac/stm32l041f6"] +stm32l041g6 = ["stm32-metapac/stm32l041g6"] +stm32l041k6 = ["stm32-metapac/stm32l041k6"] +stm32l051c6 = ["stm32-metapac/stm32l051c6"] +stm32l051c8 = ["stm32-metapac/stm32l051c8"] +stm32l051k6 = ["stm32-metapac/stm32l051k6"] +stm32l051k8 = ["stm32-metapac/stm32l051k8"] +stm32l051r6 = ["stm32-metapac/stm32l051r6"] +stm32l051r8 = ["stm32-metapac/stm32l051r8"] +stm32l051t6 = ["stm32-metapac/stm32l051t6"] +stm32l051t8 = ["stm32-metapac/stm32l051t8"] +stm32l052c6 = ["stm32-metapac/stm32l052c6"] +stm32l052c8 = ["stm32-metapac/stm32l052c8"] +stm32l052k6 = ["stm32-metapac/stm32l052k6"] +stm32l052k8 = ["stm32-metapac/stm32l052k8"] +stm32l052r6 = ["stm32-metapac/stm32l052r6"] +stm32l052r8 = ["stm32-metapac/stm32l052r8"] +stm32l052t6 = ["stm32-metapac/stm32l052t6"] +stm32l052t8 = ["stm32-metapac/stm32l052t8"] +stm32l053c6 = ["stm32-metapac/stm32l053c6"] +stm32l053c8 = ["stm32-metapac/stm32l053c8"] +stm32l053r6 = ["stm32-metapac/stm32l053r6"] +stm32l053r8 = ["stm32-metapac/stm32l053r8"] +stm32l062c8 = ["stm32-metapac/stm32l062c8"] +stm32l062k8 = ["stm32-metapac/stm32l062k8"] +stm32l063c8 = ["stm32-metapac/stm32l063c8"] +stm32l063r8 = ["stm32-metapac/stm32l063r8"] +stm32l071c8 = ["stm32-metapac/stm32l071c8"] +stm32l071cb = ["stm32-metapac/stm32l071cb"] +stm32l071cz = ["stm32-metapac/stm32l071cz"] +stm32l071k8 = ["stm32-metapac/stm32l071k8"] +stm32l071kb = ["stm32-metapac/stm32l071kb"] +stm32l071kz = ["stm32-metapac/stm32l071kz"] +stm32l071rb = ["stm32-metapac/stm32l071rb"] +stm32l071rz = ["stm32-metapac/stm32l071rz"] +stm32l071v8 = ["stm32-metapac/stm32l071v8"] +stm32l071vb = ["stm32-metapac/stm32l071vb"] +stm32l071vz = ["stm32-metapac/stm32l071vz"] +stm32l072cb = ["stm32-metapac/stm32l072cb"] +stm32l072cz = ["stm32-metapac/stm32l072cz"] +stm32l072kb = ["stm32-metapac/stm32l072kb"] +stm32l072kz = ["stm32-metapac/stm32l072kz"] +stm32l072rb = ["stm32-metapac/stm32l072rb"] +stm32l072rz = ["stm32-metapac/stm32l072rz"] +stm32l072v8 = ["stm32-metapac/stm32l072v8"] +stm32l072vb = ["stm32-metapac/stm32l072vb"] +stm32l072vz = ["stm32-metapac/stm32l072vz"] +stm32l073cb = ["stm32-metapac/stm32l073cb"] +stm32l073cz = ["stm32-metapac/stm32l073cz"] +stm32l073rb = ["stm32-metapac/stm32l073rb"] +stm32l073rz = ["stm32-metapac/stm32l073rz"] +stm32l073v8 = ["stm32-metapac/stm32l073v8"] +stm32l073vb = ["stm32-metapac/stm32l073vb"] +stm32l073vz = ["stm32-metapac/stm32l073vz"] +stm32l081cb = ["stm32-metapac/stm32l081cb"] +stm32l081cz = ["stm32-metapac/stm32l081cz"] +stm32l081kz = ["stm32-metapac/stm32l081kz"] +stm32l082cz = ["stm32-metapac/stm32l082cz"] +stm32l082kb = ["stm32-metapac/stm32l082kb"] +stm32l082kz = ["stm32-metapac/stm32l082kz"] +stm32l083cb = ["stm32-metapac/stm32l083cb"] +stm32l083cz = ["stm32-metapac/stm32l083cz"] +stm32l083rb = ["stm32-metapac/stm32l083rb"] +stm32l083rz = ["stm32-metapac/stm32l083rz"] +stm32l083v8 = ["stm32-metapac/stm32l083v8"] +stm32l083vb = ["stm32-metapac/stm32l083vb"] +stm32l083vz = ["stm32-metapac/stm32l083vz"] +stm32l100c6 = ["stm32-metapac/stm32l100c6"] +stm32l100c6-a = ["stm32-metapac/stm32l100c6-a"] +stm32l100r8 = ["stm32-metapac/stm32l100r8"] +stm32l100r8-a = ["stm32-metapac/stm32l100r8-a"] +stm32l100rb = ["stm32-metapac/stm32l100rb"] +stm32l100rb-a = ["stm32-metapac/stm32l100rb-a"] +stm32l100rc = ["stm32-metapac/stm32l100rc"] +stm32l151c6 = ["stm32-metapac/stm32l151c6"] +stm32l151c6-a = ["stm32-metapac/stm32l151c6-a"] +stm32l151c8 = ["stm32-metapac/stm32l151c8"] +stm32l151c8-a = ["stm32-metapac/stm32l151c8-a"] +stm32l151cb = ["stm32-metapac/stm32l151cb"] +stm32l151cb-a = ["stm32-metapac/stm32l151cb-a"] +stm32l151cc = ["stm32-metapac/stm32l151cc"] +stm32l151qc = ["stm32-metapac/stm32l151qc"] +stm32l151qd = ["stm32-metapac/stm32l151qd"] +stm32l151qe = ["stm32-metapac/stm32l151qe"] +stm32l151r6 = ["stm32-metapac/stm32l151r6"] +stm32l151r6-a = ["stm32-metapac/stm32l151r6-a"] +stm32l151r8 = ["stm32-metapac/stm32l151r8"] +stm32l151r8-a = ["stm32-metapac/stm32l151r8-a"] +stm32l151rb = ["stm32-metapac/stm32l151rb"] +stm32l151rb-a = ["stm32-metapac/stm32l151rb-a"] +stm32l151rc = ["stm32-metapac/stm32l151rc"] +stm32l151rc-a = ["stm32-metapac/stm32l151rc-a"] +stm32l151rd = ["stm32-metapac/stm32l151rd"] +stm32l151re = ["stm32-metapac/stm32l151re"] +stm32l151uc = ["stm32-metapac/stm32l151uc"] +stm32l151v8 = ["stm32-metapac/stm32l151v8"] +stm32l151v8-a = ["stm32-metapac/stm32l151v8-a"] +stm32l151vb = ["stm32-metapac/stm32l151vb"] +stm32l151vb-a = ["stm32-metapac/stm32l151vb-a"] +stm32l151vc = ["stm32-metapac/stm32l151vc"] +stm32l151vc-a = ["stm32-metapac/stm32l151vc-a"] +stm32l151vd = ["stm32-metapac/stm32l151vd"] +stm32l151vd-x = ["stm32-metapac/stm32l151vd-x"] +stm32l151ve = ["stm32-metapac/stm32l151ve"] +stm32l151zc = ["stm32-metapac/stm32l151zc"] +stm32l151zd = ["stm32-metapac/stm32l151zd"] +stm32l151ze = ["stm32-metapac/stm32l151ze"] +stm32l152c6 = ["stm32-metapac/stm32l152c6"] +stm32l152c6-a = ["stm32-metapac/stm32l152c6-a"] +stm32l152c8 = ["stm32-metapac/stm32l152c8"] +stm32l152c8-a = ["stm32-metapac/stm32l152c8-a"] +stm32l152cb = ["stm32-metapac/stm32l152cb"] +stm32l152cb-a = ["stm32-metapac/stm32l152cb-a"] +stm32l152cc = ["stm32-metapac/stm32l152cc"] +stm32l152qc = ["stm32-metapac/stm32l152qc"] +stm32l152qd = ["stm32-metapac/stm32l152qd"] +stm32l152qe = ["stm32-metapac/stm32l152qe"] +stm32l152r6 = ["stm32-metapac/stm32l152r6"] +stm32l152r6-a = ["stm32-metapac/stm32l152r6-a"] +stm32l152r8 = ["stm32-metapac/stm32l152r8"] +stm32l152r8-a = ["stm32-metapac/stm32l152r8-a"] +stm32l152rb = ["stm32-metapac/stm32l152rb"] +stm32l152rb-a = ["stm32-metapac/stm32l152rb-a"] +stm32l152rc = ["stm32-metapac/stm32l152rc"] +stm32l152rc-a = ["stm32-metapac/stm32l152rc-a"] +stm32l152rd = ["stm32-metapac/stm32l152rd"] +stm32l152re = ["stm32-metapac/stm32l152re"] +stm32l152uc = ["stm32-metapac/stm32l152uc"] +stm32l152v8 = ["stm32-metapac/stm32l152v8"] +stm32l152v8-a = ["stm32-metapac/stm32l152v8-a"] +stm32l152vb = ["stm32-metapac/stm32l152vb"] +stm32l152vb-a = ["stm32-metapac/stm32l152vb-a"] +stm32l152vc = ["stm32-metapac/stm32l152vc"] +stm32l152vc-a = ["stm32-metapac/stm32l152vc-a"] +stm32l152vd = ["stm32-metapac/stm32l152vd"] +stm32l152vd-x = ["stm32-metapac/stm32l152vd-x"] +stm32l152ve = ["stm32-metapac/stm32l152ve"] +stm32l152zc = ["stm32-metapac/stm32l152zc"] +stm32l152zd = ["stm32-metapac/stm32l152zd"] +stm32l152ze = ["stm32-metapac/stm32l152ze"] +stm32l162qc = ["stm32-metapac/stm32l162qc"] +stm32l162qd = ["stm32-metapac/stm32l162qd"] +stm32l162rc = ["stm32-metapac/stm32l162rc"] +stm32l162rc-a = ["stm32-metapac/stm32l162rc-a"] +stm32l162rd = ["stm32-metapac/stm32l162rd"] +stm32l162re = ["stm32-metapac/stm32l162re"] +stm32l162vc = ["stm32-metapac/stm32l162vc"] +stm32l162vc-a = ["stm32-metapac/stm32l162vc-a"] +stm32l162vd = ["stm32-metapac/stm32l162vd"] +stm32l162vd-x = ["stm32-metapac/stm32l162vd-x"] +stm32l162ve = ["stm32-metapac/stm32l162ve"] +stm32l162zc = ["stm32-metapac/stm32l162zc"] +stm32l162zd = ["stm32-metapac/stm32l162zd"] +stm32l162ze = ["stm32-metapac/stm32l162ze"] +stm32l412c8 = ["stm32-metapac/stm32l412c8"] +stm32l412cb = ["stm32-metapac/stm32l412cb"] +stm32l412k8 = ["stm32-metapac/stm32l412k8"] +stm32l412kb = ["stm32-metapac/stm32l412kb"] +stm32l412r8 = ["stm32-metapac/stm32l412r8"] +stm32l412rb = ["stm32-metapac/stm32l412rb"] +stm32l412t8 = ["stm32-metapac/stm32l412t8"] +stm32l412tb = ["stm32-metapac/stm32l412tb"] +stm32l422cb = ["stm32-metapac/stm32l422cb"] +stm32l422kb = ["stm32-metapac/stm32l422kb"] +stm32l422rb = ["stm32-metapac/stm32l422rb"] +stm32l422tb = ["stm32-metapac/stm32l422tb"] +stm32l431cb = ["stm32-metapac/stm32l431cb"] +stm32l431cc = ["stm32-metapac/stm32l431cc"] +stm32l431kb = ["stm32-metapac/stm32l431kb"] +stm32l431kc = ["stm32-metapac/stm32l431kc"] +stm32l431rb = ["stm32-metapac/stm32l431rb"] +stm32l431rc = ["stm32-metapac/stm32l431rc"] +stm32l431vc = ["stm32-metapac/stm32l431vc"] +stm32l432kb = ["stm32-metapac/stm32l432kb"] +stm32l432kc = ["stm32-metapac/stm32l432kc"] +stm32l433cb = ["stm32-metapac/stm32l433cb"] +stm32l433cc = ["stm32-metapac/stm32l433cc"] +stm32l433rb = ["stm32-metapac/stm32l433rb"] +stm32l433rc = ["stm32-metapac/stm32l433rc"] +stm32l433vc = ["stm32-metapac/stm32l433vc"] +stm32l442kc = ["stm32-metapac/stm32l442kc"] +stm32l443cc = ["stm32-metapac/stm32l443cc"] +stm32l443rc = ["stm32-metapac/stm32l443rc"] +stm32l443vc = ["stm32-metapac/stm32l443vc"] +stm32l451cc = ["stm32-metapac/stm32l451cc"] +stm32l451ce = ["stm32-metapac/stm32l451ce"] +stm32l451rc = ["stm32-metapac/stm32l451rc"] +stm32l451re = ["stm32-metapac/stm32l451re"] +stm32l451vc = ["stm32-metapac/stm32l451vc"] +stm32l451ve = ["stm32-metapac/stm32l451ve"] +stm32l452cc = ["stm32-metapac/stm32l452cc"] +stm32l452ce = ["stm32-metapac/stm32l452ce"] +stm32l452rc = ["stm32-metapac/stm32l452rc"] +stm32l452re = ["stm32-metapac/stm32l452re"] +stm32l452vc = ["stm32-metapac/stm32l452vc"] +stm32l452ve = ["stm32-metapac/stm32l452ve"] +stm32l462ce = ["stm32-metapac/stm32l462ce"] +stm32l462re = ["stm32-metapac/stm32l462re"] +stm32l462ve = ["stm32-metapac/stm32l462ve"] +stm32l471qe = ["stm32-metapac/stm32l471qe"] +stm32l471qg = ["stm32-metapac/stm32l471qg"] +stm32l471re = ["stm32-metapac/stm32l471re"] +stm32l471rg = ["stm32-metapac/stm32l471rg"] +stm32l471ve = ["stm32-metapac/stm32l471ve"] +stm32l471vg = ["stm32-metapac/stm32l471vg"] +stm32l471ze = ["stm32-metapac/stm32l471ze"] +stm32l471zg = ["stm32-metapac/stm32l471zg"] +stm32l475rc = ["stm32-metapac/stm32l475rc"] +stm32l475re = ["stm32-metapac/stm32l475re"] +stm32l475rg = ["stm32-metapac/stm32l475rg"] +stm32l475vc = ["stm32-metapac/stm32l475vc"] +stm32l475ve = ["stm32-metapac/stm32l475ve"] +stm32l475vg = ["stm32-metapac/stm32l475vg"] +stm32l476je = ["stm32-metapac/stm32l476je"] +stm32l476jg = ["stm32-metapac/stm32l476jg"] +stm32l476me = ["stm32-metapac/stm32l476me"] +stm32l476mg = ["stm32-metapac/stm32l476mg"] +stm32l476qe = ["stm32-metapac/stm32l476qe"] +stm32l476qg = ["stm32-metapac/stm32l476qg"] +stm32l476rc = ["stm32-metapac/stm32l476rc"] +stm32l476re = ["stm32-metapac/stm32l476re"] +stm32l476rg = ["stm32-metapac/stm32l476rg"] +stm32l476vc = ["stm32-metapac/stm32l476vc"] +stm32l476ve = ["stm32-metapac/stm32l476ve"] +stm32l476vg = ["stm32-metapac/stm32l476vg"] +stm32l476ze = ["stm32-metapac/stm32l476ze"] +stm32l476zg = ["stm32-metapac/stm32l476zg"] +stm32l486jg = ["stm32-metapac/stm32l486jg"] +stm32l486qg = ["stm32-metapac/stm32l486qg"] +stm32l486rg = ["stm32-metapac/stm32l486rg"] +stm32l486vg = ["stm32-metapac/stm32l486vg"] +stm32l486zg = ["stm32-metapac/stm32l486zg"] +stm32l496ae = ["stm32-metapac/stm32l496ae"] +stm32l496ag = ["stm32-metapac/stm32l496ag"] +stm32l496qe = ["stm32-metapac/stm32l496qe"] +stm32l496qg = ["stm32-metapac/stm32l496qg"] +stm32l496re = ["stm32-metapac/stm32l496re"] +stm32l496rg = ["stm32-metapac/stm32l496rg"] +stm32l496ve = ["stm32-metapac/stm32l496ve"] +stm32l496vg = ["stm32-metapac/stm32l496vg"] +stm32l496wg = ["stm32-metapac/stm32l496wg"] +stm32l496ze = ["stm32-metapac/stm32l496ze"] +stm32l496zg = ["stm32-metapac/stm32l496zg"] +stm32l4a6ag = ["stm32-metapac/stm32l4a6ag"] +stm32l4a6qg = ["stm32-metapac/stm32l4a6qg"] +stm32l4a6rg = ["stm32-metapac/stm32l4a6rg"] +stm32l4a6vg = ["stm32-metapac/stm32l4a6vg"] +stm32l4a6zg = ["stm32-metapac/stm32l4a6zg"] +stm32l4p5ae = ["stm32-metapac/stm32l4p5ae"] +stm32l4p5ag = ["stm32-metapac/stm32l4p5ag"] +stm32l4p5ce = ["stm32-metapac/stm32l4p5ce"] +stm32l4p5cg = ["stm32-metapac/stm32l4p5cg"] +stm32l4p5qe = ["stm32-metapac/stm32l4p5qe"] +stm32l4p5qg = ["stm32-metapac/stm32l4p5qg"] +stm32l4p5re = ["stm32-metapac/stm32l4p5re"] +stm32l4p5rg = ["stm32-metapac/stm32l4p5rg"] +stm32l4p5ve = ["stm32-metapac/stm32l4p5ve"] +stm32l4p5vg = ["stm32-metapac/stm32l4p5vg"] +stm32l4p5ze = ["stm32-metapac/stm32l4p5ze"] +stm32l4p5zg = ["stm32-metapac/stm32l4p5zg"] +stm32l4q5ag = ["stm32-metapac/stm32l4q5ag"] +stm32l4q5cg = ["stm32-metapac/stm32l4q5cg"] +stm32l4q5qg = ["stm32-metapac/stm32l4q5qg"] +stm32l4q5rg = ["stm32-metapac/stm32l4q5rg"] +stm32l4q5vg = ["stm32-metapac/stm32l4q5vg"] +stm32l4q5zg = ["stm32-metapac/stm32l4q5zg"] +stm32l4r5ag = ["stm32-metapac/stm32l4r5ag"] +stm32l4r5ai = ["stm32-metapac/stm32l4r5ai"] +stm32l4r5qg = ["stm32-metapac/stm32l4r5qg"] +stm32l4r5qi = ["stm32-metapac/stm32l4r5qi"] +stm32l4r5vg = ["stm32-metapac/stm32l4r5vg"] +stm32l4r5vi = ["stm32-metapac/stm32l4r5vi"] +stm32l4r5zg = ["stm32-metapac/stm32l4r5zg"] +stm32l4r5zi = ["stm32-metapac/stm32l4r5zi"] +stm32l4r7ai = ["stm32-metapac/stm32l4r7ai"] +stm32l4r7vi = ["stm32-metapac/stm32l4r7vi"] +stm32l4r7zi = ["stm32-metapac/stm32l4r7zi"] +stm32l4r9ag = ["stm32-metapac/stm32l4r9ag"] +stm32l4r9ai = ["stm32-metapac/stm32l4r9ai"] +stm32l4r9vg = ["stm32-metapac/stm32l4r9vg"] +stm32l4r9vi = ["stm32-metapac/stm32l4r9vi"] +stm32l4r9zg = ["stm32-metapac/stm32l4r9zg"] +stm32l4r9zi = ["stm32-metapac/stm32l4r9zi"] +stm32l4s5ai = ["stm32-metapac/stm32l4s5ai"] +stm32l4s5qi = ["stm32-metapac/stm32l4s5qi"] +stm32l4s5vi = ["stm32-metapac/stm32l4s5vi"] +stm32l4s5zi = ["stm32-metapac/stm32l4s5zi"] +stm32l4s7ai = ["stm32-metapac/stm32l4s7ai"] +stm32l4s7vi = ["stm32-metapac/stm32l4s7vi"] +stm32l4s7zi = ["stm32-metapac/stm32l4s7zi"] +stm32l4s9ai = ["stm32-metapac/stm32l4s9ai"] +stm32l4s9vi = ["stm32-metapac/stm32l4s9vi"] +stm32l4s9zi = ["stm32-metapac/stm32l4s9zi"] +stm32l552cc = ["stm32-metapac/stm32l552cc"] +stm32l552ce = ["stm32-metapac/stm32l552ce"] +stm32l552me = ["stm32-metapac/stm32l552me"] +stm32l552qc = ["stm32-metapac/stm32l552qc"] +stm32l552qe = ["stm32-metapac/stm32l552qe"] +stm32l552rc = ["stm32-metapac/stm32l552rc"] +stm32l552re = ["stm32-metapac/stm32l552re"] +stm32l552vc = ["stm32-metapac/stm32l552vc"] +stm32l552ve = ["stm32-metapac/stm32l552ve"] +stm32l552zc = ["stm32-metapac/stm32l552zc"] +stm32l552ze = ["stm32-metapac/stm32l552ze"] +stm32l562ce = ["stm32-metapac/stm32l562ce"] +stm32l562me = ["stm32-metapac/stm32l562me"] +stm32l562qe = ["stm32-metapac/stm32l562qe"] +stm32l562re = ["stm32-metapac/stm32l562re"] +stm32l562ve = ["stm32-metapac/stm32l562ve"] +stm32l562ze = ["stm32-metapac/stm32l562ze"] +stm32u535cb = ["stm32-metapac/stm32u535cb"] +stm32u535cc = ["stm32-metapac/stm32u535cc"] +stm32u535ce = ["stm32-metapac/stm32u535ce"] +stm32u535je = ["stm32-metapac/stm32u535je"] +stm32u535nc = ["stm32-metapac/stm32u535nc"] +stm32u535ne = ["stm32-metapac/stm32u535ne"] +stm32u535rb = ["stm32-metapac/stm32u535rb"] +stm32u535rc = ["stm32-metapac/stm32u535rc"] +stm32u535re = ["stm32-metapac/stm32u535re"] +stm32u535vc = ["stm32-metapac/stm32u535vc"] +stm32u535ve = ["stm32-metapac/stm32u535ve"] +stm32u545ce = ["stm32-metapac/stm32u545ce"] +stm32u545je = ["stm32-metapac/stm32u545je"] +stm32u545ne = ["stm32-metapac/stm32u545ne"] +stm32u545re = ["stm32-metapac/stm32u545re"] +stm32u545ve = ["stm32-metapac/stm32u545ve"] +stm32u575ag = ["stm32-metapac/stm32u575ag"] +stm32u575ai = ["stm32-metapac/stm32u575ai"] +stm32u575cg = ["stm32-metapac/stm32u575cg"] +stm32u575ci = ["stm32-metapac/stm32u575ci"] +stm32u575og = ["stm32-metapac/stm32u575og"] +stm32u575oi = ["stm32-metapac/stm32u575oi"] +stm32u575qg = ["stm32-metapac/stm32u575qg"] +stm32u575qi = ["stm32-metapac/stm32u575qi"] +stm32u575rg = ["stm32-metapac/stm32u575rg"] +stm32u575ri = ["stm32-metapac/stm32u575ri"] +stm32u575vg = ["stm32-metapac/stm32u575vg"] +stm32u575vi = ["stm32-metapac/stm32u575vi"] +stm32u575zg = ["stm32-metapac/stm32u575zg"] +stm32u575zi = ["stm32-metapac/stm32u575zi"] +stm32u585ai = ["stm32-metapac/stm32u585ai"] +stm32u585ci = ["stm32-metapac/stm32u585ci"] +stm32u585oi = ["stm32-metapac/stm32u585oi"] +stm32u585qi = ["stm32-metapac/stm32u585qi"] +stm32u585ri = ["stm32-metapac/stm32u585ri"] +stm32u585vi = ["stm32-metapac/stm32u585vi"] +stm32u585zi = ["stm32-metapac/stm32u585zi"] +stm32u595ai = ["stm32-metapac/stm32u595ai"] +stm32u595aj = ["stm32-metapac/stm32u595aj"] +stm32u595qi = ["stm32-metapac/stm32u595qi"] +stm32u595qj = ["stm32-metapac/stm32u595qj"] +stm32u595ri = ["stm32-metapac/stm32u595ri"] +stm32u595rj = ["stm32-metapac/stm32u595rj"] +stm32u595vi = ["stm32-metapac/stm32u595vi"] +stm32u595vj = ["stm32-metapac/stm32u595vj"] +stm32u595zi = ["stm32-metapac/stm32u595zi"] +stm32u595zj = ["stm32-metapac/stm32u595zj"] +stm32u599bj = ["stm32-metapac/stm32u599bj"] +stm32u599ni = ["stm32-metapac/stm32u599ni"] +stm32u599nj = ["stm32-metapac/stm32u599nj"] +stm32u599vi = ["stm32-metapac/stm32u599vi"] +stm32u599vj = ["stm32-metapac/stm32u599vj"] +stm32u599zi = ["stm32-metapac/stm32u599zi"] +stm32u599zj = ["stm32-metapac/stm32u599zj"] +stm32u5a5aj = ["stm32-metapac/stm32u5a5aj"] +stm32u5a5qj = ["stm32-metapac/stm32u5a5qj"] +stm32u5a5rj = ["stm32-metapac/stm32u5a5rj"] +stm32u5a5vj = ["stm32-metapac/stm32u5a5vj"] +stm32u5a5zj = ["stm32-metapac/stm32u5a5zj"] +stm32u5a9bj = ["stm32-metapac/stm32u5a9bj"] +stm32u5a9nj = ["stm32-metapac/stm32u5a9nj"] +stm32u5a9vj = ["stm32-metapac/stm32u5a9vj"] +stm32u5a9zj = ["stm32-metapac/stm32u5a9zj"] +stm32wb10cc = ["stm32-metapac/stm32wb10cc"] +stm32wb15cc = ["stm32-metapac/stm32wb15cc"] +stm32wb30ce = ["stm32-metapac/stm32wb30ce"] +stm32wb35cc = ["stm32-metapac/stm32wb35cc"] +stm32wb35ce = ["stm32-metapac/stm32wb35ce"] +stm32wb50cg = ["stm32-metapac/stm32wb50cg"] +stm32wb55cc = ["stm32-metapac/stm32wb55cc"] +stm32wb55ce = ["stm32-metapac/stm32wb55ce"] +stm32wb55cg = ["stm32-metapac/stm32wb55cg"] +stm32wb55rc = ["stm32-metapac/stm32wb55rc"] +stm32wb55re = ["stm32-metapac/stm32wb55re"] +stm32wb55rg = ["stm32-metapac/stm32wb55rg"] +stm32wb55vc = ["stm32-metapac/stm32wb55vc"] +stm32wb55ve = ["stm32-metapac/stm32wb55ve"] +stm32wb55vg = ["stm32-metapac/stm32wb55vg"] +stm32wb55vy = ["stm32-metapac/stm32wb55vy"] +stm32wl54cc-cm4 = ["stm32-metapac/stm32wl54cc-cm4"] +stm32wl54cc-cm0p = ["stm32-metapac/stm32wl54cc-cm0p"] +stm32wl54jc-cm4 = ["stm32-metapac/stm32wl54jc-cm4"] +stm32wl54jc-cm0p = ["stm32-metapac/stm32wl54jc-cm0p"] +stm32wl55cc-cm4 = ["stm32-metapac/stm32wl55cc-cm4"] +stm32wl55cc-cm0p = ["stm32-metapac/stm32wl55cc-cm0p"] +stm32wl55jc-cm4 = ["stm32-metapac/stm32wl55jc-cm4"] +stm32wl55jc-cm0p = ["stm32-metapac/stm32wl55jc-cm0p"] +stm32wle4c8 = ["stm32-metapac/stm32wle4c8"] +stm32wle4cb = ["stm32-metapac/stm32wle4cb"] +stm32wle4cc = ["stm32-metapac/stm32wle4cc"] +stm32wle4j8 = ["stm32-metapac/stm32wle4j8"] +stm32wle4jb = ["stm32-metapac/stm32wle4jb"] +stm32wle4jc = ["stm32-metapac/stm32wle4jc"] +stm32wle5c8 = ["stm32-metapac/stm32wle5c8"] +stm32wle5cb = ["stm32-metapac/stm32wle5cb"] +stm32wle5cc = ["stm32-metapac/stm32wle5cc"] +stm32wle5j8 = ["stm32-metapac/stm32wle5j8"] +stm32wle5jb = ["stm32-metapac/stm32wle5jb"] +stm32wle5jc = ["stm32-metapac/stm32wle5jc"] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 11820b7a0..b3dbe1e2f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -57,6 +57,8 @@ pub mod rtc; pub mod sdmmc; #[cfg(spi)] pub mod spi; +#[cfg(stm32wb)] +pub mod tl_mbox; #[cfg(usart)] pub mod usart; #[cfg(all(usb, feature = "time"))] diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs new file mode 100644 index 000000000..a2c0758d1 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -0,0 +1,26 @@ +use core::mem::MaybeUninit; + +use super::unsafe_linked_list::LST_init_head; +use super::{channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; +use crate::ipcc::Ipcc; + +pub struct Ble; + +impl Ble { + pub fn new(ipcc: &mut Ipcc) -> Self { + unsafe { + LST_init_head(EVT_QUEUE.as_mut_ptr()); + + TL_BLE_TABLE = MaybeUninit::new(BleTable { + pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), + pcs_buffer: CS_BUFFER.as_mut_ptr().cast(), + pevt_queue: EVT_QUEUE.as_ptr().cast(), + phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(), + }); + } + + ipcc.c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); + + Ble + } +} diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs new file mode 100644 index 000000000..1dde5d61c --- /dev/null +++ b/embassy-stm32/src/tl_mbox/channels.rs @@ -0,0 +1,104 @@ +//! CPU1 CPU2 +//! | (SYSTEM) | +//! |----HW_IPCC_SYSTEM_CMD_RSP_CHANNEL-------------->| +//! | | +//! |<---HW_IPCC_SYSTEM_EVENT_CHANNEL-----------------| +//! | | +//! | (ZIGBEE) | +//! |----HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL------------>| +//! | | +//! |----HW_IPCC_ZIGBEE_CMD_CLI_CHANNEL-------------->| +//! | | +//! |<---HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL-------| +//! | | +//! |<---HW_IPCC_ZIGBEE_CLI_NOTIF_ACK_CHANNEL---------| +//! | | +//! | (THREAD) | +//! |----HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL----------->| +//! | | +//! |----HW_IPCC_THREAD_CLI_CMD_CHANNEL-------------->| +//! | | +//! |<---HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL------| +//! | | +//! |<---HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL--| +//! | | +//! | (BLE) | +//! |----HW_IPCC_BLE_CMD_CHANNEL--------------------->| +//! | | +//! |----HW_IPCC_HCI_ACL_DATA_CHANNEL---------------->| +//! | | +//! |<---HW_IPCC_BLE_EVENT_CHANNEL--------------------| +//! | | +//! | (BLE LLD) | +//! |----HW_IPCC_BLE_LLD_CMD_CHANNEL----------------->| +//! | | +//! |<---HW_IPCC_BLE_LLD_RSP_CHANNEL------------------| +//! | | +//! |<---HW_IPCC_BLE_LLD_M0_CMD_CHANNEL---------------| +//! | | +//! | (MAC) | +//! |----HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL-------->| +//! | | +//! |<---HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL| +//! | | +//! | (BUFFER) | +//! |----HW_IPCC_MM_RELEASE_BUFFER_CHANNE------------>| +//! | | +//! | (TRACE) | +//! |<----HW_IPCC_TRACES_CHANNEL----------------------| +//! | | +//! + +pub mod cpu1 { + use crate::ipcc::IpccChannel; + + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6; +} + +pub mod cpu2 { + use crate::ipcc::IpccChannel; + + pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; + pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5; +} diff --git a/embassy-stm32/src/tl_mbox/cmd.rs b/embassy-stm32/src/tl_mbox/cmd.rs new file mode 100644 index 000000000..3507c3231 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/cmd.rs @@ -0,0 +1,49 @@ +use super::PacketHeader; + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct Cmd { + pub cmd_code: u16, + pub payload_len: u8, + pub payload: [u8; 255], +} + +impl Default for Cmd { + fn default() -> Self { + Self { + cmd_code: 0, + payload_len: 0, + payload: [0u8; 255], + } + } +} + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct CmdSerial { + pub ty: u8, + pub cmd: Cmd, +} + +#[repr(C, packed)] +#[derive(Copy, Clone, Default)] +pub struct CmdPacket { + pub header: PacketHeader, + pub cmd_serial: CmdSerial, +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct AclDataSerial { + pub ty: u8, + pub handle: u16, + pub length: u16, + pub acl_data: [u8; 1], +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct AclDataPacket { + pub header: PacketHeader, + pub acl_data_serial: AclDataSerial, +} diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs new file mode 100644 index 000000000..4244db810 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -0,0 +1,8 @@ +/// the payload of [`Evt`] for a command status event +#[derive(Copy, Clone)] +#[repr(C, packed)] +pub struct CsEvt { + pub status: u8, + pub num_cmd: u8, + pub cmd_code: u16, +} diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs new file mode 100644 index 000000000..cf4797305 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -0,0 +1,30 @@ +use core::mem::MaybeUninit; + +use super::unsafe_linked_list::LST_init_head; +use super::{ + MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, + TL_MEM_MANAGER_TABLE, +}; + +pub struct MemoryManager; + +impl MemoryManager { + pub fn new() -> Self { + unsafe { + LST_init_head(FREE_BUFF_QUEUE.as_mut_ptr()); + LST_init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); + + TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable { + spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), + spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), + ble_pool: EVT_POOL.as_ptr().cast(), + ble_pool_size: POOL_SIZE as u32, + pevt_free_buffer_queue: FREE_BUFF_QUEUE.as_mut_ptr(), + traces_evt_pool: core::ptr::null(), + traces_pool_size: 0, + }); + } + + MemoryManager + } +} diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs new file mode 100644 index 000000000..623546dc9 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -0,0 +1,318 @@ +use core::mem::MaybeUninit; + +use bit_field::BitField; + +use self::ble::Ble; +use self::cmd::{AclDataPacket, CmdPacket}; +use self::evt::CsEvt; +use self::mm::MemoryManager; +use self::sys::Sys; +use self::unsafe_linked_list::LinkedListNode; +use crate::ipcc::Ipcc; + +mod ble; +mod channels; +mod cmd; +mod evt; +mod mm; +mod sys; +mod unsafe_linked_list; + +pub type PacketHeader = LinkedListNode; + +const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); +const TL_EVT_HEADER_SIZE: usize = 3; +const TL_CS_EVT_SIZE: usize = core::mem::size_of::(); + +const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5; +const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; +const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE; + +const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); + +const fn divc(x: usize, y: usize) -> usize { + (x + y - 1) / y +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct SafeBootInfoTable { + version: u32, +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct RssInfoTable { + version: u32, + memory_size: u32, + rss_info: u32, +} + +/// # Version +/// - 0 -> 3 = Build - 0: Untracked - 15:Released - x: Tracked version +/// - 4 -> 7 = branch - 0: Mass Market - x: ... +/// - 8 -> 15 = Subversion +/// - 16 -> 23 = Version minor +/// - 24 -> 31 = Version major +/// # Memory Size +/// - 0 -> 7 = Flash ( Number of 4k sector) +/// - 8 -> 15 = Reserved ( Shall be set to 0 - may be used as flash extension ) +/// - 16 -> 23 = SRAM2b ( Number of 1k sector) +/// - 24 -> 31 = SRAM2a ( Number of 1k sector) +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct WirelessFwInfoTable { + version: u32, + memory_size: u32, + thread_info: u32, + ble_info: u32, +} + +impl WirelessFwInfoTable { + pub fn version_major(&self) -> u8 { + let version = self.version; + (version.get_bits(24..31) & 0xff) as u8 + } + + pub fn version_minor(&self) -> u8 { + let version = self.version; + (version.get_bits(16..23) & 0xff) as u8 + } + + pub fn subversion(&self) -> u8 { + let version = self.version; + (version.get_bits(8..15) & 0xff) as u8 + } + + /// size of FLASH, expressed in number of 4K sectors + pub fn flash_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.get_bits(0..7) & 0xff) as u8 + } + + /// size for SRAM2a, expressed in number of 1K sectors + pub fn sram2a_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.get_bits(24..31) & 0xff) as u8 + } + + /// size of SRAM2b, expressed in number of 1K sectors + pub fn sram2b_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.get_bits(16..23) & 0xff) as u8 + } +} + +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct DeviceInfoTable { + pub safe_boot_info_table: SafeBootInfoTable, + pub rss_info_table: RssInfoTable, + pub wireless_fw_info_table: WirelessFwInfoTable, +} + +#[repr(C, packed)] +struct BleTable { + pcmd_buffer: *const CmdPacket, + pcs_buffer: *const u8, + pevt_queue: *const u8, + phci_acl_data_buffer: *mut AclDataPacket, +} + +#[repr(C, packed)] +struct ThreadTable { + no_stack_buffer: *const u8, + cli_cmd_rsp_buffer: *const u8, + ot_cmd_rsp_buffer: *const u8, +} + +#[repr(C, packed)] +struct SysTable { + pcmd_buffer: *mut CmdPacket, + sys_queue: *const LinkedListNode, +} + +#[allow(dead_code)] // Not used currently but reserved +#[repr(C, packed)] +struct LldTestTable { + cli_cmd_rsp_buffer: *const u8, + m0_cmd_buffer: *const u8, +} + +#[allow(dead_code)] // Not used currently but reserved +#[repr(C, packed)] +struct BleLldTable { + cmd_rsp_buffer: *const u8, + m0_cmd_buffer: *const u8, +} + +#[allow(dead_code)] // Not used currently but reserved +#[repr(C, packed)] +struct ZigbeeTable { + notif_m0_to_m4_buffer: *const u8, + appli_cmd_m4_to_m0_buffer: *const u8, + request_m0_to_m4_buffer: *const u8, +} + +#[repr(C, packed)] +struct MemManagerTable { + spare_ble_buffer: *const u8, + spare_sys_buffer: *const u8, + + ble_pool: *const u8, + ble_pool_size: u32, + + pevt_free_buffer_queue: *mut LinkedListNode, + + traces_evt_pool: *const u8, + traces_pool_size: u32, +} + +#[repr(C, packed)] +struct TracesTable { + traces_queue: *const u8, +} + +#[repr(C, packed)] +struct Mac802_15_4Table { + pcmd_rsp_buffer: *const u8, + pnotack_buffer: *const u8, + evt_queue: *const u8, +} + +/// reference table. Contains pointers to all other tables +#[repr(C, packed)] +#[derive(Copy, Clone)] +pub struct RefTable { + pub device_info_table: *const DeviceInfoTable, + ble_table: *const BleTable, + thread_table: *const ThreadTable, + sys_table: *const SysTable, + mem_manager_table: *const MemManagerTable, + traces_table: *const TracesTable, + mac_802_15_4_table: *const Mac802_15_4Table, +} + +#[link_section = "TL_REF_TABLE"] +pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "TL_DEVICE_INFO_TABLE"] +static mut TL_DEVICE_INFO_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "TL_BLE_TABLE"] +static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "TL_THREAD_TABLE"] +static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "TL_SYS_TABLE"] +static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "TL_MEM_MANAGER_TABLE"] +static mut TL_MEM_MANAGER_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "TL_TRACES_TABLE"] +static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "TL_MAC_802_15_4_TABLE"] +static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[allow(dead_code)] // Not used currently but reserved +#[link_section = "FREE_BUF_QUEUE"] +static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +// not in shared RAM +static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[allow(dead_code)] // Not used currently but reserved +#[link_section = "TRACES_EVT_QUEUE"] +static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "CS_BUFFER"] +static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = + MaybeUninit::uninit(); + +#[link_section = "EVT_QUEUE"] +static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "SYSTEM_EVT_QUEUE"] +static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "SYS_CMD_BUF"] +static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "EVT_POOL"] +static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); + +#[link_section = "SYS_SPARE_EVT_BUF"] +static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + +#[link_section = "BLE_SPARE_EVT_BUF"] +static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + +#[link_section = "BLE_CMD_BUFFER"] +static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "HCI_ACL_DATA_BUFFER"] +// "magic" numbers from ST ---v---v +static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); + +pub struct TlMbox { + _sys: Sys, + _ble: Ble, + _mm: MemoryManager, +} + +impl TlMbox { + /// initializes low-level transport between CPU1 and BLE stack on CPU2 + pub fn init(ipcc: &mut Ipcc) -> TlMbox { + unsafe { + TL_REF_TABLE = MaybeUninit::new(RefTable { + device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), + ble_table: TL_BLE_TABLE.as_ptr(), + thread_table: TL_THREAD_TABLE.as_ptr(), + sys_table: TL_SYS_TABLE.as_ptr(), + mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), + traces_table: TL_TRACES_TABLE.as_ptr(), + mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), + }); + + TL_SYS_TABLE = MaybeUninit::zeroed(); + TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed(); + TL_BLE_TABLE = MaybeUninit::zeroed(); + TL_THREAD_TABLE = MaybeUninit::zeroed(); + TL_MEM_MANAGER_TABLE = MaybeUninit::zeroed(); + TL_TRACES_TABLE = MaybeUninit::zeroed(); + TL_MAC_802_15_4_TABLE = MaybeUninit::zeroed(); + + EVT_POOL = MaybeUninit::zeroed(); + SYS_SPARE_EVT_BUF = MaybeUninit::zeroed(); + BLE_SPARE_EVT_BUF = MaybeUninit::zeroed(); + + CS_BUFFER = MaybeUninit::zeroed(); + BLE_CMD_BUFFER = MaybeUninit::zeroed(); + HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed(); + } + + ipcc.init(); + + let _sys = Sys::new(ipcc); + let _ble = Ble::new(ipcc); + let _mm = MemoryManager::new(); + + TlMbox { _sys, _ble, _mm } + } + + pub fn wireless_fw_info(&self) -> Option { + let info = unsafe { &(*(*TL_REF_TABLE.as_ptr()).device_info_table).wireless_fw_info_table }; + + // zero version indicates that CPU2 wasn't active and didn't fill the information table + if info.version != 0 { + Some(*info) + } else { + None + } + } +} diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs new file mode 100644 index 000000000..13ae7f9f9 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -0,0 +1,24 @@ +use core::mem::MaybeUninit; + +use super::unsafe_linked_list::LST_init_head; +use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; +use crate::ipcc::Ipcc; + +pub struct Sys; + +impl Sys { + pub fn new(ipcc: &mut Ipcc) -> Self { + unsafe { + LST_init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); + + TL_SYS_TABLE = MaybeUninit::new(SysTable { + pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), + sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), + }); + } + + ipcc.c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); + + Sys + } +} diff --git a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs new file mode 100644 index 000000000..9caf01d1d --- /dev/null +++ b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs @@ -0,0 +1,123 @@ +//! Unsafe linked list. +//! Translated from ST's C by `c2rust` tool. + +#![allow( + dead_code, + mutable_transmutes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] + +use cortex_m::interrupt; + +#[derive(Copy, Clone)] +#[repr(C, packed(4))] +pub struct LinkedListNode { + pub next: *mut LinkedListNode, + pub prev: *mut LinkedListNode, +} + +impl Default for LinkedListNode { + fn default() -> Self { + LinkedListNode { + next: core::ptr::null_mut(), + prev: core::ptr::null_mut(), + } + } +} + +pub unsafe fn LST_init_head(mut listHead: *mut LinkedListNode) { + (*listHead).next = listHead; + (*listHead).prev = listHead; +} + +pub unsafe fn LST_is_empty(mut listHead: *mut LinkedListNode) -> bool { + interrupt::free(|_| ((*listHead).next) == listHead) +} + +pub unsafe fn LST_insert_head(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = (*listHead).next; + (*node).prev = listHead; + (*listHead).next = node; + (*(*node).next).prev = node; + }); +} + +pub unsafe fn LST_insert_tail(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = listHead; + (*node).prev = (*listHead).prev; + (*listHead).prev = node; + (*(*node).prev).next = node; + }); +} + +pub unsafe fn LST_remove_node(mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*(*node).prev).next = (*node).next; + (*(*node).next).prev = (*node).prev; + }); +} + +pub unsafe fn LST_remove_head(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*listHead).next; + LST_remove_node((*listHead).next); + }); +} + +pub unsafe fn LST_remove_tail(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*listHead).prev; + LST_remove_node((*listHead).prev); + }); +} + +pub unsafe fn LST_insert_node_after(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = (*ref_node).next; + (*node).prev = ref_node; + (*ref_node).next = node; + (*(*node).next).prev = node; + }); +} + +pub unsafe fn LST_insert_node_before(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = ref_node; + (*node).prev = (*ref_node).prev; + (*ref_node).prev = node; + (*(*node).prev).next = node; + }); +} + +pub unsafe fn LST_get_size(mut listHead: *mut LinkedListNode) -> usize { + interrupt::free(|_| { + let mut size = 0; + let mut temp: *mut LinkedListNode = core::ptr::null_mut::(); + + temp = (*listHead).next; + while temp != listHead { + size += 1; + temp = (*temp).next + } + + size + }) +} + +pub unsafe fn LST_get_next_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*ref_node).next; + }); +} + +pub unsafe fn LST_get_prev_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*ref_node).prev; + }); +} diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs new file mode 100644 index 000000000..ee090e6eb --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::tl_mbox::TlMbox; +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 config = Config::default(); + let mut ipcc = Ipcc::new(p.IPCC, config); + + let mbox = TlMbox::init(&mut ipcc); + + loop { + let wireless_fw_info = mbox.wireless_fw_info(); + match wireless_fw_info { + None => error!("not yet initialized"), + Some(fw_info) => { + let version_major = fw_info.version_major(); + let version_minor = fw_info.version_minor(); + let subversion = fw_info.subversion(); + + let sram2a_size = fw_info.sram2a_size(); + let sram2b_size = fw_info.sram2b_size(); + + info!( + "version {}.{}.{} - SRAM2a {} - SRAM2b {}", + version_major, version_minor, subversion, sram2a_size, sram2b_size + ); + + break; + } + } + } + + loop {} +} From 8e22d574478d7480bc0473ed0ad9dac7d02602a8 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 10:44:00 +0200 Subject: [PATCH 1018/1575] rp/pio: add hd44780 example add an hd44780 example for pio. hd44780 with busy polling is a pretty complicated protocol if the busy polling is to be done by the peripheral, and this example exercises many pio features that we don't have good examples for yet. --- examples/rp/src/bin/pio_hd44780.rs | 244 +++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 examples/rp/src/bin/pio_hd44780.rs diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs new file mode 100644 index 000000000..6bcd0652b --- /dev/null +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -0,0 +1,244 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::fmt::Write; + +use embassy_executor::Spawner; +use embassy_rp::dma::{AnyChannel, Channel}; +use embassy_rp::gpio::Pin; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{ + FifoJoin, PioCommon, PioInstanceBase, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, + SmInstanceBase, +}; +use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::relocate::RelocatedProgram; +use embassy_rp::{into_ref, Peripheral, PeripheralRef}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // this test assumes a 2x16 HD44780 display attached as follow: + // rs = PIN0 + // rw = PIN1 + // e = PIN2 + // db4 = PIN3 + // db5 = PIN4 + // db6 = PIN5 + // db7 = PIN6 + // additionally a pwm signal for a bias voltage charge pump is provided on pin 15, + // allowing direct connection of the display to the RP2040 without level shifters. + let p = embassy_rp::init(Default::default()); + + let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { + let mut c = Config::default(); + c.divider = 125.into(); + c.top = 100; + c.compare_b = 50; + c + }); + + let mut hd = HD44780::new( + p.PIO0, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, + ) + .await; + + loop { + struct Buf([u8; N], usize); + impl Write for Buf { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + for b in s.as_bytes() { + if self.1 >= N { + return Err(core::fmt::Error); + } + self.0[self.1] = *b; + self.1 += 1; + } + Ok(()) + } + } + let mut buf = Buf([0; 16], 0); + write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap(); + hd.add_line(&buf.0[0..buf.1]).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +pub struct HD44780<'l> { + dma: PeripheralRef<'l, AnyChannel>, + sm: PioStateMachineInstance, SmInstanceBase<0>>, + + buf: [u8; 40], +} + +impl<'l> HD44780<'l> { + pub async fn new( + pio: PIO0, + dma: impl Peripheral

+ 'l, + rs: impl Pin, + rw: impl Pin, + e: impl Pin, + db4: impl Pin, + db5: impl Pin, + db6: impl Pin, + db7: impl Pin, + ) -> HD44780<'l> { + into_ref!(dma); + + let db7pin = db7.pin(); + let (mut common, mut sm0, ..) = pio.split(); + + // takes command words ( <0:4>) + let prg = pio_proc::pio_asm!( + r#" + .side_set 1 opt + + loop: + out x, 24 + delay: + jmp x--, delay + out pins, 4 side 1 + out null, 4 side 0 + jmp !osre, loop + irq 0 + "#, + ); + + let rs = common.make_pio_pin(rs); + let rw = common.make_pio_pin(rw); + let e = common.make_pio_pin(e); + let db4 = common.make_pio_pin(db4); + let db5 = common.make_pio_pin(db5); + let db6 = common.make_pio_pin(db6); + let db7 = common.make_pio_pin(db7); + + sm0.set_set_pins(&[&rs, &rw]); + embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11); + sm0.set_set_pins(&[&e]); + embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b1); + sm0.set_set_pins(&[&db4, &db5, &db6, &db7]); + embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111); + + let relocated = RelocatedProgram::new(&prg.program); + common.write_instr(relocated.origin() as usize, relocated.code()); + embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); + sm0.set_clkdiv(125 * 256); + let pio::Wrap { source, target } = relocated.wrap(); + sm0.set_wrap(source, target); + sm0.set_side_enable(true); + sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); + sm0.set_sideset_base_pin(&e); + sm0.set_sideset_count(2); + sm0.set_out_shift_dir(ShiftDirection::Left); + sm0.set_fifo_join(FifoJoin::TxOnly); + sm0.set_autopull(true); + sm0.set_pull_threshold(32); + + sm0.set_enable(true); + // init to 8 bit thrice + sm0.push_tx((50000 << 8) | 0x30); + sm0.push_tx((5000 << 8) | 0x30); + sm0.push_tx((200 << 8) | 0x30); + // init 4 bit + sm0.push_tx((200 << 8) | 0x20); + // set font and lines + sm0.push_tx((50 << 8) | 0x20); + sm0.push_tx(0b1100_0000); + + sm0.wait_irq(0).await; + sm0.set_enable(false); + + // takes command sequences ( , data...) + // many side sets are only there to free up a delay bit! + let prg = pio_proc::pio_asm!( + r#" + .origin 7 + .side_set 1 + + .wrap_target + pull side 0 + out x 1 side 0 ; !rs + out y 7 side 0 ; #data - 1 + + ; rs/rw to e: >= 60ns + ; e high time: >= 500ns + ; e low time: >= 500ns + ; read data valid after e falling: ~5ns + ; write data hold after e falling: ~10ns + + loop: + pull side 0 + jmp !x data side 0 + command: + set pins 0b00 side 0 + jmp shift side 0 + data: + set pins 0b01 side 0 + shift: + out pins 4 side 1 [9] + nop side 0 [9] + out pins 4 side 1 [9] + mov osr null side 0 [7] + out pindirs 4 side 0 + set pins 0b10 side 0 + busy: + nop side 1 [9] + jmp pin more side 0 [9] + mov osr ~osr side 1 [9] + nop side 0 [4] + out pindirs 4 side 0 + jmp y-- loop side 0 + .wrap + more: + nop side 1 [9] + jmp busy side 0 [9] + "# + ); + + let relocated = RelocatedProgram::new(&prg.program); + common.write_instr(relocated.origin() as usize, relocated.code()); + embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); + let pio::Wrap { source, target } = relocated.wrap(); + sm0.set_clkdiv(8 * 256); // ~64ns/insn + sm0.set_side_enable(false); + sm0.set_jmp_pin(db7pin); + sm0.set_wrap(source, target); + sm0.set_set_pins(&[&rs, &rw]); + sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); + sm0.set_sideset_base_pin(&e); + sm0.set_sideset_count(1); + sm0.set_out_shift_dir(ShiftDirection::Left); + sm0.set_fifo_join(FifoJoin::TxOnly); + + sm0.set_enable(true); + + // display on and cursor on and blinking, reset display + sm0.dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; + + Self { + dma: dma.map_into(), + sm: sm0, + buf: [0x20; 40], + } + } + + pub async fn add_line(&mut self, s: &[u8]) { + // move cursor to 0:0, prepare 16 characters + self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); + // move line 2 up + self.buf.copy_within(22..38, 3); + // move cursor to 1:0, prepare 16 characters + self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); + // file line 2 with spaces + self.buf[22..38].fill(0x20); + // copy input line + let len = s.len().min(16); + self.buf[22..22 + len].copy_from_slice(&s[0..len]); + // set cursor to 1:15 + self.buf[38..].copy_from_slice(&[0x80, 0xcf]); + + self.sm.dma_push(self.dma.reborrow(), &self.buf).await; + } +} From 47ae9b79815af97e7e5d8b2f3c94af2326d7fd79 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 21:55:33 +0200 Subject: [PATCH 1019/1575] rp/pio: add funcsel values to PioInstance makes code setting funcsels easier to read and should make it easier to hook up more pio blocks, should they ever appear --- embassy-rp/src/pio.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index cab57f765..ecf7c9227 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -959,17 +959,7 @@ pub trait PioCommon: sealed::PioCommon + Sized { fn make_pio_pin(&self, pin: impl Pin) -> PioPin { unsafe { - pin.io().ctrl().write(|w| { - w.set_funcsel( - if Self::Pio::PIO_NO == 1 { - pac::io::vals::Gpio0ctrlFuncsel::PIO1_0 - } else { - // PIO == 0 - pac::io::vals::Gpio0ctrlFuncsel::PIO0_0 - } - .0, - ); - }); + pin.io().ctrl().write(|w| w.set_funcsel(Self::Pio::FUNCSEL.0)); } PioPin { pin_bank: pin.pin_bank(), @@ -1031,6 +1021,7 @@ mod sealed { pub trait PioInstance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; + const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; } pub trait PioCommon { @@ -1059,12 +1050,14 @@ pub trait PioInstance: sealed::PioInstance + Unpin {} impl sealed::PioInstance for PioInstanceBase<0> { const PIO_NO: u8 = 0; const PIO: &'static pac::pio::Pio = &pac::PIO0; + const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO0_0; } impl PioInstance for PioInstanceBase<0> {} impl sealed::PioInstance for PioInstanceBase<1> { const PIO_NO: u8 = 1; const PIO: &'static pac::pio::Pio = &pac::PIO1; + const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO1_0; } impl PioInstance for PioInstanceBase<1> {} From 0d224a00e1023082f03610b74c37933506561c26 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 22:19:13 +0200 Subject: [PATCH 1020/1575] rp/pio: avoid sm(SM_NO) indexing accessing the current state machine is an extremely common operation that shouldn't have its specifics repeated myriad times. --- embassy-rp/src/pio.rs | 159 ++++++++++++------------------------------ 1 file changed, 43 insertions(+), 116 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index ecf7c9227..b20ebbc77 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -410,15 +410,12 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_clkdiv(&mut self, div_x_256: u32) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .clkdiv() - .write(|w| w.0 = div_x_256 << 8); + Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); } } fn get_clkdiv(&self) -> u32 { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).clkdiv().read().0 >> 8 } + unsafe { Self::this_sm().clkdiv().read().0 >> 8 } } fn clkdiv_restart(&mut self) { @@ -431,52 +428,37 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_side_enable(&self, enable: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .modify(|w| w.set_side_en(enable)); + Self::this_sm().execctrl().modify(|w| w.set_side_en(enable)); } } fn is_side_enabled(&self) -> bool { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read().side_en() } + unsafe { Self::this_sm().execctrl().read().side_en() } } fn set_side_pindir(&mut self, pindir: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .modify(|w| w.set_side_pindir(pindir)); + Self::this_sm().execctrl().modify(|w| w.set_side_pindir(pindir)); } } fn is_side_pindir(&self) -> bool { - unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .read() - .side_pindir() - } + unsafe { Self::this_sm().execctrl().read().side_pindir() } } fn set_jmp_pin(&mut self, pin: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .modify(|w| w.set_jmp_pin(pin)); + Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); } } fn get_jmp_pin(&mut self) -> u8 { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read().jmp_pin() } + unsafe { Self::this_sm().execctrl().read().jmp_pin() } } fn set_wrap(&self, source: u8, target: u8) { unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().modify(|w| { + Self::this_sm().execctrl().modify(|w| { w.set_wrap_top(source); w.set_wrap_bottom(target) }); @@ -486,7 +468,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { /// Get wrapping addresses. Returns (source, target). fn get_wrap(&self) -> (u8, u8) { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read(); + let r = Self::this_sm().execctrl().read(); (r.wrap_top(), r.wrap_bottom()) } } @@ -498,7 +480,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { FifoJoin::TxOnly => (false, true), }; unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().modify(|w| { + Self::this_sm().shiftctrl().modify(|w| { w.set_fjoin_rx(rx); w.set_fjoin_tx(tx) }); @@ -506,7 +488,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_fifo_join(&self) -> FifoJoin { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); + let r = Self::this_sm().shiftctrl().read(); // Ignores the invalid state when both bits are set if r.fjoin_rx() { FifoJoin::RxOnly @@ -521,7 +503,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs unsafe { - let shiftctrl = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl(); + let shiftctrl = Self::this_sm().shiftctrl(); shiftctrl.modify(|w| { w.set_fjoin_rx(!w.fjoin_rx()); }); @@ -533,51 +515,33 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_pull_threshold(&mut self, threshold: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_pull_thresh(threshold)); + Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold)); } } fn get_pull_threshold(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); - r.pull_thresh() - } + unsafe { Self::this_sm().shiftctrl().read().pull_thresh() } } fn set_push_threshold(&mut self, threshold: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_push_thresh(threshold)); + Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold)); } } fn get_push_threshold(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); - r.push_thresh() - } + unsafe { Self::this_sm().shiftctrl().read().push_thresh() } } fn set_out_shift_dir(&mut self, dir: ShiftDirection) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) + Self::this_sm() .shiftctrl() .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); } } fn get_out_shiftdir(&self) -> ShiftDirection { unsafe { - if Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .out_shiftdir() - { + if Self::this_sm().shiftctrl().read().out_shiftdir() { ShiftDirection::Right } else { ShiftDirection::Left @@ -587,20 +551,14 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_in_shift_dir(&mut self, dir: ShiftDirection) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) + Self::this_sm() .shiftctrl() .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); } } fn get_in_shiftdir(&self) -> ShiftDirection { unsafe { - if Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .in_shiftdir() - { + if Self::this_sm().shiftctrl().read().in_shiftdir() { ShiftDirection::Right } else { ShiftDirection::Left @@ -610,76 +568,46 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_autopull(&mut self, auto: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_autopull(auto)); + Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto)); } } fn is_autopull(&self) -> bool { - unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .autopull() - } + unsafe { Self::this_sm().shiftctrl().read().autopull() } } fn set_autopush(&mut self, auto: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_autopush(auto)); + Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto)); } } fn is_autopush(&self) -> bool { - unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .autopush() - } + unsafe { Self::this_sm().shiftctrl().read().autopush() } } fn get_addr(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).addr().read(); - r.addr() - } + unsafe { Self::this_sm().addr().read().addr() } } fn set_sideset_count(&mut self, count: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| w.set_sideset_count(count)); + Self::this_sm().pinctrl().modify(|w| w.set_sideset_count(count)); } } fn get_sideset_count(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); - r.sideset_count() - } + unsafe { Self::this_sm().pinctrl().read().sideset_count() } } fn set_sideset_base_pin(&mut self, base_pin: &PioPin) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| w.set_sideset_base(base_pin.pin())); + Self::this_sm().pinctrl().modify(|w| w.set_sideset_base(base_pin.pin())); } } fn get_sideset_base(&self) -> u8 { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); r.sideset_base() } } @@ -688,7 +616,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_set_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().modify(|w| { + Self::this_sm().pinctrl().modify(|w| { w.set_set_base(base); w.set_set_count(count) }); @@ -698,23 +626,20 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { /// Get the range of out pins affected by a set instruction. Returns (base, count). fn get_set_range(&self) -> (u8, u8) { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); (r.set_base(), r.set_count()) } } fn set_in_base_pin(&mut self, base: &PioPin) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| w.set_in_base(base.pin())); + Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); } } fn get_in_base(&self) -> u8 { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); r.in_base() } } @@ -722,7 +647,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_out_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().modify(|w| { + Self::this_sm().pinctrl().modify(|w| { w.set_out_base(base); w.set_out_count(count) }); @@ -732,7 +657,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { /// Get the range of out pins affected by a set instruction. Returns (base, count). fn get_out_range(&self) -> (u8, u8) { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); (r.out_base(), r.out_count()) } } @@ -760,15 +685,12 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_current_instr() -> u32 { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).instr().read().0 } + unsafe { Self::this_sm().instr().read().0 } } fn exec_instr(&mut self, instr: u16) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .instr() - .write(|w| w.set_instr(instr)); + Self::this_sm().instr().write(|w| w.set_instr(instr)); } } @@ -1031,6 +953,11 @@ mod sealed { pub trait PioStateMachine { type Pio: super::PioInstance; type Sm: super::SmInstance; + + #[inline(always)] + fn this_sm() -> crate::pac::pio::StateMachine { + Self::Pio::PIO.sm(Self::Sm::SM_NO as usize) + } } pub trait SmInstance { From 6cec6fa09b3bcc01ada4d5d5decd02cc2a3a8e4f Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 23:17:57 +0200 Subject: [PATCH 1021/1575] rp/pio: don't use modify on shared registers pio control registers are notionally shared between state machines as well. state machine operations that change these registers must use atomic accesses (or critical sections, which would be overkill). notably PioPin::set_input_sync_bypass was even wrong, enabling the bypass on a pin requires the corresponding bit to be set (not cleared). the PioCommon function got it right. --- embassy-rp/src/pio.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index b20ebbc77..e9a67fd48 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -298,9 +298,11 @@ impl PioPin { pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { let mask = 1 << self.pin(); unsafe { - PIO::PIO - .input_sync_bypass() - .modify(|w| *w = if bypass { *w & !mask } else { *w | mask }); + if bypass { + PIO::PIO.input_sync_bypass().write_set(|w| *w = mask); + } else { + PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask); + } } } @@ -336,18 +338,19 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn restart(&mut self) { + let mask = 1u8 << Self::Sm::SM_NO; unsafe { - Self::Pio::PIO - .ctrl() - .modify(|w| w.set_sm_restart(1u8 << Self::Sm::SM_NO)); + Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); } } fn set_enable(&mut self, enable: bool) { let mask = 1u8 << Self::Sm::SM_NO; unsafe { - Self::Pio::PIO - .ctrl() - .modify(|w| w.set_sm_enable((w.sm_enable() & !mask) | (if enable { mask } else { 0 }))); + if enable { + Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); + } else { + Self::Pio::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); + } } } @@ -419,10 +422,9 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn clkdiv_restart(&mut self) { + let mask = 1u8 << Self::Sm::SM_NO; unsafe { - Self::Pio::PIO - .ctrl() - .modify(|w| w.set_clkdiv_restart(1u8 << Self::Sm::SM_NO)); + Self::Pio::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); } } @@ -869,9 +871,11 @@ pub trait PioCommon: sealed::PioCommon + Sized { fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { unsafe { - Self::Pio::PIO - .input_sync_bypass() - .modify(|w| *w = (*w & !mask) | (bypass & mask)); + // this can interfere with per-pin bypass functions. splitting the + // modification is going to be fine since nothing that relies on + // it can reasonably run before we finish. + Self::Pio::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); + Self::Pio::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); } } From 849011b8261194629830b4b85d062394e8eb3c58 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 08:39:05 +0200 Subject: [PATCH 1022/1575] rp/gpio: set up gpio interrupts only once doing this setup work repeatedly, on every wait, is unnecessary. with nothing ever disabling the interrupt it is sufficient to enable it once during device init and never touch it again. --- embassy-rp/src/gpio.rs | 13 +++--- embassy-rp/src/lib.rs | 1 + embassy-rp/src/multicore.rs | 5 ++- tests/rp/src/bin/gpio_multicore.rs | 63 ++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 tests/rp/src/bin/gpio_multicore.rs diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 66b9af04b..da8efba91 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -136,6 +136,13 @@ pub enum InterruptTrigger { AnyEdge, } +pub(crate) unsafe fn init() { + let irq = interrupt::IO_IRQ_BANK0::steal(); + irq.disable(); + irq.set_priority(interrupt::Priority::P3); + irq.enable(); +} + #[interrupt] unsafe fn IO_IRQ_BANK0() { let cpu = SIO.cpuid().read() as usize; @@ -179,10 +186,6 @@ impl<'d, T: Pin> InputFuture<'d, T> { pub fn new(pin: impl Peripheral

+ 'd, level: InterruptTrigger) -> Self { into_ref!(pin); unsafe { - let irq = interrupt::IO_IRQ_BANK0::steal(); - irq.disable(); - irq.set_priority(interrupt::Priority::P3); - let pin_group = (pin.pin() % 8) as usize; // first, clear the INTR register bits. without this INTR will still // contain reports of previous edges, causing the IRQ to fire early @@ -221,8 +224,6 @@ impl<'d, T: Pin> InputFuture<'d, T> { w.set_edge_low(pin_group, true); } }); - - irq.enable(); } Self { pin, level } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d69d12a30..cba7559df 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -157,6 +157,7 @@ pub fn init(_config: config::Config) -> Peripherals { timer::init(); dma::init(); pio::init(); + gpio::init(); } peripherals diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index e51fc218a..c84fea5c8 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -33,7 +33,7 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::peripherals::CORE1; -use crate::{interrupt, pac}; +use crate::{gpio, interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; @@ -68,6 +68,9 @@ fn install_stack_guard(stack_bottom: *mut usize) { #[inline(always)] fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); + unsafe { + gpio::init(); + } } /// Data type for a properly aligned stack of N bytes diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs new file mode 100644 index 000000000..6c13ccaae --- /dev/null +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::peripherals::{PIN_0, PIN_1}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<1024> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); +static CHANNEL0: Channel = Channel::new(); +static CHANNEL1: Channel = Channel::new(); + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(p.PIN_1)))); + }); + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| unwrap!(spawner.spawn(core0_task(p.PIN_0)))); +} + +#[embassy_executor::task] +async fn core0_task(p: PIN_0) { + info!("CORE0 is running"); + + let mut pin = Output::new(p, Level::Low); + + CHANNEL0.send(()).await; + CHANNEL1.recv().await; + + pin.set_high(); + + CHANNEL1.recv().await; + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[embassy_executor::task] +async fn core1_task(p: PIN_1) { + info!("CORE1 is running"); + + CHANNEL0.recv().await; + + let mut pin = Input::new(p, Pull::Down); + let wait = pin.wait_for_rising_edge(); + + CHANNEL1.send(()).await; + + wait.await; + + CHANNEL1.send(()).await; +} From 371a80e1a2fc08aa6c4cb98326df19725b14f502 Mon Sep 17 00:00:00 2001 From: goueslati Date: Tue, 2 May 2023 14:16:59 +0100 Subject: [PATCH 1023/1575] whoops, plugin formatted Cargo.toml, reverting --- embassy-stm32/Cargo.toml | 2668 +++++++++++++++++++------------------- 1 file changed, 1334 insertions(+), 1334 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6810aca98..ce26c31df 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -111,1337 +111,1337 @@ unstable-pac = [] unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] # Chip-selection features -stm32c011d6 = ["stm32-metapac/stm32c011d6"] -stm32c011f4 = ["stm32-metapac/stm32c011f4"] -stm32c011f6 = ["stm32-metapac/stm32c011f6"] -stm32c011j4 = ["stm32-metapac/stm32c011j4"] -stm32c011j6 = ["stm32-metapac/stm32c011j6"] -stm32c031c4 = ["stm32-metapac/stm32c031c4"] -stm32c031c6 = ["stm32-metapac/stm32c031c6"] -stm32c031f4 = ["stm32-metapac/stm32c031f4"] -stm32c031f6 = ["stm32-metapac/stm32c031f6"] -stm32c031g4 = ["stm32-metapac/stm32c031g4"] -stm32c031g6 = ["stm32-metapac/stm32c031g6"] -stm32c031k4 = ["stm32-metapac/stm32c031k4"] -stm32c031k6 = ["stm32-metapac/stm32c031k6"] -stm32f030c6 = ["stm32-metapac/stm32f030c6"] -stm32f030c8 = ["stm32-metapac/stm32f030c8"] -stm32f030cc = ["stm32-metapac/stm32f030cc"] -stm32f030f4 = ["stm32-metapac/stm32f030f4"] -stm32f030k6 = ["stm32-metapac/stm32f030k6"] -stm32f030r8 = ["stm32-metapac/stm32f030r8"] -stm32f030rc = ["stm32-metapac/stm32f030rc"] -stm32f031c4 = ["stm32-metapac/stm32f031c4"] -stm32f031c6 = ["stm32-metapac/stm32f031c6"] -stm32f031e6 = ["stm32-metapac/stm32f031e6"] -stm32f031f4 = ["stm32-metapac/stm32f031f4"] -stm32f031f6 = ["stm32-metapac/stm32f031f6"] -stm32f031g4 = ["stm32-metapac/stm32f031g4"] -stm32f031g6 = ["stm32-metapac/stm32f031g6"] -stm32f031k4 = ["stm32-metapac/stm32f031k4"] -stm32f031k6 = ["stm32-metapac/stm32f031k6"] -stm32f038c6 = ["stm32-metapac/stm32f038c6"] -stm32f038e6 = ["stm32-metapac/stm32f038e6"] -stm32f038f6 = ["stm32-metapac/stm32f038f6"] -stm32f038g6 = ["stm32-metapac/stm32f038g6"] -stm32f038k6 = ["stm32-metapac/stm32f038k6"] -stm32f042c4 = ["stm32-metapac/stm32f042c4"] -stm32f042c6 = ["stm32-metapac/stm32f042c6"] -stm32f042f4 = ["stm32-metapac/stm32f042f4"] -stm32f042f6 = ["stm32-metapac/stm32f042f6"] -stm32f042g4 = ["stm32-metapac/stm32f042g4"] -stm32f042g6 = ["stm32-metapac/stm32f042g6"] -stm32f042k4 = ["stm32-metapac/stm32f042k4"] -stm32f042k6 = ["stm32-metapac/stm32f042k6"] -stm32f042t6 = ["stm32-metapac/stm32f042t6"] -stm32f048c6 = ["stm32-metapac/stm32f048c6"] -stm32f048g6 = ["stm32-metapac/stm32f048g6"] -stm32f048t6 = ["stm32-metapac/stm32f048t6"] -stm32f051c4 = ["stm32-metapac/stm32f051c4"] -stm32f051c6 = ["stm32-metapac/stm32f051c6"] -stm32f051c8 = ["stm32-metapac/stm32f051c8"] -stm32f051k4 = ["stm32-metapac/stm32f051k4"] -stm32f051k6 = ["stm32-metapac/stm32f051k6"] -stm32f051k8 = ["stm32-metapac/stm32f051k8"] -stm32f051r4 = ["stm32-metapac/stm32f051r4"] -stm32f051r6 = ["stm32-metapac/stm32f051r6"] -stm32f051r8 = ["stm32-metapac/stm32f051r8"] -stm32f051t8 = ["stm32-metapac/stm32f051t8"] -stm32f058c8 = ["stm32-metapac/stm32f058c8"] -stm32f058r8 = ["stm32-metapac/stm32f058r8"] -stm32f058t8 = ["stm32-metapac/stm32f058t8"] -stm32f070c6 = ["stm32-metapac/stm32f070c6"] -stm32f070cb = ["stm32-metapac/stm32f070cb"] -stm32f070f6 = ["stm32-metapac/stm32f070f6"] -stm32f070rb = ["stm32-metapac/stm32f070rb"] -stm32f071c8 = ["stm32-metapac/stm32f071c8"] -stm32f071cb = ["stm32-metapac/stm32f071cb"] -stm32f071rb = ["stm32-metapac/stm32f071rb"] -stm32f071v8 = ["stm32-metapac/stm32f071v8"] -stm32f071vb = ["stm32-metapac/stm32f071vb"] -stm32f072c8 = ["stm32-metapac/stm32f072c8"] -stm32f072cb = ["stm32-metapac/stm32f072cb"] -stm32f072r8 = ["stm32-metapac/stm32f072r8"] -stm32f072rb = ["stm32-metapac/stm32f072rb"] -stm32f072v8 = ["stm32-metapac/stm32f072v8"] -stm32f072vb = ["stm32-metapac/stm32f072vb"] -stm32f078cb = ["stm32-metapac/stm32f078cb"] -stm32f078rb = ["stm32-metapac/stm32f078rb"] -stm32f078vb = ["stm32-metapac/stm32f078vb"] -stm32f091cb = ["stm32-metapac/stm32f091cb"] -stm32f091cc = ["stm32-metapac/stm32f091cc"] -stm32f091rb = ["stm32-metapac/stm32f091rb"] -stm32f091rc = ["stm32-metapac/stm32f091rc"] -stm32f091vb = ["stm32-metapac/stm32f091vb"] -stm32f091vc = ["stm32-metapac/stm32f091vc"] -stm32f098cc = ["stm32-metapac/stm32f098cc"] -stm32f098rc = ["stm32-metapac/stm32f098rc"] -stm32f098vc = ["stm32-metapac/stm32f098vc"] -stm32f100c4 = ["stm32-metapac/stm32f100c4"] -stm32f100c6 = ["stm32-metapac/stm32f100c6"] -stm32f100c8 = ["stm32-metapac/stm32f100c8"] -stm32f100cb = ["stm32-metapac/stm32f100cb"] -stm32f100r4 = ["stm32-metapac/stm32f100r4"] -stm32f100r6 = ["stm32-metapac/stm32f100r6"] -stm32f100r8 = ["stm32-metapac/stm32f100r8"] -stm32f100rb = ["stm32-metapac/stm32f100rb"] -stm32f100rc = ["stm32-metapac/stm32f100rc"] -stm32f100rd = ["stm32-metapac/stm32f100rd"] -stm32f100re = ["stm32-metapac/stm32f100re"] -stm32f100v8 = ["stm32-metapac/stm32f100v8"] -stm32f100vb = ["stm32-metapac/stm32f100vb"] -stm32f100vc = ["stm32-metapac/stm32f100vc"] -stm32f100vd = ["stm32-metapac/stm32f100vd"] -stm32f100ve = ["stm32-metapac/stm32f100ve"] -stm32f100zc = ["stm32-metapac/stm32f100zc"] -stm32f100zd = ["stm32-metapac/stm32f100zd"] -stm32f100ze = ["stm32-metapac/stm32f100ze"] -stm32f101c4 = ["stm32-metapac/stm32f101c4"] -stm32f101c6 = ["stm32-metapac/stm32f101c6"] -stm32f101c8 = ["stm32-metapac/stm32f101c8"] -stm32f101cb = ["stm32-metapac/stm32f101cb"] -stm32f101r4 = ["stm32-metapac/stm32f101r4"] -stm32f101r6 = ["stm32-metapac/stm32f101r6"] -stm32f101r8 = ["stm32-metapac/stm32f101r8"] -stm32f101rb = ["stm32-metapac/stm32f101rb"] -stm32f101rc = ["stm32-metapac/stm32f101rc"] -stm32f101rd = ["stm32-metapac/stm32f101rd"] -stm32f101re = ["stm32-metapac/stm32f101re"] -stm32f101rf = ["stm32-metapac/stm32f101rf"] -stm32f101rg = ["stm32-metapac/stm32f101rg"] -stm32f101t4 = ["stm32-metapac/stm32f101t4"] -stm32f101t6 = ["stm32-metapac/stm32f101t6"] -stm32f101t8 = ["stm32-metapac/stm32f101t8"] -stm32f101tb = ["stm32-metapac/stm32f101tb"] -stm32f101v8 = ["stm32-metapac/stm32f101v8"] -stm32f101vb = ["stm32-metapac/stm32f101vb"] -stm32f101vc = ["stm32-metapac/stm32f101vc"] -stm32f101vd = ["stm32-metapac/stm32f101vd"] -stm32f101ve = ["stm32-metapac/stm32f101ve"] -stm32f101vf = ["stm32-metapac/stm32f101vf"] -stm32f101vg = ["stm32-metapac/stm32f101vg"] -stm32f101zc = ["stm32-metapac/stm32f101zc"] -stm32f101zd = ["stm32-metapac/stm32f101zd"] -stm32f101ze = ["stm32-metapac/stm32f101ze"] -stm32f101zf = ["stm32-metapac/stm32f101zf"] -stm32f101zg = ["stm32-metapac/stm32f101zg"] -stm32f102c4 = ["stm32-metapac/stm32f102c4"] -stm32f102c6 = ["stm32-metapac/stm32f102c6"] -stm32f102c8 = ["stm32-metapac/stm32f102c8"] -stm32f102cb = ["stm32-metapac/stm32f102cb"] -stm32f102r4 = ["stm32-metapac/stm32f102r4"] -stm32f102r6 = ["stm32-metapac/stm32f102r6"] -stm32f102r8 = ["stm32-metapac/stm32f102r8"] -stm32f102rb = ["stm32-metapac/stm32f102rb"] -stm32f103c4 = ["stm32-metapac/stm32f103c4"] -stm32f103c6 = ["stm32-metapac/stm32f103c6"] -stm32f103c8 = ["stm32-metapac/stm32f103c8"] -stm32f103cb = ["stm32-metapac/stm32f103cb"] -stm32f103r4 = ["stm32-metapac/stm32f103r4"] -stm32f103r6 = ["stm32-metapac/stm32f103r6"] -stm32f103r8 = ["stm32-metapac/stm32f103r8"] -stm32f103rb = ["stm32-metapac/stm32f103rb"] -stm32f103rc = ["stm32-metapac/stm32f103rc"] -stm32f103rd = ["stm32-metapac/stm32f103rd"] -stm32f103re = ["stm32-metapac/stm32f103re"] -stm32f103rf = ["stm32-metapac/stm32f103rf"] -stm32f103rg = ["stm32-metapac/stm32f103rg"] -stm32f103t4 = ["stm32-metapac/stm32f103t4"] -stm32f103t6 = ["stm32-metapac/stm32f103t6"] -stm32f103t8 = ["stm32-metapac/stm32f103t8"] -stm32f103tb = ["stm32-metapac/stm32f103tb"] -stm32f103v8 = ["stm32-metapac/stm32f103v8"] -stm32f103vb = ["stm32-metapac/stm32f103vb"] -stm32f103vc = ["stm32-metapac/stm32f103vc"] -stm32f103vd = ["stm32-metapac/stm32f103vd"] -stm32f103ve = ["stm32-metapac/stm32f103ve"] -stm32f103vf = ["stm32-metapac/stm32f103vf"] -stm32f103vg = ["stm32-metapac/stm32f103vg"] -stm32f103zc = ["stm32-metapac/stm32f103zc"] -stm32f103zd = ["stm32-metapac/stm32f103zd"] -stm32f103ze = ["stm32-metapac/stm32f103ze"] -stm32f103zf = ["stm32-metapac/stm32f103zf"] -stm32f103zg = ["stm32-metapac/stm32f103zg"] -stm32f105r8 = ["stm32-metapac/stm32f105r8"] -stm32f105rb = ["stm32-metapac/stm32f105rb"] -stm32f105rc = ["stm32-metapac/stm32f105rc"] -stm32f105v8 = ["stm32-metapac/stm32f105v8"] -stm32f105vb = ["stm32-metapac/stm32f105vb"] -stm32f105vc = ["stm32-metapac/stm32f105vc"] -stm32f107rb = ["stm32-metapac/stm32f107rb"] -stm32f107rc = ["stm32-metapac/stm32f107rc"] -stm32f107vb = ["stm32-metapac/stm32f107vb"] -stm32f107vc = ["stm32-metapac/stm32f107vc"] -stm32f205rb = ["stm32-metapac/stm32f205rb"] -stm32f205rc = ["stm32-metapac/stm32f205rc"] -stm32f205re = ["stm32-metapac/stm32f205re"] -stm32f205rf = ["stm32-metapac/stm32f205rf"] -stm32f205rg = ["stm32-metapac/stm32f205rg"] -stm32f205vb = ["stm32-metapac/stm32f205vb"] -stm32f205vc = ["stm32-metapac/stm32f205vc"] -stm32f205ve = ["stm32-metapac/stm32f205ve"] -stm32f205vf = ["stm32-metapac/stm32f205vf"] -stm32f205vg = ["stm32-metapac/stm32f205vg"] -stm32f205zc = ["stm32-metapac/stm32f205zc"] -stm32f205ze = ["stm32-metapac/stm32f205ze"] -stm32f205zf = ["stm32-metapac/stm32f205zf"] -stm32f205zg = ["stm32-metapac/stm32f205zg"] -stm32f207ic = ["stm32-metapac/stm32f207ic"] -stm32f207ie = ["stm32-metapac/stm32f207ie"] -stm32f207if = ["stm32-metapac/stm32f207if"] -stm32f207ig = ["stm32-metapac/stm32f207ig"] -stm32f207vc = ["stm32-metapac/stm32f207vc"] -stm32f207ve = ["stm32-metapac/stm32f207ve"] -stm32f207vf = ["stm32-metapac/stm32f207vf"] -stm32f207vg = ["stm32-metapac/stm32f207vg"] -stm32f207zc = ["stm32-metapac/stm32f207zc"] -stm32f207ze = ["stm32-metapac/stm32f207ze"] -stm32f207zf = ["stm32-metapac/stm32f207zf"] -stm32f207zg = ["stm32-metapac/stm32f207zg"] -stm32f215re = ["stm32-metapac/stm32f215re"] -stm32f215rg = ["stm32-metapac/stm32f215rg"] -stm32f215ve = ["stm32-metapac/stm32f215ve"] -stm32f215vg = ["stm32-metapac/stm32f215vg"] -stm32f215ze = ["stm32-metapac/stm32f215ze"] -stm32f215zg = ["stm32-metapac/stm32f215zg"] -stm32f217ie = ["stm32-metapac/stm32f217ie"] -stm32f217ig = ["stm32-metapac/stm32f217ig"] -stm32f217ve = ["stm32-metapac/stm32f217ve"] -stm32f217vg = ["stm32-metapac/stm32f217vg"] -stm32f217ze = ["stm32-metapac/stm32f217ze"] -stm32f217zg = ["stm32-metapac/stm32f217zg"] -stm32f301c6 = ["stm32-metapac/stm32f301c6"] -stm32f301c8 = ["stm32-metapac/stm32f301c8"] -stm32f301k6 = ["stm32-metapac/stm32f301k6"] -stm32f301k8 = ["stm32-metapac/stm32f301k8"] -stm32f301r6 = ["stm32-metapac/stm32f301r6"] -stm32f301r8 = ["stm32-metapac/stm32f301r8"] -stm32f302c6 = ["stm32-metapac/stm32f302c6"] -stm32f302c8 = ["stm32-metapac/stm32f302c8"] -stm32f302cb = ["stm32-metapac/stm32f302cb"] -stm32f302cc = ["stm32-metapac/stm32f302cc"] -stm32f302k6 = ["stm32-metapac/stm32f302k6"] -stm32f302k8 = ["stm32-metapac/stm32f302k8"] -stm32f302r6 = ["stm32-metapac/stm32f302r6"] -stm32f302r8 = ["stm32-metapac/stm32f302r8"] -stm32f302rb = ["stm32-metapac/stm32f302rb"] -stm32f302rc = ["stm32-metapac/stm32f302rc"] -stm32f302rd = ["stm32-metapac/stm32f302rd"] -stm32f302re = ["stm32-metapac/stm32f302re"] -stm32f302vb = ["stm32-metapac/stm32f302vb"] -stm32f302vc = ["stm32-metapac/stm32f302vc"] -stm32f302vd = ["stm32-metapac/stm32f302vd"] -stm32f302ve = ["stm32-metapac/stm32f302ve"] -stm32f302zd = ["stm32-metapac/stm32f302zd"] -stm32f302ze = ["stm32-metapac/stm32f302ze"] -stm32f303c6 = ["stm32-metapac/stm32f303c6"] -stm32f303c8 = ["stm32-metapac/stm32f303c8"] -stm32f303cb = ["stm32-metapac/stm32f303cb"] -stm32f303cc = ["stm32-metapac/stm32f303cc"] -stm32f303k6 = ["stm32-metapac/stm32f303k6"] -stm32f303k8 = ["stm32-metapac/stm32f303k8"] -stm32f303r6 = ["stm32-metapac/stm32f303r6"] -stm32f303r8 = ["stm32-metapac/stm32f303r8"] -stm32f303rb = ["stm32-metapac/stm32f303rb"] -stm32f303rc = ["stm32-metapac/stm32f303rc"] -stm32f303rd = ["stm32-metapac/stm32f303rd"] -stm32f303re = ["stm32-metapac/stm32f303re"] -stm32f303vb = ["stm32-metapac/stm32f303vb"] -stm32f303vc = ["stm32-metapac/stm32f303vc"] -stm32f303vd = ["stm32-metapac/stm32f303vd"] -stm32f303ve = ["stm32-metapac/stm32f303ve"] -stm32f303zd = ["stm32-metapac/stm32f303zd"] -stm32f303ze = ["stm32-metapac/stm32f303ze"] -stm32f318c8 = ["stm32-metapac/stm32f318c8"] -stm32f318k8 = ["stm32-metapac/stm32f318k8"] -stm32f328c8 = ["stm32-metapac/stm32f328c8"] -stm32f334c4 = ["stm32-metapac/stm32f334c4"] -stm32f334c6 = ["stm32-metapac/stm32f334c6"] -stm32f334c8 = ["stm32-metapac/stm32f334c8"] -stm32f334k4 = ["stm32-metapac/stm32f334k4"] -stm32f334k6 = ["stm32-metapac/stm32f334k6"] -stm32f334k8 = ["stm32-metapac/stm32f334k8"] -stm32f334r6 = ["stm32-metapac/stm32f334r6"] -stm32f334r8 = ["stm32-metapac/stm32f334r8"] -stm32f358cc = ["stm32-metapac/stm32f358cc"] -stm32f358rc = ["stm32-metapac/stm32f358rc"] -stm32f358vc = ["stm32-metapac/stm32f358vc"] -stm32f373c8 = ["stm32-metapac/stm32f373c8"] -stm32f373cb = ["stm32-metapac/stm32f373cb"] -stm32f373cc = ["stm32-metapac/stm32f373cc"] -stm32f373r8 = ["stm32-metapac/stm32f373r8"] -stm32f373rb = ["stm32-metapac/stm32f373rb"] -stm32f373rc = ["stm32-metapac/stm32f373rc"] -stm32f373v8 = ["stm32-metapac/stm32f373v8"] -stm32f373vb = ["stm32-metapac/stm32f373vb"] -stm32f373vc = ["stm32-metapac/stm32f373vc"] -stm32f378cc = ["stm32-metapac/stm32f378cc"] -stm32f378rc = ["stm32-metapac/stm32f378rc"] -stm32f378vc = ["stm32-metapac/stm32f378vc"] -stm32f398ve = ["stm32-metapac/stm32f398ve"] -stm32f401cb = ["stm32-metapac/stm32f401cb"] -stm32f401cc = ["stm32-metapac/stm32f401cc"] -stm32f401cd = ["stm32-metapac/stm32f401cd"] -stm32f401ce = ["stm32-metapac/stm32f401ce"] -stm32f401rb = ["stm32-metapac/stm32f401rb"] -stm32f401rc = ["stm32-metapac/stm32f401rc"] -stm32f401rd = ["stm32-metapac/stm32f401rd"] -stm32f401re = ["stm32-metapac/stm32f401re"] -stm32f401vb = ["stm32-metapac/stm32f401vb"] -stm32f401vc = ["stm32-metapac/stm32f401vc"] -stm32f401vd = ["stm32-metapac/stm32f401vd"] -stm32f401ve = ["stm32-metapac/stm32f401ve"] -stm32f405oe = ["stm32-metapac/stm32f405oe"] -stm32f405og = ["stm32-metapac/stm32f405og"] -stm32f405rg = ["stm32-metapac/stm32f405rg"] -stm32f405vg = ["stm32-metapac/stm32f405vg"] -stm32f405zg = ["stm32-metapac/stm32f405zg"] -stm32f407ie = ["stm32-metapac/stm32f407ie"] -stm32f407ig = ["stm32-metapac/stm32f407ig"] -stm32f407ve = ["stm32-metapac/stm32f407ve"] -stm32f407vg = ["stm32-metapac/stm32f407vg"] -stm32f407ze = ["stm32-metapac/stm32f407ze"] -stm32f407zg = ["stm32-metapac/stm32f407zg"] -stm32f410c8 = ["stm32-metapac/stm32f410c8"] -stm32f410cb = ["stm32-metapac/stm32f410cb"] -stm32f410r8 = ["stm32-metapac/stm32f410r8"] -stm32f410rb = ["stm32-metapac/stm32f410rb"] -stm32f410t8 = ["stm32-metapac/stm32f410t8"] -stm32f410tb = ["stm32-metapac/stm32f410tb"] -stm32f411cc = ["stm32-metapac/stm32f411cc"] -stm32f411ce = ["stm32-metapac/stm32f411ce"] -stm32f411rc = ["stm32-metapac/stm32f411rc"] -stm32f411re = ["stm32-metapac/stm32f411re"] -stm32f411vc = ["stm32-metapac/stm32f411vc"] -stm32f411ve = ["stm32-metapac/stm32f411ve"] -stm32f412ce = ["stm32-metapac/stm32f412ce"] -stm32f412cg = ["stm32-metapac/stm32f412cg"] -stm32f412re = ["stm32-metapac/stm32f412re"] -stm32f412rg = ["stm32-metapac/stm32f412rg"] -stm32f412ve = ["stm32-metapac/stm32f412ve"] -stm32f412vg = ["stm32-metapac/stm32f412vg"] -stm32f412ze = ["stm32-metapac/stm32f412ze"] -stm32f412zg = ["stm32-metapac/stm32f412zg"] -stm32f413cg = ["stm32-metapac/stm32f413cg"] -stm32f413ch = ["stm32-metapac/stm32f413ch"] -stm32f413mg = ["stm32-metapac/stm32f413mg"] -stm32f413mh = ["stm32-metapac/stm32f413mh"] -stm32f413rg = ["stm32-metapac/stm32f413rg"] -stm32f413rh = ["stm32-metapac/stm32f413rh"] -stm32f413vg = ["stm32-metapac/stm32f413vg"] -stm32f413vh = ["stm32-metapac/stm32f413vh"] -stm32f413zg = ["stm32-metapac/stm32f413zg"] -stm32f413zh = ["stm32-metapac/stm32f413zh"] -stm32f415og = ["stm32-metapac/stm32f415og"] -stm32f415rg = ["stm32-metapac/stm32f415rg"] -stm32f415vg = ["stm32-metapac/stm32f415vg"] -stm32f415zg = ["stm32-metapac/stm32f415zg"] -stm32f417ie = ["stm32-metapac/stm32f417ie"] -stm32f417ig = ["stm32-metapac/stm32f417ig"] -stm32f417ve = ["stm32-metapac/stm32f417ve"] -stm32f417vg = ["stm32-metapac/stm32f417vg"] -stm32f417ze = ["stm32-metapac/stm32f417ze"] -stm32f417zg = ["stm32-metapac/stm32f417zg"] -stm32f423ch = ["stm32-metapac/stm32f423ch"] -stm32f423mh = ["stm32-metapac/stm32f423mh"] -stm32f423rh = ["stm32-metapac/stm32f423rh"] -stm32f423vh = ["stm32-metapac/stm32f423vh"] -stm32f423zh = ["stm32-metapac/stm32f423zh"] -stm32f427ag = ["stm32-metapac/stm32f427ag"] -stm32f427ai = ["stm32-metapac/stm32f427ai"] -stm32f427ig = ["stm32-metapac/stm32f427ig"] -stm32f427ii = ["stm32-metapac/stm32f427ii"] -stm32f427vg = ["stm32-metapac/stm32f427vg"] -stm32f427vi = ["stm32-metapac/stm32f427vi"] -stm32f427zg = ["stm32-metapac/stm32f427zg"] -stm32f427zi = ["stm32-metapac/stm32f427zi"] -stm32f429ag = ["stm32-metapac/stm32f429ag"] -stm32f429ai = ["stm32-metapac/stm32f429ai"] -stm32f429be = ["stm32-metapac/stm32f429be"] -stm32f429bg = ["stm32-metapac/stm32f429bg"] -stm32f429bi = ["stm32-metapac/stm32f429bi"] -stm32f429ie = ["stm32-metapac/stm32f429ie"] -stm32f429ig = ["stm32-metapac/stm32f429ig"] -stm32f429ii = ["stm32-metapac/stm32f429ii"] -stm32f429ne = ["stm32-metapac/stm32f429ne"] -stm32f429ng = ["stm32-metapac/stm32f429ng"] -stm32f429ni = ["stm32-metapac/stm32f429ni"] -stm32f429ve = ["stm32-metapac/stm32f429ve"] -stm32f429vg = ["stm32-metapac/stm32f429vg"] -stm32f429vi = ["stm32-metapac/stm32f429vi"] -stm32f429ze = ["stm32-metapac/stm32f429ze"] -stm32f429zg = ["stm32-metapac/stm32f429zg"] -stm32f429zi = ["stm32-metapac/stm32f429zi"] -stm32f437ai = ["stm32-metapac/stm32f437ai"] -stm32f437ig = ["stm32-metapac/stm32f437ig"] -stm32f437ii = ["stm32-metapac/stm32f437ii"] -stm32f437vg = ["stm32-metapac/stm32f437vg"] -stm32f437vi = ["stm32-metapac/stm32f437vi"] -stm32f437zg = ["stm32-metapac/stm32f437zg"] -stm32f437zi = ["stm32-metapac/stm32f437zi"] -stm32f439ai = ["stm32-metapac/stm32f439ai"] -stm32f439bg = ["stm32-metapac/stm32f439bg"] -stm32f439bi = ["stm32-metapac/stm32f439bi"] -stm32f439ig = ["stm32-metapac/stm32f439ig"] -stm32f439ii = ["stm32-metapac/stm32f439ii"] -stm32f439ng = ["stm32-metapac/stm32f439ng"] -stm32f439ni = ["stm32-metapac/stm32f439ni"] -stm32f439vg = ["stm32-metapac/stm32f439vg"] -stm32f439vi = ["stm32-metapac/stm32f439vi"] -stm32f439zg = ["stm32-metapac/stm32f439zg"] -stm32f439zi = ["stm32-metapac/stm32f439zi"] -stm32f446mc = ["stm32-metapac/stm32f446mc"] -stm32f446me = ["stm32-metapac/stm32f446me"] -stm32f446rc = ["stm32-metapac/stm32f446rc"] -stm32f446re = ["stm32-metapac/stm32f446re"] -stm32f446vc = ["stm32-metapac/stm32f446vc"] -stm32f446ve = ["stm32-metapac/stm32f446ve"] -stm32f446zc = ["stm32-metapac/stm32f446zc"] -stm32f446ze = ["stm32-metapac/stm32f446ze"] -stm32f469ae = ["stm32-metapac/stm32f469ae"] -stm32f469ag = ["stm32-metapac/stm32f469ag"] -stm32f469ai = ["stm32-metapac/stm32f469ai"] -stm32f469be = ["stm32-metapac/stm32f469be"] -stm32f469bg = ["stm32-metapac/stm32f469bg"] -stm32f469bi = ["stm32-metapac/stm32f469bi"] -stm32f469ie = ["stm32-metapac/stm32f469ie"] -stm32f469ig = ["stm32-metapac/stm32f469ig"] -stm32f469ii = ["stm32-metapac/stm32f469ii"] -stm32f469ne = ["stm32-metapac/stm32f469ne"] -stm32f469ng = ["stm32-metapac/stm32f469ng"] -stm32f469ni = ["stm32-metapac/stm32f469ni"] -stm32f469ve = ["stm32-metapac/stm32f469ve"] -stm32f469vg = ["stm32-metapac/stm32f469vg"] -stm32f469vi = ["stm32-metapac/stm32f469vi"] -stm32f469ze = ["stm32-metapac/stm32f469ze"] -stm32f469zg = ["stm32-metapac/stm32f469zg"] -stm32f469zi = ["stm32-metapac/stm32f469zi"] -stm32f479ag = ["stm32-metapac/stm32f479ag"] -stm32f479ai = ["stm32-metapac/stm32f479ai"] -stm32f479bg = ["stm32-metapac/stm32f479bg"] -stm32f479bi = ["stm32-metapac/stm32f479bi"] -stm32f479ig = ["stm32-metapac/stm32f479ig"] -stm32f479ii = ["stm32-metapac/stm32f479ii"] -stm32f479ng = ["stm32-metapac/stm32f479ng"] -stm32f479ni = ["stm32-metapac/stm32f479ni"] -stm32f479vg = ["stm32-metapac/stm32f479vg"] -stm32f479vi = ["stm32-metapac/stm32f479vi"] -stm32f479zg = ["stm32-metapac/stm32f479zg"] -stm32f479zi = ["stm32-metapac/stm32f479zi"] -stm32f722ic = ["stm32-metapac/stm32f722ic"] -stm32f722ie = ["stm32-metapac/stm32f722ie"] -stm32f722rc = ["stm32-metapac/stm32f722rc"] -stm32f722re = ["stm32-metapac/stm32f722re"] -stm32f722vc = ["stm32-metapac/stm32f722vc"] -stm32f722ve = ["stm32-metapac/stm32f722ve"] -stm32f722zc = ["stm32-metapac/stm32f722zc"] -stm32f722ze = ["stm32-metapac/stm32f722ze"] -stm32f723ic = ["stm32-metapac/stm32f723ic"] -stm32f723ie = ["stm32-metapac/stm32f723ie"] -stm32f723vc = ["stm32-metapac/stm32f723vc"] -stm32f723ve = ["stm32-metapac/stm32f723ve"] -stm32f723zc = ["stm32-metapac/stm32f723zc"] -stm32f723ze = ["stm32-metapac/stm32f723ze"] -stm32f730i8 = ["stm32-metapac/stm32f730i8"] -stm32f730r8 = ["stm32-metapac/stm32f730r8"] -stm32f730v8 = ["stm32-metapac/stm32f730v8"] -stm32f730z8 = ["stm32-metapac/stm32f730z8"] -stm32f732ie = ["stm32-metapac/stm32f732ie"] -stm32f732re = ["stm32-metapac/stm32f732re"] -stm32f732ve = ["stm32-metapac/stm32f732ve"] -stm32f732ze = ["stm32-metapac/stm32f732ze"] -stm32f733ie = ["stm32-metapac/stm32f733ie"] -stm32f733ve = ["stm32-metapac/stm32f733ve"] -stm32f733ze = ["stm32-metapac/stm32f733ze"] -stm32f745ie = ["stm32-metapac/stm32f745ie"] -stm32f745ig = ["stm32-metapac/stm32f745ig"] -stm32f745ve = ["stm32-metapac/stm32f745ve"] -stm32f745vg = ["stm32-metapac/stm32f745vg"] -stm32f745ze = ["stm32-metapac/stm32f745ze"] -stm32f745zg = ["stm32-metapac/stm32f745zg"] -stm32f746be = ["stm32-metapac/stm32f746be"] -stm32f746bg = ["stm32-metapac/stm32f746bg"] -stm32f746ie = ["stm32-metapac/stm32f746ie"] -stm32f746ig = ["stm32-metapac/stm32f746ig"] -stm32f746ne = ["stm32-metapac/stm32f746ne"] -stm32f746ng = ["stm32-metapac/stm32f746ng"] -stm32f746ve = ["stm32-metapac/stm32f746ve"] -stm32f746vg = ["stm32-metapac/stm32f746vg"] -stm32f746ze = ["stm32-metapac/stm32f746ze"] -stm32f746zg = ["stm32-metapac/stm32f746zg"] -stm32f750n8 = ["stm32-metapac/stm32f750n8"] -stm32f750v8 = ["stm32-metapac/stm32f750v8"] -stm32f750z8 = ["stm32-metapac/stm32f750z8"] -stm32f756bg = ["stm32-metapac/stm32f756bg"] -stm32f756ig = ["stm32-metapac/stm32f756ig"] -stm32f756ng = ["stm32-metapac/stm32f756ng"] -stm32f756vg = ["stm32-metapac/stm32f756vg"] -stm32f756zg = ["stm32-metapac/stm32f756zg"] -stm32f765bg = ["stm32-metapac/stm32f765bg"] -stm32f765bi = ["stm32-metapac/stm32f765bi"] -stm32f765ig = ["stm32-metapac/stm32f765ig"] -stm32f765ii = ["stm32-metapac/stm32f765ii"] -stm32f765ng = ["stm32-metapac/stm32f765ng"] -stm32f765ni = ["stm32-metapac/stm32f765ni"] -stm32f765vg = ["stm32-metapac/stm32f765vg"] -stm32f765vi = ["stm32-metapac/stm32f765vi"] -stm32f765zg = ["stm32-metapac/stm32f765zg"] -stm32f765zi = ["stm32-metapac/stm32f765zi"] -stm32f767bg = ["stm32-metapac/stm32f767bg"] -stm32f767bi = ["stm32-metapac/stm32f767bi"] -stm32f767ig = ["stm32-metapac/stm32f767ig"] -stm32f767ii = ["stm32-metapac/stm32f767ii"] -stm32f767ng = ["stm32-metapac/stm32f767ng"] -stm32f767ni = ["stm32-metapac/stm32f767ni"] -stm32f767vg = ["stm32-metapac/stm32f767vg"] -stm32f767vi = ["stm32-metapac/stm32f767vi"] -stm32f767zg = ["stm32-metapac/stm32f767zg"] -stm32f767zi = ["stm32-metapac/stm32f767zi"] -stm32f768ai = ["stm32-metapac/stm32f768ai"] -stm32f769ag = ["stm32-metapac/stm32f769ag"] -stm32f769ai = ["stm32-metapac/stm32f769ai"] -stm32f769bg = ["stm32-metapac/stm32f769bg"] -stm32f769bi = ["stm32-metapac/stm32f769bi"] -stm32f769ig = ["stm32-metapac/stm32f769ig"] -stm32f769ii = ["stm32-metapac/stm32f769ii"] -stm32f769ng = ["stm32-metapac/stm32f769ng"] -stm32f769ni = ["stm32-metapac/stm32f769ni"] -stm32f777bi = ["stm32-metapac/stm32f777bi"] -stm32f777ii = ["stm32-metapac/stm32f777ii"] -stm32f777ni = ["stm32-metapac/stm32f777ni"] -stm32f777vi = ["stm32-metapac/stm32f777vi"] -stm32f777zi = ["stm32-metapac/stm32f777zi"] -stm32f778ai = ["stm32-metapac/stm32f778ai"] -stm32f779ai = ["stm32-metapac/stm32f779ai"] -stm32f779bi = ["stm32-metapac/stm32f779bi"] -stm32f779ii = ["stm32-metapac/stm32f779ii"] -stm32f779ni = ["stm32-metapac/stm32f779ni"] -stm32g030c6 = ["stm32-metapac/stm32g030c6"] -stm32g030c8 = ["stm32-metapac/stm32g030c8"] -stm32g030f6 = ["stm32-metapac/stm32g030f6"] -stm32g030j6 = ["stm32-metapac/stm32g030j6"] -stm32g030k6 = ["stm32-metapac/stm32g030k6"] -stm32g030k8 = ["stm32-metapac/stm32g030k8"] -stm32g031c4 = ["stm32-metapac/stm32g031c4"] -stm32g031c6 = ["stm32-metapac/stm32g031c6"] -stm32g031c8 = ["stm32-metapac/stm32g031c8"] -stm32g031f4 = ["stm32-metapac/stm32g031f4"] -stm32g031f6 = ["stm32-metapac/stm32g031f6"] -stm32g031f8 = ["stm32-metapac/stm32g031f8"] -stm32g031g4 = ["stm32-metapac/stm32g031g4"] -stm32g031g6 = ["stm32-metapac/stm32g031g6"] -stm32g031g8 = ["stm32-metapac/stm32g031g8"] -stm32g031j4 = ["stm32-metapac/stm32g031j4"] -stm32g031j6 = ["stm32-metapac/stm32g031j6"] -stm32g031k4 = ["stm32-metapac/stm32g031k4"] -stm32g031k6 = ["stm32-metapac/stm32g031k6"] -stm32g031k8 = ["stm32-metapac/stm32g031k8"] -stm32g031y8 = ["stm32-metapac/stm32g031y8"] -stm32g041c6 = ["stm32-metapac/stm32g041c6"] -stm32g041c8 = ["stm32-metapac/stm32g041c8"] -stm32g041f6 = ["stm32-metapac/stm32g041f6"] -stm32g041f8 = ["stm32-metapac/stm32g041f8"] -stm32g041g6 = ["stm32-metapac/stm32g041g6"] -stm32g041g8 = ["stm32-metapac/stm32g041g8"] -stm32g041j6 = ["stm32-metapac/stm32g041j6"] -stm32g041k6 = ["stm32-metapac/stm32g041k6"] -stm32g041k8 = ["stm32-metapac/stm32g041k8"] -stm32g041y8 = ["stm32-metapac/stm32g041y8"] -stm32g050c6 = ["stm32-metapac/stm32g050c6"] -stm32g050c8 = ["stm32-metapac/stm32g050c8"] -stm32g050f6 = ["stm32-metapac/stm32g050f6"] -stm32g050k6 = ["stm32-metapac/stm32g050k6"] -stm32g050k8 = ["stm32-metapac/stm32g050k8"] -stm32g051c6 = ["stm32-metapac/stm32g051c6"] -stm32g051c8 = ["stm32-metapac/stm32g051c8"] -stm32g051f6 = ["stm32-metapac/stm32g051f6"] -stm32g051f8 = ["stm32-metapac/stm32g051f8"] -stm32g051g6 = ["stm32-metapac/stm32g051g6"] -stm32g051g8 = ["stm32-metapac/stm32g051g8"] -stm32g051k6 = ["stm32-metapac/stm32g051k6"] -stm32g051k8 = ["stm32-metapac/stm32g051k8"] -stm32g061c6 = ["stm32-metapac/stm32g061c6"] -stm32g061c8 = ["stm32-metapac/stm32g061c8"] -stm32g061f6 = ["stm32-metapac/stm32g061f6"] -stm32g061f8 = ["stm32-metapac/stm32g061f8"] -stm32g061g6 = ["stm32-metapac/stm32g061g6"] -stm32g061g8 = ["stm32-metapac/stm32g061g8"] -stm32g061k6 = ["stm32-metapac/stm32g061k6"] -stm32g061k8 = ["stm32-metapac/stm32g061k8"] -stm32g070cb = ["stm32-metapac/stm32g070cb"] -stm32g070kb = ["stm32-metapac/stm32g070kb"] -stm32g070rb = ["stm32-metapac/stm32g070rb"] -stm32g071c6 = ["stm32-metapac/stm32g071c6"] -stm32g071c8 = ["stm32-metapac/stm32g071c8"] -stm32g071cb = ["stm32-metapac/stm32g071cb"] -stm32g071eb = ["stm32-metapac/stm32g071eb"] -stm32g071g6 = ["stm32-metapac/stm32g071g6"] -stm32g071g8 = ["stm32-metapac/stm32g071g8"] -stm32g071gb = ["stm32-metapac/stm32g071gb"] -stm32g071k6 = ["stm32-metapac/stm32g071k6"] -stm32g071k8 = ["stm32-metapac/stm32g071k8"] -stm32g071kb = ["stm32-metapac/stm32g071kb"] -stm32g071r6 = ["stm32-metapac/stm32g071r6"] -stm32g071r8 = ["stm32-metapac/stm32g071r8"] -stm32g071rb = ["stm32-metapac/stm32g071rb"] -stm32g081cb = ["stm32-metapac/stm32g081cb"] -stm32g081eb = ["stm32-metapac/stm32g081eb"] -stm32g081gb = ["stm32-metapac/stm32g081gb"] -stm32g081kb = ["stm32-metapac/stm32g081kb"] -stm32g081rb = ["stm32-metapac/stm32g081rb"] -stm32g0b0ce = ["stm32-metapac/stm32g0b0ce"] -stm32g0b0ke = ["stm32-metapac/stm32g0b0ke"] -stm32g0b0re = ["stm32-metapac/stm32g0b0re"] -stm32g0b0ve = ["stm32-metapac/stm32g0b0ve"] -stm32g0b1cb = ["stm32-metapac/stm32g0b1cb"] -stm32g0b1cc = ["stm32-metapac/stm32g0b1cc"] -stm32g0b1ce = ["stm32-metapac/stm32g0b1ce"] -stm32g0b1kb = ["stm32-metapac/stm32g0b1kb"] -stm32g0b1kc = ["stm32-metapac/stm32g0b1kc"] -stm32g0b1ke = ["stm32-metapac/stm32g0b1ke"] -stm32g0b1mb = ["stm32-metapac/stm32g0b1mb"] -stm32g0b1mc = ["stm32-metapac/stm32g0b1mc"] -stm32g0b1me = ["stm32-metapac/stm32g0b1me"] -stm32g0b1ne = ["stm32-metapac/stm32g0b1ne"] -stm32g0b1rb = ["stm32-metapac/stm32g0b1rb"] -stm32g0b1rc = ["stm32-metapac/stm32g0b1rc"] -stm32g0b1re = ["stm32-metapac/stm32g0b1re"] -stm32g0b1vb = ["stm32-metapac/stm32g0b1vb"] -stm32g0b1vc = ["stm32-metapac/stm32g0b1vc"] -stm32g0b1ve = ["stm32-metapac/stm32g0b1ve"] -stm32g0c1cc = ["stm32-metapac/stm32g0c1cc"] -stm32g0c1ce = ["stm32-metapac/stm32g0c1ce"] -stm32g0c1kc = ["stm32-metapac/stm32g0c1kc"] -stm32g0c1ke = ["stm32-metapac/stm32g0c1ke"] -stm32g0c1mc = ["stm32-metapac/stm32g0c1mc"] -stm32g0c1me = ["stm32-metapac/stm32g0c1me"] -stm32g0c1ne = ["stm32-metapac/stm32g0c1ne"] -stm32g0c1rc = ["stm32-metapac/stm32g0c1rc"] -stm32g0c1re = ["stm32-metapac/stm32g0c1re"] -stm32g0c1vc = ["stm32-metapac/stm32g0c1vc"] -stm32g0c1ve = ["stm32-metapac/stm32g0c1ve"] -stm32g431c6 = ["stm32-metapac/stm32g431c6"] -stm32g431c8 = ["stm32-metapac/stm32g431c8"] -stm32g431cb = ["stm32-metapac/stm32g431cb"] -stm32g431k6 = ["stm32-metapac/stm32g431k6"] -stm32g431k8 = ["stm32-metapac/stm32g431k8"] -stm32g431kb = ["stm32-metapac/stm32g431kb"] -stm32g431m6 = ["stm32-metapac/stm32g431m6"] -stm32g431m8 = ["stm32-metapac/stm32g431m8"] -stm32g431mb = ["stm32-metapac/stm32g431mb"] -stm32g431r6 = ["stm32-metapac/stm32g431r6"] -stm32g431r8 = ["stm32-metapac/stm32g431r8"] -stm32g431rb = ["stm32-metapac/stm32g431rb"] -stm32g431v6 = ["stm32-metapac/stm32g431v6"] -stm32g431v8 = ["stm32-metapac/stm32g431v8"] -stm32g431vb = ["stm32-metapac/stm32g431vb"] -stm32g441cb = ["stm32-metapac/stm32g441cb"] -stm32g441kb = ["stm32-metapac/stm32g441kb"] -stm32g441mb = ["stm32-metapac/stm32g441mb"] -stm32g441rb = ["stm32-metapac/stm32g441rb"] -stm32g441vb = ["stm32-metapac/stm32g441vb"] -stm32g471cc = ["stm32-metapac/stm32g471cc"] -stm32g471ce = ["stm32-metapac/stm32g471ce"] -stm32g471mc = ["stm32-metapac/stm32g471mc"] -stm32g471me = ["stm32-metapac/stm32g471me"] -stm32g471qc = ["stm32-metapac/stm32g471qc"] -stm32g471qe = ["stm32-metapac/stm32g471qe"] -stm32g471rc = ["stm32-metapac/stm32g471rc"] -stm32g471re = ["stm32-metapac/stm32g471re"] -stm32g471vc = ["stm32-metapac/stm32g471vc"] -stm32g471ve = ["stm32-metapac/stm32g471ve"] -stm32g473cb = ["stm32-metapac/stm32g473cb"] -stm32g473cc = ["stm32-metapac/stm32g473cc"] -stm32g473ce = ["stm32-metapac/stm32g473ce"] -stm32g473mb = ["stm32-metapac/stm32g473mb"] -stm32g473mc = ["stm32-metapac/stm32g473mc"] -stm32g473me = ["stm32-metapac/stm32g473me"] -stm32g473pb = ["stm32-metapac/stm32g473pb"] -stm32g473pc = ["stm32-metapac/stm32g473pc"] -stm32g473pe = ["stm32-metapac/stm32g473pe"] -stm32g473qb = ["stm32-metapac/stm32g473qb"] -stm32g473qc = ["stm32-metapac/stm32g473qc"] -stm32g473qe = ["stm32-metapac/stm32g473qe"] -stm32g473rb = ["stm32-metapac/stm32g473rb"] -stm32g473rc = ["stm32-metapac/stm32g473rc"] -stm32g473re = ["stm32-metapac/stm32g473re"] -stm32g473vb = ["stm32-metapac/stm32g473vb"] -stm32g473vc = ["stm32-metapac/stm32g473vc"] -stm32g473ve = ["stm32-metapac/stm32g473ve"] -stm32g474cb = ["stm32-metapac/stm32g474cb"] -stm32g474cc = ["stm32-metapac/stm32g474cc"] -stm32g474ce = ["stm32-metapac/stm32g474ce"] -stm32g474mb = ["stm32-metapac/stm32g474mb"] -stm32g474mc = ["stm32-metapac/stm32g474mc"] -stm32g474me = ["stm32-metapac/stm32g474me"] -stm32g474pb = ["stm32-metapac/stm32g474pb"] -stm32g474pc = ["stm32-metapac/stm32g474pc"] -stm32g474pe = ["stm32-metapac/stm32g474pe"] -stm32g474qb = ["stm32-metapac/stm32g474qb"] -stm32g474qc = ["stm32-metapac/stm32g474qc"] -stm32g474qe = ["stm32-metapac/stm32g474qe"] -stm32g474rb = ["stm32-metapac/stm32g474rb"] -stm32g474rc = ["stm32-metapac/stm32g474rc"] -stm32g474re = ["stm32-metapac/stm32g474re"] -stm32g474vb = ["stm32-metapac/stm32g474vb"] -stm32g474vc = ["stm32-metapac/stm32g474vc"] -stm32g474ve = ["stm32-metapac/stm32g474ve"] -stm32g483ce = ["stm32-metapac/stm32g483ce"] -stm32g483me = ["stm32-metapac/stm32g483me"] -stm32g483pe = ["stm32-metapac/stm32g483pe"] -stm32g483qe = ["stm32-metapac/stm32g483qe"] -stm32g483re = ["stm32-metapac/stm32g483re"] -stm32g483ve = ["stm32-metapac/stm32g483ve"] -stm32g484ce = ["stm32-metapac/stm32g484ce"] -stm32g484me = ["stm32-metapac/stm32g484me"] -stm32g484pe = ["stm32-metapac/stm32g484pe"] -stm32g484qe = ["stm32-metapac/stm32g484qe"] -stm32g484re = ["stm32-metapac/stm32g484re"] -stm32g484ve = ["stm32-metapac/stm32g484ve"] -stm32g491cc = ["stm32-metapac/stm32g491cc"] -stm32g491ce = ["stm32-metapac/stm32g491ce"] -stm32g491kc = ["stm32-metapac/stm32g491kc"] -stm32g491ke = ["stm32-metapac/stm32g491ke"] -stm32g491mc = ["stm32-metapac/stm32g491mc"] -stm32g491me = ["stm32-metapac/stm32g491me"] -stm32g491rc = ["stm32-metapac/stm32g491rc"] -stm32g491re = ["stm32-metapac/stm32g491re"] -stm32g491vc = ["stm32-metapac/stm32g491vc"] -stm32g491ve = ["stm32-metapac/stm32g491ve"] -stm32g4a1ce = ["stm32-metapac/stm32g4a1ce"] -stm32g4a1ke = ["stm32-metapac/stm32g4a1ke"] -stm32g4a1me = ["stm32-metapac/stm32g4a1me"] -stm32g4a1re = ["stm32-metapac/stm32g4a1re"] -stm32g4a1ve = ["stm32-metapac/stm32g4a1ve"] -stm32h503cb = ["stm32-metapac/stm32h503cb"] -stm32h503eb = ["stm32-metapac/stm32h503eb"] -stm32h503kb = ["stm32-metapac/stm32h503kb"] -stm32h503rb = ["stm32-metapac/stm32h503rb"] -stm32h562ag = ["stm32-metapac/stm32h562ag"] -stm32h562ai = ["stm32-metapac/stm32h562ai"] -stm32h562ig = ["stm32-metapac/stm32h562ig"] -stm32h562ii = ["stm32-metapac/stm32h562ii"] -stm32h562rg = ["stm32-metapac/stm32h562rg"] -stm32h562ri = ["stm32-metapac/stm32h562ri"] -stm32h562vg = ["stm32-metapac/stm32h562vg"] -stm32h562vi = ["stm32-metapac/stm32h562vi"] -stm32h562zg = ["stm32-metapac/stm32h562zg"] -stm32h562zi = ["stm32-metapac/stm32h562zi"] -stm32h563ag = ["stm32-metapac/stm32h563ag"] -stm32h563ai = ["stm32-metapac/stm32h563ai"] -stm32h563ig = ["stm32-metapac/stm32h563ig"] -stm32h563ii = ["stm32-metapac/stm32h563ii"] -stm32h563mi = ["stm32-metapac/stm32h563mi"] -stm32h563rg = ["stm32-metapac/stm32h563rg"] -stm32h563ri = ["stm32-metapac/stm32h563ri"] -stm32h563vg = ["stm32-metapac/stm32h563vg"] -stm32h563vi = ["stm32-metapac/stm32h563vi"] -stm32h563zg = ["stm32-metapac/stm32h563zg"] -stm32h563zi = ["stm32-metapac/stm32h563zi"] -stm32h573ai = ["stm32-metapac/stm32h573ai"] -stm32h573ii = ["stm32-metapac/stm32h573ii"] -stm32h573mi = ["stm32-metapac/stm32h573mi"] -stm32h573ri = ["stm32-metapac/stm32h573ri"] -stm32h573vi = ["stm32-metapac/stm32h573vi"] -stm32h573zi = ["stm32-metapac/stm32h573zi"] -stm32h723ve = ["stm32-metapac/stm32h723ve"] -stm32h723vg = ["stm32-metapac/stm32h723vg"] -stm32h723ze = ["stm32-metapac/stm32h723ze"] -stm32h723zg = ["stm32-metapac/stm32h723zg"] -stm32h725ae = ["stm32-metapac/stm32h725ae"] -stm32h725ag = ["stm32-metapac/stm32h725ag"] -stm32h725ie = ["stm32-metapac/stm32h725ie"] -stm32h725ig = ["stm32-metapac/stm32h725ig"] -stm32h725re = ["stm32-metapac/stm32h725re"] -stm32h725rg = ["stm32-metapac/stm32h725rg"] -stm32h725ve = ["stm32-metapac/stm32h725ve"] -stm32h725vg = ["stm32-metapac/stm32h725vg"] -stm32h725ze = ["stm32-metapac/stm32h725ze"] -stm32h725zg = ["stm32-metapac/stm32h725zg"] -stm32h730ab = ["stm32-metapac/stm32h730ab"] -stm32h730ib = ["stm32-metapac/stm32h730ib"] -stm32h730vb = ["stm32-metapac/stm32h730vb"] -stm32h730zb = ["stm32-metapac/stm32h730zb"] -stm32h733vg = ["stm32-metapac/stm32h733vg"] -stm32h733zg = ["stm32-metapac/stm32h733zg"] -stm32h735ag = ["stm32-metapac/stm32h735ag"] -stm32h735ig = ["stm32-metapac/stm32h735ig"] -stm32h735rg = ["stm32-metapac/stm32h735rg"] -stm32h735vg = ["stm32-metapac/stm32h735vg"] -stm32h735zg = ["stm32-metapac/stm32h735zg"] -stm32h742ag = ["stm32-metapac/stm32h742ag"] -stm32h742ai = ["stm32-metapac/stm32h742ai"] -stm32h742bg = ["stm32-metapac/stm32h742bg"] -stm32h742bi = ["stm32-metapac/stm32h742bi"] -stm32h742ig = ["stm32-metapac/stm32h742ig"] -stm32h742ii = ["stm32-metapac/stm32h742ii"] -stm32h742vg = ["stm32-metapac/stm32h742vg"] -stm32h742vi = ["stm32-metapac/stm32h742vi"] -stm32h742xg = ["stm32-metapac/stm32h742xg"] -stm32h742xi = ["stm32-metapac/stm32h742xi"] -stm32h742zg = ["stm32-metapac/stm32h742zg"] -stm32h742zi = ["stm32-metapac/stm32h742zi"] -stm32h743ag = ["stm32-metapac/stm32h743ag"] -stm32h743ai = ["stm32-metapac/stm32h743ai"] -stm32h743bg = ["stm32-metapac/stm32h743bg"] -stm32h743bi = ["stm32-metapac/stm32h743bi"] -stm32h743ig = ["stm32-metapac/stm32h743ig"] -stm32h743ii = ["stm32-metapac/stm32h743ii"] -stm32h743vg = ["stm32-metapac/stm32h743vg"] -stm32h743vi = ["stm32-metapac/stm32h743vi"] -stm32h743xg = ["stm32-metapac/stm32h743xg"] -stm32h743xi = ["stm32-metapac/stm32h743xi"] -stm32h743zg = ["stm32-metapac/stm32h743zg"] -stm32h743zi = ["stm32-metapac/stm32h743zi"] -stm32h745bg-cm7 = ["stm32-metapac/stm32h745bg-cm7"] -stm32h745bg-cm4 = ["stm32-metapac/stm32h745bg-cm4"] -stm32h745bi-cm7 = ["stm32-metapac/stm32h745bi-cm7"] -stm32h745bi-cm4 = ["stm32-metapac/stm32h745bi-cm4"] -stm32h745ig-cm7 = ["stm32-metapac/stm32h745ig-cm7"] -stm32h745ig-cm4 = ["stm32-metapac/stm32h745ig-cm4"] -stm32h745ii-cm7 = ["stm32-metapac/stm32h745ii-cm7"] -stm32h745ii-cm4 = ["stm32-metapac/stm32h745ii-cm4"] -stm32h745xg-cm7 = ["stm32-metapac/stm32h745xg-cm7"] -stm32h745xg-cm4 = ["stm32-metapac/stm32h745xg-cm4"] -stm32h745xi-cm7 = ["stm32-metapac/stm32h745xi-cm7"] -stm32h745xi-cm4 = ["stm32-metapac/stm32h745xi-cm4"] -stm32h745zg-cm7 = ["stm32-metapac/stm32h745zg-cm7"] -stm32h745zg-cm4 = ["stm32-metapac/stm32h745zg-cm4"] -stm32h745zi-cm7 = ["stm32-metapac/stm32h745zi-cm7"] -stm32h745zi-cm4 = ["stm32-metapac/stm32h745zi-cm4"] -stm32h747ag-cm7 = ["stm32-metapac/stm32h747ag-cm7"] -stm32h747ag-cm4 = ["stm32-metapac/stm32h747ag-cm4"] -stm32h747ai-cm7 = ["stm32-metapac/stm32h747ai-cm7"] -stm32h747ai-cm4 = ["stm32-metapac/stm32h747ai-cm4"] -stm32h747bg-cm7 = ["stm32-metapac/stm32h747bg-cm7"] -stm32h747bg-cm4 = ["stm32-metapac/stm32h747bg-cm4"] -stm32h747bi-cm7 = ["stm32-metapac/stm32h747bi-cm7"] -stm32h747bi-cm4 = ["stm32-metapac/stm32h747bi-cm4"] -stm32h747ig-cm7 = ["stm32-metapac/stm32h747ig-cm7"] -stm32h747ig-cm4 = ["stm32-metapac/stm32h747ig-cm4"] -stm32h747ii-cm7 = ["stm32-metapac/stm32h747ii-cm7"] -stm32h747ii-cm4 = ["stm32-metapac/stm32h747ii-cm4"] -stm32h747xg-cm7 = ["stm32-metapac/stm32h747xg-cm7"] -stm32h747xg-cm4 = ["stm32-metapac/stm32h747xg-cm4"] -stm32h747xi-cm7 = ["stm32-metapac/stm32h747xi-cm7"] -stm32h747xi-cm4 = ["stm32-metapac/stm32h747xi-cm4"] -stm32h747zi-cm7 = ["stm32-metapac/stm32h747zi-cm7"] -stm32h747zi-cm4 = ["stm32-metapac/stm32h747zi-cm4"] -stm32h750ib = ["stm32-metapac/stm32h750ib"] -stm32h750vb = ["stm32-metapac/stm32h750vb"] -stm32h750xb = ["stm32-metapac/stm32h750xb"] -stm32h750zb = ["stm32-metapac/stm32h750zb"] -stm32h753ai = ["stm32-metapac/stm32h753ai"] -stm32h753bi = ["stm32-metapac/stm32h753bi"] -stm32h753ii = ["stm32-metapac/stm32h753ii"] -stm32h753vi = ["stm32-metapac/stm32h753vi"] -stm32h753xi = ["stm32-metapac/stm32h753xi"] -stm32h753zi = ["stm32-metapac/stm32h753zi"] -stm32h755bi-cm7 = ["stm32-metapac/stm32h755bi-cm7"] -stm32h755bi-cm4 = ["stm32-metapac/stm32h755bi-cm4"] -stm32h755ii-cm7 = ["stm32-metapac/stm32h755ii-cm7"] -stm32h755ii-cm4 = ["stm32-metapac/stm32h755ii-cm4"] -stm32h755xi-cm7 = ["stm32-metapac/stm32h755xi-cm7"] -stm32h755xi-cm4 = ["stm32-metapac/stm32h755xi-cm4"] -stm32h755zi-cm7 = ["stm32-metapac/stm32h755zi-cm7"] -stm32h755zi-cm4 = ["stm32-metapac/stm32h755zi-cm4"] -stm32h757ai-cm7 = ["stm32-metapac/stm32h757ai-cm7"] -stm32h757ai-cm4 = ["stm32-metapac/stm32h757ai-cm4"] -stm32h757bi-cm7 = ["stm32-metapac/stm32h757bi-cm7"] -stm32h757bi-cm4 = ["stm32-metapac/stm32h757bi-cm4"] -stm32h757ii-cm7 = ["stm32-metapac/stm32h757ii-cm7"] -stm32h757ii-cm4 = ["stm32-metapac/stm32h757ii-cm4"] -stm32h757xi-cm7 = ["stm32-metapac/stm32h757xi-cm7"] -stm32h757xi-cm4 = ["stm32-metapac/stm32h757xi-cm4"] -stm32h757zi-cm7 = ["stm32-metapac/stm32h757zi-cm7"] -stm32h757zi-cm4 = ["stm32-metapac/stm32h757zi-cm4"] -stm32h7a3ag = ["stm32-metapac/stm32h7a3ag"] -stm32h7a3ai = ["stm32-metapac/stm32h7a3ai"] -stm32h7a3ig = ["stm32-metapac/stm32h7a3ig"] -stm32h7a3ii = ["stm32-metapac/stm32h7a3ii"] -stm32h7a3lg = ["stm32-metapac/stm32h7a3lg"] -stm32h7a3li = ["stm32-metapac/stm32h7a3li"] -stm32h7a3ng = ["stm32-metapac/stm32h7a3ng"] -stm32h7a3ni = ["stm32-metapac/stm32h7a3ni"] -stm32h7a3qi = ["stm32-metapac/stm32h7a3qi"] -stm32h7a3rg = ["stm32-metapac/stm32h7a3rg"] -stm32h7a3ri = ["stm32-metapac/stm32h7a3ri"] -stm32h7a3vg = ["stm32-metapac/stm32h7a3vg"] -stm32h7a3vi = ["stm32-metapac/stm32h7a3vi"] -stm32h7a3zg = ["stm32-metapac/stm32h7a3zg"] -stm32h7a3zi = ["stm32-metapac/stm32h7a3zi"] -stm32h7b0ab = ["stm32-metapac/stm32h7b0ab"] -stm32h7b0ib = ["stm32-metapac/stm32h7b0ib"] -stm32h7b0rb = ["stm32-metapac/stm32h7b0rb"] -stm32h7b0vb = ["stm32-metapac/stm32h7b0vb"] -stm32h7b0zb = ["stm32-metapac/stm32h7b0zb"] -stm32h7b3ai = ["stm32-metapac/stm32h7b3ai"] -stm32h7b3ii = ["stm32-metapac/stm32h7b3ii"] -stm32h7b3li = ["stm32-metapac/stm32h7b3li"] -stm32h7b3ni = ["stm32-metapac/stm32h7b3ni"] -stm32h7b3qi = ["stm32-metapac/stm32h7b3qi"] -stm32h7b3ri = ["stm32-metapac/stm32h7b3ri"] -stm32h7b3vi = ["stm32-metapac/stm32h7b3vi"] -stm32h7b3zi = ["stm32-metapac/stm32h7b3zi"] -stm32l010c6 = ["stm32-metapac/stm32l010c6"] -stm32l010f4 = ["stm32-metapac/stm32l010f4"] -stm32l010k4 = ["stm32-metapac/stm32l010k4"] -stm32l010k8 = ["stm32-metapac/stm32l010k8"] -stm32l010r8 = ["stm32-metapac/stm32l010r8"] -stm32l010rb = ["stm32-metapac/stm32l010rb"] -stm32l011d3 = ["stm32-metapac/stm32l011d3"] -stm32l011d4 = ["stm32-metapac/stm32l011d4"] -stm32l011e3 = ["stm32-metapac/stm32l011e3"] -stm32l011e4 = ["stm32-metapac/stm32l011e4"] -stm32l011f3 = ["stm32-metapac/stm32l011f3"] -stm32l011f4 = ["stm32-metapac/stm32l011f4"] -stm32l011g3 = ["stm32-metapac/stm32l011g3"] -stm32l011g4 = ["stm32-metapac/stm32l011g4"] -stm32l011k3 = ["stm32-metapac/stm32l011k3"] -stm32l011k4 = ["stm32-metapac/stm32l011k4"] -stm32l021d4 = ["stm32-metapac/stm32l021d4"] -stm32l021f4 = ["stm32-metapac/stm32l021f4"] -stm32l021g4 = ["stm32-metapac/stm32l021g4"] -stm32l021k4 = ["stm32-metapac/stm32l021k4"] -stm32l031c4 = ["stm32-metapac/stm32l031c4"] -stm32l031c6 = ["stm32-metapac/stm32l031c6"] -stm32l031e4 = ["stm32-metapac/stm32l031e4"] -stm32l031e6 = ["stm32-metapac/stm32l031e6"] -stm32l031f4 = ["stm32-metapac/stm32l031f4"] -stm32l031f6 = ["stm32-metapac/stm32l031f6"] -stm32l031g4 = ["stm32-metapac/stm32l031g4"] -stm32l031g6 = ["stm32-metapac/stm32l031g6"] -stm32l031k4 = ["stm32-metapac/stm32l031k4"] -stm32l031k6 = ["stm32-metapac/stm32l031k6"] -stm32l041c4 = ["stm32-metapac/stm32l041c4"] -stm32l041c6 = ["stm32-metapac/stm32l041c6"] -stm32l041e6 = ["stm32-metapac/stm32l041e6"] -stm32l041f6 = ["stm32-metapac/stm32l041f6"] -stm32l041g6 = ["stm32-metapac/stm32l041g6"] -stm32l041k6 = ["stm32-metapac/stm32l041k6"] -stm32l051c6 = ["stm32-metapac/stm32l051c6"] -stm32l051c8 = ["stm32-metapac/stm32l051c8"] -stm32l051k6 = ["stm32-metapac/stm32l051k6"] -stm32l051k8 = ["stm32-metapac/stm32l051k8"] -stm32l051r6 = ["stm32-metapac/stm32l051r6"] -stm32l051r8 = ["stm32-metapac/stm32l051r8"] -stm32l051t6 = ["stm32-metapac/stm32l051t6"] -stm32l051t8 = ["stm32-metapac/stm32l051t8"] -stm32l052c6 = ["stm32-metapac/stm32l052c6"] -stm32l052c8 = ["stm32-metapac/stm32l052c8"] -stm32l052k6 = ["stm32-metapac/stm32l052k6"] -stm32l052k8 = ["stm32-metapac/stm32l052k8"] -stm32l052r6 = ["stm32-metapac/stm32l052r6"] -stm32l052r8 = ["stm32-metapac/stm32l052r8"] -stm32l052t6 = ["stm32-metapac/stm32l052t6"] -stm32l052t8 = ["stm32-metapac/stm32l052t8"] -stm32l053c6 = ["stm32-metapac/stm32l053c6"] -stm32l053c8 = ["stm32-metapac/stm32l053c8"] -stm32l053r6 = ["stm32-metapac/stm32l053r6"] -stm32l053r8 = ["stm32-metapac/stm32l053r8"] -stm32l062c8 = ["stm32-metapac/stm32l062c8"] -stm32l062k8 = ["stm32-metapac/stm32l062k8"] -stm32l063c8 = ["stm32-metapac/stm32l063c8"] -stm32l063r8 = ["stm32-metapac/stm32l063r8"] -stm32l071c8 = ["stm32-metapac/stm32l071c8"] -stm32l071cb = ["stm32-metapac/stm32l071cb"] -stm32l071cz = ["stm32-metapac/stm32l071cz"] -stm32l071k8 = ["stm32-metapac/stm32l071k8"] -stm32l071kb = ["stm32-metapac/stm32l071kb"] -stm32l071kz = ["stm32-metapac/stm32l071kz"] -stm32l071rb = ["stm32-metapac/stm32l071rb"] -stm32l071rz = ["stm32-metapac/stm32l071rz"] -stm32l071v8 = ["stm32-metapac/stm32l071v8"] -stm32l071vb = ["stm32-metapac/stm32l071vb"] -stm32l071vz = ["stm32-metapac/stm32l071vz"] -stm32l072cb = ["stm32-metapac/stm32l072cb"] -stm32l072cz = ["stm32-metapac/stm32l072cz"] -stm32l072kb = ["stm32-metapac/stm32l072kb"] -stm32l072kz = ["stm32-metapac/stm32l072kz"] -stm32l072rb = ["stm32-metapac/stm32l072rb"] -stm32l072rz = ["stm32-metapac/stm32l072rz"] -stm32l072v8 = ["stm32-metapac/stm32l072v8"] -stm32l072vb = ["stm32-metapac/stm32l072vb"] -stm32l072vz = ["stm32-metapac/stm32l072vz"] -stm32l073cb = ["stm32-metapac/stm32l073cb"] -stm32l073cz = ["stm32-metapac/stm32l073cz"] -stm32l073rb = ["stm32-metapac/stm32l073rb"] -stm32l073rz = ["stm32-metapac/stm32l073rz"] -stm32l073v8 = ["stm32-metapac/stm32l073v8"] -stm32l073vb = ["stm32-metapac/stm32l073vb"] -stm32l073vz = ["stm32-metapac/stm32l073vz"] -stm32l081cb = ["stm32-metapac/stm32l081cb"] -stm32l081cz = ["stm32-metapac/stm32l081cz"] -stm32l081kz = ["stm32-metapac/stm32l081kz"] -stm32l082cz = ["stm32-metapac/stm32l082cz"] -stm32l082kb = ["stm32-metapac/stm32l082kb"] -stm32l082kz = ["stm32-metapac/stm32l082kz"] -stm32l083cb = ["stm32-metapac/stm32l083cb"] -stm32l083cz = ["stm32-metapac/stm32l083cz"] -stm32l083rb = ["stm32-metapac/stm32l083rb"] -stm32l083rz = ["stm32-metapac/stm32l083rz"] -stm32l083v8 = ["stm32-metapac/stm32l083v8"] -stm32l083vb = ["stm32-metapac/stm32l083vb"] -stm32l083vz = ["stm32-metapac/stm32l083vz"] -stm32l100c6 = ["stm32-metapac/stm32l100c6"] -stm32l100c6-a = ["stm32-metapac/stm32l100c6-a"] -stm32l100r8 = ["stm32-metapac/stm32l100r8"] -stm32l100r8-a = ["stm32-metapac/stm32l100r8-a"] -stm32l100rb = ["stm32-metapac/stm32l100rb"] -stm32l100rb-a = ["stm32-metapac/stm32l100rb-a"] -stm32l100rc = ["stm32-metapac/stm32l100rc"] -stm32l151c6 = ["stm32-metapac/stm32l151c6"] -stm32l151c6-a = ["stm32-metapac/stm32l151c6-a"] -stm32l151c8 = ["stm32-metapac/stm32l151c8"] -stm32l151c8-a = ["stm32-metapac/stm32l151c8-a"] -stm32l151cb = ["stm32-metapac/stm32l151cb"] -stm32l151cb-a = ["stm32-metapac/stm32l151cb-a"] -stm32l151cc = ["stm32-metapac/stm32l151cc"] -stm32l151qc = ["stm32-metapac/stm32l151qc"] -stm32l151qd = ["stm32-metapac/stm32l151qd"] -stm32l151qe = ["stm32-metapac/stm32l151qe"] -stm32l151r6 = ["stm32-metapac/stm32l151r6"] -stm32l151r6-a = ["stm32-metapac/stm32l151r6-a"] -stm32l151r8 = ["stm32-metapac/stm32l151r8"] -stm32l151r8-a = ["stm32-metapac/stm32l151r8-a"] -stm32l151rb = ["stm32-metapac/stm32l151rb"] -stm32l151rb-a = ["stm32-metapac/stm32l151rb-a"] -stm32l151rc = ["stm32-metapac/stm32l151rc"] -stm32l151rc-a = ["stm32-metapac/stm32l151rc-a"] -stm32l151rd = ["stm32-metapac/stm32l151rd"] -stm32l151re = ["stm32-metapac/stm32l151re"] -stm32l151uc = ["stm32-metapac/stm32l151uc"] -stm32l151v8 = ["stm32-metapac/stm32l151v8"] -stm32l151v8-a = ["stm32-metapac/stm32l151v8-a"] -stm32l151vb = ["stm32-metapac/stm32l151vb"] -stm32l151vb-a = ["stm32-metapac/stm32l151vb-a"] -stm32l151vc = ["stm32-metapac/stm32l151vc"] -stm32l151vc-a = ["stm32-metapac/stm32l151vc-a"] -stm32l151vd = ["stm32-metapac/stm32l151vd"] -stm32l151vd-x = ["stm32-metapac/stm32l151vd-x"] -stm32l151ve = ["stm32-metapac/stm32l151ve"] -stm32l151zc = ["stm32-metapac/stm32l151zc"] -stm32l151zd = ["stm32-metapac/stm32l151zd"] -stm32l151ze = ["stm32-metapac/stm32l151ze"] -stm32l152c6 = ["stm32-metapac/stm32l152c6"] -stm32l152c6-a = ["stm32-metapac/stm32l152c6-a"] -stm32l152c8 = ["stm32-metapac/stm32l152c8"] -stm32l152c8-a = ["stm32-metapac/stm32l152c8-a"] -stm32l152cb = ["stm32-metapac/stm32l152cb"] -stm32l152cb-a = ["stm32-metapac/stm32l152cb-a"] -stm32l152cc = ["stm32-metapac/stm32l152cc"] -stm32l152qc = ["stm32-metapac/stm32l152qc"] -stm32l152qd = ["stm32-metapac/stm32l152qd"] -stm32l152qe = ["stm32-metapac/stm32l152qe"] -stm32l152r6 = ["stm32-metapac/stm32l152r6"] -stm32l152r6-a = ["stm32-metapac/stm32l152r6-a"] -stm32l152r8 = ["stm32-metapac/stm32l152r8"] -stm32l152r8-a = ["stm32-metapac/stm32l152r8-a"] -stm32l152rb = ["stm32-metapac/stm32l152rb"] -stm32l152rb-a = ["stm32-metapac/stm32l152rb-a"] -stm32l152rc = ["stm32-metapac/stm32l152rc"] -stm32l152rc-a = ["stm32-metapac/stm32l152rc-a"] -stm32l152rd = ["stm32-metapac/stm32l152rd"] -stm32l152re = ["stm32-metapac/stm32l152re"] -stm32l152uc = ["stm32-metapac/stm32l152uc"] -stm32l152v8 = ["stm32-metapac/stm32l152v8"] -stm32l152v8-a = ["stm32-metapac/stm32l152v8-a"] -stm32l152vb = ["stm32-metapac/stm32l152vb"] -stm32l152vb-a = ["stm32-metapac/stm32l152vb-a"] -stm32l152vc = ["stm32-metapac/stm32l152vc"] -stm32l152vc-a = ["stm32-metapac/stm32l152vc-a"] -stm32l152vd = ["stm32-metapac/stm32l152vd"] -stm32l152vd-x = ["stm32-metapac/stm32l152vd-x"] -stm32l152ve = ["stm32-metapac/stm32l152ve"] -stm32l152zc = ["stm32-metapac/stm32l152zc"] -stm32l152zd = ["stm32-metapac/stm32l152zd"] -stm32l152ze = ["stm32-metapac/stm32l152ze"] -stm32l162qc = ["stm32-metapac/stm32l162qc"] -stm32l162qd = ["stm32-metapac/stm32l162qd"] -stm32l162rc = ["stm32-metapac/stm32l162rc"] -stm32l162rc-a = ["stm32-metapac/stm32l162rc-a"] -stm32l162rd = ["stm32-metapac/stm32l162rd"] -stm32l162re = ["stm32-metapac/stm32l162re"] -stm32l162vc = ["stm32-metapac/stm32l162vc"] -stm32l162vc-a = ["stm32-metapac/stm32l162vc-a"] -stm32l162vd = ["stm32-metapac/stm32l162vd"] -stm32l162vd-x = ["stm32-metapac/stm32l162vd-x"] -stm32l162ve = ["stm32-metapac/stm32l162ve"] -stm32l162zc = ["stm32-metapac/stm32l162zc"] -stm32l162zd = ["stm32-metapac/stm32l162zd"] -stm32l162ze = ["stm32-metapac/stm32l162ze"] -stm32l412c8 = ["stm32-metapac/stm32l412c8"] -stm32l412cb = ["stm32-metapac/stm32l412cb"] -stm32l412k8 = ["stm32-metapac/stm32l412k8"] -stm32l412kb = ["stm32-metapac/stm32l412kb"] -stm32l412r8 = ["stm32-metapac/stm32l412r8"] -stm32l412rb = ["stm32-metapac/stm32l412rb"] -stm32l412t8 = ["stm32-metapac/stm32l412t8"] -stm32l412tb = ["stm32-metapac/stm32l412tb"] -stm32l422cb = ["stm32-metapac/stm32l422cb"] -stm32l422kb = ["stm32-metapac/stm32l422kb"] -stm32l422rb = ["stm32-metapac/stm32l422rb"] -stm32l422tb = ["stm32-metapac/stm32l422tb"] -stm32l431cb = ["stm32-metapac/stm32l431cb"] -stm32l431cc = ["stm32-metapac/stm32l431cc"] -stm32l431kb = ["stm32-metapac/stm32l431kb"] -stm32l431kc = ["stm32-metapac/stm32l431kc"] -stm32l431rb = ["stm32-metapac/stm32l431rb"] -stm32l431rc = ["stm32-metapac/stm32l431rc"] -stm32l431vc = ["stm32-metapac/stm32l431vc"] -stm32l432kb = ["stm32-metapac/stm32l432kb"] -stm32l432kc = ["stm32-metapac/stm32l432kc"] -stm32l433cb = ["stm32-metapac/stm32l433cb"] -stm32l433cc = ["stm32-metapac/stm32l433cc"] -stm32l433rb = ["stm32-metapac/stm32l433rb"] -stm32l433rc = ["stm32-metapac/stm32l433rc"] -stm32l433vc = ["stm32-metapac/stm32l433vc"] -stm32l442kc = ["stm32-metapac/stm32l442kc"] -stm32l443cc = ["stm32-metapac/stm32l443cc"] -stm32l443rc = ["stm32-metapac/stm32l443rc"] -stm32l443vc = ["stm32-metapac/stm32l443vc"] -stm32l451cc = ["stm32-metapac/stm32l451cc"] -stm32l451ce = ["stm32-metapac/stm32l451ce"] -stm32l451rc = ["stm32-metapac/stm32l451rc"] -stm32l451re = ["stm32-metapac/stm32l451re"] -stm32l451vc = ["stm32-metapac/stm32l451vc"] -stm32l451ve = ["stm32-metapac/stm32l451ve"] -stm32l452cc = ["stm32-metapac/stm32l452cc"] -stm32l452ce = ["stm32-metapac/stm32l452ce"] -stm32l452rc = ["stm32-metapac/stm32l452rc"] -stm32l452re = ["stm32-metapac/stm32l452re"] -stm32l452vc = ["stm32-metapac/stm32l452vc"] -stm32l452ve = ["stm32-metapac/stm32l452ve"] -stm32l462ce = ["stm32-metapac/stm32l462ce"] -stm32l462re = ["stm32-metapac/stm32l462re"] -stm32l462ve = ["stm32-metapac/stm32l462ve"] -stm32l471qe = ["stm32-metapac/stm32l471qe"] -stm32l471qg = ["stm32-metapac/stm32l471qg"] -stm32l471re = ["stm32-metapac/stm32l471re"] -stm32l471rg = ["stm32-metapac/stm32l471rg"] -stm32l471ve = ["stm32-metapac/stm32l471ve"] -stm32l471vg = ["stm32-metapac/stm32l471vg"] -stm32l471ze = ["stm32-metapac/stm32l471ze"] -stm32l471zg = ["stm32-metapac/stm32l471zg"] -stm32l475rc = ["stm32-metapac/stm32l475rc"] -stm32l475re = ["stm32-metapac/stm32l475re"] -stm32l475rg = ["stm32-metapac/stm32l475rg"] -stm32l475vc = ["stm32-metapac/stm32l475vc"] -stm32l475ve = ["stm32-metapac/stm32l475ve"] -stm32l475vg = ["stm32-metapac/stm32l475vg"] -stm32l476je = ["stm32-metapac/stm32l476je"] -stm32l476jg = ["stm32-metapac/stm32l476jg"] -stm32l476me = ["stm32-metapac/stm32l476me"] -stm32l476mg = ["stm32-metapac/stm32l476mg"] -stm32l476qe = ["stm32-metapac/stm32l476qe"] -stm32l476qg = ["stm32-metapac/stm32l476qg"] -stm32l476rc = ["stm32-metapac/stm32l476rc"] -stm32l476re = ["stm32-metapac/stm32l476re"] -stm32l476rg = ["stm32-metapac/stm32l476rg"] -stm32l476vc = ["stm32-metapac/stm32l476vc"] -stm32l476ve = ["stm32-metapac/stm32l476ve"] -stm32l476vg = ["stm32-metapac/stm32l476vg"] -stm32l476ze = ["stm32-metapac/stm32l476ze"] -stm32l476zg = ["stm32-metapac/stm32l476zg"] -stm32l486jg = ["stm32-metapac/stm32l486jg"] -stm32l486qg = ["stm32-metapac/stm32l486qg"] -stm32l486rg = ["stm32-metapac/stm32l486rg"] -stm32l486vg = ["stm32-metapac/stm32l486vg"] -stm32l486zg = ["stm32-metapac/stm32l486zg"] -stm32l496ae = ["stm32-metapac/stm32l496ae"] -stm32l496ag = ["stm32-metapac/stm32l496ag"] -stm32l496qe = ["stm32-metapac/stm32l496qe"] -stm32l496qg = ["stm32-metapac/stm32l496qg"] -stm32l496re = ["stm32-metapac/stm32l496re"] -stm32l496rg = ["stm32-metapac/stm32l496rg"] -stm32l496ve = ["stm32-metapac/stm32l496ve"] -stm32l496vg = ["stm32-metapac/stm32l496vg"] -stm32l496wg = ["stm32-metapac/stm32l496wg"] -stm32l496ze = ["stm32-metapac/stm32l496ze"] -stm32l496zg = ["stm32-metapac/stm32l496zg"] -stm32l4a6ag = ["stm32-metapac/stm32l4a6ag"] -stm32l4a6qg = ["stm32-metapac/stm32l4a6qg"] -stm32l4a6rg = ["stm32-metapac/stm32l4a6rg"] -stm32l4a6vg = ["stm32-metapac/stm32l4a6vg"] -stm32l4a6zg = ["stm32-metapac/stm32l4a6zg"] -stm32l4p5ae = ["stm32-metapac/stm32l4p5ae"] -stm32l4p5ag = ["stm32-metapac/stm32l4p5ag"] -stm32l4p5ce = ["stm32-metapac/stm32l4p5ce"] -stm32l4p5cg = ["stm32-metapac/stm32l4p5cg"] -stm32l4p5qe = ["stm32-metapac/stm32l4p5qe"] -stm32l4p5qg = ["stm32-metapac/stm32l4p5qg"] -stm32l4p5re = ["stm32-metapac/stm32l4p5re"] -stm32l4p5rg = ["stm32-metapac/stm32l4p5rg"] -stm32l4p5ve = ["stm32-metapac/stm32l4p5ve"] -stm32l4p5vg = ["stm32-metapac/stm32l4p5vg"] -stm32l4p5ze = ["stm32-metapac/stm32l4p5ze"] -stm32l4p5zg = ["stm32-metapac/stm32l4p5zg"] -stm32l4q5ag = ["stm32-metapac/stm32l4q5ag"] -stm32l4q5cg = ["stm32-metapac/stm32l4q5cg"] -stm32l4q5qg = ["stm32-metapac/stm32l4q5qg"] -stm32l4q5rg = ["stm32-metapac/stm32l4q5rg"] -stm32l4q5vg = ["stm32-metapac/stm32l4q5vg"] -stm32l4q5zg = ["stm32-metapac/stm32l4q5zg"] -stm32l4r5ag = ["stm32-metapac/stm32l4r5ag"] -stm32l4r5ai = ["stm32-metapac/stm32l4r5ai"] -stm32l4r5qg = ["stm32-metapac/stm32l4r5qg"] -stm32l4r5qi = ["stm32-metapac/stm32l4r5qi"] -stm32l4r5vg = ["stm32-metapac/stm32l4r5vg"] -stm32l4r5vi = ["stm32-metapac/stm32l4r5vi"] -stm32l4r5zg = ["stm32-metapac/stm32l4r5zg"] -stm32l4r5zi = ["stm32-metapac/stm32l4r5zi"] -stm32l4r7ai = ["stm32-metapac/stm32l4r7ai"] -stm32l4r7vi = ["stm32-metapac/stm32l4r7vi"] -stm32l4r7zi = ["stm32-metapac/stm32l4r7zi"] -stm32l4r9ag = ["stm32-metapac/stm32l4r9ag"] -stm32l4r9ai = ["stm32-metapac/stm32l4r9ai"] -stm32l4r9vg = ["stm32-metapac/stm32l4r9vg"] -stm32l4r9vi = ["stm32-metapac/stm32l4r9vi"] -stm32l4r9zg = ["stm32-metapac/stm32l4r9zg"] -stm32l4r9zi = ["stm32-metapac/stm32l4r9zi"] -stm32l4s5ai = ["stm32-metapac/stm32l4s5ai"] -stm32l4s5qi = ["stm32-metapac/stm32l4s5qi"] -stm32l4s5vi = ["stm32-metapac/stm32l4s5vi"] -stm32l4s5zi = ["stm32-metapac/stm32l4s5zi"] -stm32l4s7ai = ["stm32-metapac/stm32l4s7ai"] -stm32l4s7vi = ["stm32-metapac/stm32l4s7vi"] -stm32l4s7zi = ["stm32-metapac/stm32l4s7zi"] -stm32l4s9ai = ["stm32-metapac/stm32l4s9ai"] -stm32l4s9vi = ["stm32-metapac/stm32l4s9vi"] -stm32l4s9zi = ["stm32-metapac/stm32l4s9zi"] -stm32l552cc = ["stm32-metapac/stm32l552cc"] -stm32l552ce = ["stm32-metapac/stm32l552ce"] -stm32l552me = ["stm32-metapac/stm32l552me"] -stm32l552qc = ["stm32-metapac/stm32l552qc"] -stm32l552qe = ["stm32-metapac/stm32l552qe"] -stm32l552rc = ["stm32-metapac/stm32l552rc"] -stm32l552re = ["stm32-metapac/stm32l552re"] -stm32l552vc = ["stm32-metapac/stm32l552vc"] -stm32l552ve = ["stm32-metapac/stm32l552ve"] -stm32l552zc = ["stm32-metapac/stm32l552zc"] -stm32l552ze = ["stm32-metapac/stm32l552ze"] -stm32l562ce = ["stm32-metapac/stm32l562ce"] -stm32l562me = ["stm32-metapac/stm32l562me"] -stm32l562qe = ["stm32-metapac/stm32l562qe"] -stm32l562re = ["stm32-metapac/stm32l562re"] -stm32l562ve = ["stm32-metapac/stm32l562ve"] -stm32l562ze = ["stm32-metapac/stm32l562ze"] -stm32u535cb = ["stm32-metapac/stm32u535cb"] -stm32u535cc = ["stm32-metapac/stm32u535cc"] -stm32u535ce = ["stm32-metapac/stm32u535ce"] -stm32u535je = ["stm32-metapac/stm32u535je"] -stm32u535nc = ["stm32-metapac/stm32u535nc"] -stm32u535ne = ["stm32-metapac/stm32u535ne"] -stm32u535rb = ["stm32-metapac/stm32u535rb"] -stm32u535rc = ["stm32-metapac/stm32u535rc"] -stm32u535re = ["stm32-metapac/stm32u535re"] -stm32u535vc = ["stm32-metapac/stm32u535vc"] -stm32u535ve = ["stm32-metapac/stm32u535ve"] -stm32u545ce = ["stm32-metapac/stm32u545ce"] -stm32u545je = ["stm32-metapac/stm32u545je"] -stm32u545ne = ["stm32-metapac/stm32u545ne"] -stm32u545re = ["stm32-metapac/stm32u545re"] -stm32u545ve = ["stm32-metapac/stm32u545ve"] -stm32u575ag = ["stm32-metapac/stm32u575ag"] -stm32u575ai = ["stm32-metapac/stm32u575ai"] -stm32u575cg = ["stm32-metapac/stm32u575cg"] -stm32u575ci = ["stm32-metapac/stm32u575ci"] -stm32u575og = ["stm32-metapac/stm32u575og"] -stm32u575oi = ["stm32-metapac/stm32u575oi"] -stm32u575qg = ["stm32-metapac/stm32u575qg"] -stm32u575qi = ["stm32-metapac/stm32u575qi"] -stm32u575rg = ["stm32-metapac/stm32u575rg"] -stm32u575ri = ["stm32-metapac/stm32u575ri"] -stm32u575vg = ["stm32-metapac/stm32u575vg"] -stm32u575vi = ["stm32-metapac/stm32u575vi"] -stm32u575zg = ["stm32-metapac/stm32u575zg"] -stm32u575zi = ["stm32-metapac/stm32u575zi"] -stm32u585ai = ["stm32-metapac/stm32u585ai"] -stm32u585ci = ["stm32-metapac/stm32u585ci"] -stm32u585oi = ["stm32-metapac/stm32u585oi"] -stm32u585qi = ["stm32-metapac/stm32u585qi"] -stm32u585ri = ["stm32-metapac/stm32u585ri"] -stm32u585vi = ["stm32-metapac/stm32u585vi"] -stm32u585zi = ["stm32-metapac/stm32u585zi"] -stm32u595ai = ["stm32-metapac/stm32u595ai"] -stm32u595aj = ["stm32-metapac/stm32u595aj"] -stm32u595qi = ["stm32-metapac/stm32u595qi"] -stm32u595qj = ["stm32-metapac/stm32u595qj"] -stm32u595ri = ["stm32-metapac/stm32u595ri"] -stm32u595rj = ["stm32-metapac/stm32u595rj"] -stm32u595vi = ["stm32-metapac/stm32u595vi"] -stm32u595vj = ["stm32-metapac/stm32u595vj"] -stm32u595zi = ["stm32-metapac/stm32u595zi"] -stm32u595zj = ["stm32-metapac/stm32u595zj"] -stm32u599bj = ["stm32-metapac/stm32u599bj"] -stm32u599ni = ["stm32-metapac/stm32u599ni"] -stm32u599nj = ["stm32-metapac/stm32u599nj"] -stm32u599vi = ["stm32-metapac/stm32u599vi"] -stm32u599vj = ["stm32-metapac/stm32u599vj"] -stm32u599zi = ["stm32-metapac/stm32u599zi"] -stm32u599zj = ["stm32-metapac/stm32u599zj"] -stm32u5a5aj = ["stm32-metapac/stm32u5a5aj"] -stm32u5a5qj = ["stm32-metapac/stm32u5a5qj"] -stm32u5a5rj = ["stm32-metapac/stm32u5a5rj"] -stm32u5a5vj = ["stm32-metapac/stm32u5a5vj"] -stm32u5a5zj = ["stm32-metapac/stm32u5a5zj"] -stm32u5a9bj = ["stm32-metapac/stm32u5a9bj"] -stm32u5a9nj = ["stm32-metapac/stm32u5a9nj"] -stm32u5a9vj = ["stm32-metapac/stm32u5a9vj"] -stm32u5a9zj = ["stm32-metapac/stm32u5a9zj"] -stm32wb10cc = ["stm32-metapac/stm32wb10cc"] -stm32wb15cc = ["stm32-metapac/stm32wb15cc"] -stm32wb30ce = ["stm32-metapac/stm32wb30ce"] -stm32wb35cc = ["stm32-metapac/stm32wb35cc"] -stm32wb35ce = ["stm32-metapac/stm32wb35ce"] -stm32wb50cg = ["stm32-metapac/stm32wb50cg"] -stm32wb55cc = ["stm32-metapac/stm32wb55cc"] -stm32wb55ce = ["stm32-metapac/stm32wb55ce"] -stm32wb55cg = ["stm32-metapac/stm32wb55cg"] -stm32wb55rc = ["stm32-metapac/stm32wb55rc"] -stm32wb55re = ["stm32-metapac/stm32wb55re"] -stm32wb55rg = ["stm32-metapac/stm32wb55rg"] -stm32wb55vc = ["stm32-metapac/stm32wb55vc"] -stm32wb55ve = ["stm32-metapac/stm32wb55ve"] -stm32wb55vg = ["stm32-metapac/stm32wb55vg"] -stm32wb55vy = ["stm32-metapac/stm32wb55vy"] -stm32wl54cc-cm4 = ["stm32-metapac/stm32wl54cc-cm4"] -stm32wl54cc-cm0p = ["stm32-metapac/stm32wl54cc-cm0p"] -stm32wl54jc-cm4 = ["stm32-metapac/stm32wl54jc-cm4"] -stm32wl54jc-cm0p = ["stm32-metapac/stm32wl54jc-cm0p"] -stm32wl55cc-cm4 = ["stm32-metapac/stm32wl55cc-cm4"] -stm32wl55cc-cm0p = ["stm32-metapac/stm32wl55cc-cm0p"] -stm32wl55jc-cm4 = ["stm32-metapac/stm32wl55jc-cm4"] -stm32wl55jc-cm0p = ["stm32-metapac/stm32wl55jc-cm0p"] -stm32wle4c8 = ["stm32-metapac/stm32wle4c8"] -stm32wle4cb = ["stm32-metapac/stm32wle4cb"] -stm32wle4cc = ["stm32-metapac/stm32wle4cc"] -stm32wle4j8 = ["stm32-metapac/stm32wle4j8"] -stm32wle4jb = ["stm32-metapac/stm32wle4jb"] -stm32wle4jc = ["stm32-metapac/stm32wle4jc"] -stm32wle5c8 = ["stm32-metapac/stm32wle5c8"] -stm32wle5cb = ["stm32-metapac/stm32wle5cb"] -stm32wle5cc = ["stm32-metapac/stm32wle5cc"] -stm32wle5j8 = ["stm32-metapac/stm32wle5j8"] -stm32wle5jb = ["stm32-metapac/stm32wle5jb"] -stm32wle5jc = ["stm32-metapac/stm32wle5jc"] +stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] +stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] +stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] +stm32c011j4 = [ "stm32-metapac/stm32c011j4" ] +stm32c011j6 = [ "stm32-metapac/stm32c011j6" ] +stm32c031c4 = [ "stm32-metapac/stm32c031c4" ] +stm32c031c6 = [ "stm32-metapac/stm32c031c6" ] +stm32c031f4 = [ "stm32-metapac/stm32c031f4" ] +stm32c031f6 = [ "stm32-metapac/stm32c031f6" ] +stm32c031g4 = [ "stm32-metapac/stm32c031g4" ] +stm32c031g6 = [ "stm32-metapac/stm32c031g6" ] +stm32c031k4 = [ "stm32-metapac/stm32c031k4" ] +stm32c031k6 = [ "stm32-metapac/stm32c031k6" ] +stm32f030c6 = [ "stm32-metapac/stm32f030c6" ] +stm32f030c8 = [ "stm32-metapac/stm32f030c8" ] +stm32f030cc = [ "stm32-metapac/stm32f030cc" ] +stm32f030f4 = [ "stm32-metapac/stm32f030f4" ] +stm32f030k6 = [ "stm32-metapac/stm32f030k6" ] +stm32f030r8 = [ "stm32-metapac/stm32f030r8" ] +stm32f030rc = [ "stm32-metapac/stm32f030rc" ] +stm32f031c4 = [ "stm32-metapac/stm32f031c4" ] +stm32f031c6 = [ "stm32-metapac/stm32f031c6" ] +stm32f031e6 = [ "stm32-metapac/stm32f031e6" ] +stm32f031f4 = [ "stm32-metapac/stm32f031f4" ] +stm32f031f6 = [ "stm32-metapac/stm32f031f6" ] +stm32f031g4 = [ "stm32-metapac/stm32f031g4" ] +stm32f031g6 = [ "stm32-metapac/stm32f031g6" ] +stm32f031k4 = [ "stm32-metapac/stm32f031k4" ] +stm32f031k6 = [ "stm32-metapac/stm32f031k6" ] +stm32f038c6 = [ "stm32-metapac/stm32f038c6" ] +stm32f038e6 = [ "stm32-metapac/stm32f038e6" ] +stm32f038f6 = [ "stm32-metapac/stm32f038f6" ] +stm32f038g6 = [ "stm32-metapac/stm32f038g6" ] +stm32f038k6 = [ "stm32-metapac/stm32f038k6" ] +stm32f042c4 = [ "stm32-metapac/stm32f042c4" ] +stm32f042c6 = [ "stm32-metapac/stm32f042c6" ] +stm32f042f4 = [ "stm32-metapac/stm32f042f4" ] +stm32f042f6 = [ "stm32-metapac/stm32f042f6" ] +stm32f042g4 = [ "stm32-metapac/stm32f042g4" ] +stm32f042g6 = [ "stm32-metapac/stm32f042g6" ] +stm32f042k4 = [ "stm32-metapac/stm32f042k4" ] +stm32f042k6 = [ "stm32-metapac/stm32f042k6" ] +stm32f042t6 = [ "stm32-metapac/stm32f042t6" ] +stm32f048c6 = [ "stm32-metapac/stm32f048c6" ] +stm32f048g6 = [ "stm32-metapac/stm32f048g6" ] +stm32f048t6 = [ "stm32-metapac/stm32f048t6" ] +stm32f051c4 = [ "stm32-metapac/stm32f051c4" ] +stm32f051c6 = [ "stm32-metapac/stm32f051c6" ] +stm32f051c8 = [ "stm32-metapac/stm32f051c8" ] +stm32f051k4 = [ "stm32-metapac/stm32f051k4" ] +stm32f051k6 = [ "stm32-metapac/stm32f051k6" ] +stm32f051k8 = [ "stm32-metapac/stm32f051k8" ] +stm32f051r4 = [ "stm32-metapac/stm32f051r4" ] +stm32f051r6 = [ "stm32-metapac/stm32f051r6" ] +stm32f051r8 = [ "stm32-metapac/stm32f051r8" ] +stm32f051t8 = [ "stm32-metapac/stm32f051t8" ] +stm32f058c8 = [ "stm32-metapac/stm32f058c8" ] +stm32f058r8 = [ "stm32-metapac/stm32f058r8" ] +stm32f058t8 = [ "stm32-metapac/stm32f058t8" ] +stm32f070c6 = [ "stm32-metapac/stm32f070c6" ] +stm32f070cb = [ "stm32-metapac/stm32f070cb" ] +stm32f070f6 = [ "stm32-metapac/stm32f070f6" ] +stm32f070rb = [ "stm32-metapac/stm32f070rb" ] +stm32f071c8 = [ "stm32-metapac/stm32f071c8" ] +stm32f071cb = [ "stm32-metapac/stm32f071cb" ] +stm32f071rb = [ "stm32-metapac/stm32f071rb" ] +stm32f071v8 = [ "stm32-metapac/stm32f071v8" ] +stm32f071vb = [ "stm32-metapac/stm32f071vb" ] +stm32f072c8 = [ "stm32-metapac/stm32f072c8" ] +stm32f072cb = [ "stm32-metapac/stm32f072cb" ] +stm32f072r8 = [ "stm32-metapac/stm32f072r8" ] +stm32f072rb = [ "stm32-metapac/stm32f072rb" ] +stm32f072v8 = [ "stm32-metapac/stm32f072v8" ] +stm32f072vb = [ "stm32-metapac/stm32f072vb" ] +stm32f078cb = [ "stm32-metapac/stm32f078cb" ] +stm32f078rb = [ "stm32-metapac/stm32f078rb" ] +stm32f078vb = [ "stm32-metapac/stm32f078vb" ] +stm32f091cb = [ "stm32-metapac/stm32f091cb" ] +stm32f091cc = [ "stm32-metapac/stm32f091cc" ] +stm32f091rb = [ "stm32-metapac/stm32f091rb" ] +stm32f091rc = [ "stm32-metapac/stm32f091rc" ] +stm32f091vb = [ "stm32-metapac/stm32f091vb" ] +stm32f091vc = [ "stm32-metapac/stm32f091vc" ] +stm32f098cc = [ "stm32-metapac/stm32f098cc" ] +stm32f098rc = [ "stm32-metapac/stm32f098rc" ] +stm32f098vc = [ "stm32-metapac/stm32f098vc" ] +stm32f100c4 = [ "stm32-metapac/stm32f100c4" ] +stm32f100c6 = [ "stm32-metapac/stm32f100c6" ] +stm32f100c8 = [ "stm32-metapac/stm32f100c8" ] +stm32f100cb = [ "stm32-metapac/stm32f100cb" ] +stm32f100r4 = [ "stm32-metapac/stm32f100r4" ] +stm32f100r6 = [ "stm32-metapac/stm32f100r6" ] +stm32f100r8 = [ "stm32-metapac/stm32f100r8" ] +stm32f100rb = [ "stm32-metapac/stm32f100rb" ] +stm32f100rc = [ "stm32-metapac/stm32f100rc" ] +stm32f100rd = [ "stm32-metapac/stm32f100rd" ] +stm32f100re = [ "stm32-metapac/stm32f100re" ] +stm32f100v8 = [ "stm32-metapac/stm32f100v8" ] +stm32f100vb = [ "stm32-metapac/stm32f100vb" ] +stm32f100vc = [ "stm32-metapac/stm32f100vc" ] +stm32f100vd = [ "stm32-metapac/stm32f100vd" ] +stm32f100ve = [ "stm32-metapac/stm32f100ve" ] +stm32f100zc = [ "stm32-metapac/stm32f100zc" ] +stm32f100zd = [ "stm32-metapac/stm32f100zd" ] +stm32f100ze = [ "stm32-metapac/stm32f100ze" ] +stm32f101c4 = [ "stm32-metapac/stm32f101c4" ] +stm32f101c6 = [ "stm32-metapac/stm32f101c6" ] +stm32f101c8 = [ "stm32-metapac/stm32f101c8" ] +stm32f101cb = [ "stm32-metapac/stm32f101cb" ] +stm32f101r4 = [ "stm32-metapac/stm32f101r4" ] +stm32f101r6 = [ "stm32-metapac/stm32f101r6" ] +stm32f101r8 = [ "stm32-metapac/stm32f101r8" ] +stm32f101rb = [ "stm32-metapac/stm32f101rb" ] +stm32f101rc = [ "stm32-metapac/stm32f101rc" ] +stm32f101rd = [ "stm32-metapac/stm32f101rd" ] +stm32f101re = [ "stm32-metapac/stm32f101re" ] +stm32f101rf = [ "stm32-metapac/stm32f101rf" ] +stm32f101rg = [ "stm32-metapac/stm32f101rg" ] +stm32f101t4 = [ "stm32-metapac/stm32f101t4" ] +stm32f101t6 = [ "stm32-metapac/stm32f101t6" ] +stm32f101t8 = [ "stm32-metapac/stm32f101t8" ] +stm32f101tb = [ "stm32-metapac/stm32f101tb" ] +stm32f101v8 = [ "stm32-metapac/stm32f101v8" ] +stm32f101vb = [ "stm32-metapac/stm32f101vb" ] +stm32f101vc = [ "stm32-metapac/stm32f101vc" ] +stm32f101vd = [ "stm32-metapac/stm32f101vd" ] +stm32f101ve = [ "stm32-metapac/stm32f101ve" ] +stm32f101vf = [ "stm32-metapac/stm32f101vf" ] +stm32f101vg = [ "stm32-metapac/stm32f101vg" ] +stm32f101zc = [ "stm32-metapac/stm32f101zc" ] +stm32f101zd = [ "stm32-metapac/stm32f101zd" ] +stm32f101ze = [ "stm32-metapac/stm32f101ze" ] +stm32f101zf = [ "stm32-metapac/stm32f101zf" ] +stm32f101zg = [ "stm32-metapac/stm32f101zg" ] +stm32f102c4 = [ "stm32-metapac/stm32f102c4" ] +stm32f102c6 = [ "stm32-metapac/stm32f102c6" ] +stm32f102c8 = [ "stm32-metapac/stm32f102c8" ] +stm32f102cb = [ "stm32-metapac/stm32f102cb" ] +stm32f102r4 = [ "stm32-metapac/stm32f102r4" ] +stm32f102r6 = [ "stm32-metapac/stm32f102r6" ] +stm32f102r8 = [ "stm32-metapac/stm32f102r8" ] +stm32f102rb = [ "stm32-metapac/stm32f102rb" ] +stm32f103c4 = [ "stm32-metapac/stm32f103c4" ] +stm32f103c6 = [ "stm32-metapac/stm32f103c6" ] +stm32f103c8 = [ "stm32-metapac/stm32f103c8" ] +stm32f103cb = [ "stm32-metapac/stm32f103cb" ] +stm32f103r4 = [ "stm32-metapac/stm32f103r4" ] +stm32f103r6 = [ "stm32-metapac/stm32f103r6" ] +stm32f103r8 = [ "stm32-metapac/stm32f103r8" ] +stm32f103rb = [ "stm32-metapac/stm32f103rb" ] +stm32f103rc = [ "stm32-metapac/stm32f103rc" ] +stm32f103rd = [ "stm32-metapac/stm32f103rd" ] +stm32f103re = [ "stm32-metapac/stm32f103re" ] +stm32f103rf = [ "stm32-metapac/stm32f103rf" ] +stm32f103rg = [ "stm32-metapac/stm32f103rg" ] +stm32f103t4 = [ "stm32-metapac/stm32f103t4" ] +stm32f103t6 = [ "stm32-metapac/stm32f103t6" ] +stm32f103t8 = [ "stm32-metapac/stm32f103t8" ] +stm32f103tb = [ "stm32-metapac/stm32f103tb" ] +stm32f103v8 = [ "stm32-metapac/stm32f103v8" ] +stm32f103vb = [ "stm32-metapac/stm32f103vb" ] +stm32f103vc = [ "stm32-metapac/stm32f103vc" ] +stm32f103vd = [ "stm32-metapac/stm32f103vd" ] +stm32f103ve = [ "stm32-metapac/stm32f103ve" ] +stm32f103vf = [ "stm32-metapac/stm32f103vf" ] +stm32f103vg = [ "stm32-metapac/stm32f103vg" ] +stm32f103zc = [ "stm32-metapac/stm32f103zc" ] +stm32f103zd = [ "stm32-metapac/stm32f103zd" ] +stm32f103ze = [ "stm32-metapac/stm32f103ze" ] +stm32f103zf = [ "stm32-metapac/stm32f103zf" ] +stm32f103zg = [ "stm32-metapac/stm32f103zg" ] +stm32f105r8 = [ "stm32-metapac/stm32f105r8" ] +stm32f105rb = [ "stm32-metapac/stm32f105rb" ] +stm32f105rc = [ "stm32-metapac/stm32f105rc" ] +stm32f105v8 = [ "stm32-metapac/stm32f105v8" ] +stm32f105vb = [ "stm32-metapac/stm32f105vb" ] +stm32f105vc = [ "stm32-metapac/stm32f105vc" ] +stm32f107rb = [ "stm32-metapac/stm32f107rb" ] +stm32f107rc = [ "stm32-metapac/stm32f107rc" ] +stm32f107vb = [ "stm32-metapac/stm32f107vb" ] +stm32f107vc = [ "stm32-metapac/stm32f107vc" ] +stm32f205rb = [ "stm32-metapac/stm32f205rb" ] +stm32f205rc = [ "stm32-metapac/stm32f205rc" ] +stm32f205re = [ "stm32-metapac/stm32f205re" ] +stm32f205rf = [ "stm32-metapac/stm32f205rf" ] +stm32f205rg = [ "stm32-metapac/stm32f205rg" ] +stm32f205vb = [ "stm32-metapac/stm32f205vb" ] +stm32f205vc = [ "stm32-metapac/stm32f205vc" ] +stm32f205ve = [ "stm32-metapac/stm32f205ve" ] +stm32f205vf = [ "stm32-metapac/stm32f205vf" ] +stm32f205vg = [ "stm32-metapac/stm32f205vg" ] +stm32f205zc = [ "stm32-metapac/stm32f205zc" ] +stm32f205ze = [ "stm32-metapac/stm32f205ze" ] +stm32f205zf = [ "stm32-metapac/stm32f205zf" ] +stm32f205zg = [ "stm32-metapac/stm32f205zg" ] +stm32f207ic = [ "stm32-metapac/stm32f207ic" ] +stm32f207ie = [ "stm32-metapac/stm32f207ie" ] +stm32f207if = [ "stm32-metapac/stm32f207if" ] +stm32f207ig = [ "stm32-metapac/stm32f207ig" ] +stm32f207vc = [ "stm32-metapac/stm32f207vc" ] +stm32f207ve = [ "stm32-metapac/stm32f207ve" ] +stm32f207vf = [ "stm32-metapac/stm32f207vf" ] +stm32f207vg = [ "stm32-metapac/stm32f207vg" ] +stm32f207zc = [ "stm32-metapac/stm32f207zc" ] +stm32f207ze = [ "stm32-metapac/stm32f207ze" ] +stm32f207zf = [ "stm32-metapac/stm32f207zf" ] +stm32f207zg = [ "stm32-metapac/stm32f207zg" ] +stm32f215re = [ "stm32-metapac/stm32f215re" ] +stm32f215rg = [ "stm32-metapac/stm32f215rg" ] +stm32f215ve = [ "stm32-metapac/stm32f215ve" ] +stm32f215vg = [ "stm32-metapac/stm32f215vg" ] +stm32f215ze = [ "stm32-metapac/stm32f215ze" ] +stm32f215zg = [ "stm32-metapac/stm32f215zg" ] +stm32f217ie = [ "stm32-metapac/stm32f217ie" ] +stm32f217ig = [ "stm32-metapac/stm32f217ig" ] +stm32f217ve = [ "stm32-metapac/stm32f217ve" ] +stm32f217vg = [ "stm32-metapac/stm32f217vg" ] +stm32f217ze = [ "stm32-metapac/stm32f217ze" ] +stm32f217zg = [ "stm32-metapac/stm32f217zg" ] +stm32f301c6 = [ "stm32-metapac/stm32f301c6" ] +stm32f301c8 = [ "stm32-metapac/stm32f301c8" ] +stm32f301k6 = [ "stm32-metapac/stm32f301k6" ] +stm32f301k8 = [ "stm32-metapac/stm32f301k8" ] +stm32f301r6 = [ "stm32-metapac/stm32f301r6" ] +stm32f301r8 = [ "stm32-metapac/stm32f301r8" ] +stm32f302c6 = [ "stm32-metapac/stm32f302c6" ] +stm32f302c8 = [ "stm32-metapac/stm32f302c8" ] +stm32f302cb = [ "stm32-metapac/stm32f302cb" ] +stm32f302cc = [ "stm32-metapac/stm32f302cc" ] +stm32f302k6 = [ "stm32-metapac/stm32f302k6" ] +stm32f302k8 = [ "stm32-metapac/stm32f302k8" ] +stm32f302r6 = [ "stm32-metapac/stm32f302r6" ] +stm32f302r8 = [ "stm32-metapac/stm32f302r8" ] +stm32f302rb = [ "stm32-metapac/stm32f302rb" ] +stm32f302rc = [ "stm32-metapac/stm32f302rc" ] +stm32f302rd = [ "stm32-metapac/stm32f302rd" ] +stm32f302re = [ "stm32-metapac/stm32f302re" ] +stm32f302vb = [ "stm32-metapac/stm32f302vb" ] +stm32f302vc = [ "stm32-metapac/stm32f302vc" ] +stm32f302vd = [ "stm32-metapac/stm32f302vd" ] +stm32f302ve = [ "stm32-metapac/stm32f302ve" ] +stm32f302zd = [ "stm32-metapac/stm32f302zd" ] +stm32f302ze = [ "stm32-metapac/stm32f302ze" ] +stm32f303c6 = [ "stm32-metapac/stm32f303c6" ] +stm32f303c8 = [ "stm32-metapac/stm32f303c8" ] +stm32f303cb = [ "stm32-metapac/stm32f303cb" ] +stm32f303cc = [ "stm32-metapac/stm32f303cc" ] +stm32f303k6 = [ "stm32-metapac/stm32f303k6" ] +stm32f303k8 = [ "stm32-metapac/stm32f303k8" ] +stm32f303r6 = [ "stm32-metapac/stm32f303r6" ] +stm32f303r8 = [ "stm32-metapac/stm32f303r8" ] +stm32f303rb = [ "stm32-metapac/stm32f303rb" ] +stm32f303rc = [ "stm32-metapac/stm32f303rc" ] +stm32f303rd = [ "stm32-metapac/stm32f303rd" ] +stm32f303re = [ "stm32-metapac/stm32f303re" ] +stm32f303vb = [ "stm32-metapac/stm32f303vb" ] +stm32f303vc = [ "stm32-metapac/stm32f303vc" ] +stm32f303vd = [ "stm32-metapac/stm32f303vd" ] +stm32f303ve = [ "stm32-metapac/stm32f303ve" ] +stm32f303zd = [ "stm32-metapac/stm32f303zd" ] +stm32f303ze = [ "stm32-metapac/stm32f303ze" ] +stm32f318c8 = [ "stm32-metapac/stm32f318c8" ] +stm32f318k8 = [ "stm32-metapac/stm32f318k8" ] +stm32f328c8 = [ "stm32-metapac/stm32f328c8" ] +stm32f334c4 = [ "stm32-metapac/stm32f334c4" ] +stm32f334c6 = [ "stm32-metapac/stm32f334c6" ] +stm32f334c8 = [ "stm32-metapac/stm32f334c8" ] +stm32f334k4 = [ "stm32-metapac/stm32f334k4" ] +stm32f334k6 = [ "stm32-metapac/stm32f334k6" ] +stm32f334k8 = [ "stm32-metapac/stm32f334k8" ] +stm32f334r6 = [ "stm32-metapac/stm32f334r6" ] +stm32f334r8 = [ "stm32-metapac/stm32f334r8" ] +stm32f358cc = [ "stm32-metapac/stm32f358cc" ] +stm32f358rc = [ "stm32-metapac/stm32f358rc" ] +stm32f358vc = [ "stm32-metapac/stm32f358vc" ] +stm32f373c8 = [ "stm32-metapac/stm32f373c8" ] +stm32f373cb = [ "stm32-metapac/stm32f373cb" ] +stm32f373cc = [ "stm32-metapac/stm32f373cc" ] +stm32f373r8 = [ "stm32-metapac/stm32f373r8" ] +stm32f373rb = [ "stm32-metapac/stm32f373rb" ] +stm32f373rc = [ "stm32-metapac/stm32f373rc" ] +stm32f373v8 = [ "stm32-metapac/stm32f373v8" ] +stm32f373vb = [ "stm32-metapac/stm32f373vb" ] +stm32f373vc = [ "stm32-metapac/stm32f373vc" ] +stm32f378cc = [ "stm32-metapac/stm32f378cc" ] +stm32f378rc = [ "stm32-metapac/stm32f378rc" ] +stm32f378vc = [ "stm32-metapac/stm32f378vc" ] +stm32f398ve = [ "stm32-metapac/stm32f398ve" ] +stm32f401cb = [ "stm32-metapac/stm32f401cb" ] +stm32f401cc = [ "stm32-metapac/stm32f401cc" ] +stm32f401cd = [ "stm32-metapac/stm32f401cd" ] +stm32f401ce = [ "stm32-metapac/stm32f401ce" ] +stm32f401rb = [ "stm32-metapac/stm32f401rb" ] +stm32f401rc = [ "stm32-metapac/stm32f401rc" ] +stm32f401rd = [ "stm32-metapac/stm32f401rd" ] +stm32f401re = [ "stm32-metapac/stm32f401re" ] +stm32f401vb = [ "stm32-metapac/stm32f401vb" ] +stm32f401vc = [ "stm32-metapac/stm32f401vc" ] +stm32f401vd = [ "stm32-metapac/stm32f401vd" ] +stm32f401ve = [ "stm32-metapac/stm32f401ve" ] +stm32f405oe = [ "stm32-metapac/stm32f405oe" ] +stm32f405og = [ "stm32-metapac/stm32f405og" ] +stm32f405rg = [ "stm32-metapac/stm32f405rg" ] +stm32f405vg = [ "stm32-metapac/stm32f405vg" ] +stm32f405zg = [ "stm32-metapac/stm32f405zg" ] +stm32f407ie = [ "stm32-metapac/stm32f407ie" ] +stm32f407ig = [ "stm32-metapac/stm32f407ig" ] +stm32f407ve = [ "stm32-metapac/stm32f407ve" ] +stm32f407vg = [ "stm32-metapac/stm32f407vg" ] +stm32f407ze = [ "stm32-metapac/stm32f407ze" ] +stm32f407zg = [ "stm32-metapac/stm32f407zg" ] +stm32f410c8 = [ "stm32-metapac/stm32f410c8" ] +stm32f410cb = [ "stm32-metapac/stm32f410cb" ] +stm32f410r8 = [ "stm32-metapac/stm32f410r8" ] +stm32f410rb = [ "stm32-metapac/stm32f410rb" ] +stm32f410t8 = [ "stm32-metapac/stm32f410t8" ] +stm32f410tb = [ "stm32-metapac/stm32f410tb" ] +stm32f411cc = [ "stm32-metapac/stm32f411cc" ] +stm32f411ce = [ "stm32-metapac/stm32f411ce" ] +stm32f411rc = [ "stm32-metapac/stm32f411rc" ] +stm32f411re = [ "stm32-metapac/stm32f411re" ] +stm32f411vc = [ "stm32-metapac/stm32f411vc" ] +stm32f411ve = [ "stm32-metapac/stm32f411ve" ] +stm32f412ce = [ "stm32-metapac/stm32f412ce" ] +stm32f412cg = [ "stm32-metapac/stm32f412cg" ] +stm32f412re = [ "stm32-metapac/stm32f412re" ] +stm32f412rg = [ "stm32-metapac/stm32f412rg" ] +stm32f412ve = [ "stm32-metapac/stm32f412ve" ] +stm32f412vg = [ "stm32-metapac/stm32f412vg" ] +stm32f412ze = [ "stm32-metapac/stm32f412ze" ] +stm32f412zg = [ "stm32-metapac/stm32f412zg" ] +stm32f413cg = [ "stm32-metapac/stm32f413cg" ] +stm32f413ch = [ "stm32-metapac/stm32f413ch" ] +stm32f413mg = [ "stm32-metapac/stm32f413mg" ] +stm32f413mh = [ "stm32-metapac/stm32f413mh" ] +stm32f413rg = [ "stm32-metapac/stm32f413rg" ] +stm32f413rh = [ "stm32-metapac/stm32f413rh" ] +stm32f413vg = [ "stm32-metapac/stm32f413vg" ] +stm32f413vh = [ "stm32-metapac/stm32f413vh" ] +stm32f413zg = [ "stm32-metapac/stm32f413zg" ] +stm32f413zh = [ "stm32-metapac/stm32f413zh" ] +stm32f415og = [ "stm32-metapac/stm32f415og" ] +stm32f415rg = [ "stm32-metapac/stm32f415rg" ] +stm32f415vg = [ "stm32-metapac/stm32f415vg" ] +stm32f415zg = [ "stm32-metapac/stm32f415zg" ] +stm32f417ie = [ "stm32-metapac/stm32f417ie" ] +stm32f417ig = [ "stm32-metapac/stm32f417ig" ] +stm32f417ve = [ "stm32-metapac/stm32f417ve" ] +stm32f417vg = [ "stm32-metapac/stm32f417vg" ] +stm32f417ze = [ "stm32-metapac/stm32f417ze" ] +stm32f417zg = [ "stm32-metapac/stm32f417zg" ] +stm32f423ch = [ "stm32-metapac/stm32f423ch" ] +stm32f423mh = [ "stm32-metapac/stm32f423mh" ] +stm32f423rh = [ "stm32-metapac/stm32f423rh" ] +stm32f423vh = [ "stm32-metapac/stm32f423vh" ] +stm32f423zh = [ "stm32-metapac/stm32f423zh" ] +stm32f427ag = [ "stm32-metapac/stm32f427ag" ] +stm32f427ai = [ "stm32-metapac/stm32f427ai" ] +stm32f427ig = [ "stm32-metapac/stm32f427ig" ] +stm32f427ii = [ "stm32-metapac/stm32f427ii" ] +stm32f427vg = [ "stm32-metapac/stm32f427vg" ] +stm32f427vi = [ "stm32-metapac/stm32f427vi" ] +stm32f427zg = [ "stm32-metapac/stm32f427zg" ] +stm32f427zi = [ "stm32-metapac/stm32f427zi" ] +stm32f429ag = [ "stm32-metapac/stm32f429ag" ] +stm32f429ai = [ "stm32-metapac/stm32f429ai" ] +stm32f429be = [ "stm32-metapac/stm32f429be" ] +stm32f429bg = [ "stm32-metapac/stm32f429bg" ] +stm32f429bi = [ "stm32-metapac/stm32f429bi" ] +stm32f429ie = [ "stm32-metapac/stm32f429ie" ] +stm32f429ig = [ "stm32-metapac/stm32f429ig" ] +stm32f429ii = [ "stm32-metapac/stm32f429ii" ] +stm32f429ne = [ "stm32-metapac/stm32f429ne" ] +stm32f429ng = [ "stm32-metapac/stm32f429ng" ] +stm32f429ni = [ "stm32-metapac/stm32f429ni" ] +stm32f429ve = [ "stm32-metapac/stm32f429ve" ] +stm32f429vg = [ "stm32-metapac/stm32f429vg" ] +stm32f429vi = [ "stm32-metapac/stm32f429vi" ] +stm32f429ze = [ "stm32-metapac/stm32f429ze" ] +stm32f429zg = [ "stm32-metapac/stm32f429zg" ] +stm32f429zi = [ "stm32-metapac/stm32f429zi" ] +stm32f437ai = [ "stm32-metapac/stm32f437ai" ] +stm32f437ig = [ "stm32-metapac/stm32f437ig" ] +stm32f437ii = [ "stm32-metapac/stm32f437ii" ] +stm32f437vg = [ "stm32-metapac/stm32f437vg" ] +stm32f437vi = [ "stm32-metapac/stm32f437vi" ] +stm32f437zg = [ "stm32-metapac/stm32f437zg" ] +stm32f437zi = [ "stm32-metapac/stm32f437zi" ] +stm32f439ai = [ "stm32-metapac/stm32f439ai" ] +stm32f439bg = [ "stm32-metapac/stm32f439bg" ] +stm32f439bi = [ "stm32-metapac/stm32f439bi" ] +stm32f439ig = [ "stm32-metapac/stm32f439ig" ] +stm32f439ii = [ "stm32-metapac/stm32f439ii" ] +stm32f439ng = [ "stm32-metapac/stm32f439ng" ] +stm32f439ni = [ "stm32-metapac/stm32f439ni" ] +stm32f439vg = [ "stm32-metapac/stm32f439vg" ] +stm32f439vi = [ "stm32-metapac/stm32f439vi" ] +stm32f439zg = [ "stm32-metapac/stm32f439zg" ] +stm32f439zi = [ "stm32-metapac/stm32f439zi" ] +stm32f446mc = [ "stm32-metapac/stm32f446mc" ] +stm32f446me = [ "stm32-metapac/stm32f446me" ] +stm32f446rc = [ "stm32-metapac/stm32f446rc" ] +stm32f446re = [ "stm32-metapac/stm32f446re" ] +stm32f446vc = [ "stm32-metapac/stm32f446vc" ] +stm32f446ve = [ "stm32-metapac/stm32f446ve" ] +stm32f446zc = [ "stm32-metapac/stm32f446zc" ] +stm32f446ze = [ "stm32-metapac/stm32f446ze" ] +stm32f469ae = [ "stm32-metapac/stm32f469ae" ] +stm32f469ag = [ "stm32-metapac/stm32f469ag" ] +stm32f469ai = [ "stm32-metapac/stm32f469ai" ] +stm32f469be = [ "stm32-metapac/stm32f469be" ] +stm32f469bg = [ "stm32-metapac/stm32f469bg" ] +stm32f469bi = [ "stm32-metapac/stm32f469bi" ] +stm32f469ie = [ "stm32-metapac/stm32f469ie" ] +stm32f469ig = [ "stm32-metapac/stm32f469ig" ] +stm32f469ii = [ "stm32-metapac/stm32f469ii" ] +stm32f469ne = [ "stm32-metapac/stm32f469ne" ] +stm32f469ng = [ "stm32-metapac/stm32f469ng" ] +stm32f469ni = [ "stm32-metapac/stm32f469ni" ] +stm32f469ve = [ "stm32-metapac/stm32f469ve" ] +stm32f469vg = [ "stm32-metapac/stm32f469vg" ] +stm32f469vi = [ "stm32-metapac/stm32f469vi" ] +stm32f469ze = [ "stm32-metapac/stm32f469ze" ] +stm32f469zg = [ "stm32-metapac/stm32f469zg" ] +stm32f469zi = [ "stm32-metapac/stm32f469zi" ] +stm32f479ag = [ "stm32-metapac/stm32f479ag" ] +stm32f479ai = [ "stm32-metapac/stm32f479ai" ] +stm32f479bg = [ "stm32-metapac/stm32f479bg" ] +stm32f479bi = [ "stm32-metapac/stm32f479bi" ] +stm32f479ig = [ "stm32-metapac/stm32f479ig" ] +stm32f479ii = [ "stm32-metapac/stm32f479ii" ] +stm32f479ng = [ "stm32-metapac/stm32f479ng" ] +stm32f479ni = [ "stm32-metapac/stm32f479ni" ] +stm32f479vg = [ "stm32-metapac/stm32f479vg" ] +stm32f479vi = [ "stm32-metapac/stm32f479vi" ] +stm32f479zg = [ "stm32-metapac/stm32f479zg" ] +stm32f479zi = [ "stm32-metapac/stm32f479zi" ] +stm32f722ic = [ "stm32-metapac/stm32f722ic" ] +stm32f722ie = [ "stm32-metapac/stm32f722ie" ] +stm32f722rc = [ "stm32-metapac/stm32f722rc" ] +stm32f722re = [ "stm32-metapac/stm32f722re" ] +stm32f722vc = [ "stm32-metapac/stm32f722vc" ] +stm32f722ve = [ "stm32-metapac/stm32f722ve" ] +stm32f722zc = [ "stm32-metapac/stm32f722zc" ] +stm32f722ze = [ "stm32-metapac/stm32f722ze" ] +stm32f723ic = [ "stm32-metapac/stm32f723ic" ] +stm32f723ie = [ "stm32-metapac/stm32f723ie" ] +stm32f723vc = [ "stm32-metapac/stm32f723vc" ] +stm32f723ve = [ "stm32-metapac/stm32f723ve" ] +stm32f723zc = [ "stm32-metapac/stm32f723zc" ] +stm32f723ze = [ "stm32-metapac/stm32f723ze" ] +stm32f730i8 = [ "stm32-metapac/stm32f730i8" ] +stm32f730r8 = [ "stm32-metapac/stm32f730r8" ] +stm32f730v8 = [ "stm32-metapac/stm32f730v8" ] +stm32f730z8 = [ "stm32-metapac/stm32f730z8" ] +stm32f732ie = [ "stm32-metapac/stm32f732ie" ] +stm32f732re = [ "stm32-metapac/stm32f732re" ] +stm32f732ve = [ "stm32-metapac/stm32f732ve" ] +stm32f732ze = [ "stm32-metapac/stm32f732ze" ] +stm32f733ie = [ "stm32-metapac/stm32f733ie" ] +stm32f733ve = [ "stm32-metapac/stm32f733ve" ] +stm32f733ze = [ "stm32-metapac/stm32f733ze" ] +stm32f745ie = [ "stm32-metapac/stm32f745ie" ] +stm32f745ig = [ "stm32-metapac/stm32f745ig" ] +stm32f745ve = [ "stm32-metapac/stm32f745ve" ] +stm32f745vg = [ "stm32-metapac/stm32f745vg" ] +stm32f745ze = [ "stm32-metapac/stm32f745ze" ] +stm32f745zg = [ "stm32-metapac/stm32f745zg" ] +stm32f746be = [ "stm32-metapac/stm32f746be" ] +stm32f746bg = [ "stm32-metapac/stm32f746bg" ] +stm32f746ie = [ "stm32-metapac/stm32f746ie" ] +stm32f746ig = [ "stm32-metapac/stm32f746ig" ] +stm32f746ne = [ "stm32-metapac/stm32f746ne" ] +stm32f746ng = [ "stm32-metapac/stm32f746ng" ] +stm32f746ve = [ "stm32-metapac/stm32f746ve" ] +stm32f746vg = [ "stm32-metapac/stm32f746vg" ] +stm32f746ze = [ "stm32-metapac/stm32f746ze" ] +stm32f746zg = [ "stm32-metapac/stm32f746zg" ] +stm32f750n8 = [ "stm32-metapac/stm32f750n8" ] +stm32f750v8 = [ "stm32-metapac/stm32f750v8" ] +stm32f750z8 = [ "stm32-metapac/stm32f750z8" ] +stm32f756bg = [ "stm32-metapac/stm32f756bg" ] +stm32f756ig = [ "stm32-metapac/stm32f756ig" ] +stm32f756ng = [ "stm32-metapac/stm32f756ng" ] +stm32f756vg = [ "stm32-metapac/stm32f756vg" ] +stm32f756zg = [ "stm32-metapac/stm32f756zg" ] +stm32f765bg = [ "stm32-metapac/stm32f765bg" ] +stm32f765bi = [ "stm32-metapac/stm32f765bi" ] +stm32f765ig = [ "stm32-metapac/stm32f765ig" ] +stm32f765ii = [ "stm32-metapac/stm32f765ii" ] +stm32f765ng = [ "stm32-metapac/stm32f765ng" ] +stm32f765ni = [ "stm32-metapac/stm32f765ni" ] +stm32f765vg = [ "stm32-metapac/stm32f765vg" ] +stm32f765vi = [ "stm32-metapac/stm32f765vi" ] +stm32f765zg = [ "stm32-metapac/stm32f765zg" ] +stm32f765zi = [ "stm32-metapac/stm32f765zi" ] +stm32f767bg = [ "stm32-metapac/stm32f767bg" ] +stm32f767bi = [ "stm32-metapac/stm32f767bi" ] +stm32f767ig = [ "stm32-metapac/stm32f767ig" ] +stm32f767ii = [ "stm32-metapac/stm32f767ii" ] +stm32f767ng = [ "stm32-metapac/stm32f767ng" ] +stm32f767ni = [ "stm32-metapac/stm32f767ni" ] +stm32f767vg = [ "stm32-metapac/stm32f767vg" ] +stm32f767vi = [ "stm32-metapac/stm32f767vi" ] +stm32f767zg = [ "stm32-metapac/stm32f767zg" ] +stm32f767zi = [ "stm32-metapac/stm32f767zi" ] +stm32f768ai = [ "stm32-metapac/stm32f768ai" ] +stm32f769ag = [ "stm32-metapac/stm32f769ag" ] +stm32f769ai = [ "stm32-metapac/stm32f769ai" ] +stm32f769bg = [ "stm32-metapac/stm32f769bg" ] +stm32f769bi = [ "stm32-metapac/stm32f769bi" ] +stm32f769ig = [ "stm32-metapac/stm32f769ig" ] +stm32f769ii = [ "stm32-metapac/stm32f769ii" ] +stm32f769ng = [ "stm32-metapac/stm32f769ng" ] +stm32f769ni = [ "stm32-metapac/stm32f769ni" ] +stm32f777bi = [ "stm32-metapac/stm32f777bi" ] +stm32f777ii = [ "stm32-metapac/stm32f777ii" ] +stm32f777ni = [ "stm32-metapac/stm32f777ni" ] +stm32f777vi = [ "stm32-metapac/stm32f777vi" ] +stm32f777zi = [ "stm32-metapac/stm32f777zi" ] +stm32f778ai = [ "stm32-metapac/stm32f778ai" ] +stm32f779ai = [ "stm32-metapac/stm32f779ai" ] +stm32f779bi = [ "stm32-metapac/stm32f779bi" ] +stm32f779ii = [ "stm32-metapac/stm32f779ii" ] +stm32f779ni = [ "stm32-metapac/stm32f779ni" ] +stm32g030c6 = [ "stm32-metapac/stm32g030c6" ] +stm32g030c8 = [ "stm32-metapac/stm32g030c8" ] +stm32g030f6 = [ "stm32-metapac/stm32g030f6" ] +stm32g030j6 = [ "stm32-metapac/stm32g030j6" ] +stm32g030k6 = [ "stm32-metapac/stm32g030k6" ] +stm32g030k8 = [ "stm32-metapac/stm32g030k8" ] +stm32g031c4 = [ "stm32-metapac/stm32g031c4" ] +stm32g031c6 = [ "stm32-metapac/stm32g031c6" ] +stm32g031c8 = [ "stm32-metapac/stm32g031c8" ] +stm32g031f4 = [ "stm32-metapac/stm32g031f4" ] +stm32g031f6 = [ "stm32-metapac/stm32g031f6" ] +stm32g031f8 = [ "stm32-metapac/stm32g031f8" ] +stm32g031g4 = [ "stm32-metapac/stm32g031g4" ] +stm32g031g6 = [ "stm32-metapac/stm32g031g6" ] +stm32g031g8 = [ "stm32-metapac/stm32g031g8" ] +stm32g031j4 = [ "stm32-metapac/stm32g031j4" ] +stm32g031j6 = [ "stm32-metapac/stm32g031j6" ] +stm32g031k4 = [ "stm32-metapac/stm32g031k4" ] +stm32g031k6 = [ "stm32-metapac/stm32g031k6" ] +stm32g031k8 = [ "stm32-metapac/stm32g031k8" ] +stm32g031y8 = [ "stm32-metapac/stm32g031y8" ] +stm32g041c6 = [ "stm32-metapac/stm32g041c6" ] +stm32g041c8 = [ "stm32-metapac/stm32g041c8" ] +stm32g041f6 = [ "stm32-metapac/stm32g041f6" ] +stm32g041f8 = [ "stm32-metapac/stm32g041f8" ] +stm32g041g6 = [ "stm32-metapac/stm32g041g6" ] +stm32g041g8 = [ "stm32-metapac/stm32g041g8" ] +stm32g041j6 = [ "stm32-metapac/stm32g041j6" ] +stm32g041k6 = [ "stm32-metapac/stm32g041k6" ] +stm32g041k8 = [ "stm32-metapac/stm32g041k8" ] +stm32g041y8 = [ "stm32-metapac/stm32g041y8" ] +stm32g050c6 = [ "stm32-metapac/stm32g050c6" ] +stm32g050c8 = [ "stm32-metapac/stm32g050c8" ] +stm32g050f6 = [ "stm32-metapac/stm32g050f6" ] +stm32g050k6 = [ "stm32-metapac/stm32g050k6" ] +stm32g050k8 = [ "stm32-metapac/stm32g050k8" ] +stm32g051c6 = [ "stm32-metapac/stm32g051c6" ] +stm32g051c8 = [ "stm32-metapac/stm32g051c8" ] +stm32g051f6 = [ "stm32-metapac/stm32g051f6" ] +stm32g051f8 = [ "stm32-metapac/stm32g051f8" ] +stm32g051g6 = [ "stm32-metapac/stm32g051g6" ] +stm32g051g8 = [ "stm32-metapac/stm32g051g8" ] +stm32g051k6 = [ "stm32-metapac/stm32g051k6" ] +stm32g051k8 = [ "stm32-metapac/stm32g051k8" ] +stm32g061c6 = [ "stm32-metapac/stm32g061c6" ] +stm32g061c8 = [ "stm32-metapac/stm32g061c8" ] +stm32g061f6 = [ "stm32-metapac/stm32g061f6" ] +stm32g061f8 = [ "stm32-metapac/stm32g061f8" ] +stm32g061g6 = [ "stm32-metapac/stm32g061g6" ] +stm32g061g8 = [ "stm32-metapac/stm32g061g8" ] +stm32g061k6 = [ "stm32-metapac/stm32g061k6" ] +stm32g061k8 = [ "stm32-metapac/stm32g061k8" ] +stm32g070cb = [ "stm32-metapac/stm32g070cb" ] +stm32g070kb = [ "stm32-metapac/stm32g070kb" ] +stm32g070rb = [ "stm32-metapac/stm32g070rb" ] +stm32g071c6 = [ "stm32-metapac/stm32g071c6" ] +stm32g071c8 = [ "stm32-metapac/stm32g071c8" ] +stm32g071cb = [ "stm32-metapac/stm32g071cb" ] +stm32g071eb = [ "stm32-metapac/stm32g071eb" ] +stm32g071g6 = [ "stm32-metapac/stm32g071g6" ] +stm32g071g8 = [ "stm32-metapac/stm32g071g8" ] +stm32g071gb = [ "stm32-metapac/stm32g071gb" ] +stm32g071k6 = [ "stm32-metapac/stm32g071k6" ] +stm32g071k8 = [ "stm32-metapac/stm32g071k8" ] +stm32g071kb = [ "stm32-metapac/stm32g071kb" ] +stm32g071r6 = [ "stm32-metapac/stm32g071r6" ] +stm32g071r8 = [ "stm32-metapac/stm32g071r8" ] +stm32g071rb = [ "stm32-metapac/stm32g071rb" ] +stm32g081cb = [ "stm32-metapac/stm32g081cb" ] +stm32g081eb = [ "stm32-metapac/stm32g081eb" ] +stm32g081gb = [ "stm32-metapac/stm32g081gb" ] +stm32g081kb = [ "stm32-metapac/stm32g081kb" ] +stm32g081rb = [ "stm32-metapac/stm32g081rb" ] +stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce" ] +stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke" ] +stm32g0b0re = [ "stm32-metapac/stm32g0b0re" ] +stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve" ] +stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb" ] +stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc" ] +stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce" ] +stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb" ] +stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc" ] +stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke" ] +stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb" ] +stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc" ] +stm32g0b1me = [ "stm32-metapac/stm32g0b1me" ] +stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne" ] +stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb" ] +stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc" ] +stm32g0b1re = [ "stm32-metapac/stm32g0b1re" ] +stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb" ] +stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc" ] +stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve" ] +stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc" ] +stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce" ] +stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc" ] +stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke" ] +stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc" ] +stm32g0c1me = [ "stm32-metapac/stm32g0c1me" ] +stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne" ] +stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc" ] +stm32g0c1re = [ "stm32-metapac/stm32g0c1re" ] +stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc" ] +stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve" ] +stm32g431c6 = [ "stm32-metapac/stm32g431c6" ] +stm32g431c8 = [ "stm32-metapac/stm32g431c8" ] +stm32g431cb = [ "stm32-metapac/stm32g431cb" ] +stm32g431k6 = [ "stm32-metapac/stm32g431k6" ] +stm32g431k8 = [ "stm32-metapac/stm32g431k8" ] +stm32g431kb = [ "stm32-metapac/stm32g431kb" ] +stm32g431m6 = [ "stm32-metapac/stm32g431m6" ] +stm32g431m8 = [ "stm32-metapac/stm32g431m8" ] +stm32g431mb = [ "stm32-metapac/stm32g431mb" ] +stm32g431r6 = [ "stm32-metapac/stm32g431r6" ] +stm32g431r8 = [ "stm32-metapac/stm32g431r8" ] +stm32g431rb = [ "stm32-metapac/stm32g431rb" ] +stm32g431v6 = [ "stm32-metapac/stm32g431v6" ] +stm32g431v8 = [ "stm32-metapac/stm32g431v8" ] +stm32g431vb = [ "stm32-metapac/stm32g431vb" ] +stm32g441cb = [ "stm32-metapac/stm32g441cb" ] +stm32g441kb = [ "stm32-metapac/stm32g441kb" ] +stm32g441mb = [ "stm32-metapac/stm32g441mb" ] +stm32g441rb = [ "stm32-metapac/stm32g441rb" ] +stm32g441vb = [ "stm32-metapac/stm32g441vb" ] +stm32g471cc = [ "stm32-metapac/stm32g471cc" ] +stm32g471ce = [ "stm32-metapac/stm32g471ce" ] +stm32g471mc = [ "stm32-metapac/stm32g471mc" ] +stm32g471me = [ "stm32-metapac/stm32g471me" ] +stm32g471qc = [ "stm32-metapac/stm32g471qc" ] +stm32g471qe = [ "stm32-metapac/stm32g471qe" ] +stm32g471rc = [ "stm32-metapac/stm32g471rc" ] +stm32g471re = [ "stm32-metapac/stm32g471re" ] +stm32g471vc = [ "stm32-metapac/stm32g471vc" ] +stm32g471ve = [ "stm32-metapac/stm32g471ve" ] +stm32g473cb = [ "stm32-metapac/stm32g473cb" ] +stm32g473cc = [ "stm32-metapac/stm32g473cc" ] +stm32g473ce = [ "stm32-metapac/stm32g473ce" ] +stm32g473mb = [ "stm32-metapac/stm32g473mb" ] +stm32g473mc = [ "stm32-metapac/stm32g473mc" ] +stm32g473me = [ "stm32-metapac/stm32g473me" ] +stm32g473pb = [ "stm32-metapac/stm32g473pb" ] +stm32g473pc = [ "stm32-metapac/stm32g473pc" ] +stm32g473pe = [ "stm32-metapac/stm32g473pe" ] +stm32g473qb = [ "stm32-metapac/stm32g473qb" ] +stm32g473qc = [ "stm32-metapac/stm32g473qc" ] +stm32g473qe = [ "stm32-metapac/stm32g473qe" ] +stm32g473rb = [ "stm32-metapac/stm32g473rb" ] +stm32g473rc = [ "stm32-metapac/stm32g473rc" ] +stm32g473re = [ "stm32-metapac/stm32g473re" ] +stm32g473vb = [ "stm32-metapac/stm32g473vb" ] +stm32g473vc = [ "stm32-metapac/stm32g473vc" ] +stm32g473ve = [ "stm32-metapac/stm32g473ve" ] +stm32g474cb = [ "stm32-metapac/stm32g474cb" ] +stm32g474cc = [ "stm32-metapac/stm32g474cc" ] +stm32g474ce = [ "stm32-metapac/stm32g474ce" ] +stm32g474mb = [ "stm32-metapac/stm32g474mb" ] +stm32g474mc = [ "stm32-metapac/stm32g474mc" ] +stm32g474me = [ "stm32-metapac/stm32g474me" ] +stm32g474pb = [ "stm32-metapac/stm32g474pb" ] +stm32g474pc = [ "stm32-metapac/stm32g474pc" ] +stm32g474pe = [ "stm32-metapac/stm32g474pe" ] +stm32g474qb = [ "stm32-metapac/stm32g474qb" ] +stm32g474qc = [ "stm32-metapac/stm32g474qc" ] +stm32g474qe = [ "stm32-metapac/stm32g474qe" ] +stm32g474rb = [ "stm32-metapac/stm32g474rb" ] +stm32g474rc = [ "stm32-metapac/stm32g474rc" ] +stm32g474re = [ "stm32-metapac/stm32g474re" ] +stm32g474vb = [ "stm32-metapac/stm32g474vb" ] +stm32g474vc = [ "stm32-metapac/stm32g474vc" ] +stm32g474ve = [ "stm32-metapac/stm32g474ve" ] +stm32g483ce = [ "stm32-metapac/stm32g483ce" ] +stm32g483me = [ "stm32-metapac/stm32g483me" ] +stm32g483pe = [ "stm32-metapac/stm32g483pe" ] +stm32g483qe = [ "stm32-metapac/stm32g483qe" ] +stm32g483re = [ "stm32-metapac/stm32g483re" ] +stm32g483ve = [ "stm32-metapac/stm32g483ve" ] +stm32g484ce = [ "stm32-metapac/stm32g484ce" ] +stm32g484me = [ "stm32-metapac/stm32g484me" ] +stm32g484pe = [ "stm32-metapac/stm32g484pe" ] +stm32g484qe = [ "stm32-metapac/stm32g484qe" ] +stm32g484re = [ "stm32-metapac/stm32g484re" ] +stm32g484ve = [ "stm32-metapac/stm32g484ve" ] +stm32g491cc = [ "stm32-metapac/stm32g491cc" ] +stm32g491ce = [ "stm32-metapac/stm32g491ce" ] +stm32g491kc = [ "stm32-metapac/stm32g491kc" ] +stm32g491ke = [ "stm32-metapac/stm32g491ke" ] +stm32g491mc = [ "stm32-metapac/stm32g491mc" ] +stm32g491me = [ "stm32-metapac/stm32g491me" ] +stm32g491rc = [ "stm32-metapac/stm32g491rc" ] +stm32g491re = [ "stm32-metapac/stm32g491re" ] +stm32g491vc = [ "stm32-metapac/stm32g491vc" ] +stm32g491ve = [ "stm32-metapac/stm32g491ve" ] +stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce" ] +stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] +stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] +stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] +stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] +stm32h503cb = [ "stm32-metapac/stm32h503cb" ] +stm32h503eb = [ "stm32-metapac/stm32h503eb" ] +stm32h503kb = [ "stm32-metapac/stm32h503kb" ] +stm32h503rb = [ "stm32-metapac/stm32h503rb" ] +stm32h562ag = [ "stm32-metapac/stm32h562ag" ] +stm32h562ai = [ "stm32-metapac/stm32h562ai" ] +stm32h562ig = [ "stm32-metapac/stm32h562ig" ] +stm32h562ii = [ "stm32-metapac/stm32h562ii" ] +stm32h562rg = [ "stm32-metapac/stm32h562rg" ] +stm32h562ri = [ "stm32-metapac/stm32h562ri" ] +stm32h562vg = [ "stm32-metapac/stm32h562vg" ] +stm32h562vi = [ "stm32-metapac/stm32h562vi" ] +stm32h562zg = [ "stm32-metapac/stm32h562zg" ] +stm32h562zi = [ "stm32-metapac/stm32h562zi" ] +stm32h563ag = [ "stm32-metapac/stm32h563ag" ] +stm32h563ai = [ "stm32-metapac/stm32h563ai" ] +stm32h563ig = [ "stm32-metapac/stm32h563ig" ] +stm32h563ii = [ "stm32-metapac/stm32h563ii" ] +stm32h563mi = [ "stm32-metapac/stm32h563mi" ] +stm32h563rg = [ "stm32-metapac/stm32h563rg" ] +stm32h563ri = [ "stm32-metapac/stm32h563ri" ] +stm32h563vg = [ "stm32-metapac/stm32h563vg" ] +stm32h563vi = [ "stm32-metapac/stm32h563vi" ] +stm32h563zg = [ "stm32-metapac/stm32h563zg" ] +stm32h563zi = [ "stm32-metapac/stm32h563zi" ] +stm32h573ai = [ "stm32-metapac/stm32h573ai" ] +stm32h573ii = [ "stm32-metapac/stm32h573ii" ] +stm32h573mi = [ "stm32-metapac/stm32h573mi" ] +stm32h573ri = [ "stm32-metapac/stm32h573ri" ] +stm32h573vi = [ "stm32-metapac/stm32h573vi" ] +stm32h573zi = [ "stm32-metapac/stm32h573zi" ] +stm32h723ve = [ "stm32-metapac/stm32h723ve" ] +stm32h723vg = [ "stm32-metapac/stm32h723vg" ] +stm32h723ze = [ "stm32-metapac/stm32h723ze" ] +stm32h723zg = [ "stm32-metapac/stm32h723zg" ] +stm32h725ae = [ "stm32-metapac/stm32h725ae" ] +stm32h725ag = [ "stm32-metapac/stm32h725ag" ] +stm32h725ie = [ "stm32-metapac/stm32h725ie" ] +stm32h725ig = [ "stm32-metapac/stm32h725ig" ] +stm32h725re = [ "stm32-metapac/stm32h725re" ] +stm32h725rg = [ "stm32-metapac/stm32h725rg" ] +stm32h725ve = [ "stm32-metapac/stm32h725ve" ] +stm32h725vg = [ "stm32-metapac/stm32h725vg" ] +stm32h725ze = [ "stm32-metapac/stm32h725ze" ] +stm32h725zg = [ "stm32-metapac/stm32h725zg" ] +stm32h730ab = [ "stm32-metapac/stm32h730ab" ] +stm32h730ib = [ "stm32-metapac/stm32h730ib" ] +stm32h730vb = [ "stm32-metapac/stm32h730vb" ] +stm32h730zb = [ "stm32-metapac/stm32h730zb" ] +stm32h733vg = [ "stm32-metapac/stm32h733vg" ] +stm32h733zg = [ "stm32-metapac/stm32h733zg" ] +stm32h735ag = [ "stm32-metapac/stm32h735ag" ] +stm32h735ig = [ "stm32-metapac/stm32h735ig" ] +stm32h735rg = [ "stm32-metapac/stm32h735rg" ] +stm32h735vg = [ "stm32-metapac/stm32h735vg" ] +stm32h735zg = [ "stm32-metapac/stm32h735zg" ] +stm32h742ag = [ "stm32-metapac/stm32h742ag" ] +stm32h742ai = [ "stm32-metapac/stm32h742ai" ] +stm32h742bg = [ "stm32-metapac/stm32h742bg" ] +stm32h742bi = [ "stm32-metapac/stm32h742bi" ] +stm32h742ig = [ "stm32-metapac/stm32h742ig" ] +stm32h742ii = [ "stm32-metapac/stm32h742ii" ] +stm32h742vg = [ "stm32-metapac/stm32h742vg" ] +stm32h742vi = [ "stm32-metapac/stm32h742vi" ] +stm32h742xg = [ "stm32-metapac/stm32h742xg" ] +stm32h742xi = [ "stm32-metapac/stm32h742xi" ] +stm32h742zg = [ "stm32-metapac/stm32h742zg" ] +stm32h742zi = [ "stm32-metapac/stm32h742zi" ] +stm32h743ag = [ "stm32-metapac/stm32h743ag" ] +stm32h743ai = [ "stm32-metapac/stm32h743ai" ] +stm32h743bg = [ "stm32-metapac/stm32h743bg" ] +stm32h743bi = [ "stm32-metapac/stm32h743bi" ] +stm32h743ig = [ "stm32-metapac/stm32h743ig" ] +stm32h743ii = [ "stm32-metapac/stm32h743ii" ] +stm32h743vg = [ "stm32-metapac/stm32h743vg" ] +stm32h743vi = [ "stm32-metapac/stm32h743vi" ] +stm32h743xg = [ "stm32-metapac/stm32h743xg" ] +stm32h743xi = [ "stm32-metapac/stm32h743xi" ] +stm32h743zg = [ "stm32-metapac/stm32h743zg" ] +stm32h743zi = [ "stm32-metapac/stm32h743zi" ] +stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7" ] +stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4" ] +stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7" ] +stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4" ] +stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7" ] +stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4" ] +stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7" ] +stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4" ] +stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7" ] +stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4" ] +stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7" ] +stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4" ] +stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7" ] +stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4" ] +stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7" ] +stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4" ] +stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7" ] +stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4" ] +stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7" ] +stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4" ] +stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7" ] +stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4" ] +stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7" ] +stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4" ] +stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7" ] +stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4" ] +stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7" ] +stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4" ] +stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7" ] +stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4" ] +stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7" ] +stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4" ] +stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7" ] +stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4" ] +stm32h750ib = [ "stm32-metapac/stm32h750ib" ] +stm32h750vb = [ "stm32-metapac/stm32h750vb" ] +stm32h750xb = [ "stm32-metapac/stm32h750xb" ] +stm32h750zb = [ "stm32-metapac/stm32h750zb" ] +stm32h753ai = [ "stm32-metapac/stm32h753ai" ] +stm32h753bi = [ "stm32-metapac/stm32h753bi" ] +stm32h753ii = [ "stm32-metapac/stm32h753ii" ] +stm32h753vi = [ "stm32-metapac/stm32h753vi" ] +stm32h753xi = [ "stm32-metapac/stm32h753xi" ] +stm32h753zi = [ "stm32-metapac/stm32h753zi" ] +stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7" ] +stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4" ] +stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7" ] +stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4" ] +stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7" ] +stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4" ] +stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7" ] +stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4" ] +stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7" ] +stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4" ] +stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7" ] +stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4" ] +stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7" ] +stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4" ] +stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7" ] +stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4" ] +stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7" ] +stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4" ] +stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] +stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] +stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] +stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii" ] +stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg" ] +stm32h7a3li = [ "stm32-metapac/stm32h7a3li" ] +stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng" ] +stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni" ] +stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi" ] +stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg" ] +stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri" ] +stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg" ] +stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi" ] +stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg" ] +stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi" ] +stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab" ] +stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib" ] +stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb" ] +stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb" ] +stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb" ] +stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai" ] +stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii" ] +stm32h7b3li = [ "stm32-metapac/stm32h7b3li" ] +stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni" ] +stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi" ] +stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri" ] +stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi" ] +stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi" ] +stm32l010c6 = [ "stm32-metapac/stm32l010c6" ] +stm32l010f4 = [ "stm32-metapac/stm32l010f4" ] +stm32l010k4 = [ "stm32-metapac/stm32l010k4" ] +stm32l010k8 = [ "stm32-metapac/stm32l010k8" ] +stm32l010r8 = [ "stm32-metapac/stm32l010r8" ] +stm32l010rb = [ "stm32-metapac/stm32l010rb" ] +stm32l011d3 = [ "stm32-metapac/stm32l011d3" ] +stm32l011d4 = [ "stm32-metapac/stm32l011d4" ] +stm32l011e3 = [ "stm32-metapac/stm32l011e3" ] +stm32l011e4 = [ "stm32-metapac/stm32l011e4" ] +stm32l011f3 = [ "stm32-metapac/stm32l011f3" ] +stm32l011f4 = [ "stm32-metapac/stm32l011f4" ] +stm32l011g3 = [ "stm32-metapac/stm32l011g3" ] +stm32l011g4 = [ "stm32-metapac/stm32l011g4" ] +stm32l011k3 = [ "stm32-metapac/stm32l011k3" ] +stm32l011k4 = [ "stm32-metapac/stm32l011k4" ] +stm32l021d4 = [ "stm32-metapac/stm32l021d4" ] +stm32l021f4 = [ "stm32-metapac/stm32l021f4" ] +stm32l021g4 = [ "stm32-metapac/stm32l021g4" ] +stm32l021k4 = [ "stm32-metapac/stm32l021k4" ] +stm32l031c4 = [ "stm32-metapac/stm32l031c4" ] +stm32l031c6 = [ "stm32-metapac/stm32l031c6" ] +stm32l031e4 = [ "stm32-metapac/stm32l031e4" ] +stm32l031e6 = [ "stm32-metapac/stm32l031e6" ] +stm32l031f4 = [ "stm32-metapac/stm32l031f4" ] +stm32l031f6 = [ "stm32-metapac/stm32l031f6" ] +stm32l031g4 = [ "stm32-metapac/stm32l031g4" ] +stm32l031g6 = [ "stm32-metapac/stm32l031g6" ] +stm32l031k4 = [ "stm32-metapac/stm32l031k4" ] +stm32l031k6 = [ "stm32-metapac/stm32l031k6" ] +stm32l041c4 = [ "stm32-metapac/stm32l041c4" ] +stm32l041c6 = [ "stm32-metapac/stm32l041c6" ] +stm32l041e6 = [ "stm32-metapac/stm32l041e6" ] +stm32l041f6 = [ "stm32-metapac/stm32l041f6" ] +stm32l041g6 = [ "stm32-metapac/stm32l041g6" ] +stm32l041k6 = [ "stm32-metapac/stm32l041k6" ] +stm32l051c6 = [ "stm32-metapac/stm32l051c6" ] +stm32l051c8 = [ "stm32-metapac/stm32l051c8" ] +stm32l051k6 = [ "stm32-metapac/stm32l051k6" ] +stm32l051k8 = [ "stm32-metapac/stm32l051k8" ] +stm32l051r6 = [ "stm32-metapac/stm32l051r6" ] +stm32l051r8 = [ "stm32-metapac/stm32l051r8" ] +stm32l051t6 = [ "stm32-metapac/stm32l051t6" ] +stm32l051t8 = [ "stm32-metapac/stm32l051t8" ] +stm32l052c6 = [ "stm32-metapac/stm32l052c6" ] +stm32l052c8 = [ "stm32-metapac/stm32l052c8" ] +stm32l052k6 = [ "stm32-metapac/stm32l052k6" ] +stm32l052k8 = [ "stm32-metapac/stm32l052k8" ] +stm32l052r6 = [ "stm32-metapac/stm32l052r6" ] +stm32l052r8 = [ "stm32-metapac/stm32l052r8" ] +stm32l052t6 = [ "stm32-metapac/stm32l052t6" ] +stm32l052t8 = [ "stm32-metapac/stm32l052t8" ] +stm32l053c6 = [ "stm32-metapac/stm32l053c6" ] +stm32l053c8 = [ "stm32-metapac/stm32l053c8" ] +stm32l053r6 = [ "stm32-metapac/stm32l053r6" ] +stm32l053r8 = [ "stm32-metapac/stm32l053r8" ] +stm32l062c8 = [ "stm32-metapac/stm32l062c8" ] +stm32l062k8 = [ "stm32-metapac/stm32l062k8" ] +stm32l063c8 = [ "stm32-metapac/stm32l063c8" ] +stm32l063r8 = [ "stm32-metapac/stm32l063r8" ] +stm32l071c8 = [ "stm32-metapac/stm32l071c8" ] +stm32l071cb = [ "stm32-metapac/stm32l071cb" ] +stm32l071cz = [ "stm32-metapac/stm32l071cz" ] +stm32l071k8 = [ "stm32-metapac/stm32l071k8" ] +stm32l071kb = [ "stm32-metapac/stm32l071kb" ] +stm32l071kz = [ "stm32-metapac/stm32l071kz" ] +stm32l071rb = [ "stm32-metapac/stm32l071rb" ] +stm32l071rz = [ "stm32-metapac/stm32l071rz" ] +stm32l071v8 = [ "stm32-metapac/stm32l071v8" ] +stm32l071vb = [ "stm32-metapac/stm32l071vb" ] +stm32l071vz = [ "stm32-metapac/stm32l071vz" ] +stm32l072cb = [ "stm32-metapac/stm32l072cb" ] +stm32l072cz = [ "stm32-metapac/stm32l072cz" ] +stm32l072kb = [ "stm32-metapac/stm32l072kb" ] +stm32l072kz = [ "stm32-metapac/stm32l072kz" ] +stm32l072rb = [ "stm32-metapac/stm32l072rb" ] +stm32l072rz = [ "stm32-metapac/stm32l072rz" ] +stm32l072v8 = [ "stm32-metapac/stm32l072v8" ] +stm32l072vb = [ "stm32-metapac/stm32l072vb" ] +stm32l072vz = [ "stm32-metapac/stm32l072vz" ] +stm32l073cb = [ "stm32-metapac/stm32l073cb" ] +stm32l073cz = [ "stm32-metapac/stm32l073cz" ] +stm32l073rb = [ "stm32-metapac/stm32l073rb" ] +stm32l073rz = [ "stm32-metapac/stm32l073rz" ] +stm32l073v8 = [ "stm32-metapac/stm32l073v8" ] +stm32l073vb = [ "stm32-metapac/stm32l073vb" ] +stm32l073vz = [ "stm32-metapac/stm32l073vz" ] +stm32l081cb = [ "stm32-metapac/stm32l081cb" ] +stm32l081cz = [ "stm32-metapac/stm32l081cz" ] +stm32l081kz = [ "stm32-metapac/stm32l081kz" ] +stm32l082cz = [ "stm32-metapac/stm32l082cz" ] +stm32l082kb = [ "stm32-metapac/stm32l082kb" ] +stm32l082kz = [ "stm32-metapac/stm32l082kz" ] +stm32l083cb = [ "stm32-metapac/stm32l083cb" ] +stm32l083cz = [ "stm32-metapac/stm32l083cz" ] +stm32l083rb = [ "stm32-metapac/stm32l083rb" ] +stm32l083rz = [ "stm32-metapac/stm32l083rz" ] +stm32l083v8 = [ "stm32-metapac/stm32l083v8" ] +stm32l083vb = [ "stm32-metapac/stm32l083vb" ] +stm32l083vz = [ "stm32-metapac/stm32l083vz" ] +stm32l100c6 = [ "stm32-metapac/stm32l100c6" ] +stm32l100c6-a = [ "stm32-metapac/stm32l100c6-a" ] +stm32l100r8 = [ "stm32-metapac/stm32l100r8" ] +stm32l100r8-a = [ "stm32-metapac/stm32l100r8-a" ] +stm32l100rb = [ "stm32-metapac/stm32l100rb" ] +stm32l100rb-a = [ "stm32-metapac/stm32l100rb-a" ] +stm32l100rc = [ "stm32-metapac/stm32l100rc" ] +stm32l151c6 = [ "stm32-metapac/stm32l151c6" ] +stm32l151c6-a = [ "stm32-metapac/stm32l151c6-a" ] +stm32l151c8 = [ "stm32-metapac/stm32l151c8" ] +stm32l151c8-a = [ "stm32-metapac/stm32l151c8-a" ] +stm32l151cb = [ "stm32-metapac/stm32l151cb" ] +stm32l151cb-a = [ "stm32-metapac/stm32l151cb-a" ] +stm32l151cc = [ "stm32-metapac/stm32l151cc" ] +stm32l151qc = [ "stm32-metapac/stm32l151qc" ] +stm32l151qd = [ "stm32-metapac/stm32l151qd" ] +stm32l151qe = [ "stm32-metapac/stm32l151qe" ] +stm32l151r6 = [ "stm32-metapac/stm32l151r6" ] +stm32l151r6-a = [ "stm32-metapac/stm32l151r6-a" ] +stm32l151r8 = [ "stm32-metapac/stm32l151r8" ] +stm32l151r8-a = [ "stm32-metapac/stm32l151r8-a" ] +stm32l151rb = [ "stm32-metapac/stm32l151rb" ] +stm32l151rb-a = [ "stm32-metapac/stm32l151rb-a" ] +stm32l151rc = [ "stm32-metapac/stm32l151rc" ] +stm32l151rc-a = [ "stm32-metapac/stm32l151rc-a" ] +stm32l151rd = [ "stm32-metapac/stm32l151rd" ] +stm32l151re = [ "stm32-metapac/stm32l151re" ] +stm32l151uc = [ "stm32-metapac/stm32l151uc" ] +stm32l151v8 = [ "stm32-metapac/stm32l151v8" ] +stm32l151v8-a = [ "stm32-metapac/stm32l151v8-a" ] +stm32l151vb = [ "stm32-metapac/stm32l151vb" ] +stm32l151vb-a = [ "stm32-metapac/stm32l151vb-a" ] +stm32l151vc = [ "stm32-metapac/stm32l151vc" ] +stm32l151vc-a = [ "stm32-metapac/stm32l151vc-a" ] +stm32l151vd = [ "stm32-metapac/stm32l151vd" ] +stm32l151vd-x = [ "stm32-metapac/stm32l151vd-x" ] +stm32l151ve = [ "stm32-metapac/stm32l151ve" ] +stm32l151zc = [ "stm32-metapac/stm32l151zc" ] +stm32l151zd = [ "stm32-metapac/stm32l151zd" ] +stm32l151ze = [ "stm32-metapac/stm32l151ze" ] +stm32l152c6 = [ "stm32-metapac/stm32l152c6" ] +stm32l152c6-a = [ "stm32-metapac/stm32l152c6-a" ] +stm32l152c8 = [ "stm32-metapac/stm32l152c8" ] +stm32l152c8-a = [ "stm32-metapac/stm32l152c8-a" ] +stm32l152cb = [ "stm32-metapac/stm32l152cb" ] +stm32l152cb-a = [ "stm32-metapac/stm32l152cb-a" ] +stm32l152cc = [ "stm32-metapac/stm32l152cc" ] +stm32l152qc = [ "stm32-metapac/stm32l152qc" ] +stm32l152qd = [ "stm32-metapac/stm32l152qd" ] +stm32l152qe = [ "stm32-metapac/stm32l152qe" ] +stm32l152r6 = [ "stm32-metapac/stm32l152r6" ] +stm32l152r6-a = [ "stm32-metapac/stm32l152r6-a" ] +stm32l152r8 = [ "stm32-metapac/stm32l152r8" ] +stm32l152r8-a = [ "stm32-metapac/stm32l152r8-a" ] +stm32l152rb = [ "stm32-metapac/stm32l152rb" ] +stm32l152rb-a = [ "stm32-metapac/stm32l152rb-a" ] +stm32l152rc = [ "stm32-metapac/stm32l152rc" ] +stm32l152rc-a = [ "stm32-metapac/stm32l152rc-a" ] +stm32l152rd = [ "stm32-metapac/stm32l152rd" ] +stm32l152re = [ "stm32-metapac/stm32l152re" ] +stm32l152uc = [ "stm32-metapac/stm32l152uc" ] +stm32l152v8 = [ "stm32-metapac/stm32l152v8" ] +stm32l152v8-a = [ "stm32-metapac/stm32l152v8-a" ] +stm32l152vb = [ "stm32-metapac/stm32l152vb" ] +stm32l152vb-a = [ "stm32-metapac/stm32l152vb-a" ] +stm32l152vc = [ "stm32-metapac/stm32l152vc" ] +stm32l152vc-a = [ "stm32-metapac/stm32l152vc-a" ] +stm32l152vd = [ "stm32-metapac/stm32l152vd" ] +stm32l152vd-x = [ "stm32-metapac/stm32l152vd-x" ] +stm32l152ve = [ "stm32-metapac/stm32l152ve" ] +stm32l152zc = [ "stm32-metapac/stm32l152zc" ] +stm32l152zd = [ "stm32-metapac/stm32l152zd" ] +stm32l152ze = [ "stm32-metapac/stm32l152ze" ] +stm32l162qc = [ "stm32-metapac/stm32l162qc" ] +stm32l162qd = [ "stm32-metapac/stm32l162qd" ] +stm32l162rc = [ "stm32-metapac/stm32l162rc" ] +stm32l162rc-a = [ "stm32-metapac/stm32l162rc-a" ] +stm32l162rd = [ "stm32-metapac/stm32l162rd" ] +stm32l162re = [ "stm32-metapac/stm32l162re" ] +stm32l162vc = [ "stm32-metapac/stm32l162vc" ] +stm32l162vc-a = [ "stm32-metapac/stm32l162vc-a" ] +stm32l162vd = [ "stm32-metapac/stm32l162vd" ] +stm32l162vd-x = [ "stm32-metapac/stm32l162vd-x" ] +stm32l162ve = [ "stm32-metapac/stm32l162ve" ] +stm32l162zc = [ "stm32-metapac/stm32l162zc" ] +stm32l162zd = [ "stm32-metapac/stm32l162zd" ] +stm32l162ze = [ "stm32-metapac/stm32l162ze" ] +stm32l412c8 = [ "stm32-metapac/stm32l412c8" ] +stm32l412cb = [ "stm32-metapac/stm32l412cb" ] +stm32l412k8 = [ "stm32-metapac/stm32l412k8" ] +stm32l412kb = [ "stm32-metapac/stm32l412kb" ] +stm32l412r8 = [ "stm32-metapac/stm32l412r8" ] +stm32l412rb = [ "stm32-metapac/stm32l412rb" ] +stm32l412t8 = [ "stm32-metapac/stm32l412t8" ] +stm32l412tb = [ "stm32-metapac/stm32l412tb" ] +stm32l422cb = [ "stm32-metapac/stm32l422cb" ] +stm32l422kb = [ "stm32-metapac/stm32l422kb" ] +stm32l422rb = [ "stm32-metapac/stm32l422rb" ] +stm32l422tb = [ "stm32-metapac/stm32l422tb" ] +stm32l431cb = [ "stm32-metapac/stm32l431cb" ] +stm32l431cc = [ "stm32-metapac/stm32l431cc" ] +stm32l431kb = [ "stm32-metapac/stm32l431kb" ] +stm32l431kc = [ "stm32-metapac/stm32l431kc" ] +stm32l431rb = [ "stm32-metapac/stm32l431rb" ] +stm32l431rc = [ "stm32-metapac/stm32l431rc" ] +stm32l431vc = [ "stm32-metapac/stm32l431vc" ] +stm32l432kb = [ "stm32-metapac/stm32l432kb" ] +stm32l432kc = [ "stm32-metapac/stm32l432kc" ] +stm32l433cb = [ "stm32-metapac/stm32l433cb" ] +stm32l433cc = [ "stm32-metapac/stm32l433cc" ] +stm32l433rb = [ "stm32-metapac/stm32l433rb" ] +stm32l433rc = [ "stm32-metapac/stm32l433rc" ] +stm32l433vc = [ "stm32-metapac/stm32l433vc" ] +stm32l442kc = [ "stm32-metapac/stm32l442kc" ] +stm32l443cc = [ "stm32-metapac/stm32l443cc" ] +stm32l443rc = [ "stm32-metapac/stm32l443rc" ] +stm32l443vc = [ "stm32-metapac/stm32l443vc" ] +stm32l451cc = [ "stm32-metapac/stm32l451cc" ] +stm32l451ce = [ "stm32-metapac/stm32l451ce" ] +stm32l451rc = [ "stm32-metapac/stm32l451rc" ] +stm32l451re = [ "stm32-metapac/stm32l451re" ] +stm32l451vc = [ "stm32-metapac/stm32l451vc" ] +stm32l451ve = [ "stm32-metapac/stm32l451ve" ] +stm32l452cc = [ "stm32-metapac/stm32l452cc" ] +stm32l452ce = [ "stm32-metapac/stm32l452ce" ] +stm32l452rc = [ "stm32-metapac/stm32l452rc" ] +stm32l452re = [ "stm32-metapac/stm32l452re" ] +stm32l452vc = [ "stm32-metapac/stm32l452vc" ] +stm32l452ve = [ "stm32-metapac/stm32l452ve" ] +stm32l462ce = [ "stm32-metapac/stm32l462ce" ] +stm32l462re = [ "stm32-metapac/stm32l462re" ] +stm32l462ve = [ "stm32-metapac/stm32l462ve" ] +stm32l471qe = [ "stm32-metapac/stm32l471qe" ] +stm32l471qg = [ "stm32-metapac/stm32l471qg" ] +stm32l471re = [ "stm32-metapac/stm32l471re" ] +stm32l471rg = [ "stm32-metapac/stm32l471rg" ] +stm32l471ve = [ "stm32-metapac/stm32l471ve" ] +stm32l471vg = [ "stm32-metapac/stm32l471vg" ] +stm32l471ze = [ "stm32-metapac/stm32l471ze" ] +stm32l471zg = [ "stm32-metapac/stm32l471zg" ] +stm32l475rc = [ "stm32-metapac/stm32l475rc" ] +stm32l475re = [ "stm32-metapac/stm32l475re" ] +stm32l475rg = [ "stm32-metapac/stm32l475rg" ] +stm32l475vc = [ "stm32-metapac/stm32l475vc" ] +stm32l475ve = [ "stm32-metapac/stm32l475ve" ] +stm32l475vg = [ "stm32-metapac/stm32l475vg" ] +stm32l476je = [ "stm32-metapac/stm32l476je" ] +stm32l476jg = [ "stm32-metapac/stm32l476jg" ] +stm32l476me = [ "stm32-metapac/stm32l476me" ] +stm32l476mg = [ "stm32-metapac/stm32l476mg" ] +stm32l476qe = [ "stm32-metapac/stm32l476qe" ] +stm32l476qg = [ "stm32-metapac/stm32l476qg" ] +stm32l476rc = [ "stm32-metapac/stm32l476rc" ] +stm32l476re = [ "stm32-metapac/stm32l476re" ] +stm32l476rg = [ "stm32-metapac/stm32l476rg" ] +stm32l476vc = [ "stm32-metapac/stm32l476vc" ] +stm32l476ve = [ "stm32-metapac/stm32l476ve" ] +stm32l476vg = [ "stm32-metapac/stm32l476vg" ] +stm32l476ze = [ "stm32-metapac/stm32l476ze" ] +stm32l476zg = [ "stm32-metapac/stm32l476zg" ] +stm32l486jg = [ "stm32-metapac/stm32l486jg" ] +stm32l486qg = [ "stm32-metapac/stm32l486qg" ] +stm32l486rg = [ "stm32-metapac/stm32l486rg" ] +stm32l486vg = [ "stm32-metapac/stm32l486vg" ] +stm32l486zg = [ "stm32-metapac/stm32l486zg" ] +stm32l496ae = [ "stm32-metapac/stm32l496ae" ] +stm32l496ag = [ "stm32-metapac/stm32l496ag" ] +stm32l496qe = [ "stm32-metapac/stm32l496qe" ] +stm32l496qg = [ "stm32-metapac/stm32l496qg" ] +stm32l496re = [ "stm32-metapac/stm32l496re" ] +stm32l496rg = [ "stm32-metapac/stm32l496rg" ] +stm32l496ve = [ "stm32-metapac/stm32l496ve" ] +stm32l496vg = [ "stm32-metapac/stm32l496vg" ] +stm32l496wg = [ "stm32-metapac/stm32l496wg" ] +stm32l496ze = [ "stm32-metapac/stm32l496ze" ] +stm32l496zg = [ "stm32-metapac/stm32l496zg" ] +stm32l4a6ag = [ "stm32-metapac/stm32l4a6ag" ] +stm32l4a6qg = [ "stm32-metapac/stm32l4a6qg" ] +stm32l4a6rg = [ "stm32-metapac/stm32l4a6rg" ] +stm32l4a6vg = [ "stm32-metapac/stm32l4a6vg" ] +stm32l4a6zg = [ "stm32-metapac/stm32l4a6zg" ] +stm32l4p5ae = [ "stm32-metapac/stm32l4p5ae" ] +stm32l4p5ag = [ "stm32-metapac/stm32l4p5ag" ] +stm32l4p5ce = [ "stm32-metapac/stm32l4p5ce" ] +stm32l4p5cg = [ "stm32-metapac/stm32l4p5cg" ] +stm32l4p5qe = [ "stm32-metapac/stm32l4p5qe" ] +stm32l4p5qg = [ "stm32-metapac/stm32l4p5qg" ] +stm32l4p5re = [ "stm32-metapac/stm32l4p5re" ] +stm32l4p5rg = [ "stm32-metapac/stm32l4p5rg" ] +stm32l4p5ve = [ "stm32-metapac/stm32l4p5ve" ] +stm32l4p5vg = [ "stm32-metapac/stm32l4p5vg" ] +stm32l4p5ze = [ "stm32-metapac/stm32l4p5ze" ] +stm32l4p5zg = [ "stm32-metapac/stm32l4p5zg" ] +stm32l4q5ag = [ "stm32-metapac/stm32l4q5ag" ] +stm32l4q5cg = [ "stm32-metapac/stm32l4q5cg" ] +stm32l4q5qg = [ "stm32-metapac/stm32l4q5qg" ] +stm32l4q5rg = [ "stm32-metapac/stm32l4q5rg" ] +stm32l4q5vg = [ "stm32-metapac/stm32l4q5vg" ] +stm32l4q5zg = [ "stm32-metapac/stm32l4q5zg" ] +stm32l4r5ag = [ "stm32-metapac/stm32l4r5ag" ] +stm32l4r5ai = [ "stm32-metapac/stm32l4r5ai" ] +stm32l4r5qg = [ "stm32-metapac/stm32l4r5qg" ] +stm32l4r5qi = [ "stm32-metapac/stm32l4r5qi" ] +stm32l4r5vg = [ "stm32-metapac/stm32l4r5vg" ] +stm32l4r5vi = [ "stm32-metapac/stm32l4r5vi" ] +stm32l4r5zg = [ "stm32-metapac/stm32l4r5zg" ] +stm32l4r5zi = [ "stm32-metapac/stm32l4r5zi" ] +stm32l4r7ai = [ "stm32-metapac/stm32l4r7ai" ] +stm32l4r7vi = [ "stm32-metapac/stm32l4r7vi" ] +stm32l4r7zi = [ "stm32-metapac/stm32l4r7zi" ] +stm32l4r9ag = [ "stm32-metapac/stm32l4r9ag" ] +stm32l4r9ai = [ "stm32-metapac/stm32l4r9ai" ] +stm32l4r9vg = [ "stm32-metapac/stm32l4r9vg" ] +stm32l4r9vi = [ "stm32-metapac/stm32l4r9vi" ] +stm32l4r9zg = [ "stm32-metapac/stm32l4r9zg" ] +stm32l4r9zi = [ "stm32-metapac/stm32l4r9zi" ] +stm32l4s5ai = [ "stm32-metapac/stm32l4s5ai" ] +stm32l4s5qi = [ "stm32-metapac/stm32l4s5qi" ] +stm32l4s5vi = [ "stm32-metapac/stm32l4s5vi" ] +stm32l4s5zi = [ "stm32-metapac/stm32l4s5zi" ] +stm32l4s7ai = [ "stm32-metapac/stm32l4s7ai" ] +stm32l4s7vi = [ "stm32-metapac/stm32l4s7vi" ] +stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ] +stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] +stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] +stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] +stm32l552cc = [ "stm32-metapac/stm32l552cc" ] +stm32l552ce = [ "stm32-metapac/stm32l552ce" ] +stm32l552me = [ "stm32-metapac/stm32l552me" ] +stm32l552qc = [ "stm32-metapac/stm32l552qc" ] +stm32l552qe = [ "stm32-metapac/stm32l552qe" ] +stm32l552rc = [ "stm32-metapac/stm32l552rc" ] +stm32l552re = [ "stm32-metapac/stm32l552re" ] +stm32l552vc = [ "stm32-metapac/stm32l552vc" ] +stm32l552ve = [ "stm32-metapac/stm32l552ve" ] +stm32l552zc = [ "stm32-metapac/stm32l552zc" ] +stm32l552ze = [ "stm32-metapac/stm32l552ze" ] +stm32l562ce = [ "stm32-metapac/stm32l562ce" ] +stm32l562me = [ "stm32-metapac/stm32l562me" ] +stm32l562qe = [ "stm32-metapac/stm32l562qe" ] +stm32l562re = [ "stm32-metapac/stm32l562re" ] +stm32l562ve = [ "stm32-metapac/stm32l562ve" ] +stm32l562ze = [ "stm32-metapac/stm32l562ze" ] +stm32u535cb = [ "stm32-metapac/stm32u535cb" ] +stm32u535cc = [ "stm32-metapac/stm32u535cc" ] +stm32u535ce = [ "stm32-metapac/stm32u535ce" ] +stm32u535je = [ "stm32-metapac/stm32u535je" ] +stm32u535nc = [ "stm32-metapac/stm32u535nc" ] +stm32u535ne = [ "stm32-metapac/stm32u535ne" ] +stm32u535rb = [ "stm32-metapac/stm32u535rb" ] +stm32u535rc = [ "stm32-metapac/stm32u535rc" ] +stm32u535re = [ "stm32-metapac/stm32u535re" ] +stm32u535vc = [ "stm32-metapac/stm32u535vc" ] +stm32u535ve = [ "stm32-metapac/stm32u535ve" ] +stm32u545ce = [ "stm32-metapac/stm32u545ce" ] +stm32u545je = [ "stm32-metapac/stm32u545je" ] +stm32u545ne = [ "stm32-metapac/stm32u545ne" ] +stm32u545re = [ "stm32-metapac/stm32u545re" ] +stm32u545ve = [ "stm32-metapac/stm32u545ve" ] +stm32u575ag = [ "stm32-metapac/stm32u575ag" ] +stm32u575ai = [ "stm32-metapac/stm32u575ai" ] +stm32u575cg = [ "stm32-metapac/stm32u575cg" ] +stm32u575ci = [ "stm32-metapac/stm32u575ci" ] +stm32u575og = [ "stm32-metapac/stm32u575og" ] +stm32u575oi = [ "stm32-metapac/stm32u575oi" ] +stm32u575qg = [ "stm32-metapac/stm32u575qg" ] +stm32u575qi = [ "stm32-metapac/stm32u575qi" ] +stm32u575rg = [ "stm32-metapac/stm32u575rg" ] +stm32u575ri = [ "stm32-metapac/stm32u575ri" ] +stm32u575vg = [ "stm32-metapac/stm32u575vg" ] +stm32u575vi = [ "stm32-metapac/stm32u575vi" ] +stm32u575zg = [ "stm32-metapac/stm32u575zg" ] +stm32u575zi = [ "stm32-metapac/stm32u575zi" ] +stm32u585ai = [ "stm32-metapac/stm32u585ai" ] +stm32u585ci = [ "stm32-metapac/stm32u585ci" ] +stm32u585oi = [ "stm32-metapac/stm32u585oi" ] +stm32u585qi = [ "stm32-metapac/stm32u585qi" ] +stm32u585ri = [ "stm32-metapac/stm32u585ri" ] +stm32u585vi = [ "stm32-metapac/stm32u585vi" ] +stm32u585zi = [ "stm32-metapac/stm32u585zi" ] +stm32u595ai = [ "stm32-metapac/stm32u595ai" ] +stm32u595aj = [ "stm32-metapac/stm32u595aj" ] +stm32u595qi = [ "stm32-metapac/stm32u595qi" ] +stm32u595qj = [ "stm32-metapac/stm32u595qj" ] +stm32u595ri = [ "stm32-metapac/stm32u595ri" ] +stm32u595rj = [ "stm32-metapac/stm32u595rj" ] +stm32u595vi = [ "stm32-metapac/stm32u595vi" ] +stm32u595vj = [ "stm32-metapac/stm32u595vj" ] +stm32u595zi = [ "stm32-metapac/stm32u595zi" ] +stm32u595zj = [ "stm32-metapac/stm32u595zj" ] +stm32u599bj = [ "stm32-metapac/stm32u599bj" ] +stm32u599ni = [ "stm32-metapac/stm32u599ni" ] +stm32u599nj = [ "stm32-metapac/stm32u599nj" ] +stm32u599vi = [ "stm32-metapac/stm32u599vi" ] +stm32u599vj = [ "stm32-metapac/stm32u599vj" ] +stm32u599zi = [ "stm32-metapac/stm32u599zi" ] +stm32u599zj = [ "stm32-metapac/stm32u599zj" ] +stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] +stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] +stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] +stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] +stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] +stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] +stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] +stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] +stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] +stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] +stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] +stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] +stm32wb35cc = [ "stm32-metapac/stm32wb35cc" ] +stm32wb35ce = [ "stm32-metapac/stm32wb35ce" ] +stm32wb50cg = [ "stm32-metapac/stm32wb50cg" ] +stm32wb55cc = [ "stm32-metapac/stm32wb55cc" ] +stm32wb55ce = [ "stm32-metapac/stm32wb55ce" ] +stm32wb55cg = [ "stm32-metapac/stm32wb55cg" ] +stm32wb55rc = [ "stm32-metapac/stm32wb55rc" ] +stm32wb55re = [ "stm32-metapac/stm32wb55re" ] +stm32wb55rg = [ "stm32-metapac/stm32wb55rg" ] +stm32wb55vc = [ "stm32-metapac/stm32wb55vc" ] +stm32wb55ve = [ "stm32-metapac/stm32wb55ve" ] +stm32wb55vg = [ "stm32-metapac/stm32wb55vg" ] +stm32wb55vy = [ "stm32-metapac/stm32wb55vy" ] +stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4" ] +stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p" ] +stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4" ] +stm32wl54jc-cm0p = [ "stm32-metapac/stm32wl54jc-cm0p" ] +stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4" ] +stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p" ] +stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4" ] +stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p" ] +stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] +stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] +stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] +stm32wle4j8 = [ "stm32-metapac/stm32wle4j8" ] +stm32wle4jb = [ "stm32-metapac/stm32wle4jb" ] +stm32wle4jc = [ "stm32-metapac/stm32wle4jc" ] +stm32wle5c8 = [ "stm32-metapac/stm32wle5c8" ] +stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] +stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] +stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] +stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] +stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] \ No newline at end of file From 3229b5e809688d99a592bbfd1f803e1fb9d62050 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 26 Apr 2023 00:23:18 +0200 Subject: [PATCH 1024/1575] rp/pio: remove PioPeripheral merge into PioInstance instead. PioPeripheral was mostly a wrapper around PioInstance anyway, and the way the wrapping was done required PioInstanceBase types where PIO{N} could've been used instead. --- embassy-rp/src/pio.rs | 64 ++++++++++-------------------- examples/rp/src/bin/pio_async.rs | 16 ++++---- examples/rp/src/bin/pio_dma.rs | 2 +- examples/rp/src/bin/pio_hd44780.rs | 5 +-- examples/rp/src/bin/ws2812-pio.rs | 4 +- 5 files changed, 33 insertions(+), 58 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index e9a67fd48..459b7806b 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -904,19 +904,19 @@ impl sealed::SmInstance for SmInstanceBase { } impl SmInstance for SmInstanceBase {} -pub trait PioPeripheral: sealed::PioPeripheral + Sized { +pub trait PioInstance: sealed::PioInstance + Sized + Unpin { fn pio(&self) -> u8 { - Self::Pio::PIO_NO + Self::PIO_NO } fn split( self, ) -> ( - PioCommonInstance, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, + PioCommonInstance, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, ) { ( PioCommonInstance { @@ -944,12 +944,6 @@ pub trait PioPeripheral: sealed::PioPeripheral + Sized { } mod sealed { - pub trait PioInstance { - const PIO_NO: u8; - const PIO: &'static crate::pac::pio::Pio; - const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; - } - pub trait PioCommon { type Pio: super::PioInstance; } @@ -968,46 +962,28 @@ mod sealed { const SM_NO: u8; } - pub trait PioPeripheral { - type Pio: super::PioInstance; + pub trait PioInstance { + const PIO_NO: u8; + const PIO: &'static crate::pac::pio::Pio; + const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; } } -// Identifies a specific PIO device -pub struct PioInstanceBase {} - -pub trait PioInstance: sealed::PioInstance + Unpin {} - -impl sealed::PioInstance for PioInstanceBase<0> { - const PIO_NO: u8 = 0; - const PIO: &'static pac::pio::Pio = &pac::PIO0; - const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO0_0; -} -impl PioInstance for PioInstanceBase<0> {} - -impl sealed::PioInstance for PioInstanceBase<1> { - const PIO_NO: u8 = 1; - const PIO: &'static pac::pio::Pio = &pac::PIO1; - const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO1_0; -} -impl PioInstance for PioInstanceBase<1> {} - -pub type Pio0 = PioInstanceBase<0>; -pub type Pio1 = PioInstanceBase<1>; - pub type Sm0 = SmInstanceBase<0>; pub type Sm1 = SmInstanceBase<1>; pub type Sm2 = SmInstanceBase<2>; pub type Sm3 = SmInstanceBase<3>; -macro_rules! impl_pio_sm { - ($name:ident, $pio:expr) => { - impl sealed::PioPeripheral for peripherals::$name { - type Pio = PioInstanceBase<$pio>; +macro_rules! impl_pio { + ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { + impl sealed::PioInstance for peripherals::$name { + const PIO_NO: u8 = $pio; + const PIO: &'static pac::pio::Pio = &pac::$pac; + const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; } - impl PioPeripheral for peripherals::$name {} + impl PioInstance for peripherals::$name {} }; } -impl_pio_sm!(PIO0, 0); -impl_pio_sm!(PIO1, 1); +impl_pio!(PIO0, 0, PIO0, PIO0_0); +impl_pio!(PIO1, 1, PIO1, PIO1_0); diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 1b075b8fd..16a09327f 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,15 +4,15 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; +use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - Pio0, PioCommon, PioCommonInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, - Sm1, Sm2, + PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance, pin: AnyPin) { // Setup sm0 // Send data serially to pin @@ -40,7 +40,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance) { +async fn pio_task_sm0(mut sm: PioStateMachineInstance) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -51,7 +51,7 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance) { } } -fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -70,7 +70,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance) { +async fn pio_task_sm1(mut sm: PioStateMachineInstance) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -78,7 +78,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance) { } } -fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -102,7 +102,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance) { +async fn pio_task_sm2(mut sm: PioStateMachineInstance) { sm.set_enable(true); loop { sm.wait_irq(3).await; diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 7d4919f75..ccbc70fe2 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{PioCommon, PioPeripheral, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{PioCommon, PioInstance, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 6bcd0652b..1b24897b0 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -9,8 +9,7 @@ use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioInstanceBase, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, - SmInstanceBase, + FifoJoin, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, }; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; @@ -68,7 +67,7 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachineInstance, SmInstanceBase<0>>, + sm: PioStateMachineInstance>, buf: [u8; 40], } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 041e8ae11..592caf244 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,8 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioCommonInstance, PioInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, - ShiftDirection, SmInstance, + FifoJoin, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, + SmInstance, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; From ac111f40d894dec12106638e47317d13728c52a1 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 13:46:51 +0200 Subject: [PATCH 1025/1575] rp/pio: fix PioPin::set_pull, set_schmitt comment --- embassy-rp/src/pio.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 459b7806b..27a6068f9 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -277,15 +277,14 @@ impl PioPin { #[inline] pub fn set_pull(&mut self, pull: Pull) { unsafe { - self.pad_ctrl().modify(|w| match pull { - Pull::Up => w.set_pue(true), - Pull::Down => w.set_pde(true), - Pull::None => {} + self.pad_ctrl().modify(|w| { + w.set_pue(pull == Pull::Up); + w.set_pde(pull == Pull::Down); }); } } - /// Set the pin's pull. + /// Set the pin's schmitt trigger. #[inline] pub fn set_schmitt(&mut self, enable: bool) { unsafe { From 8839f3f62ab85a1abd066fcbfa15693965e6fae8 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 26 Apr 2023 19:43:57 +0200 Subject: [PATCH 1026/1575] rp/pio: PioInstance::split -> Pio::new not requiring a PioInstance for splitting lets us split from a PeripheralRef or borrowed PIO as well, mirroring every other peripheral in embassy_rp. pio pins still have to be constructed from owned pin instances for now. --- embassy-rp/src/pio.rs | 92 +++++++++++++++--------------- examples/rp/src/bin/pio_async.rs | 22 ++++--- examples/rp/src/bin/pio_dma.rs | 10 +++- examples/rp/src/bin/pio_hd44780.rs | 10 ++-- examples/rp/src/bin/ws2812-pio.rs | 18 +++--- 5 files changed, 85 insertions(+), 67 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 27a6068f9..97e97b749 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; -use embassy_hal_common::PeripheralRef; +use embassy_hal_common::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{Channel, Transfer, Word}; @@ -316,16 +316,16 @@ impl SealedPin for PioPin { } } -pub struct PioStateMachineInstance { - pio: PhantomData, +pub struct PioStateMachineInstance<'d, PIO: PioInstance, SM: SmInstance> { + pio: PhantomData<&'d PIO>, sm: PhantomData, } -impl sealed::PioStateMachine for PioStateMachineInstance { +impl<'d, PIO: PioInstance, SM: SmInstance> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { type Pio = PIO; type Sm = SM; } -impl PioStateMachine for PioStateMachineInstance {} +impl<'d, PIO: PioInstance, SM: SmInstance> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn pio_no(&self) -> u8 { @@ -792,21 +792,21 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } -pub struct PioCommonInstance { +pub struct PioCommonInstance<'d, PIO: PioInstance> { instructions_used: u32, - pio: PhantomData, + pio: PhantomData<&'d PIO>, } -pub struct PioInstanceMemory { +pub struct PioInstanceMemory<'d, PIO: PioInstance> { used_mask: u32, - pio: PhantomData, + pio: PhantomData<&'d PIO>, } -impl sealed::PioCommon for PioCommonInstance { +impl<'d, PIO: PioInstance> sealed::PioCommon for PioCommonInstance<'d, PIO> { type Pio = PIO; } -impl PioCommon for PioCommonInstance { - fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory +impl<'d, PIO: PioInstance> PioCommon for PioCommonInstance<'d, PIO> { + fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, Self::Pio> where I: Iterator, { @@ -903,43 +903,45 @@ impl sealed::SmInstance for SmInstanceBase { } impl SmInstance for SmInstanceBase {} +pub struct Pio<'d, PIO: PioInstance> { + pub common: PioCommonInstance<'d, PIO>, + pub sm0: PioStateMachineInstance<'d, PIO, SmInstanceBase<0>>, + pub sm1: PioStateMachineInstance<'d, PIO, SmInstanceBase<1>>, + pub sm2: PioStateMachineInstance<'d, PIO, SmInstanceBase<2>>, + pub sm3: PioStateMachineInstance<'d, PIO, SmInstanceBase<3>>, +} + +impl<'d, PIO: PioInstance> Pio<'d, PIO> { + pub fn new(_pio: impl Peripheral

+ 'd) -> Self { + Self { + common: PioCommonInstance { + instructions_used: 0, + pio: PhantomData, + }, + sm0: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + sm1: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + sm2: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + sm3: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + } + } +} + pub trait PioInstance: sealed::PioInstance + Sized + Unpin { fn pio(&self) -> u8 { Self::PIO_NO } - - fn split( - self, - ) -> ( - PioCommonInstance, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - ) { - ( - PioCommonInstance { - instructions_used: 0, - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - ) - } } mod sealed { diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 16a09327f..69a22f238 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, + Pio, PioCommon, PioCommonInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; @@ -40,7 +40,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance) { +async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -70,7 +70,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance) { +async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -102,7 +102,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance) { +async fn pio_task_sm2(mut sm: PioStateMachineInstance<'static, PIO0, Sm2>) { sm.set_enable(true); loop { sm.wait_irq(3).await; @@ -115,11 +115,17 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let pio = p.PIO0; - let (mut pio0, mut sm0, mut sm1, mut sm2, ..) = pio.split(); + let Pio { + mut common, + mut sm0, + mut sm1, + mut sm2, + .. + } = Pio::new(pio); - setup_pio_task_sm0(&mut pio0, &mut sm0, p.PIN_0.degrade()); - setup_pio_task_sm1(&mut pio0, &mut sm1); - setup_pio_task_sm2(&mut pio0, &mut sm2); + setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0.degrade()); + setup_pio_task_sm1(&mut common, &mut sm1); + setup_pio_task_sm2(&mut common, &mut sm2); spawner.spawn(pio_task_sm0(sm0)).unwrap(); spawner.spawn(pio_task_sm1(sm1)).unwrap(); spawner.spawn(pio_task_sm2(sm2)).unwrap(); diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index ccbc70fe2..33c320b8f 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{PioCommon, PioInstance, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +19,11 @@ fn swap_nibbles(v: u32) -> u32 { async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let pio = p.PIO0; - let (mut pio0, mut sm, ..) = pio.split(); + let Pio { + mut common, + sm0: mut sm, + .. + } = Pio::new(pio); let prg = pio_proc::pio_asm!( ".origin 0", @@ -34,7 +38,7 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - pio0.write_instr(relocated.origin() as usize, relocated.code()); + common.write_instr(relocated.origin() as usize, relocated.code()); pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); let pio::Wrap { source, target } = relocated.wrap(); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 1b24897b0..994d4600a 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -9,7 +9,7 @@ use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, + FifoJoin, Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, }; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; @@ -67,14 +67,14 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachineInstance>, + sm: PioStateMachineInstance<'l, PIO0, SmInstanceBase<0>>, buf: [u8; 40], } impl<'l> HD44780<'l> { pub async fn new( - pio: PIO0, + pio: impl Peripheral

+ 'l, dma: impl Peripheral

+ 'l, rs: impl Pin, rw: impl Pin, @@ -87,7 +87,9 @@ impl<'l> HD44780<'l> { into_ref!(dma); let db7pin = db7.pin(); - let (mut common, mut sm0, ..) = pio.split(); + let Pio { + mut common, mut sm0, .. + } = Pio::new(pio); // takes command words ( <0:4>) let prg = pio_proc::pio_asm!( diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 592caf244..42c731bd7 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, + FifoJoin, Pio, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, }; use embassy_rp::pio_instr_util; @@ -14,12 +14,16 @@ use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812 { - sm: PioStateMachineInstance, +pub struct Ws2812<'d, P: PioInstance, S: SmInstance> { + sm: PioStateMachineInstance<'d, P, S>, } -impl Ws2812 { - pub fn new(mut pio: PioCommonInstance

, mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { +impl<'d, P: PioInstance, S: SmInstance> Ws2812<'d, P, S> { + pub fn new( + mut pio: PioCommonInstance<'d, P>, + mut sm: PioStateMachineInstance<'d, P, S>, + pin: gpio::AnyPin, + ) -> Self { // Setup sm0 // prepare the PIO program @@ -116,7 +120,7 @@ async fn main(_spawner: Spawner) { info!("Start"); let p = embassy_rp::init(Default::default()); - let (pio0, sm0, _sm1, _sm2, _sm3) = p.PIO0.split(); + let Pio { common, sm0, .. } = Pio::new(p.PIO0); // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit // feather boards for the 2040 both have one built in. @@ -125,7 +129,7 @@ async fn main(_spawner: Spawner) { // For the thing plus, use pin 8 // For the feather, use pin 16 - let mut ws2812 = Ws2812::new(pio0, sm0, p.PIN_8.degrade()); + let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8.degrade()); // Loop forever making RGB values and pushing them out to the WS2812. loop { From a167c77d3928e1304ccccec6ddf7572d1e3c4cd9 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 26 Apr 2023 20:27:31 +0200 Subject: [PATCH 1027/1575] rp/pio: make PioCommon a struct the PioCommon trait does not serve much of a purpose; there can be only two implementations and they only differ in a few associated constants. --- embassy-rp/src/pio.rs | 59 +++++++++++------------------- examples/rp/src/bin/pio_async.rs | 10 ++--- examples/rp/src/bin/pio_dma.rs | 2 +- examples/rp/src/bin/pio_hd44780.rs | 4 +- examples/rp/src/bin/ws2812-pio.rs | 9 +---- 5 files changed, 30 insertions(+), 54 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 97e97b749..6827e5119 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -792,7 +792,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } -pub struct PioCommonInstance<'d, PIO: PioInstance> { +pub struct PioCommon<'d, PIO: PioInstance> { instructions_used: u32, pio: PhantomData<&'d PIO>, } @@ -802,11 +802,8 @@ pub struct PioInstanceMemory<'d, PIO: PioInstance> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> sealed::PioCommon for PioCommonInstance<'d, PIO> { - type Pio = PIO; -} -impl<'d, PIO: PioInstance> PioCommon for PioCommonInstance<'d, PIO> { - fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, Self::Pio> +impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { + pub fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, PIO> where I: Iterator, { @@ -833,58 +830,50 @@ impl<'d, PIO: PioInstance> PioCommon for PioCommonInstance<'d, PIO> { } } - fn free_instr(&mut self, instrs: PioInstanceMemory) { + // TODO make instruction memory that is currently in use unfreeable + pub fn free_instr(&mut self, instrs: PioInstanceMemory) { self.instructions_used &= !instrs.used_mask; } -} -pub trait PioCommon: sealed::PioCommon + Sized { - fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory - where - I: Iterator; - - // TODO make instruction memory that is currently in use unfreeable - fn free_instr(&mut self, instrs: PioInstanceMemory); - - fn is_irq_set(&self, irq_no: u8) -> bool { + pub fn is_irq_set(&self, irq_no: u8) -> bool { assert!(irq_no < 8); unsafe { - let irq_flags = Self::Pio::PIO.irq(); + let irq_flags = PIO::PIO.irq(); irq_flags.read().0 & (1 << irq_no) != 0 } } - fn clear_irq(&mut self, irq_no: usize) { + pub fn clear_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); - unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } + unsafe { PIO::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } } - fn clear_irqs(&mut self, mask: u8) { - unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(mask)) } + pub fn clear_irqs(&mut self, mask: u8) { + unsafe { PIO::PIO.irq().write(|w| w.set_irq(mask)) } } - fn force_irq(&mut self, irq_no: usize) { + pub fn force_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); - unsafe { Self::Pio::PIO.irq_force().write(|w| w.set_irq_force(1 << irq_no)) } + unsafe { PIO::PIO.irq_force().write(|w| w.set_irq_force(1 << irq_no)) } } - fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { + pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { unsafe { // this can interfere with per-pin bypass functions. splitting the // modification is going to be fine since nothing that relies on // it can reasonably run before we finish. - Self::Pio::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); - Self::Pio::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); + PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); + PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); } } - fn get_input_sync_bypass(&self) -> u32 { - unsafe { Self::Pio::PIO.input_sync_bypass().read() } + pub fn get_input_sync_bypass(&self) -> u32 { + unsafe { PIO::PIO.input_sync_bypass().read() } } - fn make_pio_pin(&self, pin: impl Pin) -> PioPin { + pub fn make_pio_pin(&self, pin: impl Pin) -> PioPin { unsafe { - pin.io().ctrl().write(|w| w.set_funcsel(Self::Pio::FUNCSEL.0)); + pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); } PioPin { pin_bank: pin.pin_bank(), @@ -904,7 +893,7 @@ impl sealed::SmInstance for SmInstanceBase { impl SmInstance for SmInstanceBase {} pub struct Pio<'d, PIO: PioInstance> { - pub common: PioCommonInstance<'d, PIO>, + pub common: PioCommon<'d, PIO>, pub sm0: PioStateMachineInstance<'d, PIO, SmInstanceBase<0>>, pub sm1: PioStateMachineInstance<'d, PIO, SmInstanceBase<1>>, pub sm2: PioStateMachineInstance<'d, PIO, SmInstanceBase<2>>, @@ -914,7 +903,7 @@ pub struct Pio<'d, PIO: PioInstance> { impl<'d, PIO: PioInstance> Pio<'d, PIO> { pub fn new(_pio: impl Peripheral

+ 'd) -> Self { Self { - common: PioCommonInstance { + common: PioCommon { instructions_used: 0, pio: PhantomData, }, @@ -945,10 +934,6 @@ pub trait PioInstance: sealed::PioInstance + Sized + Unpin { } mod sealed { - pub trait PioCommon { - type Pio: super::PioInstance; - } - pub trait PioStateMachine { type Pio: super::PioInstance; type Sm: super::SmInstance; diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 69a22f238..50b001b69 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -5,14 +5,12 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - Pio, PioCommon, PioCommonInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, -}; +use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: AnyPin) { // Setup sm0 // Send data serially to pin @@ -51,7 +49,7 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { } } -fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -78,7 +76,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { } } -fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 33c320b8f..0f1f6df12 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Pio, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 994d4600a..20c6a0565 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -8,9 +8,7 @@ use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, -}; +use embassy_rp::pio::{FifoJoin, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 42c731bd7..a2121df42 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,8 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, - SmInstance, + FifoJoin, Pio, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; @@ -19,11 +18,7 @@ pub struct Ws2812<'d, P: PioInstance, S: SmInstance> { } impl<'d, P: PioInstance, S: SmInstance> Ws2812<'d, P, S> { - pub fn new( - mut pio: PioCommonInstance<'d, P>, - mut sm: PioStateMachineInstance<'d, P, S>, - pin: gpio::AnyPin, - ) -> Self { + pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachineInstance<'d, P, S>, pin: gpio::AnyPin) -> Self { // Setup sm0 // prepare the PIO program From 7a36072a15b2164a903ae3f36ee251eaf311216d Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 17:55:40 +0200 Subject: [PATCH 1028/1575] rp/pio: drop SmInstance{,Base} these are just overly convoluted ways of writing down numbers. --- embassy-rp/src/pio.rs | 130 +++++++++++------------------ examples/rp/src/bin/pio_async.rs | 14 ++-- examples/rp/src/bin/pio_hd44780.rs | 4 +- examples/rp/src/bin/ws2812-pio.rs | 6 +- 4 files changed, 60 insertions(+), 94 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 6827e5119..5433d3f21 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -12,7 +12,7 @@ use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{Drive, Pin, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; -use crate::pio::sealed::{PioInstance as _, SmInstance as _}; +use crate::pio::sealed::PioInstance as _; use crate::{interrupt, pac, peripherals, RegExt}; struct Wakers([AtomicWaker; 12]); @@ -119,10 +119,10 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture if self.get_mut().sm.try_push_tx(value) { Poll::Ready(()) } else { - WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::Sm::SM_NO as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::SM as usize].register(cx.waker()); unsafe { PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = TXNFULL_MASK << SM::Sm::SM_NO; + m.0 = TXNFULL_MASK << SM::SM; }); } // debug!("Pending"); @@ -135,7 +135,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<' fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = TXNFULL_MASK << SM::Sm::SM_NO; + m.0 = TXNFULL_MASK << SM::SM; }); } } @@ -164,10 +164,10 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, if let Some(v) = self.sm.try_pull_rx() { Poll::Ready(v) } else { - WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::Sm::SM_NO as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::SM].register(cx.waker()); unsafe { PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; + m.0 = RXNEMPTY_MASK << SM::SM; }); } //debug!("Pending"); @@ -180,7 +180,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, S fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; + m.0 = RXNEMPTY_MASK << SM::SM; }); } } @@ -316,16 +316,15 @@ impl SealedPin for PioPin { } } -pub struct PioStateMachineInstance<'d, PIO: PioInstance, SM: SmInstance> { +pub struct PioStateMachineInstance<'d, PIO: PioInstance, const SM: usize> { pio: PhantomData<&'d PIO>, - sm: PhantomData, } -impl<'d, PIO: PioInstance, SM: SmInstance> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { +impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { type Pio = PIO; - type Sm = SM; + const SM: usize = SM; } -impl<'d, PIO: PioInstance, SM: SmInstance> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} +impl<'d, PIO: PioInstance, const SM: usize> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn pio_no(&self) -> u8 { @@ -333,17 +332,17 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn sm_no(&self) -> u8 { - Self::Sm::SM_NO + Self::SM as u8 } fn restart(&mut self) { - let mask = 1u8 << Self::Sm::SM_NO; + let mask = 1u8 << Self::SM; unsafe { Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); } } fn set_enable(&mut self, enable: bool) { - let mask = 1u8 << Self::Sm::SM_NO; + let mask = 1u8 << Self::SM; unsafe { if enable { Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); @@ -354,40 +353,40 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn is_enabled(&self) -> bool { - unsafe { Self::Pio::PIO.ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.ctrl().read().sm_enable() & (1u8 << Self::SM) != 0 } } fn is_tx_empty(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().txempty() & (1u8 << Self::SM) != 0 } } fn is_tx_full(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().txfull() & (1u8 << Self::SM) != 0 } } fn is_rx_empty(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().rxempty() & (1u8 << Self::SM) != 0 } } fn is_rx_full(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().rxfull() & (1u8 << Self::SM) != 0 } } fn tx_level(&self) -> u8 { unsafe { let flevel = Self::Pio::PIO.flevel().read().0; - (flevel >> (Self::Sm::SM_NO * 8)) as u8 & 0x0f + (flevel >> (Self::SM * 8)) as u8 & 0x0f } } fn rx_level(&self) -> u8 { unsafe { let flevel = Self::Pio::PIO.flevel().read().0; - (flevel >> (Self::Sm::SM_NO * 8 + 4)) as u8 & 0x0f + (flevel >> (Self::SM * 8 + 4)) as u8 & 0x0f } } fn push_tx(&mut self, v: u32) { unsafe { - Self::Pio::PIO.txf(Self::Sm::SM_NO as usize).write_value(v); + Self::Pio::PIO.txf(Self::SM).write_value(v); } } @@ -400,7 +399,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn pull_rx(&mut self) -> u32 { - unsafe { Self::Pio::PIO.rxf(Self::Sm::SM_NO as usize).read() } + unsafe { Self::Pio::PIO.rxf(Self::SM).read() } } fn try_pull_rx(&mut self) -> Option { @@ -421,7 +420,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn clkdiv_restart(&mut self) { - let mask = 1u8 << Self::Sm::SM_NO; + let mask = 1u8 << Self::SM; unsafe { Self::Pio::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); } @@ -710,8 +709,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_tx_stalled(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().txstall() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_txstall(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().txstall() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_txstall(1 << Self::SM)); ret } } @@ -719,8 +718,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_tx_overflowed(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().txover() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_txover(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().txover() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_txover(1 << Self::SM)); ret } } @@ -728,8 +727,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_rx_stalled(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().rxstall() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_rxstall(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().rxstall() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_rxstall(1 << Self::SM)); ret } } @@ -737,8 +736,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_rx_underflowed(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().rxunder() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_rxunder(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().rxunder() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_rxunder(1 << Self::SM)); ret } } @@ -746,16 +745,15 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; - let sm_no = Self::Sm::SM_NO; + let sm_no = Self::SM; let p = ch.regs(); p.read_addr().write_value(data.as_ptr() as u32); - p.write_addr() - .write_value(Self::Pio::PIO.txf(sm_no as usize).ptr() as u32); + p.write_addr().write_value(Self::Pio::PIO.txf(sm_no).ptr() as u32); p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + sm_no)); + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no as u8)); w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(true); @@ -770,16 +768,15 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; - let sm_no = Self::Sm::SM_NO; + let sm_no = Self::SM; let p = ch.regs(); p.write_addr().write_value(data.as_ptr() as u32); - p.read_addr() - .write_value(Self::Pio::PIO.rxf(sm_no as usize).ptr() as u32); + p.read_addr().write_value(Self::Pio::PIO.rxf(sm_no).ptr() as u32); p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set RX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no as u8 + 4)); w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(false); @@ -882,22 +879,12 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } } -// Identifies a specific state machine inside a PIO device -pub struct SmInstanceBase {} - -pub trait SmInstance: sealed::SmInstance + Unpin {} - -impl sealed::SmInstance for SmInstanceBase { - const SM_NO: u8 = SM_NO; -} -impl SmInstance for SmInstanceBase {} - pub struct Pio<'d, PIO: PioInstance> { pub common: PioCommon<'d, PIO>, - pub sm0: PioStateMachineInstance<'d, PIO, SmInstanceBase<0>>, - pub sm1: PioStateMachineInstance<'d, PIO, SmInstanceBase<1>>, - pub sm2: PioStateMachineInstance<'d, PIO, SmInstanceBase<2>>, - pub sm3: PioStateMachineInstance<'d, PIO, SmInstanceBase<3>>, + pub sm0: PioStateMachineInstance<'d, PIO, 0>, + pub sm1: PioStateMachineInstance<'d, PIO, 1>, + pub sm2: PioStateMachineInstance<'d, PIO, 2>, + pub sm3: PioStateMachineInstance<'d, PIO, 3>, } impl<'d, PIO: PioInstance> Pio<'d, PIO> { @@ -907,22 +894,10 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { instructions_used: 0, pio: PhantomData, }, - sm0: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, - sm1: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, - sm2: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, - sm3: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, + sm0: PioStateMachineInstance { pio: PhantomData }, + sm1: PioStateMachineInstance { pio: PhantomData }, + sm2: PioStateMachineInstance { pio: PhantomData }, + sm3: PioStateMachineInstance { pio: PhantomData }, } } } @@ -936,18 +911,14 @@ pub trait PioInstance: sealed::PioInstance + Sized + Unpin { mod sealed { pub trait PioStateMachine { type Pio: super::PioInstance; - type Sm: super::SmInstance; + const SM: usize; #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize) + Self::Pio::PIO.sm(Self::SM as usize) } } - pub trait SmInstance { - const SM_NO: u8; - } - pub trait PioInstance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; @@ -955,11 +926,6 @@ mod sealed { } } -pub type Sm0 = SmInstanceBase<0>; -pub type Sm1 = SmInstanceBase<1>; -pub type Sm2 = SmInstanceBase<2>; -pub type Sm3 = SmInstanceBase<3>; - macro_rules! impl_pio { ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { impl sealed::PioInstance for peripherals::$name { diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 50b001b69..5fea7034b 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -5,12 +5,12 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; +use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: AnyPin) { // Setup sm0 // Send data serially to pin @@ -38,7 +38,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { +async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, 0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -49,7 +49,7 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { } } -fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -68,7 +68,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { +async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, 1>) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -76,7 +76,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { } } -fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -100,7 +100,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance<'static, PIO0, Sm2>) { +async fn pio_task_sm2(mut sm: PioStateMachineInstance<'static, PIO0, 2>) { sm.set_enable(true); loop { sm.wait_irq(3).await; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 20c6a0565..59b4c1f52 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -8,7 +8,7 @@ use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase}; +use embassy_rp::pio::{FifoJoin, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; @@ -65,7 +65,7 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachineInstance<'l, PIO0, SmInstanceBase<0>>, + sm: PioStateMachineInstance<'l, PIO0, 0>, buf: [u8; 40], } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index a2121df42..0975559d7 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,18 +6,18 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, + FifoJoin, Pio, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812<'d, P: PioInstance, S: SmInstance> { +pub struct Ws2812<'d, P: PioInstance, const S: usize> { sm: PioStateMachineInstance<'d, P, S>, } -impl<'d, P: PioInstance, S: SmInstance> Ws2812<'d, P, S> { +impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachineInstance<'d, P, S>, pin: gpio::AnyPin) -> Self { // Setup sm0 From a61701b7565536e6e1a224d64eafd373f7c18af5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 02:51:55 +0200 Subject: [PATCH 1029/1575] stm32/usart: add OVER8 and PRESC support, update PAC --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/usart/buffered.rs | 4 +- embassy-stm32/src/usart/mod.rs | 128 +++++++++++++++++++++------- 3 files changed, 102 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9686b10ce..b9887f9b3 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "6" +stm32-metapac = "7" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "6", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "7", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 3e23e7ca1..12cf8b0fc 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -84,7 +84,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } - #[cfg(not(usart_v1))] + #[cfg(not(any(usart_v1, usart_v2)))] pub fn new_with_de( peri: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -133,7 +133,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); } - configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + configure(r, &config, T::frequency(), T::KIND, true, true); unsafe { r.cr1().modify(|w| { diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ad450f2b3..dbce668c2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -12,10 +12,11 @@ use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; -#[cfg(any(lpuart_v1, lpuart_v2))] -use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; -#[cfg(not(any(lpuart_v1, lpuart_v2)))] -use crate::pac::usart::{regs, vals, Usart as Regs}; +#[cfg(not(any(usart_v1, usart_v2)))] +use crate::pac::usart::Lpuart as Regs; +#[cfg(any(usart_v1, usart_v2))] +use crate::pac::usart::Usart as Regs; +use crate::pac::usart::{regs, vals}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -159,7 +160,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); } - configure(r, &config, T::frequency(), T::MULTIPLIER, false, true); + configure(r, &config, T::frequency(), T::KIND, false, true); // create state once! let _s = T::state(); @@ -261,7 +262,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx.set_as_af(rx.af_num(), AFType::Input); } - configure(r, &config, T::frequency(), T::MULTIPLIER, true, false); + configure(r, &config, T::frequency(), T::KIND, true, false); irq.set_handler(Self::on_interrupt); irq.unpend(); @@ -653,7 +654,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } - #[cfg(not(usart_v1))] + #[cfg(not(any(usart_v1, usart_v2)))] pub fn new_with_de( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -696,7 +697,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); } - configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + configure(r, &config, T::frequency(), T::KIND, true, true); irq.set_handler(UartRx::::on_interrupt); irq.unpend(); @@ -763,16 +764,74 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } -fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable_rx: bool, enable_tx: bool) { +fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: bool, enable_tx: bool) { if !enable_rx && !enable_tx { panic!("USART: At least one of RX or TX should be enabled"); } - // TODO: better calculation, including error checking and OVER8 if possible. - let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier; + #[cfg(not(usart_v4))] + static DIVS: [(u16, ()); 1] = [(1, ())]; + + #[cfg(usart_v4)] + static DIVS: [(u16, vals::Presc); 12] = [ + (1, vals::Presc::DIV1), + (2, vals::Presc::DIV2), + (4, vals::Presc::DIV4), + (6, vals::Presc::DIV6), + (8, vals::Presc::DIV8), + (10, vals::Presc::DIV10), + (12, vals::Presc::DIV12), + (16, vals::Presc::DIV16), + (32, vals::Presc::DIV32), + (64, vals::Presc::DIV64), + (128, vals::Presc::DIV128), + (256, vals::Presc::DIV256), + ]; + + let (mul, brr_min, brr_max) = match kind { + #[cfg(any(usart_v3, usart_v4))] + Kind::Lpuart => (256, 0x300, 0x10_0000), + Kind::Uart => (1, 0x10, 0x1_0000), + }; + + #[cfg(not(usart_v1))] + let mut over8 = false; + let mut found = false; + for &(presc, _presc_val) in &DIVS { + let denom = (config.baudrate * presc as u32) as u64; + let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; + trace!("USART: presc={} div={:08x}", presc, div); + + if div < brr_min { + #[cfg(not(usart_v1))] + if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + over8 = true; + let div = div as u32; + unsafe { + r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + } + found = true; + break; + } + panic!("USART: baudrate too high"); + } + + if div < brr_max { + unsafe { + r.brr().write_value(regs::Brr(div as u32)); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + } + found = true; + break; + } + } + + assert!(found, "USART: baudrate too low"); unsafe { - r.brr().write_value(regs::Brr(div)); r.cr2().write(|w| { w.set_stop(match config.stop_bits { StopBits::STOP0P5 => vals::Stop::STOP0P5, @@ -801,6 +860,8 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable Parity::ParityEven => vals::Ps::EVEN, _ => vals::Ps::EVEN, }); + #[cfg(not(usart_v1))] + w.set_over8(vals::Over8(over8 as _)); }); } } @@ -986,43 +1047,45 @@ mod rx_ringbuffered; #[cfg(not(gpdma))] pub use rx_ringbuffered::RingBufferedUartRx; -#[cfg(usart_v1)] +use self::sealed::Kind; + +#[cfg(any(usart_v1, usart_v2))] fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { r.dr().ptr() as _ } -#[cfg(usart_v1)] +#[cfg(any(usart_v1, usart_v2))] fn rdr(r: crate::pac::usart::Usart) -> *mut u8 { r.dr().ptr() as _ } -#[cfg(usart_v1)] +#[cfg(any(usart_v1, usart_v2))] fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg { r.sr() } -#[cfg(usart_v1)] +#[cfg(any(usart_v1, usart_v2))] #[allow(unused)] unsafe fn clear_interrupt_flags(_r: Regs, _sr: regs::Sr) { // On v1 the flags are cleared implicitly by reads and writes to DR. } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] fn tdr(r: Regs) -> *mut u8 { r.tdr().ptr() as _ } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] fn rdr(r: Regs) -> *mut u8 { r.rdr().ptr() as _ } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] fn sr(r: Regs) -> crate::pac::common::Reg { r.isr() } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] #[allow(unused)] unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { r.icr().write(|w| *w = regs::Icr(sr.0)); @@ -1033,6 +1096,13 @@ pub(crate) mod sealed { use super::*; + #[derive(Clone, Copy, PartialEq, Eq)] + pub enum Kind { + Uart, + #[cfg(any(usart_v3, usart_v4))] + Lpuart, + } + pub struct State { pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, @@ -1048,7 +1118,7 @@ pub(crate) mod sealed { } pub trait BasicInstance: crate::rcc::RccPeripheral { - const MULTIPLIER: u32; + const KIND: Kind; type Interrupt: crate::interrupt::Interrupt; fn regs() -> Regs; @@ -1077,10 +1147,10 @@ pin_trait!(DePin, BasicInstance); dma_trait!(TxDma, BasicInstance); dma_trait!(RxDma, BasicInstance); -macro_rules! impl_lpuart { - ($inst:ident, $irq:ident, $mul:expr) => { +macro_rules! impl_usart { + ($inst:ident, $irq:ident, $kind:expr) => { impl sealed::BasicInstance for crate::peripherals::$inst { - const MULTIPLIER: u32 = $mul; + const KIND: Kind = $kind; type Interrupt = crate::interrupt::$irq; fn regs() -> Regs { @@ -1104,21 +1174,19 @@ macro_rules! impl_lpuart { } foreach_interrupt!( - ($inst:ident, lpuart, $block:ident, $signal_name:ident, $irq:ident) => { - impl_lpuart!($inst, $irq, 256); + ($inst:ident, usart, LPUART, $signal_name:ident, $irq:ident) => { + impl_usart!($inst, $irq, Kind::Lpuart); }; ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => { - impl_lpuart!($inst, $irq, 1); + impl_usart!($inst, $irq, Kind::Uart); impl sealed::FullInstance for peripherals::$inst { - fn regs_uart() -> crate::pac::usart::Usart { crate::pac::$inst } } - impl FullInstance for peripherals::$inst { - } + impl FullInstance for peripherals::$inst {} }; ); From 2bb6e93e86af02af97b4fb39197a1d1e87e635b9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 02:52:37 +0200 Subject: [PATCH 1030/1575] stm32/usart: add baudrate calc test. --- tests/stm32/Cargo.toml | 1 + tests/stm32/build.rs | 13 +++--- tests/stm32/src/bin/usart.rs | 77 +++++++++++++++++++++++-------- tests/stm32/src/example_common.rs | 5 ++ 4 files changed, 72 insertions(+), 24 deletions(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 5cd949661..83bf1e9c9 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-stm32-tests" version = "0.1.0" license = "MIT OR Apache-2.0" +autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 3e67a7392..4c76a8eeb 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -6,15 +6,16 @@ fn main() -> Result<(), Box> { let out = PathBuf::from(env::var("OUT_DIR").unwrap()); fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap(); println!("cargo:rustc-link-search={}", out.display()); - println!("cargo:rerun-if-changed=link_ram.x"); - println!("cargo:rustc-link-arg-bins=--nmagic"); // too little RAM to run from RAM. - #[cfg(any(feature = "stm32c031c6"))] - println!("cargo:rustc-link-arg-bins=-Tlink.x"); - #[cfg(not(any(feature = "stm32c031c6")))] - println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + if cfg!(any(feature = "stm32c031c6")) { + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rerun-if-changed=link.x"); + } else { + println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + println!("cargo:rerun-if-changed=link_ram.x"); + } println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index cca8c42ee..bda2ce9c2 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -9,6 +9,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_time::{Duration, Instant}; use example_common::*; #[embassy_executor::main] @@ -19,36 +20,76 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); #[cfg(feature = "stm32h563zi")] - let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); #[cfg(feature = "stm32c031c6")] - let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); - let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); + { + let config = Config::default(); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); - // We can't send too many bytes, they have to fit in the FIFO. - // This is because we aren't sending+receiving at the same time. + // We can't send too many bytes, they have to fit in the FIFO. + // This is because we aren't sending+receiving at the same time. - let data = [0xC0, 0xDE]; - usart.blocking_write(&data).unwrap(); + let data = [0xC0, 0xDE]; + usart.blocking_write(&data).unwrap(); - let mut buf = [0; 2]; - usart.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, data); + let mut buf = [0; 2]; + usart.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, data); + } + + // Test that baudrate divider is calculated correctly. + // Do it by comparing the time it takes to send a known number of bytes. + for baudrate in [ + 300, + 9600, + 115200, + 250_000, + 337_934, + #[cfg(not(feature = "stm32f103c8"))] + 1_000_000, + #[cfg(not(feature = "stm32f103c8"))] + 2_000_000, + ] { + info!("testing baudrate {}", baudrate); + + let mut config = Config::default(); + config.baudrate = baudrate; + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); + + let n = (baudrate as usize / 100).max(64); + + let start = Instant::now(); + for _ in 0..n { + usart.blocking_write(&[0x00]).unwrap(); + } + let dur = Instant::now() - start; + let want_dur = Duration::from_micros(n as u64 * 10 * 1_000_000 / (baudrate as u64)); + let fuzz = want_dur / 5; + if dur < want_dur - fuzz || dur > want_dur + fuzz { + defmt::panic!( + "bad duration for baudrate {}: got {:?} want {:?}", + baudrate, + dur, + want_dur + ); + } + } info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/stm32/src/example_common.rs b/tests/stm32/src/example_common.rs index a4f8668c7..3d150da60 100644 --- a/tests/stm32/src/example_common.rs +++ b/tests/stm32/src/example_common.rs @@ -16,5 +16,10 @@ pub fn config() -> Config { config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); } + #[cfg(feature = "stm32u585ai")] + { + config.rcc.mux = embassy_stm32::rcc::ClockSrc::MSI(embassy_stm32::rcc::MSIRange::Range48mhz); + } + config } From 1078f6f4e7c8473013c46543663cb6ca6b0e0c3d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 19:35:02 +0200 Subject: [PATCH 1031/1575] stm32/test: workaround #1426 --- tests/stm32/src/bin/usart_dma.rs | 3 +++ tests/stm32/src/bin/usart_rx_ringbuffered.rs | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index de6cd41d1..62444f0a8 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -94,6 +94,9 @@ async fn main(_spawner: Spawner) { let rx_fut = async { rx.read(&mut rx_buf).await.unwrap(); }; + + // note: rx needs to be polled first, to workaround this bug: + // https://github.com/embassy-rs/embassy/issues/1426 join(rx_fut, tx_fut).await; assert_eq!(tx_buf, rx_buf); diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 2c4a8fdf4..9d75dbe55 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -145,13 +145,16 @@ async fn main(spawner: Spawner) { #[embassy_executor::task] async fn transmit_task(mut tx: UartTx<'static, board::Uart, board::TxDma>) { + // workaround https://github.com/embassy-rs/embassy/issues/1426 + Timer::after(Duration::from_millis(100) as _).await; + let mut rng = ChaCha8Rng::seed_from_u64(1337); info!("Starting random transmissions into void..."); let mut i: u8 = 0; loop { - let mut buf = [0; 32]; + let mut buf = [0; 256]; let len = 1 + (rng.next_u32() as usize % buf.len()); for b in &mut buf[..len] { *b = i; @@ -172,7 +175,7 @@ async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::Rx let mut i = 0; let mut expected = 0; loop { - let mut buf = [0; 100]; + let mut buf = [0; 256]; let max_len = 1 + (rng.next_u32() as usize % buf.len()); let received = match rx.read(&mut buf[..max_len]).await { Ok(r) => r, From a85b34c1fec458f00c37db2f395345540d73d519 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 19:35:18 +0200 Subject: [PATCH 1032/1575] stm32/test: F1 no longer fits in RAM. --- tests/stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 4c76a8eeb..7ae311778 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -9,7 +9,7 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-arg-bins=--nmagic"); // too little RAM to run from RAM. - if cfg!(any(feature = "stm32c031c6")) { + if cfg!(any(feature = "stm32f103c8", feature = "stm32c031c6")) { println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rerun-if-changed=link.x"); } else { From 433422b9f2b08d4bf9f2cef4ded37c14914db157 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 19:35:32 +0200 Subject: [PATCH 1033/1575] stm32/test: remove adsfa --- tests/stm32/src/bin/spi.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 0f5e563b1..a87ac3237 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -35,7 +35,6 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "stm32c031c6")] let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); - info!("asdfa;"); let mut spi = Spi::new( spi, sck, // Arduino D13 From 1e8da91defc2b5b328d733dedffc25893c786c1f Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 20:39:19 +0200 Subject: [PATCH 1034/1575] rp/pio: make free_instr unsafe we can't prove that some instruction memory is not used as long as state machines are alive, and we can pass instance memory handles between instances as well. mark free_instr unsafe, with documentation for this caveat. --- embassy-rp/src/pio.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 5433d3f21..0616f6fc0 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -827,8 +827,10 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } } - // TODO make instruction memory that is currently in use unfreeable - pub fn free_instr(&mut self, instrs: PioInstanceMemory) { + /// Free instruction memory previously allocated with [`PioCommon::write_instr`]. + /// This is always possible but unsafe if any state machine is still using this + /// bit of memory. + pub unsafe fn free_instr(&mut self, instrs: PioInstanceMemory) { self.instructions_used &= !instrs.used_mask; } From 62841dd5b958fa2458aceb638a17d243e72d987f Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 12:52:29 +0200 Subject: [PATCH 1035/1575] rp/pio: revert pio pin funcsel to null on pio+sms drop once all sharing owners of pio pins have been dropped we should reset the pin for use by other hal objects. unfortunately this needs an atomic state per pio block because PioCommon and all of the state machines really do share ownership of any wrapped pins. only PioCommon can create them, but all state machines can keep them alive. since state machines can be moved to core1 we can't do reference counting in relaxed mode, but we *can* do relaxed pin accounting (since only common and the final drop can modify this). --- embassy-rp/src/pio.rs | 67 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 0616f6fc0..32e9be74d 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -4,9 +4,11 @@ use core::pin::Pin as FuturePin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; +use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use pac::io::vals::Gpio0ctrlFuncsel; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; @@ -320,6 +322,12 @@ pub struct PioStateMachineInstance<'d, PIO: PioInstance, const SM: usize> { pio: PhantomData<&'d PIO>, } +impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachineInstance<'d, PIO, SM> { + fn drop(&mut self) { + on_pio_drop::(); + } +} + impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { type Pio = PIO; const SM: usize = SM; @@ -794,6 +802,12 @@ pub struct PioCommon<'d, PIO: PioInstance> { pio: PhantomData<&'d PIO>, } +impl<'d, PIO: PioInstance> Drop for PioCommon<'d, PIO> { + fn drop(&mut self) { + on_pio_drop::(); + } +} + pub struct PioInstanceMemory<'d, PIO: PioInstance> { used_mask: u32, pio: PhantomData<&'d PIO>, @@ -870,10 +884,15 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { unsafe { PIO::PIO.input_sync_bypass().read() } } - pub fn make_pio_pin(&self, pin: impl Pin) -> PioPin { + /// Register a pin for PIO usage. Pins will be released from the PIO block + /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* + /// all [`PioStateMachine`]s for this block have been dropped. + pub fn make_pio_pin(&mut self, pin: impl Pin) -> PioPin { unsafe { pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); } + // we can be relaxed about this because we're &mut here and nothing is cached + PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); PioPin { pin_bank: pin.pin_bank(), pio: PhantomData::default(), @@ -891,6 +910,8 @@ pub struct Pio<'d, PIO: PioInstance> { impl<'d, PIO: PioInstance> Pio<'d, PIO> { pub fn new(_pio: impl Peripheral

+ 'd) -> Self { + PIO::state().users.store(5, Ordering::Release); + PIO::state().used_pins.store(0, Ordering::Release); Self { common: PioCommon { instructions_used: 0, @@ -904,13 +925,35 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { } } -pub trait PioInstance: sealed::PioInstance + Sized + Unpin { - fn pio(&self) -> u8 { - Self::PIO_NO +// we need to keep a record of which pins are assigned to each PIO. make_pio_pin +// notionally takes ownership of the pin it is given, but the wrapped pin cannot +// be treated as an owned resource since dropping it would have to deconfigure +// the pin, breaking running state machines in the process. pins are also shared +// between all state machines, which makes ownership even messier to track any +// other way. +pub struct State { + users: AtomicU8, + used_pins: AtomicU32, +} + +fn on_pio_drop() { + let state = PIO::state(); + if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { + let used_pins = state.used_pins.load(Ordering::Relaxed); + let null = Gpio0ctrlFuncsel::NULL.0; + for i in 0..32 { + if used_pins & (1 << i) != 0 { + unsafe { + pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); + } + } + } } } mod sealed { + use super::*; + pub trait PioStateMachine { type Pio: super::PioInstance; const SM: usize; @@ -925,6 +968,22 @@ mod sealed { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; + + #[inline] + fn state() -> &'static State { + static STATE: State = State { + users: AtomicU8::new(0), + used_pins: AtomicU32::new(0), + }; + + &STATE + } + } +} + +pub trait PioInstance: sealed::PioInstance + Sized + Unpin { + fn pio(&self) -> u8 { + Self::PIO_NO } } From 17e78175a69dc96ea8f71c41949cbc705d82b51a Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 11:21:58 +0200 Subject: [PATCH 1036/1575] rp/pio: disable state machines on drop --- embassy-rp/src/pio.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 32e9be74d..d05eef04b 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -324,6 +324,9 @@ pub struct PioStateMachineInstance<'d, PIO: PioInstance, const SM: usize> { impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachineInstance<'d, PIO, SM> { fn drop(&mut self) { + unsafe { + PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); + } on_pio_drop::(); } } From 4ccb2bc95aab6202d6f53882a59265427cdd5655 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 08:15:46 +0200 Subject: [PATCH 1037/1575] rp/pio: add PioPin trait pio can only access pins in bank 0, so it doesn't make sense to even allow wrapping of other banks' pins. --- embassy-rp/src/pio.rs | 41 ++++++++++++++++++++++-------- examples/rp/src/bin/pio_async.rs | 7 +++-- examples/rp/src/bin/pio_hd44780.rs | 17 ++++++------- examples/rp/src/bin/ws2812-pio.rs | 7 +++-- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index d05eef04b..21223eafc 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -12,7 +12,7 @@ use pac::io::vals::Gpio0ctrlFuncsel; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; -use crate::gpio::{Drive, Pin, Pull, SlewRate}; +use crate::gpio::{self, Drive, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; use crate::pio::sealed::PioInstance as _; use crate::{interrupt, pac, peripherals, RegExt}; @@ -244,12 +244,12 @@ impl<'d, PIO: PioInstance> Drop for IrqFuture { } } -pub struct PioPin { +pub struct Pin { pin_bank: u8, pio: PhantomData, } -impl PioPin { +impl Pin { /// Set the pin's drive strength. #[inline] pub fn set_drive_strength(&mut self, strength: Drive) { @@ -312,7 +312,7 @@ impl PioPin { } } -impl SealedPin for PioPin { +impl SealedPin for Pin { fn pin_bank(&self) -> u8 { self.pin_bank } @@ -610,7 +610,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { unsafe { Self::this_sm().pinctrl().read().sideset_count() } } - fn set_sideset_base_pin(&mut self, base_pin: &PioPin) { + fn set_sideset_base_pin(&mut self, base_pin: &Pin) { unsafe { Self::this_sm().pinctrl().modify(|w| w.set_sideset_base(base_pin.pin())); } @@ -642,7 +642,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } - fn set_in_base_pin(&mut self, base: &PioPin) { + fn set_in_base_pin(&mut self, base: &Pin) { unsafe { Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); } @@ -673,7 +673,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } - fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin]) { + fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { let count = pins.len(); assert!(count >= 1); let start = pins[0].pin() as usize; @@ -684,7 +684,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { self.set_out_range(start as u8, count as u8); } - fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin]) { + fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { let count = pins.len(); assert!(count >= 1); let start = pins[0].pin() as usize; @@ -890,13 +890,13 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { /// Register a pin for PIO usage. Pins will be released from the PIO block /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* /// all [`PioStateMachine`]s for this block have been dropped. - pub fn make_pio_pin(&mut self, pin: impl Pin) -> PioPin { + pub fn make_pio_pin(&mut self, pin: impl PioPin) -> Pin { unsafe { pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); } // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); - PioPin { + Pin { pin_bank: pin.pin_bank(), pio: PhantomData::default(), } @@ -967,6 +967,8 @@ mod sealed { } } + pub trait PioPin {} + pub trait PioInstance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; @@ -1003,3 +1005,22 @@ macro_rules! impl_pio { impl_pio!(PIO0, 0, PIO0, PIO0_0); impl_pio!(PIO1, 1, PIO1, PIO1_0); + +pub trait PioPin: sealed::PioPin + gpio::Pin {} + +macro_rules! impl_pio_pin { + ($( $num:tt )*) => { + $( + paste::paste!{ + impl sealed::PioPin for peripherals::[< PIN_ $num >] {} + impl PioPin for peripherals::[< PIN_ $num >] {} + } + )* + }; +} + +impl_pio_pin! { + 0 1 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 +} diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 5fea7034b..154cc6b65 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -3,14 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::info; use embassy_executor::Spawner; -use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection}; +use embassy_rp::pio::{Pio, PioCommon, PioPin, PioStateMachine, PioStateMachineInstance, ShiftDirection}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: impl PioPin) { // Setup sm0 // Send data serially to pin @@ -121,7 +120,7 @@ async fn main(spawner: Spawner) { .. } = Pio::new(pio); - setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0.degrade()); + setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0); setup_pio_task_sm1(&mut common, &mut sm1); setup_pio_task_sm2(&mut common, &mut sm2); spawner.spawn(pio_task_sm0(sm0)).unwrap(); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 59b4c1f52..6d56bc233 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -6,9 +6,8 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; -use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection}; +use embassy_rp::pio::{FifoJoin, Pio, PioPin, PioStateMachine, PioStateMachineInstance, ShiftDirection}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; @@ -74,13 +73,13 @@ impl<'l> HD44780<'l> { pub async fn new( pio: impl Peripheral

+ 'l, dma: impl Peripheral

+ 'l, - rs: impl Pin, - rw: impl Pin, - e: impl Pin, - db4: impl Pin, - db5: impl Pin, - db6: impl Pin, - db7: impl Pin, + rs: impl PioPin, + rw: impl PioPin, + e: impl PioPin, + db4: impl PioPin, + db5: impl PioPin, + db6: impl PioPin, + db7: impl PioPin, ) -> HD44780<'l> { into_ref!(dma); diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 0975559d7..8276fad64 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -4,9 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, + FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, PioStateMachineInstance, ShiftDirection, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; @@ -18,7 +17,7 @@ pub struct Ws2812<'d, P: PioInstance, const S: usize> { } impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { - pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachineInstance<'d, P, S>, pin: gpio::AnyPin) -> Self { + pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachineInstance<'d, P, S>, pin: impl PioPin) -> Self { // Setup sm0 // prepare the PIO program @@ -124,7 +123,7 @@ async fn main(_spawner: Spawner) { // For the thing plus, use pin 8 // For the feather, use pin 16 - let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8.degrade()); + let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8); // Loop forever making RGB values and pushing them out to the WS2812. loop { From 6ad58f428a42fd73377b6b8ec81315da06357049 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 08:46:08 +0200 Subject: [PATCH 1038/1575] rp/pio: wrap PioPins from ref, like everything else also store peripheral refs instead of a raw pin/bank number, like everything else. --- embassy-rp/src/pio.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 21223eafc..bb9bdb8ac 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -6,13 +6,13 @@ use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; -use embassy_hal_common::{Peripheral, PeripheralRef}; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::io::vals::Gpio0ctrlFuncsel; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; -use crate::gpio::{self, Drive, Pull, SlewRate}; +use crate::gpio::{self, AnyPin, Drive, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; use crate::pio::sealed::PioInstance as _; use crate::{interrupt, pac, peripherals, RegExt}; @@ -244,17 +244,17 @@ impl<'d, PIO: PioInstance> Drop for IrqFuture { } } -pub struct Pin { - pin_bank: u8, +pub struct Pin<'l, PIO: PioInstance> { + pin: PeripheralRef<'l, AnyPin>, pio: PhantomData, } -impl Pin { +impl<'l, PIO: PioInstance> Pin<'l, PIO> { /// Set the pin's drive strength. #[inline] pub fn set_drive_strength(&mut self, strength: Drive) { unsafe { - self.pad_ctrl().modify(|w| { + self.pin.pad_ctrl().modify(|w| { w.set_drive(match strength { Drive::_2mA => pac::pads::vals::Drive::_2MA, Drive::_4mA => pac::pads::vals::Drive::_4MA, @@ -269,7 +269,7 @@ impl Pin { #[inline] pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { unsafe { - self.pad_ctrl().modify(|w| { + self.pin.pad_ctrl().modify(|w| { w.set_slewfast(slew_rate == SlewRate::Fast); }); } @@ -279,7 +279,7 @@ impl Pin { #[inline] pub fn set_pull(&mut self, pull: Pull) { unsafe { - self.pad_ctrl().modify(|w| { + self.pin.pad_ctrl().modify(|w| { w.set_pue(pull == Pull::Up); w.set_pde(pull == Pull::Down); }); @@ -290,7 +290,7 @@ impl Pin { #[inline] pub fn set_schmitt(&mut self, enable: bool) { unsafe { - self.pad_ctrl().modify(|w| { + self.pin.pad_ctrl().modify(|w| { w.set_schmitt(enable); }); } @@ -308,13 +308,7 @@ impl Pin { } pub fn pin(&self) -> u8 { - self._pin() - } -} - -impl SealedPin for Pin { - fn pin_bank(&self) -> u8 { - self.pin_bank + self.pin._pin() } } @@ -890,14 +884,15 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { /// Register a pin for PIO usage. Pins will be released from the PIO block /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* /// all [`PioStateMachine`]s for this block have been dropped. - pub fn make_pio_pin(&mut self, pin: impl PioPin) -> Pin { + pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { + into_ref!(pin); unsafe { pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); } // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); Pin { - pin_bank: pin.pin_bank(), + pin: pin.into_ref().map_into(), pio: PhantomData::default(), } } From 79985f003646d379f2aea2738dc0291770e701c5 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 09:11:19 +0200 Subject: [PATCH 1039/1575] rp/pio: hide pio/sm numbers nothing should care which number pio it is running on, and the state machine index could always be extracted from type information. --- embassy-rp/src/pio.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index bb9bdb8ac..ffd8d53dd 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -332,14 +332,6 @@ impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachine for PioState impl<'d, PIO: PioInstance, const SM: usize> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { - fn pio_no(&self) -> u8 { - Self::Pio::PIO_NO - } - - fn sm_no(&self) -> u8 { - Self::SM as u8 - } - fn restart(&mut self) { let mask = 1u8 << Self::SM; unsafe { @@ -981,11 +973,7 @@ mod sealed { } } -pub trait PioInstance: sealed::PioInstance + Sized + Unpin { - fn pio(&self) -> u8 { - Self::PIO_NO - } -} +pub trait PioInstance: sealed::PioInstance + Sized + Unpin {} macro_rules! impl_pio { ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { From 906d2b2db78d287ad818ab2a7abb1fec572ac6f6 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 09:42:42 +0200 Subject: [PATCH 1040/1575] rp/pio: PioStateMachine{Instance, => ,Instance} next step: get rid of the insance trait entirely --- embassy-rp/src/pio.rs | 44 +++++++++++++++--------------- embassy-rp/src/pio_instr_util.rs | 20 +++++++------- examples/rp/src/bin/pio_async.rs | 12 ++++---- examples/rp/src/bin/pio_dma.rs | 2 +- examples/rp/src/bin/pio_hd44780.rs | 2 +- examples/rp/src/bin/ws2812-pio.rs | 4 +-- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index ffd8d53dd..a2e3d3a91 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -97,13 +97,13 @@ pub(crate) unsafe fn init() { /// Future that waits for TX-FIFO to become writable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { +pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> { sm: &'a mut SM, pio: PhantomData, value: u32, } -impl<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> FifoOutFuture<'a, PIO, SM> { +impl<'a, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> FifoOutFuture<'a, PIO, SM> { pub fn new(sm: &'a mut SM, value: u32) -> Self { FifoOutFuture { sm, @@ -113,7 +113,7 @@ impl<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> FifoOutFuture<'a, PIO, S } } -impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture<'d, PIO, SM> { +impl<'d, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> Future for FifoOutFuture<'d, PIO, SM> { type Output = (); fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -133,7 +133,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture } } -impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { +impl<'d, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -145,12 +145,12 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<' /// Future that waits for RX-FIFO to become readable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachine> { +pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachineInstance> { sm: &'a mut SM, pio: PhantomData, } -impl<'a, PIO: PioInstance, SM: PioStateMachine> FifoInFuture<'a, PIO, SM> { +impl<'a, PIO: PioInstance, SM: PioStateMachineInstance> FifoInFuture<'a, PIO, SM> { pub fn new(sm: &'a mut SM) -> Self { FifoInFuture { sm, @@ -159,7 +159,7 @@ impl<'a, PIO: PioInstance, SM: PioStateMachine> FifoInFuture<'a, PIO, SM> { } } -impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, SM> { +impl<'d, PIO: PioInstance, SM: PioStateMachineInstance> Future for FifoInFuture<'d, PIO, SM> { type Output = u32; fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -178,7 +178,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, } } -impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, SM> { +impl<'d, PIO: PioInstance, SM: PioStateMachineInstance> Drop for FifoInFuture<'d, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -312,11 +312,11 @@ impl<'l, PIO: PioInstance> Pin<'l, PIO> { } } -pub struct PioStateMachineInstance<'d, PIO: PioInstance, const SM: usize> { +pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachineInstance<'d, PIO, SM> { +impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); @@ -325,13 +325,13 @@ impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachineInstance<'d, } } -impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { +impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachineInstance for PioStateMachine<'d, PIO, SM> { type Pio = PIO; const SM: usize = SM; } -impl<'d, PIO: PioInstance, const SM: usize> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} +impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineInstance for PioStateMachine<'d, PIO, SM> {} -pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { +pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unpin { fn restart(&mut self) { let mask = 1u8 << Self::SM; unsafe { @@ -892,10 +892,10 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { pub struct Pio<'d, PIO: PioInstance> { pub common: PioCommon<'d, PIO>, - pub sm0: PioStateMachineInstance<'d, PIO, 0>, - pub sm1: PioStateMachineInstance<'d, PIO, 1>, - pub sm2: PioStateMachineInstance<'d, PIO, 2>, - pub sm3: PioStateMachineInstance<'d, PIO, 3>, + pub sm0: PioStateMachine<'d, PIO, 0>, + pub sm1: PioStateMachine<'d, PIO, 1>, + pub sm2: PioStateMachine<'d, PIO, 2>, + pub sm3: PioStateMachine<'d, PIO, 3>, } impl<'d, PIO: PioInstance> Pio<'d, PIO> { @@ -907,10 +907,10 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { instructions_used: 0, pio: PhantomData, }, - sm0: PioStateMachineInstance { pio: PhantomData }, - sm1: PioStateMachineInstance { pio: PhantomData }, - sm2: PioStateMachineInstance { pio: PhantomData }, - sm3: PioStateMachineInstance { pio: PhantomData }, + sm0: PioStateMachine { pio: PhantomData }, + sm1: PioStateMachine { pio: PhantomData }, + sm2: PioStateMachine { pio: PhantomData }, + sm3: PioStateMachine { pio: PhantomData }, } } } @@ -944,7 +944,7 @@ fn on_pio_drop() { mod sealed { use super::*; - pub trait PioStateMachine { + pub trait PioStateMachineInstance { type Pio: super::PioInstance; const SM: usize; diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index ae26ff1dc..f5e7d7abd 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs @@ -1,8 +1,8 @@ use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; -use crate::pio::PioStateMachine; +use crate::pio::PioStateMachineInstance; -pub fn set_x(sm: &mut SM, value: u32) { +pub fn set_x(sm: &mut SM, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::X, bit_count: 32, @@ -12,7 +12,7 @@ pub fn set_x(sm: &mut SM, value: u32) { sm.exec_instr(OUT); } -pub fn get_x(sm: &mut SM) -> u32 { +pub fn get_x(sm: &mut SM) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::X, bit_count: 32, @@ -22,7 +22,7 @@ pub fn get_x(sm: &mut SM) -> u32 { sm.pull_rx() } -pub fn set_y(sm: &mut SM, value: u32) { +pub fn set_y(sm: &mut SM, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::Y, bit_count: 32, @@ -32,7 +32,7 @@ pub fn set_y(sm: &mut SM, value: u32) { sm.exec_instr(OUT); } -pub fn get_y(sm: &mut SM) -> u32 { +pub fn get_y(sm: &mut SM) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::Y, bit_count: 32, @@ -43,7 +43,7 @@ pub fn get_y(sm: &mut SM) -> u32 { sm.pull_rx() } -pub fn set_pindir(sm: &mut SM, data: u8) { +pub fn set_pindir(sm: &mut SM, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINDIRS, data, @@ -52,7 +52,7 @@ pub fn set_pindir(sm: &mut SM, data: u8) { sm.exec_instr(set); } -pub fn set_pin(sm: &mut SM, data: u8) { +pub fn set_pin(sm: &mut SM, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINS, data, @@ -61,7 +61,7 @@ pub fn set_pin(sm: &mut SM, data: u8) { sm.exec_instr(set); } -pub fn set_out_pin(sm: &mut SM, data: u32) { +pub fn set_out_pin(sm: &mut SM, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINS, bit_count: 32, @@ -70,7 +70,7 @@ pub fn set_out_pin(sm: &mut SM, data: u32) { sm.push_tx(data); sm.exec_instr(OUT); } -pub fn set_out_pindir(sm: &mut SM, data: u32) { +pub fn set_out_pindir(sm: &mut SM, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINDIRS, bit_count: 32, @@ -80,7 +80,7 @@ pub fn set_out_pindir(sm: &mut SM, data: u32) { sm.exec_instr(OUT); } -pub fn exec_jmp(sm: &mut SM, to_addr: u8) { +pub fn exec_jmp(sm: &mut SM, to_addr: u8) { let jmp: u16 = InstructionOperands::JMP { address: to_addr, condition: JmpCondition::Always, diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 154cc6b65..8c02f9f16 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -9,7 +9,7 @@ use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: impl PioPin) { +fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachine, pin: impl PioPin) { // Setup sm0 // Send data serially to pin @@ -37,7 +37,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, 0>) { +async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -48,7 +48,7 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, 0>) { } } -fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachine) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -67,7 +67,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, 1>) { +async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -75,7 +75,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, 1>) { } } -fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachine) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -99,7 +99,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance<'static, PIO0, 2>) { +async fn pio_task_sm2(mut sm: PioStateMachine<'static, PIO0, 2>) { sm.set_enable(true); loop { sm.wait_irq(3).await; diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 0f1f6df12..a351e2c7c 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Pio, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Pio, PioStateMachineInstance, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 6d56bc233..249711a32 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -64,7 +64,7 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachineInstance<'l, PIO0, 0>, + sm: PioStateMachine<'l, PIO0, 0>, buf: [u8; 40], } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 8276fad64..c141560e5 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -13,11 +13,11 @@ use embassy_time::{Duration, Timer}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; pub struct Ws2812<'d, P: PioInstance, const S: usize> { - sm: PioStateMachineInstance<'d, P, S>, + sm: PioStateMachine<'d, P, S>, } impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { - pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachineInstance<'d, P, S>, pin: impl PioPin) -> Self { + pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachine<'d, P, S>, pin: impl PioPin) -> Self { // Setup sm0 // prepare the PIO program From 486fe9e59da7474c5162f56d89d5b6c279d02753 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 10:18:24 +0200 Subject: [PATCH 1041/1575] rp/pio: remove PioStateMachineInstance move all methods into PioStateMachine instead. the huge trait wasn't object-safe and thus didn't have any benefits whatsoever except for making it *slightly* easier to write bounds for passing around state machines. that would be much better solved with generics-less instances. --- embassy-rp/src/pio.rs | 271 +++++++++++++---------------- embassy-rp/src/pio_instr_util.rs | 20 +-- examples/rp/src/bin/pio_async.rs | 2 +- examples/rp/src/bin/pio_dma.rs | 2 +- examples/rp/src/bin/pio_hd44780.rs | 2 +- examples/rp/src/bin/ws2812-pio.rs | 4 +- 6 files changed, 138 insertions(+), 163 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index a2e3d3a91..52ef89a16 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -14,7 +14,6 @@ use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{self, AnyPin, Drive, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; -use crate::pio::sealed::PioInstance as _; use crate::{interrupt, pac, peripherals, RegExt}; struct Wakers([AtomicWaker; 12]); @@ -97,23 +96,18 @@ pub(crate) unsafe fn init() { /// Future that waits for TX-FIFO to become writable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> { - sm: &'a mut SM, - pio: PhantomData, +pub struct FifoOutFuture<'a, 'd, PIO: PioInstance, const SM: usize> { + sm: &'a mut PioStateMachine<'d, PIO, SM>, value: u32, } -impl<'a, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> FifoOutFuture<'a, PIO, SM> { - pub fn new(sm: &'a mut SM, value: u32) -> Self { - FifoOutFuture { - sm, - pio: PhantomData::default(), - value, - } +impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { + pub fn new(sm: &'a mut PioStateMachine<'d, PIO, SM>, value: u32) -> Self { + FifoOutFuture { sm, value } } } -impl<'d, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> Future for FifoOutFuture<'d, PIO, SM> { +impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> { type Output = (); fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -121,10 +115,10 @@ impl<'d, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> Future for FifoO if self.get_mut().sm.try_push_tx(value) { Poll::Ready(()) } else { - WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::SM as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker()); unsafe { PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = TXNFULL_MASK << SM::SM; + m.0 = TXNFULL_MASK << SM; }); } // debug!("Pending"); @@ -133,11 +127,11 @@ impl<'d, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> Future for FifoO } } -impl<'d, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { +impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = TXNFULL_MASK << SM::SM; + m.0 = TXNFULL_MASK << SM; }); } } @@ -145,31 +139,27 @@ impl<'d, PIO: PioInstance, SM: PioStateMachineInstance + Unpin> Drop for FifoOut /// Future that waits for RX-FIFO to become readable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachineInstance> { - sm: &'a mut SM, - pio: PhantomData, +pub struct FifoInFuture<'a, 'd, PIO: PioInstance, const SM: usize> { + sm: &'a mut PioStateMachine<'d, PIO, SM>, } -impl<'a, PIO: PioInstance, SM: PioStateMachineInstance> FifoInFuture<'a, PIO, SM> { - pub fn new(sm: &'a mut SM) -> Self { - FifoInFuture { - sm, - pio: PhantomData::default(), - } +impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { + pub fn new(sm: &'a mut PioStateMachine<'d, PIO, SM>) -> Self { + FifoInFuture { sm } } } -impl<'d, PIO: PioInstance, SM: PioStateMachineInstance> Future for FifoInFuture<'d, PIO, SM> { +impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> { type Output = u32; fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); if let Some(v) = self.sm.try_pull_rx() { Poll::Ready(v) } else { - WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::SM].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker()); unsafe { PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = RXNEMPTY_MASK << SM::SM; + m.0 = RXNEMPTY_MASK << SM; }); } //debug!("Pending"); @@ -178,11 +168,11 @@ impl<'d, PIO: PioInstance, SM: PioStateMachineInstance> Future for FifoInFuture< } } -impl<'d, PIO: PioInstance, SM: PioStateMachineInstance> Drop for FifoInFuture<'d, PIO, SM> { +impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = RXNEMPTY_MASK << SM::SM; + m.0 = RXNEMPTY_MASK << SM; }); } } @@ -325,69 +315,68 @@ impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM } } -impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachineInstance for PioStateMachine<'d, PIO, SM> { - type Pio = PIO; - const SM: usize = SM; -} -impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineInstance for PioStateMachine<'d, PIO, SM> {} +impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { + #[inline(always)] + fn this_sm() -> crate::pac::pio::StateMachine { + PIO::PIO.sm(SM) + } -pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unpin { - fn restart(&mut self) { - let mask = 1u8 << Self::SM; + pub fn restart(&mut self) { + let mask = 1u8 << SM; unsafe { - Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); + PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); } } - fn set_enable(&mut self, enable: bool) { - let mask = 1u8 << Self::SM; + pub fn set_enable(&mut self, enable: bool) { + let mask = 1u8 << SM; unsafe { if enable { - Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); + PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); } else { - Self::Pio::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); + PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); } } } - fn is_enabled(&self) -> bool { - unsafe { Self::Pio::PIO.ctrl().read().sm_enable() & (1u8 << Self::SM) != 0 } + pub fn is_enabled(&self) -> bool { + unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } } - fn is_tx_empty(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().txempty() & (1u8 << Self::SM) != 0 } + pub fn is_tx_empty(&self) -> bool { + unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } } - fn is_tx_full(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().txfull() & (1u8 << Self::SM) != 0 } + pub fn is_tx_full(&self) -> bool { + unsafe { PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 } } - fn is_rx_empty(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().rxempty() & (1u8 << Self::SM) != 0 } + pub fn is_rx_empty(&self) -> bool { + unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } } - fn is_rx_full(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().rxfull() & (1u8 << Self::SM) != 0 } + pub fn is_rx_full(&self) -> bool { + unsafe { PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 } } - fn tx_level(&self) -> u8 { + pub fn tx_level(&self) -> u8 { unsafe { - let flevel = Self::Pio::PIO.flevel().read().0; - (flevel >> (Self::SM * 8)) as u8 & 0x0f + let flevel = PIO::PIO.flevel().read().0; + (flevel >> (SM * 8)) as u8 & 0x0f } } - fn rx_level(&self) -> u8 { + pub fn rx_level(&self) -> u8 { unsafe { - let flevel = Self::Pio::PIO.flevel().read().0; - (flevel >> (Self::SM * 8 + 4)) as u8 & 0x0f + let flevel = PIO::PIO.flevel().read().0; + (flevel >> (SM * 8 + 4)) as u8 & 0x0f } } - fn push_tx(&mut self, v: u32) { + pub fn push_tx(&mut self, v: u32) { unsafe { - Self::Pio::PIO.txf(Self::SM).write_value(v); + PIO::PIO.txf(SM).write_value(v); } } - fn try_push_tx(&mut self, v: u32) -> bool { + pub fn try_push_tx(&mut self, v: u32) -> bool { if self.is_tx_full() { return false; } @@ -395,65 +384,65 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp true } - fn pull_rx(&mut self) -> u32 { - unsafe { Self::Pio::PIO.rxf(Self::SM).read() } + pub fn pull_rx(&mut self) -> u32 { + unsafe { PIO::PIO.rxf(SM).read() } } - fn try_pull_rx(&mut self) -> Option { + pub fn try_pull_rx(&mut self) -> Option { if self.is_rx_empty() { return None; } Some(self.pull_rx()) } - fn set_clkdiv(&mut self, div_x_256: u32) { + pub fn set_clkdiv(&mut self, div_x_256: u32) { unsafe { Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); } } - fn get_clkdiv(&self) -> u32 { + pub fn get_clkdiv(&self) -> u32 { unsafe { Self::this_sm().clkdiv().read().0 >> 8 } } - fn clkdiv_restart(&mut self) { - let mask = 1u8 << Self::SM; + pub fn clkdiv_restart(&mut self) { + let mask = 1u8 << SM; unsafe { - Self::Pio::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); + PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); } } - fn set_side_enable(&self, enable: bool) { + pub fn set_side_enable(&self, enable: bool) { unsafe { Self::this_sm().execctrl().modify(|w| w.set_side_en(enable)); } } - fn is_side_enabled(&self) -> bool { + pub fn is_side_enabled(&self) -> bool { unsafe { Self::this_sm().execctrl().read().side_en() } } - fn set_side_pindir(&mut self, pindir: bool) { + pub fn set_side_pindir(&mut self, pindir: bool) { unsafe { Self::this_sm().execctrl().modify(|w| w.set_side_pindir(pindir)); } } - fn is_side_pindir(&self) -> bool { + pub fn is_side_pindir(&self) -> bool { unsafe { Self::this_sm().execctrl().read().side_pindir() } } - fn set_jmp_pin(&mut self, pin: u8) { + pub fn set_jmp_pin(&mut self, pin: u8) { unsafe { Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); } } - fn get_jmp_pin(&mut self) -> u8 { + pub fn get_jmp_pin(&mut self) -> u8 { unsafe { Self::this_sm().execctrl().read().jmp_pin() } } - fn set_wrap(&self, source: u8, target: u8) { + pub fn set_wrap(&self, source: u8, target: u8) { unsafe { Self::this_sm().execctrl().modify(|w| { w.set_wrap_top(source); @@ -463,14 +452,14 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } /// Get wrapping addresses. Returns (source, target). - fn get_wrap(&self) -> (u8, u8) { + pub fn get_wrap(&self) -> (u8, u8) { unsafe { let r = Self::this_sm().execctrl().read(); (r.wrap_top(), r.wrap_bottom()) } } - fn set_fifo_join(&mut self, join: FifoJoin) { + pub fn set_fifo_join(&mut self, join: FifoJoin) { let (rx, tx) = match join { FifoJoin::Duplex => (false, false), FifoJoin::RxOnly => (true, false), @@ -483,7 +472,7 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp }); } } - fn get_fifo_join(&self) -> FifoJoin { + pub fn get_fifo_join(&self) -> FifoJoin { unsafe { let r = Self::this_sm().shiftctrl().read(); // Ignores the invalid state when both bits are set @@ -497,7 +486,7 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } } - fn clear_fifos(&mut self) { + pub fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs unsafe { let shiftctrl = Self::this_sm().shiftctrl(); @@ -510,33 +499,33 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } } - fn set_pull_threshold(&mut self, threshold: u8) { + pub fn set_pull_threshold(&mut self, threshold: u8) { unsafe { Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold)); } } - fn get_pull_threshold(&self) -> u8 { + pub fn get_pull_threshold(&self) -> u8 { unsafe { Self::this_sm().shiftctrl().read().pull_thresh() } } - fn set_push_threshold(&mut self, threshold: u8) { + pub fn set_push_threshold(&mut self, threshold: u8) { unsafe { Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold)); } } - fn get_push_threshold(&self) -> u8 { + pub fn get_push_threshold(&self) -> u8 { unsafe { Self::this_sm().shiftctrl().read().push_thresh() } } - fn set_out_shift_dir(&mut self, dir: ShiftDirection) { + pub fn set_out_shift_dir(&mut self, dir: ShiftDirection) { unsafe { Self::this_sm() .shiftctrl() .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); } } - fn get_out_shiftdir(&self) -> ShiftDirection { + pub fn get_out_shiftdir(&self) -> ShiftDirection { unsafe { if Self::this_sm().shiftctrl().read().out_shiftdir() { ShiftDirection::Right @@ -546,14 +535,14 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } } - fn set_in_shift_dir(&mut self, dir: ShiftDirection) { + pub fn set_in_shift_dir(&mut self, dir: ShiftDirection) { unsafe { Self::this_sm() .shiftctrl() .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); } } - fn get_in_shiftdir(&self) -> ShiftDirection { + pub fn get_in_shiftdir(&self) -> ShiftDirection { unsafe { if Self::this_sm().shiftctrl().read().in_shiftdir() { ShiftDirection::Right @@ -563,46 +552,46 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } } - fn set_autopull(&mut self, auto: bool) { + pub fn set_autopull(&mut self, auto: bool) { unsafe { Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto)); } } - fn is_autopull(&self) -> bool { + pub fn is_autopull(&self) -> bool { unsafe { Self::this_sm().shiftctrl().read().autopull() } } - fn set_autopush(&mut self, auto: bool) { + pub fn set_autopush(&mut self, auto: bool) { unsafe { Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto)); } } - fn is_autopush(&self) -> bool { + pub fn is_autopush(&self) -> bool { unsafe { Self::this_sm().shiftctrl().read().autopush() } } - fn get_addr(&self) -> u8 { + pub fn get_addr(&self) -> u8 { unsafe { Self::this_sm().addr().read().addr() } } - fn set_sideset_count(&mut self, count: u8) { + pub fn set_sideset_count(&mut self, count: u8) { unsafe { Self::this_sm().pinctrl().modify(|w| w.set_sideset_count(count)); } } - fn get_sideset_count(&self) -> u8 { + pub fn get_sideset_count(&self) -> u8 { unsafe { Self::this_sm().pinctrl().read().sideset_count() } } - fn set_sideset_base_pin(&mut self, base_pin: &Pin) { + pub fn set_sideset_base_pin(&mut self, base_pin: &Pin) { unsafe { Self::this_sm().pinctrl().modify(|w| w.set_sideset_base(base_pin.pin())); } } - fn get_sideset_base(&self) -> u8 { + pub fn get_sideset_base(&self) -> u8 { unsafe { let r = Self::this_sm().pinctrl().read(); r.sideset_base() @@ -610,7 +599,7 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } /// Set the range of out pins affected by a set instruction. - fn set_set_range(&mut self, base: u8, count: u8) { + pub fn set_set_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { Self::this_sm().pinctrl().modify(|w| { @@ -621,27 +610,27 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } /// Get the range of out pins affected by a set instruction. Returns (base, count). - fn get_set_range(&self) -> (u8, u8) { + pub fn get_set_range(&self) -> (u8, u8) { unsafe { let r = Self::this_sm().pinctrl().read(); (r.set_base(), r.set_count()) } } - fn set_in_base_pin(&mut self, base: &Pin) { + pub fn set_in_base_pin(&mut self, base: &Pin) { unsafe { Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); } } - fn get_in_base(&self) -> u8 { + pub fn get_in_base(&self) -> u8 { unsafe { let r = Self::this_sm().pinctrl().read(); r.in_base() } } - fn set_out_range(&mut self, base: u8, count: u8) { + pub fn set_out_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { Self::this_sm().pinctrl().modify(|w| { @@ -652,14 +641,14 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp } /// Get the range of out pins affected by a set instruction. Returns (base, count). - fn get_out_range(&self) -> (u8, u8) { + pub fn get_out_range(&self) -> (u8, u8) { unsafe { let r = Self::this_sm().pinctrl().read(); (r.out_base(), r.out_count()) } } - fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { + pub fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { let count = pins.len(); assert!(count >= 1); let start = pins[0].pin() as usize; @@ -670,7 +659,7 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp self.set_out_range(start as u8, count as u8); } - fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { + pub fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { let count = pins.len(); assert!(count >= 1); let start = pins[0].pin() as usize; @@ -681,76 +670,75 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp self.set_set_range(start as u8, count as u8); } - fn get_current_instr() -> u32 { + pub fn get_current_instr() -> u32 { unsafe { Self::this_sm().instr().read().0 } } - fn exec_instr(&mut self, instr: u16) { + pub fn exec_instr(&mut self, instr: u16) { unsafe { Self::this_sm().instr().write(|w| w.set_instr(instr)); } } - fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, Self::Pio, Self> { + pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> { FifoOutFuture::new(self, value) } - fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, Self::Pio, Self> { + pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> { FifoInFuture::new(self) } - fn wait_irq(&self, irq_no: u8) -> IrqFuture { + pub fn wait_irq(&self, irq_no: u8) -> IrqFuture { IrqFuture::new(irq_no) } - fn has_tx_stalled(&self) -> bool { + pub fn has_tx_stalled(&self) -> bool { unsafe { - let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().txstall() & (1 << Self::SM) != 0; - fdebug.write(|w| w.set_txstall(1 << Self::SM)); + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().txstall() & (1 << SM) != 0; + fdebug.write(|w| w.set_txstall(1 << SM)); ret } } - fn has_tx_overflowed(&self) -> bool { + pub fn has_tx_overflowed(&self) -> bool { unsafe { - let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().txover() & (1 << Self::SM) != 0; - fdebug.write(|w| w.set_txover(1 << Self::SM)); + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().txover() & (1 << SM) != 0; + fdebug.write(|w| w.set_txover(1 << SM)); ret } } - fn has_rx_stalled(&self) -> bool { + pub fn has_rx_stalled(&self) -> bool { unsafe { - let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().rxstall() & (1 << Self::SM) != 0; - fdebug.write(|w| w.set_rxstall(1 << Self::SM)); + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().rxstall() & (1 << SM) != 0; + fdebug.write(|w| w.set_rxstall(1 << SM)); ret } } - fn has_rx_underflowed(&self) -> bool { + pub fn has_rx_underflowed(&self) -> bool { unsafe { - let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().rxunder() & (1 << Self::SM) != 0; - fdebug.write(|w| w.set_rxunder(1 << Self::SM)); + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().rxunder() & (1 << SM) != 0; + fdebug.write(|w| w.set_rxunder(1 << SM)); ret } } - fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { + pub fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { unsafe { - let pio_no = Self::Pio::PIO_NO; - let sm_no = Self::SM; + let pio_no = PIO::PIO_NO; let p = ch.regs(); p.read_addr().write_value(data.as_ptr() as u32); - p.write_addr().write_value(Self::Pio::PIO.txf(sm_no).ptr() as u32); + p.write_addr().write_value(PIO::PIO.txf(SM).ptr() as u32); p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + sm_no as u8)); + w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8)); w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(true); @@ -762,18 +750,17 @@ pub trait PioStateMachineInstance: sealed::PioStateMachineInstance + Sized + Unp Transfer::new(ch) } - fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> { + pub fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> { unsafe { - let pio_no = Self::Pio::PIO_NO; - let sm_no = Self::SM; + let pio_no = PIO::PIO_NO; let p = ch.regs(); p.write_addr().write_value(data.as_ptr() as u32); - p.read_addr().write_value(Self::Pio::PIO.rxf(sm_no).ptr() as u32); + p.read_addr().write_value(PIO::PIO.rxf(SM).ptr() as u32); p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set RX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + sm_no as u8 + 4)); + w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4)); w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(false); @@ -944,16 +931,6 @@ fn on_pio_drop() { mod sealed { use super::*; - pub trait PioStateMachineInstance { - type Pio: super::PioInstance; - const SM: usize; - - #[inline(always)] - fn this_sm() -> crate::pac::pio::StateMachine { - Self::Pio::PIO.sm(Self::SM as usize) - } - } - pub trait PioPin {} pub trait PioInstance { diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index f5e7d7abd..ffe160246 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs @@ -1,8 +1,8 @@ use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; -use crate::pio::PioStateMachineInstance; +use crate::pio::{PioInstance, PioStateMachine}; -pub fn set_x(sm: &mut SM, value: u32) { +pub fn set_x(sm: &mut PioStateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::X, bit_count: 32, @@ -12,7 +12,7 @@ pub fn set_x(sm: &mut SM, value: u32) { sm.exec_instr(OUT); } -pub fn get_x(sm: &mut SM) -> u32 { +pub fn get_x(sm: &mut PioStateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::X, bit_count: 32, @@ -22,7 +22,7 @@ pub fn get_x(sm: &mut SM) -> u32 { sm.pull_rx() } -pub fn set_y(sm: &mut SM, value: u32) { +pub fn set_y(sm: &mut PioStateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::Y, bit_count: 32, @@ -32,7 +32,7 @@ pub fn set_y(sm: &mut SM, value: u32) { sm.exec_instr(OUT); } -pub fn get_y(sm: &mut SM) -> u32 { +pub fn get_y(sm: &mut PioStateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::Y, bit_count: 32, @@ -43,7 +43,7 @@ pub fn get_y(sm: &mut SM) -> u32 { sm.pull_rx() } -pub fn set_pindir(sm: &mut SM, data: u8) { +pub fn set_pindir(sm: &mut PioStateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINDIRS, data, @@ -52,7 +52,7 @@ pub fn set_pindir(sm: &mut SM, data: u8) { sm.exec_instr(set); } -pub fn set_pin(sm: &mut SM, data: u8) { +pub fn set_pin(sm: &mut PioStateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINS, data, @@ -61,7 +61,7 @@ pub fn set_pin(sm: &mut SM, data: u8) { sm.exec_instr(set); } -pub fn set_out_pin(sm: &mut SM, data: u32) { +pub fn set_out_pin(sm: &mut PioStateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINS, bit_count: 32, @@ -70,7 +70,7 @@ pub fn set_out_pin(sm: &mut SM, data: u32) { sm.push_tx(data); sm.exec_instr(OUT); } -pub fn set_out_pindir(sm: &mut SM, data: u32) { +pub fn set_out_pindir(sm: &mut PioStateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINDIRS, bit_count: 32, @@ -80,7 +80,7 @@ pub fn set_out_pindir(sm: &mut SM, data: u32) { sm.exec_instr(OUT); } -pub fn exec_jmp(sm: &mut SM, to_addr: u8) { +pub fn exec_jmp(sm: &mut PioStateMachine, to_addr: u8) { let jmp: u16 = InstructionOperands::JMP { address: to_addr, condition: JmpCondition::Always, diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 8c02f9f16..11b290869 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Pio, PioCommon, PioPin, PioStateMachine, PioStateMachineInstance, ShiftDirection}; +use embassy_rp::pio::{Pio, PioCommon, PioPin, PioStateMachine, ShiftDirection}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index a351e2c7c..a2a2ee39a 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Pio, PioStateMachineInstance, ShiftDirection}; +use embassy_rp::pio::{Pio, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 249711a32..bc51d43c4 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -7,7 +7,7 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioPin, PioStateMachine, PioStateMachineInstance, ShiftDirection}; +use embassy_rp::pio::{FifoJoin, Pio, PioPin, PioStateMachine, ShiftDirection}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index c141560e5..713e01b44 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -4,9 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, PioStateMachineInstance, ShiftDirection, -}; +use embassy_rp::pio::{FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, ShiftDirection}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; From 0997021a0585271ef6677659764e7c2bed84faea Mon Sep 17 00:00:00 2001 From: goueslati Date: Wed, 3 May 2023 11:11:51 +0100 Subject: [PATCH 1042/1575] fixed ble table cmd buffer being constant --- embassy-stm32/src/tl_mbox/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index 623546dc9..54db78bd8 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -113,7 +113,7 @@ pub struct DeviceInfoTable { #[repr(C, packed)] struct BleTable { - pcmd_buffer: *const CmdPacket, + pcmd_buffer: *mut CmdPacket, pcs_buffer: *const u8, pevt_queue: *const u8, phci_acl_data_buffer: *mut AclDataPacket, From 909a5fe2e513ef91129a29ccdd8772824879383c Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 27 Apr 2023 02:12:49 +0200 Subject: [PATCH 1043/1575] rp/pio: split irqs from state machines we can only have one active waiter for any given irq at any given time. allowing waits for irqs on state machines bypasses this limitation and causes lost events for all but the latest waiter for a given irq. splitting this out also allows us to signal from state machines to other parts of the application without monopolizing state machine access for the irq wait, as would be necessary to make irq waiting sound. --- embassy-rp/src/pio.rs | 45 ++++++++++++++++++------------ examples/rp/src/bin/pio_async.rs | 9 +++--- examples/rp/src/bin/pio_hd44780.rs | 7 +++-- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 52ef89a16..769641ac9 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -180,21 +180,12 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PI /// Future that waits for IRQ #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct IrqFuture { - pio: PhantomData, +pub struct IrqFuture<'a, 'd, PIO: PioInstance> { + pio: PhantomData<&'a PioIrq<'d, PIO, 0>>, irq_no: u8, } -impl<'a, PIO: PioInstance> IrqFuture { - pub fn new(irq_no: u8) -> Self { - IrqFuture { - pio: PhantomData::default(), - irq_no, - } - } -} - -impl<'d, PIO: PioInstance> Future for IrqFuture { +impl<'a, 'd, PIO: PioInstance> Future for IrqFuture<'a, 'd, PIO> { type Output = (); fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -224,7 +215,7 @@ impl<'d, PIO: PioInstance> Future for IrqFuture { } } -impl<'d, PIO: PioInstance> Drop for IrqFuture { +impl<'a, 'd, PIO: PioInstance> Drop for IrqFuture<'a, 'd, PIO> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -688,10 +679,6 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { FifoInFuture::new(self) } - pub fn wait_irq(&self, irq_no: u8) -> IrqFuture { - IrqFuture::new(irq_no) - } - pub fn has_tx_stalled(&self) -> bool { unsafe { let fdebug = PIO::PIO.fdebug(); @@ -862,7 +849,8 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { /// Register a pin for PIO usage. Pins will be released from the PIO block /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* - /// all [`PioStateMachine`]s for this block have been dropped. + /// all [`PioStateMachine`]s for this block have been dropped. **Other members + /// of [`Pio`] do not keep pin registrations alive.** pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); unsafe { @@ -877,8 +865,25 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } } +pub struct PioIrq<'d, PIO: PioInstance, const N: usize> { + pio: PhantomData<&'d PIO>, +} + +impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { + pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> { + IrqFuture { + pio: PhantomData, + irq_no: N as u8, + } + } +} + pub struct Pio<'d, PIO: PioInstance> { pub common: PioCommon<'d, PIO>, + pub irq0: PioIrq<'d, PIO, 0>, + pub irq1: PioIrq<'d, PIO, 1>, + pub irq2: PioIrq<'d, PIO, 2>, + pub irq3: PioIrq<'d, PIO, 3>, pub sm0: PioStateMachine<'d, PIO, 0>, pub sm1: PioStateMachine<'d, PIO, 1>, pub sm2: PioStateMachine<'d, PIO, 2>, @@ -894,6 +899,10 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { instructions_used: 0, pio: PhantomData, }, + irq0: PioIrq { pio: PhantomData }, + irq1: PioIrq { pio: PhantomData }, + irq2: PioIrq { pio: PhantomData }, + irq3: PioIrq { pio: PhantomData }, sm0: PioStateMachine { pio: PhantomData }, sm1: PioStateMachine { pio: PhantomData }, sm2: PioStateMachine { pio: PhantomData }, diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 11b290869..3d76a7d7b 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Pio, PioCommon, PioPin, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Pio, PioCommon, PioIrq, PioPin, PioStateMachine, ShiftDirection}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; @@ -99,10 +99,10 @@ fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachine) { +async fn pio_task_sm2(mut irq: PioIrq<'static, PIO0, 3>, mut sm: PioStateMachine<'static, PIO0, 2>) { sm.set_enable(true); loop { - sm.wait_irq(3).await; + irq.wait().await; info!("IRQ trigged"); } } @@ -114,6 +114,7 @@ async fn main(spawner: Spawner) { let Pio { mut common, + irq3, mut sm0, mut sm1, mut sm2, @@ -125,5 +126,5 @@ async fn main(spawner: Spawner) { setup_pio_task_sm2(&mut common, &mut sm2); spawner.spawn(pio_task_sm0(sm0)).unwrap(); spawner.spawn(pio_task_sm1(sm1)).unwrap(); - spawner.spawn(pio_task_sm2(sm2)).unwrap(); + spawner.spawn(pio_task_sm2(irq3, sm2)).unwrap(); } diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index bc51d43c4..7c1d7acfe 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -85,7 +85,10 @@ impl<'l> HD44780<'l> { let db7pin = db7.pin(); let Pio { - mut common, mut sm0, .. + mut common, + mut irq0, + mut sm0, + .. } = Pio::new(pio); // takes command words ( <0:4>) @@ -145,7 +148,7 @@ impl<'l> HD44780<'l> { sm0.push_tx((50 << 8) | 0x20); sm0.push_tx(0b1100_0000); - sm0.wait_irq(0).await; + irq0.wait().await; sm0.set_enable(false); // takes command sequences ( , data...) From 77f7830da396b8666600a39e860c46ddde395f04 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 10:51:50 +0200 Subject: [PATCH 1044/1575] rp/pio: move irq flag handling to own struct this way we can share irq handling between state machines and common without having to duplicate the methods. it also lets us give irq flag access to places without having to dedicate a state machine or the common instance to those places, which can be very useful to eg trigger an event and wait for a confirmation using an irq wait object. --- embassy-rp/src/pio.rs | 62 ++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 769641ac9..3c5969dbd 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -811,28 +811,6 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { self.instructions_used &= !instrs.used_mask; } - pub fn is_irq_set(&self, irq_no: u8) -> bool { - assert!(irq_no < 8); - unsafe { - let irq_flags = PIO::PIO.irq(); - irq_flags.read().0 & (1 << irq_no) != 0 - } - } - - pub fn clear_irq(&mut self, irq_no: usize) { - assert!(irq_no < 8); - unsafe { PIO::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } - } - - pub fn clear_irqs(&mut self, mask: u8) { - unsafe { PIO::PIO.irq().write(|w| w.set_irq(mask)) } - } - - pub fn force_irq(&mut self, irq_no: usize) { - assert!(irq_no < 8); - unsafe { PIO::PIO.irq_force().write(|w| w.set_irq_force(1 << irq_no)) } - } - pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { unsafe { // this can interfere with per-pin bypass functions. splitting the @@ -878,8 +856,47 @@ impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { } } +#[derive(Clone)] +pub struct PioIrqFlags<'d, PIO: PioInstance> { + pio: PhantomData<&'d PIO>, +} + +impl<'d, PIO: PioInstance> PioIrqFlags<'d, PIO> { + pub fn check(&self, irq_no: u8) -> bool { + assert!(irq_no < 8); + self.check_any(1 << irq_no) + } + + pub fn check_any(&self, irqs: u8) -> bool { + unsafe { PIO::PIO.irq().read().irq() & irqs != 0 } + } + + pub fn check_all(&self, irqs: u8) -> bool { + unsafe { PIO::PIO.irq().read().irq() & irqs == irqs } + } + + pub fn clear(&self, irq_no: usize) { + assert!(irq_no < 8); + self.clear_all(1 << irq_no); + } + + pub fn clear_all(&self, irqs: u8) { + unsafe { PIO::PIO.irq().write(|w| w.set_irq(irqs)) } + } + + pub fn set(&self, irq_no: usize) { + assert!(irq_no < 8); + self.set_all(1 << irq_no); + } + + pub fn set_all(&self, irqs: u8) { + unsafe { PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs)) } + } +} + pub struct Pio<'d, PIO: PioInstance> { pub common: PioCommon<'d, PIO>, + pub irq_flags: PioIrqFlags<'d, PIO>, pub irq0: PioIrq<'d, PIO, 0>, pub irq1: PioIrq<'d, PIO, 1>, pub irq2: PioIrq<'d, PIO, 2>, @@ -899,6 +916,7 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { instructions_used: 0, pio: PhantomData, }, + irq_flags: PioIrqFlags { pio: PhantomData }, irq0: PioIrq { pio: PhantomData }, irq1: PioIrq { pio: PhantomData }, irq2: PioIrq { pio: PhantomData }, From c44c108db57cbe34e21411054840f61c61efa8a8 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 12:49:55 +0200 Subject: [PATCH 1045/1575] rp/pio: wrap sm rx, tx in structs and allow splitting this *finally* allows sound implementions of bidirectional transfers without blocking. the futures previously allowed only a single direction to be active at any given time, and the dma transfers didn't take a mutable reference and were thus unsound. --- embassy-rp/src/pio.rs | 334 ++++++++++++++++------------- embassy-rp/src/pio_instr_util.rs | 12 +- examples/rp/src/bin/pio_async.rs | 4 +- examples/rp/src/bin/pio_dma.rs | 5 +- examples/rp/src/bin/pio_hd44780.rs | 16 +- examples/rp/src/bin/ws2812-pio.rs | 2 +- 6 files changed, 204 insertions(+), 169 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 3c5969dbd..2cf4761a5 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -97,13 +97,13 @@ pub(crate) unsafe fn init() { /// Future that waits for TX-FIFO to become writable #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FifoOutFuture<'a, 'd, PIO: PioInstance, const SM: usize> { - sm: &'a mut PioStateMachine<'d, PIO, SM>, + sm_tx: &'a mut PioStateMachineTx<'d, PIO, SM>, value: u32, } impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { - pub fn new(sm: &'a mut PioStateMachine<'d, PIO, SM>, value: u32) -> Self { - FifoOutFuture { sm, value } + pub fn new(sm: &'a mut PioStateMachineTx<'d, PIO, SM>, value: u32) -> Self { + FifoOutFuture { sm_tx: sm, value } } } @@ -112,7 +112,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); let value = self.value; - if self.get_mut().sm.try_push_tx(value) { + if self.get_mut().sm_tx.try_push(value) { Poll::Ready(()) } else { WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker()); @@ -140,12 +140,12 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, P /// Future that waits for RX-FIFO to become readable #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct FifoInFuture<'a, 'd, PIO: PioInstance, const SM: usize> { - sm: &'a mut PioStateMachine<'d, PIO, SM>, + sm_rx: &'a mut PioStateMachineRx<'d, PIO, SM>, } impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { - pub fn new(sm: &'a mut PioStateMachine<'d, PIO, SM>) -> Self { - FifoInFuture { sm } + pub fn new(sm: &'a mut PioStateMachineRx<'d, PIO, SM>) -> Self { + FifoInFuture { sm_rx: sm } } } @@ -153,7 +153,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, type Output = u32; fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); - if let Some(v) = self.sm.try_pull_rx() { + if let Some(v) = self.sm_rx.try_pull() { Poll::Ready(v) } else { WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker()); @@ -293,10 +293,163 @@ impl<'l, PIO: PioInstance> Pin<'l, PIO> { } } -pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> { +pub struct PioStateMachineRx<'d, PIO: PioInstance, const SM: usize> { pio: PhantomData<&'d PIO>, } +impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { + pub fn empty(&self) -> bool { + unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } + } + + pub fn full(&self) -> bool { + unsafe { PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 } + } + + pub fn level(&self) -> u8 { + unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f } + } + + pub fn stalled(&self) -> bool { + unsafe { + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().rxstall() & (1 << SM) != 0; + fdebug.write(|w| w.set_rxstall(1 << SM)); + ret + } + } + + pub fn underflowed(&self) -> bool { + unsafe { + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().rxunder() & (1 << SM) != 0; + fdebug.write(|w| w.set_rxunder(1 << SM)); + ret + } + } + + pub fn pull(&mut self) -> u32 { + unsafe { PIO::PIO.rxf(SM).read() } + } + + pub fn try_pull(&mut self) -> Option { + if self.empty() { + return None; + } + Some(self.pull()) + } + + pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> { + FifoInFuture::new(self) + } + + pub fn dma_pull<'a, C: Channel, W: Word>( + &'a mut self, + ch: PeripheralRef<'a, C>, + data: &'a mut [W], + ) -> Transfer<'a, C> { + unsafe { + let pio_no = PIO::PIO_NO; + let p = ch.regs(); + p.write_addr().write_value(data.as_ptr() as u32); + p.read_addr().write_value(PIO::PIO.rxf(SM).ptr() as u32); + p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); + p.ctrl_trig().write(|w| { + // Set RX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4)); + w.set_data_size(W::size()); + w.set_chain_to(ch.number()); + w.set_incr_read(false); + w.set_incr_write(true); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); + } + Transfer::new(ch) + } +} + +pub struct PioStateMachineTx<'d, PIO: PioInstance, const SM: usize> { + pio: PhantomData<&'d PIO>, +} + +impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { + pub fn empty(&self) -> bool { + unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } + } + pub fn full(&self) -> bool { + unsafe { PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 } + } + + pub fn level(&self) -> u8 { + unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f } + } + + pub fn stalled(&self) -> bool { + unsafe { + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().txstall() & (1 << SM) != 0; + fdebug.write(|w| w.set_txstall(1 << SM)); + ret + } + } + + pub fn overflowed(&self) -> bool { + unsafe { + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().txover() & (1 << SM) != 0; + fdebug.write(|w| w.set_txover(1 << SM)); + ret + } + } + + pub fn push(&mut self, v: u32) { + unsafe { + PIO::PIO.txf(SM).write_value(v); + } + } + + pub fn try_push(&mut self, v: u32) -> bool { + if self.full() { + return false; + } + self.push(v); + true + } + + pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> { + FifoOutFuture::new(self, value) + } + + pub fn dma_push<'a, C: Channel, W: Word>(&'a mut self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { + unsafe { + let pio_no = PIO::PIO_NO; + let p = ch.regs(); + p.read_addr().write_value(data.as_ptr() as u32); + p.write_addr().write_value(PIO::PIO.txf(SM).ptr() as u32); + p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); + p.ctrl_trig().write(|w| { + // Set TX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8)); + w.set_data_size(W::size()); + w.set_chain_to(ch.number()); + w.set_incr_read(true); + w.set_incr_write(false); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); + } + Transfer::new(ch) + } +} + +pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> { + rx: PioStateMachineRx<'d, PIO, SM>, + tx: PioStateMachineTx<'d, PIO, SM>, +} + impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM> { fn drop(&mut self) { unsafe { @@ -333,59 +486,6 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } } - pub fn is_tx_empty(&self) -> bool { - unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } - } - pub fn is_tx_full(&self) -> bool { - unsafe { PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 } - } - - pub fn is_rx_empty(&self) -> bool { - unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } - } - pub fn is_rx_full(&self) -> bool { - unsafe { PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 } - } - - pub fn tx_level(&self) -> u8 { - unsafe { - let flevel = PIO::PIO.flevel().read().0; - (flevel >> (SM * 8)) as u8 & 0x0f - } - } - - pub fn rx_level(&self) -> u8 { - unsafe { - let flevel = PIO::PIO.flevel().read().0; - (flevel >> (SM * 8 + 4)) as u8 & 0x0f - } - } - - pub fn push_tx(&mut self, v: u32) { - unsafe { - PIO::PIO.txf(SM).write_value(v); - } - } - - pub fn try_push_tx(&mut self, v: u32) -> bool { - if self.is_tx_full() { - return false; - } - self.push_tx(v); - true - } - - pub fn pull_rx(&mut self) -> u32 { - unsafe { PIO::PIO.rxf(SM).read() } - } - - pub fn try_pull_rx(&mut self) -> Option { - if self.is_rx_empty() { - return None; - } - Some(self.pull_rx()) - } - pub fn set_clkdiv(&mut self, div_x_256: u32) { unsafe { Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); @@ -671,92 +771,14 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { } } - pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> { - FifoOutFuture::new(self, value) + pub fn rx(&mut self) -> &mut PioStateMachineRx<'d, PIO, SM> { + &mut self.rx } - - pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> { - FifoInFuture::new(self) + pub fn tx(&mut self) -> &mut PioStateMachineTx<'d, PIO, SM> { + &mut self.tx } - - pub fn has_tx_stalled(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().txstall() & (1 << SM) != 0; - fdebug.write(|w| w.set_txstall(1 << SM)); - ret - } - } - - pub fn has_tx_overflowed(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().txover() & (1 << SM) != 0; - fdebug.write(|w| w.set_txover(1 << SM)); - ret - } - } - - pub fn has_rx_stalled(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().rxstall() & (1 << SM) != 0; - fdebug.write(|w| w.set_rxstall(1 << SM)); - ret - } - } - - pub fn has_rx_underflowed(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().rxunder() & (1 << SM) != 0; - fdebug.write(|w| w.set_rxunder(1 << SM)); - ret - } - } - - pub fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { - unsafe { - let pio_no = PIO::PIO_NO; - let p = ch.regs(); - p.read_addr().write_value(data.as_ptr() as u32); - p.write_addr().write_value(PIO::PIO.txf(SM).ptr() as u32); - p.trans_count().write_value(data.len() as u32); - compiler_fence(Ordering::SeqCst); - p.ctrl_trig().write(|w| { - // Set TX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8)); - w.set_data_size(W::size()); - w.set_chain_to(ch.number()); - w.set_incr_read(true); - w.set_incr_write(false); - w.set_en(true); - }); - compiler_fence(Ordering::SeqCst); - } - Transfer::new(ch) - } - - pub fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> { - unsafe { - let pio_no = PIO::PIO_NO; - let p = ch.regs(); - p.write_addr().write_value(data.as_ptr() as u32); - p.read_addr().write_value(PIO::PIO.rxf(SM).ptr() as u32); - p.trans_count().write_value(data.len() as u32); - compiler_fence(Ordering::SeqCst); - p.ctrl_trig().write(|w| { - // Set RX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4)); - w.set_data_size(W::size()); - w.set_chain_to(ch.number()); - w.set_incr_read(false); - w.set_incr_write(true); - w.set_en(true); - }); - compiler_fence(Ordering::SeqCst); - } - Transfer::new(ch) + pub fn rx_tx(&mut self) -> (&mut PioStateMachineRx<'d, PIO, SM>, &mut PioStateMachineTx<'d, PIO, SM>) { + (&mut self.rx, &mut self.tx) } } @@ -921,10 +943,22 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { irq1: PioIrq { pio: PhantomData }, irq2: PioIrq { pio: PhantomData }, irq3: PioIrq { pio: PhantomData }, - sm0: PioStateMachine { pio: PhantomData }, - sm1: PioStateMachine { pio: PhantomData }, - sm2: PioStateMachine { pio: PhantomData }, - sm3: PioStateMachine { pio: PhantomData }, + sm0: PioStateMachine { + rx: PioStateMachineRx { pio: PhantomData }, + tx: PioStateMachineTx { pio: PhantomData }, + }, + sm1: PioStateMachine { + rx: PioStateMachineRx { pio: PhantomData }, + tx: PioStateMachineTx { pio: PhantomData }, + }, + sm2: PioStateMachine { + rx: PioStateMachineRx { pio: PhantomData }, + tx: PioStateMachineTx { pio: PhantomData }, + }, + sm3: PioStateMachine { + rx: PioStateMachineRx { pio: PhantomData }, + tx: PioStateMachineTx { pio: PhantomData }, + }, } } } diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index ffe160246..81abdb666 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs @@ -8,7 +8,7 @@ pub fn set_x(sm: &mut PioStateMachine(sm: &mut PioStateMachine(sm: &mut PioStateMachine, value: u32) { @@ -28,7 +28,7 @@ pub fn set_y(sm: &mut PioStateMachine(sm: &mut PioStateMachine(sm: &mut PioStateMachine, data: u8) { @@ -67,7 +67,7 @@ pub fn set_out_pin(sm: &mut PioStateMachine

(sm: &mut PioStateMachine, data: u32) { @@ -76,7 +76,7 @@ pub fn set_out_pindir(sm: &mut PioStateMachin bit_count: 32, } .encode(); - sm.push_tx(data); + sm.tx().push(data); sm.exec_instr(OUT); } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 3d76a7d7b..4e0ab5e3c 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -42,7 +42,7 @@ async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) { let mut v = 0x0f0caffa; loop { - sm.wait_push(v).await; + sm.tx().wait_push(v).await; v ^= 0xffff; info!("Pushed {:032b} to FIFO", v); } @@ -70,7 +70,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachine) { sm.set_enable(true); loop { - let rx = sm.wait_pull().await; + let rx = sm.rx().wait_pull().await; info!("Pulled {:032b} from FIFO", rx); } } diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index a2a2ee39a..c664482e5 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -60,9 +60,10 @@ async fn main(_spawner: Spawner) { } let mut din = [0u32; 29]; loop { + let (rx, tx) = sm.rx_tx(); join( - sm.dma_push(dma_out_ref.reborrow(), &dout), - sm.dma_pull(dma_in_ref.reborrow(), &mut din), + tx.dma_push(dma_out_ref.reborrow(), &dout), + rx.dma_pull(dma_in_ref.reborrow(), &mut din), ) .await; for i in 0..din.len() { diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 7c1d7acfe..f76d334e7 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -139,14 +139,14 @@ impl<'l> HD44780<'l> { sm0.set_enable(true); // init to 8 bit thrice - sm0.push_tx((50000 << 8) | 0x30); - sm0.push_tx((5000 << 8) | 0x30); - sm0.push_tx((200 << 8) | 0x30); + sm0.tx().push((50000 << 8) | 0x30); + sm0.tx().push((5000 << 8) | 0x30); + sm0.tx().push((200 << 8) | 0x30); // init 4 bit - sm0.push_tx((200 << 8) | 0x20); + sm0.tx().push((200 << 8) | 0x20); // set font and lines - sm0.push_tx((50 << 8) | 0x20); - sm0.push_tx(0b1100_0000); + sm0.tx().push((50 << 8) | 0x20); + sm0.tx().push(0b1100_0000); irq0.wait().await; sm0.set_enable(false); @@ -216,7 +216,7 @@ impl<'l> HD44780<'l> { sm0.set_enable(true); // display on and cursor on and blinking, reset display - sm0.dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; + sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; Self { dma: dma.map_into(), @@ -240,6 +240,6 @@ impl<'l> HD44780<'l> { // set cursor to 1:15 self.buf[38..].copy_from_slice(&[0x80, 0xcf]); - self.sm.dma_push(self.dma.reborrow(), &self.buf).await; + self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; } } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 713e01b44..c9c701a70 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -87,7 +87,7 @@ impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { pub async fn write(&mut self, colors: &[RGB8]) { for color in colors { let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); - self.sm.wait_push(word).await; + self.sm.tx().wait_push(word).await; } } } From 8dbe397f993cb3a9c330c3d6d5a90a8695d87c67 Mon Sep 17 00:00:00 2001 From: Kai Bleeke <39027073+kbleeke@users.noreply.github.com> Date: Wed, 3 May 2023 20:15:43 +0200 Subject: [PATCH 1046/1575] cleanup ioctl response logging --- src/ioctl.rs | 2 +- src/runner.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ioctl.rs b/src/ioctl.rs index 66c6a10e5..61524c274 100644 --- a/src/ioctl.rs +++ b/src/ioctl.rs @@ -110,7 +110,7 @@ impl IoctlState { pub fn ioctl_done(&self, response: &[u8]) { if let IoctlStateInner::Sent { buf } = self.state.get() { - info!("IOCTL Response: {:02x}", Bytes(response)); + trace!("IOCTL Response: {:02x}", Bytes(response)); // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); diff --git a/src/runner.rs b/src/runner.rs index 9b99e174f..56b9a609c 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -351,8 +351,6 @@ where panic!("IOCTL error {}", cdc_header.status as i32); } - info!("IOCTL Response: {:02x}", Bytes(response)); - self.ioctl_state.ioctl_done(response); } } From a0b1299890425908fb7f5c2f8c26c3f0e125a5fb Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 3 May 2023 17:36:31 -0500 Subject: [PATCH 1047/1575] stm32/tests: add hil test for ble --- examples/stm32wb/src/bin/tl_mbox.rs | 1 - tests/stm32/Cargo.toml | 8 ++++- tests/stm32/src/bin/ble.rs | 51 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/stm32/src/bin/ble.rs diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index ee090e6eb..2cca02ff9 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -40,5 +40,4 @@ async fn main(_spawner: Spawner) { } } - loop {} } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 5cd949661..03ddd3d0e 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -11,12 +11,13 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "ble", "not-gpdma"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] chrono = ["embassy-stm32/chrono", "dep:chrono"] +ble = [] not-gpdma = [] [dependencies] @@ -42,6 +43,11 @@ chrono = { version = "^0.4", default-features = false, optional = true} # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. +[[bin]] +name = "ble" +path = "src/bin/ble.rs" +required-features = [ "ble",] + [[bin]] name = "gpio" path = "src/bin/gpio.rs" diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/ble.rs new file mode 100644 index 000000000..f4c864c65 --- /dev/null +++ b/tests/stm32/src/bin/ble.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// required-features: ble + +#[path = "../example_common.rs"] +mod example_common; +use embassy_executor::Spawner; +use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::tl_mbox::TlMbox; +use embassy_time::{Duration, Timer}; +use example_common::*; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(config()); + info!("Hello World!"); + + let config = Config::default(); + let mut ipcc = Ipcc::new(p.IPCC, config); + + let mbox = TlMbox::init(&mut ipcc); + + loop { + let wireless_fw_info = mbox.wireless_fw_info(); + match wireless_fw_info { + None => error!("not yet initialized"), + Some(fw_info) => { + let version_major = fw_info.version_major(); + let version_minor = fw_info.version_minor(); + let subversion = fw_info.subversion(); + + let sram2a_size = fw_info.sram2a_size(); + let sram2b_size = fw_info.sram2b_size(); + + info!( + "version {}.{}.{} - SRAM2a {} - SRAM2b {}", + version_major, version_minor, subversion, sram2a_size, sram2b_size + ); + + break; + } + } + + Timer::after(Duration::from_millis(500)).await; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 7750ea65ba7a6ac766247fda22a9acefd6879a6a Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 3 May 2023 18:14:42 -0500 Subject: [PATCH 1048/1575] rustfmt --- examples/stm32wb/src/bin/tl_mbox.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 2cca02ff9..6a9b9c936 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -39,5 +39,4 @@ async fn main(_spawner: Spawner) { } } } - } From 02d6e0d14dec98c01a2b327b0050e80845922510 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 3 May 2023 18:17:57 -0500 Subject: [PATCH 1049/1575] stm32/i2s: add module and example for f4 --- embassy-stm32/build.rs | 4 + embassy-stm32/src/i2s.rs | 310 ++++++++++++++++++++++++++++ embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/spi/mod.rs | 15 ++ examples/stm32f4/src/bin/i2s_dma.rs | 36 ++++ 5 files changed, 367 insertions(+) create mode 100644 embassy-stm32/src/i2s.rs create mode 100644 examples/stm32f4/src/bin/i2s_dma.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index a00c6c416..dcee535b5 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -420,6 +420,10 @@ fn main() { (("spi", "SCK"), quote!(crate::spi::SckPin)), (("spi", "MOSI"), quote!(crate::spi::MosiPin)), (("spi", "MISO"), quote!(crate::spi::MisoPin)), + (("spi", "NSS"), quote!(crate::spi::CsPin)), + (("spi", "I2S_MCK"), quote!(crate::spi::MckPin)), + (("spi", "I2S_CK"), quote!(crate::spi::CkPin)), + (("spi", "I2S_WS"), quote!(crate::spi::WsPin)), (("i2c", "SDA"), quote!(crate::i2c::SdaPin)), (("i2c", "SCL"), quote!(crate::i2c::SclPin)), (("rcc", "MCO_1"), quote!(crate::rcc::McoPin)), diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs new file mode 100644 index 000000000..2bb199f68 --- /dev/null +++ b/embassy-stm32/src/i2s.rs @@ -0,0 +1,310 @@ +use embassy_hal_common::into_ref; + +use crate::gpio::sealed::{AFType, Pin as _}; +use crate::gpio::AnyPin; +use crate::pac::spi::vals; +use crate::rcc::get_freqs; +use crate::spi::{Config as SpiConfig, *}; +use crate::time::Hertz; +use crate::{Peripheral, PeripheralRef}; + +#[derive(Copy, Clone)] +pub enum Mode { + Master, + Slave, +} + +#[derive(Copy, Clone)] +pub enum Function { + Transmit, + Receive, +} + +#[derive(Copy, Clone)] +pub enum Standard { + Philips, + MsbFirst, + LsbFirst, + PcmLongSync, + PcmShortSync, +} + +impl Standard { + #[cfg(any(spi_v1, spi_f1))] + pub const fn i2sstd(&self) -> vals::I2sstd { + match self { + Standard::Philips => vals::I2sstd::PHILIPS, + Standard::MsbFirst => vals::I2sstd::MSB, + Standard::LsbFirst => vals::I2sstd::LSB, + Standard::PcmLongSync => vals::I2sstd::PCM, + Standard::PcmShortSync => vals::I2sstd::PCM, + } + } + + #[cfg(any(spi_v1, spi_f1))] + pub const fn pcmsync(&self) -> vals::Pcmsync { + match self { + Standard::PcmLongSync => vals::Pcmsync::LONG, + _ => vals::Pcmsync::SHORT, + } + } +} + +#[derive(Copy, Clone)] +pub enum Format { + /// 16 bit data length on 16 bit wide channel + Data16Channel16, + /// 16 bit data length on 32 bit wide channel + Data16Channel32, + /// 24 bit data length on 32 bit wide channel + Data24Channel32, + /// 32 bit data length on 32 bit wide channel + Data32Channel32, +} + +impl Format { + #[cfg(any(spi_v1, spi_f1))] + pub const fn datlen(&self) -> vals::Datlen { + match self { + Format::Data16Channel16 => vals::Datlen::SIXTEENBIT, + Format::Data16Channel32 => vals::Datlen::SIXTEENBIT, + Format::Data24Channel32 => vals::Datlen::TWENTYFOURBIT, + Format::Data32Channel32 => vals::Datlen::THIRTYTWOBIT, + } + } + + #[cfg(any(spi_v1, spi_f1))] + pub const fn chlen(&self) -> vals::Chlen { + match self { + Format::Data16Channel16 => vals::Chlen::SIXTEENBIT, + Format::Data16Channel32 => vals::Chlen::THIRTYTWOBIT, + Format::Data24Channel32 => vals::Chlen::THIRTYTWOBIT, + Format::Data32Channel32 => vals::Chlen::THIRTYTWOBIT, + } + } +} + +#[derive(Copy, Clone)] +pub enum ClockPolarity { + IdleLow, + IdleHigh, +} + +impl ClockPolarity { + #[cfg(any(spi_v1, spi_f1))] + pub const fn ckpol(&self) -> vals::Ckpol { + match self { + ClockPolarity::IdleHigh => vals::Ckpol::IDLEHIGH, + ClockPolarity::IdleLow => vals::Ckpol::IDLELOW, + } + } +} + +/// [`I2S`] configuration. +/// +/// - `MS`: `Master` or `Slave` +/// - `TR`: `Transmit` or `Receive` +/// - `STD`: I2S standard, eg `Philips` +/// - `FMT`: Frame Format marker, eg `Data16Channel16` +#[non_exhaustive] +#[derive(Copy, Clone)] +pub struct Config { + pub mode: Mode, + pub function: Function, + pub standard: Standard, + pub format: Format, + pub clock_polarity: ClockPolarity, + pub master_clock: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + mode: Mode::Master, + function: Function::Transmit, + standard: Standard::Philips, + format: Format::Data16Channel16, + clock_polarity: ClockPolarity::IdleLow, + master_clock: true, + } + } +} + +pub struct I2S<'d, T: Instance, Tx, Rx> { + _peri: Spi<'d, T, Tx, Rx>, + sd: Option>, + ws: Option>, + ck: Option>, + mck: Option>, +} + +impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { + /// Note: Full-Duplex modes are not supported at this time + pub fn new( + peri: impl Peripheral

+ 'd, + sd: impl Peripheral

> + 'd, + ws: impl Peripheral

> + 'd, + ck: impl Peripheral

> + 'd, + mck: impl Peripheral

> + 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ 'd, + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(sd, ws, ck, mck); + + unsafe { + sd.set_as_af(sd.af_num(), AFType::OutputPushPull); + sd.set_speed(crate::gpio::Speed::VeryHigh); + + ws.set_as_af(ws.af_num(), AFType::OutputPushPull); + ws.set_speed(crate::gpio::Speed::VeryHigh); + + ck.set_as_af(ck.af_num(), AFType::OutputPushPull); + ck.set_speed(crate::gpio::Speed::VeryHigh); + + mck.set_as_af(mck.af_num(), AFType::OutputPushPull); + mck.set_speed(crate::gpio::Speed::VeryHigh); + } + + let spi = Spi::new_internal(peri, txdma, rxdma, freq, SpiConfig::default()); + + #[cfg(all(rcc_f4, not(stm32f410)))] + let pclk = unsafe { get_freqs() }.plli2s.unwrap(); + + #[cfg(stm32f410)] + let pclk = T::frequency(); + + let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format); + + #[cfg(any(spi_v1, spi_f1))] + unsafe { + use stm32_metapac::spi::vals::{I2scfg, Odd}; + + // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR register to define the serial clock baud + // rate to reach the proper audio sample frequency. The ODD bit in the SPI_I2SPR + // register also has to be defined. + + T::REGS.i2spr().modify(|w| { + w.set_i2sdiv(div); + w.set_odd(match odd { + true => Odd::ODD, + false => Odd::EVEN, + }); + + w.set_mckoe(config.master_clock); + }); + + // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the + // MCKOE bit in the SPI_I2SPR register if the master clock MCK needs to be provided to + // the external DAC/ADC audio component (the I2SDIV and ODD values should be + // computed depending on the state of the MCK output, for more details refer to + // Section 28.4.4: Clock generator). + + // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the + // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the + // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit. + // Select also the I2S master mode and direction (Transmitter or Receiver) through the + // I2SCFG[1:0] bits in the SPI_I2SCFGR register. + + // 4. If needed, select all the potential interruption sources and the DMA capabilities by + // writing the SPI_CR2 register. + + // 5. The I2SE bit in SPI_I2SCFGR register must be set. + + T::REGS.i2scfgr().modify(|w| { + w.set_ckpol(config.clock_polarity.ckpol()); + + w.set_i2smod(true); + w.set_i2sstd(config.standard.i2sstd()); + w.set_pcmsync(config.standard.pcmsync()); + + w.set_datlen(config.format.datlen()); + w.set_chlen(config.format.chlen()); + + w.set_i2scfg(match (config.mode, config.function) { + (Mode::Master, Function::Transmit) => I2scfg::MASTERTX, + (Mode::Master, Function::Receive) => I2scfg::MASTERRX, + (Mode::Slave, Function::Transmit) => I2scfg::SLAVETX, + (Mode::Slave, Function::Receive) => I2scfg::SLAVERX, + }); + + w.set_i2se(true) + }); + } + #[cfg(spi_v2)] + unsafe {} + #[cfg(any(spi_v3, spi_v4))] + unsafe {} + + Self { + _peri: spi, + sd: Some(sd.map_into()), + ws: Some(ws.map_into()), + ck: Some(ck.map_into()), + mck: Some(mck.map_into()), + } + } + + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> + where + Tx: TxDma, + { + self._peri.write(data).await + } + + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> + where + Tx: TxDma, + Rx: RxDma, + { + self._peri.read(data).await + } +} + +impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> { + fn drop(&mut self) { + unsafe { + self.sd.as_ref().map(|x| x.set_as_disconnected()); + self.ws.as_ref().map(|x| x.set_as_disconnected()); + self.ck.as_ref().map(|x| x.set_as_disconnected()); + self.mck.as_ref().map(|x| x.set_as_disconnected()); + } + } +} + +// Note, calculation details: +// Fs = i2s_clock / [256 * ((2 * div) + odd)] when master clock is enabled +// Fs = i2s_clock / [(channel_length * 2) * ((2 * div) + odd)]` when master clock is disabled +// channel_length is 16 or 32 +// +// can be rewritten as +// Fs = i2s_clock / (coef * division) +// where coef is a constant equal to 256, 64 or 32 depending channel length and master clock +// and where division = (2 * div) + odd +// +// Equation can be rewritten as +// division = i2s_clock/ (coef * Fs) +// +// note: division = (2 * div) + odd = (div << 1) + odd +// in other word, from bits point of view, division[8:1] = div[7:0] and division[0] = odd +fn compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_format: Format) -> (bool, u8) { + let coef = if mclk { + 256 + } else if let Format::Data16Channel16 = data_format { + 32 + } else { + 64 + }; + + let (n, d) = (i2s_clock.0, coef * request_freq.0); + let division = (n + (d >> 1)) / d; + + if division < 4 { + (false, 2) + } else if division > 511 { + (true, 255) + } else { + ((division & 1) == 1, (division >> 1) as u8) + } +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 11820b7a0..7c83a6984 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -44,6 +44,8 @@ pub mod i2c; #[cfg(crc)] pub mod crc; pub mod flash; +#[cfg(all(spi_v1, rcc_f4))] +pub mod i2s; #[cfg(stm32wb)] pub mod ipcc; pub mod pwm; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index aefa42435..22ab423b6 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -207,6 +207,17 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) } + #[allow(dead_code)] + pub(crate) fn new_internal( + peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ 'd, + freq: Hertz, + config: Config, + ) -> Self { + Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + } + fn new_inner( peri: impl Peripheral

+ 'd, sck: Option>, @@ -1039,6 +1050,10 @@ pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} pin_trait!(SckPin, Instance); pin_trait!(MosiPin, Instance); pin_trait!(MisoPin, Instance); +pin_trait!(CsPin, Instance); +pin_trait!(MckPin, Instance); +pin_trait!(CkPin, Instance); +pin_trait!(WsPin, Instance); dma_trait!(RxDma, Instance); dma_trait!(TxDma, Instance); diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs new file mode 100644 index 000000000..e8d7b5f77 --- /dev/null +++ b/examples/stm32f4/src/bin/i2s_dma.rs @@ -0,0 +1,36 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::fmt::Write; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2s::{Config, I2S}; +use embassy_stm32::time::Hertz; +use heapless::String; +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 i2s = I2S::new( + p.SPI2, + p.PC3, // sd + p.PB12, // ws + p.PB10, // ck + p.PC6, // mck + p.DMA1_CH4, + p.DMA1_CH3, + Hertz(1_000_000), + Config::default(), + ); + + for n in 0u32.. { + let mut write: String<128> = String::new(); + core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); + i2s.write(&mut write.as_bytes()).await.ok(); + } +} From 629e0ea595b28d89aad3f953b7190b04d78ff9d3 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 3 May 2023 21:05:47 -0500 Subject: [PATCH 1050/1575] Handle SUBGHZSPI as async. --- embassy-stm32/src/spi/mod.rs | 11 ++++++++--- examples/stm32wl/src/bin/lora_lorawan.rs | 18 +++++------------- examples/stm32wl/src/bin/lora_p2p_receive.rs | 14 +++----------- examples/stm32wl/src/bin/lora_p2p_send.rs | 14 +++----------- 4 files changed, 19 insertions(+), 38 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index aefa42435..ead8411ec 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -195,15 +195,20 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } /// Useful for on chip peripherals like SUBGHZ which are hardwired. - /// The bus can optionally be exposed externally with `Spi::new()` still. #[allow(dead_code)] pub fn new_subghz( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, - config: Config, + pclk3_freq: u32, ) -> Self { + // see RM0453 rev 1 section 7.2.13 page 291 + // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. + // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. + let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); + let mut config = Config::default(); + config.mode = MODE_0; + config.bit_order = BitOrder::MsbFirst; Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) } diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 4bcc5420e..467ba7604 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -7,17 +7,14 @@ #![allow(incomplete_features)] use defmt::info; -use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_lora::iv::Stm32wlInterfaceVariant; use embassy_lora::LoraTimer; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; use embassy_stm32::peripherals::SUBGHZSPI; use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::rng::Rng; -use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; -use embassy_stm32::time::Hertz; +use embassy_stm32::spi::Spi; use embassy_stm32::{interrupt, into_ref, pac, Peripheral}; use embassy_time::Delay; use lora_phy::mod_params::*; @@ -33,19 +30,14 @@ const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set th #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); - config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16; - config.rcc.enable_lsi = true; + config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; + config.rcc.enable_lsi = true; // enable RNG let p = embassy_stm32::init(config); unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } - let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); - let mut spi_config = SpiConfig::default(); - spi_config.mode = MODE_0; - spi_config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config); - - let spi = BlockingAsync::new(spi); + let pclk3_freq = SUBGHZSPI::frequency().0; + let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2, pclk3_freq); let irq = interrupt::take!(SUBGHZ_RADIO); into_ref!(irq); diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs index bb31518c4..cb0a8e349 100644 --- a/examples/stm32wl/src/bin/lora_p2p_receive.rs +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs @@ -7,15 +7,12 @@ #![allow(incomplete_features)] use defmt::info; -use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_lora::iv::Stm32wlInterfaceVariant; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; use embassy_stm32::peripherals::SUBGHZSPI; use embassy_stm32::rcc::low_level::RccPeripheral; -use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; -use embassy_stm32::time::Hertz; +use embassy_stm32::spi::Spi; use embassy_stm32::{interrupt, into_ref, Peripheral}; use embassy_time::{Delay, Duration, Timer}; use lora_phy::mod_params::*; @@ -31,13 +28,8 @@ async fn main(_spawner: Spawner) { config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; let p = embassy_stm32::init(config); - let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); - let mut spi_config = SpiConfig::default(); - spi_config.mode = MODE_0; - spi_config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config); - - let spi = BlockingAsync::new(spi); + let pclk3_freq = SUBGHZSPI::frequency().0; + let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2, pclk3_freq); let irq = interrupt::take!(SUBGHZ_RADIO); into_ref!(irq); diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs index 8a38402fa..f267f2ae6 100644 --- a/examples/stm32wl/src/bin/lora_p2p_send.rs +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -7,15 +7,12 @@ #![allow(incomplete_features)] use defmt::info; -use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_lora::iv::Stm32wlInterfaceVariant; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; use embassy_stm32::peripherals::SUBGHZSPI; use embassy_stm32::rcc::low_level::RccPeripheral; -use embassy_stm32::spi::{BitOrder, Config as SpiConfig, Spi, MODE_0}; -use embassy_stm32::time::Hertz; +use embassy_stm32::spi::Spi; use embassy_stm32::{interrupt, into_ref, Peripheral}; use embassy_time::Delay; use lora_phy::mod_params::*; @@ -31,13 +28,8 @@ async fn main(_spawner: Spawner) { config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; let p = embassy_stm32::init(config); - let clk = Hertz(core::cmp::min(SUBGHZSPI::frequency().0 / 2, 16_000_000)); - let mut spi_config = SpiConfig::default(); - spi_config.mode = MODE_0; - spi_config.bit_order = BitOrder::MsbFirst; - let spi = Spi::new_subghz(p.SUBGHZSPI, NoDma, NoDma, clk, spi_config); - - let spi = BlockingAsync::new(spi); + let pclk3_freq = SUBGHZSPI::frequency().0; + let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2, pclk3_freq); let irq = interrupt::take!(SUBGHZ_RADIO); into_ref!(irq); From 007f45292762ab27291dd54bd0cfdeb23e390de4 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 4 May 2023 11:02:17 +0100 Subject: [PATCH 1051/1575] removed hardcoded addresses in memory.x --- embassy-stm32/src/tl_mbox/mod.rs | 38 ++++++++++++++--------------- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wb/memory.x | 21 ++-------------- examples/stm32wb/src/bin/tl_mbox.rs | 3 +++ 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index 54db78bd8..73d2ca6d6 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -196,67 +196,67 @@ pub struct RefTable { #[link_section = "TL_REF_TABLE"] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "TL_DEVICE_INFO_TABLE"] +#[link_section = "MB_MEM1"] static mut TL_DEVICE_INFO_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "TL_BLE_TABLE"] +#[link_section = "MB_MEM1"] static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "TL_THREAD_TABLE"] +#[link_section = "MB_MEM1"] static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "TL_SYS_TABLE"] +#[link_section = "MB_MEM1"] static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "TL_MEM_MANAGER_TABLE"] +#[link_section = "MB_MEM1"] static mut TL_MEM_MANAGER_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "TL_TRACES_TABLE"] +#[link_section = "MB_MEM1"] static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "TL_MAC_802_15_4_TABLE"] +#[link_section = "MB_MEM1"] static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); #[allow(dead_code)] // Not used currently but reserved -#[link_section = "FREE_BUF_QUEUE"] +#[link_section = "MB_MEM2"] static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); // not in shared RAM static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[allow(dead_code)] // Not used currently but reserved -#[link_section = "TRACES_EVT_QUEUE"] +#[link_section = "MB_MEM2"] static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "CS_BUFFER"] +#[link_section = "MB_MEM2"] static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = MaybeUninit::uninit(); -#[link_section = "EVT_QUEUE"] +#[link_section = "MB_MEM2"] static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "SYSTEM_EVT_QUEUE"] +#[link_section = "MB_MEM2"] static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "SYS_CMD_BUF"] +#[link_section = "MB_MEM2"] static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "EVT_POOL"] +#[link_section = "MB_MEM2"] static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); -#[link_section = "SYS_SPARE_EVT_BUF"] +#[link_section = "MB_MEM2"] static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = MaybeUninit::uninit(); -#[link_section = "BLE_SPARE_EVT_BUF"] +#[link_section = "MB_MEM2"] static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = MaybeUninit::uninit(); -#[link_section = "BLE_CMD_BUFFER"] +#[link_section = "MB_MEM2"] static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "HCI_ACL_DATA_BUFFER"] -// "magic" numbers from ST ---v---v +#[link_section = "MB_MEM2"] +// "magic" numbers from ST ---v---v static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); pub struct TlMbox { diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index db1816da3..3c7e3e874 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wb55cc", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "exti"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x index 0e48c916d..c75d07352 100644 --- a/examples/stm32wb/memory.x +++ b/examples/stm32wb/memory.x @@ -19,23 +19,6 @@ _stack_start = ORIGIN(RAM) + LENGTH(RAM); SECTIONS { TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED - TL_DEVICE_INFO_TABLE 0x2003001c (NOLOAD) : { *(TL_DEVICE_INFO_TABLE) } >RAM_SHARED - TL_BLE_TABLE 0x2003003c (NOLOAD) : { *(TL_BLE_TABLE) } >RAM_SHARED - TL_THREAD_TABLE 0x2003004c (NOLOAD) : { *(TL_THREAD_TABLE) } >RAM_SHARED - TL_SYS_TABLE 0x20030058 (NOLOAD) : { *(TL_SYS_TABLE) } >RAM_SHARED - TL_MEM_MANAGER_TABLE 0x20030060 (NOLOAD) : { *(TL_MEM_MANAGER_TABLE) } >RAM_SHARED - TL_TRACES_TABLE 0x2003007c (NOLOAD) : { *(TL_TRACES_TABLE) } >RAM_SHARED - TL_MAC_802_15_4_TABLE 0x20030080 (NOLOAD) : { *(TL_MAC_802_15_4_TABLE) } >RAM_SHARED - - HCI_ACL_DATA_BUFFER 0x20030a08 (NOLOAD) : { *(HCI_ACL_DATA_BUFFER) } >RAM_SHARED - BLE_CMD_BUFFER 0x200308fc (NOLOAD) : { *(BLE_CMD_BUFFER) } >RAM_SHARED - BLE_SPARE_EVT_BUF 0x200301a8 (NOLOAD) : { *(BLE_SPARE_EVT_BUF) } >RAM_SHARED - SYS_SPARE_EVT_BUF 0x200302b4 (NOLOAD) : { *(SYS_SPARE_EVT_BUF) } >RAM_SHARED - EVT_POOL 0x200303c0 (NOLOAD) : { *(EVT_POOL) } >RAM_SHARED - SYS_CMD_BUF 0x2003009c (NOLOAD) : { *(SYS_CMD_BUF) } >RAM_SHARED - SYSTEM_EVT_QUEUE 0x20030b28 (NOLOAD) : { *(SYSTEM_EVT_QUEUE) } >RAM_SHARED - EVT_QUEUE 0x20030b10 (NOLOAD) : { *(EVT_QUEUE) } >RAM_SHARED - CS_BUFFER 0x20030b18 (NOLOAD) : { *(CS_BUFFER) } >RAM_SHARED - TRACES_EVT_QUEUE 0x20030094 (NOLOAD) : { *(TRACES_EVT_QUEUE) } >RAM_SHARED - FREE_BUF_QUEUE 0x2003008c (NOLOAD) : { *(FREE_BUF_QUEUE) } >RAM_SHARED + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED } diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 6a9b9c936..fadeb0d22 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::ipcc::{Config, Ipcc}; use embassy_stm32::tl_mbox::TlMbox; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -38,5 +39,7 @@ async fn main(_spawner: Spawner) { break; } } + + Timer::after(Duration::from_millis(500)).await; } } From 91612b7446b5cd50cd018b3aeb7a10c77b612012 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Thu, 4 May 2023 09:45:18 -0500 Subject: [PATCH 1052/1575] Simplify SUBGHZSPI configuration. --- embassy-stm32/src/spi/mod.rs | 4 ++-- examples/stm32wl/src/bin/lora_lorawan.rs | 5 +---- examples/stm32wl/src/bin/lora_p2p_receive.rs | 5 +---- examples/stm32wl/src/bin/lora_p2p_send.rs | 5 +---- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index ead8411ec..7390830dc 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -194,17 +194,17 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) } + #[cfg(stm32wl)] /// Useful for on chip peripherals like SUBGHZ which are hardwired. - #[allow(dead_code)] pub fn new_subghz( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - pclk3_freq: u32, ) -> Self { // see RM0453 rev 1 section 7.2.13 page 291 // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. + let pclk3_freq = ::frequency().0; let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); let mut config = Config::default(); config.mode = MODE_0; diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 467ba7604..644ce2959 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -11,8 +11,6 @@ use embassy_executor::Spawner; use embassy_lora::iv::Stm32wlInterfaceVariant; use embassy_lora::LoraTimer; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::peripherals::SUBGHZSPI; -use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::rng::Rng; use embassy_stm32::spi::Spi; use embassy_stm32::{interrupt, into_ref, pac, Peripheral}; @@ -36,8 +34,7 @@ async fn main(_spawner: Spawner) { unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } - let pclk3_freq = SUBGHZSPI::frequency().0; - let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2, pclk3_freq); + let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); let irq = interrupt::take!(SUBGHZ_RADIO); into_ref!(irq); diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs index cb0a8e349..81e9c7057 100644 --- a/examples/stm32wl/src/bin/lora_p2p_receive.rs +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs @@ -10,8 +10,6 @@ use defmt::info; use embassy_executor::Spawner; use embassy_lora::iv::Stm32wlInterfaceVariant; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::peripherals::SUBGHZSPI; -use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::spi::Spi; use embassy_stm32::{interrupt, into_ref, Peripheral}; use embassy_time::{Delay, Duration, Timer}; @@ -28,8 +26,7 @@ async fn main(_spawner: Spawner) { config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; let p = embassy_stm32::init(config); - let pclk3_freq = SUBGHZSPI::frequency().0; - let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2, pclk3_freq); + let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); let irq = interrupt::take!(SUBGHZ_RADIO); into_ref!(irq); diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs index f267f2ae6..263d4e670 100644 --- a/examples/stm32wl/src/bin/lora_p2p_send.rs +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -10,8 +10,6 @@ use defmt::info; use embassy_executor::Spawner; use embassy_lora::iv::Stm32wlInterfaceVariant; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::peripherals::SUBGHZSPI; -use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::spi::Spi; use embassy_stm32::{interrupt, into_ref, Peripheral}; use embassy_time::Delay; @@ -28,8 +26,7 @@ async fn main(_spawner: Spawner) { config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSE32; let p = embassy_stm32::init(config); - let pclk3_freq = SUBGHZSPI::frequency().0; - let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2, pclk3_freq); + let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); let irq = interrupt::take!(SUBGHZ_RADIO); into_ref!(irq); From 0d8d8d3320ad44eda53d4ac793fb7c9fed03b63a Mon Sep 17 00:00:00 2001 From: kbleeke Date: Wed, 3 May 2023 21:49:35 +0200 Subject: [PATCH 1053/1575] simple error handling for join instead of looping internally --- examples/rpi-pico-w/src/bin/tcp_server.rs | 11 ++++- src/control.rs | 50 +++++++++++++++-------- src/lib.rs | 2 +- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/examples/rpi-pico-w/src/bin/tcp_server.rs b/examples/rpi-pico-w/src/bin/tcp_server.rs index 036f79308..9581602a7 100644 --- a/examples/rpi-pico-w/src/bin/tcp_server.rs +++ b/examples/rpi-pico-w/src/bin/tcp_server.rs @@ -94,8 +94,15 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(net_task(stack))); - //control.join_open(env!("WIFI_NETWORK")).await; - control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; + loop { + //control.join_open(env!("WIFI_NETWORK")).await; + match control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await { + Ok(_) => break, + Err(err) => { + info!("join failed with status={}", err.status); + } + } + } // And now we can use it! diff --git a/src/control.rs b/src/control.rs index e1ad06e6b..3d7d4dd38 100644 --- a/src/control.rs +++ b/src/control.rs @@ -12,6 +12,11 @@ use crate::ioctl::{IoctlState, IoctlType}; use crate::structs::*; use crate::{countries, events, PowerManagementMode}; +#[derive(Debug)] +pub struct Error { + pub status: u32, +} + pub struct Control<'a> { state_ch: ch::StateRunner<'a>, events: &'a Events, @@ -145,7 +150,7 @@ impl<'a> Control<'a> { self.ioctl_set_u32(86, 0, mode_num).await; } - pub async fn join_open(&mut self, ssid: &str) { + pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { self.set_iovar_u32("ampdu_ba_wsize", 8).await; self.ioctl_set_u32(134, 0, 0).await; // wsec = open @@ -159,10 +164,10 @@ impl<'a> Control<'a> { }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.wait_for_join(i).await; + self.wait_for_join(i).await } - pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) { + pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> { self.set_iovar_u32("ampdu_ba_wsize", 8).await; self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 @@ -191,33 +196,42 @@ impl<'a> Control<'a> { }; i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - self.wait_for_join(i).await; + self.wait_for_join(i).await } - async fn wait_for_join(&mut self, i: SsidInfo) { - self.events.mask.enable(&[Event::JOIN, Event::AUTH]); + async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { + self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); let mut subscriber = self.events.queue.subscriber().unwrap(); // the actual join operation starts here // we make sure to enable events before so we don't miss any + + // set_ssid self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) .await; - // set_ssid - loop { + // to complete the join, we wait for a SET_SSID event + // we also save the AUTH status for the user, it may be interesting + let mut auth_status = 0; + let status = loop { let msg = subscriber.next_message_pure().await; if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { - // retry - warn!("JOIN failed with status={}", msg.header.status); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) - .await; - } else if msg.header.event_type == Event::JOIN && msg.header.status == EStatus::SUCCESS { - // successful join - break; + auth_status = msg.header.status; + } else if msg.header.event_type == Event::SET_SSID { + // join operation ends with SET_SSID event + break msg.header.status; } - } + }; + self.events.mask.disable_all(); - self.state_ch.set_link_state(LinkState::Up); - info!("JOINED"); + if status == EStatus::SUCCESS { + // successful join + self.state_ch.set_link_state(LinkState::Up); + info!("JOINED"); + Ok(()) + } else { + warn!("JOIN failed with status={} auth={}", status, auth_status); + Err(Error { status }) + } } pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { diff --git a/src/lib.rs b/src/lib.rs index d437a882e..4a9ada90f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ use ioctl::IoctlState; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; -pub use crate::control::Control; +pub use crate::control::{Control, Error as ControlError}; pub use crate::runner::Runner; pub use crate::structs::BssInfo; From 1cc61dc68a64398159214018296c6a6141e760c5 Mon Sep 17 00:00:00 2001 From: Marco Pastrello Date: Thu, 4 May 2023 21:32:37 +0200 Subject: [PATCH 1054/1575] Support PLLXTPRE switch. See figure 2. Clock tree page 12 DS5319 Rev 18 https://www.st.com/resource/en/datasheet/stm32f103cb.pdf --- embassy-stm32/src/rcc/f1.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index e667dbf90..1c14429fc 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -24,10 +24,15 @@ pub struct Config { pub pclk1: Option, pub pclk2: Option, pub adcclk: Option, + pub pllxtpre: Option, } pub(crate) unsafe fn init(config: Config) { - let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0 / 2); + let pllsrcclk = config.hse.map(|hse| hse.0 / match config.pllxtpre { + Some(b) => if b {2} else {1}, + None => {1}, + }).unwrap_or(HSI_FREQ.0 / 2); + let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); let pllmul = sysclk / pllsrcclk; @@ -143,6 +148,7 @@ pub(crate) unsafe fn init(config: Config) { } if let Some(pllmul_bits) = pllmul_bits { + RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(config.pllxtpre.is_some() as u8))); // enable PLL and wait for it to be ready RCC.cfgr().modify(|w| { w.set_pllmul(Pllmul(pllmul_bits)); From 4439031d4323a5d1e11af22887a32bb76cb953fb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 4 May 2023 22:39:37 +0200 Subject: [PATCH 1055/1575] lora: fix docs build. --- embassy-lora/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index e62ff431f..05b6fa2d0 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/" -features = ["stm32wl", "time", "defmt"] +features = ["stm32wl", "embassy-stm32?/stm32wl55jc-cm4", "embassy-stm32?/unstable-pac", "time", "defmt"] target = "thumbv7em-none-eabi" [features] From 5158014f3f77b20db34dd398633fc26e8e7d2e60 Mon Sep 17 00:00:00 2001 From: Marco Pastrello Date: Thu, 4 May 2023 22:59:52 +0200 Subject: [PATCH 1056/1575] PPLXTPRE is a bool. This flag for example permits the following clock tree configuration on stm32f103r8 let mut config = Config::default(); config.rcc.hse = Some(Hertz(16_000_000)); config.rcc.sys_ck = Some(Hertz(72_000_000)); config.rcc.pclk1 = Some(Hertz(36_000_000)); config.rcc.pclk2 = Some(Hertz(72_000_000)); config.rcc.pllxtpre = true; Init fails if pllxtpre is false. --- embassy-stm32/src/rcc/f1.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index 1c14429fc..620638abd 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -24,14 +24,11 @@ pub struct Config { pub pclk1: Option, pub pclk2: Option, pub adcclk: Option, - pub pllxtpre: Option, + pub pllxtpre: bool, } pub(crate) unsafe fn init(config: Config) { - let pllsrcclk = config.hse.map(|hse| hse.0 / match config.pllxtpre { - Some(b) => if b {2} else {1}, - None => {1}, - }).unwrap_or(HSI_FREQ.0 / 2); + let pllsrcclk = config.hse.map(|hse| hse.0 / if config.pllxtpre {2} else {1}).unwrap_or(HSI_FREQ.0 / 2); let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); let pllmul = sysclk / pllsrcclk; @@ -148,7 +145,7 @@ pub(crate) unsafe fn init(config: Config) { } if let Some(pllmul_bits) = pllmul_bits { - RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(config.pllxtpre.is_some() as u8))); + RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(if config.pllxtpre {1u8} else {0u8}))); // enable PLL and wait for it to be ready RCC.cfgr().modify(|w| { w.set_pllmul(Pllmul(pllmul_bits)); From 2dcbe75cca84cc44fc2357091aec20d2d9d5be00 Mon Sep 17 00:00:00 2001 From: Marco Pastrello Date: Thu, 4 May 2023 23:51:42 +0200 Subject: [PATCH 1057/1575] beautify --- embassy-stm32/src/rcc/f1.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index 620638abd..106acb097 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -28,7 +28,10 @@ pub struct Config { } pub(crate) unsafe fn init(config: Config) { - let pllsrcclk = config.hse.map(|hse| hse.0 / if config.pllxtpre {2} else {1}).unwrap_or(HSI_FREQ.0 / 2); + let pllsrcclk = config + .hse + .map(|hse| if config.pllxtpre { hse.0 / 2 } else { hse.0 }) + .unwrap_or(HSI_FREQ.0 / 2); let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); let pllmul = sysclk / pllsrcclk; @@ -145,7 +148,11 @@ pub(crate) unsafe fn init(config: Config) { } if let Some(pllmul_bits) = pllmul_bits { - RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(if config.pllxtpre {1u8} else {0u8}))); + { + let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 }; + RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(pllctpre_flag))); + } + // enable PLL and wait for it to be ready RCC.cfgr().modify(|w| { w.set_pllmul(Pllmul(pllmul_bits)); From c37f86ff1c507cc036b9754a00dea85d439a8369 Mon Sep 17 00:00:00 2001 From: Marco Pastrello Date: Fri, 5 May 2023 00:12:32 +0200 Subject: [PATCH 1058/1575] removes unecessary braces --- embassy-stm32/src/rcc/f1.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index 106acb097..3c374adf4 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -148,10 +148,8 @@ pub(crate) unsafe fn init(config: Config) { } if let Some(pllmul_bits) = pllmul_bits { - { - let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 }; - RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(pllctpre_flag))); - } + let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 }; + RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(pllctpre_flag))); // enable PLL and wait for it to be ready RCC.cfgr().modify(|w| { From db2bc8783e756d0e10838869603c844d8c276feb Mon Sep 17 00:00:00 2001 From: Marco Pastrello Date: Fri, 5 May 2023 19:04:58 +0200 Subject: [PATCH 1059/1575] Improve readability --- embassy-stm32/src/rcc/f1.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index 3c374adf4..4769b7059 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -28,10 +28,8 @@ pub struct Config { } pub(crate) unsafe fn init(config: Config) { - let pllsrcclk = config - .hse - .map(|hse| if config.pllxtpre { hse.0 / 2 } else { hse.0 }) - .unwrap_or(HSI_FREQ.0 / 2); + let pllxtpre_div = if config.pllxtpre { 2 } else { 1 }; + let pllsrcclk = config.hse.map(|hse| hse.0 / pllxtpre_div).unwrap_or(HSI_FREQ.0 / 2); let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk); let pllmul = sysclk / pllsrcclk; From 8ebe6e5f2029026594c703820c11d703da2c0334 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 17:16:35 +0200 Subject: [PATCH 1060/1575] rp/pio: drop Pio prefix from almost all names it's only any good for PioPin because there it follows a pattern of gpio pin alternate functions being named like that, everything else can just as well be referred to as `pio::Thing` --- embassy-rp/src/pio.rs | 158 ++++++++++++++--------------- embassy-rp/src/pio_instr_util.rs | 20 ++-- examples/rp/src/bin/pio_async.rs | 14 +-- examples/rp/src/bin/pio_hd44780.rs | 4 +- examples/rp/src/bin/ws2812-pio.rs | 10 +- 5 files changed, 103 insertions(+), 103 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 2cf4761a5..31273b5d8 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -96,18 +96,18 @@ pub(crate) unsafe fn init() { /// Future that waits for TX-FIFO to become writable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoOutFuture<'a, 'd, PIO: PioInstance, const SM: usize> { - sm_tx: &'a mut PioStateMachineTx<'d, PIO, SM>, +pub struct FifoOutFuture<'a, 'd, PIO: Instance, const SM: usize> { + sm_tx: &'a mut StateMachineTx<'d, PIO, SM>, value: u32, } -impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { - pub fn new(sm: &'a mut PioStateMachineTx<'d, PIO, SM>, value: u32) -> Self { +impl<'a, 'd, PIO: Instance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { + pub fn new(sm: &'a mut StateMachineTx<'d, PIO, SM>, value: u32) -> Self { FifoOutFuture { sm_tx: sm, value } } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> { type Output = (); fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -127,7 +127,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -139,17 +139,17 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, P /// Future that waits for RX-FIFO to become readable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoInFuture<'a, 'd, PIO: PioInstance, const SM: usize> { - sm_rx: &'a mut PioStateMachineRx<'d, PIO, SM>, +pub struct FifoInFuture<'a, 'd, PIO: Instance, const SM: usize> { + sm_rx: &'a mut StateMachineRx<'d, PIO, SM>, } -impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { - pub fn new(sm: &'a mut PioStateMachineRx<'d, PIO, SM>) -> Self { +impl<'a, 'd, PIO: Instance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { + pub fn new(sm: &'a mut StateMachineRx<'d, PIO, SM>) -> Self { FifoInFuture { sm_rx: sm } } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> { type Output = u32; fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -168,7 +168,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -180,12 +180,12 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PI /// Future that waits for IRQ #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct IrqFuture<'a, 'd, PIO: PioInstance> { - pio: PhantomData<&'a PioIrq<'d, PIO, 0>>, +pub struct IrqFuture<'a, 'd, PIO: Instance> { + pio: PhantomData<&'a Irq<'d, PIO, 0>>, irq_no: u8, } -impl<'a, 'd, PIO: PioInstance> Future for IrqFuture<'a, 'd, PIO> { +impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { type Output = (); fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -215,7 +215,7 @@ impl<'a, 'd, PIO: PioInstance> Future for IrqFuture<'a, 'd, PIO> { } } -impl<'a, 'd, PIO: PioInstance> Drop for IrqFuture<'a, 'd, PIO> { +impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -225,12 +225,12 @@ impl<'a, 'd, PIO: PioInstance> Drop for IrqFuture<'a, 'd, PIO> { } } -pub struct Pin<'l, PIO: PioInstance> { +pub struct Pin<'l, PIO: Instance> { pin: PeripheralRef<'l, AnyPin>, pio: PhantomData, } -impl<'l, PIO: PioInstance> Pin<'l, PIO> { +impl<'l, PIO: Instance> Pin<'l, PIO> { /// Set the pin's drive strength. #[inline] pub fn set_drive_strength(&mut self, strength: Drive) { @@ -293,11 +293,11 @@ impl<'l, PIO: PioInstance> Pin<'l, PIO> { } } -pub struct PioStateMachineRx<'d, PIO: PioInstance, const SM: usize> { +pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { +impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { pub fn empty(&self) -> bool { unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } } @@ -370,11 +370,11 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { } } -pub struct PioStateMachineTx<'d, PIO: PioInstance, const SM: usize> { +pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { +impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { pub fn empty(&self) -> bool { unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } } @@ -445,12 +445,12 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { } } -pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> { - rx: PioStateMachineRx<'d, PIO, SM>, - tx: PioStateMachineTx<'d, PIO, SM>, +pub struct StateMachine<'d, PIO: Instance, const SM: usize> { + rx: StateMachineRx<'d, PIO, SM>, + tx: StateMachineTx<'d, PIO, SM>, } -impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM> { +impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); @@ -459,7 +459,7 @@ impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM } } -impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { +impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { PIO::PIO.sm(SM) @@ -771,35 +771,35 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { } } - pub fn rx(&mut self) -> &mut PioStateMachineRx<'d, PIO, SM> { + pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> { &mut self.rx } - pub fn tx(&mut self) -> &mut PioStateMachineTx<'d, PIO, SM> { + pub fn tx(&mut self) -> &mut StateMachineTx<'d, PIO, SM> { &mut self.tx } - pub fn rx_tx(&mut self) -> (&mut PioStateMachineRx<'d, PIO, SM>, &mut PioStateMachineTx<'d, PIO, SM>) { + pub fn rx_tx(&mut self) -> (&mut StateMachineRx<'d, PIO, SM>, &mut StateMachineTx<'d, PIO, SM>) { (&mut self.rx, &mut self.tx) } } -pub struct PioCommon<'d, PIO: PioInstance> { +pub struct Common<'d, PIO: Instance> { instructions_used: u32, pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> Drop for PioCommon<'d, PIO> { +impl<'d, PIO: Instance> Drop for Common<'d, PIO> { fn drop(&mut self) { on_pio_drop::(); } } -pub struct PioInstanceMemory<'d, PIO: PioInstance> { +pub struct InstanceMemory<'d, PIO: Instance> { used_mask: u32, pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { - pub fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, PIO> +impl<'d, PIO: Instance> Common<'d, PIO> { + pub fn write_instr(&mut self, start: usize, instrs: I) -> InstanceMemory<'d, PIO> where I: Iterator, { @@ -820,16 +820,16 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { used_mask |= mask; } self.instructions_used |= used_mask; - PioInstanceMemory { + InstanceMemory { used_mask, pio: PhantomData, } } - /// Free instruction memory previously allocated with [`PioCommon::write_instr`]. + /// Free instruction memory previously allocated with [`Common::write_instr`]. /// This is always possible but unsafe if any state machine is still using this /// bit of memory. - pub unsafe fn free_instr(&mut self, instrs: PioInstanceMemory) { + pub unsafe fn free_instr(&mut self, instrs: InstanceMemory) { self.instructions_used &= !instrs.used_mask; } @@ -848,8 +848,8 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } /// Register a pin for PIO usage. Pins will be released from the PIO block - /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* - /// all [`PioStateMachine`]s for this block have been dropped. **Other members + /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`Common`] *and* + /// all [`StateMachine`]s for this block have been dropped. **Other members /// of [`Pio`] do not keep pin registrations alive.** pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); @@ -865,11 +865,11 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } } -pub struct PioIrq<'d, PIO: PioInstance, const N: usize> { +pub struct Irq<'d, PIO: Instance, const N: usize> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { +impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> { pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> { IrqFuture { pio: PhantomData, @@ -879,11 +879,11 @@ impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { } #[derive(Clone)] -pub struct PioIrqFlags<'d, PIO: PioInstance> { +pub struct IrqFlags<'d, PIO: Instance> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> PioIrqFlags<'d, PIO> { +impl<'d, PIO: Instance> IrqFlags<'d, PIO> { pub fn check(&self, irq_no: u8) -> bool { assert!(irq_no < 8); self.check_any(1 << irq_no) @@ -916,48 +916,48 @@ impl<'d, PIO: PioInstance> PioIrqFlags<'d, PIO> { } } -pub struct Pio<'d, PIO: PioInstance> { - pub common: PioCommon<'d, PIO>, - pub irq_flags: PioIrqFlags<'d, PIO>, - pub irq0: PioIrq<'d, PIO, 0>, - pub irq1: PioIrq<'d, PIO, 1>, - pub irq2: PioIrq<'d, PIO, 2>, - pub irq3: PioIrq<'d, PIO, 3>, - pub sm0: PioStateMachine<'d, PIO, 0>, - pub sm1: PioStateMachine<'d, PIO, 1>, - pub sm2: PioStateMachine<'d, PIO, 2>, - pub sm3: PioStateMachine<'d, PIO, 3>, +pub struct Pio<'d, PIO: Instance> { + pub common: Common<'d, PIO>, + pub irq_flags: IrqFlags<'d, PIO>, + pub irq0: Irq<'d, PIO, 0>, + pub irq1: Irq<'d, PIO, 1>, + pub irq2: Irq<'d, PIO, 2>, + pub irq3: Irq<'d, PIO, 3>, + pub sm0: StateMachine<'d, PIO, 0>, + pub sm1: StateMachine<'d, PIO, 1>, + pub sm2: StateMachine<'d, PIO, 2>, + pub sm3: StateMachine<'d, PIO, 3>, } -impl<'d, PIO: PioInstance> Pio<'d, PIO> { +impl<'d, PIO: Instance> Pio<'d, PIO> { pub fn new(_pio: impl Peripheral

+ 'd) -> Self { PIO::state().users.store(5, Ordering::Release); PIO::state().used_pins.store(0, Ordering::Release); Self { - common: PioCommon { + common: Common { instructions_used: 0, pio: PhantomData, }, - irq_flags: PioIrqFlags { pio: PhantomData }, - irq0: PioIrq { pio: PhantomData }, - irq1: PioIrq { pio: PhantomData }, - irq2: PioIrq { pio: PhantomData }, - irq3: PioIrq { pio: PhantomData }, - sm0: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + irq_flags: IrqFlags { pio: PhantomData }, + irq0: Irq { pio: PhantomData }, + irq1: Irq { pio: PhantomData }, + irq2: Irq { pio: PhantomData }, + irq3: Irq { pio: PhantomData }, + sm0: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, - sm1: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + sm1: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, - sm2: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + sm2: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, - sm3: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + sm3: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, } } @@ -974,7 +974,7 @@ pub struct State { used_pins: AtomicU32, } -fn on_pio_drop() { +fn on_pio_drop() { let state = PIO::state(); if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { let used_pins = state.used_pins.load(Ordering::Relaxed); @@ -994,7 +994,7 @@ mod sealed { pub trait PioPin {} - pub trait PioInstance { + pub trait Instance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; @@ -1011,16 +1011,16 @@ mod sealed { } } -pub trait PioInstance: sealed::PioInstance + Sized + Unpin {} +pub trait Instance: sealed::Instance + Sized + Unpin {} macro_rules! impl_pio { ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { - impl sealed::PioInstance for peripherals::$name { + impl sealed::Instance for peripherals::$name { const PIO_NO: u8 = $pio; const PIO: &'static pac::pio::Pio = &pac::$pac; const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; } - impl PioInstance for peripherals::$name {} + impl Instance for peripherals::$name {} }; } diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index 81abdb666..e425cf092 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs @@ -1,8 +1,8 @@ use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; -use crate::pio::{PioInstance, PioStateMachine}; +use crate::pio::{Instance, StateMachine}; -pub fn set_x(sm: &mut PioStateMachine, value: u32) { +pub fn set_x(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::X, bit_count: 32, @@ -12,7 +12,7 @@ pub fn set_x(sm: &mut PioStateMachine(sm: &mut PioStateMachine) -> u32 { +pub fn get_x(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::X, bit_count: 32, @@ -22,7 +22,7 @@ pub fn get_x(sm: &mut PioStateMachine(sm: &mut PioStateMachine, value: u32) { +pub fn set_y(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::Y, bit_count: 32, @@ -32,7 +32,7 @@ pub fn set_y(sm: &mut PioStateMachine(sm: &mut PioStateMachine) -> u32 { +pub fn get_y(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::Y, bit_count: 32, @@ -43,7 +43,7 @@ pub fn get_y(sm: &mut PioStateMachine(sm: &mut PioStateMachine, data: u8) { +pub fn set_pindir(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINDIRS, data, @@ -52,7 +52,7 @@ pub fn set_pindir(sm: &mut PioStateMachine(sm: &mut PioStateMachine, data: u8) { +pub fn set_pin(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINS, data, @@ -61,7 +61,7 @@ pub fn set_pin(sm: &mut PioStateMachine(sm: &mut PioStateMachine, data: u32) { +pub fn set_out_pin(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINS, bit_count: 32, @@ -70,7 +70,7 @@ pub fn set_out_pin(sm: &mut PioStateMachine

(sm: &mut PioStateMachine, data: u32) { +pub fn set_out_pindir(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINDIRS, bit_count: 32, @@ -80,7 +80,7 @@ pub fn set_out_pindir(sm: &mut PioStateMachin sm.exec_instr(OUT); } -pub fn exec_jmp(sm: &mut PioStateMachine, to_addr: u8) { +pub fn exec_jmp(sm: &mut StateMachine, to_addr: u8) { let jmp: u16 = InstructionOperands::JMP { address: to_addr, condition: JmpCondition::Always, diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 4e0ab5e3c..461ea3ff9 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,12 +4,12 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Pio, PioCommon, PioIrq, PioPin, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Common, Irq, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachine, pin: impl PioPin) { +fn setup_pio_task_sm0(pio: &mut Common, sm: &mut StateMachine, pin: impl PioPin) { // Setup sm0 // Send data serially to pin @@ -37,7 +37,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachine) { +async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -48,7 +48,7 @@ async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) { } } -fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachine) { +fn setup_pio_task_sm1(pio: &mut Common, sm: &mut StateMachine) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -67,7 +67,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachine) { +async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { sm.set_enable(true); loop { let rx = sm.rx().wait_pull().await; @@ -75,7 +75,7 @@ async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) { } } -fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachine) { +fn setup_pio_task_sm2(pio: &mut Common, sm: &mut StateMachine) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -99,7 +99,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachine, mut sm: PioStateMachine<'static, PIO0, 2>) { +async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) { sm.set_enable(true); loop { irq.wait().await; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index f76d334e7..17b2440cf 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -7,7 +7,7 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioPin, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; @@ -64,7 +64,7 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachine<'l, PIO0, 0>, + sm: StateMachine<'l, PIO0, 0>, buf: [u8; 40], } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index c9c701a70..2e6860d8b 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -4,18 +4,18 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::pio::{FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Common, FifoJoin, Instance, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812<'d, P: PioInstance, const S: usize> { - sm: PioStateMachine<'d, P, S>, +pub struct Ws2812<'d, P: Instance, const S: usize> { + sm: StateMachine<'d, P, S>, } -impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { - pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachine<'d, P, S>, pin: impl PioPin) -> Self { +impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { + pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self { // Setup sm0 // prepare the PIO program From 09f078a1cc33781a01012ebc43e9d20cff53e2a3 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 4 May 2023 10:34:20 +0200 Subject: [PATCH 1061/1575] rp/pio: remove critical section in IrqFuture::poll there's nothing this critical section protects against. both read and write-to-clear are atomic and don't interfere with other irq futures, only potentially with setting/clearing an irq flag from an arm core. neither have ever been synchronized, and both have the same observable effects under atomic writes and critical sections. (for both setting and clearing an irq flag observable differences could only happen if the set/clear happened after the poll read, but before the write. if it's a clear we observe the same effects as sequencing the clear entirely after the poll, and if it's a set we observe the same effects as sequencing the set entirely before the poll) --- embassy-rp/src/pio.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 31273b5d8..c2c144c00 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -191,17 +191,10 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { //debug!("Poll {},{}", PIO::PIO_NO, SM); // Check if IRQ flag is already set - if critical_section::with(|_| unsafe { - let irq_flags = PIO::PIO.irq(); - if irq_flags.read().0 & (1 << self.irq_no) != 0 { - irq_flags.write(|m| { - m.0 = 1 << self.irq_no; - }); - true - } else { - false + if unsafe { PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 } { + unsafe { + PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no); } - }) { return Poll::Ready(()); } From ed843b519b1cfdcc69f9022e3e14fbb9f62972bb Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 4 May 2023 11:14:21 +0200 Subject: [PATCH 1062/1575] rp/pio: tighten variance of particle structs all of these exist in 1:1 correspondence to their parent hal objects, so let's make all of their lifetimes invariant. --- embassy-rp/src/pio.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index c2c144c00..d2eedc72a 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -181,7 +181,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, /// Future that waits for IRQ #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct IrqFuture<'a, 'd, PIO: Instance> { - pio: PhantomData<&'a Irq<'d, PIO, 0>>, + pio: PhantomData<&'a mut Irq<'d, PIO, 0>>, irq_no: u8, } @@ -287,7 +287,7 @@ impl<'l, PIO: Instance> Pin<'l, PIO> { } pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { @@ -364,7 +364,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { } pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { @@ -777,7 +777,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { pub struct Common<'d, PIO: Instance> { instructions_used: u32, - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> Drop for Common<'d, PIO> { @@ -788,7 +788,7 @@ impl<'d, PIO: Instance> Drop for Common<'d, PIO> { pub struct InstanceMemory<'d, PIO: Instance> { used_mask: u32, - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> Common<'d, PIO> { @@ -859,7 +859,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { } pub struct Irq<'d, PIO: Instance, const N: usize> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> { @@ -873,7 +873,7 @@ impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> { #[derive(Clone)] pub struct IrqFlags<'d, PIO: Instance> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> IrqFlags<'d, PIO> { @@ -920,6 +920,7 @@ pub struct Pio<'d, PIO: Instance> { pub sm1: StateMachine<'d, PIO, 1>, pub sm2: StateMachine<'d, PIO, 2>, pub sm3: StateMachine<'d, PIO, 3>, + _pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> Pio<'d, PIO> { @@ -952,6 +953,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { rx: StateMachineRx { pio: PhantomData }, tx: StateMachineTx { pio: PhantomData }, }, + _pio: PhantomData, } } } From 5f7ef8bed0d665c2f59351194fbc155203321d24 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 4 May 2023 11:55:11 +0200 Subject: [PATCH 1063/1575] rp/pio: only clear diag bits if they're set otherwise we may lose a bit being raised after it was read, but before it was cleared. --- embassy-rp/src/pio.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index d2eedc72a..ea6814fb8 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -307,7 +307,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().rxstall() & (1 << SM) != 0; - fdebug.write(|w| w.set_rxstall(1 << SM)); + if ret { + fdebug.write(|w| w.set_rxstall(1 << SM)); + } ret } } @@ -316,7 +318,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().rxunder() & (1 << SM) != 0; - fdebug.write(|w| w.set_rxunder(1 << SM)); + if ret { + fdebug.write(|w| w.set_rxunder(1 << SM)); + } ret } } @@ -383,7 +387,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().txstall() & (1 << SM) != 0; - fdebug.write(|w| w.set_txstall(1 << SM)); + if ret { + fdebug.write(|w| w.set_txstall(1 << SM)); + } ret } } @@ -392,7 +398,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().txover() & (1 << SM) != 0; - fdebug.write(|w| w.set_txover(1 << SM)); + if ret { + fdebug.write(|w| w.set_txover(1 << SM)); + } ret } } From 41ec4170a5ae9920fe31327252ba1bba754b6d9f Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 5 May 2023 19:49:34 +0200 Subject: [PATCH 1064/1575] rp/pio: add load_program, use_program programs contain information we could pull from them directly and use to validate other configuration of the state machine instead of asking the user to pull them out and hand them to us bit by bit. unfortunately programs do not specify how many in or out bits they use, so we can only handle side-set and wrapping jumps like this. it's still something though. --- embassy-rp/src/pio.rs | 128 ++++++++++++++--------------- examples/rp/src/bin/pio_async.rs | 25 ++---- examples/rp/src/bin/pio_dma.rs | 7 +- examples/rp/src/bin/pio_hd44780.rs | 16 +--- examples/rp/src/bin/ws2812-pio.rs | 13 +-- 5 files changed, 74 insertions(+), 115 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index ea6814fb8..f835fc8d9 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -9,12 +9,14 @@ use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::io::vals::Gpio0ctrlFuncsel; +use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{self, AnyPin, Drive, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; -use crate::{interrupt, pac, peripherals, RegExt}; +use crate::relocate::RelocatedProgram; +use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; struct Wakers([AtomicWaker; 12]); @@ -460,6 +462,13 @@ impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { } } +fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) { + for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) { + // purposely does not allow wrap-around because we can't claim pins 30 and 31. + assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive"); + } +} + impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { @@ -504,26 +513,26 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - pub fn set_side_enable(&self, enable: bool) { + /// Configures this state machine to use the given program, including jumping to the origin + /// of the program. The state machine is not started. + pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { + assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); + assert_consecutive(side_set); unsafe { - Self::this_sm().execctrl().modify(|w| w.set_side_en(enable)); + Self::this_sm().execctrl().modify(|w| { + w.set_side_en(prog.side_set.optional()); + w.set_side_pindir(prog.side_set.pindirs()); + w.set_wrap_bottom(prog.wrap.target); + w.set_wrap_top(prog.wrap.source); + }); + Self::this_sm().pinctrl().modify(|w| { + w.set_sideset_count(prog.side_set.bits()); + w.set_sideset_base(side_set.first().map_or(0, |p| p.pin())); + }); + pio_instr_util::exec_jmp(self, prog.origin); } } - pub fn is_side_enabled(&self) -> bool { - unsafe { Self::this_sm().execctrl().read().side_en() } - } - - pub fn set_side_pindir(&mut self, pindir: bool) { - unsafe { - Self::this_sm().execctrl().modify(|w| w.set_side_pindir(pindir)); - } - } - - pub fn is_side_pindir(&self) -> bool { - unsafe { Self::this_sm().execctrl().read().side_pindir() } - } - pub fn set_jmp_pin(&mut self, pin: u8) { unsafe { Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); @@ -534,23 +543,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { unsafe { Self::this_sm().execctrl().read().jmp_pin() } } - pub fn set_wrap(&self, source: u8, target: u8) { - unsafe { - Self::this_sm().execctrl().modify(|w| { - w.set_wrap_top(source); - w.set_wrap_bottom(target) - }); - } - } - - /// Get wrapping addresses. Returns (source, target). - pub fn get_wrap(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().execctrl().read(); - (r.wrap_top(), r.wrap_bottom()) - } - } - pub fn set_fifo_join(&mut self, join: FifoJoin) { let (rx, tx) = match join { FifoJoin::Duplex => (false, false), @@ -667,28 +659,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { pub fn get_addr(&self) -> u8 { unsafe { Self::this_sm().addr().read().addr() } } - pub fn set_sideset_count(&mut self, count: u8) { - unsafe { - Self::this_sm().pinctrl().modify(|w| w.set_sideset_count(count)); - } - } - - pub fn get_sideset_count(&self) -> u8 { - unsafe { Self::this_sm().pinctrl().read().sideset_count() } - } - - pub fn set_sideset_base_pin(&mut self, base_pin: &Pin) { - unsafe { - Self::this_sm().pinctrl().modify(|w| w.set_sideset_base(base_pin.pin())); - } - } - - pub fn get_sideset_base(&self) -> u8 { - unsafe { - let r = Self::this_sm().pinctrl().read(); - r.sideset_base() - } - } /// Set the range of out pins affected by a set instruction. pub fn set_set_range(&mut self, base: u8, count: u8) { @@ -799,8 +769,35 @@ pub struct InstanceMemory<'d, PIO: Instance> { pio: PhantomData<&'d mut PIO>, } +pub struct LoadedProgram<'d, PIO: Instance> { + pub used_memory: InstanceMemory<'d, PIO>, + origin: u8, + wrap: Wrap, + side_set: SideSet, +} + impl<'d, PIO: Instance> Common<'d, PIO> { - pub fn write_instr(&mut self, start: usize, instrs: I) -> InstanceMemory<'d, PIO> + pub fn load_program(&mut self, prog: &RelocatedProgram) -> LoadedProgram<'d, PIO> { + match self.try_load_program(prog) { + Ok(r) => r, + Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at), + } + } + + pub fn try_load_program( + &mut self, + prog: &RelocatedProgram, + ) -> Result, usize> { + let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?; + Ok(LoadedProgram { + used_memory, + origin: prog.origin(), + wrap: prog.wrap(), + side_set: prog.side_set(), + }) + } + + pub fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> where I: Iterator, { @@ -808,11 +805,9 @@ impl<'d, PIO: Instance> Common<'d, PIO> { for (i, instr) in instrs.enumerate() { let addr = (i + start) as u8; let mask = 1 << (addr as usize); - assert!( - self.instructions_used & mask == 0, - "Trying to write already used PIO instruction memory at {}", - addr - ); + if self.instructions_used & mask != 0 { + return Err(addr as usize); + } unsafe { PIO::PIO.instr_mem(addr as usize).write(|w| { w.set_instr_mem(instr); @@ -821,15 +816,14 @@ impl<'d, PIO: Instance> Common<'d, PIO> { used_mask |= mask; } self.instructions_used |= used_mask; - InstanceMemory { + Ok(InstanceMemory { used_mask, pio: PhantomData, - } + }) } - /// Free instruction memory previously allocated with [`Common::write_instr`]. - /// This is always possible but unsafe if any state machine is still using this - /// bit of memory. + /// Free instruction memory. This is always possible but unsafe if any + /// state machine is still using this bit of memory. pub unsafe fn free_instr(&mut self, instrs: InstanceMemory) { self.instructions_used &= !instrs.used_mask; } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 461ea3ff9..9f47c2316 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -5,11 +5,10 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Irq, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut Common, sm: &mut StateMachine, pin: impl PioPin) { +fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { // Setup sm0 // Send data serially to pin @@ -22,15 +21,12 @@ fn setup_pio_task_sm0(pio: &mut Common, sm: &mut StateMachine, pi ); let relocated = RelocatedProgram::new(&prg.program); + sm.use_program(&pio.load_program(&relocated), &[]); let out_pin = pio.make_pio_pin(pin); let pio_pins = [&out_pin]; sm.set_out_pins(&pio_pins); - pio.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(sm, relocated.origin()); sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); sm.set_set_range(0, 1); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); sm.set_autopull(true); sm.set_out_shift_dir(ShiftDirection::Left); @@ -48,20 +44,16 @@ async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { } } -fn setup_pio_task_sm1(pio: &mut Common, sm: &mut StateMachine) { +fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); let relocated = RelocatedProgram::new(&prg.program); - pio.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(sm, relocated.origin()); + sm.use_program(&pio.load_program(&relocated), &[]); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); sm.set_set_range(0, 0); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); - sm.set_autopush(true); sm.set_in_shift_dir(ShiftDirection::Right); } @@ -75,7 +67,7 @@ async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { } } -fn setup_pio_task_sm2(pio: &mut Common, sm: &mut StateMachine) { +fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -89,12 +81,7 @@ fn setup_pio_task_sm2(pio: &mut Common, sm: &mut StateMachine) { ".wrap", ); let relocated = RelocatedProgram::new(&prg.program); - pio.write_instr(relocated.origin() as usize, relocated.code()); - - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); - - pio_instr_util::exec_jmp(sm, relocated.origin()); + sm.use_program(&pio.load_program(&relocated), &[]); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); } diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index c664482e5..1c4e127c7 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::pio::{Pio, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::{pio_instr_util, Peripheral}; +use embassy_rp::Peripheral; use {defmt_rtt as _, panic_probe as _}; fn swap_nibbles(v: u32) -> u32 { @@ -38,11 +38,8 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - common.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(&mut sm, relocated.origin()); + sm.use_program(&common.load_program(&relocated), &[]); sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); sm.set_autopull(true); sm.set_autopush(true); sm.set_pull_threshold(32); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 17b2440cf..c3466b554 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -123,15 +123,9 @@ impl<'l> HD44780<'l> { embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111); let relocated = RelocatedProgram::new(&prg.program); - common.write_instr(relocated.origin() as usize, relocated.code()); - embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); + sm0.use_program(&common.load_program(&relocated), &[&e]); sm0.set_clkdiv(125 * 256); - let pio::Wrap { source, target } = relocated.wrap(); - sm0.set_wrap(source, target); - sm0.set_side_enable(true); sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_sideset_base_pin(&e); - sm0.set_sideset_count(2); sm0.set_out_shift_dir(ShiftDirection::Left); sm0.set_fifo_join(FifoJoin::TxOnly); sm0.set_autopull(true); @@ -199,17 +193,11 @@ impl<'l> HD44780<'l> { ); let relocated = RelocatedProgram::new(&prg.program); - common.write_instr(relocated.origin() as usize, relocated.code()); - embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); - let pio::Wrap { source, target } = relocated.wrap(); + sm0.use_program(&common.load_program(&relocated), &[&e]); sm0.set_clkdiv(8 * 256); // ~64ns/insn - sm0.set_side_enable(false); sm0.set_jmp_pin(db7pin); - sm0.set_wrap(source, target); sm0.set_set_pins(&[&rs, &rw]); sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_sideset_base_pin(&e); - sm0.set_sideset_count(1); sm0.set_out_shift_dir(ShiftDirection::Left); sm0.set_fifo_join(FifoJoin::TxOnly); diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 2e6860d8b..889970541 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::pio::{Common, FifoJoin, Instance, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; @@ -45,15 +44,11 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { let prg = a.assemble_with_wrap(wrap_source, wrap_target); - let relocated = RelocatedProgram::new(&prg); - pio.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(&mut sm, relocated.origin()); - // Pin config let out_pin = pio.make_pio_pin(pin); - sm.set_set_pins(&[&out_pin]); - sm.set_sideset_base_pin(&out_pin); - sm.set_sideset_count(1); + + let relocated = RelocatedProgram::new(&prg); + sm.use_program(&pio.load_program(&relocated), &[&out_pin]); // Clock config // TODO CLOCK_FREQ should come from embassy_rp @@ -70,8 +65,6 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { } sm.set_clkdiv((int << 8) | frac); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); // FIFO config sm.set_autopull(true); From 37b460637df0a20885ba8a0fbb0699e2d44ee4ec Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 5 May 2023 20:45:02 +0200 Subject: [PATCH 1065/1575] rp/pio: add set-pin-{values,dirs} convenience functions these are needed a lot during state machine setup, it makes sense to provide convenience functions for them. --- embassy-rp/src/pio.rs | 60 +++++++++++++++++++++++++++++- examples/rp/src/bin/pio_hd44780.rs | 9 +---- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index f835fc8d9..8d0547907 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -13,7 +13,7 @@ use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; -use crate::gpio::{self, AnyPin, Drive, Pull, SlewRate}; +use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; use crate::relocate::RelocatedProgram; use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; @@ -54,6 +54,14 @@ pub enum ShiftDirection { Left = 0, } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum Direction { + In = 0, + Out = 1, +} + const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; @@ -533,6 +541,56 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } + fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { + let enabled = self.is_enabled(); + self.set_enable(false); + let pincfg = unsafe { Self::this_sm().pinctrl().read() }; + let execcfg = unsafe { Self::this_sm().execctrl().read() }; + unsafe { + Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true)); + } + f(self); + unsafe { + Self::this_sm().pinctrl().write_value(pincfg); + Self::this_sm().execctrl().write_value(execcfg); + } + self.set_enable(enabled); + } + + /// Sets pin directions. This pauses the current state machine to run `SET` commands + /// and temporarily unsets the `OUT_STICKY` bit. + pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { + self.with_paused(|sm| { + for pin in pins { + unsafe { + Self::this_sm().pinctrl().write(|w| { + w.set_set_base(pin.pin()); + w.set_set_count(1); + }); + // SET PINDIRS, (dir) + sm.exec_instr(0b111_00000_100_00000 | dir as u16); + } + } + }); + } + + /// Sets pin output values. This pauses the current state machine to run + /// `SET` commands and temporarily unsets the `OUT_STICKY` bit. + pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) { + self.with_paused(|sm| { + for pin in pins { + unsafe { + Self::this_sm().pinctrl().write(|w| { + w.set_set_base(pin.pin()); + w.set_set_count(1); + }); + // SET PINS, (dir) + sm.exec_instr(0b111_00000_000_00000 | level as u16); + } + } + }); + } + pub fn set_jmp_pin(&mut self, pin: u8) { unsafe { Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index c3466b554..40dee1c4d 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -7,7 +7,7 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Direction, FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; @@ -115,12 +115,7 @@ impl<'l> HD44780<'l> { let db6 = common.make_pio_pin(db6); let db7 = common.make_pio_pin(db7); - sm0.set_set_pins(&[&rs, &rw]); - embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11); - sm0.set_set_pins(&[&e]); - embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b1); - sm0.set_set_pins(&[&db4, &db5, &db6, &db7]); - embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111); + sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); let relocated = RelocatedProgram::new(&prg.program); sm0.use_program(&common.load_program(&relocated), &[&e]); From 2873cb93ee3111d984c35287ea9d98f1218d1024 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 5 May 2023 20:46:10 +0200 Subject: [PATCH 1066/1575] rp/pio: mark pio_instr_util unsafe none of these are safe. the x/y functions mangle the fifos, the set functions require the state machine to be stopped to be in any way safe, the out functions do both of those things at once. only the jump instruction is marginally safe, but running this on an active program is bound to cause problems. --- embassy-rp/src/pio_instr_util.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index e425cf092..25393b476 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs @@ -2,7 +2,7 @@ use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestin use crate::pio::{Instance, StateMachine}; -pub fn set_x(sm: &mut StateMachine, value: u32) { +pub unsafe fn set_x(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::X, bit_count: 32, @@ -12,7 +12,7 @@ pub fn set_x(sm: &mut StateMachine, val sm.exec_instr(OUT); } -pub fn get_x(sm: &mut StateMachine) -> u32 { +pub unsafe fn get_x(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::X, bit_count: 32, @@ -22,7 +22,7 @@ pub fn get_x(sm: &mut StateMachine) -> sm.rx().pull() } -pub fn set_y(sm: &mut StateMachine, value: u32) { +pub unsafe fn set_y(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::Y, bit_count: 32, @@ -32,7 +32,7 @@ pub fn set_y(sm: &mut StateMachine, val sm.exec_instr(OUT); } -pub fn get_y(sm: &mut StateMachine) -> u32 { +pub unsafe fn get_y(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::Y, bit_count: 32, @@ -43,7 +43,7 @@ pub fn get_y(sm: &mut StateMachine) -> sm.rx().pull() } -pub fn set_pindir(sm: &mut StateMachine, data: u8) { +pub unsafe fn set_pindir(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINDIRS, data, @@ -52,7 +52,7 @@ pub fn set_pindir(sm: &mut StateMachine sm.exec_instr(set); } -pub fn set_pin(sm: &mut StateMachine, data: u8) { +pub unsafe fn set_pin(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINS, data, @@ -61,7 +61,7 @@ pub fn set_pin(sm: &mut StateMachine, d sm.exec_instr(set); } -pub fn set_out_pin(sm: &mut StateMachine, data: u32) { +pub unsafe fn set_out_pin(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINS, bit_count: 32, @@ -70,7 +70,7 @@ pub fn set_out_pin(sm: &mut StateMachine(sm: &mut StateMachine, data: u32) { +pub unsafe fn set_out_pindir(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINDIRS, bit_count: 32, @@ -80,7 +80,7 @@ pub fn set_out_pindir(sm: &mut StateMachine(sm: &mut StateMachine, to_addr: u8) { +pub unsafe fn exec_jmp(sm: &mut StateMachine, to_addr: u8) { let jmp: u16 = InstructionOperands::JMP { address: to_addr, condition: JmpCondition::Always, From 8e4d65e163bd484efc4fb31d20b14e6ac4a88de7 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 11:36:07 +0200 Subject: [PATCH 1067/1575] rp/pio: configure state machines with Config struct the many individual sets aren't very efficient, and almost no checks were done to ensure that the configuration written to the hardware was actually valid. this adresses both of these. --- embassy-rp/src/pio.rs | 438 ++++++++++++++--------------- examples/rp/Cargo.toml | 2 + examples/rp/src/bin/pio_async.rs | 37 +-- examples/rp/src/bin/pio_dma.rs | 27 +- examples/rp/src/bin/pio_hd44780.rs | 42 +-- examples/rp/src/bin/ws2812-pio.rs | 37 ++- 6 files changed, 299 insertions(+), 284 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 8d0547907..a1b7cf1c5 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -6,9 +6,13 @@ use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use fixed::types::extra::U8; +use fixed::FixedU32; use pac::io::vals::Gpio0ctrlFuncsel; +use pac::pio::vals::SmExecctrlStatusSel; use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; @@ -39,8 +43,12 @@ const NEW_AW: AtomicWaker = AtomicWaker::new(); const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] pub enum FifoJoin { /// Both TX and RX fifo is enabled + #[default] Duplex, /// Rx fifo twice as deep. TX fifo disabled RxOnly, @@ -48,8 +56,11 @@ pub enum FifoJoin { TxOnly, } -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] pub enum ShiftDirection { + #[default] Right = 1, Left = 0, } @@ -62,6 +73,15 @@ pub enum Direction { Out = 1, } +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum StatusSource { + #[default] + TxFifoLevel = 0, + RxFifoLevel = 1, +} + const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; @@ -477,6 +497,202 @@ fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) { } } +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ExecConfig { + pub side_en: bool, + pub side_pindir: bool, + pub jmp_pin: u8, + pub wrap_top: u8, + pub wrap_bottom: u8, +} + +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ShiftConfig { + pub threshold: u8, + pub direction: ShiftDirection, + pub auto_fill: bool, +} + +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PinConfig { + pub sideset_count: u8, + pub set_count: u8, + pub out_count: u8, + pub in_base: u8, + pub sideset_base: u8, + pub set_base: u8, + pub out_base: u8, +} + +#[derive(Clone, Copy, Debug)] +pub struct Config<'d, PIO: Instance> { + // CLKDIV + pub clock_divider: FixedU32, + // EXECCTRL + pub out_en_sel: u8, + pub inline_out_en: bool, + pub out_sticky: bool, + pub status_sel: StatusSource, + pub status_n: u8, + exec: ExecConfig, + origin: Option, + // SHIFTCTRL + pub fifo_join: FifoJoin, + pub shift_in: ShiftConfig, + pub shift_out: ShiftConfig, + // PINCTRL + pins: PinConfig, + in_count: u8, + _pio: PhantomData<&'d mut PIO>, +} + +impl<'d, PIO: Instance> Default for Config<'d, PIO> { + fn default() -> Self { + Self { + clock_divider: 1u8.into(), + out_en_sel: Default::default(), + inline_out_en: Default::default(), + out_sticky: Default::default(), + status_sel: Default::default(), + status_n: Default::default(), + exec: Default::default(), + origin: Default::default(), + fifo_join: Default::default(), + shift_in: Default::default(), + shift_out: Default::default(), + pins: Default::default(), + in_count: Default::default(), + _pio: Default::default(), + } + } +} + +impl<'d, PIO: Instance> Config<'d, PIO> { + pub fn get_exec(&self) -> ExecConfig { + self.exec + } + pub unsafe fn set_exec(&mut self, e: ExecConfig) { + self.exec = e; + } + + pub fn get_pins(&self) -> PinConfig { + self.pins + } + pub unsafe fn set_pins(&mut self, p: PinConfig) { + self.pins = p; + } + + /// Configures this state machine to use the given program, including jumping to the origin + /// of the program. The state machine is not started. + /// + /// `side_set` sets the range of pins affected by side-sets. The range must be consecutive. + /// Side-set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { + assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); + assert_consecutive(side_set); + self.exec.side_en = prog.side_set.optional(); + self.exec.side_pindir = prog.side_set.pindirs(); + self.exec.wrap_bottom = prog.wrap.target; + self.exec.wrap_top = prog.wrap.source; + self.pins.sideset_count = prog.side_set.bits(); + self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin()); + self.origin = Some(prog.origin); + } + + pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) { + self.exec.jmp_pin = pin.pin(); + } + + /// Sets the range of pins affected by SET instructions. The range must be consecutive. + /// Set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert!(pins.len() <= 5); + assert_consecutive(pins); + self.pins.set_base = pins.first().map_or(0, |p| p.pin()); + self.pins.set_count = pins.len() as u8; + } + + /// Sets the range of pins affected by OUT instructions. The range must be consecutive. + /// Out pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert_consecutive(pins); + self.pins.out_base = pins.first().map_or(0, |p| p.pin()); + self.pins.out_count = pins.len() as u8; + } + + /// Sets the range of pins used by IN instructions. The range must be consecutive. + /// In pins must configured as inputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert_consecutive(pins); + self.pins.in_base = pins.first().map_or(0, |p| p.pin()); + self.in_count = pins.len() as u8; + } +} + +impl<'d, PIO: Instance, const SM: usize> SetConfig for StateMachine<'d, PIO, SM> { + type Config = Config<'d, PIO>; + + fn set_config(&mut self, config: &Self::Config) { + // sm expects 0 for 65536, truncation makes that happen + assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536"); + assert!(config.clock_divider >= 1, "clkdiv must be >= 1"); + assert!(config.out_en_sel < 32, "out_en_sel must be < 32"); + assert!(config.status_n < 32, "status_n must be < 32"); + // sm expects 0 for 32, truncation makes that happen + assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32"); + assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32"); + let sm = Self::this_sm(); + unsafe { + sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8); + sm.execctrl().write(|w| { + w.set_side_en(config.exec.side_en); + w.set_side_pindir(config.exec.side_pindir); + w.set_jmp_pin(config.exec.jmp_pin); + w.set_out_en_sel(config.out_en_sel); + w.set_inline_out_en(config.inline_out_en); + w.set_out_sticky(config.out_sticky); + w.set_wrap_top(config.exec.wrap_top); + w.set_wrap_bottom(config.exec.wrap_bottom); + w.set_status_sel(match config.status_sel { + StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, + StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL, + }); + w.set_status_n(config.status_n); + }); + sm.shiftctrl().write(|w| { + w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); + w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly); + w.set_pull_thresh(config.shift_out.threshold); + w.set_push_thresh(config.shift_in.threshold); + w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right); + w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right); + w.set_autopull(config.shift_out.auto_fill); + w.set_autopush(config.shift_in.auto_fill); + }); + sm.pinctrl().write(|w| { + w.set_sideset_count(config.pins.sideset_count); + w.set_set_count(config.pins.set_count); + w.set_out_count(config.pins.out_count); + w.set_in_base(config.pins.in_base); + w.set_sideset_base(config.pins.sideset_base); + w.set_set_base(config.pins.set_base); + w.set_out_base(config.pins.out_base); + }); + if let Some(origin) = config.origin { + pio_instr_util::exec_jmp(self, origin); + } + } + } +} + impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { @@ -504,16 +720,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } } - pub fn set_clkdiv(&mut self, div_x_256: u32) { - unsafe { - Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); - } - } - - pub fn get_clkdiv(&self) -> u32 { - unsafe { Self::this_sm().clkdiv().read().0 >> 8 } - } - pub fn clkdiv_restart(&mut self) { let mask = 1u8 << SM; unsafe { @@ -521,26 +727,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - /// Configures this state machine to use the given program, including jumping to the origin - /// of the program. The state machine is not started. - pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { - assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); - assert_consecutive(side_set); - unsafe { - Self::this_sm().execctrl().modify(|w| { - w.set_side_en(prog.side_set.optional()); - w.set_side_pindir(prog.side_set.pindirs()); - w.set_wrap_bottom(prog.wrap.target); - w.set_wrap_top(prog.wrap.source); - }); - Self::this_sm().pinctrl().modify(|w| { - w.set_sideset_count(prog.side_set.bits()); - w.set_sideset_base(side_set.first().map_or(0, |p| p.pin())); - }); - pio_instr_util::exec_jmp(self, prog.origin); - } - } - fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { let enabled = self.is_enabled(); self.set_enable(false); @@ -591,43 +777,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { }); } - pub fn set_jmp_pin(&mut self, pin: u8) { - unsafe { - Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); - } - } - - pub fn get_jmp_pin(&mut self) -> u8 { - unsafe { Self::this_sm().execctrl().read().jmp_pin() } - } - - pub fn set_fifo_join(&mut self, join: FifoJoin) { - let (rx, tx) = match join { - FifoJoin::Duplex => (false, false), - FifoJoin::RxOnly => (true, false), - FifoJoin::TxOnly => (false, true), - }; - unsafe { - Self::this_sm().shiftctrl().modify(|w| { - w.set_fjoin_rx(rx); - w.set_fjoin_tx(tx) - }); - } - } - pub fn get_fifo_join(&self) -> FifoJoin { - unsafe { - let r = Self::this_sm().shiftctrl().read(); - // Ignores the invalid state when both bits are set - if r.fjoin_rx() { - FifoJoin::RxOnly - } else if r.fjoin_tx() { - FifoJoin::TxOnly - } else { - FifoJoin::Duplex - } - } - } - pub fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs unsafe { @@ -641,159 +790,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - pub fn set_pull_threshold(&mut self, threshold: u8) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold)); - } - } - - pub fn get_pull_threshold(&self) -> u8 { - unsafe { Self::this_sm().shiftctrl().read().pull_thresh() } - } - pub fn set_push_threshold(&mut self, threshold: u8) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold)); - } - } - - pub fn get_push_threshold(&self) -> u8 { - unsafe { Self::this_sm().shiftctrl().read().push_thresh() } - } - - pub fn set_out_shift_dir(&mut self, dir: ShiftDirection) { - unsafe { - Self::this_sm() - .shiftctrl() - .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); - } - } - pub fn get_out_shiftdir(&self) -> ShiftDirection { - unsafe { - if Self::this_sm().shiftctrl().read().out_shiftdir() { - ShiftDirection::Right - } else { - ShiftDirection::Left - } - } - } - - pub fn set_in_shift_dir(&mut self, dir: ShiftDirection) { - unsafe { - Self::this_sm() - .shiftctrl() - .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); - } - } - pub fn get_in_shiftdir(&self) -> ShiftDirection { - unsafe { - if Self::this_sm().shiftctrl().read().in_shiftdir() { - ShiftDirection::Right - } else { - ShiftDirection::Left - } - } - } - - pub fn set_autopull(&mut self, auto: bool) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto)); - } - } - - pub fn is_autopull(&self) -> bool { - unsafe { Self::this_sm().shiftctrl().read().autopull() } - } - - pub fn set_autopush(&mut self, auto: bool) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto)); - } - } - - pub fn is_autopush(&self) -> bool { - unsafe { Self::this_sm().shiftctrl().read().autopush() } - } - - pub fn get_addr(&self) -> u8 { - unsafe { Self::this_sm().addr().read().addr() } - } - - /// Set the range of out pins affected by a set instruction. - pub fn set_set_range(&mut self, base: u8, count: u8) { - assert!(base + count < 32); - unsafe { - Self::this_sm().pinctrl().modify(|w| { - w.set_set_base(base); - w.set_set_count(count) - }); - } - } - - /// Get the range of out pins affected by a set instruction. Returns (base, count). - pub fn get_set_range(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().pinctrl().read(); - (r.set_base(), r.set_count()) - } - } - - pub fn set_in_base_pin(&mut self, base: &Pin) { - unsafe { - Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); - } - } - - pub fn get_in_base(&self) -> u8 { - unsafe { - let r = Self::this_sm().pinctrl().read(); - r.in_base() - } - } - - pub fn set_out_range(&mut self, base: u8, count: u8) { - assert!(base + count < 32); - unsafe { - Self::this_sm().pinctrl().modify(|w| { - w.set_out_base(base); - w.set_out_count(count) - }); - } - } - - /// Get the range of out pins affected by a set instruction. Returns (base, count). - pub fn get_out_range(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().pinctrl().read(); - (r.out_base(), r.out_count()) - } - } - - pub fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { - let count = pins.len(); - assert!(count >= 1); - let start = pins[0].pin() as usize; - assert!(start + pins.len() <= 32); - for i in 0..count { - assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); - } - self.set_out_range(start as u8, count as u8); - } - - pub fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { - let count = pins.len(); - assert!(count >= 1); - let start = pins[0].pin() as usize; - assert!(start + pins.len() <= 32); - for i in 0..count { - assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); - } - self.set_set_range(start as u8, count as u8); - } - - pub fn get_current_instr() -> u32 { - unsafe { Self::this_sm().instr().read().0 } - } - pub fn exec_instr(&mut self, instr: u16) { unsafe { Self::this_sm().instr().write(|w| w.set_instr(instr)); diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57f6f5c67..d2829df99 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -22,6 +22,8 @@ lorawan = { version = "0.7.3", default-features = false, features = ["default-cr defmt = "0.3" defmt-rtt = "0.4" +fixed = "1.23.1" +fixed-macro = "1.2" #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm"] } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 9f47c2316..12484e882 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -2,10 +2,13 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Irq, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; +use fixed::traits::ToFixed; +use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { @@ -21,15 +24,14 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); let out_pin = pio.make_pio_pin(pin); - let pio_pins = [&out_pin]; - sm.set_out_pins(&pio_pins); - sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); - sm.set_set_range(0, 1); - - sm.set_autopull(true); - sm.set_out_shift_dir(ShiftDirection::Left); + cfg.set_out_pins(&[&out_pin]); + cfg.set_set_pins(&[&out_pin]); + cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed(); + cfg.shift_out.auto_fill = true; + sm.set_config(&cfg); } #[embassy_executor::task] @@ -51,11 +53,12 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); - sm.set_set_range(0, 0); - sm.set_autopush(true); - sm.set_in_shift_dir(ShiftDirection::Right); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); + cfg.shift_in.auto_fill = true; + cfg.shift_in.direction = ShiftDirection::Right; + sm.set_config(&cfg); } #[embassy_executor::task] @@ -81,8 +84,10 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); + sm.set_config(&cfg); } #[embassy_executor::task] diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 1c4e127c7..7f85288bf 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -2,11 +2,14 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Pio, ShiftDirection}; +use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::Peripheral; +use fixed::traits::ToFixed; +use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; fn swap_nibbles(v: u32) -> u32 { @@ -38,15 +41,21 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&common.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); - sm.set_autopull(true); - sm.set_autopush(true); - sm.set_pull_threshold(32); - sm.set_push_threshold(32); - sm.set_out_shift_dir(ShiftDirection::Right); - sm.set_in_shift_dir(ShiftDirection::Left); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); + cfg.shift_in = ShiftConfig { + auto_fill: true, + threshold: 32, + direction: ShiftDirection::Left, + }; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 32, + direction: ShiftDirection::Right, + }; + sm.set_config(&cfg); sm.set_enable(true); let mut dma_out_ref = p.DMA_CH0.into_ref(); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 40dee1c4d..61c5565d3 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -4,11 +4,12 @@ use core::fmt::Write; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Direction, FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::pwm::{self, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; @@ -29,7 +30,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { - let mut c = Config::default(); + let mut c = pwm::Config::default(); c.divider = 125.into(); c.top = 100; c.compare_b = 50; @@ -83,7 +84,6 @@ impl<'l> HD44780<'l> { ) -> HD44780<'l> { into_ref!(dma); - let db7pin = db7.pin(); let Pio { mut common, mut irq0, @@ -118,13 +118,17 @@ impl<'l> HD44780<'l> { sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); let relocated = RelocatedProgram::new(&prg.program); - sm0.use_program(&common.load_program(&relocated), &[&e]); - sm0.set_clkdiv(125 * 256); - sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_out_shift_dir(ShiftDirection::Left); - sm0.set_fifo_join(FifoJoin::TxOnly); - sm0.set_autopull(true); - sm0.set_pull_threshold(32); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.clock_divider = 125u8.into(); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out = ShiftConfig { + auto_fill: true, + direction: ShiftDirection::Left, + threshold: 32, + }; + cfg.fifo_join = FifoJoin::TxOnly; + sm0.set_config(&cfg); sm0.set_enable(true); // init to 8 bit thrice @@ -188,13 +192,15 @@ impl<'l> HD44780<'l> { ); let relocated = RelocatedProgram::new(&prg.program); - sm0.use_program(&common.load_program(&relocated), &[&e]); - sm0.set_clkdiv(8 * 256); // ~64ns/insn - sm0.set_jmp_pin(db7pin); - sm0.set_set_pins(&[&rs, &rw]); - sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_out_shift_dir(ShiftDirection::Left); - sm0.set_fifo_join(FifoJoin::TxOnly); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.clock_divider = 8u8.into(); // ~64ns/insn + cfg.set_jmp_pin(&db7); + cfg.set_set_pins(&[&rs, &rw]); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out.direction = ShiftDirection::Left; + cfg.fifo_join = FifoJoin::TxOnly; + sm0.set_config(&cfg); sm0.set_enable(true); diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 889970541..d7c4742d8 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -3,10 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::*; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; -use embassy_rp::pio::{Common, FifoJoin, Instance, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; +use fixed_macro::fixed; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; pub struct Ws2812<'d, P: Instance, const S: usize> { @@ -43,35 +45,30 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { a.bind(&mut wrap_source); let prg = a.assemble_with_wrap(wrap_source, wrap_target); + let mut cfg = Config::default(); // Pin config let out_pin = pio.make_pio_pin(pin); let relocated = RelocatedProgram::new(&prg); - sm.use_program(&pio.load_program(&relocated), &[&out_pin]); + cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); - // Clock config + // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp - const CLOCK_FREQ: u32 = 125_000_000; - const WS2812_FREQ: u32 = 800_000; - - let bit_freq = WS2812_FREQ * CYCLES_PER_BIT; - let mut int = CLOCK_FREQ / bit_freq; - let rem = CLOCK_FREQ - (int * bit_freq); - let frac = (rem * 256) / bit_freq; - // 65536.0 is represented as 0 in the pio's clock divider - if int == 65536 { - int = 0; - } - - sm.set_clkdiv((int << 8) | frac); + let clock_freq = fixed!(125_000: U24F8); + let ws2812_freq = fixed!(800: U24F8); + let bit_freq = ws2812_freq * CYCLES_PER_BIT; + cfg.clock_divider = clock_freq / bit_freq; // FIFO config - sm.set_autopull(true); - sm.set_fifo_join(FifoJoin::TxOnly); - sm.set_pull_threshold(24); - sm.set_out_shift_dir(ShiftDirection::Left); + cfg.fifo_join = FifoJoin::TxOnly; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 24, + direction: ShiftDirection::Left, + }; + sm.set_config(&cfg); sm.set_enable(true); Self { sm } From bdcea84ca106a0bd3a131fa1de4fb35218e9149a Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 12:23:53 +0200 Subject: [PATCH 1068/1575] rp/pio: add sm batch operations sometimes state machines need to be started, restarted, or synchronized at exactly the same time. the current interface does not allow this but the hardware does, so let's expose that. --- embassy-rp/src/pio.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index a1b7cf1c5..7cf605d8e 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -912,6 +912,47 @@ impl<'d, PIO: Instance> Common<'d, PIO> { pio: PhantomData::default(), } } + + pub fn apply_sm_batch(&mut self, f: impl FnOnce(&mut PioBatch<'d, PIO>)) { + let mut batch = PioBatch { + clkdiv_restart: 0, + sm_restart: 0, + sm_enable_mask: 0, + sm_enable: 0, + _pio: PhantomData, + }; + f(&mut batch); + unsafe { + PIO::PIO.ctrl().modify(|w| { + w.set_clkdiv_restart(batch.clkdiv_restart); + w.set_sm_restart(batch.sm_restart); + w.set_sm_enable((w.sm_enable() & !batch.sm_enable_mask) | batch.sm_enable); + }); + } + } +} + +pub struct PioBatch<'a, PIO: Instance> { + clkdiv_restart: u8, + sm_restart: u8, + sm_enable_mask: u8, + sm_enable: u8, + _pio: PhantomData<&'a PIO>, +} + +impl<'a, PIO: Instance> PioBatch<'a, PIO> { + pub fn restart_clockdiv(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) { + self.clkdiv_restart |= 1 << SM; + } + + pub fn restart(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) { + self.clkdiv_restart |= 1 << SM; + } + + pub fn set_enable(&mut self, _sm: &mut StateMachine<'a, PIO, SM>, enable: bool) { + self.sm_enable_mask |= 1 << SM; + self.sm_enable |= (enable as u8) << SM; + } } pub struct Irq<'d, PIO: Instance, const N: usize> { From 374c7513f905fcbf353bee2987e78d68fe8ea303 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 17:24:06 +0200 Subject: [PATCH 1069/1575] rp/pio: mark exec_instr as unsafe because it most definitely is. --- embassy-rp/src/pio.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 7cf605d8e..5fed4f1fa 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -790,10 +790,8 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - pub fn exec_instr(&mut self, instr: u16) { - unsafe { - Self::this_sm().instr().write(|w| w.set_instr(instr)); - } + pub unsafe fn exec_instr(&mut self, instr: u16) { + Self::this_sm().instr().write(|w| w.set_instr(instr)); } pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> { From b38d496d519f87d7f455e6c6b32f5d6379af44a5 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 20:53:06 +0200 Subject: [PATCH 1070/1575] rp/pio: allow wrap-around program loading execution wraps around after the end of instruction memory and wrapping works with this, so we may as well allow program loading across this boundary. could be useful for reusing chunks of instruction memory. --- embassy-rp/src/pio.rs | 11 ++++++----- embassy-rp/src/relocate.rs | 10 +++------- examples/rp/src/bin/pio_hd44780.rs | 3 ++- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 5fed4f1fa..fe66d9aef 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -855,13 +855,14 @@ impl<'d, PIO: Instance> Common<'d, PIO> { { let mut used_mask = 0; for (i, instr) in instrs.enumerate() { - let addr = (i + start) as u8; - let mask = 1 << (addr as usize); - if self.instructions_used & mask != 0 { - return Err(addr as usize); + // wrapping around the end of program memory is valid, let's make use of that. + let addr = (i + start) % 32; + let mask = 1 << addr; + if (self.instructions_used | used_mask) & mask != 0 { + return Err(addr); } unsafe { - PIO::PIO.instr_mem(addr as usize).write(|w| { + PIO::PIO.instr_mem(addr).write(|w| { w.set_instr_mem(instr); }); } diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index f36170e03..9cb279ccd 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -26,11 +26,7 @@ where Some(if instr & 0b1110_0000_0000_0000 == 0 { // this is a JMP instruction -> add offset to address let address = (instr & 0b1_1111) as u8; - let address = address + self.offset; - assert!( - address < pio::RP2040_MAX_PROGRAM_SIZE as u8, - "Invalid JMP out of the program after offset addition" - ); + let address = address.wrapping_add(self.offset) % 32; instr & (!0b11111) | address as u16 } else { instr @@ -62,8 +58,8 @@ impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { let wrap = self.program.wrap; let origin = self.origin; Wrap { - source: wrap.source + origin, - target: wrap.target + origin, + source: wrap.source.wrapping_add(origin) % 32, + target: wrap.target.wrapping_add(origin) % 32, } } diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 61c5565d3..088fd5649 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -95,6 +95,7 @@ impl<'l> HD44780<'l> { let prg = pio_proc::pio_asm!( r#" .side_set 1 opt + .origin 20 loop: out x, 24 @@ -148,7 +149,7 @@ impl<'l> HD44780<'l> { // many side sets are only there to free up a delay bit! let prg = pio_proc::pio_asm!( r#" - .origin 7 + .origin 27 .side_set 1 .wrap_target From db9b8eb88f095687ed624171cf79fbacf06c199a Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 21:14:00 +0200 Subject: [PATCH 1071/1575] rp/pio: make sure gpio() asserts are compiled out we'll have to touch pio one way or other if the number of gpio pins ever increases. may as well make sure an assert never fires until that happens. --- embassy-rp/src/pio.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index fe66d9aef..1e41bed30 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -1070,7 +1070,8 @@ fn on_pio_drop() { if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { let used_pins = state.used_pins.load(Ordering::Relaxed); let null = Gpio0ctrlFuncsel::NULL.0; - for i in 0..32 { + // we only have 30 pins. don't test the other two since gpio() asserts. + for i in 0..30 { if used_pins & (1 << i) != 0 { unsafe { pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); From 87795cbca84a67ce9c5bdb5c3043b41251d75bbc Mon Sep 17 00:00:00 2001 From: Fabian Kunze Date: Sun, 7 May 2023 01:00:13 +0200 Subject: [PATCH 1072/1575] added example multi priority executors rp2040 --- embassy-rp/Cargo.toml | 2 +- embassy-rp/src/interrupt.rs | 6 ++ examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/multiprio.rs | 152 +++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 examples/rp/src/bin/multiprio.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 59d0bf338..ea8f54f36 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,7 +61,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "2", features = ["rt"] } +rp-pac = { version = "2", features = ["rt"], path = "../../rp-pac" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} diff --git a/embassy-rp/src/interrupt.rs b/embassy-rp/src/interrupt.rs index f21a5433b..989f5dc2d 100644 --- a/embassy-rp/src/interrupt.rs +++ b/embassy-rp/src/interrupt.rs @@ -34,3 +34,9 @@ declare!(ADC_IRQ_FIFO); declare!(I2C0_IRQ); declare!(I2C1_IRQ); declare!(RTC_IRQ); +declare!(SWI_IRQ_0); +declare!(SWI_IRQ_1); +declare!(SWI_IRQ_2); +declare!(SWI_IRQ_3); +declare!(SWI_IRQ_4); +declare!(SWI_IRQ_5); diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57f6f5c67..3f74129a4 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs new file mode 100644 index 000000000..2f79ba49e --- /dev/null +++ b/examples/rp/src/bin/multiprio.rs @@ -0,0 +1,152 @@ +//! This example showcases how to create multiple Executor instances to run tasks at +//! different priority levels. +//! +//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling +//! there's work in the queue, and `wfe` for waiting for work. +//! +//! Medium and high priority executors run in two interrupts with different priorities. +//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since +//! when there's work the interrupt will trigger and run the executor. +//! +//! Sample output below. Note that high priority ticks can interrupt everything else, and +//! medium priority computations can interrupt low priority computations, making them to appear +//! to take significantly longer time. +//! +//! ```not_rust +//! [med] Starting long computation +//! [med] done in 992 ms +//! [high] tick! +//! [low] Starting long computation +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [low] done in 3972 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! ``` +//! +//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. +//! You will get an output like the following. Note that no computation is ever interrupted. +//! +//! ```not_rust +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [low] Starting long computation +//! [low] done in 992 ms +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! [low] Starting long computation +//! [low] done in 992 ms +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! ``` +//! + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::mem; + +use cortex_m::peripheral::NVIC; +use cortex_m_rt::entry; +use defmt::{info, unwrap}; +use embassy_rp::executor::{Executor, InterruptExecutor}; +use embassy_rp::interrupt; +use embassy_rp::pac::Interrupt; +use embassy_time::{Duration, Instant, Timer, TICK_HZ}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run_high() { + loop { + info!(" [high] tick!"); + Timer::after(Duration::from_ticks(673740)).await; + } +} + +#[embassy_executor::task] +async fn run_med() { + loop { + let start = Instant::now(); + info!(" [med] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(125_000_000); // ~1 second + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; + info!(" [med] done in {} ms", ms); + + Timer::after(Duration::from_ticks(53421)).await; + } +} + +#[embassy_executor::task] +async fn run_low() { + loop { + let start = Instant::now(); + info!("[low] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(250_000_000); // ~2 seconds + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() * 1000 / TICK_HZ; + info!("[low] done in {} ms", ms); + + Timer::after(Duration::from_ticks(82983)).await; + } +} + +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_LOW: StaticCell = StaticCell::new(); + +#[interrupt] +unsafe fn SWI_IRQ_1() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn SWI_IRQ_0() { + EXECUTOR_MED.on_interrupt() +} + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let _p = embassy_rp::init(Default::default()); + let mut nvic: NVIC = unsafe { mem::transmute(()) }; + + // High-priority executor: SWI_IRQ_1, priority level 2 + unsafe { nvic.set_priority(Interrupt::SWI_IRQ_1, 2 << 6) }; + info!("bla: {}", NVIC::get_priority(Interrupt::SWI_IRQ_1)); + let spawner = EXECUTOR_HIGH.start(Interrupt::SWI_IRQ_1); + unwrap!(spawner.spawn(run_high())); + + // Medium-priority executor: SWI_IRQ_0, priority level 3 + unsafe { nvic.set_priority(Interrupt::SWI_IRQ_0, 3 << 6) }; + info!("bla: {}", NVIC::get_priority(Interrupt::SWI_IRQ_0)); + let spawner = EXECUTOR_MED.start(Interrupt::SWI_IRQ_0); + unwrap!(spawner.spawn(run_med())); + + // Low priority executor: runs in thread mode, using WFE/SEV + let executor = EXECUTOR_LOW.init(Executor::new()); + executor.run(|spawner| { + unwrap!(spawner.spawn(run_low())); + }); +} From fb2d5b484aa9533f29528d0789fac559d48859ad Mon Sep 17 00:00:00 2001 From: Fabian Kunze Date: Sun, 7 May 2023 01:18:19 +0200 Subject: [PATCH 1073/1575] changed relative cargo.toml dependency to github revision --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ea8f54f36..2748c0591 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,7 +61,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "2", features = ["rt"], path = "../../rp-pac" } +rp-pac = { features = ["rt"], git = "https://github.com/fakusb/rp-pac.git", rev = "b1e0a150f37efb69644f5474d879e9180eeb167b" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} From 1a96eae22c38afbd0c1c15d1d5dcc43bc639369b Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sun, 7 May 2023 13:49:48 -0400 Subject: [PATCH 1074/1575] rp clock configuration --- embassy-rp/src/clocks.rs | 515 ++++++++++++++++++++++++++++++++------- embassy-rp/src/lib.rs | 20 +- 2 files changed, 438 insertions(+), 97 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 8a34b293d..4ae967aae 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -2,10 +2,138 @@ use pac::clocks::vals::*; use crate::{pac, reset}; -const XOSC_MHZ: u32 = 12; +static mut EXTERNAL_HZ: u32 = 0; + +pub struct ClockConfig { + rosc_config: Option, + xosc_config: Option, + ref_clk_config: (RefClkSrc, u8), + sys_clk_config: (SysClkSrc, u32), + peri_clk_src: Option, + usb_clk_config: Option<(ClkUsbCtrlAuxsrc, u8)>, + adc_clk_config: Option<(ClkAdcCtrlAuxsrc, u8)>, + rtc_clk_config: Option<(ClkRtcCtrlAuxsrc, u32)>, +} + +impl ClockConfig { + pub fn crystal(crystal_hz: u32) -> Self { + Self { + rosc_config: Some(RoscConfig { + range: pac::rosc::vals::FreqRange::MEDIUM, + drive_strength_0: 0, + drive_strength_1: 0, + drive_strength_2: 0, + drive_strength_3: 0, + drive_strength_4: 0, + drive_strength_5: 0, + drive_strength_6: 0, + drive_strength_7: 0, + div: 16, + }), + xosc_config: Some(XoscConfig { + hz: crystal_hz, + clock_type: ExternalClock::Crystal, + sys_pll: Some(PllConfig { + refdiv: 1, + vco_freq: 1500_000_000, + post_div1: 6, + post_div2: 2, + }), + usb_pll: Some(PllConfig { + refdiv: 1, + vco_freq: 480_000_000, + post_div1: 5, + post_div2: 2, + }), + }), + ref_clk_config: (RefClkSrc::Xosc, 1), + sys_clk_config: (SysClkSrc::Aux(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS), 1), + peri_clk_src: Some(ClkPeriCtrlAuxsrc::CLK_SYS), + usb_clk_config: Some((ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS, 1)), + adc_clk_config: Some((ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB, 1)), + rtc_clk_config: Some((ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB, 1024)), + } + } + + pub fn rosc() -> Self { + Self { + rosc_config: Some(RoscConfig { + range: pac::rosc::vals::FreqRange::HIGH, + drive_strength_0: 0, + drive_strength_1: 0, + drive_strength_2: 0, + drive_strength_3: 0, + drive_strength_4: 0, + drive_strength_5: 0, + drive_strength_6: 0, + drive_strength_7: 0, + div: 1, + }), + xosc_config: None, + ref_clk_config: (RefClkSrc::Rosc, 4), + sys_clk_config: (SysClkSrc::Aux(ClkSysCtrlAuxsrc::ROSC_CLKSRC), 1), + peri_clk_src: Some(ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH), + usb_clk_config: None, + adc_clk_config: Some((ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH, 1)), + rtc_clk_config: Some((ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH, 1024)), + } + } +} +pub enum ExternalClock { + Crystal, + Clock, +} + +pub struct XoscConfig { + hz: u32, + clock_type: ExternalClock, + sys_pll: Option, + usb_pll: Option, +} + +pub struct RoscConfig { + range: pac::rosc::vals::FreqRange, + drive_strength_0: u8, + drive_strength_1: u8, + drive_strength_2: u8, + drive_strength_3: u8, + drive_strength_4: u8, + drive_strength_5: u8, + drive_strength_6: u8, + drive_strength_7: u8, + div: u16, +} + +pub struct PllConfig { + pub refdiv: u32, + pub vco_freq: u32, + pub post_div1: u8, + pub post_div2: u8, +} + +pub struct RefClkConfig { + pub src: RefClkSrc, + pub div: u8, +} + +pub enum RefClkSrc { + Xosc, + Rosc, + Aux(ClkRefCtrlAuxsrc), +} + +pub struct SysClkConfig { + pub src: SysClkSrc, + pub div: u32, +} + +pub enum SysClkSrc { + Ref, + Aux(ClkSysCtrlAuxsrc), +} /// safety: must be called exactly once at bootup -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(config: ClockConfig) { // Reset everything except: // - QSPI (we're using it to run this code!) // - PLLs (it may be suicide if that's what's clocking us) @@ -15,124 +143,325 @@ pub(crate) unsafe fn init() { peris.set_pads_qspi(false); peris.set_pll_sys(false); peris.set_pll_usb(false); + // TODO investigate if usb should be unreset here peris.set_usbctrl(false); peris.set_syscfg(false); reset::reset(peris); - // Remove reset from peripherals which are clocked only by clk_sys and - // clk_ref. Other peripherals stay in reset until we've configured clocks. - let mut peris = reset::ALL_PERIPHERALS; - peris.set_adc(false); - peris.set_rtc(false); - peris.set_spi0(false); - peris.set_spi1(false); - peris.set_uart0(false); - peris.set_uart1(false); - peris.set_usbctrl(false); - reset::unreset_wait(peris); - - // Start tick in watchdog - // xosc 12 mhz - pac::WATCHDOG.tick().write(|w| { - w.set_cycles(XOSC_MHZ as u16); - w.set_enable(true); - }); - // Disable resus that may be enabled from previous software let c = pac::CLOCKS; c.clk_sys_resus_ctrl() .write_value(pac::clocks::regs::ClkSysResusCtrl(0)); - // start XOSC - start_xosc(); - // Before we touch PLLs, switch sys and ref cleanly away from their aux sources. c.clk_sys_ctrl().modify(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); while c.clk_sys_selected().read() != 1 {} c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH)); while c.clk_ref_selected().read() != 1 {} - // Configure PLLs - // REF FBDIV VCO POSTDIV - // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz - // PLL USB: 12 / 1 = 12MHz * 40 = 480 MHz / 5 / 2 = 48MHz - configure_pll(pac::PLL_SYS, 1, 1500_000_000, 6, 2); - configure_pll(pac::PLL_USB, 1, 480_000_000, 5, 2); + if let Some(config) = config.rosc_config { + configure_rosc(config); + } - // CLK_REF = XOSC (12MHz) / 1 = 12MHz2Mhz - c.clk_ref_ctrl().write(|w| { - w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); - }); - while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {} - c.clk_ref_div().write(|w| w.set_int(1)); + if let Some(config) = config.xosc_config { + EXTERNAL_HZ = config.hz; - // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz - c.clk_sys_ctrl().write(|w| { - w.set_src(ClkSysCtrlSrc::CLK_REF); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - c.clk_sys_div().write(|w| w.set_int(1)); - c.clk_sys_ctrl().write(|w| { - w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS); - w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {} + pac::WATCHDOG.tick().write(|w| { + w.set_cycles((config.hz / 1_000_000) as u16); + w.set_enable(true); + }); - // CLK USB = PLL USB (48MHz) / 1 = 48MHz - c.clk_usb_div().write(|w| w.set_int(1)); - c.clk_usb_ctrl().write(|w| { + // start XOSC + match config.clock_type { + ExternalClock::Crystal => start_xosc(config.hz), + // TODO The datasheet says the xosc needs to be put into a bypass mode to use an + // external clock, but is mum about how to do that. + ExternalClock::Clock => todo!(), + } + + if let Some(sys_pll_config) = config.sys_pll { + configure_pll(pac::PLL_SYS, config.hz, sys_pll_config); + } + if let Some(usb_pll_config) = config.usb_pll { + configure_pll(pac::PLL_USB, config.hz, usb_pll_config); + } + } + + let (src, div) = config.ref_clk_config; + match src { + RefClkSrc::Xosc => { + c.clk_ref_ctrl().write(|w| { + w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); + }); + while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {} + c.clk_ref_div().write(|w| w.set_int(div)); + } + RefClkSrc::Rosc => { + c.clk_ref_ctrl().write(|w| { + w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH); + }); + while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::ROSC_CLKSRC_PH.0 {} + c.clk_ref_div().write(|w| w.set_int(div)); + } + RefClkSrc::Aux(src) => { + c.clk_ref_ctrl().write(|w| { + w.set_auxsrc(src); + w.set_src(ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX); + }); + while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX.0 {} + c.clk_ref_div().write(|w| w.set_int(div)); + } + } + + pac::WATCHDOG.tick().write(|w| { + w.set_cycles((clk_ref_freq() / 1_000_000) as u16); w.set_enable(true); - w.set_auxsrc(ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB); }); - // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz - c.clk_adc_div().write(|w| w.set_int(1)); - c.clk_adc_ctrl().write(|w| { - w.set_enable(true); - w.set_auxsrc(ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB); - }); + let (src, div) = config.sys_clk_config; + match src { + SysClkSrc::Ref => { + c.clk_sys_ctrl().write(|w| { + w.set_src(ClkSysCtrlSrc::CLK_REF); + }); + while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} + c.clk_sys_div().write(|w| w.set_int(div)); + } + SysClkSrc::Aux(src) => { + c.clk_sys_ctrl().write(|w| { + w.set_src(ClkSysCtrlSrc::CLK_REF); + }); + while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz - c.clk_rtc_ctrl().modify(|w| { - w.set_enable(false); - }); - c.clk_rtc_div().write(|w| w.set_int(1024)); - c.clk_rtc_ctrl().write(|w| { - w.set_enable(true); - w.set_auxsrc(ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB); - }); + c.clk_sys_div().write(|w| w.set_int(div)); + c.clk_sys_ctrl().write(|w| { + w.set_auxsrc(src); + w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); + }); + while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {} + } + } - // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable - // Normally choose clk_sys or clk_usb - c.clk_peri_ctrl().write(|w| { - w.set_enable(true); - w.set_auxsrc(ClkPeriCtrlAuxsrc::CLK_SYS); - }); + let mut peris = reset::ALL_PERIPHERALS; + + if let Some(src) = config.peri_clk_src { + c.clk_peri_ctrl().write(|w| { + w.set_enable(true); + w.set_auxsrc(src); + }); + } else { + peris.set_spi0(false); + peris.set_spi1(false); + peris.set_uart0(false); + peris.set_uart1(false); + } + + if let Some((src, div)) = config.usb_clk_config { + // CLK USB = PLL USB (48MHz) / 1 = 48MHz + c.clk_usb_div().write(|w| w.set_int(div)); + c.clk_usb_ctrl().write(|w| { + w.set_enable(true); + w.set_auxsrc(src); + }); + } else { + peris.set_usbctrl(false); + } + + if let Some((src, div)) = config.adc_clk_config { + // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz + c.clk_adc_div().write(|w| w.set_int(div)); + c.clk_adc_ctrl().write(|w| { + w.set_enable(true); + w.set_auxsrc(src); + }); + } else { + peris.set_adc(false); + } + + if let Some((src, div)) = config.rtc_clk_config { + // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz + c.clk_rtc_ctrl().modify(|w| { + w.set_enable(false); + }); + c.clk_rtc_div().write(|w| w.set_int(div)); + c.clk_rtc_ctrl().write(|w| { + w.set_enable(true); + w.set_auxsrc(src); + }); + } else { + peris.set_rtc(false); + } // Peripheral clocks should now all be running - let peris = reset::ALL_PERIPHERALS; reset::unreset_wait(peris); } -pub(crate) fn _clk_sys_freq() -> u32 { - 125_000_000 +unsafe fn configure_rosc(config: RoscConfig) { + let p = pac::ROSC; + + p.freqa().write(|w| { + w.set_passwd(pac::rosc::vals::Passwd::PASS); + w.set_ds0(config.drive_strength_0); + w.set_ds1(config.drive_strength_1); + w.set_ds2(config.drive_strength_2); + w.set_ds3(config.drive_strength_3); + }); + + p.freqb().write(|w| { + w.set_passwd(pac::rosc::vals::Passwd::PASS); + w.set_ds4(config.drive_strength_4); + w.set_ds5(config.drive_strength_5); + w.set_ds6(config.drive_strength_6); + w.set_ds7(config.drive_strength_7); + }); + + p.div().write(|w| { + w.set_div(pac::rosc::vals::Div(config.div + pac::rosc::vals::Div::PASS.0)); + }); + + p.ctrl().write(|w| { + w.set_enable(pac::rosc::vals::Enable::ENABLE); + w.set_freq_range(config.range); + }); +} + +pub fn estimate_rosc_freq() -> u32 { + let p = pac::ROSC; + + let base = match unsafe { p.ctrl().read().freq_range() } { + pac::rosc::vals::FreqRange::LOW => 84_000_000, + pac::rosc::vals::FreqRange::MEDIUM => 104_000_000, + pac::rosc::vals::FreqRange::HIGH => 140_000_000, + pac::rosc::vals::FreqRange::TOOHIGH => 208_000_000, + _ => unreachable!(), + }; + let mut div = unsafe { p.div().read().0 - pac::rosc::vals::Div::PASS.0 as u32 }; + if div == 0 { + div = 32 + } + + base / div +} + +pub(crate) fn clk_sys_freq() -> u32 { + let c = pac::CLOCKS; + let ctrl = unsafe { c.clk_sys_ctrl().read() }; + + let base = match ctrl.src() { + ClkSysCtrlSrc::CLK_REF => clk_ref_freq(), + ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX => { + match ctrl.auxsrc() { + ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS => clk_sys_pll_freq(), + ClkSysCtrlAuxsrc::CLKSRC_PLL_USB => clk_usb_pll_freq(), + ClkSysCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkSysCtrlAuxsrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, + // TODO not sure how to handle clkin sources + _ => todo!(), + } + } + _ => unreachable!(), + }; + + let div = unsafe { c.clk_sys_div().read() }; + let int = if div.int() == 0 { 65536 } else { div.int() }; + // TODO handle fractional clock div + let _frac = div.frac(); + + base / int +} + +pub(crate) fn clk_sys_pll_freq() -> u32 { + let p = pac::PLL_SYS; + + let input_freq = unsafe { EXTERNAL_HZ }; + let cs = unsafe { p.cs().read() }; + + let refdiv = cs.refdiv() as u32; + let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; + let (postdiv1, postdiv2) = unsafe { + let prim = p.prim().read(); + (prim.postdiv1() as u32, prim.postdiv2() as u32) + }; + + (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 +} + +pub(crate) fn clk_usb_pll_freq() -> u32 { + let p = pac::PLL_USB; + + let input_freq = unsafe { EXTERNAL_HZ }; + let cs = unsafe { p.cs().read() }; + + let refdiv = cs.refdiv() as u32; + let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; + let (postdiv1, postdiv2) = unsafe { + let prim = p.prim().read(); + (prim.postdiv1() as u32, prim.postdiv2() as u32) + }; + + (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 } pub(crate) fn clk_peri_freq() -> u32 { - 125_000_000 + let c = pac::CLOCKS; + let src = unsafe { c.clk_peri_ctrl().read().auxsrc() }; + + match src { + ClkPeriCtrlAuxsrc::CLK_SYS => clk_sys_freq(), + ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS => clk_sys_pll_freq(), + ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), + ClkPeriCtrlAuxsrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, + // TODO not sure how to handle clkin sources + _ => todo!(), + } +} + +pub fn clk_ref_freq() -> u32 { + let c = pac::CLOCKS; + let ctrl = unsafe { c.clk_ref_ctrl().read() }; + + let base = match ctrl.src() { + ClkRefCtrlSrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), + ClkRefCtrlSrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, + ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX => todo!(), + _ => unreachable!(), + }; + + let mut div = unsafe { c.clk_ref_div().read().int() } as u32; + if div == 0 { + div = 4; + } + + base / div } pub(crate) fn clk_rtc_freq() -> u32 { - 46875 + let c = pac::CLOCKS; + let src = unsafe { c.clk_rtc_ctrl().read().auxsrc() }; + + let base = match src { + ClkRtcCtrlAuxsrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, + ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), + ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB => clk_usb_pll_freq(), + ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS => clk_sys_pll_freq(), + // TODO not sure how to handle clkin sources + _ => todo!(), + }; + + let div = unsafe { c.clk_rtc_div().read() }; + let int = if div.int() == 0 { 65536 } else { div.int() }; + // TODO handle fractional clock div + let _frac = div.frac(); + + base / int } -unsafe fn start_xosc() { - const XOSC_MHZ: u32 = 12; +unsafe fn start_xosc(crystal_hz: u32) { pac::XOSC .ctrl() .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ)); - let startup_delay = (((XOSC_MHZ * 1_000_000) / 1000) + 128) / 256; + let startup_delay = ((crystal_hz / 1000) + 128) / 256; pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); pac::XOSC.ctrl().write(|w| { w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ); @@ -141,24 +470,24 @@ unsafe fn start_xosc() { while !pac::XOSC.status().read().stable() {} } -unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: u8, post_div2: u8) { - let ref_freq = XOSC_MHZ * 1_000_000 / refdiv; +unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { + let ref_freq = input_freq / config.refdiv; - let fbdiv = vco_freq / ref_freq; + let fbdiv = config.vco_freq / ref_freq; assert!(fbdiv >= 16 && fbdiv <= 320); - assert!(post_div1 >= 1 && post_div1 <= 7); - assert!(post_div2 >= 1 && post_div2 <= 7); - assert!(post_div2 <= post_div1); - assert!(ref_freq <= (vco_freq / 16)); + assert!(config.post_div1 >= 1 && config.post_div1 <= 7); + assert!(config.post_div2 >= 1 && config.post_div2 <= 7); + assert!(config.post_div2 <= config.post_div1); + assert!(ref_freq <= (config.vco_freq / 16)); // do not disrupt PLL that is already correctly configured and operating let cs = p.cs().read(); let prim = p.prim().read(); if cs.lock() - && cs.refdiv() == refdiv as u8 + && cs.refdiv() == config.refdiv as u8 && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16 - && prim.postdiv1() == post_div1 - && prim.postdiv2() == post_div2 + && prim.postdiv1() == config.post_div1 + && prim.postdiv2() == config.post_div2 { return; } @@ -174,7 +503,7 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: reset::unreset_wait(peris); // Load VCO-related dividers before starting VCO - p.cs().write(|w| w.set_refdiv(refdiv as _)); + p.cs().write(|w| w.set_refdiv(config.refdiv as _)); p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); // Turn on PLL @@ -189,8 +518,8 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1: // Wait for PLL to lock p.prim().write(|w| { - w.set_postdiv1(post_div1); - w.set_postdiv2(post_div2); + w.set_postdiv1(config.post_div1); + w.set_postdiv2(config.post_div2); }); // Turn on post divider diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index cba7559df..118ce5237 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -136,23 +136,35 @@ embassy_hal_common::peripherals! { static BOOT2: [u8; 256] = *include_bytes!("boot2.bin"); pub mod config { + use crate::clocks::ClockConfig; + #[non_exhaustive] - pub struct Config {} + pub struct Config { + pub clocks: ClockConfig, + } impl Default for Config { fn default() -> Self { - Self {} + Self { + clocks: ClockConfig::crystal(12_000_000), + } + } + } + + impl Config { + pub fn new(clocks: ClockConfig) -> Self { + Self { clocks } } } } -pub fn init(_config: config::Config) -> Peripherals { +pub fn init(config: config::Config) -> Peripherals { // Do this first, so that it panics if user is calling `init` a second time // before doing anything important. let peripherals = Peripherals::take(); unsafe { - clocks::init(); + clocks::init(config.clocks); #[cfg(feature = "time-driver")] timer::init(); dma::init(); From 59132514cfd04709bffb119642baf9d0dcf4733a Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 8 May 2023 09:43:58 -0400 Subject: [PATCH 1075/1575] Add missing functions, Cleanup, Gpout example --- embassy-rp/src/clocks.rs | 299 ++++++++++++++++++++++++++--------- examples/rp/src/bin/gpout.rs | 21 +++ 2 files changed, 247 insertions(+), 73 deletions(-) create mode 100644 examples/rp/src/bin/gpout.rs diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 4ae967aae..5d3444442 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -2,7 +2,8 @@ use pac::clocks::vals::*; use crate::{pac, reset}; -static mut EXTERNAL_HZ: u32 = 0; +// TODO fix terrible use of global here +static mut XIN_HZ: u32 = 0; pub struct ClockConfig { rosc_config: Option, @@ -49,7 +50,7 @@ impl ClockConfig { ref_clk_config: (RefClkSrc::Xosc, 1), sys_clk_config: (SysClkSrc::Aux(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS), 1), peri_clk_src: Some(ClkPeriCtrlAuxsrc::CLK_SYS), - usb_clk_config: Some((ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS, 1)), + usb_clk_config: Some((ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB, 1)), adc_clk_config: Some((ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB, 1)), rtc_clk_config: Some((ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB, 1024)), } @@ -164,7 +165,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { } if let Some(config) = config.xosc_config { - EXTERNAL_HZ = config.hz; + XIN_HZ = config.hz; pac::WATCHDOG.tick().write(|w| { w.set_cycles((config.hz / 1_000_000) as u16); @@ -343,22 +344,64 @@ pub fn estimate_rosc_freq() -> u32 { base / div } -pub(crate) fn clk_sys_freq() -> u32 { +pub fn xosc_freq() -> u32 { + unsafe { XIN_HZ } +} + +pub fn gpin0_freq() -> u32 { + todo!() +} +pub fn gpin1_freq() -> u32 { + todo!() +} + +pub fn pll_sys_freq() -> u32 { + let p = pac::PLL_SYS; + + let input_freq = xosc_freq(); + let cs = unsafe { p.cs().read() }; + + let refdiv = cs.refdiv() as u32; + let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; + let (postdiv1, postdiv2) = unsafe { + let prim = p.prim().read(); + (prim.postdiv1() as u32, prim.postdiv2() as u32) + }; + + (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 +} + +pub fn pll_usb_freq() -> u32 { + let p = pac::PLL_USB; + + let input_freq = xosc_freq(); + let cs = unsafe { p.cs().read() }; + + let refdiv = cs.refdiv() as u32; + let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; + let (postdiv1, postdiv2) = unsafe { + let prim = p.prim().read(); + (prim.postdiv1() as u32, prim.postdiv2() as u32) + }; + + (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 +} + +pub fn clk_sys_freq() -> u32 { let c = pac::CLOCKS; let ctrl = unsafe { c.clk_sys_ctrl().read() }; let base = match ctrl.src() { ClkSysCtrlSrc::CLK_REF => clk_ref_freq(), - ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX => { - match ctrl.auxsrc() { - ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS => clk_sys_pll_freq(), - ClkSysCtrlAuxsrc::CLKSRC_PLL_USB => clk_usb_pll_freq(), - ClkSysCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), - ClkSysCtrlAuxsrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, - // TODO not sure how to handle clkin sources - _ => todo!(), - } - } + ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX => match ctrl.auxsrc() { + ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkSysCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkSysCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkSysCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkSysCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkSysCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + _ => unreachable!(), + }, _ => unreachable!(), }; @@ -370,82 +413,96 @@ pub(crate) fn clk_sys_freq() -> u32 { base / int } -pub(crate) fn clk_sys_pll_freq() -> u32 { - let p = pac::PLL_SYS; - - let input_freq = unsafe { EXTERNAL_HZ }; - let cs = unsafe { p.cs().read() }; - - let refdiv = cs.refdiv() as u32; - let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; - let (postdiv1, postdiv2) = unsafe { - let prim = p.prim().read(); - (prim.postdiv1() as u32, prim.postdiv2() as u32) - }; - - (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 -} - -pub(crate) fn clk_usb_pll_freq() -> u32 { - let p = pac::PLL_USB; - - let input_freq = unsafe { EXTERNAL_HZ }; - let cs = unsafe { p.cs().read() }; - - let refdiv = cs.refdiv() as u32; - let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; - let (postdiv1, postdiv2) = unsafe { - let prim = p.prim().read(); - (prim.postdiv1() as u32, prim.postdiv2() as u32) - }; - - (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 -} - -pub(crate) fn clk_peri_freq() -> u32 { - let c = pac::CLOCKS; - let src = unsafe { c.clk_peri_ctrl().read().auxsrc() }; - - match src { - ClkPeriCtrlAuxsrc::CLK_SYS => clk_sys_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS => clk_sys_pll_freq(), - ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkPeriCtrlAuxsrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, - // TODO not sure how to handle clkin sources - _ => todo!(), - } -} - pub fn clk_ref_freq() -> u32 { let c = pac::CLOCKS; let ctrl = unsafe { c.clk_ref_ctrl().read() }; let base = match ctrl.src() { ClkRefCtrlSrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkRefCtrlSrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, - ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX => todo!(), + ClkRefCtrlSrc::XOSC_CLKSRC => xosc_freq(), + ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX => match ctrl.auxsrc() { + ClkRefCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkRefCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkRefCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + _ => unreachable!(), + }, _ => unreachable!(), }; - let mut div = unsafe { c.clk_ref_div().read().int() } as u32; - if div == 0 { - div = 4; - } + let div = unsafe { c.clk_ref_div().read() }; + let int = if div.int() == 0 { 4 } else { div.int() as u32 }; - base / div + base / int } -pub(crate) fn clk_rtc_freq() -> u32 { +pub fn clk_peri_freq() -> u32 { + let c = pac::CLOCKS; + let src = unsafe { c.clk_peri_ctrl().read().auxsrc() }; + + match src { + ClkPeriCtrlAuxsrc::CLK_SYS => clk_sys_freq(), + ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), + ClkPeriCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + _ => unreachable!(), + } +} + +pub fn clk_usb_freq() -> u32 { + let c = pac::CLOCKS; + let ctrl = unsafe { c.clk_usb_ctrl().read() }; + + let base = match ctrl.auxsrc() { + ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), + ClkUsbCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + _ => unreachable!(), + }; + + let div = unsafe { c.clk_ref_div().read() }; + let int = if div.int() == 0 { 4 } else { div.int() as u32 }; + + base / int +} + +pub fn clk_adc_freq() -> u32 { + let c = pac::CLOCKS; + let ctrl = unsafe { c.clk_adc_ctrl().read() }; + + let base = match ctrl.auxsrc() { + ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), + ClkAdcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + _ => unreachable!(), + }; + + let div = unsafe { c.clk_adc_div().read() }; + let int = if div.int() == 0 { 4 } else { div.int() as u32 }; + + base / int +} + +pub fn clk_rtc_freq() -> u32 { let c = pac::CLOCKS; let src = unsafe { c.clk_rtc_ctrl().read().auxsrc() }; let base = match src { - ClkRtcCtrlAuxsrc::XOSC_CLKSRC => unsafe { EXTERNAL_HZ }, + ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB => clk_usb_pll_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS => clk_sys_pll_freq(), - // TODO not sure how to handle clkin sources - _ => todo!(), + ClkRtcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + _ => unreachable!(), }; let div = unsafe { c.clk_rtc_div().read() }; @@ -456,6 +513,60 @@ pub(crate) fn clk_rtc_freq() -> u32 { base / int } +pub fn clk_gpout0_freq() -> u32 { + let c = pac::CLOCKS; + let src = unsafe { c.clk_gpout0_ctrl().read().auxsrc() }; + + let base = match src { + ClkGpout0ctrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkGpout0ctrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkGpout0ctrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + ClkGpout0ctrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkGpout0ctrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkGpout0ctrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkGpout0ctrlAuxsrc::CLK_SYS => clk_sys_freq(), + ClkGpout0ctrlAuxsrc::CLK_USB => clk_usb_freq(), + ClkGpout0ctrlAuxsrc::CLK_ADC => clk_adc_freq(), + ClkGpout0ctrlAuxsrc::CLK_RTC => clk_rtc_freq(), + ClkGpout0ctrlAuxsrc::CLK_REF => clk_ref_freq(), + _ => unreachable!(), + }; + + let div = unsafe { c.clk_gpout0_div().read() }; + let int = if div.int() == 0 { 65536 } else { div.int() }; + // TODO handle fractional clock div + let _frac = div.frac(); + + base / int +} + +pub fn clk_gpout1_freq() -> u32 { + let c = pac::CLOCKS; + let src = unsafe { c.clk_gpout1_ctrl().read().auxsrc() }; + + let base = match src { + ClkGpout1ctrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkGpout1ctrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkGpout1ctrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + ClkGpout1ctrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkGpout1ctrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkGpout1ctrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkGpout1ctrlAuxsrc::CLK_SYS => clk_sys_freq(), + ClkGpout1ctrlAuxsrc::CLK_USB => clk_usb_freq(), + ClkGpout1ctrlAuxsrc::CLK_ADC => clk_adc_freq(), + ClkGpout1ctrlAuxsrc::CLK_RTC => clk_rtc_freq(), + ClkGpout1ctrlAuxsrc::CLK_REF => clk_ref_freq(), + _ => unreachable!(), + }; + + let div = unsafe { c.clk_gpout1_div().read() }; + let int = if div.int() == 0 { 65536 } else { div.int() }; + // TODO handle fractional clock div + let _frac = div.frac(); + + base / int +} + unsafe fn start_xosc(crystal_hz: u32) { pac::XOSC .ctrl() @@ -526,6 +637,48 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { p.pwr().modify(|w| w.set_postdivpd(false)); } +pub struct Gpout0 { + _pin: crate::peripherals::PIN_21, +} + +impl Gpout0 { + pub fn new(pin: crate::peripherals::PIN_21) -> Self { + unsafe { + let p = pac::IO_BANK0.gpio(21).ctrl(); + p.write(|w| w.set_funcsel(pac::io::vals::Gpio21ctrlFuncsel::CLOCKS_GPOUT_0.0)) + } + Self { _pin: pin } + } + + pub fn set_div(&self, int: u32, frac: u8) { + unsafe { + let c = pac::CLOCKS; + c.clk_gpout0_div().write(|w| { + w.set_int(int); + w.set_frac(frac); + }); + } + } + + pub fn set_src(&self, src: ClkGpout0ctrlAuxsrc) { + unsafe { + let c = pac::CLOCKS; + c.clk_gpout0_ctrl().modify(|w| { + w.set_auxsrc(src); + }); + } + } + + pub fn enable(&self) { + unsafe { + let c = pac::CLOCKS; + c.clk_gpout0_ctrl().modify(|w| { + w.set_enable(true); + }); + } + } +} + /// Random number generator based on the ROSC RANDOMBIT register. /// /// This will not produce random values if the ROSC is stopped or run at some diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs new file mode 100644 index 000000000..0503212e8 --- /dev/null +++ b/examples/rp/src/bin/gpout.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::{clocks, pac}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + //let mut led = Output::new(p.PIN_25, Level::Low); + + let gpout0 = clocks::Gpout0::new(p.PIN_21); + gpout0.set_src(pac::clocks::vals::ClkGpout0ctrlAuxsrc::CLK_SYS); + gpout0.set_div(1000, 0); + gpout0.enable(); + + info!("Pin 21 should be toggling at {} hz", clocks::clk_gpout0_freq()); +} From 2910b09cba432d79befa42a7f994c60dfe832d40 Mon Sep 17 00:00:00 2001 From: Fabian Kunze Date: Mon, 8 May 2023 17:53:39 +0200 Subject: [PATCH 1076/1575] bumped rp-pac version --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 2748c0591..b2b7fb083 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,7 +61,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { features = ["rt"], git = "https://github.com/fakusb/rp-pac.git", rev = "b1e0a150f37efb69644f5474d879e9180eeb167b" } +rp-pac = { version = "3", 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.10", optional = true} From 008b1fd30c945fdfb36d95dcfc38ffa9cbcf2cf1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 8 May 2023 20:26:17 +0200 Subject: [PATCH 1077/1575] update defmt to 0.3.4, now that probe-run is fixed. --- examples/rpi-pico-w/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 8df65e188..5b46726d2 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -14,7 +14,7 @@ embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium atomic-polyfill = "0.1.5" static_cell = "1.0" -defmt = "=0.3.2" +defmt = "0.3.4" defmt-rtt = "0.3" panic-probe = { version = "0.3", features = ["print-defmt"] } From d3d424dad348c78222a6d962e2d51b56b485807d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 8 May 2023 20:27:21 +0200 Subject: [PATCH 1078/1575] remove comment. --- cyw43-pio/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index c5632d5fd..8272903c3 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -3,8 +3,6 @@ name = "cyw43-pio" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] cyw43 = { path = "../" } embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] } From a7dee5b65c602637f8209d46d4611ed846a17459 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 8 May 2023 20:27:44 +0200 Subject: [PATCH 1079/1575] Change all logging level to debug. --- src/control.rs | 14 +++++++------- src/runner.rs | 22 +++++++++++----------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/control.rs b/src/control.rs index 3d7d4dd38..6919d569e 100644 --- a/src/control.rs +++ b/src/control.rs @@ -35,7 +35,7 @@ impl<'a> Control<'a> { pub async fn init(&mut self, clm: &[u8]) { const CHUNK_SIZE: usize = 1024; - info!("Downloading CLM..."); + debug!("Downloading CLM..."); let mut offs = 0; for chunk in clm.chunks(CHUNK_SIZE) { @@ -65,7 +65,7 @@ impl<'a> Control<'a> { // check clmload ok assert_eq!(self.get_iovar_u32("clmload_status").await, 0); - info!("Configuring misc stuff..."); + debug!("Configuring misc stuff..."); // Disable tx gloming which transfers multiple packets in one request. // 'glom' is short for "conglomerate" which means "gather together into @@ -76,7 +76,7 @@ impl<'a> Control<'a> { // read MAC addr. let mut mac_addr = [0; 6]; assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); - info!("mac addr: {:02x}", Bytes(&mac_addr)); + debug!("mac addr: {:02x}", Bytes(&mac_addr)); let country = countries::WORLD_WIDE_XX; let country_info = CountryInfo { @@ -135,7 +135,7 @@ impl<'a> Control<'a> { self.state_ch.set_ethernet_address(mac_addr); - info!("INIT DONE"); + debug!("INIT DONE"); } pub async fn set_power_management(&mut self, mode: PowerManagementMode) { @@ -226,7 +226,7 @@ impl<'a> Control<'a> { if status == EStatus::SUCCESS { // successful join self.state_ch.set_link_state(LinkState::Up); - info!("JOINED"); + debug!("JOINED"); Ok(()) } else { warn!("JOIN failed with status={} auth={}", status, auth_status); @@ -330,7 +330,7 @@ impl<'a> Control<'a> { } async fn set_iovar_v(&mut self, name: &str, val: &[u8]) { - info!("set {} = {:02x}", name, Bytes(val)); + debug!("set {} = {:02x}", name, Bytes(val)); let mut buf = [0; BUFSIZE]; buf[..name.len()].copy_from_slice(name.as_bytes()); @@ -344,7 +344,7 @@ impl<'a> Control<'a> { // TODO this is not really working, it always returns all zeros. async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { - info!("get {}", name); + debug!("get {}", name); let mut buf = [0; 64]; buf[..name.len()].copy_from_slice(name.as_bytes()); diff --git a/src/runner.rs b/src/runner.rs index 56b9a609c..98f8aff7f 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -80,12 +80,12 @@ where self.bus .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) .await; - info!("waiting for clock..."); + debug!("waiting for clock..."); while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} - info!("clock ok"); + debug!("clock ok"); let chip_id = self.bus.bp_read16(0x1800_0000).await; - info!("chip ID: {}", chip_id); + debug!("chip ID: {}", chip_id); // Upload firmware. self.core_disable(Core::WLAN).await; @@ -95,10 +95,10 @@ where let ram_addr = CHIP.atcm_ram_base_address; - info!("loading fw"); + debug!("loading fw"); self.bus.bp_write(ram_addr, firmware).await; - info!("loading nvram"); + debug!("loading nvram"); // Round up to 4 bytes. let nvram_len = (NVRAM.len() + 3) / 4 * 4; self.bus @@ -112,7 +112,7 @@ where .await; // Start core! - info!("starting up core..."); + debug!("starting up core..."); self.core_reset(Core::WLAN).await; assert!(self.core_is_up(Core::WLAN).await); @@ -132,7 +132,7 @@ where .await; // wait for wifi startup - info!("waiting for wifi init..."); + debug!("waiting for wifi init..."); while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} // Some random configs related to sleep. @@ -158,14 +158,14 @@ where // start HT clock //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; - //info!("waiting for HT clock..."); + //debug!("waiting for HT clock..."); //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} - //info!("clock ok"); + //debug!("clock ok"); #[cfg(feature = "firmware-logs")] self.log_init().await; - info!("init done "); + debug!("wifi init done"); } #[cfg(feature = "firmware-logs")] @@ -174,7 +174,7 @@ where let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; let shared_addr = self.bus.bp_read32(addr).await; - info!("shared_addr {:08x}", shared_addr); + debug!("shared_addr {:08x}", shared_addr); let mut shared = [0; SharedMemData::SIZE]; self.bus.bp_read(shared_addr, &mut shared).await; From 881e9d07d2e1107b27952c8bfe1d5afeef172165 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 8 May 2023 21:45:54 +0200 Subject: [PATCH 1080/1575] Fix missing padding in tx. Makes max-sized packets not get dropped --- src/runner.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 98f8aff7f..1d8ec4359 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -249,7 +249,16 @@ where let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); - let total_len = SdpcmHeader::SIZE + BcdHeader::SIZE + packet.len(); + // There MUST be 2 bytes of padding between the SDPCM and BCD headers. + // And ONLY for data packets! + // No idea why, but the firmware will append two zero bytes to the tx'd packets + // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it + // be oversized and get dropped. + // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90 + // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597 + // ¯\_(ツ)_/¯ + const PADDING_SIZE: usize = 2; + let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BcdHeader::SIZE + packet.len(); let seq = self.sdpcm_seq; self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); @@ -260,7 +269,7 @@ where sequence: seq, channel_and_flags: CHANNEL_TYPE_DATA, next_length: 0, - header_length: SdpcmHeader::SIZE as _, + header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _, wireless_flow_control: 0, bus_data_credit: 0, reserved: [0, 0], @@ -276,8 +285,10 @@ where trace!(" {:?}", bcd_header); buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE..][..BcdHeader::SIZE].copy_from_slice(&bcd_header.to_bytes()); - buf8[SdpcmHeader::SIZE + BcdHeader::SIZE..][..packet.len()].copy_from_slice(packet); + buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BcdHeader::SIZE] + .copy_from_slice(&bcd_header.to_bytes()); + buf8[SdpcmHeader::SIZE + PADDING_SIZE + BcdHeader::SIZE..][..packet.len()] + .copy_from_slice(packet); let total_len = (total_len + 3) & !3; // round up to 4byte From 6b5d9642d583bc034ee35b88c903545e7f423b4e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 8 May 2023 22:01:44 +0200 Subject: [PATCH 1081/1575] Rename BCD -> BDC. That's what Broadcom calls it. Still no idea what it means. --- src/runner.rs | 24 ++++++++++++------------ src/structs.rs | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 1d8ec4359..5706696b4 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -249,7 +249,7 @@ where let mut buf = [0; 512]; let buf8 = slice8_mut(&mut buf); - // There MUST be 2 bytes of padding between the SDPCM and BCD headers. + // There MUST be 2 bytes of padding between the SDPCM and BDC headers. // And ONLY for data packets! // No idea why, but the firmware will append two zero bytes to the tx'd packets // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it @@ -258,7 +258,7 @@ where // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597 // ¯\_(ツ)_/¯ const PADDING_SIZE: usize = 2; - let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BcdHeader::SIZE + packet.len(); + let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len(); let seq = self.sdpcm_seq; self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); @@ -275,19 +275,19 @@ where reserved: [0, 0], }; - let bcd_header = BcdHeader { + let bdc_header = BdcHeader { flags: BDC_VERSION << BDC_VERSION_SHIFT, priority: 0, flags2: 0, data_offset: 0, }; trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", bcd_header); + trace!(" {:?}", bdc_header); buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BcdHeader::SIZE] - .copy_from_slice(&bcd_header.to_bytes()); - buf8[SdpcmHeader::SIZE + PADDING_SIZE + BcdHeader::SIZE..][..packet.len()] + buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE] + .copy_from_slice(&bdc_header.to_bytes()); + buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()] .copy_from_slice(packet); let total_len = (total_len + 3) & !3; // round up to 4byte @@ -366,13 +366,13 @@ where } } CHANNEL_TYPE_EVENT => { - let Some((_, bcd_packet)) = BcdHeader::parse(payload) else { - warn!("BCD event, incomplete header"); + let Some((_, bdc_packet)) = BdcHeader::parse(payload) else { + warn!("BDC event, incomplete header"); return; }; - let Some((event_packet, evt_data)) = EventPacket::parse(bcd_packet) else { - warn!("BCD event, incomplete data"); + let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else { + warn!("BDC event, incomplete data"); return; }; @@ -439,7 +439,7 @@ where } } CHANNEL_TYPE_DATA => { - let Some((_, packet)) = BcdHeader::parse(payload) else { return }; + let Some((_, packet)) = BdcHeader::parse(payload) else { return }; trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); match self.ch.try_rx_buf() { diff --git a/src/structs.rs b/src/structs.rs index 3b646e1a8..5ba633c74 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -165,7 +165,7 @@ pub const BDC_VERSION_SHIFT: u8 = 4; #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] -pub struct BcdHeader { +pub struct BdcHeader { pub flags: u8, /// 802.1d Priority (low 3 bits) pub priority: u8, @@ -173,24 +173,24 @@ pub struct BcdHeader { /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. pub data_offset: u8, } -impl_bytes!(BcdHeader); +impl_bytes!(BdcHeader); -impl BcdHeader { +impl BdcHeader { pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { if packet.len() < Self::SIZE { return None; } - let (bcd_header, bcd_packet) = packet.split_at_mut(Self::SIZE); - let bcd_header = Self::from_bytes_mut(bcd_header.try_into().unwrap()); - trace!(" {:?}", bcd_header); + let (bdc_header, bdc_packet) = packet.split_at_mut(Self::SIZE); + let bdc_header = Self::from_bytes_mut(bdc_header.try_into().unwrap()); + trace!(" {:?}", bdc_header); - let packet_start = 4 * bcd_header.data_offset as usize; + let packet_start = 4 * bdc_header.data_offset as usize; - let bcd_packet = bcd_packet.get_mut(packet_start..)?; - trace!(" {:02x}", Bytes(&bcd_packet[..bcd_packet.len().min(36)])); + let bdc_packet = bdc_packet.get_mut(packet_start..)?; + trace!(" {:02x}", Bytes(&bdc_packet[..bdc_packet.len().min(36)])); - Some((bcd_header, bcd_packet)) + Some((bdc_header, bdc_packet)) } } From 0584312ef0324d2ac67dbb9517176fabf628eec9 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Mon, 8 May 2023 23:25:01 +0200 Subject: [PATCH 1082/1575] Fix some typos --- docs/modules/ROOT/pages/getting_started.adoc | 2 +- embassy-hal-common/src/atomic_ring_buffer.rs | 4 ++-- embassy-nrf/src/nvmc.rs | 2 +- embassy-nrf/src/twim.rs | 2 +- embassy-nrf/src/twis.rs | 14 +++++++------- embassy-rp/src/flash.rs | 2 +- embassy-rp/src/i2c.rs | 2 +- embassy-rp/src/uart/buffered.rs | 2 +- embassy-rp/src/uart/mod.rs | 4 ++-- embassy-stm32/src/time_driver.rs | 2 +- embassy-stm32/src/usart/mod.rs | 6 +++--- embassy-usb-driver/src/lib.rs | 2 +- embassy-usb/src/class/cdc_ncm/mod.rs | 4 ++-- embassy-usb/src/class/hid.rs | 2 +- embassy-usb/src/msos.rs | 2 +- examples/nrf52840/src/bin/pubsub.rs | 4 ++-- examples/nrf52840/src/bin/usb_serial.rs | 2 +- examples/nrf52840/src/bin/usb_serial_multitask.rs | 2 +- examples/nrf52840/src/bin/usb_serial_winusb.rs | 2 +- examples/rp/src/bin/usb_serial.rs | 2 +- .../stm32f0/src/bin/button_controlled_blink.rs | 4 ++-- examples/stm32f4/src/bin/usb_serial.rs | 2 +- examples/stm32f7/src/bin/usb_serial.rs | 2 +- examples/stm32h5/src/bin/usb_serial.rs | 2 +- examples/stm32h7/src/bin/usb_serial.rs | 2 +- examples/stm32l4/src/bin/usb_serial.rs | 2 +- examples/stm32u5/src/bin/usb_serial.rs | 2 +- examples/stm32wl/src/bin/uart_async.rs | 2 +- 28 files changed, 41 insertions(+), 41 deletions(-) diff --git a/docs/modules/ROOT/pages/getting_started.adoc b/docs/modules/ROOT/pages/getting_started.adoc index 9015d7845..881e449b6 100644 --- a/docs/modules/ROOT/pages/getting_started.adoc +++ b/docs/modules/ROOT/pages/getting_started.adoc @@ -49,7 +49,7 @@ cd examples/nrf52840 cargo run --bin blinky --release ---- -== Whats next? +== What's next? Congratulations, you have your first Embassy application running! Here are some alternatives on where to go from here: diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index afd3ce1de..0eb39cb33 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -133,7 +133,7 @@ impl<'a> Writer<'a> { /// Push one data byte. /// - /// Returns true if pushed succesfully. + /// Returns true if pushed successfully. pub fn push_one(&mut self, val: u8) -> bool { let n = self.push(|f| match f { [] => 0, @@ -265,7 +265,7 @@ impl<'a> Reader<'a> { /// Pop one data byte. /// - /// Returns true if popped succesfully. + /// Returns true if popped successfully. pub fn pop_one(&mut self) -> Option { let mut res = None; self.pop(|f| match f { diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 6f48853d5..91a5a272f 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -24,7 +24,7 @@ pub const FLASH_SIZE: usize = crate::chip::FLASH_SIZE; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// Opration using a location not in flash. + /// Operation using a location not in flash. OutOfBounds, /// Unaligned operation or using unaligned buffers. Unaligned, diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 9ae569609..cab36884f 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -336,7 +336,7 @@ impl<'d, T: Instance> Twim<'d, T> { return Poll::Ready(()); } - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index bfa30b044..f68a9940a 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -320,7 +320,7 @@ impl<'d, T: Instance> Twis<'d, T> { fn blocking_listen_wait_end(&mut self, status: Status) -> Result { let r = T::regs(); loop { - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); @@ -346,7 +346,7 @@ impl<'d, T: Instance> Twis<'d, T> { fn blocking_wait(&mut self) -> Result { let r = T::regs(); loop { - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); @@ -372,7 +372,7 @@ impl<'d, T: Instance> Twis<'d, T> { let r = T::regs(); let deadline = Instant::now() + timeout; loop { - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); @@ -432,7 +432,7 @@ impl<'d, T: Instance> Twis<'d, T> { let r = T::regs(); let deadline = Instant::now() + timeout; loop { - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); @@ -465,7 +465,7 @@ impl<'d, T: Instance> Twis<'d, T> { s.waker.register(cx.waker()); - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); @@ -495,7 +495,7 @@ impl<'d, T: Instance> Twis<'d, T> { s.waker.register(cx.waker()); - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); @@ -522,7 +522,7 @@ impl<'d, T: Instance> Twis<'d, T> { s.waker.register(cx.waker()); - // stop if an error occured + // stop if an error occurred if r.events_error.read().bits() != 0 { r.events_error.reset(); r.tasks_stop.write(|w| unsafe { w.bits(1) }); diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 6ab05ff0b..51c7af913 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -25,7 +25,7 @@ pub const ERASE_SIZE: usize = 4096; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// Opration using a location not in flash. + /// Operation using a location not in flash. OutOfBounds, /// Unaligned operation or using unaligned buffers. Unaligned, diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index cd5364393..d5dc94406 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -551,7 +551,7 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { if abort_reason.is_err() || (send_stop && last) { // If the transaction was aborted or if it completed - // successfully wait until the STOP condition has occured. + // successfully wait until the STOP condition has occurred. while !p.ic_raw_intr_stat().read().stop_det() {} diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 59fec8f1b..9d3de1bd8 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -544,7 +544,7 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { s.rx_waker.wake(); } // Disable any further RX interrupts when the buffer becomes full or - // errors have occured. this lets us buffer additional errors in the + // errors have occurred. This lets us buffer additional errors in the // fifo without needing more error storage locations, and most applications // will want to do a full reset of their uart state anyway once an error // has happened. diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 4084c158a..a0ee6b4ce 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -231,7 +231,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { } impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { - /// Create a new DMA-enabled UART which can only recieve data + /// Create a new DMA-enabled UART which can only receive data pub fn new( _uart: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -690,7 +690,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { self.tx.send_break(bits).await } - /// Split the Uart into a transmitter and receiver, which is particuarly + /// Split the Uart into a transmitter and receiver, which is particularly /// useful when having two tasks correlating to transmitting and receiving. pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { (self.tx, self.rx) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 8e84570a4..d45c90dd8 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -250,7 +250,7 @@ impl RtcDriver { // Call after clearing alarm, so the callback can set another alarm. // safety: - // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. + // - we can ignore the possibility of `f` being unset (null) because of the safety contract of `allocate_alarm`. // - other than that we only store valid function pointers into alarm.callback let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; f(alarm.ctx.get()); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dbce668c2..e946762af 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -112,7 +112,7 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { } impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { - /// usefull if you only want Uart Tx. It saves 1 pin and consumes a little less power + /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, @@ -210,7 +210,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { - /// usefull if you only want Uart Rx. It saves 1 pin and consumes a little less power + /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -757,7 +757,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } /// Split the Uart into a transmitter and receiver, which is - /// particuarly useful when having two tasks correlating to + /// particularly useful when having two tasks correlating to /// transmitting and receiving. pub fn split(self) -> (UartTx<'d, T, TxDma>, UartRx<'d, T, RxDma>) { (self.tx, self.rx) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index e8f71a2dc..2c05f94f7 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -97,7 +97,7 @@ impl EndpointAddress { } } -/// Infomation for an endpoint. +/// Information for an endpoint. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointInfo { diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 262499ccb..d5556dd0b 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -370,7 +370,7 @@ pub struct Sender<'d, D: Driver<'d>> { impl<'d, D: Driver<'d>> Sender<'d, D> { /// Write a packet. /// - /// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers. + /// This waits until the packet is successfully stored in the CDC-NCM endpoint buffers. pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { let seq = self.seq; self.seq = self.seq.wrapping_add(1); @@ -436,7 +436,7 @@ pub struct Receiver<'d, D: Driver<'d>> { impl<'d, D: Driver<'d>> Receiver<'d, D> { /// Write a network packet. /// - /// This waits until a packet is succesfully received from the endpoint buffers. + /// This waits until a packet is successfully received from the endpoint buffers. pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result { // Retry loop loop { diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 03e4c1dbb..889d66ec5 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -165,7 +165,7 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit } } - /// Splits into seperate readers/writers for input and output reports. + /// Splits into separate readers/writers for input and output reports. pub fn split(self) -> (HidReader<'d, D, READ_N>, HidWriter<'d, D, WRITE_N>) { (self.reader, self.writer) } diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 218d9931a..187b2ff8e 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -186,7 +186,7 @@ impl<'d> MsOsDescriptorWriter<'d> { capability_type::PLATFORM, &[ 0, // reserved - // platform capability UUID, Microsoft OS 2.0 platform compabitility + // platform capability UUID, Microsoft OS 2.0 platform compatibility 0xdf, 0x60, 0xdd, diff --git a/examples/nrf52840/src/bin/pubsub.rs b/examples/nrf52840/src/bin/pubsub.rs index 688e6d075..cca60ebc9 100644 --- a/examples/nrf52840/src/bin/pubsub.rs +++ b/examples/nrf52840/src/bin/pubsub.rs @@ -74,9 +74,9 @@ async fn fast_logger(mut messages: Subscriber<'static, ThreadModeRawMutex, Messa } /// A logger task that awaits the messages, but also does some other work. -/// Because of this, depeding on how the messages were published, the subscriber might miss some messages +/// Because of this, depending on how the messages were published, the subscriber might miss some messages. /// -/// This takes the dynamic `DynSubscriber`. This is not as performant as the generic version, but let's you ignore some of the generics +/// This takes the dynamic `DynSubscriber`. This is not as performant as the generic version, but let's you ignore some of the generics. #[embassy_executor::task] async fn slow_logger(mut messages: DynSubscriber<'static, Message>) { loop { diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 9727a4f57..dc95cde84 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -40,7 +40,7 @@ async fn main(_spawner: Spawner) { config.max_power = 100; config.max_packet_size_0 = 64; - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 6da2c2a2f..ac22d9499 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -66,7 +66,7 @@ async fn main(spawner: Spawner) { config.max_power = 100; config.max_packet_size_0 = 64; - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 6e4f71a48..1d39d3841 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { config.max_power = 100; config.max_packet_size_0 = 64; - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index a991082ee..8160a1875 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -30,7 +30,7 @@ async fn main(_spawner: Spawner) { config.max_power = 100; config.max_packet_size_0 = 64; - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs index e1f223433..f362c53f5 100644 --- a/examples/stm32f0/src/bin/button_controlled_blink.rs +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs @@ -17,8 +17,8 @@ static BLINK_MS: AtomicU32 = AtomicU32::new(0); #[embassy_executor::task] async fn led_task(led: AnyPin) { - // Configure the LED pin as a push pull ouput and obtain handler. - // On the Nucleo F091RC theres an on-board LED connected to pin PA5. + // Configure the LED pin as a push pull output and obtain handler. + // On the Nucleo F091RC there's an on-board LED connected to pin PA5. let mut led = Output::new(led, Level::Low, Speed::Low); loop { diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index baabc1a2d..d2b1dca43 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) { config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 5fd9d2ec9..dca90d9cb 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -35,7 +35,7 @@ async fn main(_spawner: Spawner) { config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 6af269c1d..4f987cbd1 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -57,7 +57,7 @@ async fn main(_spawner: Spawner) { config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 9ef520ae2..475af116d 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -34,7 +34,7 @@ async fn main(_spawner: Spawner) { config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 663f60d52..bdb290e63 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index 8cd3bf2f4..4882cd2e0 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { config.product = Some("USB-serial example"); config.serial_number = Some("12345678"); - // Required for windows compatiblity. + // Required for windows compatibility. // 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; diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs index f12fec4c8..ac8766af6 100644 --- a/examples/stm32wl/src/bin/uart_async.rs +++ b/examples/stm32wl/src/bin/uart_async.rs @@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) { //Write suc. } Err(..) => { - //Wasnt able to write + //Wasn't able to write } } } From 5df263db38c593ca6946a854c4b53e6224285332 Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Mon, 8 May 2023 23:28:01 +0200 Subject: [PATCH 1083/1575] Update GitHub Actions CI The following updates are performed: * update actions/cache to v3 * update actions/checkout to v3 --- .github/workflows/doc.yml | 2 +- .github/workflows/rust.yml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 411b7589f..b4e225e64 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -29,7 +29,7 @@ jobs: concurrency: doc-${{ matrix.crates }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true - name: Install Rust targets diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 79354fe70..47dc8fd7a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,11 +22,11 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true - name: Cache multiple paths - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.cargo/bin/ @@ -44,11 +44,11 @@ jobs: build-stable: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true - name: Cache multiple paths - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.cargo/bin/ @@ -67,7 +67,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Test boot working-directory: ./embassy-boot/boot From 14eecf2fc44d9894e7e053280f771a0e98c255ef Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 8 May 2023 19:13:50 -0400 Subject: [PATCH 1084/1575] Address Dirbaio comments Gpout still incomplete. --- embassy-rp/src/clocks.rs | 241 ++++++++++++++++++++++++--------------- 1 file changed, 147 insertions(+), 94 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 5d3444442..def34fc70 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -6,32 +6,25 @@ use crate::{pac, reset}; static mut XIN_HZ: u32 = 0; pub struct ClockConfig { - rosc_config: Option, - xosc_config: Option, - ref_clk_config: (RefClkSrc, u8), - sys_clk_config: (SysClkSrc, u32), + rosc: Option, + xosc: Option, + ref_clk: RefClkConfig, + sys_clk: SysClkConfig, peri_clk_src: Option, - usb_clk_config: Option<(ClkUsbCtrlAuxsrc, u8)>, - adc_clk_config: Option<(ClkAdcCtrlAuxsrc, u8)>, - rtc_clk_config: Option<(ClkRtcCtrlAuxsrc, u32)>, + usb_clk: Option, + adc_clk: Option, + rtc_clk: Option, } impl ClockConfig { pub fn crystal(crystal_hz: u32) -> Self { Self { - rosc_config: Some(RoscConfig { + rosc: Some(RoscConfig { range: pac::rosc::vals::FreqRange::MEDIUM, - drive_strength_0: 0, - drive_strength_1: 0, - drive_strength_2: 0, - drive_strength_3: 0, - drive_strength_4: 0, - drive_strength_5: 0, - drive_strength_6: 0, - drive_strength_7: 0, + drive_strength: [0; 8], div: 16, }), - xosc_config: Some(XoscConfig { + xosc: Some(XoscConfig { hz: crystal_hz, clock_type: ExternalClock::Crystal, sys_pll: Some(PllConfig { @@ -47,42 +40,68 @@ impl ClockConfig { post_div2: 2, }), }), - ref_clk_config: (RefClkSrc::Xosc, 1), - sys_clk_config: (SysClkSrc::Aux(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS), 1), + ref_clk: RefClkConfig { + src: RefClkSrc::Xosc, + div: 1, + }, + sys_clk: SysClkConfig { + src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS), + div_int: 1, + div_frac: 0, + }, peri_clk_src: Some(ClkPeriCtrlAuxsrc::CLK_SYS), - usb_clk_config: Some((ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB, 1)), - adc_clk_config: Some((ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB, 1)), - rtc_clk_config: Some((ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB, 1024)), + usb_clk: Some(UsbClkConfig { + src: ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB, + div: 1, + }), + adc_clk: Some(AdcClkConfig { + src: ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB, + div: 1, + }), + rtc_clk: Some(RtcClkConfig { + src: ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB, + div_int: 1024, + div_frac: 0, + }), } } pub fn rosc() -> Self { Self { - rosc_config: Some(RoscConfig { + rosc: Some(RoscConfig { range: pac::rosc::vals::FreqRange::HIGH, - drive_strength_0: 0, - drive_strength_1: 0, - drive_strength_2: 0, - drive_strength_3: 0, - drive_strength_4: 0, - drive_strength_5: 0, - drive_strength_6: 0, - drive_strength_7: 0, + drive_strength: [0; 8], div: 1, }), - xosc_config: None, - ref_clk_config: (RefClkSrc::Rosc, 4), - sys_clk_config: (SysClkSrc::Aux(ClkSysCtrlAuxsrc::ROSC_CLKSRC), 1), + xosc: None, + ref_clk: RefClkConfig { + src: RefClkSrc::Rosc, + div: 1, + }, + sys_clk: SysClkConfig { + src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::ROSC_CLKSRC), + div_int: 1, + div_frac: 0, + }, peri_clk_src: Some(ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH), - usb_clk_config: None, - adc_clk_config: Some((ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH, 1)), - rtc_clk_config: Some((ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH, 1024)), + usb_clk: None, + adc_clk: Some(AdcClkConfig { + src: ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH, + div: 1, + }), + rtc_clk: Some(RtcClkConfig { + src: ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH, + div_int: 1024, + div_frac: 0, + }), } } } -pub enum ExternalClock { - Crystal, - Clock, + +pub struct RoscConfig { + range: pac::rosc::vals::FreqRange, + drive_strength: [u8; 8], + div: u16, } pub struct XoscConfig { @@ -92,19 +111,6 @@ pub struct XoscConfig { usb_pll: Option, } -pub struct RoscConfig { - range: pac::rosc::vals::FreqRange, - drive_strength_0: u8, - drive_strength_1: u8, - drive_strength_2: u8, - drive_strength_3: u8, - drive_strength_4: u8, - drive_strength_5: u8, - drive_strength_6: u8, - drive_strength_7: u8, - div: u16, -} - pub struct PllConfig { pub refdiv: u32, pub vco_freq: u32, @@ -112,9 +118,13 @@ pub struct PllConfig { pub post_div2: u8, } +pub enum ExternalClock { + Crystal, + Clock, +} pub struct RefClkConfig { - pub src: RefClkSrc, - pub div: u8, + src: RefClkSrc, + div: u8, } pub enum RefClkSrc { @@ -123,16 +133,33 @@ pub enum RefClkSrc { Aux(ClkRefCtrlAuxsrc), } -pub struct SysClkConfig { - pub src: SysClkSrc, - pub div: u32, -} - pub enum SysClkSrc { Ref, Aux(ClkSysCtrlAuxsrc), } +pub struct SysClkConfig { + src: SysClkSrc, + div_int: u32, + div_frac: u8, +} + +pub struct UsbClkConfig { + src: ClkUsbCtrlAuxsrc, + div: u8, +} + +pub struct AdcClkConfig { + src: ClkAdcCtrlAuxsrc, + div: u8, +} + +pub struct RtcClkConfig { + src: ClkRtcCtrlAuxsrc, + div_int: u32, + div_frac: u8, +} + /// safety: must be called exactly once at bootup pub(crate) unsafe fn init(config: ClockConfig) { // Reset everything except: @@ -160,11 +187,11 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH)); while c.clk_ref_selected().read() != 1 {} - if let Some(config) = config.rosc_config { + if let Some(config) = config.rosc { configure_rosc(config); } - if let Some(config) = config.xosc_config { + if let Some(config) = config.xosc { XIN_HZ = config.hz; pac::WATCHDOG.tick().write(|w| { @@ -188,21 +215,18 @@ pub(crate) unsafe fn init(config: ClockConfig) { } } - let (src, div) = config.ref_clk_config; - match src { + match config.ref_clk.src { RefClkSrc::Xosc => { c.clk_ref_ctrl().write(|w| { w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); }); while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {} - c.clk_ref_div().write(|w| w.set_int(div)); } RefClkSrc::Rosc => { c.clk_ref_ctrl().write(|w| { w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH); }); while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::ROSC_CLKSRC_PH.0 {} - c.clk_ref_div().write(|w| w.set_int(div)); } RefClkSrc::Aux(src) => { c.clk_ref_ctrl().write(|w| { @@ -210,23 +234,23 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_src(ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX); }); while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX.0 {} - c.clk_ref_div().write(|w| w.set_int(div)); } } + c.clk_ref_div().write(|w| { + w.set_int(config.ref_clk.div); + }); pac::WATCHDOG.tick().write(|w| { w.set_cycles((clk_ref_freq() / 1_000_000) as u16); w.set_enable(true); }); - let (src, div) = config.sys_clk_config; - match src { + match config.sys_clk.src { SysClkSrc::Ref => { c.clk_sys_ctrl().write(|w| { w.set_src(ClkSysCtrlSrc::CLK_REF); }); while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - c.clk_sys_div().write(|w| w.set_int(div)); } SysClkSrc::Aux(src) => { c.clk_sys_ctrl().write(|w| { @@ -234,7 +258,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { }); while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - c.clk_sys_div().write(|w| w.set_int(div)); c.clk_sys_ctrl().write(|w| { w.set_auxsrc(src); w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); @@ -242,6 +265,10 @@ pub(crate) unsafe fn init(config: ClockConfig) { while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {} } } + c.clk_sys_div().write(|w| { + w.set_int(config.sys_clk.div_int); + w.set_frac(config.sys_clk.div_frac); + }); let mut peris = reset::ALL_PERIPHERALS; @@ -257,37 +284,40 @@ pub(crate) unsafe fn init(config: ClockConfig) { peris.set_uart1(false); } - if let Some((src, div)) = config.usb_clk_config { + if let Some(conf) = config.usb_clk { // CLK USB = PLL USB (48MHz) / 1 = 48MHz - c.clk_usb_div().write(|w| w.set_int(div)); + c.clk_usb_div().write(|w| w.set_int(conf.div)); c.clk_usb_ctrl().write(|w| { w.set_enable(true); - w.set_auxsrc(src); + w.set_auxsrc(conf.src); }); } else { peris.set_usbctrl(false); } - if let Some((src, div)) = config.adc_clk_config { + if let Some(conf) = config.adc_clk { // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz - c.clk_adc_div().write(|w| w.set_int(div)); + c.clk_adc_div().write(|w| w.set_int(conf.div)); c.clk_adc_ctrl().write(|w| { w.set_enable(true); - w.set_auxsrc(src); + w.set_auxsrc(conf.src); }); } else { peris.set_adc(false); } - if let Some((src, div)) = config.rtc_clk_config { + if let Some(conf) = config.rtc_clk { // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz c.clk_rtc_ctrl().modify(|w| { w.set_enable(false); }); - c.clk_rtc_div().write(|w| w.set_int(div)); + c.clk_rtc_div().write(|w| { + w.set_int(conf.div_int); + w.set_frac(conf.div_frac); + }); c.clk_rtc_ctrl().write(|w| { w.set_enable(true); - w.set_auxsrc(src); + w.set_auxsrc(conf.src); }); } else { peris.set_rtc(false); @@ -302,18 +332,18 @@ unsafe fn configure_rosc(config: RoscConfig) { p.freqa().write(|w| { w.set_passwd(pac::rosc::vals::Passwd::PASS); - w.set_ds0(config.drive_strength_0); - w.set_ds1(config.drive_strength_1); - w.set_ds2(config.drive_strength_2); - w.set_ds3(config.drive_strength_3); + w.set_ds0(config.drive_strength[0]); + w.set_ds1(config.drive_strength[1]); + w.set_ds2(config.drive_strength[2]); + w.set_ds3(config.drive_strength[3]); }); p.freqb().write(|w| { w.set_passwd(pac::rosc::vals::Passwd::PASS); - w.set_ds4(config.drive_strength_4); - w.set_ds5(config.drive_strength_5); - w.set_ds6(config.drive_strength_6); - w.set_ds7(config.drive_strength_7); + w.set_ds4(config.drive_strength[4]); + w.set_ds5(config.drive_strength[5]); + w.set_ds6(config.drive_strength[6]); + w.set_ds7(config.drive_strength[7]); }); p.div().write(|w| { @@ -637,17 +667,31 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { p.pwr().modify(|w| w.set_postdivpd(false)); } -pub struct Gpout0 { - _pin: crate::peripherals::PIN_21, +pub trait GpoutPin {} + +impl GpoutPin for crate::peripherals::PIN_21 {} +impl GpoutPin for crate::peripherals::PIN_23 {} +impl GpoutPin for crate::peripherals::PIN_24 {} +impl GpoutPin for crate::peripherals::PIN_25 {} + +use embassy_hal_common::{into_ref, PeripheralRef}; + +use crate::Peripheral; + +pub struct Gpout<'d, T: GpoutPin> { + _pin: PeripheralRef<'d, T>, } -impl Gpout0 { - pub fn new(pin: crate::peripherals::PIN_21) -> Self { +impl<'d, T: GpoutPin> Gpout<'d, T> { + pub fn new(_pin: impl Peripheral

+ 'd) -> Self { + into_ref!(_pin); + unsafe { let p = pac::IO_BANK0.gpio(21).ctrl(); - p.write(|w| w.set_funcsel(pac::io::vals::Gpio21ctrlFuncsel::CLOCKS_GPOUT_0.0)) + p.write(|w| w.set_funcsel(pac::io::vals::Gpio21ctrlFuncsel::CLOCKS_GPOUT_0.0)); } - Self { _pin: pin } + + Self { _pin } } pub fn set_div(&self, int: u32, frac: u8) { @@ -677,6 +721,15 @@ impl Gpout0 { }); } } + + pub fn disable(&self) { + unsafe { + let c = pac::CLOCKS; + c.clk_gpout0_ctrl().modify(|w| { + w.set_enable(true); + }); + } + } } /// Random number generator based on the ROSC RANDOMBIT register. From 72b0379125b87bcd274bdb81127dd5f0ab29d661 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 01:51:08 +0200 Subject: [PATCH 1085/1575] :rainbow: --- .gitignore | 4 + .vscode/settings.json | 5 + Cargo.toml | 26 +++++ LICENSE-APACHE | 201 ++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++ README.md | 7 ++ examples/.cargo/config.toml | 8 ++ examples/Cargo.toml | 70 +++++++++++ examples/README.md | 33 ++++++ examples/build.rs | 36 ++++++ examples/memory.x | 5 + examples/src/bin/multisocket.rs | 148 +++++++++++++++++++++++ examples/src/bin/tcp-client.rs | 128 ++++++++++++++++++++ examples/src/bin/tcp-server.rs | 136 +++++++++++++++++++++ examples/src/bin/udp.rs | 123 +++++++++++++++++++ rust-toolchain.toml | 8 ++ src/device.rs | 161 +++++++++++++++++++++++++ src/lib.rs | 114 ++++++++++++++++++ src/socket.rs | 114 ++++++++++++++++++ src/spi.rs | 37 ++++++ 20 files changed, 1389 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 examples/.cargo/config.toml create mode 100644 examples/Cargo.toml create mode 100644 examples/README.md create mode 100644 examples/build.rs create mode 100644 examples/memory.x create mode 100644 examples/src/bin/multisocket.rs create mode 100644 examples/src/bin/tcp-client.rs create mode 100644 examples/src/bin/tcp-server.rs create mode 100644 examples/src/bin/udp.rs create mode 100644 rust-toolchain.toml create mode 100644 src/device.rs create mode 100644 src/lib.rs create mode 100644 src/socket.rs create mode 100644 src/spi.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a2ac3d82e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.iml +**/target +**/*.rs.bk +Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..11fb40927 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.linkedProjects": [ + ".\\examples\\Cargo.toml" + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..ac2257f44 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "embassy-net-w5500" +version = "0.1.0" +description = "embassy-net driver for the W5500 ethernet chip" +keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +embedded-hal = { version = "1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } +embassy-net-driver-channel = { version = "0.1.0" } +embassy-time = { version = "0.1.0" } +embassy-futures = { version = "0.1.0" } +defmt = { version = "0.3", optional = true } + +[patch.crates-io] +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..ea4fa15c9 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2019-2022 Embassy project contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..87c052836 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2019-2022 Embassy project contributors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 000000000..9eaf4b700 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# WIZnet W5500 `embassy-net` integration + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode. + +Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async) + +See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. \ No newline at end of file diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml new file mode 100644 index 000000000..e6b6b4a41 --- /dev/null +++ b/examples/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs-cli run --chip RP2040" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "info" diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 000000000..013a2755f --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "embassy-net-w5500-examples" +version = "0.1.0" +edition = "2021" + +[dependencies] +embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } +embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } +embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } +embassy-sync = { version = "0.1.0" } +embassy-futures = { version = "0.1.0" } +embassy-net-driver = { version = "0.1.0" } +embassy-net-driver-channel = { version = "0.1.0" } +atomic-polyfill = "0.1.5" +static_cell = "1.0" + +defmt = "=0.3.2" +defmt-rtt = "0.3" +panic-probe = { version = "0.3", features = ["print-defmt"] } +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" + +embedded-io = { version = "0.4.0", features = ["async", "defmt"] } +heapless = "0.7.15" +embedded-hal = { version = "1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } +rand = { version = "0.8.5", default-features = false } + +embassy-net-w5500 = { path = "../" } + +[patch.crates-io] +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } + +[profile.dev] +debug = 2 +debug-assertions = true +opt-level = 1 +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 1 +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/examples/README.md b/examples/README.md new file mode 100644 index 000000000..d818c4a89 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,33 @@ +# Examples for the rp2040 `WIZnet W5500-EVB-Pico` board + +Examples are written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + +## Prerequisites +```bash +cargo install probe-rs-cli +``` + +## TCP server example +```bash +cargo run --bin tcp-server --release +``` +This example implements a TCP echo server on port 1234 and using DHCP. +Send it some data, you should see it echoed back and printed in the console. + +## Multi-socket example +```bash +cargo run --bin multisocket --release +``` +This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. + +## TCP client example +```bash +cargo run --bin tcp-client --release +``` +This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. + +## UDP server example +```bash +cargo run --bin udp --release +``` +This example implements a UDP server listening on port 1234 and echoing back the data. diff --git a/examples/build.rs b/examples/build.rs new file mode 100644 index 000000000..3f915f931 --- /dev/null +++ b/examples/build.rs @@ -0,0 +1,36 @@ +//! 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=-Tlink-rp.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/memory.x b/examples/memory.x new file mode 100644 index 000000000..eb8c1731d --- /dev/null +++ b/examples/memory.x @@ -0,0 +1,5 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 1024K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} \ No newline at end of file diff --git a/examples/src/bin/multisocket.rs b/examples/src/bin/multisocket.rs new file mode 100644 index 000000000..49bcbdbb0 --- /dev/null +++ b/examples/src/bin/multisocket.rs @@ -0,0 +1,148 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<3>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + // Create two sockets listening to the same port, to handle simultaneous connections + unwrap!(spawner.spawn(listen_task(&stack, 0, 1234))); + unwrap!(spawner.spawn(listen_task(&stack, 1, 1234))); +} + +#[embassy_executor::task(pool_size = 2)] +async fn listen_task(stack: &'static Stack>, id: u8, port: u16) { + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + info!("SOCKET {}: Listening on TCP:{}...", id, port); + if let Err(e) = socket.accept(port).await { + warn!("accept error: {:?}", e); + continue; + } + info!( + "SOCKET {}: Received connection from {:?}", + id, + socket.remote_endpoint() + ); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("SOCKET {}: {:?}", id, e); + break; + } + }; + info!( + "SOCKET {}: rxd {}", + id, + core::str::from_utf8(&buf[..n]).unwrap() + ); + + if let Err(e) = socket.write_all(&buf[..n]).await { + warn!("write error: {:?}", e); + break; + } + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/examples/src/bin/tcp-client.rs b/examples/src/bin/tcp-client.rs new file mode 100644 index 000000000..32dfb6a68 --- /dev/null +++ b/examples/src/bin/tcp-client.rs @@ -0,0 +1,128 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::str::FromStr; +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + let mut led = Output::new(p.PIN_25, Level::Low); + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + led.set_low(); + info!("Connecting..."); + let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap(); + if let Err(e) = socket.connect((host_addr, 1234)).await { + warn!("connect error: {:?}", e); + continue; + } + info!("Connected to {:?}", socket.remote_endpoint()); + led.set_high(); + + let msg = b"Hello world!\n"; + loop { + if let Err(e) = socket.write_all(msg).await { + warn!("write error: {:?}", e); + break; + } + info!("txd: {}", core::str::from_utf8(msg).unwrap()); + Timer::after(Duration::from_secs(1)).await; + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/examples/src/bin/tcp-server.rs b/examples/src/bin/tcp-server.rs new file mode 100644 index 000000000..04b220146 --- /dev/null +++ b/examples/src/bin/tcp-server.rs @@ -0,0 +1,136 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + let mut led = Output::new(p.PIN_25, Level::Low); + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + loop { + let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + led.set_low(); + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + info!("Received connection from {:?}", socket.remote_endpoint()); + led.set_high(); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("{:?}", e); + break; + } + }; + info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap()); + + if let Err(e) = socket.write_all(&buf[..n]).await { + warn!("write error: {:?}", e); + break; + } + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/examples/src/bin/udp.rs b/examples/src/bin/udp.rs new file mode 100644 index 000000000..4dc5e1f2f --- /dev/null +++ b/examples/src/bin/udp.rs @@ -0,0 +1,123 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::yield_now; +use embassy_net::udp::UdpSocket; +use embassy_net::{PacketMetadata, Stack, StackResources}; +use embassy_net_w5500::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embedded_hal_async::spi::ExclusiveDevice; +use rand::RngCore; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + ExclusiveDevice, Output<'static, PIN_17>>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = singleton!(State::<8, 8>::new()); + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*singleton!(Stack::new( + device, + embassy_net::Config::Dhcp(Default::default()), + singleton!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP..."); + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut rx_meta = [PacketMetadata::EMPTY; 16]; + let mut tx_meta = [PacketMetadata::EMPTY; 16]; + let mut buf = [0; 4096]; + loop { + let mut socket = UdpSocket::new( + stack, + &mut rx_meta, + &mut rx_buffer, + &mut tx_meta, + &mut tx_buffer, + ); + socket.bind(1234).unwrap(); + + loop { + let (n, ep) = socket.recv_from(&mut buf).await.unwrap(); + if let Ok(s) = core::str::from_utf8(&buf[..n]) { + info!("rxd from {}: {}", ep, s); + } + socket.send_to(&buf[..n], ep).await.unwrap(); + } + } +} + +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { + loop { + if let Some(config) = stack.config() { + return config.clone(); + } + yield_now().await; + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..fb284c1ee --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,8 @@ +# Before upgrading check that everything is available on all tier1 targets here: +# https://rust-lang.github.io/rustup-components-history +[toolchain] +channel = "nightly-2023-04-04" +components = [ "rust-src", "rustfmt" ] +targets = [ + "thumbv6m-none-eabi", +] diff --git a/src/device.rs b/src/device.rs new file mode 100644 index 000000000..3875fde0e --- /dev/null +++ b/src/device.rs @@ -0,0 +1,161 @@ +use crate::socket; +use crate::spi::SpiInterface; +use embedded_hal_async::spi::SpiDevice; + +pub const MODE: u16 = 0x00; +pub const MAC: u16 = 0x09; +pub const SOCKET_INTR: u16 = 0x18; +pub const PHY_CFG: u16 = 0x2E; + +#[repr(u8)] +pub enum RegisterBlock { + Common = 0x00, + Socket0 = 0x01, + TxBuf = 0x02, + RxBuf = 0x03, +} + +/// W5500 in MACRAW mode +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct W5500 { + bus: SpiInterface, +} + +impl W5500 { + /// Create and initialize the W5500 driver + pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { + let mut bus = SpiInterface(spi); + // Reset device + bus.write_frame(RegisterBlock::Common, MODE, &[0x80]) + .await?; + + // Enable interrupt pin + bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]) + .await?; + // Enable receive interrupt + bus.write_frame( + RegisterBlock::Socket0, + socket::SOCKET_INTR_MASK, + &[socket::Interrupt::Receive as u8], + ) + .await?; + + // Set MAC address + bus.write_frame(RegisterBlock::Common, MAC, &mac_addr) + .await?; + + // Set the raw socket RX/TX buffer sizes to 16KB + bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16]) + .await?; + bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16]) + .await?; + + // MACRAW mode with MAC filtering. + let mode: u8 = (1 << 2) | (1 << 7); + bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]) + .await?; + socket::command(&mut bus, socket::Command::Open).await?; + + Ok(Self { bus }) + } + + /// Read bytes from the RX buffer. Returns the number of bytes read. + async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result { + let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + + let read_buffer = if rx_size > buffer.len() + offset as usize { + buffer + } else { + &mut buffer[..rx_size - offset as usize] + }; + + let read_ptr = socket::get_rx_read_ptr(&mut self.bus) + .await? + .wrapping_add(offset); + self.bus + .read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer) + .await?; + socket::set_rx_read_ptr( + &mut self.bus, + read_ptr.wrapping_add(read_buffer.len() as u16), + ) + .await?; + + Ok(read_buffer.len()) + } + + /// Read an ethernet frame from the device. Returns the number of bytes read. + pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result { + let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + if rx_size == 0 { + return Ok(0); + } + + socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?; + + // First two bytes gives the size of the received ethernet frame + let expected_frame_size: usize = { + let mut frame_bytes = [0u8; 2]; + assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2); + u16::from_be_bytes(frame_bytes) as usize - 2 + }; + + // Read the ethernet frame + let read_buffer = if frame.len() > expected_frame_size { + &mut frame[..expected_frame_size] + } else { + frame + }; + + let recvd_frame_size = self.read_bytes(read_buffer, 2).await?; + + // Register RX as completed + socket::command(&mut self.bus, socket::Command::Receive).await?; + + // If the whole frame wasn't read, drop it + if recvd_frame_size < expected_frame_size { + Ok(0) + } else { + Ok(recvd_frame_size) + } + } + + /// Write an ethernet frame to the device. Returns number of bytes written + pub async fn write_frame(&mut self, frame: &[u8]) -> Result { + let max_size = socket::get_tx_free_size(&mut self.bus).await? as usize; + + let write_data = if frame.len() < max_size { + frame + } else { + &frame[..max_size] + }; + + let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; + self.bus + .write_frame(RegisterBlock::TxBuf, write_ptr, write_data) + .await?; + socket::set_tx_write_ptr( + &mut self.bus, + write_ptr.wrapping_add(write_data.len() as u16), + ) + .await?; + + socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; + socket::command(&mut self.bus, socket::Command::Send).await?; + // Wait for TX to complete + while !socket::is_interrupt(&mut self.bus, socket::Interrupt::SendOk).await? {} + socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; + + Ok(write_data.len()) + } + + pub async fn is_link_up(&mut self) -> bool { + let mut link = [0]; + self.bus + .read_frame(RegisterBlock::Common, PHY_CFG, &mut link) + .await + .ok(); + link[0] & 1 == 1 + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..bf14b05b4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,114 @@ +#![no_std] +/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. +mod device; +mod socket; +mod spi; + +use crate::device::W5500; +use embassy_futures::select::{select, Either}; +use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; +use embassy_time::{Duration, Timer}; +use embedded_hal::digital::OutputPin; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiDevice; +const MTU: usize = 1514; + +/// Type alias for the embassy-net driver for W5500 +pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; + +/// Internal state for the embassy-net integration. +pub struct State { + ch_state: ch::State, +} + +impl State { + /// Create a new `State`. + pub const fn new() -> Self { + Self { + ch_state: ch::State::new(), + } + } +} + +/// Background runner for the W5500. +/// +/// You must call `.run()` in a background task for the W5500 to operate. +pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> { + mac: W5500, + ch: ch::Runner<'d, MTU>, + int: INT, + _reset: RST, +} + +/// You must call this in a background task for the W5500 to operate. +impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { + pub async fn run(mut self) -> ! { + let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); + loop { + if self.mac.is_link_up().await { + state_chan.set_link_state(LinkState::Up); + loop { + match select( + async { + self.int.wait_for_low().await.ok(); + rx_chan.rx_buf().await + }, + tx_chan.tx_buf(), + ) + .await + { + Either::First(p) => { + if let Ok(n) = self.mac.read_frame(p).await { + rx_chan.rx_done(n); + } + } + Either::Second(p) => { + self.mac.write_frame(p).await.ok(); + tx_chan.tx_done(); + } + } + } + } else { + state_chan.set_link_state(LinkState::Down); + } + } + } +} + +/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net). +pub async fn new< + 'a, + const N_RX: usize, + const N_TX: usize, + SPI: SpiDevice, + INT: Wait, + RST: OutputPin, +>( + mac_addr: [u8; 6], + state: &'a mut State, + spi_dev: SPI, + int: INT, + mut reset: RST, +) -> (Device<'a>, Runner<'a, SPI, INT, RST>) { + // Reset the W5500. + reset.set_low().ok(); + // Ensure the reset is registered. + Timer::after(Duration::from_millis(1)).await; + reset.set_high().ok(); + // Wait for the W5500 to achieve PLL lock. + Timer::after(Duration::from_millis(2)).await; + + let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); + + let (runner, device) = ch::new(&mut state.ch_state, mac_addr); + ( + device, + Runner { + ch: runner, + mac, + int, + _reset: reset, + }, + ) +} diff --git a/src/socket.rs b/src/socket.rs new file mode 100644 index 000000000..0d3d1aeb2 --- /dev/null +++ b/src/socket.rs @@ -0,0 +1,114 @@ +use crate::device::RegisterBlock; +use crate::spi::SpiInterface; +use embedded_hal_async::spi::SpiDevice; + +pub const MODE: u16 = 0x00; +pub const COMMAND: u16 = 0x01; +pub const RXBUF_SIZE: u16 = 0x1E; +pub const TXBUF_SIZE: u16 = 0x1F; +pub const TX_FREE_SIZE: u16 = 0x20; +pub const TX_DATA_WRITE_PTR: u16 = 0x24; +pub const RECVD_SIZE: u16 = 0x26; +pub const RX_DATA_READ_PTR: u16 = 0x28; +pub const SOCKET_INTR_MASK: u16 = 0x2C; + +#[repr(u8)] +pub enum Command { + Open = 0x01, + Send = 0x20, + Receive = 0x40, +} + +pub const INTR: u16 = 0x02; +#[repr(u8)] +pub enum Interrupt { + SendOk = 0b010000_u8, + Receive = 0b00100_u8, +} + +pub async fn reset_interrupt( + bus: &mut SpiInterface, + code: Interrupt, +) -> Result<(), SPI::Error> { + let data = [code as u8]; + bus.write_frame(RegisterBlock::Socket0, INTR, &data).await +} + +pub async fn is_interrupt( + bus: &mut SpiInterface, + code: Interrupt, +) -> Result { + let mut data = [0u8]; + bus.read_frame(RegisterBlock::Socket0, INTR, &mut data) + .await?; + Ok(data[0] & code as u8 != 0) +} + +pub async fn get_tx_write_ptr( + bus: &mut SpiInterface, +) -> Result { + let mut data = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data) + .await?; + Ok(u16::from_be_bytes(data)) +} + +pub async fn set_tx_write_ptr( + bus: &mut SpiInterface, + ptr: u16, +) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data) + .await +} + +pub async fn get_rx_read_ptr( + bus: &mut SpiInterface, +) -> Result { + let mut data = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data) + .await?; + Ok(u16::from_be_bytes(data)) +} + +pub async fn set_rx_read_ptr( + bus: &mut SpiInterface, + ptr: u16, +) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data) + .await +} + +pub async fn command( + bus: &mut SpiInterface, + command: Command, +) -> Result<(), SPI::Error> { + let data = [command as u8]; + bus.write_frame(RegisterBlock::Socket0, COMMAND, &data) + .await +} + +pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { + loop { + // Wait until two sequential reads are equal + let mut res0 = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0) + .await?; + let mut res1 = [0u8; 2]; + bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1) + .await?; + if res0 == res1 { + break Ok(u16::from_be_bytes(res0)); + } + } +} + +pub async fn get_tx_free_size( + bus: &mut SpiInterface, +) -> Result { + let mut data = [0; 2]; + bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data) + .await?; + Ok(u16::from_be_bytes(data)) +} diff --git a/src/spi.rs b/src/spi.rs new file mode 100644 index 000000000..55d311888 --- /dev/null +++ b/src/spi.rs @@ -0,0 +1,37 @@ +use crate::device::RegisterBlock; +use embedded_hal_async::spi::{Operation, SpiDevice}; + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct SpiInterface(pub SPI); + +impl SpiInterface { + pub async fn read_frame( + &mut self, + block: RegisterBlock, + address: u16, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.to_be_bytes(); + let control_phase = [(block as u8) << 3]; + let operations = &mut [ + Operation::Write(&address_phase), + Operation::Write(&control_phase), + Operation::TransferInPlace(data), + ]; + self.0.transaction(operations).await + } + + pub async fn write_frame( + &mut self, + block: RegisterBlock, + address: u16, + data: &[u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.to_be_bytes(); + let control_phase = [(block as u8) << 3 | 0b0000_0100]; + let data_phase = data; + let operations = &[&address_phase[..], &control_phase, &data_phase]; + self.0.write_transaction(operations).await + } +} From fdc87a8e7f70e5246ab608df931a9623fb0289b4 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 02:21:17 +0200 Subject: [PATCH 1086/1575] Add CI --- .github/workflows/rust.yml | 29 +++++++++++++++++++++++++++++ ci.sh | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/workflows/rust.yml create mode 100644 ci.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 000000000..dfa96dd0e --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,29 @@ +name: Rust + +on: + push: + branches: [main] + pull_request: + branches: [main] + merge_group: + +env: + CARGO_TERM_COLOR: always + +jobs: + build-nightly: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Check fmt + run: cargo fmt -- --check + - name: Build + run: ./ci.sh diff --git a/ci.sh b/ci.sh new file mode 100644 index 000000000..74610ff28 --- /dev/null +++ b/ci.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euxo pipefail + +export DEFMT_LOG=trace + +# build examples +#================== + +(cd examples; cargo build --bin multisocket --release) +(cd examples; cargo build --bin tcp-client --release) +(cd examples; cargo build --bin tcp-server --release) +(cd examples; cargo build --bin tcp-udp --release) + +# build lib +#============ + +cargo build --target thumbv6m-none-eabi From 9d018a0075ee3390c5fd7c8af5da3bb8a8787c7a Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 02:24:25 +0200 Subject: [PATCH 1087/1575] Add CI --- ci.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ci.sh diff --git a/ci.sh b/ci.sh old mode 100644 new mode 100755 From d40589f08216c1fce6b9c8d8ad74d18fb5e58285 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 9 May 2023 02:27:28 +0200 Subject: [PATCH 1088/1575] Fix CI --- ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 74610ff28..0a2876987 100755 --- a/ci.sh +++ b/ci.sh @@ -10,7 +10,7 @@ export DEFMT_LOG=trace (cd examples; cargo build --bin multisocket --release) (cd examples; cargo build --bin tcp-client --release) (cd examples; cargo build --bin tcp-server --release) -(cd examples; cargo build --bin tcp-udp --release) +(cd examples; cargo build --bin udp --release) # build lib #============ From c1eaad41f30d0b712b94e05f62dc7bb90fe8cc6d Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 9 May 2023 11:46:25 -0400 Subject: [PATCH 1089/1575] Gpout cleanup, basic Gpin support Requires rp-pac #3 --- embassy-rp/Cargo.toml | 5 +- embassy-rp/src/clocks.rs | 162 ++++++++++++++++++++++++--------------- 2 files changed, 106 insertions(+), 61 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 59d0bf338..0d0e6fff0 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,7 +61,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "2", features = ["rt"] } +rp-pac = { version = "3", 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.10", optional = true} @@ -71,3 +71,6 @@ embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} paste = "1.0" pio-proc = {version= "0.2" } pio = {version= "0.2.1" } + +[patch.crates-io] +rp-pac = {git = "https://github.com/CBJamo/rp-pac.git"} diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index def34fc70..743a3fd53 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,6 +1,7 @@ +use embassy_hal_common::{into_ref, PeripheralRef}; use pac::clocks::vals::*; -use crate::{pac, reset}; +use crate::{pac, reset, Peripheral}; // TODO fix terrible use of global here static mut XIN_HZ: u32 = 0; @@ -543,53 +544,26 @@ pub fn clk_rtc_freq() -> u32 { base / int } -pub fn clk_gpout0_freq() -> u32 { +pub fn clk_gpout_freq(num: usize) -> u32 { let c = pac::CLOCKS; - let src = unsafe { c.clk_gpout0_ctrl().read().auxsrc() }; + let src = unsafe { c.clk_gpout_ctrl(num).read().auxsrc() }; let base = match src { - ClkGpout0ctrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkGpout0ctrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkGpout0ctrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - ClkGpout0ctrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkGpout0ctrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), - ClkGpout0ctrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkGpout0ctrlAuxsrc::CLK_SYS => clk_sys_freq(), - ClkGpout0ctrlAuxsrc::CLK_USB => clk_usb_freq(), - ClkGpout0ctrlAuxsrc::CLK_ADC => clk_adc_freq(), - ClkGpout0ctrlAuxsrc::CLK_RTC => clk_rtc_freq(), - ClkGpout0ctrlAuxsrc::CLK_REF => clk_ref_freq(), + ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), + ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), + ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), + ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq(), + ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), _ => unreachable!(), }; - let div = unsafe { c.clk_gpout0_div().read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); - - base / int -} - -pub fn clk_gpout1_freq() -> u32 { - let c = pac::CLOCKS; - let src = unsafe { c.clk_gpout1_ctrl().read().auxsrc() }; - - let base = match src { - ClkGpout1ctrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkGpout1ctrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkGpout1ctrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - ClkGpout1ctrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkGpout1ctrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), - ClkGpout1ctrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkGpout1ctrlAuxsrc::CLK_SYS => clk_sys_freq(), - ClkGpout1ctrlAuxsrc::CLK_USB => clk_usb_freq(), - ClkGpout1ctrlAuxsrc::CLK_ADC => clk_adc_freq(), - ClkGpout1ctrlAuxsrc::CLK_RTC => clk_rtc_freq(), - ClkGpout1ctrlAuxsrc::CLK_REF => clk_ref_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_gpout1_div().read() }; + let div = unsafe { c.clk_gpout_div(num).read() }; let int = if div.int() == 0 { 65536 } else { div.int() }; // TODO handle fractional clock div let _frac = div.frac(); @@ -667,47 +641,106 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { p.pwr().modify(|w| w.set_postdivpd(false)); } -pub trait GpoutPin {} +pub trait GpinPin { + fn gpin_number(&self) -> usize; + fn pin_number(&self) -> usize; +} -impl GpoutPin for crate::peripherals::PIN_21 {} -impl GpoutPin for crate::peripherals::PIN_23 {} -impl GpoutPin for crate::peripherals::PIN_24 {} -impl GpoutPin for crate::peripherals::PIN_25 {} +macro_rules! impl_gpinpin { + ($name:ident, $pin_num:expr, $gpin_num:expr) => { + impl GpinPin for crate::peripherals::$name { + fn gpin_number(&self) -> usize { + $gpin_num + } + fn pin_number(&self) -> usize { + $pin_num + } + } + }; +} -use embassy_hal_common::{into_ref, PeripheralRef}; +impl_gpinpin!(PIN_20, 20, 0); +impl_gpinpin!(PIN_22, 22, 1); -use crate::Peripheral; +pub struct Gpin<'d, T: GpinPin> { + gpout: PeripheralRef<'d, T>, +} + +impl<'d, T: GpinPin> Gpin<'d, T> { + pub fn new(gpout: impl Peripheral

+ 'd) -> Self { + into_ref!(gpout); + + unsafe { + let p = pac::IO_BANK0.gpio(gpout.pin_number()).ctrl(); + p.write(|w| w.set_funcsel(0x08)); + } + + Self { gpout } + } +} + +impl<'d, T: GpinPin> Drop for Gpin<'d, T> { + fn drop(&mut self) { + unsafe { + let p = pac::IO_BANK0.gpio(self.gpout.pin_number()).ctrl(); + p.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); + } + } +} + +pub trait GpoutPin { + fn gpout_number(&self) -> usize; + fn pin_number(&self) -> usize; +} + +macro_rules! impl_gpoutpin { + ($name:ident, $pin_num:expr, $gpout_num:expr) => { + impl GpoutPin for crate::peripherals::$name { + fn gpout_number(&self) -> usize { + $gpout_num + } + fn pin_number(&self) -> usize { + $pin_num + } + } + }; +} + +impl_gpoutpin!(PIN_21, 21, 0); +impl_gpoutpin!(PIN_23, 23, 1); +impl_gpoutpin!(PIN_24, 24, 2); +impl_gpoutpin!(PIN_25, 25, 3); pub struct Gpout<'d, T: GpoutPin> { - _pin: PeripheralRef<'d, T>, + gpout: PeripheralRef<'d, T>, } impl<'d, T: GpoutPin> Gpout<'d, T> { - pub fn new(_pin: impl Peripheral

+ 'd) -> Self { - into_ref!(_pin); + pub fn new(gpout: impl Peripheral

+ 'd) -> Self { + into_ref!(gpout); unsafe { - let p = pac::IO_BANK0.gpio(21).ctrl(); - p.write(|w| w.set_funcsel(pac::io::vals::Gpio21ctrlFuncsel::CLOCKS_GPOUT_0.0)); + let p = pac::IO_BANK0.gpio(gpout.pin_number()).ctrl(); + p.write(|w| w.set_funcsel(0x08)); } - Self { _pin } + Self { gpout } } pub fn set_div(&self, int: u32, frac: u8) { unsafe { let c = pac::CLOCKS; - c.clk_gpout0_div().write(|w| { + c.clk_gpout_div(self.gpout.gpout_number()).write(|w| { w.set_int(int); w.set_frac(frac); }); } } - pub fn set_src(&self, src: ClkGpout0ctrlAuxsrc) { + pub fn set_src(&self, src: ClkGpoutCtrlAuxsrc) { unsafe { let c = pac::CLOCKS; - c.clk_gpout0_ctrl().modify(|w| { + c.clk_gpout_ctrl(self.gpout.gpout_number()).modify(|w| { w.set_auxsrc(src); }); } @@ -716,7 +749,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn enable(&self) { unsafe { let c = pac::CLOCKS; - c.clk_gpout0_ctrl().modify(|w| { + c.clk_gpout_ctrl(self.gpout.gpout_number()).modify(|w| { w.set_enable(true); }); } @@ -725,13 +758,22 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn disable(&self) { unsafe { let c = pac::CLOCKS; - c.clk_gpout0_ctrl().modify(|w| { + c.clk_gpout_ctrl(self.gpout.gpout_number()).modify(|w| { w.set_enable(true); }); } } } +impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { + fn drop(&mut self) { + unsafe { + let p = pac::IO_BANK0.gpio(self.gpout.pin_number()).ctrl(); + p.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); + } + } +} + /// Random number generator based on the ROSC RANDOMBIT register. /// /// This will not produce random values if the ROSC is stopped or run at some From 5015c845c59e8769d404dce5ca618cdf6fd16aeb Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 9 May 2023 12:10:24 -0400 Subject: [PATCH 1090/1575] Improve gpout example, clk_gpout_freq --- embassy-rp/src/clocks.rs | 8 ++++---- examples/rp/Cargo.toml | 3 +++ examples/rp/src/bin/gpout.rs | 25 +++++++++++++++++++------ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 743a3fd53..b919b98a9 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -544,9 +544,9 @@ pub fn clk_rtc_freq() -> u32 { base / int } -pub fn clk_gpout_freq(num: usize) -> u32 { +pub fn clk_gpout_freq(gpout: &Gpout) -> u32 { let c = pac::CLOCKS; - let src = unsafe { c.clk_gpout_ctrl(num).read().auxsrc() }; + let src = unsafe { c.clk_gpout_ctrl(gpout.gpout.gpout_number()).read().auxsrc() }; let base = match src { ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), @@ -563,7 +563,7 @@ pub fn clk_gpout_freq(num: usize) -> u32 { _ => unreachable!(), }; - let div = unsafe { c.clk_gpout_div(num).read() }; + let div = unsafe { c.clk_gpout_div(gpout.gpout.gpout_number()).read() }; let int = if div.int() == 0 { 65536 } else { div.int() }; // TODO handle fractional clock div let _frac = div.frac(); @@ -759,7 +759,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { unsafe { let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.gpout_number()).modify(|w| { - w.set_enable(true); + w.set_enable(false); }); } } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index d2829df99..7d5473f2c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -48,3 +48,6 @@ pio = "0.2.1" [profile.release] debug = true + +[patch.crates-io] +rp-pac = {git = "https://github.com/CBJamo/rp-pac.git"} diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs index 0503212e8..ea0efb859 100644 --- a/examples/rp/src/bin/gpout.rs +++ b/examples/rp/src/bin/gpout.rs @@ -5,17 +5,30 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::{clocks, pac}; +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 mut led = Output::new(p.PIN_25, Level::Low); - let gpout0 = clocks::Gpout0::new(p.PIN_21); - gpout0.set_src(pac::clocks::vals::ClkGpout0ctrlAuxsrc::CLK_SYS); - gpout0.set_div(1000, 0); - gpout0.enable(); + let gpout3 = clocks::Gpout::new(p.PIN_25); + gpout3.set_div(1000, 0); + gpout3.enable(); - info!("Pin 21 should be toggling at {} hz", clocks::clk_gpout0_freq()); + loop { + gpout3.set_src(pac::clocks::vals::ClkGpoutCtrlAuxsrc::CLK_SYS); + info!( + "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}", + clocks::clk_gpout_freq(&gpout3) + ); + Timer::after(Duration::from_secs(2)).await; + + gpout3.set_src(pac::clocks::vals::ClkGpoutCtrlAuxsrc::CLK_REF); + info!( + "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}", + clocks::clk_gpout_freq(&gpout3) + ); + Timer::after(Duration::from_secs(2)).await; + } } From 6bea07848717857659c737a3bb3eab7c2cd8abdb Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 9 May 2023 12:20:23 -0400 Subject: [PATCH 1091/1575] Remove patches, bump rp-pac version --- embassy-rp/Cargo.toml | 5 +---- examples/rp/Cargo.toml | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 0d0e6fff0..a2eb6c581 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,7 +61,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "3", features = ["rt"] } +rp-pac = { version = "4", 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.10", optional = true} @@ -71,6 +71,3 @@ embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} paste = "1.0" pio-proc = {version= "0.2" } pio = {version= "0.2.1" } - -[patch.crates-io] -rp-pac = {git = "https://github.com/CBJamo/rp-pac.git"} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 7d5473f2c..d2829df99 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -48,6 +48,3 @@ pio = "0.2.1" [profile.release] debug = true - -[patch.crates-io] -rp-pac = {git = "https://github.com/CBJamo/rp-pac.git"} From 0e3cd87a323c464842ea66eeaf7b6da00d70d300 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 9 May 2023 18:36:17 +0200 Subject: [PATCH 1092/1575] rp: use rp2040-boot2 to provide the boot2 blob we're currently shipping an old boot2 that runs the flash at half speed. use the more recent version instead, and allow user to choose between the different supported boot2 versions for different flash chips if they need that. --- embassy-rp/Cargo.toml | 10 ++++++++++ embassy-rp/src/boot2.bin | Bin 256 -> 0 bytes embassy-rp/src/lib.rs | 29 ++++++++++++++++++++++++++--- 3 files changed, 36 insertions(+), 3 deletions(-) delete mode 100644 embassy-rp/src/boot2.bin diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 59d0bf338..28a35a1bd 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -30,6 +30,15 @@ rom-func-cache = [] intrinsics = [] rom-v2-intrinsics = [] +# boot2 flash chip support. if none of these is enabled we'll default to w25q080 (used on the pico) +boot2-at25sf128a = [] +boot2-gd25q64cs = [] +boot2-generic-03h = [] +boot2-is25lp080 = [] +boot2-ram-memcpy = [] +boot2-w25q080 = [] +boot2-w25x10cl = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] @@ -71,3 +80,4 @@ embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} paste = "1.0" pio-proc = {version= "0.2" } pio = {version= "0.2.1" } +rp2040-boot2 = "0.3" diff --git a/embassy-rp/src/boot2.bin b/embassy-rp/src/boot2.bin deleted file mode 100644 index fdc1fc756b0e876b93b99f5475bea32902b13656..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 256 zcmZSBYUHh`5Rot=gGsT&c}BvG1c}6mL_Kc?#hD2#ijj$oiXW6_255UqCNKg;OcfYD zxcp#Jn&2dIflW~|jp2jI56L`65KBQKO$x}@0rEkR6{u4G2csf~)6a__v!sD0FepYQ zNO?+oFoMl`4b-&&Y=+PekP0zR5vUB~9tI7D3k>c;9>Sg+3Ct%NK67+%xHE5^nZ=^O z==A#nBar-Yk$ErER*5u;wEy2f{g=p-$Ya{ez`*3dz`!8Ez`)MHz#Pw@sKmhV1;|%Y XU|7Jw$RNP+mf-z53y?sZ3 diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index cba7559df..99f62738d 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -131,9 +131,32 @@ embassy_hal_common::peripherals! { WATCHDOG, } -#[link_section = ".boot2"] -#[used] -static BOOT2: [u8; 256] = *include_bytes!("boot2.bin"); +macro_rules! select_bootloader { + ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => { + $( + #[cfg(feature = $feature)] + #[link_section = ".boot2"] + #[used] + static BOOT2: [u8; 256] = rp2040_boot2::$loader; + )* + + #[cfg(not(any( $( feature = $feature),* )))] + #[link_section = ".boot2"] + #[used] + static BOOT2: [u8; 256] = rp2040_boot2::$default; + } +} + +select_bootloader! { + "boot2-at25sf128a" => BOOT_LOADER_AT25SF128A, + "boot2-gd25q64cs" => BOOT_LOADER_GD25Q64CS, + "boot2-generic-03h" => BOOT_LOADER_GENERIC_03H, + "boot2-is25lp080" => BOOT_LOADER_IS25LP080, + "boot2-ram-memcpy" => BOOT_LOADER_RAM_MEMCPY, + "boot2-w25q080" => BOOT_LOADER_W25Q080, + "boot2-w25x10cl" => BOOT_LOADER_W25X10CL, + default => BOOT_LOADER_W25Q080 +} pub mod config { #[non_exhaustive] From 5cfe1a1fb40470dfaf256fc87989fd67884113f1 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 9 May 2023 17:45:24 -0400 Subject: [PATCH 1093/1575] Dirbaio comments round 2 --- embassy-rp/src/clocks.rs | 191 +++++++++++++++++++---------------- examples/rp/src/bin/gpout.rs | 10 +- 2 files changed, 107 insertions(+), 94 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index b919b98a9..1354ccd27 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -6,15 +6,22 @@ use crate::{pac, reset, Peripheral}; // TODO fix terrible use of global here static mut XIN_HZ: u32 = 0; +pub use rp_pac::clocks::vals::{ + ClkAdcCtrlAuxsrc as AdcAuxsrc, ClkGpoutCtrlAuxsrc as GpoutSrc, ClkPeriCtrlAuxsrc as PeriClkAuxsrc, + ClkRefCtrlAuxsrc as RefAuxsrc, ClkRtcCtrlAuxsrc as RtcAuxsrc, ClkSysCtrlAuxsrc as SysAuxsrc, + ClkUsbCtrlAuxsrc as UsbAuxsrc, +}; + +#[non_exhaustive] pub struct ClockConfig { - rosc: Option, - xosc: Option, - ref_clk: RefClkConfig, - sys_clk: SysClkConfig, - peri_clk_src: Option, - usb_clk: Option, - adc_clk: Option, - rtc_clk: Option, + pub rosc: Option, + pub xosc: Option, + pub ref_clk: RefClkConfig, + pub sys_clk: SysClkConfig, + pub peri_clk_src: Option, + pub usb_clk: Option, + pub adc_clk: Option, + pub rtc_clk: Option, } impl ClockConfig { @@ -54,15 +61,18 @@ impl ClockConfig { usb_clk: Some(UsbClkConfig { src: ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB, div: 1, + phase: 0, }), adc_clk: Some(AdcClkConfig { src: ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB, div: 1, + phase: 0, }), rtc_clk: Some(RtcClkConfig { src: ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB, div_int: 1024, div_frac: 0, + phase: 0, }), } } @@ -89,27 +99,29 @@ impl ClockConfig { adc_clk: Some(AdcClkConfig { src: ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH, div: 1, + phase: 0, }), rtc_clk: Some(RtcClkConfig { src: ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH, div_int: 1024, div_frac: 0, + phase: 0, }), } } } pub struct RoscConfig { - range: pac::rosc::vals::FreqRange, - drive_strength: [u8; 8], - div: u16, + pub range: pac::rosc::vals::FreqRange, + pub drive_strength: [u8; 8], + pub div: u16, } pub struct XoscConfig { - hz: u32, - clock_type: ExternalClock, - sys_pll: Option, - usb_pll: Option, + pub hz: u32, + pub clock_type: ExternalClock, + pub sys_pll: Option, + pub usb_pll: Option, } pub struct PllConfig { @@ -124,8 +136,8 @@ pub enum ExternalClock { Clock, } pub struct RefClkConfig { - src: RefClkSrc, - div: u8, + pub src: RefClkSrc, + pub div: u8, } pub enum RefClkSrc { @@ -140,25 +152,28 @@ pub enum SysClkSrc { } pub struct SysClkConfig { - src: SysClkSrc, - div_int: u32, - div_frac: u8, + pub src: SysClkSrc, + pub div_int: u32, + pub div_frac: u8, } pub struct UsbClkConfig { - src: ClkUsbCtrlAuxsrc, - div: u8, + pub src: ClkUsbCtrlAuxsrc, + pub div: u8, + pub phase: u8, } pub struct AdcClkConfig { - src: ClkAdcCtrlAuxsrc, - div: u8, + pub src: ClkAdcCtrlAuxsrc, + pub div: u8, + pub phase: u8, } pub struct RtcClkConfig { - src: ClkRtcCtrlAuxsrc, - div_int: u32, - div_frac: u8, + pub src: ClkRtcCtrlAuxsrc, + pub div_int: u32, + pub div_frac: u8, + pub phase: u8, } /// safety: must be called exactly once at bootup @@ -289,6 +304,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // CLK USB = PLL USB (48MHz) / 1 = 48MHz c.clk_usb_div().write(|w| w.set_int(conf.div)); c.clk_usb_ctrl().write(|w| { + w.set_phase(conf.phase); w.set_enable(true); w.set_auxsrc(conf.src); }); @@ -300,6 +316,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz c.clk_adc_div().write(|w| w.set_int(conf.div)); c.clk_adc_ctrl().write(|w| { + w.set_phase(conf.phase); w.set_enable(true); w.set_auxsrc(conf.src); }); @@ -317,6 +334,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_frac(conf.div_frac); }); c.clk_rtc_ctrl().write(|w| { + w.set_phase(conf.phase); w.set_enable(true); w.set_auxsrc(conf.src); }); @@ -544,33 +562,6 @@ pub fn clk_rtc_freq() -> u32 { base / int } -pub fn clk_gpout_freq(gpout: &Gpout) -> u32 { - let c = pac::CLOCKS; - let src = unsafe { c.clk_gpout_ctrl(gpout.gpout.gpout_number()).read().auxsrc() }; - - let base = match src { - ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), - ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), - ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), - ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), - ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq(), - ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_gpout_div(gpout.gpout.gpout_number()).read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); - - base / int -} - unsafe fn start_xosc(crystal_hz: u32) { pac::XOSC .ctrl() @@ -641,20 +632,16 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { p.pwr().modify(|w| w.set_postdivpd(false)); } -pub trait GpinPin { - fn gpin_number(&self) -> usize; - fn pin_number(&self) -> usize; +pub trait GpinPin: crate::gpio::Pin { + fn number(&self) -> usize; } macro_rules! impl_gpinpin { ($name:ident, $pin_num:expr, $gpin_num:expr) => { impl GpinPin for crate::peripherals::$name { - fn gpin_number(&self) -> usize { + fn number(&self) -> usize { $gpin_num } - fn pin_number(&self) -> usize { - $pin_num - } } }; } @@ -663,53 +650,50 @@ impl_gpinpin!(PIN_20, 20, 0); impl_gpinpin!(PIN_22, 22, 1); pub struct Gpin<'d, T: GpinPin> { - gpout: PeripheralRef<'d, T>, + gpin: PeripheralRef<'d, T>, } impl<'d, T: GpinPin> Gpin<'d, T> { - pub fn new(gpout: impl Peripheral

+ 'd) -> Self { - into_ref!(gpout); + pub fn new(gpin: impl Peripheral

+ 'd) -> Self { + into_ref!(gpin); unsafe { - let p = pac::IO_BANK0.gpio(gpout.pin_number()).ctrl(); - p.write(|w| w.set_funcsel(0x08)); + gpin.io().ctrl().write(|w| w.set_funcsel(0x08)); } - Self { gpout } + Self { gpin } } } impl<'d, T: GpinPin> Drop for Gpin<'d, T> { fn drop(&mut self) { unsafe { - let p = pac::IO_BANK0.gpio(self.gpout.pin_number()).ctrl(); - p.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); + self.gpin + .io() + .ctrl() + .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); } } } -pub trait GpoutPin { - fn gpout_number(&self) -> usize; - fn pin_number(&self) -> usize; +pub trait GpoutPin: crate::gpio::Pin { + fn number(&self) -> usize; } macro_rules! impl_gpoutpin { - ($name:ident, $pin_num:expr, $gpout_num:expr) => { + ($name:ident, $gpout_num:expr) => { impl GpoutPin for crate::peripherals::$name { - fn gpout_number(&self) -> usize { + fn number(&self) -> usize { $gpout_num } - fn pin_number(&self) -> usize { - $pin_num - } } }; } -impl_gpoutpin!(PIN_21, 21, 0); -impl_gpoutpin!(PIN_23, 23, 1); -impl_gpoutpin!(PIN_24, 24, 2); -impl_gpoutpin!(PIN_25, 25, 3); +impl_gpoutpin!(PIN_21, 0); +impl_gpoutpin!(PIN_23, 1); +impl_gpoutpin!(PIN_24, 2); +impl_gpoutpin!(PIN_25, 3); pub struct Gpout<'d, T: GpoutPin> { gpout: PeripheralRef<'d, T>, @@ -720,8 +704,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { into_ref!(gpout); unsafe { - let p = pac::IO_BANK0.gpio(gpout.pin_number()).ctrl(); - p.write(|w| w.set_funcsel(0x08)); + gpout.io().ctrl().write(|w| w.set_funcsel(0x08)); } Self { gpout } @@ -730,7 +713,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn set_div(&self, int: u32, frac: u8) { unsafe { let c = pac::CLOCKS; - c.clk_gpout_div(self.gpout.gpout_number()).write(|w| { + c.clk_gpout_div(self.gpout.number()).write(|w| { w.set_int(int); w.set_frac(frac); }); @@ -740,7 +723,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn set_src(&self, src: ClkGpoutCtrlAuxsrc) { unsafe { let c = pac::CLOCKS; - c.clk_gpout_ctrl(self.gpout.gpout_number()).modify(|w| { + c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { w.set_auxsrc(src); }); } @@ -749,7 +732,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn enable(&self) { unsafe { let c = pac::CLOCKS; - c.clk_gpout_ctrl(self.gpout.gpout_number()).modify(|w| { + c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { w.set_enable(true); }); } @@ -758,18 +741,48 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn disable(&self) { unsafe { let c = pac::CLOCKS; - c.clk_gpout_ctrl(self.gpout.gpout_number()).modify(|w| { + c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { w.set_enable(false); }); } } + + pub fn get_freq(&self) -> u32 { + let c = pac::CLOCKS; + let src = unsafe { c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc() }; + + let base = match src { + ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), + ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), + ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), + ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), + ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), + ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), + ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq(), + ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), + _ => unreachable!(), + }; + + let div = unsafe { c.clk_gpout_div(self.gpout.number()).read() }; + let int = if div.int() == 0 { 65536 } else { div.int() }; + // TODO handle fractional clock div + let _frac = div.frac(); + + base / int + } } impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { fn drop(&mut self) { + self.disable(); unsafe { - let p = pac::IO_BANK0.gpio(self.gpout.pin_number()).ctrl(); - p.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); + self.gpout + .io() + .ctrl() + .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); } } } diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs index ea0efb859..236a653ac 100644 --- a/examples/rp/src/bin/gpout.rs +++ b/examples/rp/src/bin/gpout.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::{clocks, pac}; +use embassy_rp::clocks; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -17,17 +17,17 @@ async fn main(_spawner: Spawner) { gpout3.enable(); loop { - gpout3.set_src(pac::clocks::vals::ClkGpoutCtrlAuxsrc::CLK_SYS); + gpout3.set_src(clocks::GpoutSrc::CLK_SYS); info!( "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}", - clocks::clk_gpout_freq(&gpout3) + gpout3.get_freq() ); Timer::after(Duration::from_secs(2)).await; - gpout3.set_src(pac::clocks::vals::ClkGpoutCtrlAuxsrc::CLK_REF); + gpout3.set_src(clocks::GpoutSrc::CLK_REF); info!( "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}", - clocks::clk_gpout_freq(&gpout3) + gpout3.get_freq() ); Timer::after(Duration::from_secs(2)).await; } From adefa4f86bd3f5da3cfe85c618603abba54921ea Mon Sep 17 00:00:00 2001 From: kalkyl Date: Wed, 10 May 2023 02:23:54 +0200 Subject: [PATCH 1094/1575] vscode settings --- .vscode/settings.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 11fb40927..af1bd7b5c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,15 @@ { + "editor.formatOnSave": true, + "[toml]": { + "editor.formatOnSave": false + }, + "rust-analyzer.check.allTargets": false, + "rust-analyzer.check.noDefaultFeatures": true, + "rust-analyzer.cargo.noDefaultFeatures": true, + "rust-analyzer.cargo.target": "thumbv6m-none-eabi", + "rust-analyzer.cargo.features": [ + "nightly", + ], "rust-analyzer.linkedProjects": [ ".\\examples\\Cargo.toml" ] From 7b83d53bbfb5be30415d966504eacbd6f4c1cc90 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Wed, 10 May 2023 02:25:18 +0200 Subject: [PATCH 1095/1575] vscode settings --- .vscode/settings.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index af1bd7b5c..231c407ad 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,12 @@ { "editor.formatOnSave": true, "[toml]": { - "editor.formatOnSave": false + "editor.formatOnSave": false }, "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.cargo.target": "thumbv6m-none-eabi", - "rust-analyzer.cargo.features": [ - "nightly", - ], "rust-analyzer.linkedProjects": [ ".\\examples\\Cargo.toml" ] From bbd687fcb0e63a1bb8eb4d31c8f5ed2f403603f6 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Wed, 10 May 2023 02:40:41 +0200 Subject: [PATCH 1096/1575] Update embassy --- Cargo.toml | 16 ++++++++-------- examples/Cargo.toml | 18 +++++++++--------- rust-toolchain.toml | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac2257f44..1921e812d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,11 +16,11 @@ embassy-futures = { version = "0.1.0" } defmt = { version = "0.3", optional = true } [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 013a2755f..46659c2bc 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } +embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } @@ -30,14 +30,14 @@ rand = { version = "0.8.5", default-features = false } embassy-net-w5500 = { path = "../" } [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } [profile.dev] debug = 2 diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fb284c1ee..2582e88f5 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-2023-04-04" +channel = "nightly-2023-04-18" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv6m-none-eabi", From 8a620fd59ca2ad958aad279eb55a1c97ef100e86 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 11 May 2023 16:45:42 -0500 Subject: [PATCH 1097/1575] stm32/ble: fix tests and add instructions to run example --- .vscode/.gitignore | 3 +++ examples/stm32wb/.cargo/config.toml | 3 ++- examples/stm32wb/memory.x | 19 +++++++++++++++---- examples/stm32wb/src/bin/tl_mbox.rs | 23 +++++++++++++++++++++++ tests/stm32/Cargo.toml | 2 +- 5 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 .vscode/.gitignore diff --git a/.vscode/.gitignore b/.vscode/.gitignore new file mode 100644 index 000000000..9fbb9ec95 --- /dev/null +++ b/.vscode/.gitignore @@ -0,0 +1,3 @@ +*.cortex-debug.*.json +launch.json +tasks.json \ No newline at end of file diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 5d78d79e5..d23fdc513 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,7 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" +# runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" +runner = "teleprobe local run --chip STM32WB55RG --elf" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x index c75d07352..75bb74466 100644 --- a/examples/stm32wb/memory.x +++ b/examples/stm32wb/memory.x @@ -1,14 +1,25 @@ /* The size of this file must be exactly the same as in other memory_xx.x files. Memory size for STM32WB55xC with 256K FLASH -*/ -MEMORY -{ + MEMORY + { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K -} + } +*/ + +/* + Memory size for STM32WB55xC with 512K FLASH + + MEMORY + { + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K + RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 + RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K + } +*/ /* Place stack at the end of SRAM1 */ _stack_start = ORIGIN(RAM) + LENGTH(RAM); diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index fadeb0d22..340235520 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -11,6 +11,29 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + let p = embassy_stm32::init(Default::default()); info!("Hello World!"); diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 03ddd3d0e..868d60093 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -11,7 +11,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "ble", "not-gpdma"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board From d7262f9849ef381079b87776421ba437b213041f Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 11 May 2023 16:53:55 -0500 Subject: [PATCH 1098/1575] rustfmt --- examples/stm32wb/src/bin/tl_mbox.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 340235520..6876526ae 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -16,8 +16,8 @@ async fn main(_spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from - gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. - Once complete, click connect to connect to the device. @@ -27,7 +27,7 @@ async fn main(_spawner: Spawner) { - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - - Disconnect from the device. + - Disconnect from the device. - In the examples folder for stm32wb, modify the memory.x file to match your target device. - Run this example. From bf45b1d83dba837dabc63361dc472902ab82cda4 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 11 May 2023 17:23:18 -0500 Subject: [PATCH 1099/1575] fix memory.x --- examples/stm32wb/memory.x | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x index 75bb74466..5a07b7d19 100644 --- a/examples/stm32wb/memory.x +++ b/examples/stm32wb/memory.x @@ -1,14 +1,14 @@ /* The size of this file must be exactly the same as in other memory_xx.x files. Memory size for STM32WB55xC with 256K FLASH +*/ - MEMORY - { +MEMORY +{ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K - } -*/ +} /* Memory size for STM32WB55xC with 512K FLASH From 3810fe6a2058d04054c7242216a7bc9d2993667b Mon Sep 17 00:00:00 2001 From: goueslati Date: Fri, 12 May 2023 10:26:46 +0100 Subject: [PATCH 1100/1575] tl_mbox: added zigee, lld tests and ble lld tables to ref table --- embassy-stm32/src/tl_mbox/mod.rs | 36 ++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index 73d2ca6d6..0cee26b74 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -42,10 +42,10 @@ pub struct SafeBootInfoTable { #[repr(C, packed)] #[derive(Copy, Clone)] -pub struct RssInfoTable { +pub struct FusInfoTable { version: u32, memory_size: u32, - rss_info: u32, + fus_info: u32, } /// # Version @@ -64,8 +64,8 @@ pub struct RssInfoTable { pub struct WirelessFwInfoTable { version: u32, memory_size: u32, - thread_info: u32, - ble_info: u32, + info_stack: u32, + reserved: u32, } impl WirelessFwInfoTable { @@ -107,7 +107,7 @@ impl WirelessFwInfoTable { #[derive(Copy, Clone)] pub struct DeviceInfoTable { pub safe_boot_info_table: SafeBootInfoTable, - pub rss_info_table: RssInfoTable, + pub fus_info_table: FusInfoTable, pub wireless_fw_info_table: WirelessFwInfoTable, } @@ -191,6 +191,9 @@ pub struct RefTable { mem_manager_table: *const MemManagerTable, traces_table: *const TracesTable, mac_802_15_4_table: *const Mac802_15_4Table, + zigbee_table: *const ZigbeeTable, + lld_tests_table: *const LldTestTable, + ble_lld_table: *const BleLldTable, } #[link_section = "TL_REF_TABLE"] @@ -205,6 +208,12 @@ static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM1"] static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); +#[link_section = "MB_MEM1"] +static mut TL_LLD_TESTS_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +static mut TL_BLE_LLD_TABLE: MaybeUninit = MaybeUninit::uninit(); + #[link_section = "MB_MEM1"] static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); @@ -217,25 +226,28 @@ static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM1"] static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); +#[link_section = "MB_MEM1"] +static mut TL_ZIGBEE_TABLE: MaybeUninit = MaybeUninit::uninit(); + #[allow(dead_code)] // Not used currently but reserved -#[link_section = "MB_MEM2"] +#[link_section = "MB_MEM1"] static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); // not in shared RAM static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[allow(dead_code)] // Not used currently but reserved -#[link_section = "MB_MEM2"] +#[link_section = "MB_MEM1"] static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = MaybeUninit::uninit(); -#[link_section = "MB_MEM2"] +#[link_section = "MB_MEM1"] static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "MB_MEM2"] +#[link_section = "MB_MEM1"] static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] @@ -277,6 +289,9 @@ impl TlMbox { mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), traces_table: TL_TRACES_TABLE.as_ptr(), mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), + zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), + lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), + ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), }); TL_SYS_TABLE = MaybeUninit::zeroed(); @@ -286,6 +301,9 @@ impl TlMbox { TL_MEM_MANAGER_TABLE = MaybeUninit::zeroed(); TL_TRACES_TABLE = MaybeUninit::zeroed(); TL_MAC_802_15_4_TABLE = MaybeUninit::zeroed(); + TL_ZIGBEE_TABLE = MaybeUninit::zeroed(); + TL_LLD_TESTS_TABLE = MaybeUninit::zeroed(); + TL_BLE_LLD_TABLE = MaybeUninit::zeroed(); EVT_POOL = MaybeUninit::zeroed(); SYS_SPARE_EVT_BUF = MaybeUninit::zeroed(); From 3edd81a94e7f3b45798e4b4a34c242ebb3eb76f5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 13 May 2023 00:33:00 +0200 Subject: [PATCH 1101/1575] rp/watchdog: fix overflow if period is longer than 4294 seconds. --- embassy-rp/src/watchdog.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index f4f165b29..3631b2a9d 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -89,14 +89,12 @@ impl Watchdog { pub fn start(&mut self, period: Duration) { const MAX_PERIOD: u32 = 0xFFFFFF; - let delay_us = period.as_micros() as u32; - if delay_us > MAX_PERIOD / 2 { - panic!( - "Period cannot exceed maximum load value of {} ({} microseconds))", - MAX_PERIOD, - MAX_PERIOD / 2 - ); + let delay_us = period.as_micros(); + if delay_us > (MAX_PERIOD / 2) as u64 { + panic!("Period cannot exceed {} microseconds", MAX_PERIOD / 2); } + let delay_us = delay_us as u32; + // Due to a logic error, the watchdog decrements by 2 and // the load value must be compensated; see RP2040-E1 self.load_value = delay_us * 2; From 2fcdfc48762849d4de4686f9a4098db49c4031e6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 13 May 2023 02:11:01 +0200 Subject: [PATCH 1102/1575] rp: don't use SetConfig trait in PWM and PIO. It was intended to allow changing baudrate on shared spi/i2c. There's no advantage in using it for PWM or PIO, and makes it less usable because you have to have `embassy-embedded-hal` as a dep to use it. --- embassy-rp/src/pio.rs | 9 ++------- embassy-rp/src/pwm.rs | 12 ++++-------- examples/rp/src/bin/pio_async.rs | 1 - examples/rp/src/bin/pio_dma.rs | 1 - examples/rp/src/bin/pio_hd44780.rs | 1 - examples/rp/src/bin/pwm.rs | 1 - examples/rp/src/bin/ws2812-pio.rs | 1 - 7 files changed, 6 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 1e41bed30..cbe45334a 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -6,7 +6,6 @@ use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; -use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::extra::U8; @@ -637,10 +636,8 @@ impl<'d, PIO: Instance> Config<'d, PIO> { } } -impl<'d, PIO: Instance, const SM: usize> SetConfig for StateMachine<'d, PIO, SM> { - type Config = Config<'d, PIO>; - - fn set_config(&mut self, config: &Self::Config) { +impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { + pub fn set_config(&mut self, config: &Config<'d, PIO>) { // sm expects 0 for 65536, truncation makes that happen assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536"); assert!(config.clock_divider >= 1, "clkdiv must be >= 1"); @@ -691,9 +688,7 @@ impl<'d, PIO: Instance, const SM: usize> SetConfig for StateMachine<'d, PIO, SM> } } } -} -impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { PIO::PIO.sm(SM) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index d2bf79584..0f9dcf479 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -1,6 +1,5 @@ //! Pulse Width Modulation (PWM) -use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use fixed::traits::ToFixed; use fixed::FixedU16; @@ -153,6 +152,10 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into()) } + pub fn set_config(&mut self, config: &Config) { + Self::configure(self.inner.regs(), config); + } + fn configure(p: pac::pwm::Channel, config: &Config) { if config.divider > FixedU16::::from_bits(0xFF_F) { panic!("Requested divider is too large"); @@ -329,10 +332,3 @@ impl_pin!(PIN_26, PWM_CH5, PwmPinA); impl_pin!(PIN_27, PWM_CH5, PwmPinB); impl_pin!(PIN_28, PWM_CH6, PwmPinA); impl_pin!(PIN_29, PWM_CH6, PwmPinB); - -impl<'d, T: Channel> SetConfig for Pwm<'d, T> { - type Config = Config; - fn set_config(&mut self, config: &Self::Config) { - Self::configure(self.inner.regs(), config); - } -} diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 12484e882..79eda1a09 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -2,7 +2,6 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; -use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine}; diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 7f85288bf..05c0ebb16 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -2,7 +2,6 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; -use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection}; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 088fd5649..bfc6c9908 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -4,7 +4,6 @@ use core::fmt::Write; -use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 69d315553..2b3d5d97a 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -3,7 +3,6 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::pwm::{Config, Pwm}; use embassy_time::{Duration, Timer}; diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index d7c4742d8..f4c2d6313 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -3,7 +3,6 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; From 8800caa216f2c90b7d998280a54dddf14e97e318 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 13 May 2023 02:20:46 +0200 Subject: [PATCH 1103/1575] Update Embassy, to new PIO API. --- Cargo.toml | 12 +- cyw43-pio/Cargo.toml | 1 + cyw43-pio/src/lib.rs | 151 ++++++++++--------- examples/rpi-pico-w/Cargo.toml | 18 +-- examples/rpi-pico-w/src/bin/tcp_server.rs | 16 +- examples/rpi-pico-w/src/bin/tcp_server_ap.rs | 16 +- examples/rpi-pico-w/src/bin/wifi_scan.rs | 16 +- 7 files changed, 112 insertions(+), 118 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c4872f423..2bb2b8d93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ firmware-logs = [] [dependencies] embassy-time = { version = "0.1.0" } -embassy-sync = { version = "0.1.0" } +embassy-sync = { version = "0.2.0" } embassy-futures = { version = "0.1.0" } embassy-net-driver-channel = { version = "0.1.0" } atomic-polyfill = "0.1.5" @@ -28,11 +28,11 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } num_enum = { version = "0.5.7", default-features = false } [patch.crates-io] -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } [workspace] members = ["cyw43-pio"] diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 8272903c3..dd50e02ba 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -8,4 +8,5 @@ cyw43 = { path = "../" } embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] } pio-proc = "0.2" pio = "0.2.1" +fixed = "1.23.1" defmt = { version = "0.3", optional = true } \ No newline at end of file diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index c468435f1..2c17b151a 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -6,30 +6,39 @@ use core::slice; use cyw43::SpiBusCyw43; use embassy_rp::dma::Channel; -use embassy_rp::gpio::{Drive, Output, Pin, Pull, SlewRate}; -use embassy_rp::pio::{PioStateMachine, ShiftDirection}; +use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; +use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::{pio_instr_util, Peripheral}; -use pio::Wrap; +use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; +use fixed::FixedU32; use pio_proc::pio_asm; -pub struct PioSpi { - cs: Output<'static, CS>, - sm: SM, - dma: DMA, +pub struct PioSpi<'d, CS: Pin, PIO: Instance, const SM: usize, DMA> { + cs: Output<'d, CS>, + sm: StateMachine<'d, PIO, SM>, + irq: Irq<'d, PIO, 0>, + dma: PeripheralRef<'d, DMA>, wrap_target: u8, } -impl PioSpi +impl<'d, CS, PIO, const SM: usize, DMA> PioSpi<'d, CS, PIO, SM, DMA> where - SM: PioStateMachine, DMA: Channel, CS: Pin, + PIO: Instance, { - pub fn new(mut sm: SM, cs: Output<'static, CS>, dio: DIO, clk: CLK, dma: DMA) -> Self + pub fn new( + common: &mut Common<'d, PIO>, + mut sm: StateMachine<'d, PIO, SM>, + irq: Irq<'d, PIO, 0>, + cs: Output<'d, CS>, + dio: DIO, + clk: CLK, + dma: impl Peripheral

+ 'd, + ) -> Self where - DIO: Pin, - CLK: Pin, + DIO: PioPin, + CLK: PioPin, { let program = pio_asm!( ".side_set 1" @@ -42,8 +51,8 @@ where // switch directions "set pindirs, 0 side 0" // these nops seem to be necessary for fast clkdiv - "nop side 1" - "nop side 0" + //"nop side 1" + //"nop side 0" "nop side 1" // read in y-1 bits "lp2:" @@ -59,68 +68,62 @@ where let relocated = RelocatedProgram::new(&program.program); - let mut pin_io = sm.make_pio_pin(dio); - pin_io.set_pull(Pull::Down); + let mut pin_io: embassy_rp::pio::Pin = common.make_pio_pin(dio); + pin_io.set_pull(Pull::None); pin_io.set_schmitt(true); pin_io.set_input_sync_bypass(true); + //pin_io.set_drive_strength(Drive::_12mA); + //pin_io.set_slew_rate(SlewRate::Fast); - let mut pin_clk = sm.make_pio_pin(clk); + let mut pin_clk = common.make_pio_pin(clk); pin_clk.set_drive_strength(Drive::_12mA); pin_clk.set_slew_rate(SlewRate::Fast); - sm.write_instr(relocated.origin() as usize, relocated.code()); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[&pin_clk]); + cfg.set_out_pins(&[&pin_io]); + cfg.set_in_pins(&[&pin_io]); + cfg.set_set_pins(&[&pin_io]); + cfg.shift_out.direction = ShiftDirection::Left; + cfg.shift_out.auto_fill = true; + //cfg.shift_out.threshold = 32; + cfg.shift_in.direction = ShiftDirection::Left; + cfg.shift_in.auto_fill = true; + //cfg.shift_in.threshold = 32; // theoretical maximum according to data sheet, 100Mhz Pio => 50Mhz SPI Freq - sm.set_clkdiv(0x0140); + // seems to cause random corruption, probably due to jitter due to the fractional divider. + // cfg.clock_divider = FixedU32::from_bits(0x0140); // same speed as pico-sdk, 62.5Mhz - // sm.set_clkdiv(0x0200); + cfg.clock_divider = FixedU32::from_bits(0x0200); // 32 Mhz - // sm.set_clkdiv(0x03E8); + // cfg.clock_divider = FixedU32::from_bits(0x03E8); // 16 Mhz - // sm.set_clkdiv(0x07d0); + // cfg.clock_divider = FixedU32::from_bits(0x07d0); // 8Mhz - // sm.set_clkdiv(0x0a_00); + // cfg.clock_divider = FixedU32::from_bits(0x0a_00); // 1Mhz - // sm.set_clkdiv(0x7d_00); + // cfg.clock_divider = FixedU32::from_bits(0x7d_00); // slowest possible - // sm.set_clkdiv(0xffff_00); + // cfg.clock_divider = FixedU32::from_bits(0xffff_00); - sm.set_autopull(true); - // sm.set_pull_threshold(32); - sm.set_autopush(true); - // sm.set_push_threshold(32); + sm.set_config(&cfg); - sm.set_out_pins(&[&pin_io]); - sm.set_in_base_pin(&pin_io); - - sm.set_set_pins(&[&pin_clk]); - pio_instr_util::set_pindir(&mut sm, 0b1); - sm.set_set_pins(&[&pin_io]); - pio_instr_util::set_pindir(&mut sm, 0b1); - - sm.set_sideset_base_pin(&pin_clk); - sm.set_sideset_count(1); - - sm.set_out_shift_dir(ShiftDirection::Left); - sm.set_in_shift_dir(ShiftDirection::Left); - - let Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); - - // pull low for startup - pio_instr_util::set_pin(&mut sm, 0); + sm.set_pin_dirs(Direction::Out, &[&pin_clk, &pin_io]); + sm.set_pins(Level::Low, &[&pin_clk, &pin_io]); Self { cs, sm, - dma, - wrap_target: target, + irq, + dma: dma.into_ref(), + wrap_target: relocated.wrap().target, } } @@ -132,18 +135,22 @@ where #[cfg(feature = "defmt")] defmt::trace!("write={} read={}", write_bits, read_bits); - let mut dma = Peripheral::into_ref(&mut self.dma); - pio_instr_util::set_x(&mut self.sm, write_bits as u32); - pio_instr_util::set_y(&mut self.sm, read_bits as u32); - pio_instr_util::set_pindir(&mut self.sm, 0b1); - pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); + unsafe { + pio_instr_util::set_x(&mut self.sm, write_bits as u32); + pio_instr_util::set_y(&mut self.sm, read_bits as u32); + pio_instr_util::set_pindir(&mut self.sm, 0b1); + pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); + } self.sm.set_enable(true); - self.sm.dma_push(dma.reborrow(), write).await; + self.sm.tx().dma_push(self.dma.reborrow(), write).await; let mut status = 0; - self.sm.dma_pull(dma.reborrow(), slice::from_mut(&mut status)).await; + self.sm + .rx() + .dma_pull(self.dma.reborrow(), slice::from_mut(&mut status)) + .await; status } @@ -155,27 +162,32 @@ where #[cfg(feature = "defmt")] defmt::trace!("write={} read={}", write_bits, read_bits); - let mut dma = Peripheral::into_ref(&mut self.dma); - pio_instr_util::set_y(&mut self.sm, read_bits as u32); - pio_instr_util::set_x(&mut self.sm, write_bits as u32); - pio_instr_util::set_pindir(&mut self.sm, 0b1); - pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); + unsafe { + pio_instr_util::set_y(&mut self.sm, read_bits as u32); + pio_instr_util::set_x(&mut self.sm, write_bits as u32); + pio_instr_util::set_pindir(&mut self.sm, 0b1); + pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); + } + // self.cs.set_low(); self.sm.set_enable(true); - self.sm.dma_push(dma.reborrow(), slice::from_ref(&cmd)).await; - self.sm.dma_pull(dma.reborrow(), read).await; + self.sm.tx().dma_push(self.dma.reborrow(), slice::from_ref(&cmd)).await; + self.sm.rx().dma_pull(self.dma.reborrow(), read).await; let mut status = 0; - self.sm.dma_pull(dma.reborrow(), slice::from_mut(&mut status)).await; + self.sm + .rx() + .dma_pull(self.dma.reborrow(), slice::from_mut(&mut status)) + .await; status } } -impl SpiBusCyw43 for PioSpi +impl<'d, CS, PIO, const SM: usize, DMA> SpiBusCyw43 for PioSpi<'d, CS, PIO, SM, DMA> where CS: Pin, - SM: PioStateMachine, + PIO: Instance, DMA: Channel, { async fn cmd_write(&mut self, write: &[u32]) -> u32 { @@ -193,7 +205,6 @@ where } async fn wait_for_event(&mut self) { - self.sm.wait_irq(0).await; - self.sm.clear_irq(0); + self.irq.wait().await; } } diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 5b46726d2..d972bf5a5 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] } -embassy-executor = { version = "0.1.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } +embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } @@ -27,14 +27,14 @@ heapless = "0.7.15" [patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "03d6363d5af5dcaf21b52734994a466ca593d2b6" } +embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } +embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } [profile.dev] debug = 2 diff --git a/examples/rpi-pico-w/src/bin/tcp_server.rs b/examples/rpi-pico-w/src/bin/tcp_server.rs index 9581602a7..8accc469f 100644 --- a/examples/rpi-pico-w/src/bin/tcp_server.rs +++ b/examples/rpi-pico-w/src/bin/tcp_server.rs @@ -12,8 +12,8 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25}; -use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachineInstance, Sm0}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; +use embassy_rp::pio::Pio; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -28,11 +28,7 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner< - 'static, - Output<'static, PIN_23>, - PioSpi, DMA_CH0>, - >, + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, ) -> ! { runner.run().await } @@ -60,10 +56,8 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - - let (_, sm, _, _, _) = p.PIO0.split(); - let dma = p.DMA_CH0; - let spi = PioSpi::new(sm, cs, p.PIN_24, p.PIN_29, dma); + let mut pio = Pio::new(p.PIO0); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; diff --git a/examples/rpi-pico-w/src/bin/tcp_server_ap.rs b/examples/rpi-pico-w/src/bin/tcp_server_ap.rs index e43412625..ee2c32379 100644 --- a/examples/rpi-pico-w/src/bin/tcp_server_ap.rs +++ b/examples/rpi-pico-w/src/bin/tcp_server_ap.rs @@ -12,8 +12,8 @@ use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25}; -use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachineInstance, Sm0}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; +use embassy_rp::pio::Pio; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -28,11 +28,7 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner< - 'static, - Output<'static, PIN_23>, - PioSpi, DMA_CH0>, - >, + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, ) -> ! { runner.run().await } @@ -60,10 +56,8 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - - let (_, sm, _, _, _) = p.PIO0.split(); - let dma = p.DMA_CH0; - let spi = PioSpi::new(sm, cs, p.PIN_24, p.PIN_29, dma); + let mut pio = Pio::new(p.PIO0); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; diff --git a/examples/rpi-pico-w/src/bin/wifi_scan.rs b/examples/rpi-pico-w/src/bin/wifi_scan.rs index da8fadfd8..a2a44f99d 100644 --- a/examples/rpi-pico-w/src/bin/wifi_scan.rs +++ b/examples/rpi-pico-w/src/bin/wifi_scan.rs @@ -11,8 +11,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::Stack; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25}; -use embassy_rp::pio::{Pio0, PioPeripheral, PioStateMachineInstance, Sm0}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; +use embassy_rp::pio::Pio; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -26,11 +26,7 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner< - 'static, - Output<'static, PIN_23>, - PioSpi, DMA_CH0>, - >, + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, ) -> ! { runner.run().await } @@ -58,10 +54,8 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - - let (_, sm, _, _, _) = p.PIO0.split(); - let dma = p.DMA_CH0; - let spi = PioSpi::new(sm, cs, p.PIN_24, p.PIN_29, dma); + let mut pio = Pio::new(p.PIO0); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = singleton!(cyw43::State::new()); let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; From 6c1137177f92f9a5c5cd9c2a0450b5db5165f8be Mon Sep 17 00:00:00 2001 From: kalkyl Date: Sat, 13 May 2023 06:34:03 +0200 Subject: [PATCH 1104/1575] Wait until there's enough space in tx buffer, remove busy wait for completed send --- src/device.rs | 25 ++++--------------------- src/socket.rs | 11 ----------- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/src/device.rs b/src/device.rs index 3875fde0e..8158bc98e 100644 --- a/src/device.rs +++ b/src/device.rs @@ -123,31 +123,14 @@ impl W5500 { /// Write an ethernet frame to the device. Returns number of bytes written pub async fn write_frame(&mut self, frame: &[u8]) -> Result { - let max_size = socket::get_tx_free_size(&mut self.bus).await? as usize; - - let write_data = if frame.len() < max_size { - frame - } else { - &frame[..max_size] - }; - + while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; self.bus - .write_frame(RegisterBlock::TxBuf, write_ptr, write_data) + .write_frame(RegisterBlock::TxBuf, write_ptr, frame) .await?; - socket::set_tx_write_ptr( - &mut self.bus, - write_ptr.wrapping_add(write_data.len() as u16), - ) - .await?; - - socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; + socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; socket::command(&mut self.bus, socket::Command::Send).await?; - // Wait for TX to complete - while !socket::is_interrupt(&mut self.bus, socket::Interrupt::SendOk).await? {} - socket::reset_interrupt(&mut self.bus, socket::Interrupt::SendOk).await?; - - Ok(write_data.len()) + Ok(frame.len()) } pub async fn is_link_up(&mut self) -> bool { diff --git a/src/socket.rs b/src/socket.rs index 0d3d1aeb2..3f64d04d1 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -22,7 +22,6 @@ pub enum Command { pub const INTR: u16 = 0x02; #[repr(u8)] pub enum Interrupt { - SendOk = 0b010000_u8, Receive = 0b00100_u8, } @@ -34,16 +33,6 @@ pub async fn reset_interrupt( bus.write_frame(RegisterBlock::Socket0, INTR, &data).await } -pub async fn is_interrupt( - bus: &mut SpiInterface, - code: Interrupt, -) -> Result { - let mut data = [0u8]; - bus.read_frame(RegisterBlock::Socket0, INTR, &mut data) - .await?; - Ok(data[0] & code as u8 != 0) -} - pub async fn get_tx_write_ptr( bus: &mut SpiInterface, ) -> Result { From 5fe36b6bb05f0460e12c726ab5554c9b4bad1829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 13 May 2023 13:44:19 +0200 Subject: [PATCH 1105/1575] Work around xtensa deadlock, take 2 --- embassy-executor/src/arch/xtensa.rs | 32 ++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 61ea92c16..017b2c52b 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -63,21 +63,29 @@ mod thread { loop { unsafe { self.inner.poll(); + + // Manual critical section implementation that only masks interrupts handlers. + // We must not acquire the cross-core on dual-core systems because that would + // prevent the other core from doing useful work while this core is sleeping. + let token: critical_section::RawRestoreState; + core::arch::asm!("rsil {0}, 5", out(reg) token); + // we do not care about race conditions between the load and store operations, interrupts // will only set this value to true. // if there is work to do, loop back to polling - // TODO can we relax this? - critical_section::with(|_| { - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here - } - }); + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + + core::arch::asm!( + "wsr.ps {0}", + "rsync", in(reg) token) + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here + } } } } From f46e0eb5f28fff64997fee7fc15b123ddc7231fe Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Sun, 14 May 2023 22:48:04 +0800 Subject: [PATCH 1106/1575] Fix PowerManagementMode::None MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mode was being set to 2 (PM2_POWERSAVE_MODE), should be 0 (NO_POWERSAVE_MODE). Setting None mode failed with a panic: 85.707099 DEBUG set pm2_sleep_ret = [00, 00, 00, 00] └─ cyw43::control::{impl#0}::set_iovar_v::{async_fn#0} @ cyw43/src/fmt.rs:127 85.710469 ERROR panicked at 'IOCTL error -29' --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 4a9ada90f..fd11f3674 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,6 +198,7 @@ impl PowerManagementMode { fn mode(&self) -> u32 { match self { PowerManagementMode::ThroughputThrottling => 1, + PowerManagementMode::None => 0, _ => 2, } } From 977a7906e47ed8b05388e8cdea0403a08a00b962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 14 May 2023 20:34:57 +0200 Subject: [PATCH 1107/1575] stm32 uart: Fix error flag handling for blocking operations Clear and report the error flags one by one and pop the data byte only after all error flags were handled. For v1/v2 we emulate the v3/v4 behaviour by buffering the status register because a read to the data register clears all flags at once which means we might loose all but the first error. --- embassy-stm32/src/usart/mod.rs | 93 ++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e946762af..b4373529c 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -109,6 +109,8 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { _peri: PeripheralRef<'d, T>, rx_dma: PeripheralRef<'d, RxDma>, detect_previous_overrun: bool, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr, } impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { @@ -275,6 +277,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { _peri: peri, rx_dma, detect_previous_overrun: config.detect_previous_overrun, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr(0), } } @@ -335,6 +339,59 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } + #[cfg(any(usart_v1, usart_v2))] + unsafe fn check_rx_flags(&mut self) -> Result { + let r = T::regs(); + loop { + // Handle all buffered error flags. + if self.buffered_sr.pe() { + self.buffered_sr.set_pe(false); + return Err(Error::Parity); + } else if self.buffered_sr.fe() { + self.buffered_sr.set_fe(false); + return Err(Error::Framing); + } else if self.buffered_sr.ne() { + self.buffered_sr.set_ne(false); + return Err(Error::Noise); + } else if self.buffered_sr.ore() { + self.buffered_sr.set_ore(false); + return Err(Error::Overrun); + } else if self.buffered_sr.rxne() { + self.buffered_sr.set_rxne(false); + return Ok(true); + } else { + // No error flags from previous iterations were set: Check the actual status register + let sr = r.sr().read(); + if !sr.rxne() { + return Ok(false); + } + + // Buffer the status register and let the loop handle the error flags. + self.buffered_sr = sr; + } + } + } + + #[cfg(any(usart_v3, usart_v4))] + unsafe fn check_rx_flags(&mut self) -> Result { + let r = T::regs(); + let sr = r.isr().read(); + if sr.pe() { + r.icr().write(|w| w.set_pe(true)); + return Err(Error::Parity); + } else if sr.fe() { + r.icr().write(|w| w.set_fe(true)); + return Err(Error::Framing); + } else if sr.ne() { + r.icr().write(|w| w.set_ne(true)); + return Err(Error::Noise); + } else if sr.ore() { + r.icr().write(|w| w.set_ore(true)); + return Err(Error::Overrun); + } + Ok(sr.rxne()) + } + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> where RxDma: crate::usart::RxDma, @@ -347,20 +404,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { pub fn nb_read(&mut self) -> Result> { let r = T::regs(); unsafe { - let sr = sr(r).read(); - if sr.pe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Parity)) - } else if sr.fe() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Framing)) - } else if sr.ne() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Noise)) - } else if sr.ore() { - rdr(r).read_volatile(); - Err(nb::Error::Other(Error::Overrun)) - } else if sr.rxne() { + if self.check_rx_flags()? { Ok(rdr(r).read_volatile()) } else { Err(nb::Error::WouldBlock) @@ -372,24 +416,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { unsafe { let r = T::regs(); for b in buffer { - loop { - let sr = sr(r).read(); - if sr.pe() { - rdr(r).read_volatile(); - return Err(Error::Parity); - } else if sr.fe() { - rdr(r).read_volatile(); - return Err(Error::Framing); - } else if sr.ne() { - rdr(r).read_volatile(); - return Err(Error::Noise); - } else if sr.ore() { - rdr(r).read_volatile(); - return Err(Error::Overrun); - } else if sr.rxne() { - break; - } - } + while !self.check_rx_flags()? {} *b = rdr(r).read_volatile(); } } @@ -715,6 +742,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { _peri: peri, rx_dma, detect_previous_overrun: config.detect_previous_overrun, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr(0), }, } } From ec7a4fd9cc6284fffbce3c74a82d2942713d4cd4 Mon Sep 17 00:00:00 2001 From: Jaap Prickartz Date: Sun, 14 May 2023 21:57:31 +0200 Subject: [PATCH 1108/1575] stm32f0 flash implementation --- embassy-stm32/src/flash/f0.rs | 105 +++++++++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 3 +- 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32/src/flash/f0.rs diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs new file mode 100644 index 000000000..033afecd5 --- /dev/null +++ b/embassy-stm32/src/flash/f0.rs @@ -0,0 +1,105 @@ +use core::convert::TryInto; +use core::ptr::write_volatile; + +use atomic_polyfill::{fence, Ordering}; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use crate::flash::Error; +use crate::pac; + +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + +pub(crate) unsafe fn lock() { + pac::FLASH.cr().modify(|w| w.set_lock(true)); +} + +pub(crate) unsafe fn unlock() { + pac::FLASH.keyr().write(|w| w.set_fkeyr(0x4567_0123)); + pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); +} + +pub(crate) unsafe fn begin_write() { + assert_eq!(0, WRITE_SIZE % 2); + + pac::FLASH.cr().write(|w| w.set_pg(true)); +} + +pub(crate) unsafe fn end_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for chunk in buf.chunks(2) { + write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); + address += chunk.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + blocking_wait_ready() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + }); + + pac::FLASH.ar().write(|w| w.set_far(sector.start)); + + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); + + let mut ret: Result<(), Error> = blocking_wait_ready(); + + if !pac::FLASH.sr().read().eop() { + trace!("FLASH: EOP not set"); + ret = Err(Error::Prog); + } else { + pac::FLASH.sr().write(|w| w.set_eop(true)); + } + + pac::FLASH.cr().modify(|w| w.set_per(false)); + + clear_all_err(); + if ret.is_err() { + return ret; + } + Ok(()) +} + +pub(crate) unsafe fn clear_all_err() { + pac::FLASH.sr().modify(|w| { + if w.pgerr() { + w.set_pgerr(true); + } + if w.wrprt() { + w.set_wrprt(true) + }; + if w.eop() { + w.set_eop(true); + } + }); +} + +unsafe fn blocking_wait_ready() -> Result<(), Error> { + loop { + let sr = pac::FLASH.sr().read(); + + if !sr.bsy() { + if sr.wrprt() { + return Err(Error::Protected); + } + + if sr.pgerr() { + return Err(Error::Seq); + } + + return Ok(()); + } + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 7d5596b1f..f6efa7753 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -49,13 +49,14 @@ impl FlashRegion { } #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] +#[cfg_attr(flash_f0, path = "f0.rs")] #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] #[cfg_attr( not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f3, flash_f4, flash_f7, flash_h7 + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_h7 )), path = "other.rs" )] From 3e9d5978c088a7d07af63e32a07583f2ff3ae7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 14 May 2023 21:59:10 +0200 Subject: [PATCH 1109/1575] stm32 uart: Add a test for blocking RX overflow --- tests/stm32/src/bin/usart.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index bda2ce9c2..0749f8406 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -8,7 +8,7 @@ use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; -use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::usart::{Config, Error, Uart}; use embassy_time::{Duration, Instant}; use example_common::*; @@ -53,6 +53,26 @@ async fn main(_spawner: Spawner) { assert_eq!(buf, data); } + // Test error handling with with an overflow error + { + let config = Config::default(); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); + + // Send enough bytes to fill the RX FIFOs off all USART versions. + let data = [0xC0, 0xDE, 0x12, 0x23, 0x34]; + usart.blocking_write(&data).unwrap(); + usart.blocking_flush().unwrap(); + + // The error should be reported first. + let mut buf = [0; 1]; + let err = usart.blocking_read(&mut buf); + assert_eq!(err, Err(Error::Overrun)); + + // At least the first data byte should still be available on all USART versions. + usart.blocking_read(&mut buf).unwrap(); + assert_eq!(buf[0], data[0]); + } + // Test that baudrate divider is calculated correctly. // Do it by comparing the time it takes to send a known number of bytes. for baudrate in [ From db907a914c7e84176a15da70385af871c9b97cf6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 14 May 2023 23:02:49 +0200 Subject: [PATCH 1110/1575] cyw43-pio: add `overclock` feature flag. --- ci.sh | 5 +++ cyw43-pio/Cargo.toml | 5 +++ cyw43-pio/src/lib.rs | 77 +++++++++++++++++++++------------- examples/rpi-pico-w/Cargo.toml | 4 +- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/ci.sh b/ci.sh index d41b3aa81..916838200 100755 --- a/ci.sh +++ b/ci.sh @@ -1,7 +1,9 @@ #!/bin/bash set -euxo pipefail +cd $(dirname $0) +export CARGO_TARGET_DIR=$(pwd)/target export DEFMT_LOG=trace # build examples @@ -18,3 +20,6 @@ cargo build --target thumbv6m-none-eabi --features 'log' cargo build --target thumbv6m-none-eabi --features 'defmt' cargo build --target thumbv6m-none-eabi --features 'log,firmware-logs' cargo build --target thumbv6m-none-eabi --features 'defmt,firmware-logs' + +(cd cyw43-pio; cargo build --target thumbv6m-none-eabi --features '') +(cd cyw43-pio; cargo build --target thumbv6m-none-eabi --features 'overclock') diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index dd50e02ba..2238f7617 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -3,6 +3,11 @@ name = "cyw43-pio" version = "0.1.0" edition = "2021" +[features] +# If disabled, SPI runs at 31.25MHz +# If enabled, SPI runs at 62.5MHz, which is 25% higher than 50Mhz which is the maximum according to the CYW43439 datasheet. +overclock = [] + [dependencies] cyw43 = { path = "../" } embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] } diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index 2c17b151a..dca30c74d 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -40,24 +40,46 @@ where DIO: PioPin, CLK: PioPin, { + #[cfg(feature = "overclock")] let program = pio_asm!( ".side_set 1" ".wrap_target" // write out x-1 bits - "lp:", + "lp:" "out pins, 1 side 0" "jmp x-- lp side 1" // switch directions "set pindirs, 0 side 0" - // these nops seem to be necessary for fast clkdiv - //"nop side 1" - //"nop side 0" - "nop side 1" + "nop side 1" // necessary for clkdiv=1. + "nop side 0" // read in y-1 bits "lp2:" - "in pins, 1 side 0" - "jmp y-- lp2 side 1" + "in pins, 1 side 1" + "jmp y-- lp2 side 0" + + // wait for event and irq host + "wait 1 pin 0 side 0" + "irq 0 side 0" + + ".wrap" + ); + #[cfg(not(feature = "overclock"))] + let program = pio_asm!( + ".side_set 1" + + ".wrap_target" + // write out x-1 bits + "lp:" + "out pins, 1 side 0" + "jmp x-- lp side 1" + // switch directions + "set pindirs, 0 side 0" + "nop side 0" + // read in y-1 bits + "lp2:" + "in pins, 1 side 1" + "jmp y-- lp2 side 0" // wait for event and irq host "wait 1 pin 0 side 0" @@ -72,8 +94,8 @@ where pin_io.set_pull(Pull::None); pin_io.set_schmitt(true); pin_io.set_input_sync_bypass(true); - //pin_io.set_drive_strength(Drive::_12mA); - //pin_io.set_slew_rate(SlewRate::Fast); + pin_io.set_drive_strength(Drive::_12mA); + pin_io.set_slew_rate(SlewRate::Fast); let mut pin_clk = common.make_pio_pin(clk); pin_clk.set_drive_strength(Drive::_12mA); @@ -91,27 +113,24 @@ where cfg.shift_in.auto_fill = true; //cfg.shift_in.threshold = 32; - // theoretical maximum according to data sheet, 100Mhz Pio => 50Mhz SPI Freq - // seems to cause random corruption, probably due to jitter due to the fractional divider. - // cfg.clock_divider = FixedU32::from_bits(0x0140); + #[cfg(feature = "overclock")] + { + // 125mhz Pio => 62.5Mhz SPI Freq. 25% higher than theoretical maximum according to + // data sheet, but seems to work fine. + cfg.clock_divider = FixedU32::from_bits(0x0100); + } - // same speed as pico-sdk, 62.5Mhz - cfg.clock_divider = FixedU32::from_bits(0x0200); - - // 32 Mhz - // cfg.clock_divider = FixedU32::from_bits(0x03E8); - - // 16 Mhz - // cfg.clock_divider = FixedU32::from_bits(0x07d0); - - // 8Mhz - // cfg.clock_divider = FixedU32::from_bits(0x0a_00); - - // 1Mhz - // cfg.clock_divider = FixedU32::from_bits(0x7d_00); - - // slowest possible - // cfg.clock_divider = FixedU32::from_bits(0xffff_00); + #[cfg(not(feature = "overclock"))] + { + // same speed as pico-sdk, 62.5Mhz + // This is actually the fastest we can go without overclocking. + // According to data sheet, the theoretical maximum is 100Mhz Pio => 50Mhz SPI Freq. + // However, the PIO uses a fractional divider, which works by introducing jitter when + // the divider is not an integer. It does some clocks at 125mhz and others at 62.5mhz + // so that it averages out to the desired frequency of 100mhz. The 125mhz clock cycles + // violate the maximum from the data sheet. + cfg.clock_divider = FixedU32::from_bits(0x0200); + } sm.set_config(&cfg); diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index d972bf5a5..525e934f4 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } -cyw43-pio = { path = "../../cyw43-pio", features = ["defmt"] } +cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } @@ -48,7 +48,7 @@ debug = 1 debug-assertions = false incremental = false lto = 'fat' -opt-level = 'z' +opt-level = 's' overflow-checks = false # do not optimize proc-macro crates = faster builds from scratch From 2d65373f637a78b1321a80898f9f5f94fcaefca4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 14 May 2023 23:44:53 +0200 Subject: [PATCH 1111/1575] Remove unused `feature(type_alias_impl_trait)`. --- embassy-embedded-hal/src/lib.rs | 5 +---- embassy-net/src/lib.rs | 5 +---- embassy-nrf/src/lib.rs | 5 +---- embassy-stm32/src/lib.rs | 5 +---- embassy-usb/src/lib.rs | 1 - 5 files changed, 4 insertions(+), 17 deletions(-) diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index a23fbdc41..a783151e4 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,8 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr( - feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections, try_blocks) -)] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))] #![cfg_attr(feature = "nightly", allow(incomplete_features))] #![warn(missing_docs)] diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 5dfb5843e..b80784c2b 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,8 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr( - feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) -)] +#![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. diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 6e7aaa57a..7c60b32b2 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,8 +1,5 @@ #![no_std] -#![cfg_attr( - feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) -)] +#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] #![cfg_attr(feature = "nightly", allow(incomplete_features))] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index b3dbe1e2f..1b6f83c3e 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,8 +1,5 @@ #![no_std] -#![cfg_attr( - feature = "nightly", - feature(type_alias_impl_trait, async_fn_in_trait, impl_trait_projections) -)] +#![cfg_attr(feature = "nightly", feature(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. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 3016b81cb..d8563d59a 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(type_alias_impl_trait)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] From 26d7610554f262c2c25f99fb441e6dbd6abec61f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 May 2023 00:38:58 +0200 Subject: [PATCH 1112/1575] net: do not use smoltcp Instant/Duration in public API. --- embassy-net/src/lib.rs | 18 ++++-------------- embassy-net/src/tcp.rs | 12 ++++++++---- embassy-net/src/time.rs | 20 ++++++++++++++++++++ examples/nrf52840/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/std/src/bin/net.rs | 3 ++- examples/stm32f4/src/bin/usb_ethernet.rs | 2 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32h5/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- 11 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 embassy-net/src/time.rs diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b80784c2b..64f558756 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -12,6 +12,7 @@ mod device; pub mod dns; #[cfg(feature = "tcp")] pub mod tcp; +mod time; #[cfg(feature = "udp")] pub mod udp; @@ -27,10 +28,6 @@ use heapless::Vec; use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; -#[cfg(feature = "dhcpv4")] -use smoltcp::time::Duration; -// smoltcp reexports -pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; @@ -40,6 +37,7 @@ pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; use crate::device::DriverAdapter; +use crate::time::{instant_from_smoltcp, instant_to_smoltcp}; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; @@ -74,7 +72,7 @@ pub struct StaticConfig { #[cfg(feature = "dhcpv4")] #[derive(Debug, Clone, PartialEq, Eq)] pub struct DhcpConfig { - pub max_lease_duration: Option, + pub max_lease_duration: Option, pub retry_config: RetryConfig, /// Ignore NAKs. pub ignore_naks: bool, @@ -384,7 +382,7 @@ impl Inner { #[cfg(feature = "dhcpv4")] fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { socket.set_ignore_naks(config.ignore_naks); - socket.set_max_lease_duration(config.max_lease_duration); + socket.set_max_lease_duration(config.max_lease_duration.map(crate::time::duration_to_smoltcp)); socket.set_ports(config.server_port, config.client_port); socket.set_retry_config(config.retry_config); } @@ -465,11 +463,3 @@ impl Inner { } } } - -fn instant_to_smoltcp(instant: Instant) -> SmolInstant { - SmolInstant::from_millis(instant.as_millis() as i64) -} - -fn instant_from_smoltcp(instant: SmolInstant) -> Instant { - Instant::from_millis(instant.total_millis() as u64) -} diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c3d8764b0..05b8bb54e 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -4,11 +4,13 @@ use core::mem; use core::task::Poll; use embassy_net_driver::Driver; +use embassy_time::Duration; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; -use smoltcp::time::Duration; +pub use smoltcp::socket::tcp::State; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; +use crate::time::duration_to_smoltcp; use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -155,11 +157,13 @@ impl<'a> TcpSocket<'a> { } pub fn set_timeout(&mut self, duration: Option) { - self.io.with_mut(|s, _| s.set_timeout(duration)) + self.io + .with_mut(|s, _| s.set_timeout(duration.map(duration_to_smoltcp))) } pub fn set_keep_alive(&mut self, interval: Option) { - self.io.with_mut(|s, _| s.set_keep_alive(interval)) + self.io + .with_mut(|s, _| s.set_keep_alive(interval.map(duration_to_smoltcp))) } pub fn set_hop_limit(&mut self, hop_limit: Option) { @@ -174,7 +178,7 @@ impl<'a> TcpSocket<'a> { self.io.with(|s, _| s.remote_endpoint()) } - pub fn state(&self) -> tcp::State { + pub fn state(&self) -> State { self.io.with(|s, _| s.state()) } diff --git a/embassy-net/src/time.rs b/embassy-net/src/time.rs new file mode 100644 index 000000000..b98d40fdc --- /dev/null +++ b/embassy-net/src/time.rs @@ -0,0 +1,20 @@ +#![allow(unused)] + +use embassy_time::{Duration, Instant}; +use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; + +pub(crate) fn instant_to_smoltcp(instant: Instant) -> SmolInstant { + SmolInstant::from_micros(instant.as_micros() as i64) +} + +pub(crate) fn instant_from_smoltcp(instant: SmolInstant) -> Instant { + Instant::from_micros(instant.total_micros() as u64) +} + +pub(crate) fn duration_to_smoltcp(duration: Duration) -> SmolDuration { + SmolDuration::from_micros(duration.as_micros()) +} + +pub(crate) fn duration_from_smoltcp(duration: SmolDuration) -> Duration { + Duration::from_micros(duration.total_micros()) +} diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index b8a72313a..786025c43 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -132,7 +132,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 66a6ed4d0..431db63eb 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -114,7 +114,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index e018e18c9..d93616254 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -6,6 +6,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_time::Duration; use embedded_io::asynch::Write; use heapless::Vec; use log::*; @@ -75,7 +76,7 @@ async fn main_task(spawner: Spawner) { let mut tx_buffer = [0; 4096]; let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); info!("connecting to {:?}...", remote_endpoint); diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index db9e18393..9131e5896 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -126,7 +126,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 9febb14e6..b947361ac 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -91,7 +91,7 @@ async fn main(spawner: Spawner) -> ! { loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); info!("connecting..."); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 6d650da9e..b2e252fc7 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -110,7 +110,7 @@ async fn main(spawner: Spawner) -> ! { loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); info!("connecting..."); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 541e49762..61bb7e37b 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -92,7 +92,7 @@ async fn main(spawner: Spawner) -> ! { loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); info!("connecting..."); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 98ec0e836..6c5645a41 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -121,7 +121,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { From 62857bdb2d0c34aa2ee9e82454ee0182139bab2c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 May 2023 00:55:34 +0200 Subject: [PATCH 1113/1575] net: reexport UDP PacketMetadata under the udp module. --- embassy-net/src/lib.rs | 4 ++-- embassy-net/src/udp.rs | 3 ++- examples/std/src/bin/net_udp.rs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 64f558756..572eda3ba 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -28,13 +28,13 @@ use heapless::Vec; use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; +#[cfg(feature = "udp")] +pub use smoltcp::wire::IpListenEndpoint; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; -#[cfg(feature = "udp")] -pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; use crate::device::DriverAdapter; use crate::time::{instant_from_smoltcp, instant_to_smoltcp}; diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 476aef12f..fe425914b 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -5,7 +5,8 @@ use core::task::Poll; use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; -use smoltcp::socket::udp::{self, PacketMetadata}; +use smoltcp::socket::udp; +pub use smoltcp::socket::udp::PacketMetadata; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::{SocketStack, Stack}; diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 328a0536c..4df23edf6 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -2,8 +2,8 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; -use embassy_net::udp::UdpSocket; -use embassy_net::{Config, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; +use embassy_net::udp::{PacketMetadata, UdpSocket}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; From d07821d8516f39568066c6a0ccf0953f3fad5318 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 May 2023 00:39:57 +0200 Subject: [PATCH 1114/1575] net: document crate. --- embassy-net/Cargo.toml | 3 ++ embassy-net/src/dns.rs | 13 ++++++- embassy-net/src/lib.rs | 49 ++++++++++++++++++++---- embassy-net/src/tcp.rs | 87 +++++++++++++++++++++++++++++++++++++++++- embassy-net/src/udp.rs | 19 +++++++++ 5 files changed, 160 insertions(+), 11 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index fe9514e46..0a47c5d94 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -11,6 +11,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/s features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] + [features] default = [] std = [] diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 97320e44b..3fd235b2c 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,4 +1,9 @@ -//! DNS socket with async support. +//! DNS client compatible with the `embedded-nal-async` traits. +//! +//! This exists only for compatibility with crates that use `embedded-nal-async`. +//! Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're +//! not using `embedded-nal-async`. + use heapless::Vec; pub use smoltcp::socket::dns::{DnsQuery, Socket}; pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; @@ -34,7 +39,11 @@ impl From for Error { } } -/// Async socket for making DNS queries. +/// DNS client compatible with the `embedded-nal-async` traits. +/// +/// This exists only for compatibility with crates that use `embedded-nal-async`. +/// Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're +/// not using `embedded-nal-async`. pub struct DnsSocket<'a, D> where D: Driver + 'static, diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 572eda3ba..9487c0913 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,12 +1,12 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] #![cfg_attr(feature = "nightly", allow(incomplete_features))] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub use embassy_net_driver as driver; - mod device; #[cfg(feature = "dns")] pub mod dns; @@ -20,11 +20,14 @@ use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; +pub use embassy_net_driver as driver; use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; +#[cfg(feature = "igmp")] +pub use smoltcp::iface::MulticastError; use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; @@ -44,6 +47,7 @@ const LOCAL_PORT_MAX: u16 = 65535; #[cfg(feature = "dns")] const MAX_QUERIES: usize = 4; +/// Memory resources needed for a network stack. pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], #[cfg(feature = "dns")] @@ -51,6 +55,7 @@ pub struct StackResources { } impl StackResources { + /// Create a new set of stack resources. pub fn new() -> Self { #[cfg(feature = "dns")] const INIT: Option = None; @@ -62,23 +67,35 @@ impl StackResources { } } +/// Static IP address configuration. #[derive(Debug, Clone, PartialEq, Eq)] pub struct StaticConfig { + /// IP address and subnet mask. pub address: Ipv4Cidr, + /// Default gateway. pub gateway: Option, + /// DNS servers. pub dns_servers: Vec, } +/// DHCP configuration. #[cfg(feature = "dhcpv4")] #[derive(Debug, Clone, PartialEq, Eq)] pub struct DhcpConfig { + /// Maximum lease duration. + /// + /// If not set, the lease duration specified by the server will be used. + /// If set, the lease duration will be capped at this value. pub max_lease_duration: Option, + /// Retry configuration. pub retry_config: RetryConfig, - /// Ignore NAKs. + /// Ignore NAKs from DHCP servers. + /// + /// This is not compliant with the DHCP RFCs, since theoretically we must stop using the assigned IP when receiving a NAK. This can increase reliability on broken networks with buggy routers or rogue DHCP servers, however. pub ignore_naks: bool, - /// Server port config + /// Server port. This is almost always 67. Do not change unless you know what you're doing. pub server_port: u16, - /// Client port config + /// Client port. This is almost always 68. Do not change unless you know what you're doing. pub client_port: u16, } @@ -95,12 +112,18 @@ impl Default for DhcpConfig { } } +/// Network stack configuration. pub enum Config { + /// Use a static IP address configuration. Static(StaticConfig), + /// Use DHCP to obtain an IP address configuration. #[cfg(feature = "dhcpv4")] Dhcp(DhcpConfig), } +/// A network stack. +/// +/// This is the main entry point for the network stack. pub struct Stack { pub(crate) socket: RefCell, inner: RefCell>, @@ -126,6 +149,7 @@ pub(crate) struct SocketStack { } impl Stack { + /// Create a new network stack. pub fn new( mut device: D, config: Config, @@ -203,22 +227,30 @@ impl Stack { f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) } + /// Get the MAC address of the network interface. pub fn ethernet_address(&self) -> [u8; 6] { self.with(|_s, i| i.device.ethernet_address()) } + /// Get whether the link is up. pub fn is_link_up(&self) -> bool { self.with(|_s, i| i.link_up) } + /// Get whether the network stack has a valid IP configuration. + /// This is true if the network stack has a static IP configuration or if DHCP has completed pub fn is_config_up(&self) -> bool { self.with(|_s, i| i.config.is_some()) } + /// Get the current IP configuration. pub fn config(&self) -> Option { self.with(|_s, i| i.config.clone()) } + /// Run the network stack. + /// + /// You must call this in a background task, to process network events. pub async fn run(&self) -> ! { poll_fn(|cx| { self.with_mut(|s, i| i.poll(cx, s)); @@ -301,7 +333,8 @@ impl Stack { #[cfg(feature = "igmp")] impl Stack { - pub fn join_multicast_group(&self, addr: T) -> Result + /// Join a multicast group. + pub fn join_multicast_group(&self, addr: T) -> Result where T: Into, { @@ -313,7 +346,8 @@ impl Stack { }) } - pub fn leave_multicast_group(&self, addr: T) -> Result + /// Leave a multicast group. + pub fn leave_multicast_group(&self, addr: T) -> Result where T: Into, { @@ -325,6 +359,7 @@ impl Stack { }) } + /// Get whether the network stack has joined the given multicast group. pub fn has_multicast_group>(&self, addr: T) -> bool { self.socket.borrow().iface.has_multicast_group(addr) } diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 05b8bb54e..732b6d217 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,3 +1,13 @@ +//! TCP sockets. +//! +//! # Listening +//! +//! `embassy-net` does not have a `TcpListener`. Instead, individual `TcpSocket`s can be put into +//! listening mode by calling [`TcpSocket::accept`]. +//! +//! Incoming connections when no socket is listening are rejected. To accept many incoming +//! connections, create many sockets and put them all into listening mode. + use core::cell::RefCell; use core::future::poll_fn; use core::mem; @@ -13,12 +23,17 @@ use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::time::duration_to_smoltcp; use crate::{SocketStack, Stack}; +/// Error returned by TcpSocket read/write functions. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// The connection was reset. + /// + /// This can happen on receiving a RST packet, or on timeout. ConnectionReset, } +/// Error returned by [`TcpSocket::connect`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ConnectError { @@ -32,6 +47,7 @@ pub enum ConnectError { NoRoute, } +/// Error returned by [`TcpSocket::accept`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AcceptError { @@ -43,35 +59,50 @@ pub enum AcceptError { ConnectionReset, } +/// A TCP socket. pub struct TcpSocket<'a> { io: TcpIo<'a>, } +/// The reader half of a TCP socket. pub struct TcpReader<'a> { io: TcpIo<'a>, } +/// The writer half of a TCP socket. pub struct TcpWriter<'a> { io: TcpIo<'a>, } impl<'a> TcpReader<'a> { + /// Read data from the socket. + /// + /// Returns how many bytes were read, or an error. If no data is available, it waits + /// until there is at least one byte available. pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } } impl<'a> TcpWriter<'a> { + /// Write data to the socket. + /// + /// Returns how many bytes were written, or an error. If the socket is not ready to + /// accept data, it waits until it is. pub async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } + /// Flushes the written data to the socket. + /// + /// This waits until all data has been sent, and ACKed by the remote host. pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } } impl<'a> TcpSocket<'a> { + /// Create a new TCP socket on the given stack, with the given buffers. pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { let s = &mut *stack.socket.borrow_mut(); let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; @@ -89,10 +120,12 @@ impl<'a> TcpSocket<'a> { } } + /// Split the socket into reader and a writer halves. pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { (TcpReader { io: self.io }, TcpWriter { io: self.io }) } + /// Connect to a remote host. pub async fn connect(&mut self, remote_endpoint: T) -> Result<(), ConnectError> where T: Into, @@ -122,6 +155,9 @@ impl<'a> TcpSocket<'a> { .await } + /// Accept a connection from a remote host. + /// + /// This function puts the socket in listening mode, and waits until a connection is received. pub async fn accept(&mut self, local_endpoint: T) -> Result<(), AcceptError> where T: Into, @@ -144,56 +180,98 @@ impl<'a> TcpSocket<'a> { .await } + /// Read data from the socket. + /// + /// Returns how many bytes were read, or an error. If no data is available, it waits + /// until there is at least one byte available. pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } + /// Write data to the socket. + /// + /// Returns how many bytes were written, or an error. If the socket is not ready to + /// accept data, it waits until it is. pub async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } + /// Flushes the written data to the socket. + /// + /// This waits until all data has been sent, and ACKed by the remote host. pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } + /// Set the timeout for the socket. + /// + /// If the timeout is set, the socket will be closed if no data is received for the + /// specified duration. pub fn set_timeout(&mut self, duration: Option) { self.io .with_mut(|s, _| s.set_timeout(duration.map(duration_to_smoltcp))) } + /// Set the keep-alive interval for the socket. + /// + /// If the keep-alive interval is set, the socket will send keep-alive packets after + /// the specified duration of inactivity. + /// + /// If not set, the socket will not send keep-alive packets. pub fn set_keep_alive(&mut self, interval: Option) { self.io .with_mut(|s, _| s.set_keep_alive(interval.map(duration_to_smoltcp))) } + /// Set the hop limit field in the IP header of sent packets. pub fn set_hop_limit(&mut self, hop_limit: Option) { self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) } + /// Get the local endpoint of the socket. + /// + /// Returns `None` if the socket is not bound (listening) or not connected. pub fn local_endpoint(&self) -> Option { self.io.with(|s, _| s.local_endpoint()) } + /// Get the remote endpoint of the socket. + /// + /// Returns `None` if the socket is not connected. pub fn remote_endpoint(&self) -> Option { self.io.with(|s, _| s.remote_endpoint()) } + /// Get the state of the socket. pub fn state(&self) -> State { self.io.with(|s, _| s.state()) } + /// Close the write half of the socket. + /// + /// This closes only the write half of the socket. The read half side remains open, the + /// socket can still receive data. + /// + /// Data that has been written to the socket and not yet sent (or not yet ACKed) will still + /// still sent. The last segment of the pending to send data is sent with the FIN flag set. pub fn close(&mut self) { self.io.with_mut(|s, _| s.close()) } + /// Forcibly close the socket. + /// + /// This instantly closes both the read and write halves of the socket. Any pending data + /// that has not been sent will be lost. pub fn abort(&mut self) { self.io.with_mut(|s, _| s.abort()) } + /// Get whether the socket is ready to send data, i.e. whether there is space in the send buffer. pub fn may_send(&self) -> bool { self.io.with(|s, _| s.may_send()) } + /// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer. pub fn may_recv(&self) -> bool { self.io.with(|s, _| s.may_recv()) } @@ -345,6 +423,7 @@ mod embedded_io_impls { } } +/// TCP client compatible with `embedded-nal-async` traits. #[cfg(all(feature = "unstable-traits", feature = "nightly"))] pub mod client { use core::cell::UnsafeCell; @@ -356,14 +435,16 @@ pub mod client { use super::*; - /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ. + /// TCP client connection pool compatible with `embedded-nal-async` traits. + /// + /// The pool is capable of managing up to N concurrent connections with tx and rx buffers according to TX_SZ and RX_SZ. pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { stack: &'d Stack, state: &'d TcpClientState, } impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { - /// Create a new TcpClient + /// Create a new `TcpClient`. pub fn new(stack: &'d Stack, state: &'d TcpClientState) -> Self { Self { stack, state } } @@ -400,6 +481,7 @@ pub mod client { } } + /// Opened TCP connection in a [`TcpClient`]. pub struct TcpConnection<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> { socket: TcpSocket<'d>, state: &'d TcpClientState, @@ -458,6 +540,7 @@ pub mod client { } impl TcpClientState { + /// Create a new `TcpClientState`. pub const fn new() -> Self { Self { pool: Pool::new() } } diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index fe425914b..c9843cfe8 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,3 +1,5 @@ +//! UDP sockets. + use core::cell::RefCell; use core::future::poll_fn; use core::mem; @@ -11,6 +13,7 @@ use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::{SocketStack, Stack}; +/// Error returned by [`UdpSocket::bind`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BindError { @@ -20,6 +23,7 @@ pub enum BindError { NoRoute, } +/// Error returned by [`UdpSocket::recv_from`] and [`UdpSocket::send_to`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -27,12 +31,14 @@ pub enum Error { NoRoute, } +/// An UDP socket. pub struct UdpSocket<'a> { stack: &'a RefCell, handle: SocketHandle, } impl<'a> UdpSocket<'a> { + /// Create a new UDP socket using the provided stack and buffers. pub fn new( stack: &'a Stack, rx_meta: &'a mut [PacketMetadata], @@ -57,6 +63,7 @@ impl<'a> UdpSocket<'a> { } } + /// Bind the socket to a local endpoint. pub fn bind(&mut self, endpoint: T) -> Result<(), BindError> where T: Into, @@ -89,6 +96,11 @@ impl<'a> UdpSocket<'a> { res } + /// Receive a datagram. + /// + /// This method will wait until a datagram is received. + /// + /// Returns the number of bytes received and the remote endpoint. pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { poll_fn(move |cx| { self.with_mut(|s, _| match s.recv_slice(buf) { @@ -103,6 +115,7 @@ impl<'a> UdpSocket<'a> { .await } + /// Send a datagram to the specified remote endpoint. pub async fn send_to(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> where T: Into, @@ -122,22 +135,28 @@ impl<'a> UdpSocket<'a> { .await } + /// Returns the local endpoint of the socket. pub fn endpoint(&self) -> IpListenEndpoint { self.with(|s, _| s.endpoint()) } + /// Returns whether the socket is open. + pub fn is_open(&self) -> bool { self.with(|s, _| s.is_open()) } + /// Close the socket. pub fn close(&mut self) { self.with_mut(|s, _| s.close()) } + /// Returns whether the socket is ready to send data, i.e. it has enough buffer space to hold a packet. pub fn may_send(&self) -> bool { self.with(|s, _| s.can_send()) } + /// Returns whether the socket is ready to receive data, i.e. it has received a packet that's now in the buffer. pub fn may_recv(&self) -> bool { self.with(|s, _| s.can_recv()) } From 288309b9d51830aada3d09311ff6da95faab78e1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 May 2023 00:52:13 +0200 Subject: [PATCH 1115/1575] net-driver: document crate. --- embassy-net-driver/README.md | 12 ++++++++++ embassy-net-driver/src/lib.rs | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 embassy-net-driver/README.md diff --git a/embassy-net-driver/README.md b/embassy-net-driver/README.md new file mode 100644 index 000000000..84f25492d --- /dev/null +++ b/embassy-net-driver/README.md @@ -0,0 +1,12 @@ +# embassy-net-driver + + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index a39cfecc1..4149bf4a4 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -1,20 +1,57 @@ #![no_std] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] use core::task::Context; +/// Main `embassy-net` driver API. +/// +/// This is essentially an interface for sending and receiving raw network frames. +/// +/// The interface is based on _tokens_, which are types that allow to receive/transmit a +/// single packet. The `receive` and `transmit` functions only construct such tokens, the +/// real sending/receiving operation are performed when the tokens are consumed. pub trait Driver { + /// A token to receive a single network packet. type RxToken<'a>: RxToken where Self: 'a; + + /// A token to transmit a single network packet. type TxToken<'a>: TxToken where Self: 'a; + /// Construct a token pair consisting of one receive token and one transmit token. + /// + /// If there is a packet ready to be received, this function must return `Some`. + /// If there isn't, it must return `None`, and wake `cx.waker()` when a packet is ready. + /// + /// The additional transmit token makes it possible to generate a reply packet based + /// on the contents of the received packet. For example, this makes it possible to + /// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes + /// need to be sent back, without heap allocation. fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + + /// Construct a transmit token. + /// + /// If there is free space in the transmit buffer to transmit a packet, this function must return `Some`. + /// If there isn't, it must return `None`, and wake `cx.waker()` when space becomes available. + /// + /// Note that [`TxToken::consume`] is infallible, so it is not allowed to return a token + /// if there is no free space and fail later. fn transmit(&mut self, cx: &mut Context) -> Option>; + + /// Get the link state. + /// + /// This function must return the current link state of the device, and wake `cx.waker()` when + /// the link state changes. fn link_state(&mut self, cx: &mut Context) -> LinkState; + /// Get a description of device capabilities. fn capabilities(&self) -> Capabilities; + + /// Get the device's Ethernet address. fn ethernet_address(&self) -> [u8; 6]; } @@ -140,10 +177,15 @@ impl Default for Medium { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct ChecksumCapabilities { + /// Checksum behavior for IPv4. pub ipv4: Checksum, + /// Checksum behavior for UDP. pub udp: Checksum, + /// Checksum behavior for TCP. pub tcp: Checksum, + /// Checksum behavior for ICMPv4. pub icmpv4: Checksum, + /// Checksum behavior for ICMPv6. pub icmpv6: Checksum, } @@ -167,9 +209,12 @@ impl Default for Checksum { } } +/// The link state of a network device. #[derive(PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum LinkState { + /// The link is down. Down, + /// The link is up. Up, } From d97724cca3164250118c732759f403f4f94d4629 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 15 May 2023 10:25:02 +0100 Subject: [PATCH 1116/1575] tl_mbox read and write --- embassy-stm32/src/ipcc.rs | 15 ++ embassy-stm32/src/tl_mbox/ble.rs | 48 ++++++- embassy-stm32/src/tl_mbox/channels.rs | 8 +- embassy-stm32/src/tl_mbox/consts.rs | 53 +++++++ embassy-stm32/src/tl_mbox/evt.rs | 163 ++++++++++++++++++++++ embassy-stm32/src/tl_mbox/mm.rs | 47 ++++++- embassy-stm32/src/tl_mbox/mod.rs | 77 ++++++++-- embassy-stm32/src/tl_mbox/shci.rs | 101 ++++++++++++++ embassy-stm32/src/tl_mbox/sys.rs | 69 ++++++++- examples/stm32wb/src/bin/tl_mbox.rs | 6 +- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 96 +++++++++++++ 11 files changed, 661 insertions(+), 22 deletions(-) create mode 100644 embassy-stm32/src/tl_mbox/consts.rs create mode 100644 embassy-stm32/src/tl_mbox/shci.rs create mode 100644 examples/stm32wb/src/bin/tl_mbox_tx_rx.rs diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 903aeca30..9af5f171f 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -158,6 +158,10 @@ impl<'d> Ipcc<'d> { pub fn is_rx_pending(&self, channel: IpccChannel) -> bool { self.c2_is_active_flag(channel) && self.c1_get_rx_channel(channel) } + + pub fn as_mut_ptr(&self) -> *mut Self { + unsafe { &mut core::ptr::read(self) as *mut _ } + } } impl sealed::Instance for crate::peripherals::IPCC { @@ -176,3 +180,14 @@ unsafe fn _configure_pwr() { // set RF wake-up clock = LSE rcc.csr().modify(|w| w.set_rfwkpsel(0b01)); } + +// TODO: if anyone has a better idea, please let me know +/// extension trait that constrains the [`Ipcc`] peripheral +pub trait IpccExt<'d> { + fn constrain(self) -> Ipcc<'d>; +} +impl<'d> IpccExt<'d> for IPCC { + fn constrain(self) -> Ipcc<'d> { + Ipcc { _peri: self.into_ref() } + } +} diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index a2c0758d1..a285e314c 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -1,13 +1,22 @@ use core::mem::MaybeUninit; -use super::unsafe_linked_list::LST_init_head; -use super::{channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; +use embassy_futures::block_on; + +use super::cmd::CmdSerial; +use super::consts::TlPacketType; +use super::evt::EvtBox; +use super::unsafe_linked_list::{LST_init_head, LST_is_empty, LST_remove_head}; +use super::{ + channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_CHANNEL, + TL_REF_TABLE, +}; use crate::ipcc::Ipcc; +use crate::tl_mbox::cmd::CmdPacket; pub struct Ble; impl Ble { - pub fn new(ipcc: &mut Ipcc) -> Self { + pub(crate) fn new(ipcc: &mut Ipcc) -> Self { unsafe { LST_init_head(EVT_QUEUE.as_mut_ptr()); @@ -23,4 +32,37 @@ impl Ble { Ble } + + pub(crate) fn evt_handler(ipcc: &mut Ipcc) { + unsafe { + let mut node_ptr = core::ptr::null_mut(); + let node_ptr_ptr: *mut _ = &mut node_ptr; + + while !LST_is_empty(EVT_QUEUE.as_mut_ptr()) { + LST_remove_head(EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + + let event = node_ptr.cast(); + let event = EvtBox::new(event); + + block_on(TL_CHANNEL.send(event)); + } + } + + ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); + } + + pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { + unsafe { + let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; + let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial; + let pcmd_serial_buf: *mut u8 = pcmd_serial.cast(); + + core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); + + let mut cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; + cmd_packet.cmd_serial.ty = TlPacketType::BleCmd as u8; + } + + ipcc.c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); + } } diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs index 1dde5d61c..aaa6ce177 100644 --- a/embassy-stm32/src/tl_mbox/channels.rs +++ b/embassy-stm32/src/tl_mbox/channels.rs @@ -52,9 +52,9 @@ pub mod cpu1 { use crate::ipcc::IpccChannel; - #[allow(dead_code)] // Not used currently but reserved + // Not used currently but reserved pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; - #[allow(dead_code)] // Not used currently but reserved + // Not used currently but reserved pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; @@ -62,7 +62,7 @@ pub mod cpu1 { pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved + // Not used currently but reserved pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; @@ -88,7 +88,7 @@ pub mod cpu2 { #[allow(dead_code)] // Not used currently but reserved pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; + pub const IPCC_BLE_LLD_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4; #[allow(dead_code)] // Not used currently but reserved diff --git a/embassy-stm32/src/tl_mbox/consts.rs b/embassy-stm32/src/tl_mbox/consts.rs new file mode 100644 index 000000000..e16a26cd0 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/consts.rs @@ -0,0 +1,53 @@ +#[derive(PartialEq)] +#[repr(C)] +pub enum TlPacketType { + BleCmd = 0x01, + AclData = 0x02, + BleEvt = 0x04, + + OtCmd = 0x08, + OtRsp = 0x09, + CliCmd = 0x0A, + OtNot = 0x0C, + OtAck = 0x0D, + CliNot = 0x0E, + CliAck = 0x0F, + + SysCmd = 0x10, + SysRsp = 0x11, + SysEvt = 0x12, + + LocCmd = 0x20, + LocRsp = 0x21, + + TracesApp = 0x40, + TracesWl = 0x41, +} + +impl TryFrom for TlPacketType { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + 0x01 => Ok(TlPacketType::BleCmd), + 0x02 => Ok(TlPacketType::AclData), + 0x04 => Ok(TlPacketType::BleEvt), + 0x08 => Ok(TlPacketType::OtCmd), + 0x09 => Ok(TlPacketType::OtRsp), + 0x0A => Ok(TlPacketType::CliCmd), + 0x0C => Ok(TlPacketType::OtNot), + 0x0D => Ok(TlPacketType::OtAck), + 0x0E => Ok(TlPacketType::CliNot), + 0x0F => Ok(TlPacketType::CliAck), + 0x10 => Ok(TlPacketType::SysCmd), + 0x11 => Ok(TlPacketType::SysRsp), + 0x12 => Ok(TlPacketType::SysEvt), + 0x20 => Ok(TlPacketType::LocCmd), + 0x21 => Ok(TlPacketType::LocRsp), + 0x40 => Ok(TlPacketType::TracesApp), + 0x41 => Ok(TlPacketType::TracesWl), + + _ => Err(()), + } + } +} diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs index 4244db810..8ae2a2559 100644 --- a/embassy-stm32/src/tl_mbox/evt.rs +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -1,3 +1,10 @@ +use core::mem::MaybeUninit; + +use super::cmd::{AclDataPacket, AclDataSerial}; +use super::consts::TlPacketType; +use super::{PacketHeader, TL_EVT_HEADER_SIZE}; +use crate::tl_mbox::mm; + /// the payload of [`Evt`] for a command status event #[derive(Copy, Clone)] #[repr(C, packed)] @@ -6,3 +13,159 @@ pub struct CsEvt { pub num_cmd: u8, pub cmd_code: u16, } + +/// the payload of [`Evt`] for a command complete event +#[derive(Clone, Copy, Default)] +#[repr(C, packed)] +pub struct CcEvt { + pub num_cmd: u8, + pub cmd_code: u8, + pub payload: [u8; 1], +} + +#[derive(Clone, Copy, Default)] +#[repr(C, packed)] +pub struct Evt { + pub evt_code: u8, + pub payload_len: u8, + pub payload: [u8; 1], +} + +#[derive(Clone, Copy, Default)] +#[repr(C, packed)] +pub struct EvtSerial { + pub kind: u8, + pub evt: Evt, +} + +/// This format shall be used for all events (asynchronous and command response) reported +/// by the CPU2 except for the command response of a system command where the header is not there +/// and the format to be used shall be `EvtSerial`. +/// +/// ### Note: +/// Be careful that the asynchronous events reported by the CPU2 on the system channel do +/// include the header and shall use `EvtPacket` format. Only the command response format on the +/// system channel is different. +#[derive(Clone, Copy, Default)] +#[repr(C, packed)] +pub struct EvtPacket { + pub header: PacketHeader, + pub evt_serial: EvtSerial, +} + +/// Smart pointer to the [`EvtPacket`] that will dispose of it automatically on drop +pub struct EvtBox { + ptr: *mut EvtPacket, +} + +unsafe impl Send for EvtBox {} +impl EvtBox { + pub(super) fn new(ptr: *mut EvtPacket) -> Self { + Self { ptr } + } + + /// Copies the event data from inner pointer and returns and event structure + pub fn evt(&self) -> EvtPacket { + let mut evt = MaybeUninit::uninit(); + unsafe { + self.ptr.copy_to(evt.as_mut_ptr(), 1); + evt.assume_init() + } + } + + /// Returns the size of a buffer required to hold this event + pub fn size(&self) -> Result { + unsafe { + let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + + if evt_kind == TlPacketType::AclData { + let acl_data: *const AclDataPacket = self.ptr.cast(); + let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; + + Ok((*acl_serial).length as usize + 5) + } else { + let evt_data: *const EvtPacket = self.ptr.cast(); + let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; + + Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE) + } + } + } + + /// writes an underlying [`EvtPacket`] into the provided buffer. Returns the number of bytes that were + /// written. Returns an error if event kind is unkown or if provided buffer size is not enough + pub fn copy_into_slice(&self, buf: &mut [u8]) -> Result { + // TODO: double check this + // unsafe { + // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + + // if let TlPacketType::AclData = evt_kind { + // let acl_data: *const AclDataPacket = self.ptr.cast(); + // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; + // let acl_serial_buf: *const u8 = acl_serial.cast(); + + // let len = (*acl_serial).length as usize + 5; + // if len > buf.len() { + // return Err(()); + // } + + // core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); + + // Ok(len) + // } else { + // let evt_data: *const EvtPacket = self.ptr.cast(); + // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; + // let evt_serial_buf: *const u8 = evt_serial.cast(); + + // let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; + // if len > buf.len() { + // return Err(()); + // } + + // core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len); + + // Ok(len) + // } + // } + + unsafe { + let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + + let evt_data: *const EvtPacket = self.ptr.cast(); + let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; + let evt_serial_buf: *const u8 = evt_serial.cast(); + + let acl_data: *const AclDataPacket = self.ptr.cast(); + let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; + let acl_serial_buf: *const u8 = acl_serial.cast(); + + if let TlPacketType::AclData = evt_kind { + let len = (*acl_serial).length as usize + 5; + if len > buf.len() { + return Err(()); + } + + core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len); + + Ok(len) + } else { + let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; + if len > buf.len() { + return Err(()); + } + + core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); + + Ok(len) + } + } + } +} + +impl Drop for EvtBox { + fn drop(&mut self) { + use crate::ipcc::IpccExt; + let mut ipcc = unsafe { crate::Peripherals::steal() }.IPCC.constrain(); + mm::MemoryManager::evt_drop(self.ptr, &mut ipcc); + } +} diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs index cf4797305..588b32919 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -1,10 +1,12 @@ use core::mem::MaybeUninit; -use super::unsafe_linked_list::LST_init_head; +use super::evt::EvtPacket; +use super::unsafe_linked_list::{LST_init_head, LST_insert_tail, LST_is_empty, LST_remove_head}; use super::{ - MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, - TL_MEM_MANAGER_TABLE, + channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, + SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, TL_REF_TABLE, }; +use crate::ipcc::Ipcc; pub struct MemoryManager; @@ -27,4 +29,43 @@ impl MemoryManager { MemoryManager } + + pub fn evt_handler(ipcc: &mut Ipcc) { + ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); + Self::send_free_buf(); + ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + } + + pub fn evt_drop(evt: *mut EvtPacket, ipcc: &mut Ipcc) { + unsafe { + let list_node = evt.cast(); + + LST_insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); + } + + let channel_is_busy = ipcc.c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + + // postpone event buffer freeing to IPCC interrupt handler + if channel_is_busy { + ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); + } else { + Self::send_free_buf(); + ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + } + } + + fn send_free_buf() { + unsafe { + let mut node_ptr = core::ptr::null_mut(); + let node_ptr_ptr: *mut _ = &mut node_ptr; + + while !LST_is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { + LST_remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), node_ptr_ptr); + LST_insert_tail( + (*(*TL_REF_TABLE.as_ptr()).mem_manager_table).pevt_free_buffer_queue, + node_ptr, + ); + } + } + } } diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index 0cee26b74..3651b8ea5 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -1,20 +1,27 @@ use core::mem::MaybeUninit; use bit_field::BitField; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; use self::ble::Ble; use self::cmd::{AclDataPacket, CmdPacket}; -use self::evt::CsEvt; +use self::evt::{CsEvt, EvtBox}; use self::mm::MemoryManager; +use self::shci::{shci_ble_init, ShciBleInitCmdParam}; use self::sys::Sys; use self::unsafe_linked_list::LinkedListNode; +use crate::_generated::interrupt::{IPCC_C1_RX, IPCC_C1_TX}; use crate::ipcc::Ipcc; mod ble; mod channels; mod cmd; +mod consts; mod evt; mod mm; +mod shci; mod sys; mod unsafe_linked_list; @@ -236,18 +243,14 @@ static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); // not in shared RAM static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); -#[allow(dead_code)] // Not used currently but reserved -#[link_section = "MB_MEM1"] -static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); - #[link_section = "MB_MEM2"] static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = MaybeUninit::uninit(); -#[link_section = "MB_MEM1"] +#[link_section = "MB_MEM2"] static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "MB_MEM1"] +#[link_section = "MB_MEM2"] static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] @@ -271,6 +274,9 @@ static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); // "magic" numbers from ST ---v---v static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); +// TODO: get a better size, this is a placeholder +pub(crate) static TL_CHANNEL: Channel = Channel::new(); + pub struct TlMbox { _sys: Sys, _ble: Ble, @@ -279,7 +285,7 @@ pub struct TlMbox { impl TlMbox { /// initializes low-level transport between CPU1 and BLE stack on CPU2 - pub fn init(ipcc: &mut Ipcc) -> TlMbox { + pub fn init(ipcc: &mut Ipcc, rx_irq: IPCC_C1_RX, tx_irq: IPCC_C1_TX) -> TlMbox { unsafe { TL_REF_TABLE = MaybeUninit::new(RefTable { device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), @@ -320,6 +326,24 @@ impl TlMbox { let _ble = Ble::new(ipcc); let _mm = MemoryManager::new(); + rx_irq.disable(); + tx_irq.disable(); + + rx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); + tx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); + + rx_irq.set_handler(|ipcc| { + let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; + Self::interrupt_ipcc_rx_handler(ipcc); + }); + tx_irq.set_handler(|ipcc| { + let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; + Self::interrupt_ipcc_tx_handler(ipcc); + }); + + rx_irq.enable(); + tx_irq.enable(); + TlMbox { _sys, _ble, _mm } } @@ -333,4 +357,41 @@ impl TlMbox { None } } + + pub fn shci_ble_init(&self, ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { + shci_ble_init(ipcc, param); + } + + pub fn send_ble_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { + ble::Ble::send_cmd(ipcc, buf); + } + + // pub fn send_sys_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { + // sys::Sys::send_cmd(ipcc, buf); + // } + + pub async fn read(&self) -> EvtBox { + TL_CHANNEL.recv().await + } + + fn interrupt_ipcc_rx_handler(ipcc: &mut Ipcc) { + if ipcc.is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { + sys::Sys::evt_handler(ipcc); + } else if ipcc.is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { + ble::Ble::evt_handler(ipcc); + } else { + todo!() + } + } + + fn interrupt_ipcc_tx_handler(ipcc: &mut Ipcc) { + if ipcc.is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { + // TODO: handle this case + let _ = sys::Sys::cmd_evt_handler(ipcc); + } else if ipcc.is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) { + mm::MemoryManager::evt_handler(ipcc); + } else { + todo!() + } + } } diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32/src/tl_mbox/shci.rs new file mode 100644 index 000000000..7f224cb1a --- /dev/null +++ b/embassy-stm32/src/tl_mbox/shci.rs @@ -0,0 +1,101 @@ +//! HCI commands for system channel + +use super::cmd::CmdPacket; +use super::consts::TlPacketType; +use super::{channels, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE}; +use crate::ipcc::Ipcc; + +const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; +pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; +#[allow(dead_code)] +const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; + +#[derive(Clone, Copy)] +#[repr(C, packed)] +pub struct ShciBleInitCmdParam { + /// NOT USED CURRENTLY + pub p_ble_buffer_address: u32, + + /// Size of the Buffer allocated in pBleBufferAddress + pub ble_buffer_size: u32, + + pub num_attr_record: u16, + pub num_attr_serv: u16, + pub attr_value_arr_size: u16, + pub num_of_links: u8, + pub extended_packet_length_enable: u8, + pub pr_write_list_size: u8, + pub mb_lock_count: u8, + + pub att_mtu: u16, + pub slave_sca: u16, + pub master_sca: u8, + pub ls_source: u8, + pub max_conn_event_length: u32, + pub hs_startup_time: u16, + pub viterbi_enable: u8, + pub ll_only: u8, + pub hw_version: u8, +} + +impl Default for ShciBleInitCmdParam { + fn default() -> Self { + Self { + p_ble_buffer_address: 0, + ble_buffer_size: 0, + num_attr_record: 68, + num_attr_serv: 8, + attr_value_arr_size: 1344, + num_of_links: 2, + extended_packet_length_enable: 1, + pr_write_list_size: 0x3A, + mb_lock_count: 0x79, + att_mtu: 156, + slave_sca: 500, + master_sca: 0, + ls_source: 1, + max_conn_event_length: 0xFFFFFFFF, + hs_startup_time: 0x148, + viterbi_enable: 1, + ll_only: 0, + hw_version: 0, + } + } +} + +#[derive(Clone, Copy, Default)] +#[repr(C, packed)] +pub struct ShciHeader { + metadata: [u32; 3], +} + +#[derive(Clone, Copy)] +#[repr(C, packed)] +pub struct ShciBleInitCmdPacket { + header: ShciHeader, + param: ShciBleInitCmdParam, +} + +pub fn shci_ble_init(ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { + let mut packet = ShciBleInitCmdPacket { + header: ShciHeader::default(), + param, + }; + + let packet_ptr: *mut ShciBleInitCmdPacket = &mut packet; + + unsafe { + let cmd_ptr: *mut CmdPacket = packet_ptr.cast(); + + (*cmd_ptr).cmd_serial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT; + (*cmd_ptr).cmd_serial.cmd.payload_len = core::mem::size_of::() as u8; + + let mut cmd_buf = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; + core::ptr::write(cmd_buf, *cmd_ptr); + + cmd_buf.cmd_serial.ty = TlPacketType::SysCmd as u8; + + ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); + ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + } +} diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index 13ae7f9f9..122657550 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -1,13 +1,18 @@ use core::mem::MaybeUninit; -use super::unsafe_linked_list::LST_init_head; -use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; +use embassy_futures::block_on; + +use super::cmd::{CmdPacket, CmdSerial}; +use super::consts::TlPacketType; +use super::evt::{CcEvt, EvtBox, EvtSerial}; +use super::unsafe_linked_list::{LST_init_head, LST_is_empty, LST_remove_head}; +use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_CHANNEL, TL_REF_TABLE, TL_SYS_TABLE}; use crate::ipcc::Ipcc; pub struct Sys; impl Sys { - pub fn new(ipcc: &mut Ipcc) -> Self { + pub(crate) fn new(ipcc: &mut Ipcc) -> Self { unsafe { LST_init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); @@ -21,4 +26,62 @@ impl Sys { Sys } + + pub(crate) fn evt_handler(ipcc: &mut Ipcc) { + unsafe { + let mut node_ptr = core::ptr::null_mut(); + let node_ptr_ptr: *mut _ = &mut node_ptr; + + while !LST_is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { + LST_remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + + let event = node_ptr.cast(); + let event = EvtBox::new(event); + + // TODO: not really happy about this + block_on(TL_CHANNEL.send(event)); + } + } + + ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); + } + + pub(crate) fn cmd_evt_handler(ipcc: &mut Ipcc) -> CcEvt { + ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); + + // ST's command response data structure is really convoluted. + // + // for command response events on SYS channel, the header is missing + // and one should: + // 1. interpret the content of CMD_BUFFER as CmdPacket + // 2. Access CmdPacket's cmdserial field and interpret its content as EvtSerial + // 3. Access EvtSerial's evt field (as Evt) and interpret its payload as CcEvt + // 4. CcEvt type is the actual SHCI response + // 5. profit + unsafe { + let cmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer; + let cmd_serial: *const CmdSerial = &(*cmd).cmd_serial; + let evt_serial: *const EvtSerial = cmd_serial.cast(); + let cc = (*evt_serial).evt.payload.as_ptr().cast(); + *cc + } + } + + #[allow(dead_code)] + pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { + unsafe { + // TODO: check this + let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; + let cmd_serial: *mut CmdSerial = &mut (*cmd_buffer).cmd_serial; + let cmd_serial_buf = cmd_serial.cast(); + + core::ptr::copy(buf.as_ptr(), cmd_serial_buf, buf.len()); + + let mut cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; + cmd_packet.cmd_serial.ty = TlPacketType::SysCmd as u8; + + ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); + ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + } + } } diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 6876526ae..ccd01cbc7 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc}; use embassy_stm32::tl_mbox::TlMbox; use embassy_time::{Duration, Timer}; @@ -40,7 +41,10 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mut ipcc = Ipcc::new(p.IPCC, config); - let mbox = TlMbox::init(&mut ipcc); + let rx_irq = interrupt::take!(IPCC_C1_RX); + let tx_irq = interrupt::take!(IPCC_C1_TX); + + let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq); loop { let wireless_fw_info = mbox.wireless_fw_info(); diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs new file mode 100644 index 000000000..315172df8 --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -0,0 +1,96 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::interrupt; +use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::tl_mbox::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mut ipcc = Ipcc::new(p.IPCC, config); + + let rx_irq = interrupt::take!(IPCC_C1_RX); + let tx_irq = interrupt::take!(IPCC_C1_TX); + + let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq); + + // initialize ble stack, does not return a response + // mbox.shci_ble_init(&mut ipcc, Default::default()); + + info!("waiting for coprocessor to boot"); + let event_box = mbox.read().await; + + let mut payload = [0u8; 6]; + event_box.copy_into_slice(&mut payload).unwrap(); + + let event_packet = event_box.evt(); + let kind = event_packet.evt_serial.kind; + + // means recieved SYS event, which indicates in this case that the coprocessor is ready + if kind == 0x12 { + let code = event_packet.evt_serial.evt.evt_code; + let payload_len = event_packet.evt_serial.evt.payload_len; + + info!( + "==> kind: {:#04x}, code: {:#04x}, payload_length: {}, payload: {:#04x}", + kind, + code, + payload_len, + payload[3..] + ); + } + + mbox.shci_ble_init(&mut ipcc, Default::default()); + + info!("resetting BLE"); + mbox.send_ble_cmd(&mut ipcc, &[0x01, 0x03, 0x0c]); + + let event_box = mbox.read().await; + + let mut payload = [0u8; 7]; + event_box.copy_into_slice(&mut payload).unwrap(); + + let event_packet = event_box.evt(); + let kind = event_packet.evt_serial.kind; + + let code = event_packet.evt_serial.evt.evt_code; + let payload_len = event_packet.evt_serial.evt.payload_len; + + info!( + "==> kind: {:#04x}, code: {:#04x}, payload_length: {}, payload: {:#04x}", + kind, code, payload_len, payload + ); + + loop {} +} From a8953b5c669da3403437e012a3d106b100e2a1f4 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 15 May 2023 10:34:52 +0100 Subject: [PATCH 1117/1575] cleanup --- embassy-stm32/src/tl_mbox/evt.rs | 33 -------------------------------- 1 file changed, 33 deletions(-) diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs index 8ae2a2559..0ecd4dab6 100644 --- a/embassy-stm32/src/tl_mbox/evt.rs +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -95,39 +95,6 @@ impl EvtBox { /// writes an underlying [`EvtPacket`] into the provided buffer. Returns the number of bytes that were /// written. Returns an error if event kind is unkown or if provided buffer size is not enough pub fn copy_into_slice(&self, buf: &mut [u8]) -> Result { - // TODO: double check this - // unsafe { - // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; - - // if let TlPacketType::AclData = evt_kind { - // let acl_data: *const AclDataPacket = self.ptr.cast(); - // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; - // let acl_serial_buf: *const u8 = acl_serial.cast(); - - // let len = (*acl_serial).length as usize + 5; - // if len > buf.len() { - // return Err(()); - // } - - // core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); - - // Ok(len) - // } else { - // let evt_data: *const EvtPacket = self.ptr.cast(); - // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; - // let evt_serial_buf: *const u8 = evt_serial.cast(); - - // let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; - // if len > buf.len() { - // return Err(()); - // } - - // core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len); - - // Ok(len) - // } - // } - unsafe { let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; From 14a5d03af2a74eccaa9930bdf81eef43791a4b33 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 15 May 2023 15:21:05 +0200 Subject: [PATCH 1118/1575] rp: remove take!, add bind_interrupts! --- embassy-rp/src/adc.rs | 31 +-- embassy-rp/src/i2c.rs | 34 ++-- embassy-rp/src/interrupt.rs | 37 +++- embassy-rp/src/uart/buffered.rs | 213 +++++++++++---------- embassy-rp/src/uart/mod.rs | 101 ++++++---- embassy-rp/src/usb.rs | 98 +++++----- examples/rp/src/bin/adc.rs | 11 +- examples/rp/src/bin/i2c_async.rs | 12 +- examples/rp/src/bin/uart_buffered_split.rs | 11 +- examples/rp/src/bin/uart_unidir.rs | 16 +- examples/rp/src/bin/usb_ethernet.rs | 12 +- examples/rp/src/bin/usb_logger.rs | 11 +- examples/rp/src/bin/usb_serial.rs | 12 +- tests/rp/src/bin/uart_buffered.rs | 20 +- tests/rp/src/bin/uart_dma.rs | 20 +- tests/rp/src/bin/uart_upgrade.rs | 12 +- 16 files changed, 374 insertions(+), 277 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 145ba9c59..59c7a47ce 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -3,12 +3,12 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::into_ref; +use embassy_cortex_m::interrupt::{Binding, Interrupt}; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal_02::adc::{Channel, OneShot}; use crate::gpio::Pin; -use crate::interrupt::{self, InterruptExt}; +use crate::interrupt::{self, InterruptExt, ADC_IRQ_FIFO}; use crate::peripherals::ADC; use crate::{pac, peripherals, Peripheral}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -47,10 +47,9 @@ impl<'d> Adc<'d> { pub fn new( _inner: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding, _config: Config, ) -> Self { - into_ref!(irq); unsafe { let reset = Self::reset(); crate::reset::reset(reset); @@ -63,14 +62,10 @@ impl<'d> Adc<'d> { } // 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(); + unsafe { + ADC_IRQ_FIFO::steal().unpend(); + ADC_IRQ_FIFO::steal().enable(); + }; Self { phantom: PhantomData } } @@ -165,6 +160,18 @@ macro_rules! impl_pin { }; } +pub struct InterruptHandler { + _empty: (), +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = Adc::regs(); + r.inte().write(|w| w.set_fifo(false)); + WAKER.wake(); + } +} + impl_pin!(PIN_26, 0); impl_pin!(PIN_27, 1); impl_pin!(PIN_28, 2); diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index d5dc94406..6ce77f073 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -2,7 +2,7 @@ use core::future; use core::marker::PhantomData; use core::task::Poll; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; @@ -75,23 +75,21 @@ impl<'d, T: Instance> I2c<'d, T, Async> { peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, config: Config, ) -> Self { - into_ref!(scl, sda, irq); + into_ref!(scl, sda); let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config); - irq.set_handler(Self::on_interrupt); unsafe { let i2c = T::regs(); // mask everything initially i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); } - irq.unpend(); - debug_assert!(!irq.is_pending()); - irq.enable(); i2c } @@ -115,14 +113,6 @@ impl<'d, T: Instance> I2c<'d, T, Async> { .await } - // Mask interrupts and wake any task waiting for this interrupt - unsafe fn on_interrupt(_: *mut ()) { - let i2c = T::regs(); - i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); - - T::waker().wake(); - } - async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> { if buffer.is_empty() { return Err(Error::InvalidReadBufferLength); @@ -320,6 +310,20 @@ impl<'d, T: Instance> I2c<'d, T, Async> { } } +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt() { + let i2c = T::regs(); + i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default()); + + T::waker().wake(); + } +} + impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { fn new_inner( _peri: impl Peripheral

+ 'd, diff --git a/embassy-rp/src/interrupt.rs b/embassy-rp/src/interrupt.rs index 989f5dc2d..1db13deef 100644 --- a/embassy-rp/src/interrupt.rs +++ b/embassy-rp/src/interrupt.rs @@ -1,11 +1,7 @@ -//! Interrupt management -//! -//! This module implements an API for managing interrupts compatible with -//! nrf_softdevice::interrupt. Intended for switching between the two at compile-time. - -// Re-exports +//! Interrupt definitions and macros to bind them. +pub use cortex_m::interrupt::{CriticalSection, Mutex}; use embassy_cortex_m::interrupt::_export::declare; -pub use embassy_cortex_m::interrupt::*; +pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; use crate::pac::Interrupt as InterruptEnum; declare!(TIMER_IRQ_0); @@ -40,3 +36,30 @@ declare!(SWI_IRQ_2); declare!(SWI_IRQ_3); declare!(SWI_IRQ_4); declare!(SWI_IRQ_5); + +/// Macro to bind interrupts to handlers. +/// +/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) +/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to +/// prove at compile-time that the right interrupts have been bound. +// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. +#[macro_export] +macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); + )* + } + + $( + unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} + )* + )* + }; +} diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9d3de1bd8..12d6b8d91 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -3,7 +3,7 @@ use core::slice; use core::task::Poll; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; @@ -52,7 +52,7 @@ pub struct BufferedUartTx<'d, T: Instance> { } pub(crate) fn init_buffers<'d, T: Instance + 'd>( - irq: PeripheralRef<'d, T::Interrupt>, + _irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], ) { @@ -79,24 +79,23 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( w.set_rtim(true); w.set_txim(true); }); - }; - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); + }; } impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx, rx); + into_ref!(tx, rx); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), Some(rx.map_into()), None, None, config); init_buffers::(irq, tx_buffer, rx_buffer); @@ -109,7 +108,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new_with_rtscts( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, @@ -118,7 +117,7 @@ impl<'d, T: Instance> BufferedUart<'d, T> { rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx, rx, cts, rts); + into_ref!(tx, rx, cts, rts); super::Uart::<'d, T, Async>::init( Some(tx.map_into()), @@ -163,12 +162,12 @@ impl<'d, T: Instance> BufferedUart<'d, T> { impl<'d, T: Instance> BufferedUartRx<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, rx: impl Peripheral

> + 'd, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, rx); + into_ref!(rx); super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), None, None, config); init_buffers::(irq, &mut [], rx_buffer); @@ -178,13 +177,13 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { pub fn new_with_rts( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, rx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, rx, rts); + into_ref!(rx, rts); super::Uart::<'d, T, Async>::init(None, Some(rx.map_into()), Some(rts.map_into()), None, config); init_buffers::(irq, &mut [], rx_buffer); @@ -312,12 +311,12 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { impl<'d, T: Instance> BufferedUartTx<'d, T> { pub fn new( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx); + into_ref!(tx); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, None, config); init_buffers::(irq, tx_buffer, &mut []); @@ -327,13 +326,13 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { pub fn new_with_cts( _uart: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(irq, tx, cts); + into_ref!(tx, cts); super::Uart::<'d, T, Async>::init(Some(tx.map_into()), None, None, Some(cts.map_into()), config); init_buffers::(irq, tx_buffer, &mut []); @@ -482,97 +481,107 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { } } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::buffered_state(); +pub struct BufferedInterruptHandler { + _uart: PhantomData, +} - unsafe { - // Clear TX and error interrupt flags - // RX interrupt flags are cleared by reading from the FIFO. - let ris = r.uartris().read(); - r.uarticr().write(|w| { - w.set_txic(ris.txris()); - w.set_feic(ris.feris()); - w.set_peic(ris.peris()); - w.set_beic(ris.beris()); - w.set_oeic(ris.oeris()); - }); - - trace!("on_interrupt ris={:#X}", ris.0); - - // Errors - if ris.feris() { - warn!("Framing error"); - } - if ris.peris() { - warn!("Parity error"); - } - if ris.beris() { - warn!("Break error"); - } - if ris.oeris() { - warn!("Overrun error"); +impl interrupt::Handler for BufferedInterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + if r.uartdmacr().read().rxdmae() { + return; } - // RX - let mut rx_writer = s.rx_buf.writer(); - let rx_buf = rx_writer.push_slice(); - let mut n_read = 0; - let mut error = false; - for rx_byte in rx_buf { - if r.uartfr().read().rxfe() { - break; - } - let dr = r.uartdr().read(); - if (dr.0 >> 8) != 0 { - s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); - error = true; - // only fill the buffer with valid characters. the current character is fine - // if the error is an overrun, but if we add it to the buffer we'll report - // the overrun one character too late. drop it instead and pretend we were - // a bit slower at draining the rx fifo than we actually were. - // this is consistent with blocking uart error reporting. - break; - } - *rx_byte = dr.data(); - n_read += 1; - } - if n_read > 0 { - rx_writer.push_done(n_read); - s.rx_waker.wake(); - } else if error { - s.rx_waker.wake(); - } - // Disable any further RX interrupts when the buffer becomes full or - // errors have occurred. This lets us buffer additional errors in the - // fifo without needing more error storage locations, and most applications - // will want to do a full reset of their uart state anyway once an error - // has happened. - if s.rx_buf.is_full() || error { - r.uartimsc().write_clear(|w| { - w.set_rxim(true); - w.set_rtim(true); + let s = T::buffered_state(); + + unsafe { + // Clear TX and error interrupt flags + // RX interrupt flags are cleared by reading from the FIFO. + let ris = r.uartris().read(); + r.uarticr().write(|w| { + w.set_txic(ris.txris()); + w.set_feic(ris.feris()); + w.set_peic(ris.peris()); + w.set_beic(ris.beris()); + w.set_oeic(ris.oeris()); }); - } - // TX - let mut tx_reader = s.tx_buf.reader(); - let tx_buf = tx_reader.pop_slice(); - let mut n_written = 0; - for tx_byte in tx_buf.iter_mut() { - if r.uartfr().read().txff() { - break; + trace!("on_interrupt ris={:#X}", ris.0); + + // Errors + if ris.feris() { + warn!("Framing error"); } - r.uartdr().write(|w| w.set_data(*tx_byte)); - n_written += 1; + if ris.peris() { + warn!("Parity error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + // RX + let mut rx_writer = s.rx_buf.writer(); + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + let mut error = false; + for rx_byte in rx_buf { + if r.uartfr().read().rxfe() { + break; + } + let dr = r.uartdr().read(); + if (dr.0 >> 8) != 0 { + s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); + error = true; + // only fill the buffer with valid characters. the current character is fine + // if the error is an overrun, but if we add it to the buffer we'll report + // the overrun one character too late. drop it instead and pretend we were + // a bit slower at draining the rx fifo than we actually were. + // this is consistent with blocking uart error reporting. + break; + } + *rx_byte = dr.data(); + n_read += 1; + } + if n_read > 0 { + rx_writer.push_done(n_read); + s.rx_waker.wake(); + } else if error { + s.rx_waker.wake(); + } + // Disable any further RX interrupts when the buffer becomes full or + // errors have occurred. This lets us buffer additional errors in the + // fifo without needing more error storage locations, and most applications + // will want to do a full reset of their uart state anyway once an error + // has happened. + if s.rx_buf.is_full() || error { + r.uartimsc().write_clear(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } + + // TX + let mut tx_reader = s.tx_buf.reader(); + let tx_buf = tx_reader.pop_slice(); + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; + } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; + } + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } + // The TX interrupt only triggers once when the FIFO threshold is + // crossed. No need to disable it when the buffer becomes empty + // as it does re-trigger anymore once we have cleared it. } - if n_written > 0 { - tx_reader.pop_done(n_written); - s.tx_waker.wake(); - } - // The TX interrupt only triggers once when the FIFO threshold is - // crossed. No need to disable it when the buffer becomes empty - // as it does re-trigger anymore once we have cleared it. } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index a0ee6b4ce..7234336b4 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; use embassy_futures::select::{select, Either}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -20,7 +20,7 @@ use crate::{pac, peripherals, Peripheral, RegExt}; #[cfg(feature = "nightly")] mod buffered; #[cfg(feature = "nightly")] -pub use buffered::{BufferedUart, BufferedUartRx, BufferedUartTx}; +pub use buffered::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, BufferedUartTx}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DataBits { @@ -203,11 +203,9 @@ impl<'d, T: Instance> UartTx<'d, T, Blocking> { #[cfg(feature = "nightly")] pub fn into_buffered( self, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx_buffer: &'d mut [u8], ) -> BufferedUartTx<'d, T> { - into_ref!(irq); - buffered::init_buffers::(irq, tx_buffer, &mut []); BufferedUartTx { phantom: PhantomData } @@ -235,25 +233,24 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { pub fn new( _uart: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(rx, irq, rx_dma); + into_ref!(rx, rx_dma); Uart::::init(None, Some(rx.map_into()), None, None, config); - Self::new_inner(Some(irq), Some(rx_dma.map_into())) + Self::new_inner(true, Some(rx_dma.map_into())) } - fn new_inner(irq: Option>, rx_dma: Option>) -> Self { - debug_assert_eq!(irq.is_some(), rx_dma.is_some()); - if let Some(irq) = irq { + fn new_inner(has_irq: bool, rx_dma: Option>) -> Self { + debug_assert_eq!(has_irq, rx_dma.is_some()); + if has_irq { unsafe { // disable all error interrupts initially T::regs().uartimsc().write(|w| w.0 = 0); + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); } - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); } Self { rx_dma, @@ -299,6 +296,12 @@ impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { if let Some(_) = self.rx_dma { unsafe { T::Interrupt::steal().disable(); + // clear dma flags. irq handlers use these to disambiguate among themselves. + T::regs().uartdmacr().write_clear(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + reg.set_dmaonerr(true); + }); } } } @@ -312,33 +315,41 @@ impl<'d, T: Instance> UartRx<'d, T, Blocking> { ) -> Self { into_ref!(rx); Uart::::init(None, Some(rx.map_into()), None, None, config); - Self::new_inner(None, None) + Self::new_inner(false, None) } #[cfg(feature = "nightly")] pub fn into_buffered( self, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, rx_buffer: &'d mut [u8], ) -> BufferedUartRx<'d, T> { - into_ref!(irq); - buffered::init_buffers::(irq, &mut [], rx_buffer); BufferedUartRx { phantom: PhantomData } } } -unsafe fn on_interrupt(_: *mut ()) { - let uart = T::regs(); - let state = T::dma_state(); - let errs = uart.uartris().read(); - state.rx_errs.store(errs.0 as u16, Ordering::Relaxed); - state.rx_err_waker.wake(); - // disable the error interrupts instead of clearing the flags. clearing the - // flags would allow the dma transfer to continue, potentially signaling - // completion before we can check for errors that happened *during* the transfer. - uart.uartimsc().write_clear(|w| w.0 = errs.0); +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let uart = T::regs(); + if !uart.uartdmacr().read().rxdmae() { + return; + } + + let state = T::dma_state(); + let errs = uart.uartris().read(); + state.rx_errs.store(errs.0 as u16, Ordering::Relaxed); + state.rx_err_waker.wake(); + // disable the error interrupts instead of clearing the flags. clearing the + // flags would allow the dma transfer to continue, potentially signaling + // completion before we can check for errors that happened *during* the transfer. + uart.uartimsc().write_clear(|w| w.0 = errs.0); + } } impl<'d, T: Instance> UartRx<'d, T, Async> { @@ -428,7 +439,17 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { config: Config, ) -> Self { into_ref!(tx, rx); - Self::new_inner(uart, tx.map_into(), rx.map_into(), None, None, None, None, None, config) + Self::new_inner( + uart, + tx.map_into(), + rx.map_into(), + None, + None, + false, + None, + None, + config, + ) } /// Create a new UART with hardware flow control (RTS/CTS) @@ -447,7 +468,7 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), - None, + false, None, None, config, @@ -457,12 +478,10 @@ impl<'d, T: Instance> Uart<'d, T, Blocking> { #[cfg(feature = "nightly")] pub fn into_buffered( self, - irq: impl Peripheral

+ 'd, + irq: impl Binding>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], ) -> BufferedUart<'d, T> { - into_ref!(irq); - buffered::init_buffers::(irq, tx_buffer, rx_buffer); BufferedUart { @@ -478,19 +497,19 @@ impl<'d, T: Instance> Uart<'d, T, Async> { uart: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, rx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(tx, rx, irq, tx_dma, rx_dma); + into_ref!(tx, rx, tx_dma, rx_dma); Self::new_inner( uart, tx.map_into(), rx.map_into(), None, None, - Some(irq), + true, Some(tx_dma.map_into()), Some(rx_dma.map_into()), config, @@ -504,19 +523,19 @@ impl<'d, T: Instance> Uart<'d, T, Async> { rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl Binding>, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(tx, rx, cts, rts, irq, tx_dma, rx_dma); + into_ref!(tx, rx, cts, rts, tx_dma, rx_dma); Self::new_inner( uart, tx.map_into(), rx.map_into(), Some(rts.map_into()), Some(cts.map_into()), - Some(irq), + true, Some(tx_dma.map_into()), Some(rx_dma.map_into()), config, @@ -531,7 +550,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { mut rx: PeripheralRef<'d, AnyPin>, mut rts: Option>, mut cts: Option>, - irq: Option>, + has_irq: bool, tx_dma: Option>, rx_dma: Option>, config: Config, @@ -546,7 +565,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { Self { tx: UartTx::new_inner(tx_dma), - rx: UartRx::new_inner(irq, rx_dma), + rx: UartRx::new_inner(has_irq, rx_dma), } } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index a049e4769..fada2790f 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -4,7 +4,7 @@ use core::slice; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::into_ref; +use embassy_cortex_m::interrupt::{self, Binding}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{ @@ -105,11 +105,11 @@ pub struct Driver<'d, T: Instance> { } impl<'d, T: Instance> Driver<'d, T> { - pub fn new(_usb: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(irq); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + pub fn new(_usb: impl Peripheral

+ 'd, _irq: impl Binding>) -> Self { + unsafe { + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); + } let regs = T::regs(); unsafe { @@ -149,47 +149,6 @@ impl<'d, T: Instance> Driver<'d, T> { } } - fn on_interrupt(_: *mut ()) { - unsafe { - let regs = T::regs(); - //let x = regs.istr().read().0; - //trace!("USB IRQ: {:08x}", x); - - let ints = regs.ints().read(); - - if ints.bus_reset() { - regs.inte().write_clear(|w| w.set_bus_reset(true)); - BUS_WAKER.wake(); - } - if ints.dev_resume_from_host() { - regs.inte().write_clear(|w| w.set_dev_resume_from_host(true)); - BUS_WAKER.wake(); - } - if ints.dev_suspend() { - regs.inte().write_clear(|w| w.set_dev_suspend(true)); - BUS_WAKER.wake(); - } - if ints.setup_req() { - regs.inte().write_clear(|w| w.set_setup_req(true)); - EP_OUT_WAKERS[0].wake(); - } - - if ints.buff_status() { - let s = regs.buff_status().read(); - regs.buff_status().write_value(s); - - for i in 0..EP_COUNT { - if s.ep_in(i) { - EP_IN_WAKERS[i].wake(); - } - if s.ep_out(i) { - EP_OUT_WAKERS[i].wake(); - } - } - } - } - } - fn alloc_endpoint( &mut self, ep_type: EndpointType, @@ -288,6 +247,51 @@ impl<'d, T: Instance> Driver<'d, T> { } } +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + //let x = regs.istr().read().0; + //trace!("USB IRQ: {:08x}", x); + + let ints = regs.ints().read(); + + if ints.bus_reset() { + regs.inte().write_clear(|w| w.set_bus_reset(true)); + BUS_WAKER.wake(); + } + if ints.dev_resume_from_host() { + regs.inte().write_clear(|w| w.set_dev_resume_from_host(true)); + BUS_WAKER.wake(); + } + if ints.dev_suspend() { + regs.inte().write_clear(|w| w.set_dev_suspend(true)); + BUS_WAKER.wake(); + } + if ints.setup_req() { + regs.inte().write_clear(|w| w.set_setup_req(true)); + EP_OUT_WAKERS[0].wake(); + } + + if ints.buff_status() { + let s = regs.buff_status().read(); + regs.buff_status().write_value(s); + + for i in 0..EP_COUNT { + if s.ep_in(i) { + EP_IN_WAKERS[i].wake(); + } + if s.ep_out(i) { + EP_OUT_WAKERS[i].wake(); + } + } + } + } +} + impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { type EndpointOut = Endpoint<'d, T, Out>; type EndpointIn = Endpoint<'d, T, In>; diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 4202fd394..7c2ca19f7 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -4,16 +4,19 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config}; -use embassy_rp::interrupt; +use embassy_rp::adc::{Adc, Config, InterruptHandler}; +use embassy_rp::bind_interrupts; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ADC_IRQ_FIFO => InterruptHandler; +}); + #[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 adc = Adc::new(p.ADC, Irqs, Config::default()); let mut p26 = p.PIN_26; let mut p27 = p.PIN_27; diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs index d1a2e3cd7..cf3cf742c 100644 --- a/examples/rp/src/bin/i2c_async.rs +++ b/examples/rp/src/bin/i2c_async.rs @@ -4,12 +4,17 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::i2c::{self, Config}; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; +use embassy_rp::i2c::{self, Config, InterruptHandler}; +use embassy_rp::peripherals::I2C1; use embassy_time::{Duration, Timer}; use embedded_hal_async::i2c::I2c; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + I2C1_IRQ => InterruptHandler; +}); + #[allow(dead_code)] mod mcp23017 { pub const ADDR: u8 = 0x20; // default addr @@ -64,10 +69,9 @@ async fn main(_spawner: Spawner) { let sda = p.PIN_14; let scl = p.PIN_15; - let irq = interrupt::take!(I2C1_IRQ); info!("set up i2c "); - let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, Config::default()); + let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, Irqs, Config::default()); use mcp23017::*; diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index a8a682274..d6f01b4de 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -5,13 +5,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_executor::_export::StaticCell; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; -use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config}; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => BufferedInterruptHandler; +}); + macro_rules! singleton { ($val:expr) => {{ type T = impl Sized; @@ -26,10 +30,9 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); - let irq = interrupt::take!(UART0_IRQ); let tx_buf = &mut singleton!([0u8; 16])[..]; let rx_buf = &mut singleton!([0u8; 16])[..]; - let uart = BufferedUart::new(uart, irq, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); + let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); let (rx, mut tx) = uart.split(); unwrap!(spawner.spawn(reader(rx))); diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index 4119a309f..c0943a1b8 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs @@ -7,24 +7,22 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART1; -use embassy_rp::uart::{Async, Config, UartRx, UartTx}; +use embassy_rp::uart::{Async, Config, InterruptHandler, UartRx, UartTx}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART1_IRQ => InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut uart_tx = UartTx::new(p.UART0, p.PIN_0, p.DMA_CH0, Config::default()); - let uart_rx = UartRx::new( - p.UART1, - p.PIN_5, - interrupt::take!(UART1_IRQ), - p.DMA_CH1, - Config::default(), - ); + let uart_rx = UartRx::new(p.UART1, p.PIN_5, Irqs, p.DMA_CH1, Config::default()); unwrap!(spawner.spawn(reader(uart_rx))); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 66a6ed4d0..2ddfb6344 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -6,8 +6,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; -use embassy_rp::usb::Driver; -use embassy_rp::{interrupt, peripherals}; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_rp::{bind_interrupts, peripherals}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; @@ -15,6 +16,10 @@ use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + type MyDriver = Driver<'static, peripherals::USB>; macro_rules! singleton { @@ -48,8 +53,7 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. - let irq = interrupt::take!(USBCTRL_IRQ); - let driver = Driver::new(p.USB, irq); + let driver = Driver::new(p.USB, Irqs); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs index 52417a02e..7c90d0ca3 100644 --- a/examples/rp/src/bin/usb_logger.rs +++ b/examples/rp/src/bin/usb_logger.rs @@ -3,12 +3,16 @@ #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; -use embassy_rp::interrupt; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::USB; -use embassy_rp::usb::Driver; +use embassy_rp::usb::{Driver, InterruptHandler}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + #[embassy_executor::task] async fn logger_task(driver: Driver<'static, USB>) { embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); @@ -17,8 +21,7 @@ async fn logger_task(driver: Driver<'static, USB>) { #[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); + let driver = Driver::new(p.USB, Irqs); spawner.spawn(logger_task(driver)).unwrap(); let mut counter = 0; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 8160a1875..ca728536c 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -5,13 +5,18 @@ use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::interrupt; -use embassy_rp::usb::{Driver, Instance}; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello there!"); @@ -19,8 +24,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. - let irq = interrupt::take!(USBCTRL_IRQ); - let driver = Driver::new(p.USB, irq); + let driver = Driver::new(p.USB, Irqs); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 1deb22ce6..1dcf57d07 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -4,13 +4,18 @@ use defmt::{assert_eq, panic, *}; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::interrupt; -use embassy_rp::uart::{BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::{Read, ReadExactError, Write}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => BufferedInterruptHandler; +}); + async fn read(uart: &mut BufferedUart<'_, impl Instance>) -> Result<[u8; N], Error> { let mut buf = [255; N]; match uart.read_exact(&mut buf).await { @@ -60,13 +65,12 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); - let mut irq = interrupt::take!(UART0_IRQ); { let config = Config::default(); let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); + let mut uart = BufferedUart::new(&mut uart, Irqs, &mut tx, &mut rx, tx_buf, rx_buf, config); // Make sure we send more bytes than fits in the FIFO, to test the actual // bufferedUart. @@ -86,7 +90,7 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); + let mut uart = BufferedUart::new(&mut uart, Irqs, &mut tx, &mut rx, tx_buf, rx_buf, config); // Make sure we send more bytes than fits in the FIFO, to test the actual // bufferedUart. @@ -121,7 +125,7 @@ async fn main(_spawner: Spawner) { config.baudrate = 1000; let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUart::new(&mut uart, &mut irq, &mut tx, &mut rx, tx_buf, rx_buf, config); + let mut uart = BufferedUart::new(&mut uart, Irqs, &mut tx, &mut rx, tx_buf, rx_buf, config); // break on empty buffer uart.send_break(20).await; @@ -155,7 +159,7 @@ async fn main(_spawner: Spawner) { config.baudrate = 1000; config.parity = Parity::ParityEven; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config); + let mut uart = BufferedUartRx::new(&mut uart, Irqs, &mut rx, rx_buf, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u32) { send(pin, v, Some(parity != 0)).await; @@ -202,7 +206,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 1000; let rx_buf = &mut [0u8; 16]; - let mut uart = BufferedUartRx::new(&mut uart, &mut irq, &mut rx, rx_buf, config); + let mut uart = BufferedUartRx::new(&mut uart, Irqs, &mut rx, rx_buf, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) { if good { diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index 52f42e582..75be76eda 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -4,12 +4,17 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::interrupt; -use embassy_rp::uart::{Async, Config, Error, Instance, Parity, Uart, UartRx}; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{Async, Config, Error, Instance, InterruptHandler, Parity, Uart, UartRx}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => InterruptHandler; +}); + async fn read(uart: &mut Uart<'_, impl Instance, Async>) -> Result<[u8; N], Error> { let mut buf = [255; N]; uart.read(&mut buf).await?; @@ -51,7 +56,6 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); - let mut irq = interrupt::take!(UART0_IRQ); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. @@ -61,7 +65,7 @@ async fn main(_spawner: Spawner) { &mut uart, &mut tx, &mut rx, - &mut irq, + Irqs, &mut p.DMA_CH0, &mut p.DMA_CH1, config, @@ -82,7 +86,7 @@ async fn main(_spawner: Spawner) { &mut uart, &mut tx, &mut rx, - &mut irq, + Irqs, &mut p.DMA_CH0, &mut p.DMA_CH1, config, @@ -111,7 +115,7 @@ async fn main(_spawner: Spawner) { &mut uart, &mut tx, &mut rx, - &mut irq, + Irqs, &mut p.DMA_CH0, &mut p.DMA_CH1, config, @@ -154,7 +158,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 1000; config.parity = Parity::ParityEven; - let mut uart = UartRx::new(&mut uart, &mut rx, &mut irq, &mut p.DMA_CH0, config); + let mut uart = UartRx::new(&mut uart, &mut rx, Irqs, &mut p.DMA_CH0, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, parity: u32) { send(pin, v, Some(parity != 0)).await; @@ -199,7 +203,7 @@ async fn main(_spawner: Spawner) { // choose a very slow baud rate to make tests reliable even with O0 let mut config = Config::default(); config.baudrate = 1000; - let mut uart = UartRx::new(&mut uart, &mut rx, &mut irq, &mut p.DMA_CH0, config); + let mut uart = UartRx::new(&mut uart, &mut rx, Irqs, &mut p.DMA_CH0, config); async fn chr(pin: &mut Output<'_, impl embassy_rp::gpio::Pin>, v: u8, good: bool) { if good { diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs index d8c9aecf6..8605bb1c5 100644 --- a/tests/rp/src/bin/uart_upgrade.rs +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -4,11 +4,16 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; -use embassy_rp::interrupt; -use embassy_rp::uart::{Config, Uart}; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::UART0; +use embassy_rp::uart::{BufferedInterruptHandler, Config, Uart}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART0_IRQ => BufferedInterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -29,11 +34,10 @@ async fn main(_spawner: Spawner) { uart.blocking_read(&mut buf).unwrap(); assert_eq!(buf, data); - let irq = interrupt::take!(UART0_IRQ); let tx_buf = &mut [0u8; 16]; let rx_buf = &mut [0u8; 16]; - let mut uart = uart.into_buffered(irq, tx_buf, rx_buf); + let mut uart = uart.into_buffered(Irqs, tx_buf, rx_buf); // Make sure we send more bytes than fits in the FIFO, to test the actual // bufferedUart. From 0c18a13cc056d4d54ca7261289615b2d03769a76 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 16 May 2023 01:21:28 +0200 Subject: [PATCH 1119/1575] rp/multicore: fix undefined behavior in multicore spawn. It is UB to pass `entry` to core1 as `&mut`, because core0 keeps an aliasing pointer to that memory region, and actually writes to it (when `spawn_core1` returns, the stack frame gets deallocated and the memory gets reused). This violates noalias requirements. Added the fence just in case, een though it works without. --- embassy-rp/src/multicore.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index c84fea5c8..9a445c26e 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -122,11 +122,16 @@ where extern "C" fn core1_startup bad::Never>( _: u64, _: u64, - entry: &mut ManuallyDrop, + entry: *mut ManuallyDrop, stack_bottom: *mut usize, ) -> ! { core1_setup(stack_bottom); - let entry = unsafe { ManuallyDrop::take(entry) }; + + let entry = unsafe { ManuallyDrop::take(&mut *entry) }; + + // make sure the preceding read doesn't get reordered past the following fifo write + compiler_fence(Ordering::SeqCst); + // Signal that it's safe for core 0 to get rid of the original value now. fifo_write(1); @@ -164,7 +169,7 @@ where // Push `entry`. stack_ptr = stack_ptr.sub(1); - stack_ptr.cast::<&mut ManuallyDrop>().write(&mut entry); + stack_ptr.cast::<*mut ManuallyDrop>().write(&mut entry); } // Make sure the compiler does not reorder the stack writes after to after the From 56c3a949af87a6179eee655c19c89144604d17a2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 16 May 2023 01:42:35 +0200 Subject: [PATCH 1120/1575] rp/multicore: ensure stack is 8-byte aligned. --- embassy-rp/src/multicore.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 9a445c26e..bbc775105 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -153,7 +153,12 @@ where psm.frce_off().modify(|w| w.set_proc1(false)); } - let mem = unsafe { core::slice::from_raw_parts_mut(stack.mem.as_mut_ptr() as *mut usize, stack.mem.len() / 4) }; + // The ARM AAPCS ABI requires 8-byte stack alignment. + // #[align] on `struct Stack` ensures the bottom is aligned, but the top could still be + // unaligned if the user chooses a stack size that's not multiple of 8. + // So, we round down to the next multiple of 8. + let stack_words = stack.mem.len() / 8 * 2; + let mem = unsafe { core::slice::from_raw_parts_mut(stack.mem.as_mut_ptr() as *mut usize, stack_words) }; // Set up the stack let mut stack_ptr = unsafe { mem.as_mut_ptr().add(mem.len()) }; From ab63f3832fdceeea994500042594e0b7c0acac95 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 16 May 2023 11:21:17 +0200 Subject: [PATCH 1121/1575] rp: Read flash unique id and jedec id --- embassy-rp/src/flash.rs | 231 +++++++++++++++++++++++++++++++++++ examples/rp/src/bin/flash.rs | 10 ++ 2 files changed, 241 insertions(+) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 51c7af913..929bd028c 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -187,6 +187,23 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { crate::multicore::resume_core1(); Ok(()) } + + /// Read SPI flash unique ID + pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { + unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid, true))? }; + Ok(()) + } + + /// Read SPI flash JEDEC ID + pub fn jedec_id(&mut self) -> Result { + let mut jedec = None; + unsafe { + self.in_ram(|| { + jedec.replace(ram_helpers::flash_jedec_id(true)); + })?; + }; + Ok(jedec.unwrap()) + } } impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { @@ -457,6 +474,220 @@ mod ram_helpers { clobber_abi("C"), ); } + + #[repr(C)] + struct FlashCommand { + cmd_addr: *const u8, + cmd_addr_len: u32, + dummy_len: u32, + data: *mut u8, + data_len: u32, + } + + /// Return SPI flash unique ID + /// + /// Not all SPI flashes implement this command, so check the JEDEC + /// ID before relying on it. The Winbond parts commonly seen on + /// RP2040 devboards (JEDEC=0xEF7015) support an 8-byte unique ID; + /// https://forums.raspberrypi.com/viewtopic.php?t=331949 suggests + /// that LCSC (Zetta) parts have a 16-byte unique ID (which is + /// *not* unique in just its first 8 bytes), + /// JEDEC=0xBA6015. Macronix and Spansion parts do not have a + /// unique ID. + /// + /// The returned bytes are relatively predictable and should be + /// salted and hashed before use if that is an issue (e.g. for MAC + /// addresses). + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) + pub unsafe fn flash_unique_id(out: &mut [u8], use_boot2: bool) { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(false, false, &boot2) + } else { + flash_function_pointers(false, false) + }; + // 4B - read unique ID + let cmd = [0x4B]; + read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers); + } + + /// Return SPI flash JEDEC ID + /// + /// This is the three-byte manufacturer-and-model identifier + /// commonly used to check before using manufacturer-specific SPI + /// flash features, e.g. 0xEF7015 for Winbond W25Q16JV. + /// + /// # Safety + /// + /// Nothing must access flash while this is running. + /// Usually this means: + /// - interrupts must be disabled + /// - 2nd core must be running code from RAM or ROM with interrupts disabled + /// - DMA must not access flash memory + /// + /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) + pub unsafe fn flash_jedec_id(use_boot2: bool) -> u32 { + let mut boot2 = [0u32; 256 / 4]; + let ptrs = if use_boot2 { + rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + flash_function_pointers_with_boot2(false, false, &boot2) + } else { + flash_function_pointers(false, false) + }; + let mut id = [0u8; 4]; + // 9F - read JEDEC ID + let cmd = [0x9F]; + read_flash(&cmd[..], 0, &mut id[1..4], &ptrs as *const FlashFunctionPointers); + u32::from_be_bytes(id) + } + + unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) { + read_flash_inner( + FlashCommand { + cmd_addr: cmd_addr.as_ptr(), + cmd_addr_len: cmd_addr.len() as u32, + dummy_len, + data: out.as_mut_ptr(), + data_len: out.len() as u32, + }, + ptrs, + ); + } + + /// Issue a generic SPI flash read command + /// + /// # Arguments + /// + /// * `cmd` - `FlashCommand` structure + /// * `ptrs` - Flash function pointers as per `write_flash_inner` + /// + /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) + #[inline(never)] + #[link_section = ".data.ram_func"] + unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { + core::arch::asm!( + "mov r10, r0", // cmd + "mov r5, r1", // ptrs + + "ldr r4, [r5, #0]", + "blx r4", // connect_internal_flash() + + "ldr r4, [r5, #4]", + "blx r4", // flash_exit_xip() + + "mov r7, r10", // cmd + + "movs r4, #0x18", + "lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13 + + // Disable, write 0 to SSIENR + "movs r0, #0", + "str r0, [r4, #8]", // SSIENR + + // Write ctrlr0 + "movs r0, #0x3", + "lsls r0, r0, #8", // TMOD=0x300 + "ldr r1, [r4, #0]", // CTRLR0 + "orrs r1, r0", + "str r1, [r4, #0]", + + // Write ctrlr1 with len-1 + "ldr r0, [r7, #8]", // dummy_len + "ldr r1, [r7, #16]", // data_len + "add r0, r1", + "subs r0, #1", + "str r0, [r4, #0x04]", // CTRLR1 + + // Enable, write 1 to ssienr + "movs r0, #1", + "str r0, [r4, #8]", // SSIENR + + // Write cmd/addr phase to DR + "mov r2, r4", + "adds r2, 0x60", // &DR + "ldr r0, [r7, #0]", // cmd_addr + "ldr r1, [r7, #4]", // cmd_addr_len + "10:", + "ldrb r3, [r0]", + "strb r3, [r2]", // DR + "adds r0, #1", + "subs r1, #1", + "bne 10b", + + // Skip any dummy cycles + "ldr r1, [r7, #8]", // dummy_len + "cmp r1, #0", + "beq 9f", + "4:", + "ldr r3, [r4, #0x28]", // SR + "movs r2, #0x8", + "tst r3, r2", // SR.RFNE + "beq 4b", + + "mov r2, r4", + "adds r2, 0x60", // &DR + "ldrb r3, [r2]", // DR + "subs r1, #1", + "bne 4b", + + // Read RX fifo + "9:", + "ldr r0, [r7, #12]", // data + "ldr r1, [r7, #16]", // data_len + + "2:", + "ldr r3, [r4, #0x28]", // SR + "movs r2, #0x8", + "tst r3, r2", // SR.RFNE + "beq 2b", + + "mov r2, r4", + "adds r2, 0x60", // &DR + "ldrb r3, [r2]", // DR + "strb r3, [r0]", + "adds r0, #1", + "subs r1, #1", + "bne 2b", + + // Disable, write 0 to ssienr + "movs r0, #0", + "str r0, [r4, #8]", // SSIENR + + // Write 0 to CTRLR1 (returning to its default value) + // + // flash_enter_cmd_xip does NOT do this, and everything goes + // wrong unless we do it here + "str r0, [r4, #4]", // CTRLR1 + + "ldr r4, [r5, #20]", + "blx r4", // flash_enter_cmd_xip(); + + in("r0") &cmd as *const FlashCommand, + in("r1") ptrs, + out("r2") _, + out("r3") _, + out("r4") _, + // Registers r8-r10 are used to store values + // from r0-r2 in registers not clobbered by + // function calls. + // The values can't be passed in using r8-r10 directly + // due to https://github.com/rust-lang/rust/issues/99071 + out("r8") _, + out("r9") _, + out("r10") _, + clobber_abi("C"), + ); + } } mod sealed { diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 8d6b379f4..19076150c 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -24,6 +24,16 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_millis(10)).await; let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); + + // Get JEDEC id + let jedec = flash.jedec_id().unwrap(); + info!("jedec id: 0x{:x}", jedec); + + // Get unique id + let mut uid = [0; 8]; + flash.unique_id(&mut uid).unwrap(); + info!("unique id: {:?}", uid); + erase_write_sector(&mut flash, 0x00); multiwrite_bytes(&mut flash, ERASE_SIZE as u32); From b950d6d72bf92f1943f885ce700685fedf4b6cd9 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 16 May 2023 11:28:35 +0200 Subject: [PATCH 1122/1575] Add HIL test --- tests/rp/src/bin/flash.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 897e3804f..00bebe2b6 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -23,6 +23,15 @@ async fn main(_spawner: Spawner) { let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); + // Get JEDEC id + let jedec = defmt::unwrap!(flash.jedec_id()); + info!("jedec id: 0x{:x}", jedec); + + // Get unique id + let mut uid = [0; 8]; + defmt::unwrap!(flash.unique_id(&mut uid)); + info!("unique id: {:?}", uid); + let mut buf = [0u8; ERASE_SIZE]; defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); From 4e9ed223a9c489ec75d2d928e26d5a83c2cdf5d4 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Tue, 16 May 2023 09:34:38 -0700 Subject: [PATCH 1123/1575] Allow for an optional user-defined entry macro when targeting RISC-V --- embassy-macros/src/lib.rs | 14 +++++++++++++- embassy-macros/src/macros/main.rs | 22 ++++++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index d2c696c72..dc5b519ff 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -79,6 +79,8 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { /// * The function must not use generics. /// * Only a single `main` task may be declared. /// +/// A user-defined entry macro can be optionally provided via the `entry` argument to override the default of `riscv_rt::entry`. +/// /// ## Examples /// Spawning a task: /// @@ -88,11 +90,21 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { /// // Function body /// } /// ``` +/// +/// Spawning a task using a custom entry macro: +/// ``` rust +/// #[embassy_executor::main(entry = "esp_riscv_rt::entry")] +/// 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() + main::run(args.clone(), f, main::riscv(args)) + .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. diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index 6ae77398d..5c099f68a 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -1,16 +1,30 @@ use darling::FromMeta; use proc_macro2::TokenStream; use quote::quote; -use syn::{ReturnType, Type}; +use syn::{Expr, ReturnType, Type}; use crate::util::ctxt::Ctxt; #[derive(Debug, FromMeta)] -struct Args {} +struct Args { + #[darling(default)] + entry: Option, +} + +pub fn riscv(args: syn::AttributeArgs) -> TokenStream { + let maybe_entry = match Args::from_list(&args) { + Ok(args) => args.entry, + Err(e) => return e.write_errors(), + }; + + let entry = maybe_entry.unwrap_or("riscv_rt::entry".into()); + let entry = match Expr::from_string(&entry) { + Ok(expr) => expr, + Err(e) => return e.write_errors(), + }; -pub fn riscv() -> TokenStream { quote! { - #[riscv_rt::entry] + #[#entry] fn main() -> ! { let mut executor = ::embassy_executor::Executor::new(); let executor = unsafe { __make_static(&mut executor) }; From a4772c15c0df5939492361f43ab65a94eca70785 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Wed, 17 May 2023 00:16:36 +0200 Subject: [PATCH 1124/1575] rp: Add system reset fn via watchdog --- embassy-rp/src/watchdog.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index 3631b2a9d..78a295ae7 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -106,4 +106,17 @@ impl Watchdog { self.load_counter(self.load_value); self.enable(true); } + + /// Trigger a system reset + pub fn trigger_reset(&mut self) { + unsafe { + self.configure_wdog_reset_triggers(); + self.pause_on_debug(false); + self.enable(true); + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| { + w.set_trigger(true); + }) + } + } } From e1e87fef25e4a8f3ac21bcb4a9cb9bf41ddfd0d2 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 13 May 2023 18:45:14 +0200 Subject: [PATCH 1125/1575] rp/clocks: always inline configure_pll this is always advantageous, except *maybe* in O0. nothing really works as expected in O0, so we may as well always inline for constant propagation. --- embassy-rp/src/clocks.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 1354ccd27..0972cb215 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -576,6 +576,7 @@ unsafe fn start_xosc(crystal_hz: u32) { while !pac::XOSC.status().read().stable() {} } +#[inline(always)] unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { let ref_freq = input_freq / config.refdiv; From 2f2860b09697845029b4827462fdf98b515e8898 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 13 May 2023 18:45:40 +0200 Subject: [PATCH 1126/1575] rp/clocks: always reconfigure pll this is only really useful for runtime *re*configuration, which we don't currently support. even runtime reconfig probably won't need it, unless we keep taking the sledgehammer approach of reconfiguring everything all the time. --- embassy-rp/src/clocks.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 0972cb215..d34082716 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -587,18 +587,6 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { assert!(config.post_div2 <= config.post_div1); assert!(ref_freq <= (config.vco_freq / 16)); - // do not disrupt PLL that is already correctly configured and operating - let cs = p.cs().read(); - let prim = p.prim().read(); - if cs.lock() - && cs.refdiv() == config.refdiv as u8 - && p.fbdiv_int().read().fbdiv_int() == fbdiv as u16 - && prim.postdiv1() == config.post_div1 - && prim.postdiv2() == config.post_div2 - { - return; - } - // Reset it let mut peris = reset::Peripherals(0); match p { From d3494a4bdf513d5b61210180fa227a9bd99935ca Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 13 May 2023 18:55:40 +0200 Subject: [PATCH 1127/1575] rp/clocks: reset all plls at once we might not configure both, so we should put the others into reset state. leaving them fully as is might leave them running, which might not be the goal for runtime reconfig (when it comes around). this now mirrors how we reset all clock-using peripherals and only unreset those that are properly clocked. --- embassy-rp/src/clocks.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index d34082716..bfca3f02e 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -203,6 +203,13 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_ref_ctrl().modify(|w| w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH)); while c.clk_ref_selected().read() != 1 {} + // Reset the PLLs + let mut peris = reset::Peripherals(0); + peris.set_pll_sys(true); + peris.set_pll_usb(true); + reset::reset(peris); + reset::unreset_wait(peris); + if let Some(config) = config.rosc { configure_rosc(config); } @@ -587,16 +594,6 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { assert!(config.post_div2 <= config.post_div1); assert!(ref_freq <= (config.vco_freq / 16)); - // Reset it - let mut peris = reset::Peripherals(0); - match p { - pac::PLL_SYS => peris.set_pll_sys(true), - pac::PLL_USB => peris.set_pll_usb(true), - _ => unreachable!(), - } - reset::reset(peris); - reset::unreset_wait(peris); - // Load VCO-related dividers before starting VCO p.cs().write(|w| w.set_refdiv(config.refdiv as _)); p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); From 5bbed315131991745efacbaa5c384e11f704923b Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 13 May 2023 19:07:36 +0200 Subject: [PATCH 1128/1575] rp/clocks: provide fbdiv, not vco_freq solvers usually output fbdiv directly, using vco_freq to get back to fbdiv is not all that necessary or useful. both vco_freq and fbdiv have hidden constraints, but vco_freq is a lot less accurate because the fbdiv value resulting from the division may be off by almost a full ref_freq's worth of frequency. also fixes the usb pll config, which ran the pll vco way out of (below) spec. --- embassy-rp/src/clocks.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index bfca3f02e..63f70cec4 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -37,15 +37,15 @@ impl ClockConfig { clock_type: ExternalClock::Crystal, sys_pll: Some(PllConfig { refdiv: 1, - vco_freq: 1500_000_000, + fbdiv: 125, post_div1: 6, post_div2: 2, }), usb_pll: Some(PllConfig { refdiv: 1, - vco_freq: 480_000_000, - post_div1: 5, - post_div2: 2, + fbdiv: 120, + post_div1: 6, + post_div2: 5, }), }), ref_clk: RefClkConfig { @@ -126,7 +126,7 @@ pub struct XoscConfig { pub struct PllConfig { pub refdiv: u32, - pub vco_freq: u32, + pub fbdiv: u16, pub post_div1: u8, pub post_div2: u8, } @@ -587,16 +587,15 @@ unsafe fn start_xosc(crystal_hz: u32) { unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { let ref_freq = input_freq / config.refdiv; - let fbdiv = config.vco_freq / ref_freq; - assert!(fbdiv >= 16 && fbdiv <= 320); + assert!(config.fbdiv >= 16 && config.fbdiv <= 320); assert!(config.post_div1 >= 1 && config.post_div1 <= 7); assert!(config.post_div2 >= 1 && config.post_div2 <= 7); assert!(config.post_div2 <= config.post_div1); - assert!(ref_freq <= (config.vco_freq / 16)); + assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); // Load VCO-related dividers before starting VCO p.cs().write(|w| w.set_refdiv(config.refdiv as _)); - p.fbdiv_int().write(|w| w.set_fbdiv_int(fbdiv as _)); + p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); // Turn on PLL p.pwr().modify(|w| { From d97a77147927b8ea6b245ec88a52de92db33813b Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 16 May 2023 17:13:09 +0200 Subject: [PATCH 1129/1575] rp/clocks: remove unsupported xosc config input the datasheet says that the xosc may be run by feeding a square wave into the XIN pin of the chip, but requires that the oscillator be set to pass through XIN in that case. it does not mention how, the xosc peripheral does not seem to have any config bits that could be set to this effect, and pico-sdk seems to have no (or at least no special) handling for this configuration at all. it can thus be assumed to either be not supported even by the reference sdk or to not need different handling. --- embassy-rp/src/clocks.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 63f70cec4..dc8505987 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -34,7 +34,6 @@ impl ClockConfig { }), xosc: Some(XoscConfig { hz: crystal_hz, - clock_type: ExternalClock::Crystal, sys_pll: Some(PllConfig { refdiv: 1, fbdiv: 125, @@ -119,7 +118,6 @@ pub struct RoscConfig { pub struct XoscConfig { pub hz: u32, - pub clock_type: ExternalClock, pub sys_pll: Option, pub usb_pll: Option, } @@ -131,10 +129,6 @@ pub struct PllConfig { pub post_div2: u8, } -pub enum ExternalClock { - Crystal, - Clock, -} pub struct RefClkConfig { pub src: RefClkSrc, pub div: u8, @@ -223,12 +217,9 @@ pub(crate) unsafe fn init(config: ClockConfig) { }); // start XOSC - match config.clock_type { - ExternalClock::Crystal => start_xosc(config.hz), - // TODO The datasheet says the xosc needs to be put into a bypass mode to use an - // external clock, but is mum about how to do that. - ExternalClock::Clock => todo!(), - } + // datasheet mentions support for clock inputs into XIN, but doesn't go into + // how this is achieved. pico-sdk doesn't support this at all. + start_xosc(config.hz); if let Some(sys_pll_config) = config.sys_pll { configure_pll(pac::PLL_SYS, config.hz, sys_pll_config); From f97b591831cab5ec29cfdb7cb98bc93debfdb57e Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 16 May 2023 18:13:15 +0200 Subject: [PATCH 1130/1575] rp/clocks: don't expose unstable pac items exposing pac items kind of undermines the unstable-pac feature. directly exposing register structure is also pretty inconvenient since the clock switching code takes care of the src/aux difference in behavior, so a user needn't really be forced to write down decomposed register values. --- embassy-rp/src/clocks.rs | 214 ++++++++++++++++++++++++----------- examples/rp/src/bin/gpout.rs | 4 +- 2 files changed, 148 insertions(+), 70 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index dc8505987..6d389cace 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -6,11 +6,18 @@ use crate::{pac, reset, Peripheral}; // TODO fix terrible use of global here static mut XIN_HZ: u32 = 0; -pub use rp_pac::clocks::vals::{ - ClkAdcCtrlAuxsrc as AdcAuxsrc, ClkGpoutCtrlAuxsrc as GpoutSrc, ClkPeriCtrlAuxsrc as PeriClkAuxsrc, - ClkRefCtrlAuxsrc as RefAuxsrc, ClkRtcCtrlAuxsrc as RtcAuxsrc, ClkSysCtrlAuxsrc as SysAuxsrc, - ClkUsbCtrlAuxsrc as UsbAuxsrc, -}; +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PeriClkSrc { + Sys = ClkPeriCtrlAuxsrc::CLK_SYS.0, + PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS.0, + PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB.0, + Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC.0, + Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0, + Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0, +} #[non_exhaustive] pub struct ClockConfig { @@ -18,7 +25,7 @@ pub struct ClockConfig { pub xosc: Option, pub ref_clk: RefClkConfig, pub sys_clk: SysClkConfig, - pub peri_clk_src: Option, + pub peri_clk_src: Option, pub usb_clk: Option, pub adc_clk: Option, pub rtc_clk: Option, @@ -28,7 +35,7 @@ impl ClockConfig { pub fn crystal(crystal_hz: u32) -> Self { Self { rosc: Some(RoscConfig { - range: pac::rosc::vals::FreqRange::MEDIUM, + range: RoscRange::Medium, drive_strength: [0; 8], div: 16, }), @@ -52,23 +59,23 @@ impl ClockConfig { div: 1, }, sys_clk: SysClkConfig { - src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS), + src: SysClkSrc::PllSys, div_int: 1, div_frac: 0, }, - peri_clk_src: Some(ClkPeriCtrlAuxsrc::CLK_SYS), + peri_clk_src: Some(PeriClkSrc::Sys), usb_clk: Some(UsbClkConfig { - src: ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB, + src: UsbClkSrc::PllUsb, div: 1, phase: 0, }), adc_clk: Some(AdcClkConfig { - src: ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB, + src: AdcClkSrc::PllUsb, div: 1, phase: 0, }), rtc_clk: Some(RtcClkConfig { - src: ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB, + src: RtcClkSrc::PllUsb, div_int: 1024, div_frac: 0, phase: 0, @@ -79,7 +86,7 @@ impl ClockConfig { pub fn rosc() -> Self { Self { rosc: Some(RoscConfig { - range: pac::rosc::vals::FreqRange::HIGH, + range: RoscRange::High, drive_strength: [0; 8], div: 1, }), @@ -89,19 +96,19 @@ impl ClockConfig { div: 1, }, sys_clk: SysClkConfig { - src: SysClkSrc::Aux(ClkSysCtrlAuxsrc::ROSC_CLKSRC), + src: SysClkSrc::Rosc, div_int: 1, div_frac: 0, }, - peri_clk_src: Some(ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH), + peri_clk_src: Some(PeriClkSrc::Rosc), usb_clk: None, adc_clk: Some(AdcClkConfig { - src: ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH, + src: AdcClkSrc::Rosc, div: 1, phase: 0, }), rtc_clk: Some(RtcClkConfig { - src: ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH, + src: RtcClkSrc::Rosc, div_int: 1024, div_frac: 0, phase: 0, @@ -110,8 +117,18 @@ impl ClockConfig { } } +#[repr(u16)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RoscRange { + Low = pac::rosc::vals::FreqRange::LOW.0, + Medium = pac::rosc::vals::FreqRange::MEDIUM.0, + High = pac::rosc::vals::FreqRange::HIGH.0, + TooHigh = pac::rosc::vals::FreqRange::TOOHIGH.0, +} + pub struct RoscConfig { - pub range: pac::rosc::vals::FreqRange, + pub range: RoscRange, pub drive_strength: [u8; 8], pub div: u16, } @@ -134,15 +151,30 @@ pub struct RefClkConfig { pub div: u8, } +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RefClkSrc { + // main sources Xosc, Rosc, - Aux(ClkRefCtrlAuxsrc), + // aux sources + PllUsb, + Gpin0, + Gpin1, } +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SysClkSrc { + // main sources Ref, - Aux(ClkSysCtrlAuxsrc), + // aux sources + PllSys, + PllUsb, + Rosc, + Xosc, + Gpin0, + Gpin1, } pub struct SysClkConfig { @@ -151,20 +183,56 @@ pub struct SysClkConfig { pub div_frac: u8, } +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UsbClkSrc { + PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB.0, + PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS.0, + Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC.0, + Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0, + Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0, +} + pub struct UsbClkConfig { - pub src: ClkUsbCtrlAuxsrc, + pub src: UsbClkSrc, pub div: u8, pub phase: u8, } +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AdcClkSrc { + PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB.0, + PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS.0, + Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC.0, + Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0, + Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0, +} + pub struct AdcClkConfig { - pub src: ClkAdcCtrlAuxsrc, + pub src: AdcClkSrc, pub div: u8, pub phase: u8, } +#[repr(u8)] +#[non_exhaustive] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum RtcClkSrc { + PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB.0, + PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS.0, + Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH.0, + Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC.0, + Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0, + Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0, +} + pub struct RtcClkConfig { - pub src: ClkRtcCtrlAuxsrc, + pub src: RtcClkSrc, pub div_int: u32, pub div_frac: u8, pub phase: u8, @@ -229,27 +297,21 @@ pub(crate) unsafe fn init(config: ClockConfig) { } } - match config.ref_clk.src { - RefClkSrc::Xosc => { - c.clk_ref_ctrl().write(|w| { - w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC); - }); - while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::XOSC_CLKSRC.0 {} + let (ref_src, ref_aux) = { + use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; + match config.ref_clk.src { + RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB), + RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB), + RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB), + RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0), + RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1), } - RefClkSrc::Rosc => { - c.clk_ref_ctrl().write(|w| { - w.set_src(ClkRefCtrlSrc::ROSC_CLKSRC_PH); - }); - while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::ROSC_CLKSRC_PH.0 {} - } - RefClkSrc::Aux(src) => { - c.clk_ref_ctrl().write(|w| { - w.set_auxsrc(src); - w.set_src(ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX); - }); - while c.clk_ref_selected().read() != 1 << ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX.0 {} - } - } + }; + c.clk_ref_ctrl().write(|w| { + w.set_src(ref_src); + w.set_auxsrc(ref_aux); + }); + while c.clk_ref_selected().read() != 1 << ref_src.0 {} c.clk_ref_div().write(|w| { w.set_int(config.ref_clk.div); }); @@ -259,26 +321,27 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_enable(true); }); - match config.sys_clk.src { - SysClkSrc::Ref => { - c.clk_sys_ctrl().write(|w| { - w.set_src(ClkSysCtrlSrc::CLK_REF); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - } - SysClkSrc::Aux(src) => { - c.clk_sys_ctrl().write(|w| { - w.set_src(ClkSysCtrlSrc::CLK_REF); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} - - c.clk_sys_ctrl().write(|w| { - w.set_auxsrc(src); - w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX); - }); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX.0 {} + let (sys_src, sys_aux) = { + use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; + match config.sys_clk.src { + SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS), + SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS), + SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB), + SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC), + SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC), + SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0), + SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1), } + }; + if sys_src != ClkSysCtrlSrc::CLK_REF { + c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); + while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} } + c.clk_sys_ctrl().write(|w| { + w.set_auxsrc(sys_aux); + w.set_src(sys_src); + }); + while c.clk_sys_selected().read() != 1 << sys_src.0 {} c.clk_sys_div().write(|w| { w.set_int(config.sys_clk.div_int); w.set_frac(config.sys_clk.div_frac); @@ -289,7 +352,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { if let Some(src) = config.peri_clk_src { c.clk_peri_ctrl().write(|w| { w.set_enable(true); - w.set_auxsrc(src); + w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); }); } else { peris.set_spi0(false); @@ -304,7 +367,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_usb_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(conf.src); + w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); }); } else { peris.set_usbctrl(false); @@ -316,7 +379,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_adc_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(conf.src); + w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); }); } else { peris.set_adc(false); @@ -334,7 +397,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_rtc_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(conf.src); + w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); }); } else { peris.set_rtc(false); @@ -369,7 +432,7 @@ unsafe fn configure_rosc(config: RoscConfig) { p.ctrl().write(|w| { w.set_enable(pac::rosc::vals::Enable::ENABLE); - w.set_freq_range(config.range); + w.set_freq_range(pac::rosc::vals::FreqRange(config.range as u16)); }); } @@ -671,6 +734,21 @@ impl_gpoutpin!(PIN_23, 1); impl_gpoutpin!(PIN_24, 2); impl_gpoutpin!(PIN_25, 3); +#[repr(u8)] +pub enum GpoutSrc { + PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS.0, + Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0, + Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0, + PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB.0, + Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC.0, + Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC.0, + Sys = ClkGpoutCtrlAuxsrc::CLK_SYS.0, + Usb = ClkGpoutCtrlAuxsrc::CLK_USB.0, + Adc = ClkGpoutCtrlAuxsrc::CLK_ADC.0, + Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC.0, + Ref = ClkGpoutCtrlAuxsrc::CLK_REF.0, +} + pub struct Gpout<'d, T: GpoutPin> { gpout: PeripheralRef<'d, T>, } @@ -696,11 +774,11 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { } } - pub fn set_src(&self, src: ClkGpoutCtrlAuxsrc) { + pub fn set_src(&self, src: GpoutSrc) { unsafe { let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { - w.set_auxsrc(src); + w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _)); }); } } diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs index 236a653ac..64461fc5f 100644 --- a/examples/rp/src/bin/gpout.rs +++ b/examples/rp/src/bin/gpout.rs @@ -17,14 +17,14 @@ async fn main(_spawner: Spawner) { gpout3.enable(); loop { - gpout3.set_src(clocks::GpoutSrc::CLK_SYS); + gpout3.set_src(clocks::GpoutSrc::Sys); info!( "Pin 25 is now outputing CLK_SYS/1000, should be toggling at {}", gpout3.get_freq() ); Timer::after(Duration::from_secs(2)).await; - gpout3.set_src(clocks::GpoutSrc::CLK_REF); + gpout3.set_src(clocks::GpoutSrc::Ref); info!( "Pin 25 is now outputing CLK_REF/1000, should be toggling at {}", gpout3.get_freq() From 1379eb4e707e2b60b381a7288a586657dc539dfc Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 17 May 2023 02:46:38 +0200 Subject: [PATCH 1131/1575] rp/clocks: handle fractional gpout dividers --- embassy-rp/src/clocks.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 6d389cace..85d4d289b 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -821,11 +821,10 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { }; let div = unsafe { c.clk_gpout_div(self.gpout.number()).read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); + let int = if div.int() == 0 { 65536 } else { div.int() } as u64; + let frac = div.frac() as u64; - base / int + ((base as u64 * 256) / (int * 256 + frac)) as u32 } } From 0d4ab559a717cef0b9a86186142f403252f38a9b Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 13 May 2023 19:14:27 +0200 Subject: [PATCH 1132/1575] rp/clocks: fix comments and rosc defaults if rosc really does run at 140MHz in high at div=1 then these values were not correct and would've exceeded the chip spec. the HIL test device seems to run fast (150MHz) so they're still not quite correct, but rosc has high variance anyway so it's probably fine. --- embassy-rp/src/clocks.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 85d4d289b..2305a1e34 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -64,16 +64,19 @@ impl ClockConfig { div_frac: 0, }, peri_clk_src: Some(PeriClkSrc::Sys), + // CLK USB = PLL USB (48MHz) / 1 = 48MHz usb_clk: Some(UsbClkConfig { src: UsbClkSrc::PllUsb, div: 1, phase: 0, }), + // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz adc_clk: Some(AdcClkConfig { src: AdcClkSrc::PllUsb, div: 1, phase: 0, }), + // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz rtc_clk: Some(RtcClkConfig { src: RtcClkSrc::PllUsb, div_int: 1024, @@ -102,15 +105,17 @@ impl ClockConfig { }, peri_clk_src: Some(PeriClkSrc::Rosc), usb_clk: None, + // CLK ADC = ROSC (140MHz) / 3 ≅ 48MHz adc_clk: Some(AdcClkConfig { src: AdcClkSrc::Rosc, - div: 1, + div: 3, phase: 0, }), + // CLK RTC = ROSC (140MHz) / 2986.667969 ≅ 46875Hz rtc_clk: Some(RtcClkConfig { src: RtcClkSrc::Rosc, - div_int: 1024, - div_frac: 0, + div_int: 2986, + div_frac: 171, phase: 0, }), } @@ -362,7 +367,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { } if let Some(conf) = config.usb_clk { - // CLK USB = PLL USB (48MHz) / 1 = 48MHz c.clk_usb_div().write(|w| w.set_int(conf.div)); c.clk_usb_ctrl().write(|w| { w.set_phase(conf.phase); @@ -374,7 +378,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { } if let Some(conf) = config.adc_clk { - // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz c.clk_adc_div().write(|w| w.set_int(conf.div)); c.clk_adc_ctrl().write(|w| { w.set_phase(conf.phase); @@ -386,7 +389,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { } if let Some(conf) = config.rtc_clk { - // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz c.clk_rtc_ctrl().modify(|w| { w.set_enable(false); }); @@ -661,7 +663,7 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { // Wait for PLL to lock while !p.cs().read().lock() {} - // Wait for PLL to lock + // Set post-dividers p.prim().write(|w| { w.set_postdiv1(config.post_div1); w.set_postdiv2(config.post_div2); From f79d8cb2d3998662eb4555d424cc75f3899a3151 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 16 May 2023 22:45:50 +0200 Subject: [PATCH 1133/1575] rp/clocks: store clock frequencies in ram don't recalculate clock frequencies every time they are asked for. while this is not very often in practice it does consume a bunch of flash space that cannot be optimized away, and was pulled in unconditionally previously. while we technically only need the configured rosc, xosc and gpin frequencies it is easier to store all frequencies (and much cheaper at runtime too). --- embassy-rp/src/clocks.rs | 382 ++++++++++++++++++-------------------- embassy-rp/src/rtc/mod.rs | 7 +- 2 files changed, 183 insertions(+), 206 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 2305a1e34..9e581f105 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,10 +1,39 @@ +use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; + use embassy_hal_common::{into_ref, PeripheralRef}; use pac::clocks::vals::*; use crate::{pac, reset, Peripheral}; -// TODO fix terrible use of global here -static mut XIN_HZ: u32 = 0; +struct Clocks { + xosc: AtomicU32, + sys: AtomicU32, + reference: AtomicU32, + pll_sys: AtomicU32, + pll_usb: AtomicU32, + usb: AtomicU32, + adc: AtomicU32, + gpin0: AtomicU32, + gpin1: AtomicU32, + rosc: AtomicU32, + peri: AtomicU32, + rtc: AtomicU16, +} + +static CLOCKS: Clocks = Clocks { + xosc: AtomicU32::new(0), + sys: AtomicU32::new(0), + reference: AtomicU32::new(0), + pll_sys: AtomicU32::new(0), + pll_usb: AtomicU32::new(0), + usb: AtomicU32::new(0), + adc: AtomicU32::new(0), + gpin0: AtomicU32::new(0), + gpin1: AtomicU32::new(0), + rosc: AtomicU32::new(0), + peri: AtomicU32::new(0), + rtc: AtomicU16::new(0), +}; #[repr(u8)] #[non_exhaustive] @@ -29,12 +58,15 @@ pub struct ClockConfig { pub usb_clk: Option, pub adc_clk: Option, pub rtc_clk: Option, + pub gpin0_hz: Option, + pub gpin1_hz: Option, } impl ClockConfig { pub fn crystal(crystal_hz: u32) -> Self { Self { rosc: Some(RoscConfig { + hz: 6_500_000, range: RoscRange::Medium, drive_strength: [0; 8], div: 16, @@ -83,12 +115,15 @@ impl ClockConfig { div_frac: 0, phase: 0, }), + gpin0_hz: None, + gpin1_hz: None, } } pub fn rosc() -> Self { Self { rosc: Some(RoscConfig { + hz: 140_000_000, range: RoscRange::High, drive_strength: [0; 8], div: 1, @@ -118,6 +153,8 @@ impl ClockConfig { div_frac: 171, phase: 0, }), + gpin0_hz: None, + gpin1_hz: None, } } } @@ -133,6 +170,11 @@ pub enum RoscRange { } pub struct RoscConfig { + /// Final frequency of the oscillator, after the divider has been applied. + /// The oscillator has a nominal frequency of 6.5MHz at medium range with + /// divider 16 and all drive strengths set to 0, other values should be + /// measured in situ. + pub hz: u32, pub range: RoscRange, pub drive_strength: [u8; 8], pub div: u16, @@ -145,7 +187,7 @@ pub struct XoscConfig { } pub struct PllConfig { - pub refdiv: u32, + pub refdiv: u8, pub fbdiv: u16, pub post_div1: u8, pub post_div2: u8, @@ -277,41 +319,60 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::reset(peris); reset::unreset_wait(peris); - if let Some(config) = config.rosc { - configure_rosc(config); - } + let gpin0_freq = config.gpin0_hz.unwrap_or(0); + CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); + let gpin1_freq = config.gpin1_hz.unwrap_or(0); + CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); - if let Some(config) = config.xosc { - XIN_HZ = config.hz; + let rosc_freq = match config.rosc { + Some(config) => configure_rosc(config), + None => 0, + }; + CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); - pac::WATCHDOG.tick().write(|w| { - w.set_cycles((config.hz / 1_000_000) as u16); - w.set_enable(true); - }); + let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { + Some(config) => { + pac::WATCHDOG.tick().write(|w| { + w.set_cycles((config.hz / 1_000_000) as u16); + w.set_enable(true); + }); - // start XOSC - // datasheet mentions support for clock inputs into XIN, but doesn't go into - // how this is achieved. pico-sdk doesn't support this at all. - start_xosc(config.hz); + // start XOSC + // datasheet mentions support for clock inputs into XIN, but doesn't go into + // how this is achieved. pico-sdk doesn't support this at all. + start_xosc(config.hz); - if let Some(sys_pll_config) = config.sys_pll { - configure_pll(pac::PLL_SYS, config.hz, sys_pll_config); + let pll_sys_freq = match config.sys_pll { + Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config), + None => 0, + }; + let pll_usb_freq = match config.usb_pll { + Some(usb_pll_config) => configure_pll(pac::PLL_USB, config.hz, usb_pll_config), + None => 0, + }; + + (config.hz, pll_sys_freq, pll_usb_freq) } - if let Some(usb_pll_config) = config.usb_pll { - configure_pll(pac::PLL_USB, config.hz, usb_pll_config); - } - } + None => (0, 0, 0), + }; + CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); + CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); + CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); - let (ref_src, ref_aux) = { + let (ref_src, ref_aux, clk_ref_freq) = { use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; + let div = config.ref_clk.div as u32; + assert!(div >= 1 && div <= 4); match config.ref_clk.src { - RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB), - RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB), - RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB), - RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0), - RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1), + RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), + RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), + RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), + RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), + RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), } }; + assert!(clk_ref_freq != 0); + CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed); c.clk_ref_ctrl().write(|w| { w.set_src(ref_src); w.set_auxsrc(ref_aux); @@ -322,22 +383,27 @@ pub(crate) unsafe fn init(config: ClockConfig) { }); pac::WATCHDOG.tick().write(|w| { - w.set_cycles((clk_ref_freq() / 1_000_000) as u16); + w.set_cycles((clk_ref_freq / 1_000_000) as u16); w.set_enable(true); }); - let (sys_src, sys_aux) = { + let (sys_src, sys_aux, clk_sys_freq) = { use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; - match config.sys_clk.src { - SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS), - SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS), - SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB), - SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC), - SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC), - SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0), - SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1), - } + let (src, aux, freq) = match config.sys_clk.src { + SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), + SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), + SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), + SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), + SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), + SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), + SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), + }; + assert!(config.sys_clk.div_int <= 0x1000000); + let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64; + (src, aux, ((freq as u64 * 256) / div) as u32) }; + assert!(clk_sys_freq != 0); + CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); if sys_src != ClkSysCtrlSrc::CLK_REF { c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} @@ -359,11 +425,23 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_enable(true); w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); }); + let peri_freq = match src { + PeriClkSrc::Sys => clk_sys_freq, + PeriClkSrc::PllSys => pll_sys_freq, + PeriClkSrc::PllUsb => pll_usb_freq, + PeriClkSrc::Rosc => rosc_freq, + PeriClkSrc::Xosc => xosc_freq, + PeriClkSrc::Gpin0 => gpin0_freq, + PeriClkSrc::Gpin1 => gpin1_freq, + }; + assert!(peri_freq != 0); + CLOCKS.peri.store(peri_freq, Ordering::Relaxed); } else { peris.set_spi0(false); peris.set_spi1(false); peris.set_uart0(false); peris.set_uart1(false); + CLOCKS.peri.store(0, Ordering::Relaxed); } if let Some(conf) = config.usb_clk { @@ -373,8 +451,20 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_enable(true); w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); }); + let usb_freq = match conf.src { + UsbClkSrc::PllUsb => pll_usb_freq, + UsbClkSrc::PllSys => pll_sys_freq, + UsbClkSrc::Rosc => rosc_freq, + UsbClkSrc::Xosc => xosc_freq, + UsbClkSrc::Gpin0 => gpin0_freq, + UsbClkSrc::Gpin1 => gpin1_freq, + }; + assert!(usb_freq != 0); + assert!(conf.div >= 1 && conf.div <= 4); + CLOCKS.usb.store(usb_freq / conf.div as u32, Ordering::Relaxed); } else { peris.set_usbctrl(false); + CLOCKS.usb.store(0, Ordering::Relaxed); } if let Some(conf) = config.adc_clk { @@ -384,8 +474,20 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_enable(true); w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); }); + let adc_in_freq = match conf.src { + AdcClkSrc::PllUsb => pll_usb_freq, + AdcClkSrc::PllSys => pll_sys_freq, + AdcClkSrc::Rosc => rosc_freq, + AdcClkSrc::Xosc => xosc_freq, + AdcClkSrc::Gpin0 => gpin0_freq, + AdcClkSrc::Gpin1 => gpin1_freq, + }; + assert!(adc_in_freq != 0); + assert!(conf.div >= 1 && conf.div <= 4); + CLOCKS.adc.store(adc_in_freq / conf.div as u32, Ordering::Relaxed); } else { peris.set_adc(false); + CLOCKS.adc.store(0, Ordering::Relaxed); } if let Some(conf) = config.rtc_clk { @@ -401,15 +503,30 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_enable(true); w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); }); + let rtc_in_freq = match conf.src { + RtcClkSrc::PllUsb => pll_usb_freq, + RtcClkSrc::PllSys => pll_sys_freq, + RtcClkSrc::Rosc => rosc_freq, + RtcClkSrc::Xosc => xosc_freq, + RtcClkSrc::Gpin0 => gpin0_freq, + RtcClkSrc::Gpin1 => gpin1_freq, + }; + assert!(rtc_in_freq != 0); + assert!(config.sys_clk.div_int <= 0x1000000); + CLOCKS.rtc.store( + ((rtc_in_freq as u64 * 256) / (conf.div_int as u64 * 256 + conf.div_frac as u64)) as u16, + Ordering::Relaxed, + ); } else { peris.set_rtc(false); + CLOCKS.rtc.store(0, Ordering::Relaxed); } // Peripheral clocks should now all be running reset::unreset_wait(peris); } -unsafe fn configure_rosc(config: RoscConfig) { +unsafe fn configure_rosc(config: RoscConfig) -> u32 { let p = pac::ROSC; p.freqa().write(|w| { @@ -436,193 +553,55 @@ unsafe fn configure_rosc(config: RoscConfig) { w.set_enable(pac::rosc::vals::Enable::ENABLE); w.set_freq_range(pac::rosc::vals::FreqRange(config.range as u16)); }); + + config.hz } -pub fn estimate_rosc_freq() -> u32 { - let p = pac::ROSC; - - let base = match unsafe { p.ctrl().read().freq_range() } { - pac::rosc::vals::FreqRange::LOW => 84_000_000, - pac::rosc::vals::FreqRange::MEDIUM => 104_000_000, - pac::rosc::vals::FreqRange::HIGH => 140_000_000, - pac::rosc::vals::FreqRange::TOOHIGH => 208_000_000, - _ => unreachable!(), - }; - let mut div = unsafe { p.div().read().0 - pac::rosc::vals::Div::PASS.0 as u32 }; - if div == 0 { - div = 32 - } - - base / div +pub fn rosc_freq() -> u32 { + CLOCKS.rosc.load(Ordering::Relaxed) } pub fn xosc_freq() -> u32 { - unsafe { XIN_HZ } + CLOCKS.xosc.load(Ordering::Relaxed) } pub fn gpin0_freq() -> u32 { - todo!() + CLOCKS.gpin0.load(Ordering::Relaxed) } pub fn gpin1_freq() -> u32 { - todo!() + CLOCKS.gpin1.load(Ordering::Relaxed) } pub fn pll_sys_freq() -> u32 { - let p = pac::PLL_SYS; - - let input_freq = xosc_freq(); - let cs = unsafe { p.cs().read() }; - - let refdiv = cs.refdiv() as u32; - let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; - let (postdiv1, postdiv2) = unsafe { - let prim = p.prim().read(); - (prim.postdiv1() as u32, prim.postdiv2() as u32) - }; - - (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 + CLOCKS.pll_sys.load(Ordering::Relaxed) } pub fn pll_usb_freq() -> u32 { - let p = pac::PLL_USB; - - let input_freq = xosc_freq(); - let cs = unsafe { p.cs().read() }; - - let refdiv = cs.refdiv() as u32; - let fbdiv = unsafe { p.fbdiv_int().read().fbdiv_int() } as u32; - let (postdiv1, postdiv2) = unsafe { - let prim = p.prim().read(); - (prim.postdiv1() as u32, prim.postdiv2() as u32) - }; - - (((input_freq / refdiv) * fbdiv) / postdiv1) / postdiv2 + CLOCKS.pll_usb.load(Ordering::Relaxed) } pub fn clk_sys_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_sys_ctrl().read() }; - - let base = match ctrl.src() { - ClkSysCtrlSrc::CLK_REF => clk_ref_freq(), - ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX => match ctrl.auxsrc() { - ClkSysCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkSysCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkSysCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), - ClkSysCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkSysCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkSysCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }, - _ => unreachable!(), - }; - - let div = unsafe { c.clk_sys_div().read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); - - base / int + CLOCKS.sys.load(Ordering::Relaxed) } pub fn clk_ref_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_ref_ctrl().read() }; - - let base = match ctrl.src() { - ClkRefCtrlSrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkRefCtrlSrc::XOSC_CLKSRC => xosc_freq(), - ClkRefCtrlSrc::CLKSRC_CLK_REF_AUX => match ctrl.auxsrc() { - ClkRefCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkRefCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkRefCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }, - _ => unreachable!(), - }; - - let div = unsafe { c.clk_ref_div().read() }; - let int = if div.int() == 0 { 4 } else { div.int() as u32 }; - - base / int + CLOCKS.reference.load(Ordering::Relaxed) } pub fn clk_peri_freq() -> u32 { - let c = pac::CLOCKS; - let src = unsafe { c.clk_peri_ctrl().read().auxsrc() }; - - match src { - ClkPeriCtrlAuxsrc::CLK_SYS => clk_sys_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkPeriCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - } + CLOCKS.peri.load(Ordering::Relaxed) } pub fn clk_usb_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_usb_ctrl().read() }; - - let base = match ctrl.auxsrc() { - ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkUsbCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_ref_div().read() }; - let int = if div.int() == 0 { 4 } else { div.int() as u32 }; - - base / int + CLOCKS.usb.load(Ordering::Relaxed) } pub fn clk_adc_freq() -> u32 { - let c = pac::CLOCKS; - let ctrl = unsafe { c.clk_adc_ctrl().read() }; - - let base = match ctrl.auxsrc() { - ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkAdcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_adc_div().read() }; - let int = if div.int() == 0 { 4 } else { div.int() as u32 }; - - base / int + CLOCKS.adc.load(Ordering::Relaxed) } -pub fn clk_rtc_freq() -> u32 { - let c = pac::CLOCKS; - let src = unsafe { c.clk_rtc_ctrl().read().auxsrc() }; - - let base = match src { - ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH => estimate_rosc_freq(), - ClkRtcCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), - _ => unreachable!(), - }; - - let div = unsafe { c.clk_rtc_div().read() }; - let int = if div.int() == 0 { 65536 } else { div.int() }; - // TODO handle fractional clock div - let _frac = div.frac(); - - base / int +pub fn clk_rtc_freq() -> u16 { + CLOCKS.rtc.load(Ordering::Relaxed) } unsafe fn start_xosc(crystal_hz: u32) { @@ -640,14 +619,15 @@ unsafe fn start_xosc(crystal_hz: u32) { } #[inline(always)] -unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { - let ref_freq = input_freq / config.refdiv; - +unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { + let ref_freq = input_freq / config.refdiv as u32; assert!(config.fbdiv >= 16 && config.fbdiv <= 320); assert!(config.post_div1 >= 1 && config.post_div1 <= 7); assert!(config.post_div2 >= 1 && config.post_div2 <= 7); - assert!(config.post_div2 <= config.post_div1); + assert!(config.refdiv >= 1 && config.refdiv <= 63); assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); + let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); + assert!(vco_freq >= 750_000_000 && vco_freq <= 1800_000_000); // Load VCO-related dividers before starting VCO p.cs().write(|w| w.set_refdiv(config.refdiv as _)); @@ -671,6 +651,8 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) { // Turn on post divider p.pwr().modify(|w| w.set_postdivpd(false)); + + vco_freq / ((config.post_div1 * config.post_div2) as u32) } pub trait GpinPin: crate::gpio::Pin { @@ -812,12 +794,12 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), - ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => estimate_rosc_freq(), + ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => rosc_freq(), ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), ClkGpoutCtrlAuxsrc::CLK_SYS => clk_sys_freq(), ClkGpoutCtrlAuxsrc::CLK_USB => clk_usb_freq(), ClkGpoutCtrlAuxsrc::CLK_ADC => clk_adc_freq(), - ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq(), + ClkGpoutCtrlAuxsrc::CLK_RTC => clk_rtc_freq() as _, ClkGpoutCtrlAuxsrc::CLK_REF => clk_ref_freq(), _ => unreachable!(), }; diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index c173909c7..c213ad174 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -26,12 +26,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { into_ref!(inner); // Set the RTC divider - unsafe { - inner - .regs() - .clkdiv_m1() - .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1)) - }; + unsafe { inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)) }; let mut result = Self { inner }; result.set_leap_year_check(true); // should be on by default, make sure this is the case. From 1b3d9a0aeffd9e0619126c3b2dc42520cc2b4209 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 17 May 2023 02:25:28 +0200 Subject: [PATCH 1134/1575] rp/clocks: compactify pll setup we don't need to preserve existing bits of the pll pwr register, so let's only write and save a few instructions. --- embassy-rp/src/clocks.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 9e581f105..cfc94f844 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -634,10 +634,12 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); // Turn on PLL - p.pwr().modify(|w| { + let pwr = p.pwr().write(|w| { + w.set_dsmpd(true); // "nothing is achieved by setting this low" w.set_pd(false); w.set_vcopd(false); w.set_postdivpd(true); + *w }); // Wait for PLL to lock @@ -650,7 +652,10 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> }); // Turn on post divider - p.pwr().modify(|w| w.set_postdivpd(false)); + p.pwr().write(|w| { + *w = pwr; + w.set_postdivpd(false); + }); vco_freq / ((config.post_div1 * config.post_div2) as u32) } From 053d5629ba88a67f4b126bd7ac4b478fbe00d17f Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 17 May 2023 02:52:29 +0200 Subject: [PATCH 1135/1575] rp/clocks: require GpinPin for gpin config we'll take static ownership of an entire pin (not just a limited reference), otherwise we cannot at all guarantee that the pin will not be reused for something else while still in use. in theory we could limit the liftime of this use, but that would require attaching lifetimes to ClockConfig (and subsequently the main config), passing those through init(), and returning an object that undoes the gpin configuration on drop. that's a lot unnecessary support code while we don't have runtime clock reconfig. --- embassy-rp/src/clocks.rs | 61 ++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index cfc94f844..7ab0ecd11 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,8 +1,11 @@ +use core::marker::PhantomData; use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; use embassy_hal_common::{into_ref, PeripheralRef}; use pac::clocks::vals::*; +use crate::gpio::sealed::Pin; +use crate::gpio::AnyPin; use crate::{pac, reset, Peripheral}; struct Clocks { @@ -58,8 +61,8 @@ pub struct ClockConfig { pub usb_clk: Option, pub adc_clk: Option, pub rtc_clk: Option, - pub gpin0_hz: Option, - pub gpin1_hz: Option, + gpin0: Option<(u32, Gpin<'static, AnyPin>)>, + gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } impl ClockConfig { @@ -115,8 +118,8 @@ impl ClockConfig { div_frac: 0, phase: 0, }), - gpin0_hz: None, - gpin1_hz: None, + gpin0: None, + gpin1: None, } } @@ -153,10 +156,20 @@ impl ClockConfig { div_frac: 171, phase: 0, }), - gpin0_hz: None, - gpin1_hz: None, + gpin0: None, + gpin1: None, } } + + pub fn bind_gpin(&mut self, gpin: Gpin<'static, P>, hz: u32) { + match P::NR { + 0 => self.gpin0 = Some((hz, gpin.map_into())), + 1 => self.gpin1 = Some((hz, gpin.map_into())), + _ => unreachable!(), + } + // pin is now provisionally bound. if the config is applied it must be forgotten, + // or Gpin::drop will deconfigure the clock input. + } } #[repr(u16)] @@ -319,9 +332,15 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::reset(peris); reset::unreset_wait(peris); - let gpin0_freq = config.gpin0_hz.unwrap_or(0); + let gpin0_freq = config.gpin0.map_or(0, |p| { + core::mem::forget(p.1); + p.0 + }); CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); - let gpin1_freq = config.gpin1_hz.unwrap_or(0); + let gpin1_freq = config.gpin1.map_or(0, |p| { + core::mem::forget(p.1); + p.0 + }); CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); let rosc_freq = match config.rosc { @@ -661,15 +680,13 @@ unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> } pub trait GpinPin: crate::gpio::Pin { - fn number(&self) -> usize; + const NR: usize; } macro_rules! impl_gpinpin { ($name:ident, $pin_num:expr, $gpin_num:expr) => { impl GpinPin for crate::peripherals::$name { - fn number(&self) -> usize { - $gpin_num - } + const NR: usize = $gpin_num; } }; } @@ -677,23 +694,31 @@ macro_rules! impl_gpinpin { impl_gpinpin!(PIN_20, 20, 0); impl_gpinpin!(PIN_22, 22, 1); -pub struct Gpin<'d, T: GpinPin> { - gpin: PeripheralRef<'d, T>, +pub struct Gpin<'d, T: Pin> { + gpin: PeripheralRef<'d, AnyPin>, + _phantom: PhantomData, } -impl<'d, T: GpinPin> Gpin<'d, T> { - pub fn new(gpin: impl Peripheral

+ 'd) -> Self { +impl<'d, T: Pin> Gpin<'d, T> { + pub fn new(gpin: impl Peripheral

+ 'd) -> Gpin<'d, P> { into_ref!(gpin); unsafe { gpin.io().ctrl().write(|w| w.set_funcsel(0x08)); } - Self { gpin } + Gpin { + gpin: gpin.map_into(), + _phantom: PhantomData, + } + } + + fn map_into(self) -> Gpin<'d, AnyPin> { + unsafe { core::mem::transmute(self) } } } -impl<'d, T: GpinPin> Drop for Gpin<'d, T> { +impl<'d, T: Pin> Drop for Gpin<'d, T> { fn drop(&mut self) { unsafe { self.gpin From 1e029a9e66f0a9621c2911e4147bb7a838190577 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 17 May 2023 03:21:07 +0200 Subject: [PATCH 1136/1575] rp/clocks: remove superfluous clock actions the rtc doesn't have to be disabled since it's always clocked from ref, and the watchdog doesn't need to be configured twice. --- embassy-rp/src/clocks.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 7ab0ecd11..c70436695 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -351,11 +351,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { Some(config) => { - pac::WATCHDOG.tick().write(|w| { - w.set_cycles((config.hz / 1_000_000) as u16); - w.set_enable(true); - }); - // start XOSC // datasheet mentions support for clock inputs into XIN, but doesn't go into // how this is achieved. pico-sdk doesn't support this at all. @@ -510,9 +505,6 @@ pub(crate) unsafe fn init(config: ClockConfig) { } if let Some(conf) = config.rtc_clk { - c.clk_rtc_ctrl().modify(|w| { - w.set_enable(false); - }); c.clk_rtc_div().write(|w| { w.set_int(conf.div_int); w.set_frac(conf.div_frac); From fc746a88b50f2d57a573d7a9ec8b8f3fbc0c9b08 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 17 May 2023 23:25:49 +0200 Subject: [PATCH 1137/1575] rp/clocks: comment out all gpin handling for now gpin clock sources aren't going to be very useful during cold boot and thus require runtime clock reconfig. once we get there we can use this for reference. or maybe we can't, only time will tell. --- embassy-rp/src/clocks.rs | 137 ++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index c70436695..67439fda3 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -8,6 +8,10 @@ use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, reset, Peripheral}; +// NOTE: all gpin handling is commented out for future reference. +// gpin is not usually safe to use during the boot init() call, so it won't +// be very useful until we have runtime clock reconfiguration. once this +// happens we can resurrect the commented-out gpin bits. struct Clocks { xosc: AtomicU32, sys: AtomicU32, @@ -16,8 +20,8 @@ struct Clocks { pll_usb: AtomicU32, usb: AtomicU32, adc: AtomicU32, - gpin0: AtomicU32, - gpin1: AtomicU32, + // gpin0: AtomicU32, + // gpin1: AtomicU32, rosc: AtomicU32, peri: AtomicU32, rtc: AtomicU16, @@ -31,8 +35,8 @@ static CLOCKS: Clocks = Clocks { pll_usb: AtomicU32::new(0), usb: AtomicU32::new(0), adc: AtomicU32::new(0), - gpin0: AtomicU32::new(0), - gpin1: AtomicU32::new(0), + // gpin0: AtomicU32::new(0), + // gpin1: AtomicU32::new(0), rosc: AtomicU32::new(0), peri: AtomicU32::new(0), rtc: AtomicU16::new(0), @@ -47,8 +51,8 @@ pub enum PeriClkSrc { PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB.0, Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH.0, Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC.0, - Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0, - Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0, + // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0, } #[non_exhaustive] @@ -61,8 +65,8 @@ pub struct ClockConfig { pub usb_clk: Option, pub adc_clk: Option, pub rtc_clk: Option, - gpin0: Option<(u32, Gpin<'static, AnyPin>)>, - gpin1: Option<(u32, Gpin<'static, AnyPin>)>, + // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, + // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, } impl ClockConfig { @@ -118,8 +122,8 @@ impl ClockConfig { div_frac: 0, phase: 0, }), - gpin0: None, - gpin1: None, + // gpin0: None, + // gpin1: None, } } @@ -156,20 +160,20 @@ impl ClockConfig { div_frac: 171, phase: 0, }), - gpin0: None, - gpin1: None, + // gpin0: None, + // gpin1: None, } } - pub fn bind_gpin(&mut self, gpin: Gpin<'static, P>, hz: u32) { - match P::NR { - 0 => self.gpin0 = Some((hz, gpin.map_into())), - 1 => self.gpin1 = Some((hz, gpin.map_into())), - _ => unreachable!(), - } - // pin is now provisionally bound. if the config is applied it must be forgotten, - // or Gpin::drop will deconfigure the clock input. - } + // pub fn bind_gpin(&mut self, gpin: Gpin<'static, P>, hz: u32) { + // match P::NR { + // 0 => self.gpin0 = Some((hz, gpin.map_into())), + // 1 => self.gpin1 = Some((hz, gpin.map_into())), + // _ => unreachable!(), + // } + // // pin is now provisionally bound. if the config is applied it must be forgotten, + // // or Gpin::drop will deconfigure the clock input. + // } } #[repr(u16)] @@ -219,8 +223,8 @@ pub enum RefClkSrc { Rosc, // aux sources PllUsb, - Gpin0, - Gpin1, + // Gpin0, + // Gpin1, } #[non_exhaustive] @@ -233,8 +237,8 @@ pub enum SysClkSrc { PllUsb, Rosc, Xosc, - Gpin0, - Gpin1, + // Gpin0, + // Gpin1, } pub struct SysClkConfig { @@ -251,8 +255,8 @@ pub enum UsbClkSrc { PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS.0, Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH.0, Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC.0, - Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0, - Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0, + // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0, } pub struct UsbClkConfig { @@ -269,8 +273,8 @@ pub enum AdcClkSrc { PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS.0, Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH.0, Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC.0, - Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0, - Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0, + // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0, } pub struct AdcClkConfig { @@ -287,8 +291,8 @@ pub enum RtcClkSrc { PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS.0, Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH.0, Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC.0, - Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0, - Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0, + // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0, } pub struct RtcClkConfig { @@ -306,6 +310,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // - USB, SYSCFG (breaks usb-to-swd on core1) let mut peris = reset::ALL_PERIPHERALS; peris.set_io_qspi(false); + // peris.set_io_bank0(false); // might be suicide if we're clocked from gpin peris.set_pads_qspi(false); peris.set_pll_sys(false); peris.set_pll_usb(false); @@ -332,16 +337,16 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::reset(peris); reset::unreset_wait(peris); - let gpin0_freq = config.gpin0.map_or(0, |p| { - core::mem::forget(p.1); - p.0 - }); - CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); - let gpin1_freq = config.gpin1.map_or(0, |p| { - core::mem::forget(p.1); - p.0 - }); - CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); + // let gpin0_freq = config.gpin0.map_or(0, |p| { + // core::mem::forget(p.1); + // p.0 + // }); + // CLOCKS.gpin0.store(gpin0_freq, Ordering::Relaxed); + // let gpin1_freq = config.gpin1.map_or(0, |p| { + // core::mem::forget(p.1); + // p.0 + // }); + // CLOCKS.gpin1.store(gpin1_freq, Ordering::Relaxed); let rosc_freq = match config.rosc { Some(config) => configure_rosc(config), @@ -381,8 +386,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), - RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), - RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), + // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), + // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), } }; assert!(clk_ref_freq != 0); @@ -409,8 +414,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), - SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), - SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), + // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), + // SysClkSrc::Gpin1 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN1, gpin1_freq), }; assert!(config.sys_clk.div_int <= 0x1000000); let div = config.sys_clk.div_int as u64 * 256 + config.sys_clk.div_frac as u64; @@ -445,8 +450,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { PeriClkSrc::PllUsb => pll_usb_freq, PeriClkSrc::Rosc => rosc_freq, PeriClkSrc::Xosc => xosc_freq, - PeriClkSrc::Gpin0 => gpin0_freq, - PeriClkSrc::Gpin1 => gpin1_freq, + // PeriClkSrc::Gpin0 => gpin0_freq, + // PeriClkSrc::Gpin1 => gpin1_freq, }; assert!(peri_freq != 0); CLOCKS.peri.store(peri_freq, Ordering::Relaxed); @@ -470,8 +475,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { UsbClkSrc::PllSys => pll_sys_freq, UsbClkSrc::Rosc => rosc_freq, UsbClkSrc::Xosc => xosc_freq, - UsbClkSrc::Gpin0 => gpin0_freq, - UsbClkSrc::Gpin1 => gpin1_freq, + // UsbClkSrc::Gpin0 => gpin0_freq, + // UsbClkSrc::Gpin1 => gpin1_freq, }; assert!(usb_freq != 0); assert!(conf.div >= 1 && conf.div <= 4); @@ -493,8 +498,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { AdcClkSrc::PllSys => pll_sys_freq, AdcClkSrc::Rosc => rosc_freq, AdcClkSrc::Xosc => xosc_freq, - AdcClkSrc::Gpin0 => gpin0_freq, - AdcClkSrc::Gpin1 => gpin1_freq, + // AdcClkSrc::Gpin0 => gpin0_freq, + // AdcClkSrc::Gpin1 => gpin1_freq, }; assert!(adc_in_freq != 0); assert!(conf.div >= 1 && conf.div <= 4); @@ -519,8 +524,8 @@ pub(crate) unsafe fn init(config: ClockConfig) { RtcClkSrc::PllSys => pll_sys_freq, RtcClkSrc::Rosc => rosc_freq, RtcClkSrc::Xosc => xosc_freq, - RtcClkSrc::Gpin0 => gpin0_freq, - RtcClkSrc::Gpin1 => gpin1_freq, + // RtcClkSrc::Gpin0 => gpin0_freq, + // RtcClkSrc::Gpin1 => gpin1_freq, }; assert!(rtc_in_freq != 0); assert!(config.sys_clk.div_int <= 0x1000000); @@ -576,12 +581,12 @@ pub fn xosc_freq() -> u32 { CLOCKS.xosc.load(Ordering::Relaxed) } -pub fn gpin0_freq() -> u32 { - CLOCKS.gpin0.load(Ordering::Relaxed) -} -pub fn gpin1_freq() -> u32 { - CLOCKS.gpin1.load(Ordering::Relaxed) -} +// pub fn gpin0_freq() -> u32 { +// CLOCKS.gpin0.load(Ordering::Relaxed) +// } +// pub fn gpin1_freq() -> u32 { +// CLOCKS.gpin1.load(Ordering::Relaxed) +// } pub fn pll_sys_freq() -> u32 { CLOCKS.pll_sys.load(Ordering::Relaxed) @@ -705,9 +710,9 @@ impl<'d, T: Pin> Gpin<'d, T> { } } - fn map_into(self) -> Gpin<'d, AnyPin> { - unsafe { core::mem::transmute(self) } - } + // fn map_into(self) -> Gpin<'d, AnyPin> { + // unsafe { core::mem::transmute(self) } + // } } impl<'d, T: Pin> Drop for Gpin<'d, T> { @@ -743,8 +748,8 @@ impl_gpoutpin!(PIN_25, 3); #[repr(u8)] pub enum GpoutSrc { PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS.0, - Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0, - Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0, + // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0, + // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0, PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB.0, Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC.0, Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC.0, @@ -813,8 +818,8 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { let base = match src { ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), - ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), - ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), + // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 => gpin0_freq(), + // ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 => gpin1_freq(), ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB => pll_usb_freq(), ClkGpoutCtrlAuxsrc::ROSC_CLKSRC => rosc_freq(), ClkGpoutCtrlAuxsrc::XOSC_CLKSRC => xosc_freq(), From f43d57846e010459b8eafcf16ec826cbbf8669e9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 19 May 2023 15:20:37 +0200 Subject: [PATCH 1138/1575] stm32/usb: do not require embassy-time. Fixes #1466 --- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/usb/usb.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1b6f83c3e..3283c4062 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -58,7 +58,7 @@ pub mod spi; pub mod tl_mbox; #[cfg(usart)] pub mod usart; -#[cfg(all(usb, feature = "time"))] +#[cfg(usb)] pub mod usb; #[cfg(otg)] pub mod usb_otg; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index ad68eaba2..56c46476c 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -7,7 +7,6 @@ use core::task::Poll; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; -use embassy_time::{block_for, Duration}; use embassy_usb_driver as driver; use embassy_usb_driver::{ Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, @@ -200,7 +199,10 @@ impl<'d, T: Instance> Driver<'d, T> { w.set_fres(true); }); - block_for(Duration::from_millis(100)); + #[cfg(time)] + embassy_time::block_for(embassy_time::Duration::from_millis(100)); + #[cfg(not(time))] + cortex_m::asm::delay(crate::rcc::get_freqs().sys.0 / 10); #[cfg(not(usb_v4))] regs.btable().write(|w| w.set_btable(0)); From a521a9b5ce78be01ed626c66bfad5e6fd9b11d32 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 19 May 2023 15:22:03 +0200 Subject: [PATCH 1139/1575] stm32: test more feature combinations in ci. --- ci.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ci.sh b/ci.sh index 4b7d73711..d2bf4df97 100755 --- a/ci.sh +++ b/ci.sh @@ -46,6 +46,20 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \ From d736c9205c1cfff2b4f5e83fd7d70a6e1b09a08d Mon Sep 17 00:00:00 2001 From: goueslati Date: Fri, 19 May 2023 15:40:09 +0100 Subject: [PATCH 1140/1575] updated test case --- tests/stm32/src/bin/ble.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/ble.rs index f4c864c65..0f711fdac 100644 --- a/tests/stm32/src/bin/ble.rs +++ b/tests/stm32/src/bin/ble.rs @@ -20,7 +20,13 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mut ipcc = Ipcc::new(p.IPCC, config); - let mbox = TlMbox::init(&mut ipcc); + let config = Config::default(); + let mut ipcc = Ipcc::new(p.IPCC, config); + + let rx_irq = interrupt::take!(IPCC_C1_RX); + let tx_irq = interrupt::take!(IPCC_C1_TX); + + let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq); loop { let wireless_fw_info = mbox.wireless_fw_info(); From 9f7392474b6a6e3a2f20e6419743afb196456c66 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 19 May 2023 17:12:29 +0200 Subject: [PATCH 1141/1575] Update Rust nightly. --- embassy-boot/boot/src/lib.rs | 1 - embassy-embedded-hal/src/lib.rs | 1 - embassy-lora/src/lib.rs | 1 - embassy-net/src/lib.rs | 1 - embassy-nrf/src/lib.rs | 1 - embassy-rp/src/lib.rs | 1 - embassy-stm32/src/lib.rs | 1 - embassy-sync/src/lib.rs | 1 - embassy-time/src/lib.rs | 1 - embassy-usb-driver/src/lib.rs | 1 - examples/stm32wl/src/bin/lora_lorawan.rs | 1 - examples/stm32wl/src/bin/lora_p2p_receive.rs | 1 - examples/stm32wl/src/bin/lora_p2p_send.rs | 1 - examples/wasm/src/lib.rs | 1 - rust-toolchain.toml | 2 +- 15 files changed, 1 insertion(+), 15 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 8eb3ba96d..4521fecb0 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,5 +1,4 @@ #![cfg_attr(feature = "nightly", feature(async_fn_in_trait))] -#![allow(incomplete_features)] #![no_std] #![warn(missing_docs)] #![doc = include_str!("../README.md")] diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index a783151e4..73c81b465 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))] -#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![warn(missing_docs)] //! Utilities to use `embedded-hal` traits with Embassy. diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 3facee6f3..c23d1d0dd 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(async_fn_in_trait, impl_trait_projections)] -#![allow(incomplete_features)] //! embassy-lora holds LoRa-specific functionality. pub(crate) mod fmt; diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 9487c0913..bccbad521 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] -#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 7c60b32b2..d4d7a1cad 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] -#![cfg_attr(feature = "nightly", allow(incomplete_features))] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 980ebe7f4..4e4542d70 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![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; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 3283c4062..1920e2642 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![cfg_attr(feature = "nightly", feature(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; diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index f9435ecff..53d95d081 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] #![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-time/src/lib.rs b/embassy-time/src/lib.rs index 8b0aebe19..8f57eabcb 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,6 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] #![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 2c05f94f7..86e37595b 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(async_fn_in_trait)] -#![allow(incomplete_features)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 644ce2959..1a271b2f2 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -4,7 +4,6 @@ #![no_main] #![macro_use] #![feature(type_alias_impl_trait, async_fn_in_trait)] -#![allow(incomplete_features)] use defmt::info; use embassy_executor::Spawner; diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs index 81e9c7057..5e80e8f6a 100644 --- a/examples/stm32wl/src/bin/lora_p2p_receive.rs +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs @@ -4,7 +4,6 @@ #![no_main] #![macro_use] #![feature(type_alias_impl_trait, async_fn_in_trait)] -#![allow(incomplete_features)] use defmt::info; use embassy_executor::Spawner; diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs index 263d4e670..e22c714bd 100644 --- a/examples/stm32wl/src/bin/lora_p2p_send.rs +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -4,7 +4,6 @@ #![no_main] #![macro_use] #![feature(type_alias_impl_trait, async_fn_in_trait)] -#![allow(incomplete_features)] use defmt::info; use embassy_executor::Spawner; diff --git a/examples/wasm/src/lib.rs b/examples/wasm/src/lib.rs index d44c020b6..edfe8bafc 100644 --- a/examples/wasm/src/lib.rs +++ b/examples/wasm/src/lib.rs @@ -1,5 +1,4 @@ #![feature(type_alias_impl_trait)] -#![allow(incomplete_features)] use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2301ddc8d..fd454db26 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-2023-04-18" +channel = "nightly-2023-05-18" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From df56f901de7aaf5fe77421a3087f64af7b7fc961 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 19 May 2023 17:38:57 +0200 Subject: [PATCH 1142/1575] time: fix unused mut. --- embassy-time/src/queue_generic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 0f67d9dbb..64a8af4bc 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -64,7 +64,7 @@ impl InnerQueue { self.queue .iter_mut() .find(|timer| timer.waker.will_wake(waker)) - .map(|mut timer| { + .map(|timer| { timer.at = min(timer.at, at); }) .unwrap_or_else(|| { From 8b9306ed5ccc932fa91931b0c6bec6fc59694bd8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 19 May 2023 18:00:33 +0200 Subject: [PATCH 1143/1575] stm32/sdmmc: fix "drop with a value that implements `Copy` does nothing" warning. --- embassy-stm32/src/sdmmc/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 433f73d79..be788f1b0 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -188,7 +188,9 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { #[cfg(sdmmc_v1)] type Transfer<'a, C> = crate::dma::Transfer<'a, C>; #[cfg(sdmmc_v2)] -type Transfer<'a, C> = core::marker::PhantomData<&'a mut C>; +struct Transfer<'a, C> { + _dummy: core::marker::PhantomData<&'a mut C>, +} #[cfg(all(sdmmc_v1, dma))] const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { @@ -539,7 +541,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let transfer = { regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); regs.idmactrlr().modify(|w| w.set_idmaen(true)); - core::marker::PhantomData + Transfer { + _dummy: core::marker::PhantomData, + } }; regs.dctrl().modify(|w| { @@ -593,7 +597,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let transfer = { regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32)); regs.idmactrlr().modify(|w| w.set_idmaen(true)); - core::marker::PhantomData + Transfer { + _dummy: core::marker::PhantomData, + } }; regs.dctrl().modify(|w| { From a1cbdd8d295f9fd816a65fa7ca6df67ac5a25ddf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 19 May 2023 18:25:34 +0200 Subject: [PATCH 1144/1575] nrf/uarte: fix deref warning, fix errata not being applied on nrf53. --- embassy-nrf/src/uarte.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 586c88b2d..032089635 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -791,20 +791,18 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { } } -#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))] +#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { // Do nothing } -#[cfg(any(feature = "_nrf9160", feature = "nrf5340"))] +#[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::RegisterBlock) { - use core::ops::Deref; - // Apply workaround for anomalies: // - nRF9160 - anomaly 23 // - nRF5340 - anomaly 44 - let rxenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x564) as *const u32; - let txenable_reg: *const u32 = ((r.deref() as *const _ as usize) + 0x568) as *const u32; + let rxenable_reg: *const u32 = ((r as *const _ as usize) + 0x564) as *const u32; + let txenable_reg: *const u32 = ((r as *const _ as usize) + 0x568) as *const u32; // NB Safety: This is taken from Nordic's driver - // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197 From 1be6e533165cb773979739c203b745df24ff1ecf Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 9 May 2023 17:05:39 -0400 Subject: [PATCH 1145/1575] Pin fix, improve fifo handling --- examples/rp/src/bin/{ws2812-pio.rs => pio_ws2812.rs} | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) rename examples/rp/src/bin/{ws2812-pio.rs => pio_ws2812.rs} (95%) diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/pio_ws2812.rs similarity index 95% rename from examples/rp/src/bin/ws2812-pio.rs rename to examples/rp/src/bin/pio_ws2812.rs index f4c2d6313..4915c7e92 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -48,6 +48,8 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { // Pin config let out_pin = pio.make_pio_pin(pin); + cfg.set_out_pins(&[&out_pin]); + cfg.set_set_pins(&[&out_pin]); let relocated = RelocatedProgram::new(&prg); cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); @@ -76,7 +78,9 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { pub async fn write(&mut self, colors: &[RGB8]) { for color in colors { let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); - self.sm.tx().wait_push(word).await; + if !self.sm.tx().try_push(word) { + self.sm.tx().wait_push(word).await; + } } } } From 1ebb742fbf9839ab393c038f320003a9346d37ff Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Fri, 19 May 2023 16:48:47 -0400 Subject: [PATCH 1146/1575] Switch to DMA, use new clocks, don't take ownership of pio common --- examples/rp/src/bin/pio_ws2812.rs | 44 ++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 4915c7e92..26422421f 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -4,18 +4,30 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; +use embassy_rp::{clocks, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; +use fixed::types::U24F8; use fixed_macro::fixed; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812<'d, P: Instance, const S: usize> { + +pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { + dma: PeripheralRef<'d, AnyChannel>, sm: StateMachine<'d, P, S>, } -impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { - pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self { +impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { + pub fn new( + pio: &mut Common<'d, P>, + mut sm: StateMachine<'d, P, S>, + dma: impl Peripheral

+ 'd, + pin: impl PioPin, + ) -> Self { + into_ref!(dma); + // Setup sm0 // prepare the PIO program @@ -56,7 +68,7 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp - let clock_freq = fixed!(125_000: U24F8); + let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); let ws2812_freq = fixed!(800: U24F8); let bit_freq = ws2812_freq * CYCLES_PER_BIT; cfg.clock_divider = clock_freq / bit_freq; @@ -72,16 +84,22 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { sm.set_config(&cfg); sm.set_enable(true); - Self { sm } + Self { + dma: dma.map_into(), + sm, + } } - pub async fn write(&mut self, colors: &[RGB8]) { - for color in colors { - let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); - if !self.sm.tx().try_push(word) { - self.sm.tx().wait_push(word).await; - } + pub async fn write(&mut self, colors: &[RGB8; N]) { + // Precompute the word bytes from the colors + let mut words = [0u32; N]; + for i in 0..N { + let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); + words[i] = word; } + + // DMA transfer + self.sm.tx().dma_push(self.dma.reborrow(), &words).await; } } @@ -105,7 +123,7 @@ async fn main(_spawner: Spawner) { info!("Start"); let p = embassy_rp::init(Default::default()); - let Pio { common, sm0, .. } = Pio::new(p.PIO0); + let Pio { mut common, sm0, .. } = Pio::new(p.PIO0); // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit // feather boards for the 2040 both have one built in. @@ -114,7 +132,7 @@ async fn main(_spawner: Spawner) { // For the thing plus, use pin 8 // For the feather, use pin 16 - let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8); + let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); // Loop forever making RGB values and pushing them out to the WS2812. loop { From 661b1f33738e56cd1b6092c0e3f014cb8e9726d3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 May 2023 10:10:21 -0500 Subject: [PATCH 1147/1575] stm32/ipcc: remove constrain --- embassy-stm32/src/ipcc.rs | 15 ++++----------- embassy-stm32/src/tl_mbox/evt.rs | 5 +++-- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 9af5f171f..2b9caf8e5 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -35,6 +35,10 @@ pub struct Ipcc<'d> { impl<'d> Ipcc<'d> { pub fn new(peri: impl Peripheral

+ 'd, _config: Config) -> Self { + Self::new_inner(peri) + } + + pub(crate) fn new_inner(peri: impl Peripheral

+ 'd) -> Self { into_ref!(peri); Self { _peri: peri } @@ -180,14 +184,3 @@ unsafe fn _configure_pwr() { // set RF wake-up clock = LSE rcc.csr().modify(|w| w.set_rfwkpsel(0b01)); } - -// TODO: if anyone has a better idea, please let me know -/// extension trait that constrains the [`Ipcc`] peripheral -pub trait IpccExt<'d> { - fn constrain(self) -> Ipcc<'d>; -} -impl<'d> IpccExt<'d> for IPCC { - fn constrain(self) -> Ipcc<'d> { - Ipcc { _peri: self.into_ref() } - } -} diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs index 0ecd4dab6..770133f29 100644 --- a/embassy-stm32/src/tl_mbox/evt.rs +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -131,8 +131,9 @@ impl EvtBox { impl Drop for EvtBox { fn drop(&mut self) { - use crate::ipcc::IpccExt; - let mut ipcc = unsafe { crate::Peripherals::steal() }.IPCC.constrain(); + use crate::ipcc::Ipcc; + + let mut ipcc = Ipcc::new_inner(unsafe { crate::Peripherals::steal() }.IPCC); mm::MemoryManager::evt_drop(self.ptr, &mut ipcc); } } From 5e86188c25331d16fb6bfe071ff4485f1a51d664 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 May 2023 10:11:29 -0500 Subject: [PATCH 1148/1575] stm32/ipcc: cleanup naming --- embassy-stm32/src/tl_mbox/ble.rs | 8 +- embassy-stm32/src/tl_mbox/mm.rs | 14 +- embassy-stm32/src/tl_mbox/sys.rs | 8 +- .../src/tl_mbox/unsafe_linked_list.rs | 184 +++++++++--------- 4 files changed, 108 insertions(+), 106 deletions(-) diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index a285e314c..84c6307fd 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -5,7 +5,7 @@ use embassy_futures::block_on; use super::cmd::CmdSerial; use super::consts::TlPacketType; use super::evt::EvtBox; -use super::unsafe_linked_list::{LST_init_head, LST_is_empty, LST_remove_head}; +use super::unsafe_linked_list::LinkedListNode; use super::{ channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_CHANNEL, TL_REF_TABLE, @@ -18,7 +18,7 @@ pub struct Ble; impl Ble { pub(crate) fn new(ipcc: &mut Ipcc) -> Self { unsafe { - LST_init_head(EVT_QUEUE.as_mut_ptr()); + LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); TL_BLE_TABLE = MaybeUninit::new(BleTable { pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), @@ -38,8 +38,8 @@ impl Ble { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; - while !LST_is_empty(EVT_QUEUE.as_mut_ptr()) { - LST_remove_head(EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + while !LinkedListNode::is_empty(EVT_QUEUE.as_mut_ptr()) { + LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); let event = node_ptr.cast(); let event = EvtBox::new(event); diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs index 588b32919..f99ffa399 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -1,7 +1,7 @@ use core::mem::MaybeUninit; use super::evt::EvtPacket; -use super::unsafe_linked_list::{LST_init_head, LST_insert_tail, LST_is_empty, LST_remove_head}; +use super::unsafe_linked_list::LinkedListNode; use super::{ channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, TL_REF_TABLE, @@ -13,8 +13,8 @@ pub struct MemoryManager; impl MemoryManager { pub fn new() -> Self { unsafe { - LST_init_head(FREE_BUFF_QUEUE.as_mut_ptr()); - LST_init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); + LinkedListNode::init_head(FREE_BUFF_QUEUE.as_mut_ptr()); + LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable { spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), @@ -40,7 +40,7 @@ impl MemoryManager { unsafe { let list_node = evt.cast(); - LST_insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); + LinkedListNode::remove_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); } let channel_is_busy = ipcc.c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); @@ -59,9 +59,9 @@ impl MemoryManager { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; - while !LST_is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { - LST_remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), node_ptr_ptr); - LST_insert_tail( + while !LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { + LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), node_ptr_ptr); + LinkedListNode::insert_tail( (*(*TL_REF_TABLE.as_ptr()).mem_manager_table).pevt_free_buffer_queue, node_ptr, ); diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index 122657550..d22dfb58c 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -5,7 +5,7 @@ use embassy_futures::block_on; use super::cmd::{CmdPacket, CmdSerial}; use super::consts::TlPacketType; use super::evt::{CcEvt, EvtBox, EvtSerial}; -use super::unsafe_linked_list::{LST_init_head, LST_is_empty, LST_remove_head}; +use super::unsafe_linked_list::LinkedListNode; use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_CHANNEL, TL_REF_TABLE, TL_SYS_TABLE}; use crate::ipcc::Ipcc; @@ -14,7 +14,7 @@ pub struct Sys; impl Sys { pub(crate) fn new(ipcc: &mut Ipcc) -> Self { unsafe { - LST_init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); + LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); TL_SYS_TABLE = MaybeUninit::new(SysTable { pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), @@ -32,8 +32,8 @@ impl Sys { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; - while !LST_is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - LST_remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { + LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); let event = node_ptr.cast(); let event = EvtBox::new(event); diff --git a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs index 9caf01d1d..45bf5bdae 100644 --- a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs +++ b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs @@ -29,95 +29,97 @@ impl Default for LinkedListNode { } } -pub unsafe fn LST_init_head(mut listHead: *mut LinkedListNode) { - (*listHead).next = listHead; - (*listHead).prev = listHead; -} - -pub unsafe fn LST_is_empty(mut listHead: *mut LinkedListNode) -> bool { - interrupt::free(|_| ((*listHead).next) == listHead) -} - -pub unsafe fn LST_insert_head(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { - interrupt::free(|_| { - (*node).next = (*listHead).next; - (*node).prev = listHead; - (*listHead).next = node; - (*(*node).next).prev = node; - }); -} - -pub unsafe fn LST_insert_tail(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { - interrupt::free(|_| { - (*node).next = listHead; - (*node).prev = (*listHead).prev; - (*listHead).prev = node; - (*(*node).prev).next = node; - }); -} - -pub unsafe fn LST_remove_node(mut node: *mut LinkedListNode) { - interrupt::free(|_| { - (*(*node).prev).next = (*node).next; - (*(*node).next).prev = (*node).prev; - }); -} - -pub unsafe fn LST_remove_head(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { - interrupt::free(|_| { - *node = (*listHead).next; - LST_remove_node((*listHead).next); - }); -} - -pub unsafe fn LST_remove_tail(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { - interrupt::free(|_| { - *node = (*listHead).prev; - LST_remove_node((*listHead).prev); - }); -} - -pub unsafe fn LST_insert_node_after(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { - interrupt::free(|_| { - (*node).next = (*ref_node).next; - (*node).prev = ref_node; - (*ref_node).next = node; - (*(*node).next).prev = node; - }); -} - -pub unsafe fn LST_insert_node_before(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { - interrupt::free(|_| { - (*node).next = ref_node; - (*node).prev = (*ref_node).prev; - (*ref_node).prev = node; - (*(*node).prev).next = node; - }); -} - -pub unsafe fn LST_get_size(mut listHead: *mut LinkedListNode) -> usize { - interrupt::free(|_| { - let mut size = 0; - let mut temp: *mut LinkedListNode = core::ptr::null_mut::(); - - temp = (*listHead).next; - while temp != listHead { - size += 1; - temp = (*temp).next - } - - size - }) -} - -pub unsafe fn LST_get_next_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { - interrupt::free(|_| { - *node = (*ref_node).next; - }); -} - -pub unsafe fn LST_get_prev_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { - interrupt::free(|_| { - *node = (*ref_node).prev; - }); +impl LinkedListNode { + pub unsafe fn init_head(mut listHead: *mut LinkedListNode) { + (*listHead).next = listHead; + (*listHead).prev = listHead; + } + + pub unsafe fn is_empty(mut listHead: *mut LinkedListNode) -> bool { + interrupt::free(|_| ((*listHead).next) == listHead) + } + + pub unsafe fn insert_head(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = (*listHead).next; + (*node).prev = listHead; + (*listHead).next = node; + (*(*node).next).prev = node; + }); + } + + pub unsafe fn insert_tail(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = listHead; + (*node).prev = (*listHead).prev; + (*listHead).prev = node; + (*(*node).prev).next = node; + }); + } + + pub unsafe fn remove_node(mut node: *mut LinkedListNode) { + interrupt::free(|_| { + (*(*node).prev).next = (*node).next; + (*(*node).next).prev = (*node).prev; + }); + } + + pub unsafe fn remove_head(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*listHead).next; + Self::remove_node((*listHead).next); + }); + } + + pub unsafe fn remove_tail(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*listHead).prev; + Self::remove_node((*listHead).prev); + }); + } + + pub unsafe fn insert_node_after(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = (*ref_node).next; + (*node).prev = ref_node; + (*ref_node).next = node; + (*(*node).next).prev = node; + }); + } + + pub unsafe fn insert_node_before(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { + interrupt::free(|_| { + (*node).next = ref_node; + (*node).prev = (*ref_node).prev; + (*ref_node).prev = node; + (*(*node).prev).next = node; + }); + } + + pub unsafe fn get_size(mut listHead: *mut LinkedListNode) -> usize { + interrupt::free(|_| { + let mut size = 0; + let mut temp: *mut LinkedListNode = core::ptr::null_mut::(); + + temp = (*listHead).next; + while temp != listHead { + size += 1; + temp = (*temp).next + } + + size + }) + } + + pub unsafe fn get_next_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*ref_node).next; + }); + } + + pub unsafe fn get_prev_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + interrupt::free(|_| { + *node = (*ref_node).prev; + }); + } } From 383bef1711d5d6464e49dfe9722e0fa3dd5a4ad9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 May 2023 10:20:35 -0500 Subject: [PATCH 1149/1575] stm32/ipcc: naming --- .../src/tl_mbox/unsafe_linked_list.rs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs index 45bf5bdae..482e2bf5a 100644 --- a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs +++ b/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs @@ -30,29 +30,29 @@ impl Default for LinkedListNode { } impl LinkedListNode { - pub unsafe fn init_head(mut listHead: *mut LinkedListNode) { - (*listHead).next = listHead; - (*listHead).prev = listHead; + pub unsafe fn init_head(mut list_head: *mut LinkedListNode) { + (*list_head).next = list_head; + (*list_head).prev = list_head; } - pub unsafe fn is_empty(mut listHead: *mut LinkedListNode) -> bool { - interrupt::free(|_| ((*listHead).next) == listHead) + pub unsafe fn is_empty(mut list_head: *mut LinkedListNode) -> bool { + interrupt::free(|_| ((*list_head).next) == list_head) } - pub unsafe fn insert_head(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + pub unsafe fn insert_head(mut list_head: *mut LinkedListNode, mut node: *mut LinkedListNode) { interrupt::free(|_| { - (*node).next = (*listHead).next; - (*node).prev = listHead; - (*listHead).next = node; + (*node).next = (*list_head).next; + (*node).prev = list_head; + (*list_head).next = node; (*(*node).next).prev = node; }); } - pub unsafe fn insert_tail(mut listHead: *mut LinkedListNode, mut node: *mut LinkedListNode) { + pub unsafe fn insert_tail(mut list_head: *mut LinkedListNode, mut node: *mut LinkedListNode) { interrupt::free(|_| { - (*node).next = listHead; - (*node).prev = (*listHead).prev; - (*listHead).prev = node; + (*node).next = list_head; + (*node).prev = (*list_head).prev; + (*list_head).prev = node; (*(*node).prev).next = node; }); } @@ -64,17 +64,17 @@ impl LinkedListNode { }); } - pub unsafe fn remove_head(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + pub unsafe fn remove_head(mut list_head: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { interrupt::free(|_| { - *node = (*listHead).next; - Self::remove_node((*listHead).next); + *node = (*list_head).next; + Self::remove_node((*list_head).next); }); } - pub unsafe fn remove_tail(mut listHead: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + pub unsafe fn remove_tail(mut list_head: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { interrupt::free(|_| { - *node = (*listHead).prev; - Self::remove_node((*listHead).prev); + *node = (*list_head).prev; + Self::remove_node((*list_head).prev); }); } @@ -96,13 +96,13 @@ impl LinkedListNode { }); } - pub unsafe fn get_size(mut listHead: *mut LinkedListNode) -> usize { + pub unsafe fn get_size(mut list_head: *mut LinkedListNode) -> usize { interrupt::free(|_| { let mut size = 0; let mut temp: *mut LinkedListNode = core::ptr::null_mut::(); - temp = (*listHead).next; - while temp != listHead { + temp = (*list_head).next; + while temp != list_head { size += 1; temp = (*temp).next } From 7f702fd6f19811a65e5ee659a5182fc291524e51 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 May 2023 11:29:53 -0500 Subject: [PATCH 1150/1575] stm32/ipcc: fix warnings --- embassy-stm32/src/tl_mbox/ble.rs | 2 +- embassy-stm32/src/tl_mbox/shci.rs | 2 +- embassy-stm32/src/tl_mbox/sys.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index 84c6307fd..d8bf14d4f 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -59,7 +59,7 @@ impl Ble { core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); - let mut cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; + let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; cmd_packet.cmd_serial.ty = TlPacketType::BleCmd as u8; } diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32/src/tl_mbox/shci.rs index 7f224cb1a..61fd9e4a3 100644 --- a/embassy-stm32/src/tl_mbox/shci.rs +++ b/embassy-stm32/src/tl_mbox/shci.rs @@ -90,7 +90,7 @@ pub fn shci_ble_init(ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { (*cmd_ptr).cmd_serial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT; (*cmd_ptr).cmd_serial.cmd.payload_len = core::mem::size_of::() as u8; - let mut cmd_buf = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; + let cmd_buf = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; core::ptr::write(cmd_buf, *cmd_ptr); cmd_buf.cmd_serial.ty = TlPacketType::SysCmd as u8; diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index d22dfb58c..31ebde721 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -77,7 +77,7 @@ impl Sys { core::ptr::copy(buf.as_ptr(), cmd_serial_buf, buf.len()); - let mut cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; + let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; cmd_packet.cmd_serial.ty = TlPacketType::SysCmd as u8; ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); From eb09d7d67174d4b1f94e84e452f2920dd5fe442b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 21 May 2023 18:39:13 -0500 Subject: [PATCH 1151/1575] stm32/ipcc: update doc --- examples/stm32wb/src/bin/tl_mbox.rs | 1 + examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index ccd01cbc7..acbc60c87 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -28,6 +28,7 @@ async fn main(_spawner: Spawner) { - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". - Disconnect from the device. - In the examples folder for stm32wb, modify the memory.x file to match your target device. - Run this example. diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 315172df8..ff506338d 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -27,6 +27,7 @@ async fn main(_spawner: Spawner) { - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". - Disconnect from the device. - In the examples folder for stm32wb, modify the memory.x file to match your target device. - Run this example. From 1f65a4eb6f4e7e0c6f5e036f3ea97650b9637ad9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 21 May 2023 18:40:29 -0500 Subject: [PATCH 1152/1575] stm32/ipcc: enable test --- tests/stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bd8d90abe..4ce056492 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,7 +12,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "ble", "not-gpdma"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board From d1dfaa190518df6adc66ab0716236bac3fe5f894 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 21 May 2023 20:18:26 -0500 Subject: [PATCH 1153/1575] stm32/ipcc: fix hil test --- examples/stm32wb/memory.x | 2 +- tests/stm32/build.rs | 3 +++ tests/stm32/memory_ble.x | 23 +++++++++++++++++++++++ tests/stm32/src/bin/ble.rs | 8 +++----- 4 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 tests/stm32/memory_ble.x diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x index 5a07b7d19..e1f0530bd 100644 --- a/examples/stm32wb/memory.x +++ b/examples/stm32wb/memory.x @@ -11,7 +11,7 @@ MEMORY } /* - Memory size for STM32WB55xC with 512K FLASH + Memory size for STM32WB55xG with 512K FLASH MEMORY { diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 7ae311778..ca76b70bb 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -12,6 +12,9 @@ fn main() -> Result<(), Box> { if cfg!(any(feature = "stm32f103c8", feature = "stm32c031c6")) { println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rerun-if-changed=link.x"); + } else if cfg!(feature = "stm32wb55rg") { + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + fs::write(out.join("memory.x"), include_bytes!("memory_ble.x")).unwrap(); } else { println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); println!("cargo:rerun-if-changed=link_ram.x"); diff --git a/tests/stm32/memory_ble.x b/tests/stm32/memory_ble.x new file mode 100644 index 000000000..4332e2c72 --- /dev/null +++ b/tests/stm32/memory_ble.x @@ -0,0 +1,23 @@ +/* + Memory size for STM32WB55xG with 512K FLASH +*/ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K + RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 + RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K +} + +/* Place stack at the end of SRAM1 */ +_stack_start = ORIGIN(RAM) + LENGTH(RAM); + +/* + * Scatter the mailbox interface memory sections in shared memory + */ +SECTIONS { + TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED + + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED +} diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/ble.rs index 0f711fdac..aedf9a380 100644 --- a/tests/stm32/src/bin/ble.rs +++ b/tests/stm32/src/bin/ble.rs @@ -7,6 +7,7 @@ #[path = "../example_common.rs"] mod example_common; use embassy_executor::Spawner; +use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc}; use embassy_stm32::tl_mbox::TlMbox; use embassy_time::{Duration, Timer}; @@ -20,9 +21,6 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mut ipcc = Ipcc::new(p.IPCC, config); - let config = Config::default(); - let mut ipcc = Ipcc::new(p.IPCC, config); - let rx_irq = interrupt::take!(IPCC_C1_RX); let tx_irq = interrupt::take!(IPCC_C1_TX); @@ -31,7 +29,7 @@ async fn main(_spawner: Spawner) { loop { let wireless_fw_info = mbox.wireless_fw_info(); match wireless_fw_info { - None => error!("not yet initialized"), + None => {} Some(fw_info) => { let version_major = fw_info.version_major(); let version_minor = fw_info.version_minor(); @@ -49,7 +47,7 @@ async fn main(_spawner: Spawner) { } } - Timer::after(Duration::from_millis(500)).await; + Timer::after(Duration::from_millis(50)).await; } info!("Test OK"); From 18c62aa5b475161c0cbd98c23c006691e084cb2b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 22 May 2023 11:32:39 +0200 Subject: [PATCH 1154/1575] Protect default implementations for FirmwareUpdater and BootLoader It seems as if the arm compiler can does not care about whether the bootloader symbols are undefined if the default() function is never used. The x64 compiler does care however, so this change ensures that we can instantiate the types from tests. --- embassy-boot/boot/src/firmware_updater.rs | 1 + embassy-boot/nrf/src/lib.rs | 1 + embassy-boot/rp/src/lib.rs | 1 + embassy-boot/stm32/src/lib.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs index 92987825f..aeea206f9 100644 --- a/embassy-boot/boot/src/firmware_updater.rs +++ b/embassy-boot/boot/src/firmware_updater.rs @@ -40,6 +40,7 @@ pub struct FirmwareUpdater { dfu: Partition, } +#[cfg(target_os = "none")] impl Default for FirmwareUpdater { fn default() -> Self { extern "C" { diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 14bea1f79..710798bdb 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -15,6 +15,7 @@ pub struct BootLoader { aligned_buf: AlignedBuffer, } +#[cfg(target_os = "none")] impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 0d577f08a..fb9bc3242 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -51,6 +51,7 @@ impl BootLoader { } } +#[cfg(target_os = "none")] impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 88ce1c878..ccf136c74 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -46,6 +46,7 @@ impl BootLoader { } } +#[cfg(target_os = "none")] impl Default for BootLoader { /// Create a new bootloader instance using parameters from linker script fn default() -> Self { From 12720737e1c099b5626e45946b4f92b606922c2d Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 22 May 2023 10:52:05 +0100 Subject: [PATCH 1155/1575] stm32/ipcc: fix incorrect example --- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index ff506338d..be606399f 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -75,7 +75,7 @@ async fn main(_spawner: Spawner) { mbox.shci_ble_init(&mut ipcc, Default::default()); info!("resetting BLE"); - mbox.send_ble_cmd(&mut ipcc, &[0x01, 0x03, 0x0c]); + mbox.send_ble_cmd(&mut ipcc, &[0x01, 0x03, 0x0c, 0x00, 0x00]); let event_box = mbox.read().await; From 059ab358a5bd9102df09e511b190a70684e9c261 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 22 May 2023 11:13:22 +0100 Subject: [PATCH 1156/1575] stm32/ipcc: uncomment shci init cmd --- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index be606399f..1008e1e41 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq); // initialize ble stack, does not return a response - // mbox.shci_ble_init(&mut ipcc, Default::default()); + mbox.shci_ble_init(&mut ipcc, Default::default()); info!("waiting for coprocessor to boot"); let event_box = mbox.read().await; From e9121cba2c624591577a928c41230f53e6d28a6b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 22 May 2023 14:22:27 +0200 Subject: [PATCH 1157/1575] stm32: Fix watchdog timeout computation --- embassy-stm32/src/wdg/mod.rs | 40 ++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 92b9a5ca8..18ebf97d8 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -13,13 +13,13 @@ 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: u16) -> u32 { - 1_000_000 * MAX_RL as u32 / (LSI_FREQ.0 / prescaler as u32) +const fn get_timeout_us(prescaler: u16, reload_value: u16) -> u32 { + 1_000_000 * (reload_value + 1) as u32 / (LSI_FREQ.0 / prescaler as u32) } /// Calculates watchdog reload value for the given prescaler and desired timeout const fn reload_value(prescaler: u16, timeout_us: u32) -> u16 { - (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 + (timeout_us / prescaler as u32 * LSI_FREQ.0 / 1_000_000) as u16 - 1 } impl<'d, T: Instance> IndependentWatchdog<'d, T> { @@ -34,7 +34,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { // This iterates from 4 (2^2) to 256 (2^8). let psc_power = unwrap!((2..=8).find(|psc_power| { let psc = 2u16.pow(*psc_power); - timeout_us <= max_timeout(psc) + timeout_us <= get_timeout_us(psc, MAX_RL) })); // Prescaler value @@ -54,6 +54,14 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { wdg.rlr().write(|w| w.set_rl(rl)); } + trace!( + "Watchdog configured with {}us timeout, desired was {}us (PR={}, RL={})", + get_timeout_us(psc, rl), + timeout_us, + pr, + rl + ); + IndependentWatchdog { wdg: PhantomData::default(), } @@ -87,3 +95,27 @@ foreach_peripheral!( impl Instance for crate::peripherals::$inst {} }; ); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_compute_timeout_us() { + assert_eq!(125, get_timeout_us(4, 0)); + assert_eq!(512_000, get_timeout_us(4, MAX_RL)); + + assert_eq!(8_000, get_timeout_us(256, 0)); + assert_eq!(32768_000, get_timeout_us(256, MAX_RL)); + + assert_eq!(8000_000, get_timeout_us(64, 3999)); + } + + #[test] + fn can_compute_reload_value() { + assert_eq!(0xFFF, reload_value(4, 512_000)); + assert_eq!(0xFFF, reload_value(256, 32768_000)); + + assert_eq!(3999, reload_value(64, 8000_000)); + } +} From d54eb1107ee45c5030449a0de0c259da7236ca05 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 22 May 2023 15:57:20 +0200 Subject: [PATCH 1158/1575] Yield between BlockingAsync NorFlash write and erase operations --- embassy-embedded-hal/Cargo.toml | 8 ++- embassy-embedded-hal/src/adapter.rs | 79 ++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 19d512585..81cece686 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -17,8 +17,11 @@ std = [] nightly = ["embedded-hal-async", "embedded-storage-async"] [dependencies] +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ + "unproven", +] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } embedded-storage = "0.3.0" @@ -26,3 +29,6 @@ embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" defmt = { version = "0.3", optional = true } + +[dev-dependencies] +futures-test = "0.3.17" diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index 171ff6c9f..169aad5e3 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -1,5 +1,6 @@ //! Adapters between embedded-hal traits. +use embassy_futures::yield_now; use embedded_hal_02::{blocking, serial}; /// Wrapper that implements async traits using blocking implementations. @@ -150,11 +151,18 @@ where const ERASE_SIZE: usize = ::ERASE_SIZE; async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { - self.wrapped.write(offset, data) + self.wrapped.write(offset, data)?; + yield_now().await; + Ok(()) } async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.wrapped.erase(from, to) + for from in (from..to).step_by(T::ERASE_SIZE) { + let to = core::cmp::min(from + T::ERASE_SIZE as u32, to); + self.wrapped.erase(from, to)?; + yield_now().await; + } + Ok(()) } } @@ -171,3 +179,70 @@ where self.wrapped.capacity() } } + +#[cfg(test)] +mod tests { + use super::*; + + extern crate std; + + #[derive(Default)] + struct FakeFlash(Vec<(u32, u32)>); + + impl embedded_storage::nor_flash::ErrorType for FakeFlash { + type Error = std::convert::Infallible; + } + + impl embedded_storage::nor_flash::ReadNorFlash for FakeFlash { + const READ_SIZE: usize = 1; + + fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> { + unimplemented!() + } + + fn capacity(&self) -> usize { + unimplemented!() + } + } + + impl embedded_storage::nor_flash::NorFlash for FakeFlash { + const WRITE_SIZE: usize = 4; + const ERASE_SIZE: usize = 128; + + fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> { + unimplemented!() + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.0.push((from, to)); + Ok(()) + } + } + + #[futures_test::test] + async fn can_erase() { + let fake = FakeFlash::default(); + let mut yielding = BlockingAsync::new(fake); + + yielding.erase(0, 256).await.unwrap(); + + let fake = yielding.wrapped; + assert_eq!(2, fake.0.len()); + assert_eq!((0, 128), fake.0[0]); + assert_eq!((128, 256), fake.0[1]); + } + + #[futures_test::test] + async fn can_erase_wrong_erase_size() { + let fake = FakeFlash::default(); + let mut yielding = BlockingAsync::new(fake); + + yielding.erase(0, 257).await.unwrap(); + + let fake = yielding.wrapped; + assert_eq!(3, fake.0.len()); + assert_eq!((0, 128), fake.0[0]); + assert_eq!((128, 256), fake.0[1]); + assert_eq!((256, 257), fake.0[2]); + } +} From cd1bf31fedbd33170507245eef1f7ae576aa3557 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 22 May 2023 16:48:31 +0200 Subject: [PATCH 1159/1575] Add YieldingAsync adapter --- embassy-embedded-hal/Cargo.toml | 4 +- .../{adapter.rs => adapter/blocking_async.rs} | 79 +----- embassy-embedded-hal/src/adapter/mod.rs | 5 + .../src/adapter/yielding_async.rs | 232 ++++++++++++++++++ 4 files changed, 241 insertions(+), 79 deletions(-) rename embassy-embedded-hal/src/{adapter.rs => adapter/blocking_async.rs} (70%) create mode 100644 embassy-embedded-hal/src/adapter/mod.rs create mode 100644 embassy-embedded-hal/src/adapter/yielding_async.rs diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 81cece686..ad2f14568 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -14,10 +14,10 @@ target = "x86_64-unknown-linux-gnu" [features] std = [] # Enable nightly-only features -nightly = ["embedded-hal-async", "embedded-storage-async"] +nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"] [dependencies] -embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs similarity index 70% rename from embassy-embedded-hal/src/adapter.rs rename to embassy-embedded-hal/src/adapter/blocking_async.rs index 169aad5e3..171ff6c9f 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -1,6 +1,5 @@ //! Adapters between embedded-hal traits. -use embassy_futures::yield_now; use embedded_hal_02::{blocking, serial}; /// Wrapper that implements async traits using blocking implementations. @@ -151,18 +150,11 @@ where const ERASE_SIZE: usize = ::ERASE_SIZE; async fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { - self.wrapped.write(offset, data)?; - yield_now().await; - Ok(()) + self.wrapped.write(offset, data) } async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - for from in (from..to).step_by(T::ERASE_SIZE) { - let to = core::cmp::min(from + T::ERASE_SIZE as u32, to); - self.wrapped.erase(from, to)?; - yield_now().await; - } - Ok(()) + self.wrapped.erase(from, to) } } @@ -179,70 +171,3 @@ where self.wrapped.capacity() } } - -#[cfg(test)] -mod tests { - use super::*; - - extern crate std; - - #[derive(Default)] - struct FakeFlash(Vec<(u32, u32)>); - - impl embedded_storage::nor_flash::ErrorType for FakeFlash { - type Error = std::convert::Infallible; - } - - impl embedded_storage::nor_flash::ReadNorFlash for FakeFlash { - const READ_SIZE: usize = 1; - - fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - fn capacity(&self) -> usize { - unimplemented!() - } - } - - impl embedded_storage::nor_flash::NorFlash for FakeFlash { - const WRITE_SIZE: usize = 4; - const ERASE_SIZE: usize = 128; - - fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.0.push((from, to)); - Ok(()) - } - } - - #[futures_test::test] - async fn can_erase() { - let fake = FakeFlash::default(); - let mut yielding = BlockingAsync::new(fake); - - yielding.erase(0, 256).await.unwrap(); - - let fake = yielding.wrapped; - assert_eq!(2, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - } - - #[futures_test::test] - async fn can_erase_wrong_erase_size() { - let fake = FakeFlash::default(); - let mut yielding = BlockingAsync::new(fake); - - yielding.erase(0, 257).await.unwrap(); - - let fake = yielding.wrapped; - assert_eq!(3, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - assert_eq!((256, 257), fake.0[2]); - } -} diff --git a/embassy-embedded-hal/src/adapter/mod.rs b/embassy-embedded-hal/src/adapter/mod.rs new file mode 100644 index 000000000..787ac2979 --- /dev/null +++ b/embassy-embedded-hal/src/adapter/mod.rs @@ -0,0 +1,5 @@ +mod blocking_async; +mod yielding_async; + +pub use blocking_async::BlockingAsync; +pub use yielding_async::YieldingAsync; diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs new file mode 100644 index 000000000..96d5cca8e --- /dev/null +++ b/embassy-embedded-hal/src/adapter/yielding_async.rs @@ -0,0 +1,232 @@ +use embassy_futures::yield_now; + +/// Wrapper that yields for each operation to the wrapped instance +/// +/// This can be used in combination with BlockingAsync to enforce yields +/// between long running blocking operations. +pub struct YieldingAsync { + wrapped: T, +} + +impl YieldingAsync { + /// Create a new instance of a wrapper that yields after each operation. + pub fn new(wrapped: T) -> Self { + Self { wrapped } + } +} + +// +// I2C implementations +// +impl embedded_hal_1::i2c::ErrorType for YieldingAsync +where + T: embedded_hal_1::i2c::ErrorType, +{ + type Error = T::Error; +} + +impl embedded_hal_async::i2c::I2c for YieldingAsync +where + T: embedded_hal_async::i2c::I2c, +{ + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(address, read).await?; + yield_now().await; + Ok(()) + } + + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(address, write).await?; + yield_now().await; + Ok(()) + } + + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.write_read(address, write, read).await?; + yield_now().await; + Ok(()) + } + + async fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal_1::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.wrapped.transaction(address, operations).await?; + yield_now().await; + Ok(()) + } +} + +// +// SPI implementations +// + +impl embedded_hal_async::spi::ErrorType for YieldingAsync +where + T: embedded_hal_async::spi::ErrorType, +{ + type Error = T::Error; +} + +impl embedded_hal_async::spi::SpiBus for YieldingAsync +where + T: embedded_hal_async::spi::SpiBus, +{ + async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer(read, write).await?; + yield_now().await; + Ok(()) + } + + async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer_in_place(words).await?; + yield_now().await; + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusFlush for YieldingAsync +where + T: embedded_hal_async::spi::SpiBusFlush, +{ + async fn flush(&mut self) -> Result<(), Self::Error> { + self.wrapped.flush().await?; + yield_now().await; + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusWrite for YieldingAsync +where + T: embedded_hal_async::spi::SpiBusWrite, +{ + async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(data).await?; + yield_now().await; + Ok(()) + } +} + +impl embedded_hal_async::spi::SpiBusRead for YieldingAsync +where + T: embedded_hal_async::spi::SpiBusRead, +{ + async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(data).await?; + yield_now().await; + Ok(()) + } +} + +/// +/// NOR flash implementations +/// +impl embedded_storage::nor_flash::ErrorType for YieldingAsync { + type Error = T::Error; +} + +impl embedded_storage_async::nor_flash::ReadNorFlash + for YieldingAsync +{ + const READ_SIZE: usize = T::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.read(offset, bytes).await?; + Ok(()) + } + + fn capacity(&self) -> usize { + self.wrapped.capacity() + } +} + +impl embedded_storage_async::nor_flash::NorFlash for YieldingAsync { + const WRITE_SIZE: usize = T::WRITE_SIZE; + const ERASE_SIZE: usize = T::ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(offset, bytes).await?; + yield_now().await; + Ok(()) + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + // Yield between each actual erase + for from in (from..to).step_by(T::ERASE_SIZE) { + let to = core::cmp::min(from + T::ERASE_SIZE as u32, to); + self.wrapped.erase(from, to).await?; + yield_now().await; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use embedded_storage_async::nor_flash::NorFlash; + + use super::*; + + extern crate std; + + #[derive(Default)] + struct FakeFlash(Vec<(u32, u32)>); + + impl embedded_storage::nor_flash::ErrorType for FakeFlash { + type Error = std::convert::Infallible; + } + + impl embedded_storage_async::nor_flash::ReadNorFlash for FakeFlash { + const READ_SIZE: usize = 1; + + async fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> { + unimplemented!() + } + + fn capacity(&self) -> usize { + unimplemented!() + } + } + + impl embedded_storage_async::nor_flash::NorFlash for FakeFlash { + const WRITE_SIZE: usize = 4; + const ERASE_SIZE: usize = 128; + + async fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> { + unimplemented!() + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.0.push((from, to)); + Ok(()) + } + } + + #[futures_test::test] + async fn can_erase() { + let fake = FakeFlash::default(); + let mut yielding = YieldingAsync::new(fake); + + yielding.erase(0, 256).await.unwrap(); + + let fake = yielding.wrapped; + assert_eq!(2, fake.0.len()); + assert_eq!((0, 128), fake.0[0]); + assert_eq!((128, 256), fake.0[1]); + } + + #[futures_test::test] + async fn can_erase_wrong_erase_size() { + let fake = FakeFlash::default(); + let mut yielding = YieldingAsync::new(fake); + + yielding.erase(0, 257).await.unwrap(); + + let fake = yielding.wrapped; + assert_eq!(3, fake.0.len()); + assert_eq!((0, 128), fake.0[0]); + assert_eq!((128, 256), fake.0[1]); + assert_eq!((256, 257), fake.0[2]); + } +} From 187551f914aba22c001eb11a48e5fd15ea439b13 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 22 May 2023 16:55:18 +0200 Subject: [PATCH 1160/1575] Move module documentation --- embassy-embedded-hal/src/adapter/blocking_async.rs | 2 -- embassy-embedded-hal/src/adapter/mod.rs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index 171ff6c9f..b996d6a75 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -1,5 +1,3 @@ -//! Adapters between embedded-hal traits. - use embedded_hal_02::{blocking, serial}; /// Wrapper that implements async traits using blocking implementations. diff --git a/embassy-embedded-hal/src/adapter/mod.rs b/embassy-embedded-hal/src/adapter/mod.rs index 787ac2979..28da56137 100644 --- a/embassy-embedded-hal/src/adapter/mod.rs +++ b/embassy-embedded-hal/src/adapter/mod.rs @@ -1,3 +1,5 @@ +//! Adapters between embedded-hal traits. + mod blocking_async; mod yielding_async; From 64092169e3133b572626c1efa106963139a63b3f Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 22 May 2023 20:14:37 -0500 Subject: [PATCH 1161/1575] stm32/ipcc: disable test --- tests/stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 4ce056492..bd8d90abe 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,7 +12,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "ble", "not-gpdma"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board From 49eaf000b89aab5b1f7fa524c06c4c37a1aec63d Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 22 May 2023 21:26:03 -0400 Subject: [PATCH 1162/1575] Add i2c transaction --- .../src/shared_bus/asynch/i2c.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index 829554045..5d8214db7 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -84,9 +84,13 @@ where address: u8, operations: &mut [embedded_hal_async::i2c::Operation<'_>], ) -> Result<(), I2cDeviceError> { - let _ = address; - let _ = operations; - todo!() + defmt::info!("lock"); + let mut bus = self.bus.lock().await; + defmt::info!("transact"); + bus.transaction(address, operations) + .await + .map_err(I2cDeviceError::I2c)?; + Ok(()) } } @@ -150,8 +154,11 @@ where } async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() + let mut bus = self.bus.lock().await; + bus.set_config(&self.config); + bus.transaction(address, operations) + .await + .map_err(I2cDeviceError::I2c)?; + Ok(()) } } From 3ad52f837d865d4b6728e58223b944d9e37a6ddf Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 22 May 2023 21:31:00 -0400 Subject: [PATCH 1163/1575] Remove debug --- embassy-embedded-hal/src/shared_bus/asynch/i2c.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index 5d8214db7..87e8a4304 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -84,9 +84,7 @@ where address: u8, operations: &mut [embedded_hal_async::i2c::Operation<'_>], ) -> Result<(), I2cDeviceError> { - defmt::info!("lock"); let mut bus = self.bus.lock().await; - defmt::info!("transact"); bus.transaction(address, operations) .await .map_err(I2cDeviceError::I2c)?; From 879c621394b4b7cf7f96242c4ae347812c17bf8e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 23 May 2023 22:49:27 +0200 Subject: [PATCH 1164/1575] Ensure FlashRegion can only be created within this crate --- embassy-stm32/build.rs | 1 + embassy-stm32/src/flash/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index a00c6c416..ca0c36c26 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -206,6 +206,7 @@ fn main() { erase_size: #erase_size, write_size: #write_size, erase_value: #erase_value, + _ensure_internal: (), }; }); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index f6efa7753..b93270ae1 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -19,6 +19,7 @@ pub struct FlashRegion { pub erase_size: u32, pub write_size: u32, pub erase_value: u8, + pub(crate) _ensure_internal: (), } #[derive(Debug, PartialEq)] From faf506b300291a04c534a22bec771a3106c72d16 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 23 May 2023 22:50:41 +0200 Subject: [PATCH 1165/1575] Remove Drop for AltFlashLayout --- embassy-stm32/src/flash/common.rs | 1 + embassy-stm32/src/flash/f0.rs | 2 ++ embassy-stm32/src/flash/f3.rs | 2 ++ embassy-stm32/src/flash/f4.rs | 15 ++++++++------- embassy-stm32/src/flash/f7.rs | 2 ++ embassy-stm32/src/flash/h7.rs | 2 ++ embassy-stm32/src/flash/l.rs | 2 ++ 7 files changed, 19 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 6d7f55974..a9bf3c3cd 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -17,6 +17,7 @@ impl<'d> Flash<'d> { } pub fn into_regions(self) -> FlashLayout<'d> { + family::set_default_layout(); FlashLayout::new(self.release()) } diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 033afecd5..c6441ef16 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -7,6 +7,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn set_default_layout() {} + pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 10a09c42c..4c8172203 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -7,6 +7,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn set_default_layout() {} + pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 60ac62c17..63293c052 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -79,19 +79,20 @@ mod alt_regions { } } - impl Drop for AltFlashLayout<'_> { - fn drop(&mut self) { - unsafe { - super::lock(); - crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) - }; - } } } #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +pub fn set_default_layout() { + unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) }; +} + +#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] +pub const fn set_default_layout() {} + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn get_flash_regions() -> &'static [&'static FlashRegion] { if unsafe { pac::FLASH.optcr().read().db1m() } { diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 6427d5a09..ac2834a84 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -6,6 +6,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn set_default_layout() {} + pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 4f38d50c0..1b1631068 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -7,6 +7,8 @@ use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn set_default_layout() {} + const fn is_dual_bank() -> bool { FLASH_REGIONS.len() == 2 } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 7d9cc6ea3..c94f61900 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -6,6 +6,8 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +pub const fn set_default_layout() {} + pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } From 14e3e72b0f54fe17245027be468fed30aba39266 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 23 May 2023 22:51:26 +0200 Subject: [PATCH 1166/1575] Add missing implementations for f4 alternate regions --- embassy-stm32/src/flash/common.rs | 18 ++++---- embassy-stm32/src/flash/f4.rs | 69 +++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index a9bf3c3cd..432c8a43b 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -26,11 +26,11 @@ impl<'d> Flash<'d> { } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } + unsafe { blocking_write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase(FLASH_BASE as u32, from, to) } + unsafe { blocking_erase_sectored(FLASH_BASE as u32, from, to) } } pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { @@ -38,7 +38,7 @@ impl<'d> Flash<'d> { } } -fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { +pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -49,7 +49,7 @@ fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result< Ok(()) } -unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { +pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -82,7 +82,7 @@ unsafe fn blocking_write(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Res Ok(()) } -unsafe fn blocking_erase(base: u32, from: u32, to: u32) -> Result<(), Error> { +pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; let regions = family::get_flash_regions(); @@ -155,11 +155,11 @@ impl FlashRegion { } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write(self.base, self.size, offset, bytes) } + unsafe { blocking_write_chunked(self.base, self.size, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase(self.base, from, to) } + unsafe { blocking_erase_sectored(self.base, from, to) } } } @@ -200,11 +200,11 @@ foreach_flash_region! { } pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write(self.0.base, self.0.size, offset, bytes) } + unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } } pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase(self.0.base, from, to) } + unsafe { blocking_erase_sectored(self.0.base, from, to) } } } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 63293c052..7f1c5f671 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -11,8 +11,11 @@ mod alt_regions { use embassy_hal_common::PeripheralRef; use stm32_metapac::FLASH_SIZE; - use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; - use crate::flash::{Bank1Region1, Bank1Region2, Flash, FlashBank, FlashRegion}; + use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; + use crate::flash::{ + blocking_erase_sectored, blocking_read, blocking_write_chunked, Bank1Region1, Bank1Region2, Error, Flash, + FlashBank, FlashRegion, + }; use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { @@ -45,20 +48,19 @@ mod alt_regions { &ALT_BANK2_REGION3, ]; - pub type AltBank1Region1<'d> = Bank1Region1<'d>; - pub type AltBank1Region2<'d> = Bank1Region2<'d>; pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltFlashLayout<'d> { - pub bank1_region1: AltBank1Region1<'d>, - pub bank1_region2: AltBank1Region2<'d>, + pub bank1_region1: Bank1Region1<'d>, + pub bank1_region2: Bank1Region2<'d>, pub bank1_region3: AltBank1Region3<'d>, pub bank2_region1: AltBank2Region1<'d>, pub bank2_region2: AltBank2Region2<'d>, pub bank2_region3: AltBank2Region3<'d>, + pub otp_region: OTPRegion<'d>, } impl<'d> Flash<'d> { @@ -66,7 +68,7 @@ mod alt_regions { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; // SAFETY: We never expose the cloned peripheral references, and their instance is not public. - // Also, all flash region operations are protected with a cs. + // Also, all blocking flash region operations are protected with a cs. let p = self.release(); AltFlashLayout { bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), @@ -75,15 +77,66 @@ mod alt_regions { bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }), bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }), bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }), + otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }), } } } + macro_rules! foreach_altflash_region { + ($type_name:ident, $region:ident) => { + impl $type_name<'_> { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + blocking_read(self.0.base, self.0.size, offset, bytes) + } + + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } + } + + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { blocking_erase_sectored(self.0.base, from, to) } + } + } + + impl embedded_storage::nor_flash::ErrorType for $type_name<'_> { + type Error = Error; + } + + impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_> { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + + impl embedded_storage::nor_flash::NorFlash for $type_name<'_> { + const WRITE_SIZE: usize = $region.write_size as usize; + const ERASE_SIZE: usize = $region.erase_size as usize; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } + } + }; } + + foreach_altflash_region!(AltBank1Region3, ALT_BANK1_REGION3); + foreach_altflash_region!(AltBank2Region1, ALT_BANK2_REGION1); + foreach_altflash_region!(AltBank2Region2, ALT_BANK2_REGION2); + foreach_altflash_region!(AltBank2Region3, ALT_BANK2_REGION3); } #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub use alt_regions::{AltFlashLayout, ALT_FLASH_REGIONS}; +pub use alt_regions::*; #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn set_default_layout() { From 87acf5f50f2a976cae7546f691fb14ba7b81c714 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 23 May 2023 23:01:55 +0200 Subject: [PATCH 1167/1575] Add missing set_default_layout() in "other" family --- embassy-stm32/src/flash/other.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index c151cb828..556034654 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -2,6 +2,8 @@ use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +pub const fn set_default_layout() {} + pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } From a19f8c32ffe085fdb68d349599dac360721b7786 Mon Sep 17 00:00:00 2001 From: Olivier Monnom <7986118+papyDoctor@users.noreply.github.com> Date: Wed, 24 May 2023 09:22:05 +0200 Subject: [PATCH 1168/1575] Update examples in README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fe8d5d93b..defea489f 100644 --- a/README.md +++ b/README.md @@ -21,27 +21,28 @@ TODO: - Setting a custom MAC address. - Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) -## Running the example +## Running the examples - `cargo install probe-rs-cli` - `cd examples/rpi-pico-w` +### Example 1: Scan the wifi stations +- `cargo run --release --bin wifi_scan` +### Example 2: Create an access point (IP and credentials in the code) +- `cargo run --release --bin tcp_server_ap` +### Example 3: Connect to an existing network and create a server - `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release` After a few seconds, you should see that DHCP picks up an IP address like this - ``` 11.944489 DEBUG Acquired IP configuration: 11.944517 DEBUG IP address: 192.168.0.250/24 11.944620 DEBUG Default gateway: 192.168.0.33 11.944722 DEBUG DNS server 0: 192.168.0.33 ``` - -The example implements a TCP echo server on port 1234. You can try connecting to it with: - +This example implements a TCP echo server on port 1234. You can try connecting to it with: ``` nc 192.168.0.250 1234 ``` - Send it some data, you should see it echoed back and printed in the firmware's logs. ## License From e785e1bc22fde8f203048d143ceafdd45ec4d4b6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 14:40:34 +0200 Subject: [PATCH 1169/1575] Add ConcatFlash utility --- embassy-embedded-hal/src/flash.rs | 286 ++++++++++++++++++++++++++++++ embassy-embedded-hal/src/lib.rs | 2 + 2 files changed, 288 insertions(+) create mode 100644 embassy-embedded-hal/src/flash.rs diff --git a/embassy-embedded-hal/src/flash.rs b/embassy-embedded-hal/src/flash.rs new file mode 100644 index 000000000..9a6e4bd92 --- /dev/null +++ b/embassy-embedded-hal/src/flash.rs @@ -0,0 +1,286 @@ +//! Utilities related to flash. + +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash}; +#[cfg(feature = "nightly")] +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +/// Convenience helper for concatenating two consecutive flashes into one. +/// This is especially useful if used with "flash regions", where one may +/// want to concatenate multiple regions into one larger region. +pub struct ConcatFlash(First, Second); + +impl ConcatFlash { + /// Create a new flash that concatenates two consecutive flashes. + pub fn new(first: First, second: Second) -> Self { + Self(first, second) + } +} + +const fn get_read_size(first_read_size: usize, second_read_size: usize) -> usize { + if first_read_size != second_read_size { + panic!("The read size for the concatenated flashes must be the same"); + } + first_read_size +} + +const fn get_write_size(first_write_size: usize, second_write_size: usize) -> usize { + if first_write_size != second_write_size { + panic!("The write size for the concatenated flashes must be the same"); + } + first_write_size +} + +const fn get_max_erase_size(first_erase_size: usize, second_erase_size: usize) -> usize { + let max_erase_size = if first_erase_size > second_erase_size { + first_erase_size + } else { + second_erase_size + }; + if max_erase_size % first_erase_size != 0 || max_erase_size % second_erase_size != 0 { + panic!("The erase sizes for the concatenated flashes must have have a gcd equal to the max erase size"); + } + max_erase_size +} + +impl ErrorType for ConcatFlash +where + First: ErrorType, + Second: ErrorType, + E: NorFlashError, +{ + type Error = E; +} + +impl ReadNorFlash for ConcatFlash +where + First: ReadNorFlash, + Second: ReadNorFlash, + E: NorFlashError, +{ + const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); + + fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.read(offset, &mut bytes[..len])?; + offset += len as u32; + bytes = &mut bytes[len..]; + } + + if !bytes.is_empty() { + self.1.read(offset - self.0.capacity() as u32, bytes)?; + } + + Ok(()) + } + + fn capacity(&self) -> usize { + self.0.capacity() + self.1.capacity() + } +} + +impl NorFlash for ConcatFlash +where + First: NorFlash, + Second: NorFlash, + E: NorFlashError, +{ + const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); + const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); + + fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.write(offset, &bytes[..len])?; + offset += len as u32; + bytes = &bytes[len..]; + } + + if !bytes.is_empty() { + self.1.write(offset - self.0.capacity() as u32, bytes)?; + } + + Ok(()) + } + + fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { + if from < self.0.capacity() as u32 { + let to = core::cmp::min(self.0.capacity() as u32, to); + self.0.erase(from, to)?; + from = self.0.capacity() as u32; + } + + if from < to { + self.1 + .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32)?; + } + + Ok(()) + } +} + +#[cfg(feature = "nightly")] +impl AsyncReadNorFlash for ConcatFlash +where + First: AsyncReadNorFlash, + Second: AsyncReadNorFlash, + E: NorFlashError, +{ + const READ_SIZE: usize = get_read_size(First::READ_SIZE, Second::READ_SIZE); + + async fn read(&mut self, mut offset: u32, mut bytes: &mut [u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.read(offset, &mut bytes[..len]).await?; + offset += len as u32; + bytes = &mut bytes[len..]; + } + + if !bytes.is_empty() { + self.1.read(offset - self.0.capacity() as u32, bytes).await?; + } + + Ok(()) + } + + fn capacity(&self) -> usize { + self.0.capacity() + self.1.capacity() + } +} + +#[cfg(feature = "nightly")] +impl AsyncNorFlash for ConcatFlash +where + First: AsyncNorFlash, + Second: AsyncNorFlash, + E: NorFlashError, +{ + const WRITE_SIZE: usize = get_write_size(First::WRITE_SIZE, Second::WRITE_SIZE); + const ERASE_SIZE: usize = get_max_erase_size(First::ERASE_SIZE, Second::ERASE_SIZE); + + async fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), E> { + if offset < self.0.capacity() as u32 { + let len = core::cmp::min(self.0.capacity() - offset as usize, bytes.len()); + self.0.write(offset, &bytes[..len]).await?; + offset += len as u32; + bytes = &bytes[len..]; + } + + if !bytes.is_empty() { + self.1.write(offset - self.0.capacity() as u32, bytes).await?; + } + + Ok(()) + } + + async fn erase(&mut self, mut from: u32, to: u32) -> Result<(), E> { + if from < self.0.capacity() as u32 { + let to = core::cmp::min(self.0.capacity() as u32, to); + self.0.erase(from, to).await?; + from = self.0.capacity() as u32; + } + + if from < to { + self.1 + .erase(from - self.0.capacity() as u32, to - self.0.capacity() as u32) + .await?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_write_and_read_across_flashes() { + let first = MemFlash::<64, 16, 4>::new(); + let second = MemFlash::<64, 64, 4>::new(); + let mut f = ConcatFlash::new(first, second); + + f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap(); + + assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0 .0[60..]); + assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1 .0[0..4]); + + let mut read_buf = [0; 8]; + f.read(60, &mut read_buf).unwrap(); + + assert_eq!(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], &read_buf); + } + + #[test] + fn can_erase_across_flashes() { + let mut first = MemFlash::<128, 16, 4>::new(); + let mut second = MemFlash::<128, 64, 4>::new(); + first.0.fill(0x00); + second.0.fill(0x00); + + let mut f = ConcatFlash::new(first, second); + + f.erase(64, 192).unwrap(); + + assert_eq!(&[0x00; 64], &f.0 .0[0..64]); + assert_eq!(&[0xff; 64], &f.0 .0[64..128]); + assert_eq!(&[0xff; 64], &f.1 .0[0..64]); + assert_eq!(&[0x00; 64], &f.1 .0[64..128]); + } + + pub struct MemFlash([u8; SIZE]); + + impl MemFlash { + pub const fn new() -> Self { + Self([0xff; SIZE]) + } + } + + impl ErrorType + for MemFlash + { + type Error = core::convert::Infallible; + } + + impl ReadNorFlash + for MemFlash + { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + let len = bytes.len(); + bytes.copy_from_slice(&self.0[offset as usize..offset as usize + len]); + Ok(()) + } + + fn capacity(&self) -> usize { + SIZE + } + } + + impl NorFlash + for MemFlash + { + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + let from = from as usize; + let to = to as usize; + assert_eq!(0, from % ERASE_SIZE); + assert_eq!(0, to % ERASE_SIZE); + self.0[from..to].fill(0xff); + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + let offset = offset as usize; + assert_eq!(0, bytes.len() % WRITE_SIZE); + assert_eq!(0, offset % WRITE_SIZE); + assert!(offset + bytes.len() <= SIZE); + + self.0[offset..offset + bytes.len()].copy_from_slice(bytes); + Ok(()) + } + } +} diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 73c81b465..3aad838bd 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -7,6 +7,8 @@ #[cfg(feature = "nightly")] pub mod adapter; +pub mod flash; + pub mod shared_bus; /// Set the configuration of a peripheral driver. From 316be179af500fdf31606f085adf77c6879a396d Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 24 May 2023 17:29:56 -0500 Subject: [PATCH 1170/1575] stm32: move to bind_interrupts disable lora functionality for now --- ci.sh | 3 +- embassy-stm32/src/dcmi.rs | 98 +- embassy-stm32/src/eth/mod.rs | 2 +- embassy-stm32/src/eth/v1/mod.rs | 53 +- embassy-stm32/src/eth/v2/mod.rs | 53 +- embassy-stm32/src/i2c/v1.rs | 13 +- embassy-stm32/src/i2c/v2.rs | 49 +- embassy-stm32/src/interrupt.rs | 4 - embassy-stm32/src/lib.rs | 36 +- embassy-stm32/src/sdmmc/mod.rs | 91 +- embassy-stm32/src/time_driver.rs | 3 +- embassy-stm32/src/tl_mbox/mod.rs | 58 +- embassy-stm32/src/usart/buffered.rs | 157 +-- embassy-stm32/src/usart/mod.rs | 164 +-- embassy-stm32/src/usb/usb.rs | 180 ++-- embassy-stm32/src/usb_otg/usb.rs | 963 +++++++++--------- examples/stm32f1/src/bin/usb_serial.rs | 9 +- examples/stm32f3/src/bin/usart_dma.rs | 9 +- examples/stm32f3/src/bin/usb_serial.rs | 9 +- examples/stm32f4/src/bin/i2c.rs | 9 +- examples/stm32f4/src/bin/sdmmc.rs | 10 +- examples/stm32f4/src/bin/usart.rs | 9 +- examples/stm32f4/src/bin/usart_buffered.rs | 9 +- examples/stm32f4/src/bin/usart_dma.rs | 9 +- examples/stm32f4/src/bin/usb_ethernet.rs | 9 +- examples/stm32f4/src/bin/usb_serial.rs | 9 +- examples/stm32f7/src/bin/eth.rs | 9 +- examples/stm32f7/src/bin/sdmmc.rs | 10 +- examples/stm32f7/src/bin/usart_dma.rs | 9 +- examples/stm32f7/src/bin/usb_serial.rs | 9 +- examples/stm32h5/src/bin/eth.rs | 9 +- examples/stm32h5/src/bin/i2c.rs | 9 +- examples/stm32h5/src/bin/usart.rs | 9 +- examples/stm32h5/src/bin/usart_dma.rs | 9 +- examples/stm32h5/src/bin/usart_split.rs | 9 +- examples/stm32h5/src/bin/usb_serial.rs | 9 +- examples/stm32h7/src/bin/camera.rs | 14 +- examples/stm32h7/src/bin/eth.rs | 9 +- examples/stm32h7/src/bin/eth_client.rs | 9 +- examples/stm32h7/src/bin/i2c.rs | 9 +- examples/stm32h7/src/bin/sdmmc.rs | 10 +- examples/stm32h7/src/bin/usart.rs | 9 +- examples/stm32h7/src/bin/usart_dma.rs | 9 +- examples/stm32h7/src/bin/usart_split.rs | 9 +- examples/stm32h7/src/bin/usb_serial.rs | 9 +- examples/stm32l0/src/bin/usart_dma.rs | 9 +- examples/stm32l0/src/bin/usart_irq.rs | 9 +- examples/stm32l4/src/bin/i2c.rs | 9 +- .../stm32l4/src/bin/i2c_blocking_async.rs | 9 +- examples/stm32l4/src/bin/i2c_dma.rs | 9 +- examples/stm32l4/src/bin/usart.rs | 9 +- examples/stm32l4/src/bin/usart_dma.rs | 9 +- examples/stm32l4/src/bin/usb_serial.rs | 9 +- examples/stm32l5/src/bin/usb_ethernet.rs | 9 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 9 +- examples/stm32l5/src/bin/usb_serial.rs | 9 +- examples/stm32u5/src/bin/usb_serial.rs | 9 +- examples/stm32wb/src/bin/tl_mbox.rs | 12 +- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 12 +- tests/stm32/src/bin/sdmmc.rs | 23 +- tests/stm32/src/bin/usart.rs | 52 +- tests/stm32/src/bin/usart_dma.rs | 85 +- tests/stm32/src/bin/usart_rx_ringbuffered.rs | 79 +- 63 files changed, 1395 insertions(+), 1172 deletions(-) delete mode 100644 embassy-stm32/src/interrupt.rs diff --git a/ci.sh b/ci.sh index d2bf4df97..106689d00 100755 --- a/ci.sh +++ b/ci.sh @@ -112,7 +112,6 @@ cargo batch \ --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- 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 --features embassy-nrf/nrf52840 --out-dir out/examples/boot/nrf --bin b \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns --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 \ @@ -143,6 +142,8 @@ cargo batch \ $BUILD_EXTRA +# --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \ + function run_elf { echo Running target=$1 elf=$2 STATUSCODE=$( diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index c19be86c6..5f3fc6a93 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -1,4 +1,5 @@ use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -8,7 +9,31 @@ use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::interrupt::{Interrupt, InterruptExt}; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let ris = crate::pac::DCMI.ris().read(); + if ris.err_ris() { + trace!("DCMI IRQ: Error."); + crate::pac::DCMI.ier().modify(|ier| ier.set_err_ie(false)); + } + if ris.ovr_ris() { + trace!("DCMI IRQ: Overrun."); + crate::pac::DCMI.ier().modify(|ier| ier.set_ovr_ie(false)); + } + if ris.frame_ris() { + trace!("DCMI IRQ: Frame captured."); + crate::pac::DCMI.ier().modify(|ier| ier.set_frame_ie(false)); + } + STATE.waker.wake(); + } +} /// The level on the VSync pin when the data is not valid on the parallel interface. #[derive(Clone, Copy, PartialEq)] @@ -94,7 +119,7 @@ where pub fn new_8bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -108,17 +133,17 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); config_pins!(v_sync, h_sync, pixclk); - Self::new_inner(peri, dma, irq, config, false, 0b00) + Self::new_inner(peri, dma, config, false, 0b00) } pub fn new_10bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -134,17 +159,17 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); config_pins!(v_sync, h_sync, pixclk); - Self::new_inner(peri, dma, irq, config, false, 0b01) + Self::new_inner(peri, dma, config, false, 0b01) } pub fn new_12bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -162,17 +187,17 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); config_pins!(v_sync, h_sync, pixclk); - Self::new_inner(peri, dma, irq, config, false, 0b10) + Self::new_inner(peri, dma, config, false, 0b10) } pub fn new_14bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -192,17 +217,17 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); config_pins!(v_sync, h_sync, pixclk); - Self::new_inner(peri, dma, irq, config, false, 0b11) + Self::new_inner(peri, dma, config, false, 0b11) } pub fn new_es_8bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -214,17 +239,17 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); config_pins!(pixclk); - Self::new_inner(peri, dma, irq, config, true, 0b00) + Self::new_inner(peri, dma, config, true, 0b00) } pub fn new_es_10bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -238,17 +263,17 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); config_pins!(pixclk); - Self::new_inner(peri, dma, irq, config, true, 0b01) + Self::new_inner(peri, dma, config, true, 0b01) } pub fn new_es_12bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -264,17 +289,17 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); config_pins!(pixclk); - Self::new_inner(peri, dma, irq, config, true, 0b10) + Self::new_inner(peri, dma, config, true, 0b10) } pub fn new_es_14bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -292,17 +317,16 @@ where pixclk: impl Peripheral

> + 'd, config: Config, ) -> Self { - into_ref!(peri, dma, irq); + into_ref!(peri, dma); config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); config_pins!(pixclk); - Self::new_inner(peri, dma, irq, config, true, 0b11) + Self::new_inner(peri, dma, config, true, 0b11) } fn new_inner( peri: PeripheralRef<'d, T>, dma: PeripheralRef<'d, Dma>, - irq: PeripheralRef<'d, T::Interrupt>, config: Config, use_embedded_synchronization: bool, edm: u8, @@ -322,30 +346,12 @@ where }); } - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { inner: peri, dma } } - unsafe fn on_interrupt(_: *mut ()) { - let ris = crate::pac::DCMI.ris().read(); - if ris.err_ris() { - trace!("DCMI IRQ: Error."); - crate::pac::DCMI.ier().modify(|ier| ier.set_err_ie(false)); - } - if ris.ovr_ris() { - trace!("DCMI IRQ: Overrun."); - crate::pac::DCMI.ier().modify(|ier| ier.set_ovr_ie(false)); - } - if ris.frame_ris() { - trace!("DCMI IRQ: Frame captured."); - crate::pac::DCMI.ier().modify(|ier| ier.set_frame_ie(false)); - } - STATE.waker.wake(); - } - unsafe fn toggle(enable: bool) { crate::pac::DCMI.cr().modify(|r| { r.set_enable(enable); diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index e1d7a09b4..4989e17c7 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -11,7 +11,7 @@ use core::task::Context; use embassy_net_driver::{Capabilities, LinkState}; use embassy_sync::waitqueue::AtomicWaker; -pub use self::_version::*; +pub use self::_version::{InterruptHandler, *}; #[allow(unused)] const MTU: usize = 1514; diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 9c0f4d66d..8ef2c3584 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -5,7 +5,7 @@ mod tx_desc; use core::sync::atomic::{fence, Ordering}; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; @@ -19,7 +19,30 @@ use crate::pac::AFIO; #[cfg(any(eth_v1b, eth_v1c))] use crate::pac::SYSCFG; use crate::pac::{ETH, RCC}; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler {} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + WAKER.wake(); + + // TODO: Check and clear more flags + unsafe { + let dma = ETH.ethernet_dma(); + + dma.dmasr().modify(|w| { + w.set_ts(true); + w.set_rs(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmasr().read(); + dma.dmasr().read(); + } + } +} pub struct Ethernet<'d, T: Instance, P: PHY> { _peri: PeripheralRef<'d, T>, @@ -77,7 +100,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { pub fn new( queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, - interrupt: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, ref_clk: impl Peripheral

> + 'd, mdio: impl Peripheral

> + 'd, mdc: impl Peripheral

> + 'd, @@ -91,7 +114,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { mac_addr: [u8; 6], phy_addr: u8, ) -> Self { - into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); unsafe { // Enable the necessary Clocks @@ -244,30 +267,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { P::phy_reset(&mut this); P::phy_init(&mut this); - interrupt.set_handler(Self::on_interrupt); - interrupt.enable(); + interrupt::ETH::steal().unpend(); + interrupt::ETH::steal().enable(); this } } - - fn on_interrupt(_cx: *mut ()) { - WAKER.wake(); - - // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmasr().modify(|w| { - w.set_ts(true); - w.set_rs(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmasr().read(); - dma.dmasr().read(); - } - } } unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index d49b1f767..b56c3e8ab 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -2,7 +2,7 @@ mod descriptors; use core::sync::atomic::{fence, Ordering}; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{into_ref, PeripheralRef}; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; @@ -10,7 +10,30 @@ use super::*; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Speed}; use crate::pac::ETH; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler {} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + WAKER.wake(); + + // TODO: Check and clear more flags + unsafe { + let dma = ETH.ethernet_dma(); + + dma.dmacsr().modify(|w| { + w.set_ti(true); + w.set_ri(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmacsr().read(); + dma.dmacsr().read(); + } + } +} const MTU: usize = 1514; // 14 Ethernet header + 1500 IP packet @@ -41,7 +64,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { pub fn new( queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, - interrupt: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, ref_clk: impl Peripheral

> + 'd, mdio: impl Peripheral

> + 'd, mdc: impl Peripheral

> + 'd, @@ -55,7 +78,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { mac_addr: [u8; 6], phy_addr: u8, ) -> Self { - into_ref!(peri, interrupt, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); unsafe { // Enable the necessary Clocks @@ -215,30 +238,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { P::phy_reset(&mut this); P::phy_init(&mut this); - interrupt.set_handler(Self::on_interrupt); - interrupt.enable(); + interrupt::ETH::steal().unpend(); + interrupt::ETH::steal().enable(); this } } - - fn on_interrupt(_cx: *mut ()) { - WAKER.wake(); - - // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmacsr().modify(|w| { - w.set_ti(true); - w.set_ri(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmacsr().read(); - dma.dmacsr().read(); - } - } } unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 4b47f0eb1..b9be2e587 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -9,7 +9,16 @@ use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; use crate::pac::i2c; use crate::time::Hertz; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() {} +} #[non_exhaustive] #[derive(Copy, Clone)] @@ -48,7 +57,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, - _irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, freq: Hertz, diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 853bc128f..642ddc18c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,7 +1,9 @@ use core::cmp; use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -11,10 +13,30 @@ use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; -use crate::interrupt::InterruptExt; use crate::pac::i2c; use crate::time::Hertz; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + let isr = regs.isr().read(); + + if isr.tcr() || isr.tc() { + T::state().waker.wake(); + } + // The flag can only be cleared by writting to nbytes, we won't do that here, so disable + // the interrupt + critical_section::with(|_| { + regs.cr1().modify(|w| w.set_tcie(false)); + }); + } +} #[non_exhaustive] #[derive(Copy, Clone)] @@ -56,13 +78,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, freq: Hertz, config: Config, ) -> Self { - into_ref!(peri, irq, scl, sda, tx_dma, rx_dma); + into_ref!(peri, scl, sda, tx_dma, rx_dma); T::enable(); T::reset(); @@ -111,9 +133,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }); } - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { _peri: peri, @@ -122,20 +143,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - unsafe fn on_interrupt(_: *mut ()) { - let regs = T::regs(); - let isr = regs.isr().read(); - - if isr.tcr() || isr.tc() { - T::state().waker.wake(); - } - // The flag can only be cleared by writting to nbytes, we won't do that here, so disable - // the interrupt - critical_section::with(|_| { - regs.cr1().modify(|w| w.set_tcie(false)); - }); - } - fn master_stop(&mut self) { unsafe { T::regs().cr2().write(|w| w.set_stop(true)); diff --git a/embassy-stm32/src/interrupt.rs b/embassy-stm32/src/interrupt.rs deleted file mode 100644 index b66e4c7ef..000000000 --- a/embassy-stm32/src/interrupt.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub use critical_section::{CriticalSection, Mutex}; -pub use embassy_cortex_m::interrupt::*; - -pub use crate::_generated::interrupt::*; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1920e2642..6722658c9 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -6,7 +6,6 @@ pub mod fmt; include!(concat!(env!("OUT_DIR"), "/_macros.rs")); // Utilities -pub mod interrupt; pub mod time; mod traits; @@ -73,6 +72,41 @@ pub(crate) mod _generated { include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } +pub mod interrupt { + //! Interrupt definitions and macros to bind them. + pub use cortex_m::interrupt::{CriticalSection, Mutex}; + pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; + + pub use crate::_generated::interrupt::*; + + /// Macro to bind interrupts to handlers. + /// + /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) + /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to + /// prove at compile-time that the right interrupts have been bound. + // developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. + #[macro_export] + macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); + )* + } + + $( + unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} + )* + )* + }; + } +} + // Reexports pub use _generated::{peripherals, Peripherals}; pub use embassy_cortex_m::executor; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index be788f1b0..be03a1bac 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -2,6 +2,7 @@ use core::default::Default; use core::future::poll_fn; +use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; @@ -17,7 +18,36 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::sdmmc::Sdmmc as RegBlock; use crate::rcc::RccPeripheral; use crate::time::Hertz; -use crate::{peripherals, Peripheral}; +use crate::{interrupt, peripherals, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl InterruptHandler { + fn data_interrupts(enable: bool) { + let regs = T::regs(); + // NOTE(unsafe) Atomic write + unsafe { + regs.maskr().write(|w| { + w.set_dcrcfailie(enable); + w.set_dtimeoutie(enable); + w.set_dataendie(enable); + + #[cfg(sdmmc_v2)] + w.set_dabortie(enable); + }); + } + } +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + Self::data_interrupts(false); + T::state().wake(); + } +} /// Frequency used for SD Card initialization. Must be no higher than 400 kHz. const SD_INIT_FREQ: Hertz = Hertz(400_000); @@ -223,7 +253,6 @@ impl Default for Config { /// Sdmmc device pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma = NoDma> { _peri: PeripheralRef<'d, T>, - irq: PeripheralRef<'d, T::Interrupt>, #[allow(unused)] dma: PeripheralRef<'d, Dma>, @@ -247,7 +276,7 @@ pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma = NoDma> { impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { pub fn new_1bit( sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, dma: impl Peripheral

+ 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, @@ -268,7 +297,6 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { Self::new_inner( sdmmc, - irq, dma, clk.map_into(), cmd.map_into(), @@ -282,7 +310,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { pub fn new_4bit( sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, dma: impl Peripheral

+ 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, @@ -312,7 +340,6 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { Self::new_inner( sdmmc, - irq, dma, clk.map_into(), cmd.map_into(), @@ -329,7 +356,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { pub fn new_1bit( sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -349,7 +376,6 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, - irq, NoDma.into_ref(), clk.map_into(), cmd.map_into(), @@ -363,7 +389,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { pub fn new_4bit( sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -392,7 +418,6 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, - irq, NoDma.into_ref(), clk.map_into(), cmd.map_into(), @@ -408,7 +433,6 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { fn new_inner( sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, clk: PeripheralRef<'d, AnyPin>, cmd: PeripheralRef<'d, AnyPin>, @@ -418,14 +442,13 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { d3: Option>, config: Config, ) -> Self { - into_ref!(sdmmc, irq, dma); + into_ref!(sdmmc, dma); T::enable(); T::reset(); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let regs = T::regs(); unsafe { @@ -451,7 +474,6 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self { _peri: sdmmc, - irq, dma, clk, @@ -691,7 +713,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); let transfer = self.prepare_datapath_read(&mut status, 64, 6); - Self::data_interrupts(true); + InterruptHandler::::data_interrupts(true); Self::cmd(Cmd::cmd6(set_function), true)?; // CMD6 let res = poll_fn(|cx| { @@ -767,7 +789,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); let transfer = self.prepare_datapath_read(&mut status, 64, 6); - Self::data_interrupts(true); + InterruptHandler::::data_interrupts(true); Self::cmd(Cmd::card_status(0), true)?; let res = poll_fn(|cx| { @@ -849,23 +871,6 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { } } - /// Enables the interrupts for data transfer - #[inline(always)] - fn data_interrupts(enable: bool) { - let regs = T::regs(); - // NOTE(unsafe) Atomic write - unsafe { - regs.maskr().write(|w| { - w.set_dcrcfailie(enable); - w.set_dtimeoutie(enable); - w.set_dataendie(enable); - - #[cfg(sdmmc_v2)] - w.set_dabortie(enable); - }); - } - } - async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { // Read the the 64-bit SCR register Self::cmd(Cmd::set_block_length(8), false)?; // CMD16 @@ -878,7 +883,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); let transfer = self.prepare_datapath_read(&mut scr[..], 8, 3); - Self::data_interrupts(true); + InterruptHandler::::data_interrupts(true); Self::cmd(Cmd::cmd51(), true)?; let res = poll_fn(|cx| { @@ -996,7 +1001,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { // Wait for the abort while Self::data_active() {} } - Self::data_interrupts(false); + InterruptHandler::::data_interrupts(false); Self::clear_interrupt_flags(); Self::stop_datapath(); } @@ -1170,7 +1175,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); let transfer = self.prepare_datapath_read(buffer, 512, 9); - Self::data_interrupts(true); + InterruptHandler::::data_interrupts(true); Self::cmd(Cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { @@ -1219,7 +1224,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::cmd(Cmd::write_single_block(address), true)?; let transfer = self.prepare_datapath_write(buffer, 512, 9); - Self::data_interrupts(true); + InterruptHandler::::data_interrupts(true); #[cfg(sdmmc_v2)] Self::cmd(Cmd::write_single_block(address), true)?; @@ -1279,17 +1284,11 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { pub fn clock(&self) -> Hertz { self.clock } - - #[inline(always)] - fn on_interrupt(_: *mut ()) { - Self::data_interrupts(false); - T::state().wake(); - } } impl<'d, T: Instance, Dma: SdmmcDma + 'd> Drop for Sdmmc<'d, T, Dma> { fn drop(&mut self) { - self.irq.disable(); + unsafe { T::Interrupt::steal() }.disable(); unsafe { Self::on_drop() }; critical_section::with(|_| unsafe { diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index d45c90dd8..2236fde28 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -4,13 +4,14 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::{mem, ptr}; use atomic_polyfill::{AtomicU32, AtomicU8}; +use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time::driver::{AlarmHandle, Driver}; use embassy_time::TICK_HZ; use stm32_metapac::timer::regs; -use crate::interrupt::{CriticalSection, InterruptExt}; +use crate::interrupt::InterruptExt; use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index 3651b8ea5..dc6104cc3 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -1,7 +1,6 @@ use core::mem::MaybeUninit; use bit_field::BitField; -use embassy_cortex_m::interrupt::InterruptExt; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -12,7 +11,7 @@ use self::mm::MemoryManager; use self::shci::{shci_ble_init, ShciBleInitCmdParam}; use self::sys::Sys; use self::unsafe_linked_list::LinkedListNode; -use crate::_generated::interrupt::{IPCC_C1_RX, IPCC_C1_TX}; +use crate::interrupt; use crate::ipcc::Ipcc; mod ble; @@ -55,6 +54,19 @@ pub struct FusInfoTable { fus_info: u32, } +/// Interrupt handler. +pub struct ReceiveInterruptHandler {} + +impl interrupt::Handler for ReceiveInterruptHandler { + unsafe fn on_interrupt() {} +} + +pub struct TransmitInterruptHandler {} + +impl interrupt::Handler for TransmitInterruptHandler { + unsafe fn on_interrupt() {} +} + /// # Version /// - 0 -> 3 = Build - 0: Untracked - 15:Released - x: Tracked version /// - 4 -> 7 = branch - 0: Mass Market - x: ... @@ -285,7 +297,11 @@ pub struct TlMbox { impl TlMbox { /// initializes low-level transport between CPU1 and BLE stack on CPU2 - pub fn init(ipcc: &mut Ipcc, rx_irq: IPCC_C1_RX, tx_irq: IPCC_C1_TX) -> TlMbox { + pub fn init( + ipcc: &mut Ipcc, + _irqs: impl interrupt::Binding + + interrupt::Binding, + ) -> TlMbox { unsafe { TL_REF_TABLE = MaybeUninit::new(RefTable { device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), @@ -326,23 +342,23 @@ impl TlMbox { let _ble = Ble::new(ipcc); let _mm = MemoryManager::new(); - rx_irq.disable(); - tx_irq.disable(); - - rx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); - tx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); - - rx_irq.set_handler(|ipcc| { - let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; - Self::interrupt_ipcc_rx_handler(ipcc); - }); - tx_irq.set_handler(|ipcc| { - let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; - Self::interrupt_ipcc_tx_handler(ipcc); - }); - - rx_irq.enable(); - tx_irq.enable(); + // rx_irq.disable(); + // tx_irq.disable(); + // + // rx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); + // tx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); + // + // rx_irq.set_handler(|ipcc| { + // let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; + // Self::interrupt_ipcc_rx_handler(ipcc); + // }); + // tx_irq.set_handler(|ipcc| { + // let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; + // Self::interrupt_ipcc_tx_handler(ipcc); + // }); + // + // rx_irq.enable(); + // tx_irq.enable(); TlMbox { _sys, _ble, _mm } } @@ -374,6 +390,7 @@ impl TlMbox { TL_CHANNEL.recv().await } + #[allow(dead_code)] fn interrupt_ipcc_rx_handler(ipcc: &mut Ipcc) { if ipcc.is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { sys::Sys::evt_handler(ipcc); @@ -384,6 +401,7 @@ impl TlMbox { } } + #[allow(dead_code)] fn interrupt_ipcc_tx_handler(ipcc: &mut Ipcc) { if ipcc.is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { // TODO: handle this case diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 12cf8b0fc..9f1da3583 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -8,6 +8,78 @@ use embassy_sync::waitqueue::AtomicWaker; use super::*; +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let state = T::buffered_state(); + + // RX + unsafe { + let sr = sr(r).read(); + clear_interrupt_flags(r, sr); + + if sr.rxne() { + if sr.pe() { + warn!("Parity error"); + } + if sr.fe() { + warn!("Framing error"); + } + if sr.ne() { + warn!("Noise error"); + } + if sr.ore() { + warn!("Overrun error"); + } + + let mut rx_writer = state.rx_buf.writer(); + let buf = rx_writer.push_slice(); + if !buf.is_empty() { + // This read also clears the error and idle interrupt flags on v1. + buf[0] = rdr(r).read_volatile(); + rx_writer.push_done(1); + } else { + // FIXME: Should we disable any further RX interrupts when the buffer becomes full. + } + + if state.rx_buf.is_full() { + state.rx_waker.wake(); + } + } + + if sr.idle() { + state.rx_waker.wake(); + }; + } + + // TX + unsafe { + if sr(r).read().txe() { + let mut tx_reader = state.tx_buf.reader(); + let buf = tx_reader.pop_slice(); + if !buf.is_empty() { + r.cr1().modify(|w| { + w.set_txeie(true); + }); + tdr(r).write_volatile(buf[0].into()); + tx_reader.pop_done(1); + state.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.cr1().modify(|w| { + w.set_txeie(false); + }); + } + } + } + } +} + pub struct State { rx_waker: AtomicWaker, rx_buf: RingBuffer, @@ -43,7 +115,7 @@ pub struct BufferedUartRx<'d, T: BasicInstance> { impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], @@ -53,12 +125,12 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::enable(); T::reset(); - Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) } pub fn new_with_rtscts( peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, @@ -81,13 +153,13 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) } #[cfg(not(any(usart_v1, usart_v2)))] pub fn new_with_de( peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, de: impl Peripheral

> + 'd, @@ -107,19 +179,18 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) + Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) } fn new_inner( _peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> BufferedUart<'d, T> { - into_ref!(_peri, rx, tx, irq); + into_ref!(_peri, rx, tx); let state = T::buffered_state(); let len = tx_buffer.len(); @@ -145,9 +216,8 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - irq.set_handler(on_interrupt::); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); Self { rx: BufferedUartRx { phantom: PhantomData }, @@ -336,71 +406,6 @@ impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { } } -unsafe fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let state = T::buffered_state(); - - // RX - unsafe { - let sr = sr(r).read(); - clear_interrupt_flags(r, sr); - - if sr.rxne() { - if sr.pe() { - warn!("Parity error"); - } - if sr.fe() { - warn!("Framing error"); - } - if sr.ne() { - warn!("Noise error"); - } - if sr.ore() { - warn!("Overrun error"); - } - - let mut rx_writer = state.rx_buf.writer(); - let buf = rx_writer.push_slice(); - if !buf.is_empty() { - // This read also clears the error and idle interrupt flags on v1. - buf[0] = rdr(r).read_volatile(); - rx_writer.push_done(1); - } else { - // FIXME: Should we disable any further RX interrupts when the buffer becomes full. - } - - if state.rx_buf.is_full() { - state.rx_waker.wake(); - } - } - - if sr.idle() { - state.rx_waker.wake(); - }; - } - - // TX - unsafe { - if sr(r).read().txe() { - let mut tx_reader = state.tx_buf.reader(); - let buf = tx_reader.pop_slice(); - if !buf.is_empty() { - r.cr1().modify(|w| { - w.set_txeie(true); - }); - tdr(r).write_volatile(buf[0].into()); - tx_reader.pop_done(1); - state.tx_waker.wake(); - } else { - // Disable interrupt until we have something to transmit again - r.cr1().modify(|w| { - w.set_txeie(false); - }); - } - } - } -} - impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index b4373529c..0f3e9412e 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use futures::future::{select, Either}; @@ -18,7 +18,71 @@ use crate::pac::usart::Lpuart as Regs; use crate::pac::usart::Usart as Regs; use crate::pac::usart::{regs, vals}; use crate::time::Hertz; -use crate::{peripherals, Peripheral}; +use crate::{interrupt, peripherals, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + let s = T::state(); + + let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; + + let mut wake = false; + let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); + if has_errors { + // clear all interrupts and DMA Rx Request + unsafe { + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); + } + + wake = true; + } else { + if cr1.idleie() && sr.idle() { + // IDLE detected: no more data will come + unsafe { + r.cr1().modify(|w| { + // disable idle line detection + w.set_idleie(false); + }); + } + + wake = true; + } + + if cr1.rxneie() { + // We cannot check the RXNE flag as it is auto-cleared by the DMA controller + + // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection + + wake = true; + } + } + + if wake { + compiler_fence(Ordering::SeqCst); + + s.rx_waker.wake(); + } + } +} #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DataBits { @@ -215,7 +279,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rx: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, config: Config, @@ -223,12 +287,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { T::enable(); T::reset(); - Self::new_inner(peri, irq, rx, rx_dma, config) + Self::new_inner(peri, rx, rx_dma, config) } pub fn new_with_rts( peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, @@ -246,17 +310,16 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { }); } - Self::new_inner(peri, irq, rx, rx_dma, config) + Self::new_inner(peri, rx, rx_dma, config) } fn new_inner( peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(peri, irq, rx, rx_dma); + into_ref!(peri, rx, rx_dma); let r = T::regs(); @@ -266,9 +329,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { configure(r, &config, T::frequency(), T::KIND, true, false); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); // create state once! let _s = T::state(); @@ -282,63 +344,6 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } - fn on_interrupt(_: *mut ()) { - let r = T::regs(); - let s = T::state(); - - let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; - - let mut wake = false; - let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); - if has_errors { - // clear all interrupts and DMA Rx Request - unsafe { - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // disable parity interrupt - w.set_peie(false); - // disable idle line interrupt - w.set_idleie(false); - }); - r.cr3().modify(|w| { - // disable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(false); - // disable DMA Rx Request - w.set_dmar(false); - }); - } - - wake = true; - } else { - if cr1.idleie() && sr.idle() { - // IDLE detected: no more data will come - unsafe { - r.cr1().modify(|w| { - // disable idle line detection - w.set_idleie(false); - }); - } - - wake = true; - } - - if cr1.rxneie() { - // We cannot check the RXNE flag as it is auto-cleared by the DMA controller - - // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection - - wake = true; - } - } - - if wake { - compiler_fence(Ordering::SeqCst); - - s.rx_waker.wake(); - } - } - #[cfg(any(usart_v1, usart_v2))] unsafe fn check_rx_flags(&mut self) -> Result { let r = T::regs(); @@ -643,7 +648,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, @@ -651,14 +656,14 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable(); T::reset(); - Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) } pub fn new_with_rtscts( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, @@ -678,7 +683,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { w.set_ctse(true); }); } - Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) } #[cfg(not(any(usart_v1, usart_v2)))] @@ -686,7 +691,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, de: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, @@ -703,19 +708,18 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { w.set_dem(true); }); } - Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) + Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) } fn new_inner( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - irq: impl Peripheral

+ 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(peri, rx, tx, irq, tx_dma, rx_dma); + into_ref!(peri, rx, tx, tx_dma, rx_dma); let r = T::regs(); @@ -726,9 +730,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { configure(r, &config, T::frequency(), T::KIND, true, true); - irq.set_handler(UartRx::::on_interrupt); - irq.unpend(); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); // create state once! let _s = T::state(); @@ -1068,6 +1071,9 @@ mod eio { #[cfg(feature = "nightly")] pub use buffered::*; + +#[cfg(feature = "nightly")] +pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler; #[cfg(feature = "nightly")] mod buffered; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 56c46476c..a9ff284ae 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -14,12 +14,99 @@ use embassy_usb_driver::{ use super::{DmPin, DpPin, Instance}; use crate::gpio::sealed::AFType; -use crate::interrupt::InterruptExt; +use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::usb::regs; use crate::pac::usb::vals::{EpType, Stat}; use crate::pac::USBRAM; use crate::rcc::sealed::RccPeripheral; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + unsafe { + let regs = T::regs(); + //let x = regs.istr().read().0; + //trace!("USB IRQ: {:08x}", x); + + let istr = regs.istr().read(); + + if istr.susp() { + //trace!("USB IRQ: susp"); + IRQ_SUSPEND.store(true, Ordering::Relaxed); + regs.cntr().modify(|w| { + w.set_fsusp(true); + w.set_lpmode(true); + }); + + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_susp(false); + regs.istr().write_value(clear); + + // Wake main thread. + BUS_WAKER.wake(); + } + + if istr.wkup() { + //trace!("USB IRQ: wkup"); + IRQ_RESUME.store(true, Ordering::Relaxed); + regs.cntr().modify(|w| { + w.set_fsusp(false); + w.set_lpmode(false); + }); + + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_wkup(false); + regs.istr().write_value(clear); + + // Wake main thread. + BUS_WAKER.wake(); + } + + if istr.reset() { + //trace!("USB IRQ: reset"); + IRQ_RESET.store(true, Ordering::Relaxed); + + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_reset(false); + regs.istr().write_value(clear); + + // Wake main thread. + BUS_WAKER.wake(); + } + + if istr.ctr() { + let index = istr.ep_id() as usize; + let mut epr = regs.epr(index).read(); + if epr.ctr_rx() { + if index == 0 && epr.setup() { + EP0_SETUP.store(true, Ordering::Relaxed); + } + //trace!("EP {} RX, setup={}", index, epr.setup()); + EP_OUT_WAKERS[index].wake(); + } + if epr.ctr_tx() { + //trace!("EP {} TX", index); + EP_IN_WAKERS[index].wake(); + } + epr.set_dtog_rx(false); + epr.set_dtog_tx(false); + epr.set_stat_rx(Stat(0)); + epr.set_stat_tx(Stat(0)); + epr.set_ctr_rx(!epr.ctr_rx()); + epr.set_ctr_tx(!epr.ctr_tx()); + regs.epr(index).write_value(epr); + } + } + } +} const EP_COUNT: usize = 8; @@ -168,14 +255,13 @@ pub struct Driver<'d, T: Instance> { impl<'d, T: Instance> Driver<'d, T> { pub fn new( _usb: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, dp: impl Peripheral

> + 'd, dm: impl Peripheral

> + 'd, ) -> Self { - into_ref!(irq, dp, dm); - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); + into_ref!(dp, dm); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); let regs = T::regs(); @@ -225,86 +311,6 @@ impl<'d, T: Instance> Driver<'d, T> { } } - fn on_interrupt(_: *mut ()) { - unsafe { - let regs = T::regs(); - //let x = regs.istr().read().0; - //trace!("USB IRQ: {:08x}", x); - - let istr = regs.istr().read(); - - if istr.susp() { - //trace!("USB IRQ: susp"); - IRQ_SUSPEND.store(true, Ordering::Relaxed); - regs.cntr().modify(|w| { - w.set_fsusp(true); - w.set_lpmode(true); - }); - - // Write 0 to clear. - let mut clear = regs::Istr(!0); - clear.set_susp(false); - regs.istr().write_value(clear); - - // Wake main thread. - BUS_WAKER.wake(); - } - - if istr.wkup() { - //trace!("USB IRQ: wkup"); - IRQ_RESUME.store(true, Ordering::Relaxed); - regs.cntr().modify(|w| { - w.set_fsusp(false); - w.set_lpmode(false); - }); - - // Write 0 to clear. - let mut clear = regs::Istr(!0); - clear.set_wkup(false); - regs.istr().write_value(clear); - - // Wake main thread. - BUS_WAKER.wake(); - } - - if istr.reset() { - //trace!("USB IRQ: reset"); - IRQ_RESET.store(true, Ordering::Relaxed); - - // Write 0 to clear. - let mut clear = regs::Istr(!0); - clear.set_reset(false); - regs.istr().write_value(clear); - - // Wake main thread. - BUS_WAKER.wake(); - } - - if istr.ctr() { - let index = istr.ep_id() as usize; - let mut epr = regs.epr(index).read(); - if epr.ctr_rx() { - if index == 0 && epr.setup() { - EP0_SETUP.store(true, Ordering::Relaxed); - } - //trace!("EP {} RX, setup={}", index, epr.setup()); - EP_OUT_WAKERS[index].wake(); - } - if epr.ctr_tx() { - //trace!("EP {} TX", index); - EP_IN_WAKERS[index].wake(); - } - epr.set_dtog_rx(false); - epr.set_dtog_tx(false); - epr.set_stat_rx(Stat(0)); - epr.set_stat_tx(Stat(0)); - epr.set_ctr_rx(!epr.ctr_rx()); - epr.set_ctr_tx(!epr.ctr_tx()); - regs.epr(index).write_value(epr); - } - } - } - fn alloc_ep_mem(&mut self, len: u16) -> u16 { assert!(len as usize % USBRAM_ALIGN == 0); let addr = self.ep_mem_free; diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 96c574ca8..921a73c8b 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -4,7 +4,7 @@ use core::task::Poll; use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; use embassy_cortex_m::interrupt::InterruptExt; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_common::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, @@ -14,490 +14,18 @@ use futures::future::poll_fn; use super::*; use crate::gpio::sealed::AFType; +use crate::interrupt; use crate::pac::otg::{regs, vals}; use crate::rcc::sealed::RccPeripheral; use crate::time::Hertz; -macro_rules! config_ulpi_pins { - ($($pin:ident),*) => { - into_ref!($($pin),*); - // NOTE(unsafe) Exclusive access to the registers - critical_section::with(|_| unsafe { - $( - $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); - #[cfg(gpio_v2)] - $pin.set_speed(crate::gpio::Speed::VeryHigh); - )* - }) - }; +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, } -// From `synopsys-usb-otg` crate: -// This calculation doesn't correspond to one in a Reference Manual. -// In fact, the required number of words is higher than indicated in RM. -// The following numbers are pessimistic and were figured out empirically. -const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30; - -/// USB PHY type -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum PhyType { - /// Internal Full-Speed PHY - /// - /// Available on most High-Speed peripherals. - InternalFullSpeed, - /// Internal High-Speed PHY - /// - /// Available on a few STM32 chips. - InternalHighSpeed, - /// External ULPI High-Speed PHY - ExternalHighSpeed, -} - -impl PhyType { - pub fn internal(&self) -> bool { - match self { - PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true, - PhyType::ExternalHighSpeed => false, - } - } - - pub fn high_speed(&self) -> bool { - match self { - PhyType::InternalFullSpeed => false, - PhyType::ExternalHighSpeed | PhyType::InternalHighSpeed => true, - } - } - - pub fn to_dspd(&self) -> vals::Dspd { - match self { - PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL, - PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED, - PhyType::ExternalHighSpeed => vals::Dspd::HIGH_SPEED, - } - } -} - -/// Indicates that [State::ep_out_buffers] is empty. -const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX; - -pub struct State { - /// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true. - ep0_setup_data: UnsafeCell<[u8; 8]>, - ep0_setup_ready: AtomicBool, - ep_in_wakers: [AtomicWaker; EP_COUNT], - ep_out_wakers: [AtomicWaker; EP_COUNT], - /// RX FIFO is shared so extra buffers are needed to dequeue all data without waiting on each endpoint. - /// Buffers are ready when associated [State::ep_out_size] != [EP_OUT_BUFFER_EMPTY]. - ep_out_buffers: [UnsafeCell<*mut u8>; EP_COUNT], - ep_out_size: [AtomicU16; EP_COUNT], - bus_waker: AtomicWaker, -} - -unsafe impl Send for State {} -unsafe impl Sync for State {} - -impl State { - pub const fn new() -> Self { - const NEW_AW: AtomicWaker = AtomicWaker::new(); - const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _); - const NEW_SIZE: AtomicU16 = AtomicU16::new(EP_OUT_BUFFER_EMPTY); - - Self { - ep0_setup_data: UnsafeCell::new([0u8; 8]), - ep0_setup_ready: AtomicBool::new(false), - ep_in_wakers: [NEW_AW; EP_COUNT], - ep_out_wakers: [NEW_AW; EP_COUNT], - ep_out_buffers: [NEW_BUF; EP_COUNT], - ep_out_size: [NEW_SIZE; EP_COUNT], - bus_waker: NEW_AW, - } - } -} - -#[derive(Debug, Clone, Copy)] -struct EndpointData { - ep_type: EndpointType, - max_packet_size: u16, - fifo_size_words: u16, -} - -pub struct Driver<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - irq: PeripheralRef<'d, T::Interrupt>, - ep_in: [Option; MAX_EP_COUNT], - ep_out: [Option; MAX_EP_COUNT], - ep_out_buffer: &'d mut [u8], - ep_out_buffer_offset: usize, - phy_type: PhyType, -} - -impl<'d, T: Instance> Driver<'d, T> { - /// Initializes USB OTG peripheral with internal Full-Speed PHY. - /// - /// # Arguments - /// - /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. - /// Must be large enough to fit all OUT endpoint max packet sizes. - /// Endpoint allocation will fail if it is too small. - pub fn new_fs( - _peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - dp: impl Peripheral

> + 'd, - dm: impl Peripheral

> + 'd, - ep_out_buffer: &'d mut [u8], - ) -> Self { - into_ref!(dp, dm, irq); - - unsafe { - dp.set_as_af(dp.af_num(), AFType::OutputPushPull); - dm.set_as_af(dm.af_num(), AFType::OutputPushPull); - } - - Self { - phantom: PhantomData, - irq, - ep_in: [None; MAX_EP_COUNT], - ep_out: [None; MAX_EP_COUNT], - ep_out_buffer, - ep_out_buffer_offset: 0, - phy_type: PhyType::InternalFullSpeed, - } - } - - /// Initializes USB OTG peripheral with external High-Speed PHY. - /// - /// # Arguments - /// - /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. - /// Must be large enough to fit all OUT endpoint max packet sizes. - /// Endpoint allocation will fail if it is too small. - pub fn new_hs_ulpi( - _peri: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - ulpi_clk: impl Peripheral

> + 'd, - ulpi_dir: impl Peripheral

> + 'd, - ulpi_nxt: impl Peripheral

> + 'd, - ulpi_stp: impl Peripheral

> + 'd, - ulpi_d0: impl Peripheral

> + 'd, - ulpi_d1: impl Peripheral

> + 'd, - ulpi_d2: impl Peripheral

> + 'd, - ulpi_d3: impl Peripheral

> + 'd, - ulpi_d4: impl Peripheral

> + 'd, - ulpi_d5: impl Peripheral

> + 'd, - ulpi_d6: impl Peripheral

> + 'd, - ulpi_d7: impl Peripheral

> + 'd, - ep_out_buffer: &'d mut [u8], - ) -> Self { - assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB"); - - config_ulpi_pins!( - ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, - ulpi_d7 - ); - - into_ref!(irq); - - Self { - phantom: PhantomData, - irq, - ep_in: [None; MAX_EP_COUNT], - ep_out: [None; MAX_EP_COUNT], - ep_out_buffer, - ep_out_buffer_offset: 0, - phy_type: PhyType::ExternalHighSpeed, - } - } - - // Returns total amount of words (u32) allocated in dedicated FIFO - fn allocated_fifo_words(&self) -> u16 { - RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out) + ep_fifo_size(&self.ep_in) - } - - fn alloc_endpoint( - &mut self, - ep_type: EndpointType, - max_packet_size: u16, - interval_ms: u8, - ) -> Result, EndpointAllocError> { - trace!( - "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", - ep_type, - max_packet_size, - interval_ms, - D::dir() - ); - - if D::dir() == Direction::Out { - if self.ep_out_buffer_offset + max_packet_size as usize >= self.ep_out_buffer.len() { - error!("Not enough endpoint out buffer capacity"); - return Err(EndpointAllocError); - } - }; - - let fifo_size_words = match D::dir() { - Direction::Out => (max_packet_size + 3) / 4, - // INEPTXFD requires minimum size of 16 words - Direction::In => u16::max((max_packet_size + 3) / 4, 16), - }; - - if fifo_size_words + self.allocated_fifo_words() > T::FIFO_DEPTH_WORDS { - error!("Not enough FIFO capacity"); - return Err(EndpointAllocError); - } - - let eps = match D::dir() { - Direction::Out => &mut self.ep_out, - Direction::In => &mut self.ep_in, - }; - - // Find free endpoint slot - let slot = eps.iter_mut().enumerate().find(|(i, ep)| { - if *i == 0 && ep_type != EndpointType::Control { - // reserved for control pipe - false - } else { - ep.is_none() - } - }); - - let index = match slot { - Some((index, ep)) => { - *ep = Some(EndpointData { - ep_type, - max_packet_size, - fifo_size_words, - }); - index - } - None => { - error!("No free endpoints available"); - return Err(EndpointAllocError); - } - }; - - trace!(" index={}", index); - - if D::dir() == Direction::Out { - // Buffer capacity check was done above, now allocation cannot fail - unsafe { - *T::state().ep_out_buffers[index].get() = - self.ep_out_buffer.as_mut_ptr().offset(self.ep_out_buffer_offset as _); - } - self.ep_out_buffer_offset += max_packet_size as usize; - } - - Ok(Endpoint { - _phantom: PhantomData, - info: EndpointInfo { - addr: EndpointAddress::from_parts(index, D::dir()), - ep_type, - max_packet_size, - interval_ms, - }, - }) - } -} - -impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { - type EndpointOut = Endpoint<'d, T, Out>; - type EndpointIn = Endpoint<'d, T, In>; - type ControlPipe = ControlPipe<'d, T>; - type Bus = Bus<'d, T>; - - fn alloc_endpoint_in( - &mut self, - ep_type: EndpointType, - max_packet_size: u16, - interval_ms: u8, - ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) - } - - fn alloc_endpoint_out( - &mut self, - ep_type: EndpointType, - max_packet_size: u16, - interval_ms: u8, - ) -> Result { - self.alloc_endpoint(ep_type, max_packet_size, interval_ms) - } - - fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { - let ep_out = self - .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) - .unwrap(); - let ep_in = self - .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) - .unwrap(); - assert_eq!(ep_out.info.addr.index(), 0); - assert_eq!(ep_in.info.addr.index(), 0); - - trace!("start"); - - ( - Bus { - phantom: PhantomData, - irq: self.irq, - ep_in: self.ep_in, - ep_out: self.ep_out, - phy_type: self.phy_type, - enabled: false, - }, - ControlPipe { - _phantom: PhantomData, - max_packet_size: control_max_packet_size, - ep_out, - ep_in, - }, - ) - } -} - -pub struct Bus<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - irq: PeripheralRef<'d, T::Interrupt>, - ep_in: [Option; MAX_EP_COUNT], - ep_out: [Option; MAX_EP_COUNT], - phy_type: PhyType, - enabled: bool, -} - -impl<'d, T: Instance> Bus<'d, T> { - fn restore_irqs() { - // SAFETY: atomic write - unsafe { - T::regs().gintmsk().write(|w| { - w.set_usbrst(true); - w.set_enumdnem(true); - w.set_usbsuspm(true); - w.set_wuim(true); - w.set_iepint(true); - w.set_oepint(true); - w.set_rxflvlm(true); - }); - } - } -} - -impl<'d, T: Instance> Bus<'d, T> { - fn init_fifo(&mut self) { - trace!("init_fifo"); - - let r = T::regs(); - - // Configure RX fifo size. All endpoints share the same FIFO area. - let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); - trace!("configuring rx fifo size={}", rx_fifo_size_words); - - // SAFETY: register is exclusive to `Bus` with `&mut self` - unsafe { r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)) }; - - // Configure TX (USB in direction) fifo size for each endpoint - let mut fifo_top = rx_fifo_size_words; - for i in 0..T::ENDPOINT_COUNT { - if let Some(ep) = self.ep_in[i] { - trace!( - "configuring tx fifo ep={}, offset={}, size={}", - i, - fifo_top, - ep.fifo_size_words - ); - - let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; - - // SAFETY: register is exclusive to `Bus` with `&mut self` - unsafe { - dieptxf.write(|w| { - w.set_fd(ep.fifo_size_words); - w.set_sa(fifo_top); - }); - } - - fifo_top += ep.fifo_size_words; - } - } - - assert!( - fifo_top <= T::FIFO_DEPTH_WORDS, - "FIFO allocations exceeded maximum capacity" - ); - } - - fn configure_endpoints(&mut self) { - trace!("configure_endpoints"); - - let r = T::regs(); - - // Configure IN endpoints - for (index, ep) in self.ep_in.iter().enumerate() { - if let Some(ep) = ep { - // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { - r.diepctl(index).write(|w| { - if index == 0 { - w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); - } else { - w.set_mpsiz(ep.max_packet_size); - w.set_eptyp(to_eptyp(ep.ep_type)); - w.set_sd0pid_sevnfrm(true); - } - }); - }); - } - } - - // Configure OUT endpoints - for (index, ep) in self.ep_out.iter().enumerate() { - if let Some(ep) = ep { - // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { - r.doepctl(index).write(|w| { - if index == 0 { - w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); - } else { - w.set_mpsiz(ep.max_packet_size); - w.set_eptyp(to_eptyp(ep.ep_type)); - w.set_sd0pid_sevnfrm(true); - } - }); - - r.doeptsiz(index).modify(|w| { - w.set_xfrsiz(ep.max_packet_size as _); - if index == 0 { - w.set_rxdpid_stupcnt(1); - } else { - w.set_pktcnt(1); - } - }); - }); - } - } - - // Enable IRQs for allocated endpoints - // SAFETY: register is exclusive to `Bus` with `&mut self` - unsafe { - r.daintmsk().modify(|w| { - w.set_iepm(ep_irq_mask(&self.ep_in)); - // OUT interrupts not used, handled in RXFLVL - // w.set_oepm(ep_irq_mask(&self.ep_out)); - }); - } - } - - fn disable(&mut self) { - self.irq.disable(); - self.irq.remove_handler(); - - ::disable(); - - #[cfg(stm32l4)] - unsafe { - crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); - // Cannot disable PWR, because other peripherals might be using it - } - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { trace!("irq"); let r = T::regs(); let state = T::state(); @@ -641,6 +169,478 @@ impl<'d, T: Instance> Bus<'d, T> { } } +macro_rules! config_ulpi_pins { + ($($pin:ident),*) => { + into_ref!($($pin),*); + // NOTE(unsafe) Exclusive access to the registers + critical_section::with(|_| unsafe { + $( + $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + $pin.set_speed(crate::gpio::Speed::VeryHigh); + )* + }) + }; +} + +// From `synopsys-usb-otg` crate: +// This calculation doesn't correspond to one in a Reference Manual. +// In fact, the required number of words is higher than indicated in RM. +// The following numbers are pessimistic and were figured out empirically. +const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30; + +/// USB PHY type +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PhyType { + /// Internal Full-Speed PHY + /// + /// Available on most High-Speed peripherals. + InternalFullSpeed, + /// Internal High-Speed PHY + /// + /// Available on a few STM32 chips. + InternalHighSpeed, + /// External ULPI High-Speed PHY + ExternalHighSpeed, +} + +impl PhyType { + pub fn internal(&self) -> bool { + match self { + PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true, + PhyType::ExternalHighSpeed => false, + } + } + + pub fn high_speed(&self) -> bool { + match self { + PhyType::InternalFullSpeed => false, + PhyType::ExternalHighSpeed | PhyType::InternalHighSpeed => true, + } + } + + pub fn to_dspd(&self) -> vals::Dspd { + match self { + PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL, + PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED, + PhyType::ExternalHighSpeed => vals::Dspd::HIGH_SPEED, + } + } +} + +/// Indicates that [State::ep_out_buffers] is empty. +const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX; + +pub struct State { + /// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true. + ep0_setup_data: UnsafeCell<[u8; 8]>, + ep0_setup_ready: AtomicBool, + ep_in_wakers: [AtomicWaker; EP_COUNT], + ep_out_wakers: [AtomicWaker; EP_COUNT], + /// RX FIFO is shared so extra buffers are needed to dequeue all data without waiting on each endpoint. + /// Buffers are ready when associated [State::ep_out_size] != [EP_OUT_BUFFER_EMPTY]. + ep_out_buffers: [UnsafeCell<*mut u8>; EP_COUNT], + ep_out_size: [AtomicU16; EP_COUNT], + bus_waker: AtomicWaker, +} + +unsafe impl Send for State {} +unsafe impl Sync for State {} + +impl State { + pub const fn new() -> Self { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _); + const NEW_SIZE: AtomicU16 = AtomicU16::new(EP_OUT_BUFFER_EMPTY); + + Self { + ep0_setup_data: UnsafeCell::new([0u8; 8]), + ep0_setup_ready: AtomicBool::new(false), + ep_in_wakers: [NEW_AW; EP_COUNT], + ep_out_wakers: [NEW_AW; EP_COUNT], + ep_out_buffers: [NEW_BUF; EP_COUNT], + ep_out_size: [NEW_SIZE; EP_COUNT], + bus_waker: NEW_AW, + } + } +} + +#[derive(Debug, Clone, Copy)] +struct EndpointData { + ep_type: EndpointType, + max_packet_size: u16, + fifo_size_words: u16, +} + +pub struct Driver<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + ep_in: [Option; MAX_EP_COUNT], + ep_out: [Option; MAX_EP_COUNT], + ep_out_buffer: &'d mut [u8], + ep_out_buffer_offset: usize, + phy_type: PhyType, +} + +impl<'d, T: Instance> Driver<'d, T> { + /// Initializes USB OTG peripheral with internal Full-Speed PHY. + /// + /// # Arguments + /// + /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// Must be large enough to fit all OUT endpoint max packet sizes. + /// Endpoint allocation will fail if it is too small. + pub fn new_fs( + _peri: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + dp: impl Peripheral

> + 'd, + dm: impl Peripheral

> + 'd, + ep_out_buffer: &'d mut [u8], + ) -> Self { + into_ref!(dp, dm); + + unsafe { + dp.set_as_af(dp.af_num(), AFType::OutputPushPull); + dm.set_as_af(dm.af_num(), AFType::OutputPushPull); + } + + Self { + phantom: PhantomData, + ep_in: [None; MAX_EP_COUNT], + ep_out: [None; MAX_EP_COUNT], + ep_out_buffer, + ep_out_buffer_offset: 0, + phy_type: PhyType::InternalFullSpeed, + } + } + + /// Initializes USB OTG peripheral with external High-Speed PHY. + /// + /// # Arguments + /// + /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets. + /// Must be large enough to fit all OUT endpoint max packet sizes. + /// Endpoint allocation will fail if it is too small. + pub fn new_hs_ulpi( + _peri: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, + ulpi_clk: impl Peripheral

> + 'd, + ulpi_dir: impl Peripheral

> + 'd, + ulpi_nxt: impl Peripheral

> + 'd, + ulpi_stp: impl Peripheral

> + 'd, + ulpi_d0: impl Peripheral

> + 'd, + ulpi_d1: impl Peripheral

> + 'd, + ulpi_d2: impl Peripheral

> + 'd, + ulpi_d3: impl Peripheral

> + 'd, + ulpi_d4: impl Peripheral

> + 'd, + ulpi_d5: impl Peripheral

> + 'd, + ulpi_d6: impl Peripheral

> + 'd, + ulpi_d7: impl Peripheral

> + 'd, + ep_out_buffer: &'d mut [u8], + ) -> Self { + assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB"); + + config_ulpi_pins!( + ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, + ulpi_d7 + ); + + Self { + phantom: PhantomData, + ep_in: [None; MAX_EP_COUNT], + ep_out: [None; MAX_EP_COUNT], + ep_out_buffer, + ep_out_buffer_offset: 0, + phy_type: PhyType::ExternalHighSpeed, + } + } + + // Returns total amount of words (u32) allocated in dedicated FIFO + fn allocated_fifo_words(&self) -> u16 { + RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out) + ep_fifo_size(&self.ep_in) + } + + fn alloc_endpoint( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result, EndpointAllocError> { + trace!( + "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", + ep_type, + max_packet_size, + interval_ms, + D::dir() + ); + + if D::dir() == Direction::Out { + if self.ep_out_buffer_offset + max_packet_size as usize >= self.ep_out_buffer.len() { + error!("Not enough endpoint out buffer capacity"); + return Err(EndpointAllocError); + } + }; + + let fifo_size_words = match D::dir() { + Direction::Out => (max_packet_size + 3) / 4, + // INEPTXFD requires minimum size of 16 words + Direction::In => u16::max((max_packet_size + 3) / 4, 16), + }; + + if fifo_size_words + self.allocated_fifo_words() > T::FIFO_DEPTH_WORDS { + error!("Not enough FIFO capacity"); + return Err(EndpointAllocError); + } + + let eps = match D::dir() { + Direction::Out => &mut self.ep_out, + Direction::In => &mut self.ep_in, + }; + + // Find free endpoint slot + let slot = eps.iter_mut().enumerate().find(|(i, ep)| { + if *i == 0 && ep_type != EndpointType::Control { + // reserved for control pipe + false + } else { + ep.is_none() + } + }); + + let index = match slot { + Some((index, ep)) => { + *ep = Some(EndpointData { + ep_type, + max_packet_size, + fifo_size_words, + }); + index + } + None => { + error!("No free endpoints available"); + return Err(EndpointAllocError); + } + }; + + trace!(" index={}", index); + + if D::dir() == Direction::Out { + // Buffer capacity check was done above, now allocation cannot fail + unsafe { + *T::state().ep_out_buffers[index].get() = + self.ep_out_buffer.as_mut_ptr().offset(self.ep_out_buffer_offset as _); + } + self.ep_out_buffer_offset += max_packet_size as usize; + } + + Ok(Endpoint { + _phantom: PhantomData, + info: EndpointInfo { + addr: EndpointAddress::from_parts(index, D::dir()), + ep_type, + max_packet_size, + interval_ms, + }, + }) + } +} + +impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { + type EndpointOut = Endpoint<'d, T, Out>; + type EndpointIn = Endpoint<'d, T, In>; + type ControlPipe = ControlPipe<'d, T>; + type Bus = Bus<'d, T>; + + fn alloc_endpoint_in( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + } + + fn alloc_endpoint_out( + &mut self, + ep_type: EndpointType, + max_packet_size: u16, + interval_ms: u8, + ) -> Result { + self.alloc_endpoint(ep_type, max_packet_size, interval_ms) + } + + fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { + let ep_out = self + .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + let ep_in = self + .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) + .unwrap(); + assert_eq!(ep_out.info.addr.index(), 0); + assert_eq!(ep_in.info.addr.index(), 0); + + trace!("start"); + + ( + Bus { + phantom: PhantomData, + ep_in: self.ep_in, + ep_out: self.ep_out, + phy_type: self.phy_type, + enabled: false, + }, + ControlPipe { + _phantom: PhantomData, + max_packet_size: control_max_packet_size, + ep_out, + ep_in, + }, + ) + } +} + +pub struct Bus<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + ep_in: [Option; MAX_EP_COUNT], + ep_out: [Option; MAX_EP_COUNT], + phy_type: PhyType, + enabled: bool, +} + +impl<'d, T: Instance> Bus<'d, T> { + fn restore_irqs() { + // SAFETY: atomic write + unsafe { + T::regs().gintmsk().write(|w| { + w.set_usbrst(true); + w.set_enumdnem(true); + w.set_usbsuspm(true); + w.set_wuim(true); + w.set_iepint(true); + w.set_oepint(true); + w.set_rxflvlm(true); + }); + } + } +} + +impl<'d, T: Instance> Bus<'d, T> { + fn init_fifo(&mut self) { + trace!("init_fifo"); + + let r = T::regs(); + + // Configure RX fifo size. All endpoints share the same FIFO area. + let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); + trace!("configuring rx fifo size={}", rx_fifo_size_words); + + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)) }; + + // Configure TX (USB in direction) fifo size for each endpoint + let mut fifo_top = rx_fifo_size_words; + for i in 0..T::ENDPOINT_COUNT { + if let Some(ep) = self.ep_in[i] { + trace!( + "configuring tx fifo ep={}, offset={}, size={}", + i, + fifo_top, + ep.fifo_size_words + ); + + let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; + + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { + dieptxf.write(|w| { + w.set_fd(ep.fifo_size_words); + w.set_sa(fifo_top); + }); + } + + fifo_top += ep.fifo_size_words; + } + } + + assert!( + fifo_top <= T::FIFO_DEPTH_WORDS, + "FIFO allocations exceeded maximum capacity" + ); + } + + fn configure_endpoints(&mut self) { + trace!("configure_endpoints"); + + let r = T::regs(); + + // Configure IN endpoints + for (index, ep) in self.ep_in.iter().enumerate() { + if let Some(ep) = ep { + // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.diepctl(index).write(|w| { + if index == 0 { + w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); + } else { + w.set_mpsiz(ep.max_packet_size); + w.set_eptyp(to_eptyp(ep.ep_type)); + w.set_sd0pid_sevnfrm(true); + } + }); + }); + } + } + + // Configure OUT endpoints + for (index, ep) in self.ep_out.iter().enumerate() { + if let Some(ep) = ep { + // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Endpoint` so critical section is needed for RMW + critical_section::with(|_| unsafe { + r.doepctl(index).write(|w| { + if index == 0 { + w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); + } else { + w.set_mpsiz(ep.max_packet_size); + w.set_eptyp(to_eptyp(ep.ep_type)); + w.set_sd0pid_sevnfrm(true); + } + }); + + r.doeptsiz(index).modify(|w| { + w.set_xfrsiz(ep.max_packet_size as _); + if index == 0 { + w.set_rxdpid_stupcnt(1); + } else { + w.set_pktcnt(1); + } + }); + }); + } + } + + // Enable IRQs for allocated endpoints + // SAFETY: register is exclusive to `Bus` with `&mut self` + unsafe { + r.daintmsk().modify(|w| { + w.set_iepm(ep_irq_mask(&self.ep_in)); + // OUT interrupts not used, handled in RXFLVL + // w.set_oepm(ep_irq_mask(&self.ep_out)); + }); + } + } + + fn disable(&mut self) { + unsafe { T::Interrupt::steal() }.disable(); + + ::disable(); + + #[cfg(stm32l4)] + unsafe { + crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); + // Cannot disable PWR, because other peripherals might be using it + } + } +} + impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { async fn poll(&mut self) -> Event { poll_fn(move |cx| { @@ -902,9 +902,8 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { ::enable(); ::reset(); - self.irq.set_handler(Self::on_interrupt); - self.irq.unpend(); - self.irq.enable(); + T::Interrupt::steal().unpend(); + T::Interrupt::steal().enable(); let r = T::regs(); let core_id = r.cid().read().0; diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index 07cad84ef..663099ff7 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -8,13 +8,17 @@ use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::{Duration, Timer}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USB_LP_CAN1_RX0 => usb::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -35,8 +39,7 @@ async fn main(_spawner: Spawner) { } // Create the driver, from the HAL. - let irq = interrupt::take!(USB_LP_CAN1_RX0); - let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); // Create embassy-usb Config let config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 47121acf1..85f01a69e 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -7,19 +7,22 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USART1 => usart::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); let config = Config::default(); - let irq = interrupt::take!(USART1); - let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, irq, p.DMA1_CH4, NoDma, config); + let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index 5b4e0a91a..f15f333b7 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -8,13 +8,17 @@ use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::{Duration, Timer}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USB_LP_CAN_RX0 => usb::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -33,8 +37,7 @@ async fn main(_spawner: Spawner) { dp_pullup.set_high(); // Create the driver, from the HAL. - let irq = interrupt::take!(USB_LP_CAN_RX0); - let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); // Create embassy-usb Config let config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index f8ae0890c..a92957325 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -6,25 +6,28 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; -use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; +bind_interrupts!(struct Irqs { + I2C2_EV => i2c::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new( p.I2C2, p.PB10, p.PB11, - irq, + Irqs, NoDma, NoDma, Hertz(100_000), diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index eeecbd321..6ec7d0fec 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -6,13 +6,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; use embassy_stm32::time::mhz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; use {defmt_rtt as _, panic_probe as _}; /// This is a safeguard to not overwrite any data on the SD card. /// If you don't care about SD card contents, set this to `true` to test writes. const ALLOW_WRITES: bool = false; +bind_interrupts!(struct Irqs { + SDIO => sdmmc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -21,11 +25,9 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - let irq = interrupt::take!(SDIO); - let mut sdmmc = Sdmmc::new_4bit( p.SDIO, - irq, + Irqs, p.DMA2_CH3, p.PC12, p.PD2, diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 8f41bb6c4..7680fe845 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs @@ -5,10 +5,14 @@ use cortex_m_rt::entry; use defmt::*; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USART3 => usart::InterruptHandler; +}); + #[entry] fn main() -> ! { info!("Hello World!"); @@ -16,8 +20,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let irq = interrupt::take!(USART3); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, NoDma, NoDma, config); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index a93f8baeb..c573dc3a3 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -4,11 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::interrupt; use embassy_stm32::usart::{BufferedUart, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use embedded_io::asynch::BufRead; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USART3 => usart::BufferedInterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); @@ -16,10 +20,9 @@ async fn main(_spawner: Spawner) { let config = Config::default(); - let irq = interrupt::take!(USART3); let mut tx_buf = [0u8; 32]; let mut rx_buf = [0u8; 32]; - let mut buf_usart = BufferedUart::new(p.USART3, irq, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config); + let mut buf_usart = BufferedUart::new(p.USART3, Irqs, p.PD9, p.PD8, &mut tx_buf, &mut rx_buf, config); loop { let buf = buf_usart.fill_buf().await.unwrap(); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index 78baeaa0d..3408ec370 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -7,19 +7,22 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USART3 => usart::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); let config = Config::default(); - let irq = interrupt::take!(USART3); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, irq, p.DMA1_CH3, NoDma, config); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 9131e5896..c4e395f0f 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -9,7 +9,7 @@ use embassy_net::{Stack, StackResources}; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::Driver; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; @@ -45,6 +45,10 @@ async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } +bind_interrupts!(struct Irqs { + OTG_FS => usb_otg::InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { info!("Hello World!"); @@ -56,9 +60,8 @@ async fn main(spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(OTG_FS); let ep_out_buffer = &mut singleton!([0; 256])[..]; - let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, ep_out_buffer); + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index d2b1dca43..f8f5940a7 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -6,13 +6,17 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use futures::future::join; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + OTG_FS => usb_otg::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello World!"); @@ -24,9 +28,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(OTG_FS); let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index b947361ac..6d286c368 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -11,7 +11,7 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, eth, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; @@ -27,6 +27,10 @@ macro_rules! singleton { }}; } +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; +}); + type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] @@ -48,13 +52,12 @@ async fn main(spawner: Spawner) -> ! { rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); - let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( singleton!(PacketQueue::<16, 16>::new()), p.ETH, - eth_int, + Irqs, p.PA1, p.PA2, p.PC1, diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index c050a4002..9d43892a0 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; use embassy_stm32::time::mhz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SDMMC1 => sdmmc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -18,11 +22,9 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let irq = interrupt::take!(SDMMC1); - let mut sdmmc = Sdmmc::new_4bit( p.SDMMC1, - irq, + Irqs, p.DMA2_CH3, p.PC12, p.PD2, diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index 4827c52ae..4700287a7 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -7,17 +7,20 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART7 => usart::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let irq = interrupt::take!(UART7); - let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, irq, p.DMA1_CH1, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index dca90d9cb..763309ce2 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -6,13 +6,17 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use futures::future::join; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + OTG_FS => usb_otg::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello World!"); @@ -25,9 +29,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(OTG_FS); let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index b2e252fc7..fa1f225fe 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -12,7 +12,7 @@ use embassy_stm32::peripherals::ETH; use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, eth, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; @@ -28,6 +28,10 @@ macro_rules! singleton { }}; } +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; +}); + type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] @@ -67,13 +71,12 @@ async fn main(spawner: Spawner) -> ! { rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); - let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( singleton!(PacketQueue::<4, 4>::new()), p.ETH, - eth_int, + Irqs, p.PA1, p.PA2, p.PC1, diff --git a/examples/stm32h5/src/bin/i2c.rs b/examples/stm32h5/src/bin/i2c.rs index 6cbf58bbc..8b6fe71ae 100644 --- a/examples/stm32h5/src/bin/i2c.rs +++ b/examples/stm32h5/src/bin/i2c.rs @@ -5,25 +5,28 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; -use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; +bind_interrupts!(struct Irqs { + I2C2_EV => i2c::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new( p.I2C2, p.PB10, p.PB11, - irq, + Irqs, p.GPDMA1_CH4, p.GPDMA1_CH5, Hertz(100_000), diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs index 405f18ec7..0abb94abb 100644 --- a/examples/stm32h5/src/bin/usart.rs +++ b/examples/stm32h5/src/bin/usart.rs @@ -6,18 +6,21 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART7 => usart::InterruptHandler; +}); + #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let irq = interrupt::take!(UART7); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs index 43d791aae..48264f884 100644 --- a/examples/stm32h5/src/bin/usart_dma.rs +++ b/examples/stm32h5/src/bin/usart_dma.rs @@ -8,19 +8,22 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART7 => usart::InterruptHandler; +}); + #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let irq = interrupt::take!(UART7); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs index 16a499582..debd6f454 100644 --- a/examples/stm32h5/src/bin/usart_split.rs +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -5,13 +5,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; use embassy_stm32::usart::{Config, Uart, UartRx}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART7 => usart::InterruptHandler; +}); + #[embassy_executor::task] async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); @@ -32,8 +36,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); let config = Config::default(); - let irq = interrupt::take!(UART7); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.GPDMA1_CH0, p.GPDMA1_CH1, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1, config); unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); let (mut tx, rx) = usart.split(); diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 4f987cbd1..3912327e2 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -7,13 +7,17 @@ use embassy_executor::Spawner; use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{interrupt, pac, Config}; +use embassy_stm32::{bind_interrupts, pac, peripherals, usb, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use futures::future::join; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USB_DRD_FS => usb::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -48,8 +52,7 @@ async fn main(_spawner: Spawner) { } // Create the driver, from the HAL. - let irq = interrupt::take!(USB_DRD_FS); - let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 9c443b83a..6f75a0630 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -8,7 +8,7 @@ use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::i2c::I2c; use embassy_stm32::rcc::{Mco, Mco1Source, McoClock}; use embassy_stm32::time::{khz, mhz}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; use embassy_time::{Duration, Timer}; use ov7725::*; use {defmt_rtt as _, panic_probe as _}; @@ -18,6 +18,11 @@ const HEIGHT: usize = 100; static mut FRAME: [u32; WIDTH * HEIGHT / 2] = [0u32; WIDTH * HEIGHT / 2]; +bind_interrupts!(struct Irqs { + I2C1_EV => i2c::InterruptHandler; + DCMI => dcmi::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -34,12 +39,11 @@ async fn main(_spawner: Spawner) { let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::Hsi, McoClock::Divided(3)); let mut led = Output::new(p.PE3, Level::High, Speed::Low); - let i2c_irq = interrupt::take!(I2C1_EV); let cam_i2c = I2c::new( p.I2C1, p.PB8, p.PB9, - i2c_irq, + Irqs, p.DMA1_CH1, p.DMA1_CH2, khz(100), @@ -55,11 +59,9 @@ async fn main(_spawner: Spawner) { defmt::info!("manufacturer: 0x{:x}, pid: 0x{:x}", manufacturer_id, camera_id); - let dcmi_irq = interrupt::take!(DCMI); let config = dcmi::Config::default(); let mut dcmi = Dcmi::new_8bit( - p.DCMI, p.DMA1_CH0, dcmi_irq, p.PC6, p.PC7, p.PE0, p.PE1, p.PE4, p.PD3, p.PE5, p.PE6, p.PB7, p.PA4, p.PA6, - config, + p.DCMI, p.DMA1_CH0, Irqs, p.PC6, p.PC7, p.PE0, p.PE1, p.PE4, p.PD3, p.PE5, p.PE6, p.PB7, p.PA4, p.PA6, config, ); defmt::info!("attempting capture"); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 61bb7e37b..dbfc90cf4 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -11,7 +11,7 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, eth, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; @@ -27,6 +27,10 @@ macro_rules! singleton { }}; } +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; +}); + type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] @@ -49,13 +53,12 @@ async fn main(spawner: Spawner) -> ! { rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); - let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( singleton!(PacketQueue::<16, 16>::new()), p.ETH, - eth_int, + Irqs, p.PA1, p.PA2, p.PC1, diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index b609fa5df..14e6b7914 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -11,7 +11,7 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, eth, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; @@ -28,6 +28,10 @@ macro_rules! singleton { }}; } +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; +}); + type Device = Ethernet<'static, ETH, GenericSMI>; #[embassy_executor::task] @@ -50,13 +54,12 @@ async fn main(spawner: Spawner) -> ! { rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); - let eth_int = interrupt::take!(ETH); let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( singleton!(PacketQueue::<16, 16>::new()), p.ETH, - eth_int, + Irqs, p.PA1, p.PA2, p.PC1, diff --git a/examples/stm32h7/src/bin/i2c.rs b/examples/stm32h7/src/bin/i2c.rs index 78e03f014..c2979c59b 100644 --- a/examples/stm32h7/src/bin/i2c.rs +++ b/examples/stm32h7/src/bin/i2c.rs @@ -5,25 +5,28 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::i2c::{Error, I2c, TimeoutI2c}; -use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; +bind_interrupts!(struct Irqs { + I2C2_EV => i2c::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new( p.I2C2, p.PB10, p.PB11, - irq, + Irqs, p.DMA1_CH4, p.DMA1_CH5, Hertz(100_000), diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 26d1db01e..ce91b6b1c 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; use embassy_stm32::time::mhz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SDMMC1 => sdmmc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let mut config = Config::default(); @@ -16,11 +20,9 @@ async fn main(_spawner: Spawner) -> ! { let p = embassy_stm32::init(config); info!("Hello World!"); - let irq = interrupt::take!(SDMMC1); - let mut sdmmc = Sdmmc::new_4bit( p.SDMMC1, - irq, + Irqs, p.PC12, p.PD2, p.PC8, diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index 405f18ec7..0abb94abb 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -6,18 +6,21 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART7 => usart::InterruptHandler; +}); + #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let irq = interrupt::take!(UART7); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, NoDma, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index 6e3491e55..f1fe7fce6 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -8,19 +8,22 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART7 => usart::InterruptHandler; +}); + #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let irq = interrupt::take!(UART7); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, NoDma, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index f97176ecb..330d1ce09 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -5,13 +5,17 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::peripherals::{DMA1_CH1, UART7}; use embassy_stm32::usart::{Config, Uart, UartRx}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART7 => usart::InterruptHandler; +}); + #[embassy_executor::task] async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); @@ -32,8 +36,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); let config = Config::default(); - let irq = interrupt::take!(UART7); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, irq, p.DMA1_CH0, p.DMA1_CH1, config); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, p.DMA1_CH1, config); unwrap!(usart.blocking_write(b"Type 8 chars to echo!\r\n")); let (mut tx, rx) = usart.split(); diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 475af116d..c622f19f7 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -6,13 +6,17 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use futures::future::join; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + OTG_FS => usb_otg::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello World!"); @@ -24,9 +28,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(OTG_FS); let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32l0/src/bin/usart_dma.rs b/examples/stm32l0/src/bin/usart_dma.rs index c307f857a..eae8f3452 100644 --- a/examples/stm32l0/src/bin/usart_dma.rs +++ b/examples/stm32l0/src/bin/usart_dma.rs @@ -4,15 +4,18 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USART1 => usart::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let irq = interrupt::take!(USART1); - let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH2, p.DMA1_CH3, Config::default()); + let mut usart = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH2, p.DMA1_CH3, Config::default()); usart.write(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index 465347004..f2c72a107 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -4,11 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::interrupt; use embassy_stm32::usart::{BufferedUart, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USART2 => usart::BufferedInterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); @@ -20,8 +24,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = 9600; - let irq = interrupt::take!(USART2); - let mut usart = unsafe { BufferedUart::new(p.USART2, irq, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) }; + let mut usart = unsafe { BufferedUart::new(p.USART2, Irqs, p.PA3, p.PA2, &mut TX_BUFFER, &mut RX_BUFFER, config) }; usart.write_all(b"Hello Embassy World!\r\n").await.unwrap(); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/i2c.rs b/examples/stm32l4/src/bin/i2c.rs index d40d6803d..d0060d20c 100644 --- a/examples/stm32l4/src/bin/i2c.rs +++ b/examples/stm32l4/src/bin/i2c.rs @@ -6,22 +6,25 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; -use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; +bind_interrupts!(struct Irqs { + I2C2_EV => i2c::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new( p.I2C2, p.PB10, p.PB11, - irq, + Irqs, NoDma, NoDma, Hertz(100_000), diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs index d868cac01..eca59087b 100644 --- a/examples/stm32l4/src/bin/i2c_blocking_async.rs +++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs @@ -7,23 +7,26 @@ use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; -use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embedded_hal_async::i2c::I2c as I2cTrait; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; +bind_interrupts!(struct Irqs { + I2C2_EV => i2c::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let irq = interrupt::take!(I2C2_EV); let i2c = I2c::new( p.I2C2, p.PB10, p.PB11, - irq, + Irqs, NoDma, NoDma, Hertz(100_000), diff --git a/examples/stm32l4/src/bin/i2c_dma.rs b/examples/stm32l4/src/bin/i2c_dma.rs index 7e62ee637..cf6f3da67 100644 --- a/examples/stm32l4/src/bin/i2c_dma.rs +++ b/examples/stm32l4/src/bin/i2c_dma.rs @@ -5,22 +5,25 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::i2c::I2c; -use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; +bind_interrupts!(struct Irqs { + I2C2_EV => i2c::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let irq = interrupt::take!(I2C2_EV); let mut i2c = I2c::new( p.I2C2, p.PB10, p.PB11, - irq, + Irqs, p.DMA1_CH4, p.DMA1_CH5, Hertz(100_000), diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 7d874d9d7..beb5ec558 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -4,10 +4,14 @@ use defmt::*; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART4 => usart::InterruptHandler; +}); + #[cortex_m_rt::entry] fn main() -> ! { info!("Hello World!"); @@ -15,8 +19,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let irq = interrupt::take!(UART4); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, NoDma, NoDma, config); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 452bede30..b7d4cb01e 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -7,19 +7,22 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + UART4 => usart::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); let config = Config::default(); - let irq = interrupt::take!(UART4); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, irq, p.DMA1_CH3, NoDma, config); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index bdb290e63..80811a43e 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -7,13 +7,17 @@ use defmt_rtt as _; // global logger use embassy_executor::Spawner; use embassy_stm32::rcc::*; use embassy_stm32::usb_otg::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use futures::future::join; use panic_probe as _; +bind_interrupts!(struct Irqs { + OTG_FS => usb_otg::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello World!"); @@ -25,9 +29,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(OTG_FS); let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 6c5645a41..b84e53d3a 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -9,7 +9,7 @@ use embassy_net::{Stack, StackResources}; use embassy_stm32::rcc::*; use embassy_stm32::rng::Rng; use embassy_stm32::usb::Driver; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; @@ -31,6 +31,10 @@ macro_rules! singleton { const MTU: usize = 1514; +bind_interrupts!(struct Irqs { + USB_FS => usb::InterruptHandler; +}); + #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { device.run().await @@ -54,8 +58,7 @@ async fn main(spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(USB_FS); - let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index e3bbe9d09..7e894e407 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::{Duration, Timer}; use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; @@ -15,6 +15,10 @@ use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USB_FS => usb::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -23,8 +27,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(USB_FS); - let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 66ccacb73..0c719560f 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -7,12 +7,16 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + USB_FS => usb::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -23,8 +27,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); // Create the driver, from the HAL. - let irq = interrupt::take!(USB_FS); - let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); // Create embassy-usb Config let config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index 4882cd2e0..f36daf91b 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -7,13 +7,17 @@ use defmt_rtt as _; // global logger use embassy_executor::Spawner; use embassy_stm32::rcc::*; use embassy_stm32::usb_otg::{Driver, Instance}; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use futures::future::join; use panic_probe as _; +bind_interrupts!(struct Irqs { + OTG_FS => usb_otg::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello World!"); @@ -26,9 +30,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let irq = interrupt::take!(OTG_FS); let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer); + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index acbc60c87..326e4be85 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -4,12 +4,17 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc}; use embassy_stm32::tl_mbox::TlMbox; +use embassy_stm32::{bind_interrupts, tl_mbox}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; + IPCC_C1_TX => tl_mbox::TransmitInterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { /* @@ -42,10 +47,7 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mut ipcc = Ipcc::new(p.IPCC, config); - let rx_irq = interrupt::take!(IPCC_C1_RX); - let tx_irq = interrupt::take!(IPCC_C1_TX); - - let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq); + let mbox = TlMbox::init(&mut ipcc, Irqs); loop { let wireless_fw_info = mbox.wireless_fw_info(); diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 1008e1e41..7a69f26b7 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -4,11 +4,16 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc}; use embassy_stm32::tl_mbox::TlMbox; +use embassy_stm32::{bind_interrupts, tl_mbox}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; + IPCC_C1_TX => tl_mbox::TransmitInterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { /* @@ -41,10 +46,7 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mut ipcc = Ipcc::new(p.IPCC, config); - let rx_irq = interrupt::take!(IPCC_C1_RX); - let tx_irq = interrupt::take!(IPCC_C1_TX); - - let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq); + let mbox = TlMbox::init(&mut ipcc, Irqs); // initialize ble stack, does not return a response mbox.shci_ble_init(&mut ipcc, Default::default()); diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index c4e50cb4a..7d96cf41e 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs @@ -7,9 +7,13 @@ use defmt::{assert_eq, *}; use embassy_executor::Spawner; use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; use embassy_stm32::time::mhz; -use embassy_stm32::{interrupt, Config}; +use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + SDIO => sdmmc::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello World!"); @@ -20,17 +24,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); #[cfg(feature = "stm32f429zi")] - let (mut sdmmc, mut irq, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) = ( - p.SDIO, - interrupt::take!(SDIO), - p.DMA2_CH3, - p.PC12, - p.PD2, - p.PC8, - p.PC9, - p.PC10, - p.PC11, - ); + let (mut sdmmc, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) = + (p.SDIO, p.DMA2_CH3, p.PC12, p.PD2, p.PC8, p.PC9, p.PC10, p.PC11); // Arbitrary block index let block_idx = 16; @@ -48,7 +43,7 @@ async fn main(_spawner: Spawner) { info!("initializing in 4-bit mode..."); let mut s = Sdmmc::new_4bit( &mut sdmmc, - &mut irq, + Irqs, &mut dma, &mut clk, &mut cmd, @@ -97,7 +92,7 @@ async fn main(_spawner: Spawner) { info!("initializing in 1-bit mode..."); let mut s = Sdmmc::new_1bit( &mut sdmmc, - &mut irq, + Irqs, &mut dma, &mut clk, &mut cmd, diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 0749f8406..415c7afa9 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -7,11 +7,37 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Error, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_time::{Duration, Instant}; use example_common::*; +#[cfg(any( + feature = "stm32f103c8", + feature = "stm32g491re", + feature = "stm32g071rb", + feature = "stm32h755zi", + feature = "stm32c031c6", +))] +bind_interrupts!(struct Irqs { + USART1 => usart::InterruptHandler; +}); + +#[cfg(feature = "stm32u585ai")] +bind_interrupts!(struct Irqs { + USART3 => usart::InterruptHandler; +}); + +#[cfg(feature = "stm32f429zi")] +bind_interrupts!(struct Irqs { + USART6 => usart::InterruptHandler; +}); + +#[cfg(any(feature = "stm32wb55rg", feature = "stm32h563zi"))] +bind_interrupts!(struct Irqs { + LPUART1 => usart::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); @@ -20,27 +46,27 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart) = (p.PA9, p.PA10, p.USART1); #[cfg(feature = "stm32g491re")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart) = (p.PC4, p.PC5, p.USART1); #[cfg(feature = "stm32g071rb")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart) = (p.PC4, p.PC5, p.USART1); #[cfg(feature = "stm32f429zi")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); + let (mut tx, mut rx, mut usart) = (p.PG14, p.PG9, p.USART6); #[cfg(feature = "stm32wb55rg")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); + let (mut tx, mut rx, mut usart) = (p.PA2, p.PA3, p.LPUART1); #[cfg(feature = "stm32h755zi")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart) = (p.PB6, p.PB7, p.USART1); #[cfg(feature = "stm32u585ai")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); + let (mut tx, mut rx, mut usart) = (p.PD8, p.PD9, p.USART3); #[cfg(feature = "stm32h563zi")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); + let (mut tx, mut rx, mut usart) = (p.PB6, p.PB7, p.LPUART1); #[cfg(feature = "stm32c031c6")] - let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart) = (p.PB6, p.PB7, p.USART1); { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, Irqs, NoDma, NoDma, config); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. @@ -56,7 +82,7 @@ async fn main(_spawner: Spawner) { // Test error handling with with an overflow error { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, Irqs, NoDma, NoDma, config); // Send enough bytes to fill the RX FIFOs off all USART versions. let data = [0xC0, 0xDE, 0x12, 0x23, 0x34]; @@ -90,7 +116,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = baudrate; - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, Irqs, NoDma, NoDma, config); let n = (baudrate as usize / 100).max(64); diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index 62444f0a8..7f002b97e 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -7,10 +7,36 @@ mod example_common; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use example_common::*; +#[cfg(any( + feature = "stm32f103c8", + feature = "stm32g491re", + feature = "stm32g071rb", + feature = "stm32h755zi", + feature = "stm32c031c6", +))] +bind_interrupts!(struct Irqs { + USART1 => usart::InterruptHandler; +}); + +#[cfg(feature = "stm32u585ai")] +bind_interrupts!(struct Irqs { + USART3 => usart::InterruptHandler; +}); + +#[cfg(feature = "stm32f429zi")] +bind_interrupts!(struct Irqs { + USART6 => usart::InterruptHandler; +}); + +#[cfg(any(feature = "stm32wb55rg", feature = "stm32h563zi"))] +bind_interrupts!(struct Irqs { + LPUART1 => usart::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); @@ -19,62 +45,23 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PA9, - p.PA10, - p.USART1, - interrupt::take!(USART1), - p.DMA1_CH4, - p.DMA1_CH5, - ); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, Irqs, p.DMA1_CH4, p.DMA1_CH5); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PG14, - p.PG9, - p.USART6, - interrupt::take!(USART6), - p.DMA2_CH6, - p.DMA2_CH1, - ); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PG14, p.PG9, p.USART6, Irqs, p.DMA2_CH6, p.DMA2_CH1); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PA2, - p.PA3, - p.LPUART1, - interrupt::take!(LPUART1), - p.DMA1_CH1, - p.DMA1_CH2, - ); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PA2, p.PA3, p.LPUART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH0, p.DMA1_CH1); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, Irqs, p.DMA1_CH0, p.DMA1_CH1); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PD8, - p.PD9, - p.USART3, - interrupt::take!(USART3), - p.GPDMA1_CH0, - p.GPDMA1_CH1, - ); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1); #[cfg(feature = "stm32h563zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PB6, - p.PB7, - p.LPUART1, - interrupt::take!(LPUART1), - p.GPDMA1_CH0, - p.GPDMA1_CH1, - ); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PB6, p.PB7, p.LPUART1, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1); #[cfg(feature = "stm32c031c6")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, irq, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, Irqs, p.DMA1_CH1, p.DMA1_CH2); let config = Config::default(); let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 9d75dbe55..3a34773f7 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -8,13 +8,40 @@ mod example_common; use defmt::{assert_eq, panic}; use embassy_executor::Spawner; -use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_time::{Duration, Timer}; use example_common::*; use rand_chacha::ChaCha8Rng; use rand_core::{RngCore, SeedableRng}; +#[cfg(any( + feature = "stm32f103c8", + feature = "stm32g491re", + feature = "stm32g071rb", + feature = "stm32h755zi", + feature = "stm32c031c6", +))] +bind_interrupts!(struct Irqs { + USART1 => usart::InterruptHandler; +}); + +#[cfg(feature = "stm32u585ai")] +bind_interrupts!(struct Irqs { + USART3 => usart::InterruptHandler; +}); + +#[cfg(feature = "stm32f429zi")] +bind_interrupts!(struct Irqs { + USART1 => usart::InterruptHandler; + USART6 => usart::InterruptHandler; +}); + +#[cfg(any(feature = "stm32wb55rg", feature = "stm32h563zi"))] +bind_interrupts!(struct Irqs { + LPUART1 => usart::InterruptHandler; +}); + #[cfg(feature = "stm32f103c8")] mod board { pub type Uart = embassy_stm32::peripherals::USART1; @@ -74,53 +101,21 @@ async fn main(spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PA9, - p.PA10, - p.USART1, - interrupt::take!(USART1), - p.DMA1_CH4, - p.DMA1_CH5, - ); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PA9, p.PA10, p.USART1, p.DMA1_CH4, p.DMA1_CH5); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PC4, p.PC5, p.USART1, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PG14, - p.PG9, - p.USART6, - interrupt::take!(USART6), - p.DMA2_CH6, - p.DMA2_CH1, - ); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PG14, p.PG9, p.USART6, p.DMA2_CH6, p.DMA2_CH1); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PA2, - p.PA3, - p.LPUART1, - interrupt::take!(LPUART1), - p.DMA1_CH1, - p.DMA1_CH2, - ); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PA2, p.PA3, p.LPUART1, p.DMA1_CH1, p.DMA1_CH2); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH0, p.DMA1_CH1); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH0, p.DMA1_CH1); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = ( - p.PD8, - p.PD9, - p.USART3, - interrupt::take!(USART3), - p.GPDMA1_CH0, - p.GPDMA1_CH1, - ); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PD8, p.PD9, p.USART3, p.GPDMA1_CH0, p.GPDMA1_CH1); #[cfg(feature = "stm32c031c6")] - let (tx, rx, usart, irq, tx_dma, rx_dma) = - (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1), p.DMA1_CH1, p.DMA1_CH2); + let (tx, rx, usart, tx_dma, rx_dma) = (p.PB6, p.PB7, p.USART1, p.DMA1_CH1, p.DMA1_CH2); // To run this test, use the saturating_serial test utility to saturate the serial port @@ -132,7 +127,7 @@ async fn main(spawner: Spawner) { config.stop_bits = StopBits::STOP1; config.parity = Parity::ParityNone; - let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config); + let usart = Uart::new(usart, rx, tx, Irqs, tx_dma, rx_dma, config); let (tx, rx) = usart.split(); static mut DMA_BUF: [u8; DMA_BUF_SIZE] = [0; DMA_BUF_SIZE]; let dma_buf = unsafe { DMA_BUF.as_mut() }; From b6ba1ea53ada2f503ae89de66490957723a21866 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 24 May 2023 18:09:04 -0500 Subject: [PATCH 1171/1575] stm32: move lora to bind_interrupts --- ci.sh | 2 +- embassy-lora/src/iv.rs | 74 +++++++++++--------- examples/stm32wl/src/bin/lora_lorawan.rs | 12 ++-- examples/stm32wl/src/bin/lora_p2p_receive.rs | 12 ++-- examples/stm32wl/src/bin/lora_p2p_send.rs | 12 ++-- examples/stm32wl/src/bin/uart_async.rs | 15 ++-- 6 files changed, 71 insertions(+), 56 deletions(-) diff --git a/ci.sh b/ci.sh index 106689d00..2c46dcc6b 100755 --- a/ci.sh +++ b/ci.sh @@ -112,6 +112,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- 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 --features embassy-nrf/nrf52840 --out-dir out/examples/boot/nrf --bin b \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns --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 \ @@ -142,7 +143,6 @@ cargo batch \ $BUILD_EXTRA -# --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \ function run_elf { echo Running target=$1 elf=$2 diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs index f81134405..d515bc365 100644 --- a/embassy-lora/src/iv.rs +++ b/embassy-lora/src/iv.rs @@ -1,7 +1,9 @@ #[cfg(feature = "stm32wl")] +use embassy_stm32::interrupt; +#[cfg(feature = "stm32wl")] use embassy_stm32::interrupt::*; #[cfg(feature = "stm32wl")] -use embassy_stm32::{pac, PeripheralRef}; +use embassy_stm32::pac; #[cfg(feature = "stm32wl")] use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; #[cfg(feature = "stm32wl")] @@ -13,47 +15,51 @@ use lora_phy::mod_params::RadioError::*; use lora_phy::mod_params::{BoardType, RadioError}; use lora_phy::mod_traits::InterfaceVariant; +/// Interrupt handler. #[cfg(feature = "stm32wl")] -static IRQ_SIGNAL: Signal = Signal::new(); +pub struct InterruptHandler {} #[cfg(feature = "stm32wl")] -/// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination -pub struct Stm32wlInterfaceVariant<'a, CTRL> { - board_type: BoardType, - irq: PeripheralRef<'a, SUBGHZ_RADIO>, - rf_switch_rx: Option, - rf_switch_tx: Option, -} - -#[cfg(feature = "stm32wl")] -impl<'a, CTRL> Stm32wlInterfaceVariant<'a, CTRL> -where - CTRL: OutputPin, -{ - /// Create an InterfaceVariant instance for an stm32wl/sx1262 combination - pub fn new( - irq: PeripheralRef<'a, SUBGHZ_RADIO>, - rf_switch_rx: Option, - rf_switch_tx: Option, - ) -> Result { - irq.disable(); - irq.set_handler(Self::on_interrupt); - Ok(Self { - board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board - irq, - rf_switch_rx, - rf_switch_tx, - }) - } - - fn on_interrupt(_: *mut ()) { +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { unsafe { SUBGHZ_RADIO::steal() }.disable(); IRQ_SIGNAL.signal(()); } } #[cfg(feature = "stm32wl")] -impl InterfaceVariant for Stm32wlInterfaceVariant<'_, CTRL> +static IRQ_SIGNAL: Signal = Signal::new(); + +#[cfg(feature = "stm32wl")] +/// Base for the InterfaceVariant implementation for an stm32wl/sx1262 combination +pub struct Stm32wlInterfaceVariant { + board_type: BoardType, + rf_switch_rx: Option, + rf_switch_tx: Option, +} + +#[cfg(feature = "stm32wl")] +impl<'a, CTRL> Stm32wlInterfaceVariant +where + CTRL: OutputPin, +{ + /// Create an InterfaceVariant instance for an stm32wl/sx1262 combination + pub fn new( + _irq: impl interrupt::Binding, + rf_switch_rx: Option, + rf_switch_tx: Option, + ) -> Result { + unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); + Ok(Self { + board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board + rf_switch_rx, + rf_switch_tx, + }) + } +} + +#[cfg(feature = "stm32wl")] +impl InterfaceVariant for Stm32wlInterfaceVariant where CTRL: OutputPin, { @@ -89,7 +95,7 @@ where } async fn await_irq(&mut self) -> Result<(), RadioError> { - self.irq.enable(); + unsafe { interrupt::SUBGHZ_RADIO::steal() }.enable(); IRQ_SIGNAL.wait().await; Ok(()) } diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 1a271b2f2..e179c5ca1 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -7,12 +7,12 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_lora::iv::Stm32wlInterfaceVariant; +use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; use embassy_lora::LoraTimer; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; use embassy_stm32::rng::Rng; use embassy_stm32::spi::Spi; -use embassy_stm32::{interrupt, into_ref, pac, Peripheral}; +use embassy_stm32::{bind_interrupts, pac}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1261_2::SX1261_2; @@ -24,6 +24,10 @@ use {defmt_rtt as _, panic_probe as _}; const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region +bind_interrupts!(struct Irqs{ + SUBGHZ_RADIO => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -35,13 +39,11 @@ async fn main(_spawner: Spawner) { let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); - let irq = interrupt::take!(SUBGHZ_RADIO); - into_ref!(irq); // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); - let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap(); + let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); let mut delay = Delay; diff --git a/examples/stm32wl/src/bin/lora_p2p_receive.rs b/examples/stm32wl/src/bin/lora_p2p_receive.rs index 5e80e8f6a..d3f051b1c 100644 --- a/examples/stm32wl/src/bin/lora_p2p_receive.rs +++ b/examples/stm32wl/src/bin/lora_p2p_receive.rs @@ -7,10 +7,10 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_lora::iv::Stm32wlInterfaceVariant; +use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; +use embassy_stm32::bind_interrupts; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; use embassy_stm32::spi::Spi; -use embassy_stm32::{interrupt, into_ref, Peripheral}; use embassy_time::{Delay, Duration, Timer}; use lora_phy::mod_params::*; use lora_phy::sx1261_2::SX1261_2; @@ -19,6 +19,10 @@ use {defmt_rtt as _, panic_probe as _}; const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region +bind_interrupts!(struct Irqs{ + SUBGHZ_RADIO => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -27,13 +31,11 @@ async fn main(_spawner: Spawner) { let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); - let irq = interrupt::take!(SUBGHZ_RADIO); - into_ref!(irq); // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); - let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap(); + let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); let mut delay = Delay; diff --git a/examples/stm32wl/src/bin/lora_p2p_send.rs b/examples/stm32wl/src/bin/lora_p2p_send.rs index e22c714bd..fc5205c85 100644 --- a/examples/stm32wl/src/bin/lora_p2p_send.rs +++ b/examples/stm32wl/src/bin/lora_p2p_send.rs @@ -7,10 +7,10 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_lora::iv::Stm32wlInterfaceVariant; +use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; +use embassy_stm32::bind_interrupts; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; use embassy_stm32::spi::Spi; -use embassy_stm32::{interrupt, into_ref, Peripheral}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1261_2::SX1261_2; @@ -19,6 +19,10 @@ use {defmt_rtt as _, panic_probe as _}; const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriately for the region +bind_interrupts!(struct Irqs{ + SUBGHZ_RADIO => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -27,13 +31,11 @@ async fn main(_spawner: Spawner) { let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); - let irq = interrupt::take!(SUBGHZ_RADIO); - into_ref!(irq); // Set CTRL1 and CTRL3 for high-power transmission, while CTRL2 acts as an RF switch between tx and rx let _ctrl1 = Output::new(p.PC4.degrade(), Level::Low, Speed::High); let ctrl2 = Output::new(p.PC5.degrade(), Level::High, Speed::High); let _ctrl3 = Output::new(p.PC3.degrade(), Level::High, Speed::High); - let iv = Stm32wlInterfaceVariant::new(irq, None, Some(ctrl2)).unwrap(); + let iv = Stm32wlInterfaceVariant::new(Irqs, None, Some(ctrl2)).unwrap(); let mut delay = Delay; diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs index ac8766af6..07b0f9d2c 100644 --- a/examples/stm32wl/src/bin/uart_async.rs +++ b/examples/stm32wl/src/bin/uart_async.rs @@ -4,10 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::interrupt; -use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::usart::{Config, InterruptHandler, Uart}; +use embassy_stm32::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs{ + USART1 => InterruptHandler; + LPUART1 => InterruptHandler; +}); + /* Pass Incoming data from LPUART1 to USART1 Example is written for the LoRa-E5 mini v1.0, @@ -28,12 +33,10 @@ async fn main(_spawner: Spawner) { config2.baudrate = 9600; //RX/TX connected to USB/UART Bridge on LoRa-E5 mini v1.0 - let irq = interrupt::take!(USART1); - let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, irq, p.DMA1_CH3, p.DMA1_CH4, config1); + let mut usart1 = Uart::new(p.USART1, p.PB7, p.PB6, Irqs, p.DMA1_CH3, p.DMA1_CH4, config1); //RX1/TX1 (LPUART) on LoRa-E5 mini v1.0 - let irq = interrupt::take!(LPUART1); - let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, irq, p.DMA1_CH5, p.DMA1_CH6, config2); + let mut usart2 = Uart::new(p.LPUART1, p.PC0, p.PC1, Irqs, p.DMA1_CH5, p.DMA1_CH6, config2); unwrap!(usart1.write(b"Hello Embassy World!\r\n").await); unwrap!(usart2.write(b"Hello Embassy World!\r\n").await); From 2a589b79047a2431dd443d50c434e7f4767eab51 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 25 May 2023 03:43:44 +0200 Subject: [PATCH 1172/1575] ci: replace openid connect with static secret. The oidc token is only valid for 5min, builds are starting to fail because HIL tests take more than 5 min and we only obtain it once at start. Instead of fixing it, let's remove it. My hope for OIDC was to allow running HIL tests on PRs from forks if the author is in a list of trusted users. However GHA simply doesn't give the ID token to PRs from forks. :shrug: Same limitation as with static tokens. So it's useless complexity, let's kill it. --- .github/workflows/rust.yml | 2 ++ ci.sh | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 47dc8fd7a..0cbca31b8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -36,6 +36,8 @@ jobs: target_ci key: rust3-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} - name: build + env: + TELEPROBE_TOKEN: ${{ secrets.TELEPROBE_TOKEN }} run: | curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.3.0/cargo-batch chmod +x /usr/local/bin/cargo-batch diff --git a/ci.sh b/ci.sh index 2c46dcc6b..6d906f5f9 100755 --- a/ci.sh +++ b/ci.sh @@ -160,12 +160,8 @@ function run_elf { } if [[ -z "${TELEPROBE_TOKEN-}" ]]; then - if [[ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN-}" ]]; then - echo No teleprobe token found, skipping running HIL tests - exit - fi - - export TELEPROBE_TOKEN=$(curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value') + echo No teleprobe token found, skipping running HIL tests + exit fi for board in $(ls out/tests); do From b2775fc90c3d7cc977f1ac3e15841462d7c8f1a4 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:17:12 +0200 Subject: [PATCH 1173/1575] stm32: Add async flash write/erase to f4 --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/flash/asynch.rs | 129 +++++++++++++++ embassy-stm32/src/flash/common.rs | 267 ++++++++++++++++-------------- embassy-stm32/src/flash/f0.rs | 18 +- embassy-stm32/src/flash/f3.rs | 18 +- embassy-stm32/src/flash/f4.rs | 183 +++++++++++++++++--- embassy-stm32/src/flash/f7.rs | 21 +-- embassy-stm32/src/flash/h7.rs | 21 +-- embassy-stm32/src/flash/l.rs | 19 ++- embassy-stm32/src/flash/mod.rs | 49 ++++++ embassy-stm32/src/flash/other.rs | 12 +- 11 files changed, 547 insertions(+), 194 deletions(-) create mode 100644 embassy-stm32/src/flash/asynch.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 21adb5ddf..440de85e8 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -47,6 +47,7 @@ embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} embedded-storage = "0.3.0" +embedded-storage-async = { version = "0.4.0", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -68,6 +69,7 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" +paste = "1.0.12" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -98,7 +100,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs new file mode 100644 index 000000000..44e23d9c4 --- /dev/null +++ b/embassy-stm32/src/flash/asynch.rs @@ -0,0 +1,129 @@ +use atomic_polyfill::{fence, Ordering}; +use embassy_hal_common::drop::OnDrop; + +use super::{ + ensure_sector_aligned, family, get_sector, Error, Flash, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + REGION_ACCESS, WRITE_SIZE, +}; + +impl<'d> Flash<'d> { + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } + } + + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_sectored(FLASH_BASE as u32, from, to).await } + } +} + +impl embedded_storage_async::nor_flash::NorFlash for Flash<'_> { + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = MAX_ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await + } +} + +pub(super) async unsafe fn write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { + return Err(Error::Size); + } + if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let mut address = base + offset; + trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); + + for chunk in bytes.chunks(WRITE_SIZE) { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_write(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { + family::disable_write(); + fence(Ordering::SeqCst); + family::lock(); + }); + + family::write(address, chunk.try_into().unwrap()).await?; + address += WRITE_SIZE as u32; + } + Ok(()) +} + +pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { + let start_address = base + from; + let end_address = base + to; + let regions = family::get_flash_regions(); + + ensure_sector_aligned(start_address, end_address, regions)?; + + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); + + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + trace!("Erasing sector: {:?}", sector); + + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| family::lock()); + + family::erase_sector(§or).await?; + address += sector.size; + } + Ok(()) +} + +foreach_flash_region! { + ($type_name:ident, $write_size:literal, $erase_size:literal) => { + impl crate::_generated::flash_regions::$type_name<'_> { + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { write_chunked(self.0.base, self.0.size, offset, bytes).await } + } + + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { erase_sectored(self.0.base, from, to).await } + } + } + + impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + + impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { + const WRITE_SIZE: usize = $write_size; + const ERASE_SIZE: usize = $erase_size; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await + } + } + }; +} diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 432c8a43b..990104a38 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,44 +1,55 @@ use atomic_polyfill::{fence, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_futures::block_on; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::Mutex; +use stm32_metapac::FLASH_BASE; -use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; -use crate::flash::FlashBank; +use super::{ + ensure_sector_aligned, family, get_sector, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, +}; +use crate::peripherals::FLASH; use crate::Peripheral; pub struct Flash<'d> { - inner: PeripheralRef<'d, crate::peripherals::FLASH>, + pub(crate) inner: PeripheralRef<'d, FLASH>, } +pub(crate) static REGION_ACCESS: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd) -> Self { - into_ref!(p); + pub fn new(p: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { + into_ref!(p, irq); + + irq.set_handler(family::on_interrupt); + irq.unpend(); + irq.enable(); + Self { inner: p } } pub fn into_regions(self) -> FlashLayout<'d> { family::set_default_layout(); - FlashLayout::new(self.release()) + FlashLayout::new(self.inner) } - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } + pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_chunked_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(FLASH_BASE as u32, from, to) } - } - - pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { - unsafe { self.inner.clone_unchecked() } + pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_sectored_blocking(FLASH_BASE as u32, from, to) } } } -pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { +pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -49,7 +60,7 @@ pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) Ok(()) } -pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { +pub(super) unsafe fn write_chunked_blocking(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -61,44 +72,31 @@ pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, b trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); for chunk in bytes.chunks(WRITE_SIZE) { - critical_section::with(|_| { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); - family::begin_write(); - fence(Ordering::SeqCst); + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_blocking_write(); + fence(Ordering::SeqCst); - let _on_drop = OnDrop::new(|| { - family::end_write(); - fence(Ordering::SeqCst); - family::lock(); - }); + let _on_drop = OnDrop::new(|| { + family::disable_blocking_write(); + fence(Ordering::SeqCst); + family::lock(); + }); - family::blocking_write(address, chunk.try_into().unwrap()) - })?; + family::write_blocking(address, chunk.try_into().unwrap())?; address += WRITE_SIZE as u32; } Ok(()) } -pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { +pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; let regions = family::get_flash_regions(); - // Test if the address range is aligned at sector base addresses - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } + ensure_sector_aligned(start_address, end_address, regions)?; trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); @@ -107,71 +105,28 @@ pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> R let sector = get_sector(address, regions); trace!("Erasing sector: {:?}", sector); - critical_section::with(|_| { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); - let _on_drop = OnDrop::new(|| { - family::lock(); - }); + let _on_drop = OnDrop::new(|| family::lock()); - family::blocking_erase_sector(§or) - })?; + family::erase_sector_blocking(§or)?; address += sector.size; } Ok(()) } -pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { - let mut current_bank = FlashBank::Bank1; - let mut bank_offset = 0; - for region in regions { - if region.bank != current_bank { - current_bank = region.bank; - bank_offset = 0; - } - - if address < region.end() { - let index_in_region = (address - region.base) / region.erase_size; - return FlashSector { - bank: region.bank, - index_in_bank: bank_offset + index_in_region as u8, - start: region.base + index_in_region * region.erase_size, - size: region.erase_size, - }; - } - - bank_offset += region.sectors(); - } - - panic!("Flash sector not found"); -} - -impl FlashRegion { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.base, self.size, offset, bytes) - } - - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.base, self.size, offset, bytes) } - } - - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.base, from, to) } - } -} - impl embedded_storage::nor_flash::ErrorType for Flash<'_> { type Error = Error; } impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -184,27 +139,112 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_> { const ERASE_SIZE: usize = MAX_ERASE_SIZE; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) + self.write_blocking(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) + self.erase_blocking(from, to) + } +} + +#[cfg(feature = "nightly")] +impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +pub struct BlockingFlashRegion<'d, const WRITE_SIZE: u32, const ERASE_SIZE: u32>( + &'static FlashRegion, + PeripheralRef<'d, FLASH>, +); + +impl BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> { + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) + } + + pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = block_on(REGION_ACCESS.lock()); + unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = block_on(REGION_ACCESS.lock()); + unsafe { erase_sectored_blocking(self.0.base, from, to) } + } +} + +impl embedded_storage::nor_flash::ErrorType + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + type Error = Error; +} + +impl embedded_storage::nor_flash::ReadNorFlash + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } +} + +impl embedded_storage::nor_flash::NorFlash + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + const WRITE_SIZE: usize = WRITE_SIZE as usize; + const ERASE_SIZE: usize = ERASE_SIZE as usize; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) } } foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl crate::_generated::flash_regions::$type_name<'_> { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.0.base, self.0.size, offset, bytes) + paste::paste! { + pub type []<'d> = BlockingFlashRegion<'d, $write_size, $erase_size>; + } + + impl<'d> crate::_generated::flash_regions::$type_name<'d> { + /// Make this flash region work in a blocking context. + /// + /// SAFETY + /// + /// This function is unsafe as incorect usage of parallel blocking operations + /// on multiple regions may cause a deadlock because each region requires mutual access to the flash. + pub unsafe fn into_blocking(self) -> BlockingFlashRegion<'d, $write_size, $erase_size> { + BlockingFlashRegion(self.0, self.1) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.0.base, from, to) } + pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { erase_sectored_blocking(self.0.base, from, to) } } } @@ -213,28 +253,15 @@ foreach_flash_region! { } impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { self.0.size as usize } } - - impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { - const WRITE_SIZE: usize = $write_size; - const ERASE_SIZE: usize = $erase_size; - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - } }; } diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index c6441ef16..ecf3a6981 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -13,6 +13,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -22,17 +26,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 2); pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); @@ -42,10 +46,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -56,7 +60,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = wait_ready_blocking(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -88,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 4c8172203..fd778f2b1 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -13,6 +13,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -22,17 +26,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 2); pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); @@ -42,10 +46,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -56,7 +60,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = wait_ready_blocking(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -88,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 7f1c5f671..3c8f81eb0 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,6 +2,8 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +use embassy_sync::waitqueue::AtomicWaker; + use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -13,8 +15,8 @@ mod alt_regions { use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; use crate::flash::{ - blocking_erase_sectored, blocking_read, blocking_write_chunked, Bank1Region1, Bank1Region2, Error, Flash, - FlashBank, FlashRegion, + asynch, common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, + READ_SIZE, REGION_ACCESS, }; use crate::peripherals::FLASH; @@ -53,6 +55,15 @@ mod alt_regions { pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub type BlockingAltBank1Region3<'d> = + BlockingFlashRegion<'d, { ALT_BANK1_REGION3.write_size }, { ALT_BANK1_REGION3.erase_size }>; + pub type BlockingAltBank2Region1<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION1.write_size }, { ALT_BANK2_REGION1.erase_size }>; + pub type BlockingAltBank2Region2<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION2.write_size }, { ALT_BANK2_REGION2.erase_size }>; + pub type BlockingAltBank2Region3<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION3.write_size }, { ALT_BANK2_REGION3.erase_size }>; + pub struct AltFlashLayout<'d> { pub bank1_region1: Bank1Region1<'d>, pub bank1_region2: Bank1Region2<'d>, @@ -69,7 +80,7 @@ mod alt_regions { // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. - let p = self.release(); + let p = self.inner; AltFlashLayout { bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), @@ -85,16 +96,30 @@ mod alt_regions { macro_rules! foreach_altflash_region { ($type_name:ident, $region:ident) => { impl $type_name<'_> { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.0.base, self.0.size, offset, bytes) + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + common::read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } + #[cfg(all(feature = "nightly"))] + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.0.base, from, to) } + #[cfg(all(feature = "nightly"))] + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { asynch::erase_sectored(self.0.base, from, to).await } + } + + pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { common::write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { common::erase_sectored_blocking(self.0.base, from, to) } } } @@ -103,10 +128,10 @@ mod alt_regions { } impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -114,16 +139,28 @@ mod alt_regions { } } - impl embedded_storage::nor_flash::NorFlash for $type_name<'_> { + impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + + impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await } - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await } } }; @@ -138,6 +175,9 @@ mod alt_regions { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub use alt_regions::*; +#[cfg(feature = "nightly")] +static WAKER: AtomicWaker = AtomicWaker::new(); + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn set_default_layout() { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) }; @@ -160,6 +200,16 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + // Clear IRQ flags + pac::FLASH.sr().write(|w| { + w.set_operr(true); + w.set_eop(true); + }); + + WAKER.wake(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -169,7 +219,28 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +#[cfg(feature = "nightly")] +pub(crate) unsafe fn enable_write() { + assert_eq!(0, WRITE_SIZE % 4); + + pac::FLASH.cr().write(|w| { + w.set_pg(true); + w.set_psize(pac::flash::vals::Psize::PSIZE32); + w.set_eopie(true); + w.set_errie(true); + }); +} + +#[cfg(feature = "nightly")] +pub(crate) unsafe fn disable_write() { + pac::FLASH.cr().write(|w| { + w.set_pg(false); + w.set_eopie(false); + w.set_errie(false); + }); +} + +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); pac::FLASH.cr().write(|w| { @@ -178,11 +249,22 @@ pub(crate) unsafe fn begin_write() { }); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +#[cfg(feature = "nightly")] +pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + write_start(start_address, buf); + wait_ready().await +} + +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + write_start(start_address, buf); + wait_ready_blocking() +} + +unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -191,11 +273,32 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) // prevents parallelism errors fence(Ordering::SeqCst); } - - blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { + let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; + + pac::FLASH.cr().modify(|w| { + w.set_ser(true); + w.set_snb(snb); + w.set_eopie(true); + w.set_errie(true); + }); + + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); + + let ret: Result<(), Error> = wait_ready().await; + pac::FLASH.cr().modify(|w| { + w.set_eopie(false); + w.set_errie(false); + }); + clear_all_err(); + ret +} + +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; pac::FLASH.cr().modify(|w| { @@ -207,10 +310,8 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = blocking_wait_ready(); - + let ret: Result<(), Error> = wait_ready_blocking(); clear_all_err(); - ret } @@ -220,11 +321,39 @@ pub(crate) unsafe fn clear_all_err() { w.set_pgperr(true); w.set_pgaerr(true); w.set_wrperr(true); - w.set_eop(true); }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +#[cfg(feature = "nightly")] +pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { + use core::task::Poll; + + use futures::future::poll_fn; + + poll_fn(|cx| { + WAKER.register(cx.waker()); + + let sr = pac::FLASH.sr().read(); + if !sr.bsy() { + Poll::Ready(if sr.pgserr() { + Err(Error::Seq) + } else if sr.pgperr() { + Err(Error::Parallelism) + } else if sr.pgaerr() { + Err(Error::Unaligned) + } else if sr.wrperr() { + Err(Error::Protected) + } else { + Ok(()) + }) + } else { + return Poll::Pending; + } + }) + .await +} + +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index ac2834a84..a0593b14b 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -12,6 +12,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -21,7 +25,7 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); pac::FLASH.cr().write(|w| { @@ -30,11 +34,11 @@ pub(crate) unsafe fn begin_write() { }); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -44,10 +48,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.index_in_bank) @@ -57,12 +61,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = blocking_wait_ready(); - + let ret: Result<(), Error> = wait_ready_blocking(); pac::FLASH.cr().modify(|w| w.set_ser(false)); - clear_all_err(); - ret } @@ -86,7 +87,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 1b1631068..865f13283 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -17,6 +17,10 @@ pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); if is_dual_bank() { @@ -33,13 +37,13 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); } -pub(crate) unsafe fn end_write() {} +pub(crate) unsafe fn disable_blocking_write() {} -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { // We cannot have the write setup sequence in begin_write as it depends on the address let bank = if start_address < BANK1_REGION.end() { pac::FLASH.bank(0) @@ -60,7 +64,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); address += val.len() as u32; - res = Some(blocking_wait_ready(bank)); + res = Some(wait_ready_blocking(bank)); bank.sr().modify(|w| { if w.eop() { w.set_eop(true); @@ -80,7 +84,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); @@ -91,12 +95,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_start(true); }); - let ret: Result<(), Error> = blocking_wait_ready(bank); - + let ret: Result<(), Error> = wait_ready_blocking(bank); bank.cr().modify(|w| w.set_ser(false)); - bank_clear_all_err(bank); - ret } @@ -141,7 +142,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { }); } -unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { +unsafe fn wait_ready_blocking(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index c94f61900..f8a0dac4c 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -12,6 +12,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -41,19 +45,19 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -63,10 +67,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -96,7 +100,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E }); } - let ret: Result<(), Error> = blocking_wait_ready(); + let ret: Result<(), Error> = wait_ready_blocking(); #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_per(false)); @@ -108,7 +112,6 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E }); clear_all_err(); - ret } @@ -150,7 +153,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b93270ae1..e781f1b86 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,5 +1,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; +#[cfg(all(feature = "nightly", flash_f4))] +pub mod asynch; #[cfg(flash)] mod common; @@ -10,6 +12,8 @@ pub use crate::_generated::flash_regions::*; pub use crate::_generated::MAX_ERASE_SIZE; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +pub const READ_SIZE: usize = 1; + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion { @@ -76,6 +80,7 @@ pub enum Error { Protected, Unaligned, Parallelism, + TryLockError, } impl NorFlashError for Error { @@ -87,3 +92,47 @@ impl NorFlashError for Error { } } } + +pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { + let mut current_bank = FlashBank::Bank1; + let mut bank_offset = 0; + for region in regions { + if region.bank != current_bank { + current_bank = region.bank; + bank_offset = 0; + } + + if address < region.end() { + let index_in_region = (address - region.base) / region.erase_size; + return FlashSector { + bank: region.bank, + index_in_bank: bank_offset + index_in_region as u8, + start: region.base + index_in_region * region.erase_size, + size: region.erase_size, + }; + } + + bank_offset += region.sectors(); + } + + panic!("Flash sector not found"); +} + +pub(crate) fn ensure_sector_aligned( + start_address: u32, + end_address: u32, + regions: &[&FlashRegion], +) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + Ok(()) +} diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index 556034654..e21b0b241 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -8,22 +8,26 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { unimplemented!(); } pub(crate) unsafe fn unlock() { unimplemented!(); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { unimplemented!(); } -pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(_sector: &FlashSector) -> Result<(), Error> { unimplemented!(); } pub(crate) unsafe fn clear_all_err() { From 0a26870d363022a7d09500b6ecb28c0b455e81e7 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:41:52 +0200 Subject: [PATCH 1174/1575] Align examples --- examples/stm32f3/src/bin/flash.rs | 5 +- examples/stm32f4/src/bin/flash.rs | 14 ++--- examples/stm32f4/src/bin/flash_async.rs | 81 +++++++++++++++++++++++++ examples/stm32f7/src/bin/flash.rs | 5 +- examples/stm32h7/src/bin/flash.rs | 5 +- examples/stm32l0/src/bin/flash.rs | 5 +- examples/stm32l1/src/bin/flash.rs | 5 +- examples/stm32wl/src/bin/flash.rs | 5 +- 8 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 examples/stm32f4/src/bin/flash_async.rs diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index e40ad4fc0..0e5fb0658 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index bd3a7c95e..de4ecdb8f 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { // Once can also call `into_regions()` to get access to NorFlash implementations // for each of the unique characteristics. - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); // Sector 5 test_flash(&mut f, 128 * 1024, 128 * 1024); @@ -31,19 +31,19 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(offset, &mut buf)); + unwrap!(f.read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.blocking_erase(offset, offset + size)); + unwrap!(f.erase_blocking(offset, offset + size)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(offset, &mut buf)); + unwrap!(f.read(offset, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.blocking_write( + unwrap!(f.write_blocking( offset, &[ 1, 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, @@ -53,7 +53,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(offset, &mut buf)); + unwrap!(f.read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs new file mode 100644 index 000000000..c9d9df34b --- /dev/null +++ b/examples/stm32f4/src/bin/flash_async.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_time::{Timer, Duration}; +use embassy_stm32::flash::Flash; +use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; +use embassy_stm32::{interrupt}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello Flash!"); + + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); + + // Led should blink uninterrupted during ~2sec erase operation + spawner.spawn(blinky(p.PB7.degrade())).unwrap(); + + // Test on bank 2 in order not to stall CPU. + test_flash(&mut f, 1024 * 1024, 128 * 1024).await; +} + +#[embassy_executor::task] +async fn blinky(p: AnyPin) { + let mut led = Output::new(p, 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; + } +} + +async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) { + info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(offset, offset + size).await); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(offset, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!( + f.write( + offset, + &[ + 1, 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 + ] + ) + .await + ); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + assert_eq!( + &buf[..], + &[ + 1, 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 + ] + ); +} \ No newline at end of file diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index aabfe8557..717c82e86 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -4,9 +4,8 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; +use embassy_stm32::{flash::Flash, interrupt}; use embassy_time::{Duration, Timer}; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -19,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region3; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region3.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index 7ee9838c9..aab72cae8 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -4,9 +4,8 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; +use embassy_stm32::{flash::Flash, interrupt}; use embassy_time::{Duration, Timer}; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -19,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH).into_regions().bank2_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank2_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 337425028..0ed0d05fc 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 38feb0d76..c4d7d029c 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index e6bc2865c..df51ceb68 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; From ff3a70ed9debd1ce4075c0a28c90bb0c6e693bce Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:42:35 +0200 Subject: [PATCH 1175/1575] Add missing nightly guards --- embassy-stm32/src/flash/f4.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 3c8f81eb0..9698bcd58 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -139,6 +139,7 @@ mod alt_regions { } } + #[cfg(all(feature = "nightly"))] impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { const READ_SIZE: usize = READ_SIZE; @@ -151,6 +152,7 @@ mod alt_regions { } } + #[cfg(all(feature = "nightly"))] impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; From 6804b6c0b49777489b132639290b5e977ec8ffd9 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:45:59 +0200 Subject: [PATCH 1176/1575] Fix unused get_sector and ensure_sector_aligned --- embassy-stm32/src/flash/common.rs | 49 +++++++++++++++++++++++++++++-- embassy-stm32/src/flash/mod.rs | 44 --------------------------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 990104a38..7cec5a3c9 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -8,8 +8,8 @@ use embassy_sync::mutex::Mutex; use stm32_metapac::FLASH_BASE; use super::{ - ensure_sector_aligned, family, get_sector, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, - WRITE_SIZE, + family, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, FlashSector, FlashBank, }; use crate::peripherals::FLASH; use crate::Peripheral; @@ -118,6 +118,51 @@ pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> R Ok(()) } +pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { + let mut current_bank = FlashBank::Bank1; + let mut bank_offset = 0; + for region in regions { + if region.bank != current_bank { + current_bank = region.bank; + bank_offset = 0; + } + + if address < region.end() { + let index_in_region = (address - region.base) / region.erase_size; + return FlashSector { + bank: region.bank, + index_in_bank: bank_offset + index_in_region as u8, + start: region.base + index_in_region * region.erase_size, + size: region.erase_size, + }; + } + + bank_offset += region.sectors(); + } + + panic!("Flash sector not found"); +} + +pub(crate) fn ensure_sector_aligned( + start_address: u32, + end_address: u32, + regions: &[&FlashRegion], +) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + Ok(()) +} + + impl embedded_storage::nor_flash::ErrorType for Flash<'_> { type Error = Error; } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index e781f1b86..1ef04e56f 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -92,47 +92,3 @@ impl NorFlashError for Error { } } } - -pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { - let mut current_bank = FlashBank::Bank1; - let mut bank_offset = 0; - for region in regions { - if region.bank != current_bank { - current_bank = region.bank; - bank_offset = 0; - } - - if address < region.end() { - let index_in_region = (address - region.base) / region.erase_size; - return FlashSector { - bank: region.bank, - index_in_bank: bank_offset + index_in_region as u8, - start: region.base + index_in_region * region.erase_size, - size: region.erase_size, - }; - } - - bank_offset += region.sectors(); - } - - panic!("Flash sector not found"); -} - -pub(crate) fn ensure_sector_aligned( - start_address: u32, - end_address: u32, - regions: &[&FlashRegion], -) -> Result<(), Error> { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } - Ok(()) -} From 1329a387e060d60ee2833d2eed6393f5dfc84d1a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:55:17 +0200 Subject: [PATCH 1177/1575] Add more missing nightly guards --- embassy-stm32/src/flash/f4.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 9698bcd58..0c008fd0d 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,12 +2,13 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use embassy_sync::waitqueue::AtomicWaker; - use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +#[cfg(feature = "nightly")] +use embassy_sync::waitqueue::AtomicWaker; + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { use embassy_hal_common::PeripheralRef; @@ -15,10 +16,12 @@ mod alt_regions { use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; use crate::flash::{ - asynch, common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, + common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, READ_SIZE, REGION_ACCESS, }; use crate::peripherals::FLASH; + #[cfg(feature = "nightly")] + use crate::flash::asynch; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { size: 3 * BANK1_REGION3.erase_size, @@ -100,13 +103,13 @@ mod alt_regions { common::read_blocking(self.0.base, self.0.size, offset, bytes) } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { let _guard = REGION_ACCESS.lock().await; unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { let _guard = REGION_ACCESS.lock().await; unsafe { asynch::erase_sectored(self.0.base, from, to).await } @@ -139,7 +142,7 @@ mod alt_regions { } } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { const READ_SIZE: usize = READ_SIZE; @@ -152,7 +155,7 @@ mod alt_regions { } } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; @@ -209,6 +212,7 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { w.set_eop(true); }); + #[cfg(feature = "nightly")] WAKER.wake(); } @@ -277,6 +281,7 @@ unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { } } +#[cfg(feature = "nightly")] pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; From 966f0abf48cc143fc33e17a5fc9e138cf82ab05f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 13:11:03 +0200 Subject: [PATCH 1178/1575] Run format with nightly --- embassy-stm32/src/flash/common.rs | 4 +--- embassy-stm32/src/flash/f4.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 7cec5a3c9..e1fe7e9da 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -8,8 +8,7 @@ use embassy_sync::mutex::Mutex; use stm32_metapac::FLASH_BASE; use super::{ - family, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, - WRITE_SIZE, FlashSector, FlashBank, + family, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, }; use crate::peripherals::FLASH; use crate::Peripheral; @@ -162,7 +161,6 @@ pub(crate) fn ensure_sector_aligned( Ok(()) } - impl embedded_storage::nor_flash::ErrorType for Flash<'_> { type Error = Error; } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 0c008fd0d..084bbdc6e 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,26 +2,26 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +#[cfg(feature = "nightly")] +use embassy_sync::waitqueue::AtomicWaker; + use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -#[cfg(feature = "nightly")] -use embassy_sync::waitqueue::AtomicWaker; - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { use embassy_hal_common::PeripheralRef; use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; - use crate::flash::{ - common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, - READ_SIZE, REGION_ACCESS, - }; - use crate::peripherals::FLASH; #[cfg(feature = "nightly")] use crate::flash::asynch; + use crate::flash::{ + common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, READ_SIZE, + REGION_ACCESS, + }; + use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { size: 3 * BANK1_REGION3.erase_size, From dfd56031713aa04af682aa1b2b113a72831728f1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 17:24:28 +0200 Subject: [PATCH 1179/1575] Let FlashLayout and FlashRegion depends on a Blocking/Async mode generic --- embassy-stm32/Cargo.toml | 1 - embassy-stm32/build.rs | 16 ++- embassy-stm32/src/flash/asynch.rs | 35 ++++- embassy-stm32/src/flash/common.rs | 218 ++++++++++++------------------ embassy-stm32/src/flash/f4.rs | 112 +++++++-------- embassy-stm32/src/flash/mod.rs | 3 + 6 files changed, 179 insertions(+), 206 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 440de85e8..2572eafce 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -69,7 +69,6 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" -paste = "1.0.12" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 540981727..29af3c80d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -213,7 +213,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { #[cfg(flash)] - pub struct #region_type<'d>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>,); + pub struct #region_type<'d, MODE>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); }); } @@ -224,11 +224,11 @@ fn main() { let field_name = format_ident!("{}", region_name.to_lowercase()); let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); let field = quote! { - pub #field_name: #field_type<'d> + pub #field_name: #field_type<'d, MODE> }; let region_name = format_ident!("{}", region_name); let init = quote! { - #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}) + #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData) }; (field, (init, region_name)) @@ -238,15 +238,17 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { #[cfg(flash)] - pub struct FlashLayout<'d> { - #(#fields),* + pub struct FlashLayout<'d, MODE> { + #(#fields),*, + _mode: core::marker::PhantomData, } #[cfg(flash)] - impl<'d> FlashLayout<'d> { + impl<'d, MODE> FlashLayout<'d, MODE> { pub(crate) fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { - #(#inits),* + #(#inits),*, + _mode: core::marker::PhantomData, } } } diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 44e23d9c4..3564bbff5 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,12 +1,21 @@ use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::Mutex; use super::{ - ensure_sector_aligned, family, get_sector, Error, Flash, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, - REGION_ACCESS, WRITE_SIZE, + ensure_sector_aligned, family, get_sector, read_blocking, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, + MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, }; +pub(super) static REGION_ACCESS: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { + pub fn into_regions(self) -> FlashLayout<'d, Async> { + family::set_default_layout(); + FlashLayout::new(self.inner) + } + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } } @@ -16,6 +25,18 @@ impl<'d> Flash<'d> { } } +impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + impl embedded_storage_async::nor_flash::NorFlash for Flash<'_> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = MAX_ERASE_SIZE; @@ -89,7 +110,11 @@ pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Resu foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl crate::_generated::flash_regions::$type_name<'_> { + impl crate::_generated::flash_regions::$type_name<'_, Async> { + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) + } + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { let _guard = REGION_ACCESS.lock().await; unsafe { write_chunked(self.0.base, self.0.size, offset, bytes).await } @@ -101,7 +126,7 @@ foreach_flash_region! { } } - impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, Async> { const READ_SIZE: usize = READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -113,7 +138,7 @@ foreach_flash_region! { } } - impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_, Async> { const WRITE_SIZE: usize = $write_size; const ERASE_SIZE: usize = $erase_size; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index e1fe7e9da..8b38745cf 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,14 +1,12 @@ use atomic_polyfill::{fence, Ordering}; use embassy_cortex_m::interrupt::InterruptExt; -use embassy_futures::block_on; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::mutex::Mutex; use stm32_metapac::FLASH_BASE; use super::{ - family, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, + family, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, }; use crate::peripherals::FLASH; use crate::Peripheral; @@ -17,8 +15,6 @@ pub struct Flash<'d> { pub(crate) inner: PeripheralRef<'d, FLASH>, } -pub(crate) static REGION_ACCESS: Mutex = Mutex::new(()); - impl<'d> Flash<'d> { pub fn new(p: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { into_ref!(p, irq); @@ -30,7 +26,7 @@ impl<'d> Flash<'d> { Self { inner: p } } - pub fn into_regions(self) -> FlashLayout<'d> { + pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { family::set_default_layout(); FlashLayout::new(self.inner) } @@ -40,11 +36,19 @@ impl<'d> Flash<'d> { } pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { write_chunked_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } + unsafe { + write_blocking( + FLASH_BASE as u32, + FLASH_SIZE as u32, + offset, + bytes, + write_chunk_unlocked, + ) + } } pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { erase_sectored_blocking(FLASH_BASE as u32, from, to) } + unsafe { erase_blocking(FLASH_BASE as u32, from, to, erase_sector_unlocked) } } } @@ -59,7 +63,13 @@ pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) Ok(()) } -pub(super) unsafe fn write_chunked_blocking(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { +pub(super) unsafe fn write_blocking( + base: u32, + size: u32, + offset: u32, + bytes: &[u8], + write_chunk: unsafe fn(u32, &[u8]) -> Result<(), Error>, +) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -71,26 +81,39 @@ pub(super) unsafe fn write_chunked_blocking(base: u32, size: u32, offset: u32, b trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); for chunk in bytes.chunks(WRITE_SIZE) { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); - family::enable_blocking_write(); - fence(Ordering::SeqCst); - - let _on_drop = OnDrop::new(|| { - family::disable_blocking_write(); - fence(Ordering::SeqCst); - family::lock(); - }); - - family::write_blocking(address, chunk.try_into().unwrap())?; + write_chunk(address, chunk)?; address += WRITE_SIZE as u32; } Ok(()) } -pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> Result<(), Error> { +pub(super) unsafe fn write_chunk_unlocked(address: u32, chunk: &[u8]) -> Result<(), Error> { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_blocking_write(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { + family::disable_blocking_write(); + fence(Ordering::SeqCst); + family::lock(); + }); + + family::write_blocking(address, chunk.try_into().unwrap()) +} + +pub(super) unsafe fn write_chunk_with_critical_section(address: u32, chunk: &[u8]) -> Result<(), Error> { + critical_section::with(|_| write_chunk_unlocked(address, chunk)) +} + +pub(super) unsafe fn erase_blocking( + base: u32, + from: u32, + to: u32, + erase_sector: unsafe fn(&FlashSector) -> Result<(), Error>, +) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; let regions = family::get_flash_regions(); @@ -103,21 +126,28 @@ pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> R while address < end_address { let sector = get_sector(address, regions); trace!("Erasing sector: {:?}", sector); - - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); - - let _on_drop = OnDrop::new(|| family::lock()); - - family::erase_sector_blocking(§or)?; + erase_sector(§or)?; address += sector.size; } Ok(()) } -pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { +pub(super) unsafe fn erase_sector_unlocked(sector: &FlashSector) -> Result<(), Error> { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| family::lock()); + + family::erase_sector_blocking(§or) +} + +pub(super) unsafe fn erase_sector_with_critical_section(sector: &FlashSector) -> Result<(), Error> { + critical_section::with(|_| erase_sector_unlocked(sector)) +} + +pub(super) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { let mut current_bank = FlashBank::Bank1; let mut bank_offset = 0; for region in regions { @@ -142,7 +172,7 @@ pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector panic!("Flash sector not found"); } -pub(crate) fn ensure_sector_aligned( +pub(super) fn ensure_sector_aligned( start_address: u32, end_address: u32, regions: &[&FlashRegion], @@ -190,121 +220,49 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_> { } } -#[cfg(feature = "nightly")] -impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { - const READ_SIZE: usize = READ_SIZE; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - FLASH_SIZE - } -} - -pub struct BlockingFlashRegion<'d, const WRITE_SIZE: u32, const ERASE_SIZE: u32>( - &'static FlashRegion, - PeripheralRef<'d, FLASH>, -); - -impl BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> { - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - read_blocking(self.0.base, self.0.size, offset, bytes) - } - - pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = block_on(REGION_ACCESS.lock()); - unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } - } - - pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = block_on(REGION_ACCESS.lock()); - unsafe { erase_sectored_blocking(self.0.base, from, to) } - } -} - -impl embedded_storage::nor_flash::ErrorType - for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> -{ - type Error = Error; -} - -impl embedded_storage::nor_flash::ReadNorFlash - for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> -{ - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.0.size as usize - } -} - -impl embedded_storage::nor_flash::NorFlash - for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> -{ - const WRITE_SIZE: usize = WRITE_SIZE as usize; - const ERASE_SIZE: usize = ERASE_SIZE as usize; - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) - } -} - foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - paste::paste! { - pub type []<'d> = BlockingFlashRegion<'d, $write_size, $erase_size>; - } - - impl<'d> crate::_generated::flash_regions::$type_name<'d> { - /// Make this flash region work in a blocking context. - /// - /// SAFETY - /// - /// This function is unsafe as incorect usage of parallel blocking operations - /// on multiple regions may cause a deadlock because each region requires mutual access to the flash. - pub unsafe fn into_blocking(self) -> BlockingFlashRegion<'d, $write_size, $erase_size> { - BlockingFlashRegion(self.0, self.1) - } - - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + impl<'d> crate::_generated::flash_regions::$type_name<'d, Blocking> { + pub fn read_blocking(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_blocking(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) } } - pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { erase_sectored_blocking(self.0.base, from, to) } + pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_blocking(self.0.base, from, to, erase_sector_with_critical_section) } } } - impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_, MODE> { type Error = Error; } - impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, Blocking> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) + self.read_blocking(offset, bytes) } fn capacity(&self) -> usize { self.0.size as usize } } + + impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_, Blocking> { + const WRITE_SIZE: usize = $write_size; + const ERASE_SIZE: usize = $erase_size; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write_blocking(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase_blocking(from, to) + } + } }; } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 084bbdc6e..d50a35b41 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -11,6 +11,8 @@ use crate::pac; #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { + use core::marker::PhantomData; + use embassy_hal_common::PeripheralRef; use stm32_metapac::FLASH_SIZE; @@ -18,8 +20,7 @@ mod alt_regions { #[cfg(feature = "nightly")] use crate::flash::asynch; use crate::flash::{ - common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, READ_SIZE, - REGION_ACCESS, + common, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion, READ_SIZE, }; use crate::peripherals::FLASH; @@ -53,101 +54,86 @@ mod alt_regions { &ALT_BANK2_REGION3, ]; - pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); - pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); - pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); - pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank1Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region1<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region2<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub type BlockingAltBank1Region3<'d> = - BlockingFlashRegion<'d, { ALT_BANK1_REGION3.write_size }, { ALT_BANK1_REGION3.erase_size }>; - pub type BlockingAltBank2Region1<'d> = - BlockingFlashRegion<'d, { ALT_BANK2_REGION1.write_size }, { ALT_BANK2_REGION1.erase_size }>; - pub type BlockingAltBank2Region2<'d> = - BlockingFlashRegion<'d, { ALT_BANK2_REGION2.write_size }, { ALT_BANK2_REGION2.erase_size }>; - pub type BlockingAltBank2Region3<'d> = - BlockingFlashRegion<'d, { ALT_BANK2_REGION3.write_size }, { ALT_BANK2_REGION3.erase_size }>; - - pub struct AltFlashLayout<'d> { - pub bank1_region1: Bank1Region1<'d>, - pub bank1_region2: Bank1Region2<'d>, - pub bank1_region3: AltBank1Region3<'d>, - pub bank2_region1: AltBank2Region1<'d>, - pub bank2_region2: AltBank2Region2<'d>, - pub bank2_region3: AltBank2Region3<'d>, - pub otp_region: OTPRegion<'d>, + pub struct AltFlashLayout<'d, MODE> { + pub bank1_region1: Bank1Region1<'d, MODE>, + pub bank1_region2: Bank1Region2<'d, MODE>, + pub bank1_region3: AltBank1Region3<'d, MODE>, + pub bank2_region1: AltBank2Region1<'d, MODE>, + pub bank2_region2: AltBank2Region2<'d, MODE>, + pub bank2_region3: AltBank2Region3<'d, MODE>, + pub otp_region: OTPRegion<'d, MODE>, } impl<'d> Flash<'d> { - pub fn into_alt_regions(self) -> AltFlashLayout<'d> { + pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> { + unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + + // SAFETY: We never expose the cloned peripheral references, and their instance is not public. + // Also, all async flash region operations are protected with a mutex. + let p = self.inner; + AltFlashLayout { + bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), + } + } + + pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. let p = self.inner; AltFlashLayout { - bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), - bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), - bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }), - bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }), - bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }), - bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }), + bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } } macro_rules! foreach_altflash_region { ($type_name:ident, $region:ident) => { - impl $type_name<'_> { - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + #[cfg(feature = "nightly")] + impl $type_name<'_, Async> { + pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { common::read_blocking(self.0.base, self.0.size, offset, bytes) } - #[cfg(feature = "nightly")] pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = REGION_ACCESS.lock().await; + let _guard = asynch::REGION_ACCESS.lock().await; unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } } - #[cfg(feature = "nightly")] pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = REGION_ACCESS.lock().await; + let _guard = asynch::REGION_ACCESS.lock().await; unsafe { asynch::erase_sectored(self.0.base, from, to).await } } - - pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { common::write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } - } - - pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { common::erase_sectored_blocking(self.0.base, from, to) } - } } - impl embedded_storage::nor_flash::ErrorType for $type_name<'_> { + impl embedded_storage::nor_flash::ErrorType for $type_name<'_, Async> { type Error = Error; } - impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_> { - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.0.size as usize - } - } - #[cfg(feature = "nightly")] - impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { + impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> { const READ_SIZE: usize = READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) + self.read(offset, bytes).await } fn capacity(&self) -> usize { @@ -156,7 +142,7 @@ mod alt_regions { } #[cfg(feature = "nightly")] - impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { + impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_, Async> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1ef04e56f..56a680a86 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -14,6 +14,9 @@ pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; pub const READ_SIZE: usize = 1; +pub struct Blocking; +pub struct Async; + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion { From 8b13a7b33874483783cfd5be8fcbd73c888cd906 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 17:31:35 +0200 Subject: [PATCH 1180/1575] Align examples --- examples/stm32f3/src/bin/flash.rs | 12 ++++++------ examples/stm32f7/src/bin/flash.rs | 12 ++++++------ examples/stm32h7/src/bin/flash.rs | 12 ++++++------ examples/stm32l0/src/bin/flash.rs | 12 ++++++------ examples/stm32l1/src/bin/flash.rs | 12 ++++++------ examples/stm32wl/src/bin/flash.rs | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index 0e5fb0658..befae0a16 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 2048)); + unwrap!(f.erase_blocking(ADDR, ADDR + 2048)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index 717c82e86..5507e7310 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region3.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region3; info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 256 * 1024)); + unwrap!(f.erase_blocking(ADDR, ADDR + 256 * 1024)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write( + unwrap!(f.write_blocking( ADDR, &[ 1, 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, @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index aab72cae8..fe6dad249 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank2_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 128 * 1024)); + unwrap!(f.erase_blocking(ADDR, ADDR + 128 * 1024)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write( + unwrap!(f.write_blocking( ADDR, &[ 1, 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, @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 0ed0d05fc..4182c87b1 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 128)); + unwrap!(f.erase_blocking(ADDR, ADDR + 128)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index c4d7d029c..53052e7c5 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 256)); + unwrap!(f.erase_blocking(ADDR, ADDR + 256)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index df51ceb68..e03b69b82 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 2048)); + unwrap!(f.erase_blocking(ADDR, ADDR + 2048)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } From 9370973846310d013f5be586a4e37031fcb21a16 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 17:35:49 +0200 Subject: [PATCH 1181/1575] Remove TryLockError, --- embassy-stm32/src/flash/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 56a680a86..02f6c5320 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -83,7 +83,6 @@ pub enum Error { Protected, Unaligned, Parallelism, - TryLockError, } impl NorFlashError for Error { From e65ff85b88deef3e32cc437d28e36274b82ce03e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 23:51:48 +0200 Subject: [PATCH 1182/1575] Default to Async mode --- embassy-stm32/build.rs | 4 ++-- embassy-stm32/src/flash/f4.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 29af3c80d..b766f0739 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -213,7 +213,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { #[cfg(flash)] - pub struct #region_type<'d, MODE>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); + pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); }); } @@ -238,7 +238,7 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { #[cfg(flash)] - pub struct FlashLayout<'d, MODE> { + pub struct FlashLayout<'d, MODE = crate::flash::Async> { #(#fields),*, _mode: core::marker::PhantomData, } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index d50a35b41..3d696223c 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -54,12 +54,12 @@ mod alt_regions { &ALT_BANK2_REGION3, ]; - pub struct AltBank1Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltBank2Region1<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltBank2Region2<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltBank2Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank1Region3<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region1<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region2<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region3<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltFlashLayout<'d, MODE> { + pub struct AltFlashLayout<'d, MODE = Async> { pub bank1_region1: Bank1Region1<'d, MODE>, pub bank1_region2: Bank1Region2<'d, MODE>, pub bank1_region3: AltBank1Region3<'d, MODE>, From cd8198037fa37db9af7e9dde1e00122df9ed15a3 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:08:40 +0200 Subject: [PATCH 1183/1575] Actually transition to dual bank mode - key was required --- embassy-stm32/src/flash/f4.rs | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 3d696223c..50ab446bd 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -71,7 +71,7 @@ mod alt_regions { impl<'d> Flash<'d> { pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> { - unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + super::set_alt_layout(); // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all async flash region operations are protected with a mutex. @@ -88,7 +88,7 @@ mod alt_regions { } pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> { - unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + super::set_alt_layout(); // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. @@ -171,12 +171,31 @@ static WAKER: AtomicWaker = AtomicWaker::new(); #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn set_default_layout() { - unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) }; + unsafe { + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x08192A3B)); + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x4C5D6E7F)); + pac::FLASH.optcr().modify(|r| { + r.set_db1m(false); + r.set_optlock(true) + }); + }; } #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] pub const fn set_default_layout() {} +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +fn set_alt_layout() { + unsafe { + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x08192A3B)); + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x4C5D6E7F)); + pac::FLASH.optcr().modify(|r| { + r.set_db1m(true); + r.set_optlock(true) + }); + }; +} + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn get_flash_regions() -> &'static [&'static FlashRegion] { if unsafe { pac::FLASH.optcr().read().db1m() } { @@ -207,8 +226,8 @@ pub(crate) unsafe fn lock() { } pub(crate) unsafe fn unlock() { - pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123)); - pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); + pac::FLASH.keyr().write(|w| w.set_key(0x45670123)); + pac::FLASH.keyr().write(|w| w.set_key(0xCDEF89AB)); } #[cfg(feature = "nightly")] From baf1c2efbe0b218f86b491b88e48531fde691851 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:42:42 +0200 Subject: [PATCH 1184/1575] Align with new bind_interrupt --- embassy-stm32/src/flash/asynch.rs | 3 ++ embassy-stm32/src/flash/common.rs | 41 +++++++++++++++---- embassy-stm32/src/flash/f0.rs | 2 +- embassy-stm32/src/flash/f3.rs | 2 +- embassy-stm32/src/flash/f4.rs | 2 +- embassy-stm32/src/flash/f7.rs | 2 +- embassy-stm32/src/flash/h7.rs | 2 +- embassy-stm32/src/flash/l.rs | 2 +- embassy-stm32/src/flash/other.rs | 2 +- examples/boot/application/rp/src/bin/a.rs | 2 +- .../boot/application/stm32f3/src/bin/a.rs | 2 +- .../boot/application/stm32f7/src/bin/a.rs | 2 +- .../boot/application/stm32h7/src/bin/a.rs | 2 +- .../boot/application/stm32l0/src/bin/a.rs | 2 +- .../boot/application/stm32l1/src/bin/a.rs | 2 +- .../boot/application/stm32l4/src/bin/a.rs | 2 +- .../boot/application/stm32wl/src/bin/a.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 3 +- examples/stm32f3/src/bin/flash.rs | 4 +- examples/stm32f4/src/bin/flash.rs | 4 +- examples/stm32f4/src/bin/flash_async.rs | 10 +++-- examples/stm32h7/src/bin/flash.rs | 4 +- examples/stm32l0/src/bin/flash.rs | 4 +- examples/stm32l1/src/bin/flash.rs | 4 +- examples/stm32wl/src/bin/flash.rs | 4 +- 25 files changed, 71 insertions(+), 40 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 3564bbff5..017fb17fa 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -12,15 +12,18 @@ pub(super) static REGION_ACCESS: Mutex = Mutex::new impl<'d> Flash<'d> { pub fn into_regions(self) -> FlashLayout<'d, Async> { + assert!(!self.blocking_only); family::set_default_layout(); FlashLayout::new(self.inner) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + assert!(!self.blocking_only); unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } } pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + assert!(!self.blocking_only); unsafe { erase_sectored(FLASH_BASE as u32, from, to).await } } } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 8b38745cf..0a1ee5166 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,5 +1,5 @@ use atomic_polyfill::{fence, Ordering}; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; @@ -9,21 +9,37 @@ use super::{ WRITE_SIZE, }; use crate::peripherals::FLASH; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; pub struct Flash<'d> { pub(crate) inner: PeripheralRef<'d, FLASH>, + pub(crate) blocking_only: bool, } impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(p, irq); + pub fn new( + p: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, + ) -> Self { + into_ref!(p); - irq.set_handler(family::on_interrupt); - irq.unpend(); - irq.enable(); + let flash_irq = unsafe { crate::interrupt::FLASH::steal() }; + flash_irq.unpend(); + flash_irq.enable(); - Self { inner: p } + Self { + inner: p, + blocking_only: false, + } + } + + pub fn new_blocking_only(p: impl Peripheral

+ 'd) -> Self { + into_ref!(p); + + Self { + inner: p, + blocking_only: true, + } } pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { @@ -52,6 +68,15 @@ impl<'d> Flash<'d> { } } +/// Interrupt handler +pub struct InterruptHandler; + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + family::on_interrupt(); + } +} + pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index ecf3a6981..cd17486e6 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -13,7 +13,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index fd778f2b1..4ce391288 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -13,7 +13,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 50ab446bd..2b0472640 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -210,7 +210,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { // Clear IRQ flags pac::FLASH.sr().write(|w| { w.set_operr(true); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index a0593b14b..ab518bf89 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -12,7 +12,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 865f13283..d6818d594 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -17,7 +17,7 @@ pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index f8a0dac4c..c2394e0c9 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -12,7 +12,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index e21b0b241..e569951f9 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -8,7 +8,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index e3ac634c2..2b84ec614 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -26,7 +26,7 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let mut flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking_only(p.FLASH); let mut updater = FirmwareUpdater::default(); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index d92d59b29..a69b6327f 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 79ab80e09..1f55db932 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new(p.FLASH); + let mut flash = Flash::new_blocking_only(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 8b452be34..b8617c3bd 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new(p.FLASH); + let mut flash = Flash::new_blocking_only(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index 59ca34386..c66635639 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 59ca34386..c66635639 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 6cddc6cc8..86936222c 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 1ff47eddd..2982e8df1 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PA0, Pull::Up); diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 49c21920b..5e8a4f2b3 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,8 +20,7 @@ fn main() -> ! { */ let mut bl: BootLoader<2048> = BootLoader::default(); - let flash = Flash::new(p.FLASH); - let layout = flash.into_regions(); + let layout = Flash::new_blocking_only(p.FLASH).into_blocking_regions(); let mut flash = BootFlash::new(layout.bank1_region); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index befae0a16..9a31b548d 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index de4ecdb8f..455af930b 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { // Once can also call `into_regions()` to get access to NorFlash implementations // for each of the unique characteristics. - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); + let mut f = Flash::new_blocking_only(p.FLASH); // Sector 5 test_flash(&mut f, 128 * 1024, 128 * 1024); diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs index c9d9df34b..675337083 100644 --- a/examples/stm32f4/src/bin/flash_async.rs +++ b/examples/stm32f4/src/bin/flash_async.rs @@ -5,17 +5,21 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_time::{Timer, Duration}; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, InterruptHandler}; use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; -use embassy_stm32::{interrupt}; +use embassy_stm32::bind_interrupts; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + FLASH => InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); + let mut f = Flash::new(p.FLASH, Irqs); // Led should blink uninterrupted during ~2sec erase operation spawner.spawn(blinky(p.PB7.degrade())).unwrap(); diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index fe6dad249..c0c332c34 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank2_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 4182c87b1..57ccf7f57 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 53052e7c5..71174bfbc 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index e03b69b82..51bd0db4e 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; From 8b1eaf00a0fd24459407a70e76bdbc16983b9af6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:54:40 +0200 Subject: [PATCH 1185/1575] Simplify SR->Result --- embassy-stm32/src/flash/f4.rs | 45 +++++++++++++---------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 2b0472640..53e58835e 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -4,6 +4,7 @@ use core::sync::atomic::{fence, Ordering}; #[cfg(feature = "nightly")] use embassy_sync::waitqueue::AtomicWaker; +use pac::flash::regs::Sr; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; @@ -347,17 +348,7 @@ pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { let sr = pac::FLASH.sr().read(); if !sr.bsy() { - Poll::Ready(if sr.pgserr() { - Err(Error::Seq) - } else if sr.pgperr() { - Err(Error::Parallelism) - } else if sr.pgaerr() { - Err(Error::Unaligned) - } else if sr.wrperr() { - Err(Error::Protected) - } else { - Ok(()) - }) + Poll::Ready(get_result(sr)) } else { return Poll::Pending; } @@ -370,27 +361,25 @@ unsafe fn wait_ready_blocking() -> Result<(), Error> { let sr = pac::FLASH.sr().read(); if !sr.bsy() { - if sr.pgserr() { - return Err(Error::Seq); - } - - if sr.pgperr() { - return Err(Error::Parallelism); - } - - if sr.pgaerr() { - return Err(Error::Unaligned); - } - - if sr.wrperr() { - return Err(Error::Protected); - } - - return Ok(()); + return get_result(sr); } } } +fn get_result(sr: Sr) -> Result<(), Error> { + if sr.pgserr() { + Err(Error::Seq) + } else if sr.pgperr() { + Err(Error::Parallelism) + } else if sr.pgaerr() { + Err(Error::Unaligned) + } else if sr.wrperr() { + Err(Error::Protected) + } else { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; From c02759ad91994191944b4fd1a4b47cd310416c04 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:59:32 +0200 Subject: [PATCH 1186/1575] Fix unused errors --- embassy-stm32/src/flash/common.rs | 3 +++ embassy-stm32/src/flash/f4.rs | 8 +++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 0a1ee5166..547e30312 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -13,6 +13,7 @@ use crate::{interrupt, Peripheral}; pub struct Flash<'d> { pub(crate) inner: PeripheralRef<'d, FLASH>, + #[cfg(all(feature = "nightly", flash_f4))] pub(crate) blocking_only: bool, } @@ -29,6 +30,7 @@ impl<'d> Flash<'d> { Self { inner: p, + #[cfg(all(feature = "nightly", flash_f4))] blocking_only: false, } } @@ -38,6 +40,7 @@ impl<'d> Flash<'d> { Self { inner: p, + #[cfg(all(feature = "nightly", flash_f4))] blocking_only: true, } } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 53e58835e..aa3433b23 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -20,9 +20,7 @@ mod alt_regions { use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; #[cfg(feature = "nightly")] use crate::flash::asynch; - use crate::flash::{ - common, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion, READ_SIZE, - }; + use crate::flash::{Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { @@ -111,7 +109,7 @@ mod alt_regions { #[cfg(feature = "nightly")] impl $type_name<'_, Async> { pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - common::read_blocking(self.0.base, self.0.size, offset, bytes) + crate::flash::common::read_blocking(self.0.base, self.0.size, offset, bytes) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { @@ -131,7 +129,7 @@ mod alt_regions { #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> { - const READ_SIZE: usize = READ_SIZE; + const READ_SIZE: usize = crate::flash::READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { self.read(offset, bytes).await From b20427b2ec7ec5bf2e4553cca24eba20e7373da4 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2023 20:07:56 +0800 Subject: [PATCH 1187/1575] net: Make flush() wait for RST packets from abort() Add docs to note that dropping a TcpSocket early will prevent the .abort() RST from being sent. --- embassy-net/src/tcp.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 732b6d217..7babb5293 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -95,7 +95,8 @@ impl<'a> TcpWriter<'a> { /// Flushes the written data to the socket. /// - /// This waits until all data has been sent, and ACKed by the remote host. + /// This waits until all data has been sent, and ACKed by the remote host. For a connection + /// closed with [`abort()`](TcpSocket::abort) it will wait for the TCP RST packet to be sent. pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } @@ -198,7 +199,8 @@ impl<'a> TcpSocket<'a> { /// Flushes the written data to the socket. /// - /// This waits until all data has been sent, and ACKed by the remote host. + /// This waits until all data has been sent, and ACKed by the remote host. For a connection + /// closed with [`abort()`](TcpSocket::abort) it will wait for the TCP RST packet to be sent. pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } @@ -262,6 +264,11 @@ impl<'a> TcpSocket<'a> { /// /// This instantly closes both the read and write halves of the socket. Any pending data /// that has not been sent will be lost. + /// + /// Note that the TCP RST packet is not sent immediately - if the `TcpSocket` is dropped too soon + /// the remote host may not know the connection has been closed. + /// `abort()` callers should wait for a [`flush()`](TcpSocket::flush) call to complete before + /// dropping or reusing the socket. pub fn abort(&mut self) { self.io.with_mut(|s, _| s.abort()) } @@ -347,9 +354,10 @@ impl<'d> TcpIo<'d> { async fn flush(&mut self) -> Result<(), Error> { poll_fn(move |cx| { self.with_mut(|s, _| { + let waiting_close = s.state() == tcp::State::Closed && s.remote_endpoint().is_some(); // If there are outstanding send operations, register for wake up and wait // smoltcp issues wake-ups when octets are dequeued from the send buffer - if s.send_queue() > 0 { + if s.send_queue() > 0 || waiting_close { s.register_send_waker(cx.waker()); Poll::Pending // No outstanding sends, socket is flushed From 373eb973574ac9390f8b4b19c2de486b1b38101a Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 25 May 2023 19:36:45 +0800 Subject: [PATCH 1188/1575] Add std example of a TCP listener This also demonstrates calling .abort() on a TCP socket and ensuring that the reset packet is sent out. --- examples/std/src/bin/tcp_accept.rs | 133 +++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 examples/std/src/bin/tcp_accept.rs diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs new file mode 100644 index 000000000..97ce77f42 --- /dev/null +++ b/examples/std/src/bin/tcp_accept.rs @@ -0,0 +1,133 @@ +#![feature(type_alias_impl_trait)] + +use core::fmt::Write as _; +use std::default::Default; + +use clap::Parser; +use embassy_executor::{Executor, Spawner}; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::Write as _; +use heapless::Vec; +use log::*; +use rand_core::{OsRng, RngCore}; +use static_cell::StaticCell; + +#[path = "../tuntap.rs"] +mod tuntap; + +use crate::tuntap::TunTapDevice; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +#[derive(Parser)] +#[clap(version = "1.0")] +struct Opts { + /// TAP device name + #[clap(long, default_value = "tap0")] + tap: String, + /// use a static IP instead of DHCP + #[clap(long)] + static_ip: bool, +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[derive(Default)] +struct StrWrite(pub heapless::Vec); + +impl core::fmt::Write for StrWrite { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + self.0.extend_from_slice(s.as_bytes()).unwrap(); + Ok(()) + } +} + +#[embassy_executor::task] +async fn main_task(spawner: Spawner) { + let opts: Opts = Opts::parse(); + + // Init network device + let device = TunTapDevice::new(&opts.tap).unwrap(); + + // Choose between dhcp or static ip + let config = if opts.static_ip { + Config::Static(embassy_net::StaticConfig { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), + dns_servers: Vec::new(), + gateway: Some(Ipv4Address::new(192, 168, 69, 1)), + }) + } else { + Config::Dhcp(Default::default()) + }; + + // Generate random seed + let mut seed = [0; 8]; + OsRng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); + + // Launch network task + spawner.spawn(net_task(stack)).unwrap(); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + info!("Listening on TCP:9999..."); + if let Err(_) = socket.accept(9999).await { + warn!("accept error"); + continue; + } + + info!("Accepted a connection"); + + // Write some quick output + for i in 1..=5 { + let mut w = StrWrite::default(); + write!(w, "{}! ", i).unwrap(); + let r = socket.write_all(&w.0).await; + if let Err(e) = r { + warn!("write error: {:?}", e); + return; + } + + Timer::after(Duration::from_millis(500)).await; + } + info!("Closing the connection"); + socket.abort(); + info!("Flushing the RST out..."); + socket.flush().await; + info!("Finished with the socket"); + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .filter_module("async_io", log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(main_task(spawner)).unwrap(); + }); +} From 963f3e305971d293d3e64f7bc073902152fbf66c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 16:06:02 +0200 Subject: [PATCH 1189/1575] Align with updated stm32 metapac --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rcc/g4.rs | 86 +++++++++++++++++++++++-------------- embassy-stm32/src/rtc/v3.rs | 4 +- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 21adb5ddf..e5e321a25 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "7" +stm32-metapac = "8" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "7", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "8", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 0f078d328..7e748c7b5 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,3 +1,5 @@ +use stm32_metapac::rcc::vals::{Hpre, Ppre, Sw}; + use crate::pac::{PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -39,30 +41,58 @@ pub enum APBPrescaler { Div16, } -impl Into for APBPrescaler { - fn into(self) -> u8 { +impl AHBPrescaler { + const fn div(self) -> u32 { match self { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 0x04, - APBPrescaler::Div4 => 0x05, - APBPrescaler::Div8 => 0x06, - APBPrescaler::Div16 => 0x07, + AHBPrescaler::NotDivided => 1, + AHBPrescaler::Div2 => 2, + AHBPrescaler::Div4 => 4, + AHBPrescaler::Div8 => 8, + AHBPrescaler::Div16 => 16, + AHBPrescaler::Div64 => 64, + AHBPrescaler::Div128 => 128, + AHBPrescaler::Div256 => 256, + AHBPrescaler::Div512 => 512, } } } -impl Into for AHBPrescaler { - fn into(self) -> u8 { +impl APBPrescaler { + const fn div(self) -> u32 { match self { - AHBPrescaler::NotDivided => 1, - AHBPrescaler::Div2 => 0x08, - AHBPrescaler::Div4 => 0x09, - AHBPrescaler::Div8 => 0x0a, - AHBPrescaler::Div16 => 0x0b, - AHBPrescaler::Div64 => 0x0c, - AHBPrescaler::Div128 => 0x0d, - AHBPrescaler::Div256 => 0x0e, - AHBPrescaler::Div512 => 0x0f, + APBPrescaler::NotDivided => 1, + APBPrescaler::Div2 => 2, + APBPrescaler::Div4 => 4, + APBPrescaler::Div8 => 8, + APBPrescaler::Div16 => 16, + } + } +} + +impl Into for APBPrescaler { + fn into(self) -> Ppre { + match self { + APBPrescaler::NotDivided => Ppre::DIV1, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +impl Into for AHBPrescaler { + fn into(self) -> Hpre { + match self { + AHBPrescaler::NotDivided => Hpre::DIV1, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, } } } @@ -96,19 +126,19 @@ pub(crate) unsafe fn init(config: Config) { RCC.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {} - (HSI_FREQ.0, 0x01) + (HSI_FREQ.0, Sw::HSI16) } ClockSrc::HSE(freq) => { // Enable HSE RCC.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {} - (freq.0, 0x02) + (freq.0, Sw::HSE) } }; RCC.cfgr().modify(|w| { - w.set_sw(sw.into()); + w.set_sw(sw); w.set_hpre(config.ahb_pre.into()); w.set_ppre1(config.apb1_pre.into()); w.set_ppre2(config.apb2_pre.into()); @@ -116,19 +146,13 @@ pub(crate) unsafe fn init(config: Config) { let ahb_freq: u32 = match config.ahb_pre { AHBPrescaler::NotDivided => sys_clk, - pre => { - let pre: u8 = pre.into(); - let pre = 1 << (pre as u32 - 7); - sys_clk / pre - } + pre => sys_clk / pre.div(), }; let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { - let pre: u8 = pre.into(); - let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / pre as u32; + let freq = ahb_freq / pre.div(); (freq, freq * 2) } }; @@ -136,9 +160,7 @@ pub(crate) unsafe fn init(config: Config) { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { - let pre: u8 = pre.into(); - let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / pre as u32; + let freq = ahb_freq / pre.div(); (freq, freq * 2) } }; diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 19c52ee02..546fe88c7 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -9,7 +9,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { // Unlock the backup domain unsafe { - #[cfg(any(rtc_v3u5, rcc_g0))] + #[cfg(any(rtc_v3u5, rcc_g0, rcc_g4))] use crate::pac::rcc::vals::Rtcsel; #[cfg(not(any(rtc_v3u5, rcc_g0, rcc_g4, rcc_wl5, rcc_wle)))] use crate::pac::rtc::vals::Rtcsel; @@ -31,7 +31,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); let config_rtcsel = rtc_config.clock_config as u8; - #[cfg(not(any(rcc_wl5, rcc_wle, rcc_g4)))] + #[cfg(not(any(rcc_wl5, rcc_wle)))] let config_rtcsel = Rtcsel(config_rtcsel); if !reg.rtcen() || reg.rtcsel() != config_rtcsel { From 673396c0e705189f17defc6e03ac94d8cfe9dbec Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 16:19:46 +0200 Subject: [PATCH 1190/1575] Update metapac version again --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index e5e321a25..0d7e03bf5 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "8" +stm32-metapac = "9" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "8", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "9", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] From cd6256a924c58ffb645b32f26d60168ca08d2d24 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 19 May 2023 15:22:32 +0200 Subject: [PATCH 1191/1575] Add assume_noise_free to usart configuration Effectively setting cr3.onebit --- embassy-stm32/src/usart/mod.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0f3e9412e..cacbced2c 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -120,6 +120,11 @@ pub struct Config { /// read will abort, the error reported and cleared /// if false, the error is ignored and cleared pub detect_previous_overrun: bool, + + /// Set this to true if the line is considered noise free. + /// This will increase the receivers tolerance to clock deviations, + /// but will effectively disable noise detection. + pub assume_noise_free: bool, } impl Default for Config { @@ -131,6 +136,7 @@ impl Default for Config { parity: Parity::ParityNone, // historical behavior detect_previous_overrun: false, + assume_noise_free: false, } } } @@ -832,7 +838,13 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: for &(presc, _presc_val) in &DIVS { let denom = (config.baudrate * presc as u32) as u64; let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; - trace!("USART: presc={} div={:08x}", presc, div); + trace!( + "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", + presc, + div, + div >> 4, + div & 0x0F + ); if div < brr_min { #[cfg(not(usart_v1))] @@ -863,6 +875,14 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: assert!(found, "USART: baudrate too low"); + let brr = unsafe { r.brr().read().brr() as u32 }; + trace!( + "Using {}, desired baudrate: {}, actual baudrate: {}", + if over8 { "OVER8" } else { "OVER16" }, + config.baudrate, + pclk_freq.0 / brr + ); + unsafe { r.cr2().write(|w| { w.set_stop(match config.stop_bits { @@ -895,6 +915,10 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: #[cfg(not(usart_v1))] w.set_over8(vals::Over8(over8 as _)); }); + + r.cr3().modify(|w| { + w.set_onebit(config.assume_noise_free); + }); } } From 387a4fcb8ec1f18b0a40cb8b0d758084814d943b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 19 May 2023 16:20:12 +0200 Subject: [PATCH 1192/1575] Exclude usart_v1 --- embassy-stm32/src/usart/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index cacbced2c..dd20109f1 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -124,6 +124,7 @@ pub struct Config { /// Set this to true if the line is considered noise free. /// This will increase the receivers tolerance to clock deviations, /// but will effectively disable noise detection. + #[cfg(not(usart_v1))] pub assume_noise_free: bool, } @@ -136,6 +137,7 @@ impl Default for Config { parity: Parity::ParityNone, // historical behavior detect_previous_overrun: false, + #[cfg(not(usart_v1))] assume_noise_free: false, } } @@ -917,6 +919,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: }); r.cr3().modify(|w| { + #[cfg(not(usart_v1))] w.set_onebit(config.assume_noise_free); }); } From abbaaeee378f41ddf9f1a3c40777be3572680484 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 25 May 2023 16:39:43 +0100 Subject: [PATCH 1193/1575] stm32/ipcc: support for MAC 802.15.4 --- embassy-stm32/Cargo.toml | 10 ++ embassy-stm32/src/tl_mbox/ble.rs | 23 +++- embassy-stm32/src/tl_mbox/channels.rs | 146 ++++++++++++++-------- embassy-stm32/src/tl_mbox/mac_802_15_4.rs | 82 ++++++++++++ embassy-stm32/src/tl_mbox/mm.rs | 12 +- embassy-stm32/src/tl_mbox/mod.rs | 115 ++++++++++++----- embassy-stm32/src/tl_mbox/shci.rs | 4 +- embassy-stm32/src/tl_mbox/sys.rs | 16 ++- 8 files changed, 306 insertions(+), 102 deletions(-) create mode 100644 embassy-stm32/src/tl_mbox/mac_802_15_4.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 21adb5ddf..c2b0a3ec2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -110,6 +110,16 @@ unstable-pac = [] # Implement embedded-hal-async traits if `nightly` is set as well. unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] +# stm32wb specific +# support for wireless stacks +ble = [] +thread = [] +lld-tests = [] +ble-lld = [] +mac-802_15_4 = [] +zigbee = [] +traces = [] + # Chip-selection features stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index d8bf14d4f..21688ff49 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -16,7 +16,7 @@ use crate::tl_mbox::cmd::CmdPacket; pub struct Ble; impl Ble { - pub(crate) fn new(ipcc: &mut Ipcc) -> Self { + pub(crate) fn init(ipcc: &mut Ipcc) -> Self { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); @@ -28,7 +28,7 @@ impl Ble { }); } - ipcc.c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); + ipcc.c1_set_rx_channel(channels::Cpu2Channel::BleEvent.into(), true); Ble } @@ -48,7 +48,11 @@ impl Ble { } } - ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); + ipcc.c1_clear_flag_channel(channels::Cpu2Channel::BleEvent.into()); + } + + pub(crate) fn acl_data_evt_handler(ipcc: &mut Ipcc) { + ipcc.c1_set_tx_channel(channels::Cpu1Channel::HciAclData.into(), false); } pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { @@ -63,6 +67,17 @@ impl Ble { cmd_packet.cmd_serial.ty = TlPacketType::BleCmd as u8; } - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); + ipcc.c1_set_flag_channel(channels::Cpu1Channel::BleCmd.into()); + } + + pub(crate) fn send_acl_data(ipcc: &mut Ipcc) { + unsafe { + (*(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer) + .acl_data_serial + .ty = TlPacketType::AclData as u8; + } + + ipcc.c1_set_flag_channel(channels::Cpu1Channel::HciAclData.into()); + ipcc.c1_set_tx_channel(channels::Cpu1Channel::HciAclData.into(), true); } } diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs index aaa6ce177..94e97230f 100644 --- a/embassy-stm32/src/tl_mbox/channels.rs +++ b/embassy-stm32/src/tl_mbox/channels.rs @@ -49,56 +49,104 @@ //! | | //! -pub mod cpu1 { - use crate::ipcc::IpccChannel; +use crate::ipcc::IpccChannel; - // Not used currently but reserved - pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; - // Not used currently but reserved - pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; - // Not used currently but reserved - pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6; +pub enum Cpu1Channel { + BleCmd, + SystemCmdRsp, + #[cfg(feature = "thread")] + ThreadOtCmdRsp, + #[cfg(feature = "zigbee")] + ZigbeeCmdAppli, + MmReleaseBuffer, + #[cfg(feature = "mac-802_15_4")] + Mac802_15_4cmdRsp, + #[cfg(feature = "thread")] + ThreadCliCmd, + #[cfg(feature = "lld-tests")] + LldTestsCliCmd, + #[cfg(feature = "ble-lld")] + BleLldCmd, + HciAclData, } -pub mod cpu2 { - use crate::ipcc::IpccChannel; - - pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; - pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_BLE_LLD_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5; +impl From for IpccChannel { + fn from(value: Cpu1Channel) -> Self { + match value { + Cpu1Channel::BleCmd => IpccChannel::Channel1, + Cpu1Channel::SystemCmdRsp => IpccChannel::Channel2, + #[cfg(feature = "thread")] + Cpu1Channel::ThreadOtCmdRsp => IpccChannel::Channel3, + #[cfg(feature = "zigbee")] + Cpu1Channel::ZigbeeCmdAppli => IpccChannel::Channel3, + #[cfg(feature = "mac-802_15_4")] + Cpu1Channel::Mac802_15_4cmdRsp => IpccChannel::Channel3, + Cpu1Channel::MmReleaseBuffer => IpccChannel::Channel4, + #[cfg(feature = "thread")] + Cpu1Channel::ThreadCliCmd => IpccChannel::Channel5, + #[cfg(feature = "lld-tests")] + Cpu1Channel::LldTestsCliCmd => IpccChannel::Channel5, + #[cfg(feature = "ble-lld")] + Cpu1Channel::BleLldCmd => IpccChannel::Channel5, + Cpu1Channel::HciAclData => IpccChannel::Channel6, + } + } +} + +pub enum Cpu2Channel { + BleEvent, + SystemEvent, + #[cfg(feature = "thread")] + ThreadNotifAck, + #[cfg(feature = "zigbee")] + ZigbeeAppliNotifAck, + #[cfg(feature = "mac-802_15_4")] + Mac802_15_4NotifAck, + #[cfg(feature = "lld-tests")] + LldTestsM0Cmd, + #[cfg(feature = "ble-lld")] + BleLldM0Cmd, + #[cfg(feature = "traces")] + Traces, + #[cfg(feature = "thread")] + ThreadCliNotifAck, + #[cfg(feature = "lld-tests")] + LldTestsCliRsp, + #[cfg(feature = "ble-lld")] + BleLldCliRsp, + #[cfg(feature = "ble-lld")] + BleLldRsp, + #[cfg(feature = "zigbee")] + ZigbeeM0Request, +} + +impl From for IpccChannel { + fn from(value: Cpu2Channel) -> Self { + match value { + Cpu2Channel::BleEvent => IpccChannel::Channel1, + Cpu2Channel::SystemEvent => IpccChannel::Channel2, + #[cfg(feature = "thread")] + Cpu2Channel::ThreadNotifAck => IpccChannel::Channel3, + #[cfg(feature = "zigbee")] + Cpu2Channel::ZigbeeAppliNotifAck => IpccChannel::Channel3, + #[cfg(feature = "mac-802_15_4")] + Cpu2Channel::Mac802_15_4NotifAck => IpccChannel::Channel3, + #[cfg(feature = "lld-tests")] + Cpu2Channel::LldTestsM0Cmd => IpccChannel::Channel3, + #[cfg(feature = "ble-lld")] + Cpu2Channel::BleLldM0Cmd => IpccChannel::Channel3, + #[cfg(feature = "traces")] + Cpu2Channel::Traces => IpccChannel::Channel4, + #[cfg(feature = "thread")] + Cpu2Channel::ThreadCliNotifAck => IpccChannel::Channel5, + #[cfg(feature = "lld-tests")] + Cpu2Channel::LldTestsCliRsp => IpccChannel::Channel5, + #[cfg(feature = "ble-lld")] + Cpu2Channel::BleLldCliRsp => IpccChannel::Channel5, + #[cfg(feature = "ble-lld")] + Cpu2Channel::BleLldRsp => IpccChannel::Channel5, + #[cfg(feature = "zigbee")] + Cpu2Channel::ZigbeeM0Request => IpccChannel::Channel5, + } + } } diff --git a/embassy-stm32/src/tl_mbox/mac_802_15_4.rs b/embassy-stm32/src/tl_mbox/mac_802_15_4.rs new file mode 100644 index 000000000..19f951130 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/mac_802_15_4.rs @@ -0,0 +1,82 @@ +use core::mem::MaybeUninit; + +use embassy_futures::block_on; + +use super::cmd::{CmdPacket, CmdSerial}; +use super::consts::TlPacketType; +use super::evt::{EvtBox, EvtPacket}; +use super::unsafe_linked_list::LinkedListNode; +use super::{ + channels, Mac802_15_4Table, EVT_QUEUE, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_CHANNEL, + TL_MAC_802_15_4_TABLE, TL_REF_TABLE, +}; +use crate::ipcc::Ipcc; + +pub struct Mac802_15_4; + +impl Mac802_15_4 { + pub(crate) fn init(ipcc: &mut Ipcc) -> Self { + unsafe { + LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); + + TL_MAC_802_15_4_TABLE = MaybeUninit::new(Mac802_15_4Table { + pcmd_rsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), + pnotack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), + evt_queue: EVT_QUEUE.as_ptr().cast(), + }); + } + + ipcc.c1_set_rx_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into(), true); + + Self + } + + pub(crate) fn notif_evt_handler(ipcc: &mut Ipcc) { + unsafe { + let notif_buffer: *mut EvtPacket = (*TL_REF_TABLE.assume_init().mac_802_15_4_table).pnotack_buffer.cast(); + let event = EvtBox::new(notif_buffer); + + block_on(TL_CHANNEL.send(event)); + } + + ipcc.c1_set_rx_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into(), false); + } + + pub(crate) fn cmd_evt_handler(ipcc: &mut Ipcc) { + unsafe { + let _notif_buffer = (*TL_REF_TABLE.assume_init().mac_802_15_4_table).pcmd_rsp_buffer; + + // NOTE: ST's HAL does nothing with this buffer, ?????? + } + + ipcc.c1_set_tx_channel(channels::Cpu1Channel::Mac802_15_4cmdRsp.into(), false); + } + + pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { + unsafe { + let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().mac_802_15_4_table).pcmd_rsp_buffer.cast(); + let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial; + let pcmd_serial_buf: *mut u8 = pcmd_serial.cast(); + + core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); + + let cmd_packet: &mut CmdPacket = + &mut *(*TL_REF_TABLE.assume_init().mac_802_15_4_table).pcmd_rsp_buffer.cast(); + cmd_packet.cmd_serial.ty = TlPacketType::OtCmd as u8; + } + + ipcc.c1_set_flag_channel(channels::Cpu1Channel::Mac802_15_4cmdRsp.into()); + ipcc.c1_set_tx_channel(channels::Cpu1Channel::Mac802_15_4cmdRsp.into(), true); + } + + pub(crate) fn send_ack(ipcc: &mut Ipcc) { + // TODO + unsafe { + let packet: &mut CmdPacket = &mut *(*TL_REF_TABLE.assume_init().mac_802_15_4_table).pnotack_buffer.cast(); + packet.cmd_serial.ty = TlPacketType::OtAck as u8; + } + + ipcc.c1_clear_flag_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into()); + ipcc.c1_set_rx_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into(), true); + } +} diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs index f99ffa399..5dec397e4 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -11,7 +11,7 @@ use crate::ipcc::Ipcc; pub struct MemoryManager; impl MemoryManager { - pub fn new() -> Self { + pub fn init() -> Self { unsafe { LinkedListNode::init_head(FREE_BUFF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); @@ -31,9 +31,9 @@ impl MemoryManager { } pub fn evt_handler(ipcc: &mut Ipcc) { - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); + ipcc.c1_set_tx_channel(channels::Cpu1Channel::MmReleaseBuffer.into(), false); Self::send_free_buf(); - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + ipcc.c1_set_flag_channel(channels::Cpu1Channel::MmReleaseBuffer.into()); } pub fn evt_drop(evt: *mut EvtPacket, ipcc: &mut Ipcc) { @@ -43,14 +43,14 @@ impl MemoryManager { LinkedListNode::remove_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); } - let channel_is_busy = ipcc.c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + let channel_is_busy = ipcc.c1_is_active_flag(channels::Cpu1Channel::MmReleaseBuffer.into()); // postpone event buffer freeing to IPCC interrupt handler if channel_is_busy { - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); + ipcc.c1_set_tx_channel(channels::Cpu1Channel::MmReleaseBuffer.into(), true); } else { Self::send_free_buf(); - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + ipcc.c1_set_flag_channel(channels::Cpu1Channel::MmReleaseBuffer.into()); } } diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index dc6104cc3..b2d3a27e1 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -4,9 +4,12 @@ use bit_field::BitField; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; +#[cfg(feature = "ble")] use self::ble::Ble; use self::cmd::{AclDataPacket, CmdPacket}; use self::evt::{CsEvt, EvtBox}; +#[cfg(feature = "mac-802_15_4")] +use self::mac_802_15_4::Mac802_15_4; use self::mm::MemoryManager; use self::shci::{shci_ble_init, ShciBleInitCmdParam}; use self::sys::Sys; @@ -14,7 +17,6 @@ use self::unsafe_linked_list::LinkedListNode; use crate::interrupt; use crate::ipcc::Ipcc; -mod ble; mod channels; mod cmd; mod consts; @@ -24,6 +26,11 @@ mod shci; mod sys; mod unsafe_linked_list; +#[cfg(feature = "ble")] +mod ble; +#[cfg(feature = "mac-802_15_4")] +mod mac_802_15_4; + pub type PacketHeader = LinkedListNode; const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); @@ -194,8 +201,8 @@ struct TracesTable { #[repr(C, packed)] struct Mac802_15_4Table { - pcmd_rsp_buffer: *const u8, - pnotack_buffer: *const u8, + pcmd_rsp_buffer: *mut u8, + pnotack_buffer: *mut u8, evt_queue: *const u8, } @@ -215,6 +222,7 @@ pub struct RefTable { ble_lld_table: *const BleLldTable, } +// -------------------- reference table -------------------- #[link_section = "TL_REF_TABLE"] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); @@ -248,38 +256,50 @@ static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::u #[link_section = "MB_MEM1"] static mut TL_ZIGBEE_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[allow(dead_code)] // Not used currently but reserved +// -------------------- tables -------------------- #[link_section = "MB_MEM1"] static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); -// not in shared RAM -static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); - #[link_section = "MB_MEM2"] static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = MaybeUninit::uninit(); -#[link_section = "MB_MEM2"] +#[link_section = "MB_MEM1"] static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "MB_MEM2"] -static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); +// not in shared RAM +static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +// -------------------- app tables -------------------- #[link_section = "MB_MEM2"] static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); +#[link_section = "MB_MEM2"] +static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); + #[link_section = "MB_MEM2"] static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = MaybeUninit::uninit(); +#[cfg(feature = "mac-802_15_4")] +#[link_section = "MB_MEM2"] +static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); + +#[cfg(feature = "mac-802_15_4")] +#[link_section = "MB_MEM2"] +static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + +#[cfg(feature = "ble")] #[link_section = "MB_MEM2"] static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = MaybeUninit::uninit(); -#[link_section = "MB_MEM2"] +#[cfg(feature = "ble")] +#[link_section = "MB_MEM1"] static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] @@ -289,10 +309,14 @@ static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251 // TODO: get a better size, this is a placeholder pub(crate) static TL_CHANNEL: Channel = Channel::new(); -pub struct TlMbox { - _sys: Sys, - _ble: Ble, - _mm: MemoryManager, +pub struct TlMbox; + +pub enum MailboxTarget { + Sys, + #[cfg(feature = "ble")] + Ble, + #[cfg(feature = "mac-802_15_4")] + Mac802_15_4, } impl TlMbox { @@ -338,9 +362,14 @@ impl TlMbox { ipcc.init(); - let _sys = Sys::new(ipcc); - let _ble = Ble::new(ipcc); - let _mm = MemoryManager::new(); + Sys::init(ipcc); + MemoryManager::init(); + + #[cfg(feature = "ble")] + Ble::init(ipcc); + + #[cfg(feature = "mac-802_15_4")] + Mac802_15_4::init(ipcc); // rx_irq.disable(); // tx_irq.disable(); @@ -360,7 +389,7 @@ impl TlMbox { // rx_irq.enable(); // tx_irq.enable(); - TlMbox { _sys, _ble, _mm } + Self } pub fn wireless_fw_info(&self) -> Option { @@ -374,17 +403,30 @@ impl TlMbox { } } + #[cfg(feature = "ble")] pub fn shci_ble_init(&self, ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { shci_ble_init(ipcc, param); } - pub fn send_ble_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { - ble::Ble::send_cmd(ipcc, buf); + pub fn send_cmd(&self, ipcc: &mut Ipcc, buf: &[u8], target: MailboxTarget) { + match target { + MailboxTarget::Sys => Sys::send_cmd(ipcc, buf), + #[cfg(feature = "ble")] + MailboxTarget::Ble => Ble::send_cmd(ipcc, buf), + #[cfg(feature = "mac-802_15_4")] + MailboxTarget::Mac802_15_4 => Mac802_15_4::send_cmd(ipcc, buf), + } } - // pub fn send_sys_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { - // sys::Sys::send_cmd(ipcc, buf); - // } + pub fn send_ack(&self, ipcc: &mut Ipcc, target: MailboxTarget) { + match target { + #[cfg(feature = "ble")] + MailboxTarget::Ble => Ble::send_acl_data(ipcc), + #[cfg(feature = "mac-802_15_4")] + MailboxTarget::Mac802_15_4 => Mac802_15_4::send_ack(ipcc), + MailboxTarget::Sys => { /* does nothing */ } + } + } pub async fn read(&self) -> EvtBox { TL_CHANNEL.recv().await @@ -392,10 +434,14 @@ impl TlMbox { #[allow(dead_code)] fn interrupt_ipcc_rx_handler(ipcc: &mut Ipcc) { - if ipcc.is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { - sys::Sys::evt_handler(ipcc); - } else if ipcc.is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { - ble::Ble::evt_handler(ipcc); + if ipcc.is_rx_pending(channels::Cpu2Channel::SystemEvent.into()) { + Sys::evt_handler(ipcc); + } else if cfg!(feature = "ble") && ipcc.is_rx_pending(channels::Cpu2Channel::BleEvent.into()) { + Ble::evt_handler(ipcc); + } else if cfg!(feature = "mac-802_15_4") + && ipcc.is_rx_pending(channels::Cpu2Channel::Mac802_15_4NotifAck.into()) + { + Mac802_15_4::notif_evt_handler(ipcc); } else { todo!() } @@ -403,11 +449,16 @@ impl TlMbox { #[allow(dead_code)] fn interrupt_ipcc_tx_handler(ipcc: &mut Ipcc) { - if ipcc.is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { + if ipcc.is_tx_pending(channels::Cpu1Channel::SystemCmdRsp.into()) { // TODO: handle this case - let _ = sys::Sys::cmd_evt_handler(ipcc); - } else if ipcc.is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) { - mm::MemoryManager::evt_handler(ipcc); + let _ = Sys::cmd_evt_handler(ipcc); + } else if ipcc.is_tx_pending(channels::Cpu1Channel::MmReleaseBuffer.into()) { + MemoryManager::evt_handler(ipcc); + } else if cfg!(feature = "ble") && ipcc.is_tx_pending(channels::Cpu1Channel::HciAclData.into()) { + Ble::acl_data_evt_handler(ipcc); + } else if cfg!(feature = "mac-802_15_4") && ipcc.is_tx_pending(channels::Cpu1Channel::Mac802_15_4cmdRsp.into()) + { + Mac802_15_4::cmd_evt_handler(ipcc) } else { todo!() } diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32/src/tl_mbox/shci.rs index 61fd9e4a3..53f0882b7 100644 --- a/embassy-stm32/src/tl_mbox/shci.rs +++ b/embassy-stm32/src/tl_mbox/shci.rs @@ -95,7 +95,7 @@ pub fn shci_ble_init(ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { cmd_buf.cmd_serial.ty = TlPacketType::SysCmd as u8; - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + ipcc.c1_set_flag_channel(channels::Cpu1Channel::SystemCmdRsp.into()); + ipcc.c1_set_tx_channel(channels::Cpu1Channel::SystemCmdRsp.into(), true); } } diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index 31ebde721..1dc43bfee 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -12,7 +12,7 @@ use crate::ipcc::Ipcc; pub struct Sys; impl Sys { - pub(crate) fn new(ipcc: &mut Ipcc) -> Self { + pub(crate) fn init(ipcc: &mut Ipcc) -> Self { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); @@ -22,7 +22,7 @@ impl Sys { }); } - ipcc.c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); + ipcc.c1_set_rx_channel(channels::Cpu2Channel::SystemEvent.into(), true); Sys } @@ -43,11 +43,11 @@ impl Sys { } } - ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); + ipcc.c1_clear_flag_channel(channels::Cpu2Channel::SystemEvent.into()); } pub(crate) fn cmd_evt_handler(ipcc: &mut Ipcc) -> CcEvt { - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); + ipcc.c1_set_tx_channel(channels::Cpu1Channel::SystemCmdRsp.into(), false); // ST's command response data structure is really convoluted. // @@ -67,12 +67,10 @@ impl Sys { } } - #[allow(dead_code)] pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { unsafe { - // TODO: check this let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; - let cmd_serial: *mut CmdSerial = &mut (*cmd_buffer).cmd_serial; + let cmd_serial: *mut CmdSerial = &mut cmd_buffer.cmd_serial; let cmd_serial_buf = cmd_serial.cast(); core::ptr::copy(buf.as_ptr(), cmd_serial_buf, buf.len()); @@ -80,8 +78,8 @@ impl Sys { let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; cmd_packet.cmd_serial.ty = TlPacketType::SysCmd as u8; - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + ipcc.c1_set_flag_channel(channels::Cpu1Channel::SystemCmdRsp.into()); + ipcc.c1_set_tx_channel(channels::Cpu1Channel::SystemCmdRsp.into(), true); } } } From c5bf36eebf663419200fcd61f3f4f5884f65008d Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 17:56:52 +0200 Subject: [PATCH 1194/1575] Fix oversampling message for usart v1 --- embassy-stm32/src/usart/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dd20109f1..4488bd77b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -878,9 +878,13 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: assert!(found, "USART: baudrate too low"); let brr = unsafe { r.brr().read().brr() as u32 }; + #[cfg(not(usart_v1))] + let oversampling = if over8 { "8 bit" } else { "16 bit" }; + #[cfg(usart_v1)] + let oversampling = "default"; trace!( - "Using {}, desired baudrate: {}, actual baudrate: {}", - if over8 { "OVER8" } else { "OVER16" }, + "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", + oversampling, config.baudrate, pclk_freq.0 / brr ); From f616b2215912dca6fd76523d390d0602dfa27bcd Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 18:16:46 +0200 Subject: [PATCH 1195/1575] Fix yet another v1 error --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 4488bd77b..565416c72 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -922,8 +922,8 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: w.set_over8(vals::Over8(over8 as _)); }); + #[cfg(not(usart_v1))] r.cr3().modify(|w| { - #[cfg(not(usart_v1))] w.set_onebit(config.assume_noise_free); }); } From 0e90e98e9b477302a3cd2550dbd438907ed10ca6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:17:12 +0200 Subject: [PATCH 1196/1575] stm32: Add async flash write/erase to f4 --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/flash/asynch.rs | 129 +++++++++++++++ embassy-stm32/src/flash/common.rs | 267 ++++++++++++++++-------------- embassy-stm32/src/flash/f0.rs | 18 +- embassy-stm32/src/flash/f3.rs | 18 +- embassy-stm32/src/flash/f4.rs | 183 +++++++++++++++++--- embassy-stm32/src/flash/f7.rs | 21 +-- embassy-stm32/src/flash/h7.rs | 21 +-- embassy-stm32/src/flash/l.rs | 19 ++- embassy-stm32/src/flash/mod.rs | 49 ++++++ embassy-stm32/src/flash/other.rs | 12 +- 11 files changed, 547 insertions(+), 194 deletions(-) create mode 100644 embassy-stm32/src/flash/asynch.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0d7e03bf5..73841bdc8 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -47,6 +47,7 @@ embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} embedded-storage = "0.3.0" +embedded-storage-async = { version = "0.4.0", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } @@ -68,6 +69,7 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" +paste = "1.0.12" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -98,7 +100,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs new file mode 100644 index 000000000..44e23d9c4 --- /dev/null +++ b/embassy-stm32/src/flash/asynch.rs @@ -0,0 +1,129 @@ +use atomic_polyfill::{fence, Ordering}; +use embassy_hal_common::drop::OnDrop; + +use super::{ + ensure_sector_aligned, family, get_sector, Error, Flash, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + REGION_ACCESS, WRITE_SIZE, +}; + +impl<'d> Flash<'d> { + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } + } + + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_sectored(FLASH_BASE as u32, from, to).await } + } +} + +impl embedded_storage_async::nor_flash::NorFlash for Flash<'_> { + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = MAX_ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await + } +} + +pub(super) async unsafe fn write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { + if offset + bytes.len() as u32 > size { + return Err(Error::Size); + } + if offset % WRITE_SIZE as u32 != 0 || bytes.len() % WRITE_SIZE != 0 { + return Err(Error::Unaligned); + } + + let mut address = base + offset; + trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); + + for chunk in bytes.chunks(WRITE_SIZE) { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_write(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { + family::disable_write(); + fence(Ordering::SeqCst); + family::lock(); + }); + + family::write(address, chunk.try_into().unwrap()).await?; + address += WRITE_SIZE as u32; + } + Ok(()) +} + +pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { + let start_address = base + from; + let end_address = base + to; + let regions = family::get_flash_regions(); + + ensure_sector_aligned(start_address, end_address, regions)?; + + trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); + + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + trace!("Erasing sector: {:?}", sector); + + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| family::lock()); + + family::erase_sector(§or).await?; + address += sector.size; + } + Ok(()) +} + +foreach_flash_region! { + ($type_name:ident, $write_size:literal, $erase_size:literal) => { + impl crate::_generated::flash_regions::$type_name<'_> { + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { write_chunked(self.0.base, self.0.size, offset, bytes).await } + } + + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { erase_sectored(self.0.base, from, to).await } + } + } + + impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + + impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { + const WRITE_SIZE: usize = $write_size; + const ERASE_SIZE: usize = $erase_size; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await + } + } + }; +} diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 432c8a43b..990104a38 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,44 +1,55 @@ use atomic_polyfill::{fence, Ordering}; +use embassy_cortex_m::interrupt::InterruptExt; +use embassy_futures::block_on; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::Mutex; +use stm32_metapac::FLASH_BASE; -use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; -use crate::flash::FlashBank; +use super::{ + ensure_sector_aligned, family, get_sector, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, +}; +use crate::peripherals::FLASH; use crate::Peripheral; pub struct Flash<'d> { - inner: PeripheralRef<'d, crate::peripherals::FLASH>, + pub(crate) inner: PeripheralRef<'d, FLASH>, } +pub(crate) static REGION_ACCESS: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd) -> Self { - into_ref!(p); + pub fn new(p: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { + into_ref!(p, irq); + + irq.set_handler(family::on_interrupt); + irq.unpend(); + irq.enable(); + Self { inner: p } } pub fn into_regions(self) -> FlashLayout<'d> { family::set_default_layout(); - FlashLayout::new(self.release()) + FlashLayout::new(self.inner) } - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } + pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_chunked_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(FLASH_BASE as u32, from, to) } - } - - pub(crate) fn release(self) -> PeripheralRef<'d, crate::peripherals::FLASH> { - unsafe { self.inner.clone_unchecked() } + pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_sectored_blocking(FLASH_BASE as u32, from, to) } } } -pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { +pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -49,7 +60,7 @@ pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) Ok(()) } -pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { +pub(super) unsafe fn write_chunked_blocking(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -61,44 +72,31 @@ pub(super) unsafe fn blocking_write_chunked(base: u32, size: u32, offset: u32, b trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); for chunk in bytes.chunks(WRITE_SIZE) { - critical_section::with(|_| { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); - family::begin_write(); - fence(Ordering::SeqCst); + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_blocking_write(); + fence(Ordering::SeqCst); - let _on_drop = OnDrop::new(|| { - family::end_write(); - fence(Ordering::SeqCst); - family::lock(); - }); + let _on_drop = OnDrop::new(|| { + family::disable_blocking_write(); + fence(Ordering::SeqCst); + family::lock(); + }); - family::blocking_write(address, chunk.try_into().unwrap()) - })?; + family::write_blocking(address, chunk.try_into().unwrap())?; address += WRITE_SIZE as u32; } Ok(()) } -pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { +pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; let regions = family::get_flash_regions(); - // Test if the address range is aligned at sector base addresses - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } + ensure_sector_aligned(start_address, end_address, regions)?; trace!("Erasing from 0x{:x} to 0x{:x}", start_address, end_address); @@ -107,71 +105,28 @@ pub(super) unsafe fn blocking_erase_sectored(base: u32, from: u32, to: u32) -> R let sector = get_sector(address, regions); trace!("Erasing sector: {:?}", sector); - critical_section::with(|_| { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); - let _on_drop = OnDrop::new(|| { - family::lock(); - }); + let _on_drop = OnDrop::new(|| family::lock()); - family::blocking_erase_sector(§or) - })?; + family::erase_sector_blocking(§or)?; address += sector.size; } Ok(()) } -pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { - let mut current_bank = FlashBank::Bank1; - let mut bank_offset = 0; - for region in regions { - if region.bank != current_bank { - current_bank = region.bank; - bank_offset = 0; - } - - if address < region.end() { - let index_in_region = (address - region.base) / region.erase_size; - return FlashSector { - bank: region.bank, - index_in_bank: bank_offset + index_in_region as u8, - start: region.base + index_in_region * region.erase_size, - size: region.erase_size, - }; - } - - bank_offset += region.sectors(); - } - - panic!("Flash sector not found"); -} - -impl FlashRegion { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.base, self.size, offset, bytes) - } - - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.base, self.size, offset, bytes) } - } - - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.base, from, to) } - } -} - impl embedded_storage::nor_flash::ErrorType for Flash<'_> { type Error = Error; } impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -184,27 +139,112 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_> { const ERASE_SIZE: usize = MAX_ERASE_SIZE; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) + self.write_blocking(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) + self.erase_blocking(from, to) + } +} + +#[cfg(feature = "nightly")] +impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +pub struct BlockingFlashRegion<'d, const WRITE_SIZE: u32, const ERASE_SIZE: u32>( + &'static FlashRegion, + PeripheralRef<'d, FLASH>, +); + +impl BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> { + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) + } + + pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = block_on(REGION_ACCESS.lock()); + unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = block_on(REGION_ACCESS.lock()); + unsafe { erase_sectored_blocking(self.0.base, from, to) } + } +} + +impl embedded_storage::nor_flash::ErrorType + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + type Error = Error; +} + +impl embedded_storage::nor_flash::ReadNorFlash + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } +} + +impl embedded_storage::nor_flash::NorFlash + for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> +{ + const WRITE_SIZE: usize = WRITE_SIZE as usize; + const ERASE_SIZE: usize = ERASE_SIZE as usize; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) } } foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl crate::_generated::flash_regions::$type_name<'_> { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.0.base, self.0.size, offset, bytes) + paste::paste! { + pub type []<'d> = BlockingFlashRegion<'d, $write_size, $erase_size>; + } + + impl<'d> crate::_generated::flash_regions::$type_name<'d> { + /// Make this flash region work in a blocking context. + /// + /// SAFETY + /// + /// This function is unsafe as incorect usage of parallel blocking operations + /// on multiple regions may cause a deadlock because each region requires mutual access to the flash. + pub unsafe fn into_blocking(self) -> BlockingFlashRegion<'d, $write_size, $erase_size> { + BlockingFlashRegion(self.0, self.1) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.0.base, from, to) } + pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { erase_sectored_blocking(self.0.base, from, to) } } } @@ -213,28 +253,15 @@ foreach_flash_region! { } impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { self.0.size as usize } } - - impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { - const WRITE_SIZE: usize = $write_size; - const ERASE_SIZE: usize = $erase_size; - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) - } - } }; } diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index c6441ef16..ecf3a6981 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -13,6 +13,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -22,17 +26,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 2); pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); @@ -42,10 +46,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -56,7 +60,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = wait_ready_blocking(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -88,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 4c8172203..fd778f2b1 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -13,6 +13,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -22,17 +26,17 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_fkeyr(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 2); pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); @@ -42,10 +46,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -56,7 +60,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = blocking_wait_ready(); + let mut ret: Result<(), Error> = wait_ready_blocking(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -88,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 7f1c5f671..3c8f81eb0 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,6 +2,8 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +use embassy_sync::waitqueue::AtomicWaker; + use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -13,8 +15,8 @@ mod alt_regions { use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; use crate::flash::{ - blocking_erase_sectored, blocking_read, blocking_write_chunked, Bank1Region1, Bank1Region2, Error, Flash, - FlashBank, FlashRegion, + asynch, common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, + READ_SIZE, REGION_ACCESS, }; use crate::peripherals::FLASH; @@ -53,6 +55,15 @@ mod alt_regions { pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub type BlockingAltBank1Region3<'d> = + BlockingFlashRegion<'d, { ALT_BANK1_REGION3.write_size }, { ALT_BANK1_REGION3.erase_size }>; + pub type BlockingAltBank2Region1<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION1.write_size }, { ALT_BANK2_REGION1.erase_size }>; + pub type BlockingAltBank2Region2<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION2.write_size }, { ALT_BANK2_REGION2.erase_size }>; + pub type BlockingAltBank2Region3<'d> = + BlockingFlashRegion<'d, { ALT_BANK2_REGION3.write_size }, { ALT_BANK2_REGION3.erase_size }>; + pub struct AltFlashLayout<'d> { pub bank1_region1: Bank1Region1<'d>, pub bank1_region2: Bank1Region2<'d>, @@ -69,7 +80,7 @@ mod alt_regions { // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. - let p = self.release(); + let p = self.inner; AltFlashLayout { bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), @@ -85,16 +96,30 @@ mod alt_regions { macro_rules! foreach_altflash_region { ($type_name:ident, $region:ident) => { impl $type_name<'_> { - pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - blocking_read(self.0.base, self.0.size, offset, bytes) + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + common::read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { blocking_write_chunked(self.0.base, self.0.size, offset, bytes) } + #[cfg(all(feature = "nightly"))] + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } } - pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { blocking_erase_sectored(self.0.base, from, to) } + #[cfg(all(feature = "nightly"))] + pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.lock().await; + unsafe { asynch::erase_sectored(self.0.base, from, to).await } + } + + pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { common::write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + } + + pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; + unsafe { common::erase_sectored_blocking(self.0.base, from, to) } } } @@ -103,10 +128,10 @@ mod alt_regions { } impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_> { - const READ_SIZE: usize = 1; + const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -114,16 +139,28 @@ mod alt_regions { } } - impl embedded_storage::nor_flash::NorFlash for $type_name<'_> { + impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + + impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(offset, bytes) + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes).await } - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.blocking_erase(from, to) + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to).await } } }; @@ -138,6 +175,9 @@ mod alt_regions { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub use alt_regions::*; +#[cfg(feature = "nightly")] +static WAKER: AtomicWaker = AtomicWaker::new(); + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn set_default_layout() { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) }; @@ -160,6 +200,16 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + // Clear IRQ flags + pac::FLASH.sr().write(|w| { + w.set_operr(true); + w.set_eop(true); + }); + + WAKER.wake(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -169,7 +219,28 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +#[cfg(feature = "nightly")] +pub(crate) unsafe fn enable_write() { + assert_eq!(0, WRITE_SIZE % 4); + + pac::FLASH.cr().write(|w| { + w.set_pg(true); + w.set_psize(pac::flash::vals::Psize::PSIZE32); + w.set_eopie(true); + w.set_errie(true); + }); +} + +#[cfg(feature = "nightly")] +pub(crate) unsafe fn disable_write() { + pac::FLASH.cr().write(|w| { + w.set_pg(false); + w.set_eopie(false); + w.set_errie(false); + }); +} + +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); pac::FLASH.cr().write(|w| { @@ -178,11 +249,22 @@ pub(crate) unsafe fn begin_write() { }); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +#[cfg(feature = "nightly")] +pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + write_start(start_address, buf); + wait_ready().await +} + +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + write_start(start_address, buf); + wait_ready_blocking() +} + +unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -191,11 +273,32 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) // prevents parallelism errors fence(Ordering::SeqCst); } - - blocking_wait_ready() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { + let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; + + pac::FLASH.cr().modify(|w| { + w.set_ser(true); + w.set_snb(snb); + w.set_eopie(true); + w.set_errie(true); + }); + + pac::FLASH.cr().modify(|w| { + w.set_strt(true); + }); + + let ret: Result<(), Error> = wait_ready().await; + pac::FLASH.cr().modify(|w| { + w.set_eopie(false); + w.set_errie(false); + }); + clear_all_err(); + ret +} + +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; pac::FLASH.cr().modify(|w| { @@ -207,10 +310,8 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = blocking_wait_ready(); - + let ret: Result<(), Error> = wait_ready_blocking(); clear_all_err(); - ret } @@ -220,11 +321,39 @@ pub(crate) unsafe fn clear_all_err() { w.set_pgperr(true); w.set_pgaerr(true); w.set_wrperr(true); - w.set_eop(true); }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +#[cfg(feature = "nightly")] +pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { + use core::task::Poll; + + use futures::future::poll_fn; + + poll_fn(|cx| { + WAKER.register(cx.waker()); + + let sr = pac::FLASH.sr().read(); + if !sr.bsy() { + Poll::Ready(if sr.pgserr() { + Err(Error::Seq) + } else if sr.pgperr() { + Err(Error::Parallelism) + } else if sr.pgaerr() { + Err(Error::Unaligned) + } else if sr.wrperr() { + Err(Error::Protected) + } else { + Ok(()) + }) + } else { + return Poll::Pending; + } + }) + .await +} + +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index ac2834a84..a0593b14b 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -12,6 +12,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } @@ -21,7 +25,7 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); pac::FLASH.cr().write(|w| { @@ -30,11 +34,11 @@ pub(crate) unsafe fn begin_write() { }); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -44,10 +48,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.index_in_bank) @@ -57,12 +61,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = blocking_wait_ready(); - + let ret: Result<(), Error> = wait_ready_blocking(); pac::FLASH.cr().modify(|w| w.set_ser(false)); - clear_all_err(); - ret } @@ -86,7 +87,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 1b1631068..865f13283 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -17,6 +17,10 @@ pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); if is_dual_bank() { @@ -33,13 +37,13 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); } -pub(crate) unsafe fn end_write() {} +pub(crate) unsafe fn disable_blocking_write() {} -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { // We cannot have the write setup sequence in begin_write as it depends on the address let bank = if start_address < BANK1_REGION.end() { pac::FLASH.bank(0) @@ -60,7 +64,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); address += val.len() as u32; - res = Some(blocking_wait_ready(bank)); + res = Some(wait_ready_blocking(bank)); bank.sr().modify(|w| { if w.eop() { w.set_eop(true); @@ -80,7 +84,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); @@ -91,12 +95,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E w.set_start(true); }); - let ret: Result<(), Error> = blocking_wait_ready(bank); - + let ret: Result<(), Error> = wait_ready_blocking(bank); bank.cr().modify(|w| w.set_ser(false)); - bank_clear_all_err(bank); - ret } @@ -141,7 +142,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { }); } -unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { +unsafe fn wait_ready_blocking(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index c94f61900..f8a0dac4c 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -12,6 +12,10 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); @@ -41,19 +45,19 @@ pub(crate) unsafe fn unlock() { } } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -63,10 +67,10 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - blocking_wait_ready() + wait_ready_blocking() } -pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -96,7 +100,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E }); } - let ret: Result<(), Error> = blocking_wait_ready(); + let ret: Result<(), Error> = wait_ready_blocking(); #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_per(false)); @@ -108,7 +112,6 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E }); clear_all_err(); - ret } @@ -150,7 +153,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn blocking_wait_ready() -> Result<(), Error> { +unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index b93270ae1..e781f1b86 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,5 +1,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; +#[cfg(all(feature = "nightly", flash_f4))] +pub mod asynch; #[cfg(flash)] mod common; @@ -10,6 +12,8 @@ pub use crate::_generated::flash_regions::*; pub use crate::_generated::MAX_ERASE_SIZE; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +pub const READ_SIZE: usize = 1; + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion { @@ -76,6 +80,7 @@ pub enum Error { Protected, Unaligned, Parallelism, + TryLockError, } impl NorFlashError for Error { @@ -87,3 +92,47 @@ impl NorFlashError for Error { } } } + +pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { + let mut current_bank = FlashBank::Bank1; + let mut bank_offset = 0; + for region in regions { + if region.bank != current_bank { + current_bank = region.bank; + bank_offset = 0; + } + + if address < region.end() { + let index_in_region = (address - region.base) / region.erase_size; + return FlashSector { + bank: region.bank, + index_in_bank: bank_offset + index_in_region as u8, + start: region.base + index_in_region * region.erase_size, + size: region.erase_size, + }; + } + + bank_offset += region.sectors(); + } + + panic!("Flash sector not found"); +} + +pub(crate) fn ensure_sector_aligned( + start_address: u32, + end_address: u32, + regions: &[&FlashRegion], +) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + Ok(()) +} diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index 556034654..e21b0b241 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -8,22 +8,26 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +pub(crate) unsafe fn on_interrupt(_: *mut ()) { + unimplemented!(); +} + pub(crate) unsafe fn lock() { unimplemented!(); } pub(crate) unsafe fn unlock() { unimplemented!(); } -pub(crate) unsafe fn begin_write() { +pub(crate) unsafe fn enable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn end_write() { +pub(crate) unsafe fn disable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn write_blocking(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { unimplemented!(); } -pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn erase_sector_blocking(_sector: &FlashSector) -> Result<(), Error> { unimplemented!(); } pub(crate) unsafe fn clear_all_err() { From 7477785bbb469a1d8d3cf21a74fd61cb12f59640 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:41:52 +0200 Subject: [PATCH 1197/1575] Align examples --- examples/stm32f3/src/bin/flash.rs | 5 +- examples/stm32f4/src/bin/flash.rs | 14 ++--- examples/stm32f4/src/bin/flash_async.rs | 81 +++++++++++++++++++++++++ examples/stm32f7/src/bin/flash.rs | 5 +- examples/stm32h7/src/bin/flash.rs | 5 +- examples/stm32l0/src/bin/flash.rs | 5 +- examples/stm32l1/src/bin/flash.rs | 5 +- examples/stm32wl/src/bin/flash.rs | 5 +- 8 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 examples/stm32f4/src/bin/flash_async.rs diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index e40ad4fc0..0e5fb0658 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index bd3a7c95e..de4ecdb8f 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { // Once can also call `into_regions()` to get access to NorFlash implementations // for each of the unique characteristics. - let mut f = Flash::new(p.FLASH); + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); // Sector 5 test_flash(&mut f, 128 * 1024, 128 * 1024); @@ -31,19 +31,19 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(offset, &mut buf)); + unwrap!(f.read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.blocking_erase(offset, offset + size)); + unwrap!(f.erase_blocking(offset, offset + size)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(offset, &mut buf)); + unwrap!(f.read(offset, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.blocking_write( + unwrap!(f.write_blocking( offset, &[ 1, 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, @@ -53,7 +53,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(offset, &mut buf)); + unwrap!(f.read(offset, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs new file mode 100644 index 000000000..c9d9df34b --- /dev/null +++ b/examples/stm32f4/src/bin/flash_async.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_time::{Timer, Duration}; +use embassy_stm32::flash::Flash; +use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; +use embassy_stm32::{interrupt}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello Flash!"); + + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); + + // Led should blink uninterrupted during ~2sec erase operation + spawner.spawn(blinky(p.PB7.degrade())).unwrap(); + + // Test on bank 2 in order not to stall CPU. + test_flash(&mut f, 1024 * 1024, 128 * 1024).await; +} + +#[embassy_executor::task] +async fn blinky(p: AnyPin) { + let mut led = Output::new(p, 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; + } +} + +async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) { + info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(offset, offset + size).await); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(offset, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!( + f.write( + offset, + &[ + 1, 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 + ] + ) + .await + ); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + assert_eq!( + &buf[..], + &[ + 1, 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 + ] + ); +} \ No newline at end of file diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index aabfe8557..717c82e86 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -4,9 +4,8 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; +use embassy_stm32::{flash::Flash, interrupt}; use embassy_time::{Duration, Timer}; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -19,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region3; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region3.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index 7ee9838c9..aab72cae8 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -4,9 +4,8 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; +use embassy_stm32::{flash::Flash, interrupt}; use embassy_time::{Duration, Timer}; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -19,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH).into_regions().bank2_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank2_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 337425028..0ed0d05fc 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 38feb0d76..c4d7d029c 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index e6bc2865c..df51ceb68 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -4,8 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; +use embassy_stm32::{flash::Flash, interrupt}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new(p.FLASH).into_regions().bank1_region; + let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; info!("Reading..."); let mut buf = [0u8; 8]; From cdb14475696484d08d717194074c4d85cff5eb63 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:42:35 +0200 Subject: [PATCH 1198/1575] Add missing nightly guards --- embassy-stm32/src/flash/f4.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 3c8f81eb0..9698bcd58 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -139,6 +139,7 @@ mod alt_regions { } } + #[cfg(all(feature = "nightly"))] impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { const READ_SIZE: usize = READ_SIZE; @@ -151,6 +152,7 @@ mod alt_regions { } } + #[cfg(all(feature = "nightly"))] impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; From 34a2804b544206920b6e960e3443a2b2f6e41f2b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:45:59 +0200 Subject: [PATCH 1199/1575] Fix unused get_sector and ensure_sector_aligned --- embassy-stm32/src/flash/common.rs | 49 +++++++++++++++++++++++++++++-- embassy-stm32/src/flash/mod.rs | 44 --------------------------- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 990104a38..7cec5a3c9 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -8,8 +8,8 @@ use embassy_sync::mutex::Mutex; use stm32_metapac::FLASH_BASE; use super::{ - ensure_sector_aligned, family, get_sector, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, - WRITE_SIZE, + family, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, FlashSector, FlashBank, }; use crate::peripherals::FLASH; use crate::Peripheral; @@ -118,6 +118,51 @@ pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> R Ok(()) } +pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { + let mut current_bank = FlashBank::Bank1; + let mut bank_offset = 0; + for region in regions { + if region.bank != current_bank { + current_bank = region.bank; + bank_offset = 0; + } + + if address < region.end() { + let index_in_region = (address - region.base) / region.erase_size; + return FlashSector { + bank: region.bank, + index_in_bank: bank_offset + index_in_region as u8, + start: region.base + index_in_region * region.erase_size, + size: region.erase_size, + }; + } + + bank_offset += region.sectors(); + } + + panic!("Flash sector not found"); +} + +pub(crate) fn ensure_sector_aligned( + start_address: u32, + end_address: u32, + regions: &[&FlashRegion], +) -> Result<(), Error> { + let mut address = start_address; + while address < end_address { + let sector = get_sector(address, regions); + if sector.start != address { + return Err(Error::Unaligned); + } + address += sector.size; + } + if address != end_address { + return Err(Error::Unaligned); + } + Ok(()) +} + + impl embedded_storage::nor_flash::ErrorType for Flash<'_> { type Error = Error; } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index e781f1b86..1ef04e56f 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -92,47 +92,3 @@ impl NorFlashError for Error { } } } - -pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { - let mut current_bank = FlashBank::Bank1; - let mut bank_offset = 0; - for region in regions { - if region.bank != current_bank { - current_bank = region.bank; - bank_offset = 0; - } - - if address < region.end() { - let index_in_region = (address - region.base) / region.erase_size; - return FlashSector { - bank: region.bank, - index_in_bank: bank_offset + index_in_region as u8, - start: region.base + index_in_region * region.erase_size, - size: region.erase_size, - }; - } - - bank_offset += region.sectors(); - } - - panic!("Flash sector not found"); -} - -pub(crate) fn ensure_sector_aligned( - start_address: u32, - end_address: u32, - regions: &[&FlashRegion], -) -> Result<(), Error> { - let mut address = start_address; - while address < end_address { - let sector = get_sector(address, regions); - if sector.start != address { - return Err(Error::Unaligned); - } - address += sector.size; - } - if address != end_address { - return Err(Error::Unaligned); - } - Ok(()) -} From c6ffece410a12779ba1ae2289782ca5a3d00dbaf Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 12:55:17 +0200 Subject: [PATCH 1200/1575] Add more missing nightly guards --- embassy-stm32/src/flash/f4.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 9698bcd58..0c008fd0d 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,12 +2,13 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use embassy_sync::waitqueue::AtomicWaker; - use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +#[cfg(feature = "nightly")] +use embassy_sync::waitqueue::AtomicWaker; + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { use embassy_hal_common::PeripheralRef; @@ -15,10 +16,12 @@ mod alt_regions { use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; use crate::flash::{ - asynch, common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, + common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, READ_SIZE, REGION_ACCESS, }; use crate::peripherals::FLASH; + #[cfg(feature = "nightly")] + use crate::flash::asynch; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { size: 3 * BANK1_REGION3.erase_size, @@ -100,13 +103,13 @@ mod alt_regions { common::read_blocking(self.0.base, self.0.size, offset, bytes) } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { let _guard = REGION_ACCESS.lock().await; unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { let _guard = REGION_ACCESS.lock().await; unsafe { asynch::erase_sectored(self.0.base, from, to).await } @@ -139,7 +142,7 @@ mod alt_regions { } } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { const READ_SIZE: usize = READ_SIZE; @@ -152,7 +155,7 @@ mod alt_regions { } } - #[cfg(all(feature = "nightly"))] + #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; @@ -209,6 +212,7 @@ pub(crate) unsafe fn on_interrupt(_: *mut ()) { w.set_eop(true); }); + #[cfg(feature = "nightly")] WAKER.wake(); } @@ -277,6 +281,7 @@ unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { } } +#[cfg(feature = "nightly")] pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; From 6df62397048778ce521c14b21b4aced7c22567c2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 13:11:03 +0200 Subject: [PATCH 1201/1575] Run format with nightly --- embassy-stm32/src/flash/common.rs | 4 +--- embassy-stm32/src/flash/f4.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 7cec5a3c9..e1fe7e9da 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -8,8 +8,7 @@ use embassy_sync::mutex::Mutex; use stm32_metapac::FLASH_BASE; use super::{ - family, Error, FlashLayout, FlashRegion, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, - WRITE_SIZE, FlashSector, FlashBank, + family, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, }; use crate::peripherals::FLASH; use crate::Peripheral; @@ -162,7 +161,6 @@ pub(crate) fn ensure_sector_aligned( Ok(()) } - impl embedded_storage::nor_flash::ErrorType for Flash<'_> { type Error = Error; } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 0c008fd0d..084bbdc6e 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,26 +2,26 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +#[cfg(feature = "nightly")] +use embassy_sync::waitqueue::AtomicWaker; + use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -#[cfg(feature = "nightly")] -use embassy_sync::waitqueue::AtomicWaker; - #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { use embassy_hal_common::PeripheralRef; use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; - use crate::flash::{ - common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, - READ_SIZE, REGION_ACCESS, - }; - use crate::peripherals::FLASH; #[cfg(feature = "nightly")] use crate::flash::asynch; + use crate::flash::{ + common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, READ_SIZE, + REGION_ACCESS, + }; + use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { size: 3 * BANK1_REGION3.erase_size, From 44b6494ab7ec3e742aa9f82a8ca9ebdfc23ebbba Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 17:24:28 +0200 Subject: [PATCH 1202/1575] Let FlashLayout and FlashRegion depends on a Blocking/Async mode generic --- embassy-stm32/Cargo.toml | 1 - embassy-stm32/build.rs | 16 ++- embassy-stm32/src/flash/asynch.rs | 35 ++++- embassy-stm32/src/flash/common.rs | 218 ++++++++++++------------------ embassy-stm32/src/flash/f4.rs | 112 +++++++-------- embassy-stm32/src/flash/mod.rs | 3 + 6 files changed, 179 insertions(+), 206 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 73841bdc8..4e29bb32f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -69,7 +69,6 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" -paste = "1.0.12" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 540981727..29af3c80d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -213,7 +213,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { #[cfg(flash)] - pub struct #region_type<'d>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>,); + pub struct #region_type<'d, MODE>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); }); } @@ -224,11 +224,11 @@ fn main() { let field_name = format_ident!("{}", region_name.to_lowercase()); let field_type = format_ident!("{}", get_flash_region_type_name(f.name)); let field = quote! { - pub #field_name: #field_type<'d> + pub #field_name: #field_type<'d, MODE> }; let region_name = format_ident!("{}", region_name); let init = quote! { - #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}) + #field_name: #field_type(&#region_name, unsafe { p.clone_unchecked()}, core::marker::PhantomData) }; (field, (init, region_name)) @@ -238,15 +238,17 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { #[cfg(flash)] - pub struct FlashLayout<'d> { - #(#fields),* + pub struct FlashLayout<'d, MODE> { + #(#fields),*, + _mode: core::marker::PhantomData, } #[cfg(flash)] - impl<'d> FlashLayout<'d> { + impl<'d, MODE> FlashLayout<'d, MODE> { pub(crate) fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { - #(#inits),* + #(#inits),*, + _mode: core::marker::PhantomData, } } } diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 44e23d9c4..3564bbff5 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,12 +1,21 @@ use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::Mutex; use super::{ - ensure_sector_aligned, family, get_sector, Error, Flash, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, - REGION_ACCESS, WRITE_SIZE, + ensure_sector_aligned, family, get_sector, read_blocking, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, + MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, }; +pub(super) static REGION_ACCESS: Mutex = Mutex::new(()); + impl<'d> Flash<'d> { + pub fn into_regions(self) -> FlashLayout<'d, Async> { + family::set_default_layout(); + FlashLayout::new(self.inner) + } + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } } @@ -16,6 +25,18 @@ impl<'d> Flash<'d> { } } +impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { + const READ_SIZE: usize = READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + impl embedded_storage_async::nor_flash::NorFlash for Flash<'_> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = MAX_ERASE_SIZE; @@ -89,7 +110,11 @@ pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Resu foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl crate::_generated::flash_regions::$type_name<'_> { + impl crate::_generated::flash_regions::$type_name<'_, Async> { + pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + read_blocking(self.0.base, self.0.size, offset, bytes) + } + pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { let _guard = REGION_ACCESS.lock().await; unsafe { write_chunked(self.0.base, self.0.size, offset, bytes).await } @@ -101,7 +126,7 @@ foreach_flash_region! { } } - impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, Async> { const READ_SIZE: usize = READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -113,7 +138,7 @@ foreach_flash_region! { } } - impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_, Async> { const WRITE_SIZE: usize = $write_size; const ERASE_SIZE: usize = $erase_size; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index e1fe7e9da..8b38745cf 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,14 +1,12 @@ use atomic_polyfill::{fence, Ordering}; use embassy_cortex_m::interrupt::InterruptExt; -use embassy_futures::block_on; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::mutex::Mutex; use stm32_metapac::FLASH_BASE; use super::{ - family, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, + family, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, }; use crate::peripherals::FLASH; use crate::Peripheral; @@ -17,8 +15,6 @@ pub struct Flash<'d> { pub(crate) inner: PeripheralRef<'d, FLASH>, } -pub(crate) static REGION_ACCESS: Mutex = Mutex::new(()); - impl<'d> Flash<'d> { pub fn new(p: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { into_ref!(p, irq); @@ -30,7 +26,7 @@ impl<'d> Flash<'d> { Self { inner: p } } - pub fn into_regions(self) -> FlashLayout<'d> { + pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { family::set_default_layout(); FlashLayout::new(self.inner) } @@ -40,11 +36,19 @@ impl<'d> Flash<'d> { } pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { write_chunked_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } + unsafe { + write_blocking( + FLASH_BASE as u32, + FLASH_SIZE as u32, + offset, + bytes, + write_chunk_unlocked, + ) + } } pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { erase_sectored_blocking(FLASH_BASE as u32, from, to) } + unsafe { erase_blocking(FLASH_BASE as u32, from, to, erase_sector_unlocked) } } } @@ -59,7 +63,13 @@ pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) Ok(()) } -pub(super) unsafe fn write_chunked_blocking(base: u32, size: u32, offset: u32, bytes: &[u8]) -> Result<(), Error> { +pub(super) unsafe fn write_blocking( + base: u32, + size: u32, + offset: u32, + bytes: &[u8], + write_chunk: unsafe fn(u32, &[u8]) -> Result<(), Error>, +) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -71,26 +81,39 @@ pub(super) unsafe fn write_chunked_blocking(base: u32, size: u32, offset: u32, b trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); for chunk in bytes.chunks(WRITE_SIZE) { - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); - family::enable_blocking_write(); - fence(Ordering::SeqCst); - - let _on_drop = OnDrop::new(|| { - family::disable_blocking_write(); - fence(Ordering::SeqCst); - family::lock(); - }); - - family::write_blocking(address, chunk.try_into().unwrap())?; + write_chunk(address, chunk)?; address += WRITE_SIZE as u32; } Ok(()) } -pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> Result<(), Error> { +pub(super) unsafe fn write_chunk_unlocked(address: u32, chunk: &[u8]) -> Result<(), Error> { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + family::enable_blocking_write(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| { + family::disable_blocking_write(); + fence(Ordering::SeqCst); + family::lock(); + }); + + family::write_blocking(address, chunk.try_into().unwrap()) +} + +pub(super) unsafe fn write_chunk_with_critical_section(address: u32, chunk: &[u8]) -> Result<(), Error> { + critical_section::with(|_| write_chunk_unlocked(address, chunk)) +} + +pub(super) unsafe fn erase_blocking( + base: u32, + from: u32, + to: u32, + erase_sector: unsafe fn(&FlashSector) -> Result<(), Error>, +) -> Result<(), Error> { let start_address = base + from; let end_address = base + to; let regions = family::get_flash_regions(); @@ -103,21 +126,28 @@ pub(super) unsafe fn erase_sectored_blocking(base: u32, from: u32, to: u32) -> R while address < end_address { let sector = get_sector(address, regions); trace!("Erasing sector: {:?}", sector); - - family::clear_all_err(); - fence(Ordering::SeqCst); - family::unlock(); - fence(Ordering::SeqCst); - - let _on_drop = OnDrop::new(|| family::lock()); - - family::erase_sector_blocking(§or)?; + erase_sector(§or)?; address += sector.size; } Ok(()) } -pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { +pub(super) unsafe fn erase_sector_unlocked(sector: &FlashSector) -> Result<(), Error> { + family::clear_all_err(); + fence(Ordering::SeqCst); + family::unlock(); + fence(Ordering::SeqCst); + + let _on_drop = OnDrop::new(|| family::lock()); + + family::erase_sector_blocking(§or) +} + +pub(super) unsafe fn erase_sector_with_critical_section(sector: &FlashSector) -> Result<(), Error> { + critical_section::with(|_| erase_sector_unlocked(sector)) +} + +pub(super) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector { let mut current_bank = FlashBank::Bank1; let mut bank_offset = 0; for region in regions { @@ -142,7 +172,7 @@ pub(crate) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector panic!("Flash sector not found"); } -pub(crate) fn ensure_sector_aligned( +pub(super) fn ensure_sector_aligned( start_address: u32, end_address: u32, regions: &[&FlashRegion], @@ -190,121 +220,49 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_> { } } -#[cfg(feature = "nightly")] -impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { - const READ_SIZE: usize = READ_SIZE; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - FLASH_SIZE - } -} - -pub struct BlockingFlashRegion<'d, const WRITE_SIZE: u32, const ERASE_SIZE: u32>( - &'static FlashRegion, - PeripheralRef<'d, FLASH>, -); - -impl BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> { - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - read_blocking(self.0.base, self.0.size, offset, bytes) - } - - pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = block_on(REGION_ACCESS.lock()); - unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } - } - - pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = block_on(REGION_ACCESS.lock()); - unsafe { erase_sectored_blocking(self.0.base, from, to) } - } -} - -impl embedded_storage::nor_flash::ErrorType - for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> -{ - type Error = Error; -} - -impl embedded_storage::nor_flash::ReadNorFlash - for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> -{ - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.0.size as usize - } -} - -impl embedded_storage::nor_flash::NorFlash - for BlockingFlashRegion<'_, WRITE_SIZE, ERASE_SIZE> -{ - const WRITE_SIZE: usize = WRITE_SIZE as usize; - const ERASE_SIZE: usize = ERASE_SIZE as usize; - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) - } - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) - } -} - foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - paste::paste! { - pub type []<'d> = BlockingFlashRegion<'d, $write_size, $erase_size>; - } - - impl<'d> crate::_generated::flash_regions::$type_name<'d> { - /// Make this flash region work in a blocking context. - /// - /// SAFETY - /// - /// This function is unsafe as incorect usage of parallel blocking operations - /// on multiple regions may cause a deadlock because each region requires mutual access to the flash. - pub unsafe fn into_blocking(self) -> BlockingFlashRegion<'d, $write_size, $erase_size> { - BlockingFlashRegion(self.0, self.1) - } - - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + impl<'d> crate::_generated::flash_regions::$type_name<'d, Blocking> { + pub fn read_blocking(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { read_blocking(self.0.base, self.0.size, offset, bytes) } - pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } + pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { write_blocking(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) } } - pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { erase_sectored_blocking(self.0.base, from, to) } + pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { erase_blocking(self.0.base, from, to, erase_sector_with_critical_section) } } } - impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage::nor_flash::ErrorType for crate::_generated::flash_regions::$type_name<'_, MODE> { type Error = Error; } - impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_> { + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, Blocking> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) + self.read_blocking(offset, bytes) } fn capacity(&self) -> usize { self.0.size as usize } } + + impl embedded_storage::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_, Blocking> { + const WRITE_SIZE: usize = $write_size; + const ERASE_SIZE: usize = $erase_size; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write_blocking(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase_blocking(from, to) + } + } }; } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 084bbdc6e..d50a35b41 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -11,6 +11,8 @@ use crate::pac; #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] mod alt_regions { + use core::marker::PhantomData; + use embassy_hal_common::PeripheralRef; use stm32_metapac::FLASH_SIZE; @@ -18,8 +20,7 @@ mod alt_regions { #[cfg(feature = "nightly")] use crate::flash::asynch; use crate::flash::{ - common, Bank1Region1, Bank1Region2, BlockingFlashRegion, Error, Flash, FlashBank, FlashRegion, READ_SIZE, - REGION_ACCESS, + common, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion, READ_SIZE, }; use crate::peripherals::FLASH; @@ -53,101 +54,86 @@ mod alt_regions { &ALT_BANK2_REGION3, ]; - pub struct AltBank1Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); - pub struct AltBank2Region1<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); - pub struct AltBank2Region2<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); - pub struct AltBank2Region3<'d>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>); + pub struct AltBank1Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region1<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region2<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub type BlockingAltBank1Region3<'d> = - BlockingFlashRegion<'d, { ALT_BANK1_REGION3.write_size }, { ALT_BANK1_REGION3.erase_size }>; - pub type BlockingAltBank2Region1<'d> = - BlockingFlashRegion<'d, { ALT_BANK2_REGION1.write_size }, { ALT_BANK2_REGION1.erase_size }>; - pub type BlockingAltBank2Region2<'d> = - BlockingFlashRegion<'d, { ALT_BANK2_REGION2.write_size }, { ALT_BANK2_REGION2.erase_size }>; - pub type BlockingAltBank2Region3<'d> = - BlockingFlashRegion<'d, { ALT_BANK2_REGION3.write_size }, { ALT_BANK2_REGION3.erase_size }>; - - pub struct AltFlashLayout<'d> { - pub bank1_region1: Bank1Region1<'d>, - pub bank1_region2: Bank1Region2<'d>, - pub bank1_region3: AltBank1Region3<'d>, - pub bank2_region1: AltBank2Region1<'d>, - pub bank2_region2: AltBank2Region2<'d>, - pub bank2_region3: AltBank2Region3<'d>, - pub otp_region: OTPRegion<'d>, + pub struct AltFlashLayout<'d, MODE> { + pub bank1_region1: Bank1Region1<'d, MODE>, + pub bank1_region2: Bank1Region2<'d, MODE>, + pub bank1_region3: AltBank1Region3<'d, MODE>, + pub bank2_region1: AltBank2Region1<'d, MODE>, + pub bank2_region2: AltBank2Region2<'d, MODE>, + pub bank2_region3: AltBank2Region3<'d, MODE>, + pub otp_region: OTPRegion<'d, MODE>, } impl<'d> Flash<'d> { - pub fn into_alt_regions(self) -> AltFlashLayout<'d> { + pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> { + unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + + // SAFETY: We never expose the cloned peripheral references, and their instance is not public. + // Also, all async flash region operations are protected with a mutex. + let p = self.inner; + AltFlashLayout { + bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), + } + } + + pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> { unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. let p = self.inner; AltFlashLayout { - bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }), - bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }), - bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }), - bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }), - bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }), - bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }), + bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), + bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), + otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } } macro_rules! foreach_altflash_region { ($type_name:ident, $region:ident) => { - impl $type_name<'_> { - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + #[cfg(feature = "nightly")] + impl $type_name<'_, Async> { + pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { common::read_blocking(self.0.base, self.0.size, offset, bytes) } - #[cfg(feature = "nightly")] pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = REGION_ACCESS.lock().await; + let _guard = asynch::REGION_ACCESS.lock().await; unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } } - #[cfg(feature = "nightly")] pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = REGION_ACCESS.lock().await; + let _guard = asynch::REGION_ACCESS.lock().await; unsafe { asynch::erase_sectored(self.0.base, from, to).await } } - - pub fn try_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { common::write_chunked_blocking(self.0.base, self.0.size, offset, bytes) } - } - - pub fn try_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - let _guard = REGION_ACCESS.try_lock().map_err(|_| Error::TryLockError)?; - unsafe { common::erase_sectored_blocking(self.0.base, from, to) } - } } - impl embedded_storage::nor_flash::ErrorType for $type_name<'_> { + impl embedded_storage::nor_flash::ErrorType for $type_name<'_, Async> { type Error = Error; } - impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_> { - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.0.size as usize - } - } - #[cfg(feature = "nightly")] - impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_> { + impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> { const READ_SIZE: usize = READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) + self.read(offset, bytes).await } fn capacity(&self) -> usize { @@ -156,7 +142,7 @@ mod alt_regions { } #[cfg(feature = "nightly")] - impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_> { + impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_, Async> { const WRITE_SIZE: usize = $region.write_size as usize; const ERASE_SIZE: usize = $region.erase_size as usize; diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1ef04e56f..56a680a86 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -14,6 +14,9 @@ pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; pub const READ_SIZE: usize = 1; +pub struct Blocking; +pub struct Async; + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FlashRegion { From 525e06547483de4d431bda2991555a97a0197346 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 17:31:35 +0200 Subject: [PATCH 1203/1575] Align examples --- examples/stm32f3/src/bin/flash.rs | 12 ++++++------ examples/stm32f7/src/bin/flash.rs | 12 ++++++------ examples/stm32h7/src/bin/flash.rs | 12 ++++++------ examples/stm32l0/src/bin/flash.rs | 12 ++++++------ examples/stm32l1/src/bin/flash.rs | 12 ++++++------ examples/stm32wl/src/bin/flash.rs | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index 0e5fb0658..befae0a16 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 2048)); + unwrap!(f.erase_blocking(ADDR, ADDR + 2048)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index 717c82e86..5507e7310 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region3.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region3; info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 256 * 1024)); + unwrap!(f.erase_blocking(ADDR, ADDR + 256 * 1024)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write( + unwrap!(f.write_blocking( ADDR, &[ 1, 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, @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index aab72cae8..fe6dad249 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank2_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 128 * 1024)); + unwrap!(f.erase_blocking(ADDR, ADDR + 128 * 1024)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write( + unwrap!(f.write_blocking( ADDR, &[ 1, 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, @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 0ed0d05fc..4182c87b1 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 128)); + unwrap!(f.erase_blocking(ADDR, ADDR + 128)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index c4d7d029c..53052e7c5 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 256)); + unwrap!(f.erase_blocking(ADDR, ADDR + 256)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index df51ceb68..e03b69b82 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -14,27 +14,27 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = unsafe { Flash::new(p.FLASH, interrupt::take!(FLASH)).into_regions().bank1_region.into_blocking() }; + let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase(ADDR, ADDR + 2048)); + unwrap!(f.erase_blocking(ADDR, ADDR + 2048)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read(ADDR, &mut buf)); + unwrap!(f.read_blocking(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } From bac8ad565e459320e8ecc2224233d69fa02f6306 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 17:35:49 +0200 Subject: [PATCH 1204/1575] Remove TryLockError, --- embassy-stm32/src/flash/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 56a680a86..02f6c5320 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -83,7 +83,6 @@ pub enum Error { Protected, Unaligned, Parallelism, - TryLockError, } impl NorFlashError for Error { From efc71e08c45a29ab9bdb940af8e7321917d7b234 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 24 May 2023 23:51:48 +0200 Subject: [PATCH 1205/1575] Default to Async mode --- embassy-stm32/build.rs | 4 ++-- embassy-stm32/src/flash/f4.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 29af3c80d..b766f0739 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -213,7 +213,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { #[cfg(flash)] - pub struct #region_type<'d, MODE>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); + pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); }); } @@ -238,7 +238,7 @@ fn main() { let regions_len = flash_memory_regions.len(); flash_regions.extend(quote! { #[cfg(flash)] - pub struct FlashLayout<'d, MODE> { + pub struct FlashLayout<'d, MODE = crate::flash::Async> { #(#fields),*, _mode: core::marker::PhantomData, } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index d50a35b41..3d696223c 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -54,12 +54,12 @@ mod alt_regions { &ALT_BANK2_REGION3, ]; - pub struct AltBank1Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltBank2Region1<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltBank2Region2<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltBank2Region3<'d, MODE>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank1Region3<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region1<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region2<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); + pub struct AltBank2Region3<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData); - pub struct AltFlashLayout<'d, MODE> { + pub struct AltFlashLayout<'d, MODE = Async> { pub bank1_region1: Bank1Region1<'d, MODE>, pub bank1_region2: Bank1Region2<'d, MODE>, pub bank1_region3: AltBank1Region3<'d, MODE>, From 15636f05f54458a184f0e117b1cfdb9d8e929f1d Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:08:40 +0200 Subject: [PATCH 1206/1575] Actually transition to dual bank mode - key was required --- embassy-stm32/src/flash/f4.rs | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 3d696223c..50ab446bd 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -71,7 +71,7 @@ mod alt_regions { impl<'d> Flash<'d> { pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> { - unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + super::set_alt_layout(); // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all async flash region operations are protected with a mutex. @@ -88,7 +88,7 @@ mod alt_regions { } pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> { - unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(true)) }; + super::set_alt_layout(); // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. @@ -171,12 +171,31 @@ static WAKER: AtomicWaker = AtomicWaker::new(); #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn set_default_layout() { - unsafe { crate::pac::FLASH.optcr().modify(|r| r.set_db1m(false)) }; + unsafe { + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x08192A3B)); + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x4C5D6E7F)); + pac::FLASH.optcr().modify(|r| { + r.set_db1m(false); + r.set_optlock(true) + }); + }; } #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] pub const fn set_default_layout() {} +#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] +fn set_alt_layout() { + unsafe { + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x08192A3B)); + pac::FLASH.optkeyr().write(|w| w.set_optkey(0x4C5D6E7F)); + pac::FLASH.optcr().modify(|r| { + r.set_db1m(true); + r.set_optlock(true) + }); + }; +} + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn get_flash_regions() -> &'static [&'static FlashRegion] { if unsafe { pac::FLASH.optcr().read().db1m() } { @@ -207,8 +226,8 @@ pub(crate) unsafe fn lock() { } pub(crate) unsafe fn unlock() { - pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123)); - pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); + pac::FLASH.keyr().write(|w| w.set_key(0x45670123)); + pac::FLASH.keyr().write(|w| w.set_key(0xCDEF89AB)); } #[cfg(feature = "nightly")] From 7371eefa8666c304fce5c0e25f5315b1f10161fa Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:42:42 +0200 Subject: [PATCH 1207/1575] Align with new bind_interrupt --- embassy-stm32/src/flash/asynch.rs | 3 ++ embassy-stm32/src/flash/common.rs | 41 +++++++++++++++---- embassy-stm32/src/flash/f0.rs | 2 +- embassy-stm32/src/flash/f3.rs | 2 +- embassy-stm32/src/flash/f4.rs | 2 +- embassy-stm32/src/flash/f7.rs | 2 +- embassy-stm32/src/flash/h7.rs | 2 +- embassy-stm32/src/flash/l.rs | 2 +- embassy-stm32/src/flash/other.rs | 2 +- examples/boot/application/rp/src/bin/a.rs | 2 +- .../boot/application/stm32f3/src/bin/a.rs | 2 +- .../boot/application/stm32f7/src/bin/a.rs | 2 +- .../boot/application/stm32h7/src/bin/a.rs | 2 +- .../boot/application/stm32l0/src/bin/a.rs | 2 +- .../boot/application/stm32l1/src/bin/a.rs | 2 +- .../boot/application/stm32l4/src/bin/a.rs | 2 +- .../boot/application/stm32wl/src/bin/a.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 3 +- examples/stm32f3/src/bin/flash.rs | 4 +- examples/stm32f4/src/bin/flash.rs | 4 +- examples/stm32f4/src/bin/flash_async.rs | 10 +++-- examples/stm32h7/src/bin/flash.rs | 4 +- examples/stm32l0/src/bin/flash.rs | 4 +- examples/stm32l1/src/bin/flash.rs | 4 +- examples/stm32wl/src/bin/flash.rs | 4 +- 25 files changed, 71 insertions(+), 40 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 3564bbff5..017fb17fa 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -12,15 +12,18 @@ pub(super) static REGION_ACCESS: Mutex = Mutex::new impl<'d> Flash<'d> { pub fn into_regions(self) -> FlashLayout<'d, Async> { + assert!(!self.blocking_only); family::set_default_layout(); FlashLayout::new(self.inner) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + assert!(!self.blocking_only); unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } } pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + assert!(!self.blocking_only); unsafe { erase_sectored(FLASH_BASE as u32, from, to).await } } } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 8b38745cf..0a1ee5166 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,5 +1,5 @@ use atomic_polyfill::{fence, Ordering}; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; @@ -9,21 +9,37 @@ use super::{ WRITE_SIZE, }; use crate::peripherals::FLASH; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; pub struct Flash<'d> { pub(crate) inner: PeripheralRef<'d, FLASH>, + pub(crate) blocking_only: bool, } impl<'d> Flash<'d> { - pub fn new(p: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd) -> Self { - into_ref!(p, irq); + pub fn new( + p: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, + ) -> Self { + into_ref!(p); - irq.set_handler(family::on_interrupt); - irq.unpend(); - irq.enable(); + let flash_irq = unsafe { crate::interrupt::FLASH::steal() }; + flash_irq.unpend(); + flash_irq.enable(); - Self { inner: p } + Self { + inner: p, + blocking_only: false, + } + } + + pub fn new_blocking_only(p: impl Peripheral

+ 'd) -> Self { + into_ref!(p); + + Self { + inner: p, + blocking_only: true, + } } pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { @@ -52,6 +68,15 @@ impl<'d> Flash<'d> { } } +/// Interrupt handler +pub struct InterruptHandler; + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + family::on_interrupt(); + } +} + pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index ecf3a6981..cd17486e6 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -13,7 +13,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index fd778f2b1..4ce391288 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -13,7 +13,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 50ab446bd..2b0472640 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -210,7 +210,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { // Clear IRQ flags pac::FLASH.sr().write(|w| { w.set_operr(true); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index a0593b14b..ab518bf89 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -12,7 +12,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 865f13283..d6818d594 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -17,7 +17,7 @@ pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index f8a0dac4c..c2394e0c9 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -12,7 +12,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index e21b0b241..e569951f9 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -8,7 +8,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt(_: *mut ()) { +pub(crate) unsafe fn on_interrupt() { unimplemented!(); } diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index e3ac634c2..2b84ec614 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -26,7 +26,7 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let mut flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking_only(p.FLASH); let mut updater = FirmwareUpdater::default(); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index d92d59b29..a69b6327f 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 79ab80e09..1f55db932 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new(p.FLASH); + let mut flash = Flash::new_blocking_only(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 8b452be34..b8617c3bd 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new(p.FLASH); + let mut flash = Flash::new_blocking_only(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index 59ca34386..c66635639 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 59ca34386..c66635639 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 6cddc6cc8..86936222c 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 1ff47eddd..2982e8df1 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new(p.FLASH); + let flash = Flash::new_blocking_only(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PA0, Pull::Up); diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 49c21920b..5e8a4f2b3 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,8 +20,7 @@ fn main() -> ! { */ let mut bl: BootLoader<2048> = BootLoader::default(); - let flash = Flash::new(p.FLASH); - let layout = flash.into_regions(); + let layout = Flash::new_blocking_only(p.FLASH).into_blocking_regions(); let mut flash = BootFlash::new(layout.bank1_region); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index befae0a16..9a31b548d 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index de4ecdb8f..455af930b 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { // Once can also call `into_regions()` to get access to NorFlash implementations // for each of the unique characteristics. - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); + let mut f = Flash::new_blocking_only(p.FLASH); // Sector 5 test_flash(&mut f, 128 * 1024, 128 * 1024); diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs index c9d9df34b..675337083 100644 --- a/examples/stm32f4/src/bin/flash_async.rs +++ b/examples/stm32f4/src/bin/flash_async.rs @@ -5,17 +5,21 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_time::{Timer, Duration}; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, InterruptHandler}; use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; -use embassy_stm32::{interrupt}; +use embassy_stm32::bind_interrupts; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + FLASH => InterruptHandler; +}); + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello Flash!"); - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)); + let mut f = Flash::new(p.FLASH, Irqs); // Led should blink uninterrupted during ~2sec erase operation spawner.spawn(blinky(p.PB7.degrade())).unwrap(); diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index fe6dad249..c0c332c34 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank2_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 4182c87b1..57ccf7f57 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 53052e7c5..71174bfbc 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index e03b69b82..51bd0db4e 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; From 49a31bd5d8496e8ceb1144e6e02de0e55dd0508b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:54:40 +0200 Subject: [PATCH 1208/1575] Simplify SR->Result --- embassy-stm32/src/flash/f4.rs | 45 +++++++++++++---------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 2b0472640..53e58835e 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -4,6 +4,7 @@ use core::sync::atomic::{fence, Ordering}; #[cfg(feature = "nightly")] use embassy_sync::waitqueue::AtomicWaker; +use pac::flash::regs::Sr; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; @@ -347,17 +348,7 @@ pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { let sr = pac::FLASH.sr().read(); if !sr.bsy() { - Poll::Ready(if sr.pgserr() { - Err(Error::Seq) - } else if sr.pgperr() { - Err(Error::Parallelism) - } else if sr.pgaerr() { - Err(Error::Unaligned) - } else if sr.wrperr() { - Err(Error::Protected) - } else { - Ok(()) - }) + Poll::Ready(get_result(sr)) } else { return Poll::Pending; } @@ -370,27 +361,25 @@ unsafe fn wait_ready_blocking() -> Result<(), Error> { let sr = pac::FLASH.sr().read(); if !sr.bsy() { - if sr.pgserr() { - return Err(Error::Seq); - } - - if sr.pgperr() { - return Err(Error::Parallelism); - } - - if sr.pgaerr() { - return Err(Error::Unaligned); - } - - if sr.wrperr() { - return Err(Error::Protected); - } - - return Ok(()); + return get_result(sr); } } } +fn get_result(sr: Sr) -> Result<(), Error> { + if sr.pgserr() { + Err(Error::Seq) + } else if sr.pgperr() { + Err(Error::Parallelism) + } else if sr.pgaerr() { + Err(Error::Unaligned) + } else if sr.wrperr() { + Err(Error::Protected) + } else { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; From e764a3d9ca7a4a0ccdf800c61a388dd103de5ac1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 13:59:32 +0200 Subject: [PATCH 1209/1575] Fix unused errors --- embassy-stm32/src/flash/common.rs | 3 +++ embassy-stm32/src/flash/f4.rs | 8 +++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 0a1ee5166..547e30312 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -13,6 +13,7 @@ use crate::{interrupt, Peripheral}; pub struct Flash<'d> { pub(crate) inner: PeripheralRef<'d, FLASH>, + #[cfg(all(feature = "nightly", flash_f4))] pub(crate) blocking_only: bool, } @@ -29,6 +30,7 @@ impl<'d> Flash<'d> { Self { inner: p, + #[cfg(all(feature = "nightly", flash_f4))] blocking_only: false, } } @@ -38,6 +40,7 @@ impl<'d> Flash<'d> { Self { inner: p, + #[cfg(all(feature = "nightly", flash_f4))] blocking_only: true, } } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 53e58835e..aa3433b23 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -20,9 +20,7 @@ mod alt_regions { use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; #[cfg(feature = "nightly")] use crate::flash::asynch; - use crate::flash::{ - common, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion, READ_SIZE, - }; + use crate::flash::{Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { @@ -111,7 +109,7 @@ mod alt_regions { #[cfg(feature = "nightly")] impl $type_name<'_, Async> { pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - common::read_blocking(self.0.base, self.0.size, offset, bytes) + crate::flash::common::read_blocking(self.0.base, self.0.size, offset, bytes) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { @@ -131,7 +129,7 @@ mod alt_regions { #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> { - const READ_SIZE: usize = READ_SIZE; + const READ_SIZE: usize = crate::flash::READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { self.read(offset, bytes).await From 8073bf22e92791618e1a11c58901f5b98ff002d1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 20:07:29 +0200 Subject: [PATCH 1210/1575] Add sector number tests --- embassy-stm32/src/flash/f4.rs | 88 +++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index aa3433b23..e8ac95ae2 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -168,6 +168,12 @@ pub use alt_regions::*; #[cfg(feature = "nightly")] static WAKER: AtomicWaker = AtomicWaker::new(); +impl FlashSector { + const fn snb(&self) -> u8 { + ((self.bank as u8) << 4) + self.index_in_bank + } +} + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn set_default_layout() { unsafe { @@ -287,11 +293,9 @@ unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { #[cfg(feature = "nightly")] pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { - let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; - pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(snb); + w.set_snb(sector.snb()); w.set_eopie(true); w.set_errie(true); }); @@ -310,11 +314,9 @@ pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Erro } pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { - let snb = ((sector.bank as u8) << 4) + sector.index_in_bank; - pac::FLASH.cr().modify(|w| { w.set_ser(true); - w.set_snb(snb) + w.set_snb(sector.snb()) }); pac::FLASH.cr().modify(|w| { @@ -390,7 +392,9 @@ mod tests { const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; const LARGE_SECTOR_SIZE: u32 = 128 * 1024; - let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32| { + let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| { + let sector = get_sector(address, &FLASH_REGIONS); + assert_eq!(snb, sector.snb()); assert_eq!( FlashSector { bank: FlashBank::Bank1, @@ -398,24 +402,26 @@ mod tests { start, size }, - get_sector(address, &FLASH_REGIONS) - ) + sector + ); }; - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); - let assert_sector = |bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { + let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { + let sector = get_sector(address, &ALT_FLASH_REGIONS); + assert_eq!(snb, sector.snb()); assert_eq!( FlashSector { bank, @@ -423,34 +429,34 @@ mod tests { start, size }, - get_sector(address, &ALT_FLASH_REGIONS) + sector ) }; - assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); - assert_sector(FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); - assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); - assert_sector(FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); + assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); + assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); + assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); + assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); - assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); - assert_sector(FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); + assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); + assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); - assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); - assert_sector(FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); - assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); - assert_sector(FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); + assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); + assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); + assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); + assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); - assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); - assert_sector(FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); - assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); - assert_sector(FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); + assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); + assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); + assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); + assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); - assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); - assert_sector(FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); + assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); + assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); - assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); - assert_sector(FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); - assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); - assert_sector(FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); + assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); + assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); + assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); + assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); } } From 0d80a95e5415d9baf5a25fa6f7cb04b4d5d89827 Mon Sep 17 00:00:00 2001 From: Alpha3__0 Date: Thu, 25 May 2023 11:33:29 -0700 Subject: [PATCH 1211/1575] Implement eh 0.2.* serial::Write for Uart/UartTx --- embassy-rp/src/uart/mod.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7234336b4..7bd7be41d 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -755,6 +755,32 @@ mod eh02 { } } + impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, T, M> { + type Error = Error; + + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + let r = T::regs(); + unsafe { + if r.uartfr().read().txff() { + return Err(nb::Error::WouldBlock); + } + + r.uartdr().write(|w| w.set_data(word)); + } + Ok(()) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + let r = T::regs(); + unsafe { + if !r.uartfr().read().txfe() { + return Err(nb::Error::WouldBlock); + } + } + Ok(()) + } + } + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { type Error = Error; @@ -775,6 +801,18 @@ mod eh02 { } } + impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for Uart<'d, T, M> { + type Error = Error; + + fn write(&mut self, word: u8) -> Result<(), nb::Error> { + embedded_hal_02::serial::Write::write(&mut self.tx, word) + } + + fn flush(&mut self) -> Result<(), nb::Error> { + embedded_hal_02::serial::Write::flush(&mut self.tx) + } + } + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { type Error = Error; From dc28a42fd2fde57d66a692dd12ada3bdafa3c295 Mon Sep 17 00:00:00 2001 From: Alpha3__0 Date: Thu, 25 May 2023 11:55:05 -0700 Subject: [PATCH 1212/1575] Fix return definition --- embassy-rp/src/uart/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7bd7be41d..44e0ca0f6 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -758,7 +758,7 @@ mod eh02 { impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Write for UartTx<'d, T, M> { type Error = Error; - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + fn write(&mut self, word: u8) -> Result<(), nb::Error> { let r = T::regs(); unsafe { if r.uartfr().read().txff() { @@ -770,7 +770,7 @@ mod eh02 { Ok(()) } - fn flush(&mut self) -> nb::Result<(), Self::Error> { + fn flush(&mut self) -> Result<(), nb::Error> { let r = T::regs(); unsafe { if !r.uartfr().read().txfe() { From b412784a7aea102ef53744fbb11d473fa3c0c984 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 20:55:12 +0200 Subject: [PATCH 1213/1575] Add runtime checks for errata 2.2.11 --- embassy-stm32/src/flash/common.rs | 3 ++ embassy-stm32/src/flash/f4.rs | 73 ++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 547e30312..3eb4a0f18 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -85,6 +85,9 @@ pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) return Err(Error::Size); } + #[cfg(flash_f4)] + family::assert_not_corrupted_read(); + let start_address = base + offset; let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index e8ac95ae2..488584212 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -380,6 +380,77 @@ fn get_result(sr: Sr) -> Result<(), Error> { } } +pub(crate) fn assert_not_corrupted_read() { + #[allow(unused)] + const REVISION_3: u16 = 0x2001; + + #[cfg(any( + feature = "stm32f427ai", + feature = "stm32f427ii", + feature = "stm32f427vi", + feature = "stm32f427zi", + feature = "stm32f429ai", + feature = "stm32f429bi", + feature = "stm32f429ii", + feature = "stm32f429ni", + feature = "stm32f429vi", + feature = "stm32f429zi", + feature = "stm32f437ai", + feature = "stm32f437ii", + feature = "stm32f437vi", + feature = "stm32f437zi", + feature = "stm32f439ai", + feature = "stm32f439bi", + feature = "stm32f439ii", + feature = "stm32f439ni", + feature = "stm32f439vi", + feature = "stm32f439zi", + ))] + if unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && pa12_is_output_pull_low() } { + panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11"); + } + + #[cfg(any( + feature = "stm32f427ag", + feature = "stm32f427ig", + feature = "stm32f427vg", + feature = "stm32f427zg", + feature = "stm32f429ag", + feature = "stm32f429bg", + feature = "stm32f429ig", + feature = "stm32f429ng", + feature = "stm32f429vg", + feature = "stm32f429zg", + feature = "stm32f437ig", + feature = "stm32f437vg", + feature = "stm32f437zg", + feature = "stm32f439bg", + feature = "stm32f439ig", + feature = "stm32f439ng", + feature = "stm32f439vg", + feature = "stm32f439zg", + ))] + if unsafe { + pac::FLASH.optcr().read().db1m() + && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 + && pa12_is_output_pull_low() + } { + panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); + } +} + +#[allow(unused)] +fn pa12_is_output_pull_low() -> bool { + use pac::gpio::vals; + use pac::GPIOA; + const PIN: usize = 12; + unsafe { + GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT + && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULLDOWN + && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW + } +} + #[cfg(test)] mod tests { use super::*; @@ -387,7 +458,7 @@ mod tests { #[test] #[cfg(stm32f429)] - fn can_get_sector_single_bank() { + fn can_get_sector() { const SMALL_SECTOR_SIZE: u32 = 16 * 1024; const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; const LARGE_SECTOR_SIZE: u32 = 128 * 1024; From 18d14dff48d1fd49cfd43fb94304bf932a74a6ca Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 21:14:35 +0200 Subject: [PATCH 1214/1575] Handle errata 2.2.12 --- embassy-stm32/src/flash/f4.rs | 41 ++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 488584212..d67e6d0a1 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -2,11 +2,12 @@ use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; +use atomic_polyfill::AtomicBool; #[cfg(feature = "nightly")] use embassy_sync::waitqueue::AtomicWaker; use pac::flash::regs::Sr; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -167,6 +168,7 @@ pub use alt_regions::*; #[cfg(feature = "nightly")] static WAKER: AtomicWaker = AtomicWaker::new(); +static DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false); impl FlashSector { const fn snb(&self) -> u8 { @@ -238,6 +240,7 @@ pub(crate) unsafe fn unlock() { #[cfg(feature = "nightly")] pub(crate) unsafe fn enable_write() { assert_eq!(0, WRITE_SIZE % 4); + save_data_cache_state(); pac::FLASH.cr().write(|w| { w.set_pg(true); @@ -254,10 +257,12 @@ pub(crate) unsafe fn disable_write() { w.set_eopie(false); w.set_errie(false); }); + restore_data_cache_state(); } pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); + save_data_cache_state(); pac::FLASH.cr().write(|w| { w.set_pg(true); @@ -267,6 +272,7 @@ pub(crate) unsafe fn enable_blocking_write() { pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); + restore_data_cache_state(); } #[cfg(feature = "nightly")] @@ -293,6 +299,8 @@ unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { #[cfg(feature = "nightly")] pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { + save_data_cache_state(); + pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.snb()); @@ -310,10 +318,13 @@ pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Erro w.set_errie(false); }); clear_all_err(); + restore_data_cache_state(); ret } pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { + save_data_cache_state(); + pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.snb()) @@ -325,6 +336,7 @@ pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), E let ret: Result<(), Error> = wait_ready_blocking(); clear_all_err(); + restore_data_cache_state(); ret } @@ -380,6 +392,33 @@ fn get_result(sr: Sr) -> Result<(), Error> { } } +fn save_data_cache_state() { + let dual_bank = get_flash_regions().last().unwrap().bank == FlashBank::Bank2; + if dual_bank { + // Disable data cache during write/erase if there are two banks, see errata 2.2.12 + let dcen = unsafe { pac::FLASH.acr().read().dcen() }; + DATA_CACHE_WAS_ENABLED.store(dcen, Ordering::Relaxed); + if dcen { + unsafe { pac::FLASH.acr().modify(|w| w.set_dcen(false)) }; + } + } +} + +fn restore_data_cache_state() { + let dual_bank = get_flash_regions().last().unwrap().bank == FlashBank::Bank2; + if dual_bank { + // Restore data cache if it was enabled + let dcen = DATA_CACHE_WAS_ENABLED.load(Ordering::Relaxed); + if dcen { + unsafe { + // Reset data cache before we enable it again + pac::FLASH.acr().modify(|w| w.set_dcrst(true)); + pac::FLASH.acr().modify(|w| w.set_dcen(true)) + }; + } + } +} + pub(crate) fn assert_not_corrupted_read() { #[allow(unused)] const REVISION_3: u16 = 0x2001; From 181c4c5311bbc955c911eeaef5d9fd761fd42b9f Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 25 May 2023 21:15:18 +0200 Subject: [PATCH 1215/1575] Add RTC MUX selection to embassy-stm32 L4 family, to select and setup LSE and/or LSI --- embassy-stm32/src/rcc/l4.rs | 45 ++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index c1bf7d0cd..f8c1a6e06 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,12 +1,12 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; -use stm32_metapac::rcc::vals::{Mcopre, Mcosel}; +use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; -use crate::pac::{FLASH, RCC}; +use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -289,6 +289,7 @@ pub struct Config { )>, #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] pub hsi48: bool, + pub rtc_mux: RtcClockSource, } impl Default for Config { @@ -302,10 +303,16 @@ impl Default for Config { pllsai1: None, #[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))] hsi48: false, + rtc_mux: RtcClockSource::LSI32, } } } +pub enum RtcClockSource { + LSE32, + LSI32, +} + pub enum McoClock { DIV1, DIV2, @@ -432,15 +439,47 @@ impl<'d, T: McoInstance> Mco<'d, T> { } pub(crate) unsafe fn init(config: Config) { + match config.rtc_mux { + RtcClockSource::LSE32 => { + // 1. Unlock the backup domain + PWR.cr1().modify(|w| w.set_dbp(true)); + + // 2. Setup the LSE + RCC.bdcr().modify(|w| { + // Enable LSE + w.set_lseon(true); + // Max drive strength + // TODO: should probably be settable + w.set_lsedrv(Lsedrv::HIGH); + }); + + // Wait until LSE is running + while !RCC.bdcr().read().lserdy() {} + } + RtcClockSource::LSI32 => { + // Turn on the internal 32 kHz LSI oscillator + RCC.csr().modify(|w| w.set_lsion(true)); + + // Wait until LSI is running + while !RCC.csr().read().lsirdy() {} + } + } + let (sys_clk, sw) = match config.mux { ClockSrc::MSI(range) => { // Enable MSI RCC.cr().write(|w| { let bits: Msirange = range.into(); w.set_msirange(bits); - w.set_msipllen(false); w.set_msirgsel(true); w.set_msion(true); + + if let RtcClockSource::LSE32 = config.rtc_mux { + // If LSE is enabled, enable calibration of MSI + w.set_msipllen(true); + } else { + w.set_msipllen(false); + } }); while !RCC.cr().read().msirdy() {} From 860b519f9993bd8991849c680aae058558aadfbd Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 21:40:54 +0200 Subject: [PATCH 1216/1575] Let Flash be a thing --- embassy-stm32/src/flash/asynch.rs | 13 +++---- embassy-stm32/src/flash/common.rs | 37 +++++++++++-------- embassy-stm32/src/flash/f4.rs | 22 ++++++++++- examples/boot/application/rp/src/bin/a.rs | 2 +- .../boot/application/stm32f3/src/bin/a.rs | 2 +- .../boot/application/stm32f7/src/bin/a.rs | 2 +- .../boot/application/stm32h7/src/bin/a.rs | 2 +- .../boot/application/stm32l0/src/bin/a.rs | 2 +- .../boot/application/stm32l1/src/bin/a.rs | 2 +- .../boot/application/stm32l4/src/bin/a.rs | 2 +- .../boot/application/stm32wl/src/bin/a.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 2 +- examples/stm32f3/src/bin/flash.rs | 2 +- examples/stm32f4/src/bin/flash.rs | 2 +- examples/stm32f7/src/bin/flash.rs | 4 +- examples/stm32h7/src/bin/flash.rs | 2 +- examples/stm32l0/src/bin/flash.rs | 2 +- examples/stm32l1/src/bin/flash.rs | 2 +- examples/stm32wl/src/bin/flash.rs | 2 +- 19 files changed, 63 insertions(+), 43 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 017fb17fa..74c54ff33 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -10,25 +10,22 @@ use super::{ pub(super) static REGION_ACCESS: Mutex = Mutex::new(()); -impl<'d> Flash<'d> { +impl<'d> Flash<'d, Async> { pub fn into_regions(self) -> FlashLayout<'d, Async> { - assert!(!self.blocking_only); family::set_default_layout(); FlashLayout::new(self.inner) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - assert!(!self.blocking_only); unsafe { write_chunked(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes).await } } pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { - assert!(!self.blocking_only); unsafe { erase_sectored(FLASH_BASE as u32, from, to).await } } } -impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { +impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_, Async> { const READ_SIZE: usize = READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -40,7 +37,7 @@ impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_> { } } -impl embedded_storage_async::nor_flash::NorFlash for Flash<'_> { +impl embedded_storage_async::nor_flash::NorFlash for Flash<'_, Async> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = MAX_ERASE_SIZE; @@ -114,7 +111,7 @@ pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Resu foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name<'_, Async> { - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { read_blocking(self.0.base, self.0.size, offset, bytes) } @@ -133,7 +130,7 @@ foreach_flash_region! { const READ_SIZE: usize = READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) + self.read(offset, bytes).await } fn capacity(&self) -> usize { diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 3eb4a0f18..8ae72ebcb 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use atomic_polyfill::{fence, Ordering}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; @@ -5,19 +7,18 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; use super::{ - family, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, - WRITE_SIZE, + family, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, + READ_SIZE, WRITE_SIZE, }; use crate::peripherals::FLASH; use crate::{interrupt, Peripheral}; -pub struct Flash<'d> { +pub struct Flash<'d, MODE = Async> { pub(crate) inner: PeripheralRef<'d, FLASH>, - #[cfg(all(feature = "nightly", flash_f4))] - pub(crate) blocking_only: bool, + _mode: PhantomData, } -impl<'d> Flash<'d> { +impl<'d> Flash<'d, Async> { pub fn new( p: impl Peripheral

+ 'd, _irq: impl interrupt::Binding + 'd, @@ -30,21 +31,23 @@ impl<'d> Flash<'d> { Self { inner: p, - #[cfg(all(feature = "nightly", flash_f4))] - blocking_only: false, + _mode: PhantomData, } } +} - pub fn new_blocking_only(p: impl Peripheral

+ 'd) -> Self { +impl<'d> Flash<'d, Blocking> { + pub fn new_blocking(p: impl Peripheral

+ 'd) -> Self { into_ref!(p); Self { inner: p, - #[cfg(all(feature = "nightly", flash_f4))] - blocking_only: true, + _mode: PhantomData, } } +} +impl<'d, MODE> Flash<'d, MODE> { pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { family::set_default_layout(); FlashLayout::new(self.inner) @@ -222,11 +225,11 @@ pub(super) fn ensure_sector_aligned( Ok(()) } -impl embedded_storage::nor_flash::ErrorType for Flash<'_> { +impl embedded_storage::nor_flash::ErrorType for Flash<'_, MODE> { type Error = Error; } -impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { +impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_, MODE> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -238,7 +241,7 @@ impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { } } -impl embedded_storage::nor_flash::NorFlash for Flash<'_> { +impl embedded_storage::nor_flash::NorFlash for Flash<'_, MODE> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = MAX_ERASE_SIZE; @@ -253,11 +256,13 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_> { foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { - impl<'d> crate::_generated::flash_regions::$type_name<'d, Blocking> { + impl crate::_generated::flash_regions::$type_name<'_, MODE> { pub fn read_blocking(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { read_blocking(self.0.base, self.0.size, offset, bytes) } + } + impl crate::_generated::flash_regions::$type_name<'_, Blocking> { pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { unsafe { write_blocking(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) } } @@ -271,7 +276,7 @@ foreach_flash_region! { type Error = Error; } - impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, Blocking> { + impl embedded_storage::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, MODE> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index d67e6d0a1..875bb912b 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -107,10 +107,16 @@ mod alt_regions { macro_rules! foreach_altflash_region { ($type_name:ident, $region:ident) => { + impl $type_name<'_, MODE> { + pub fn read_blocking(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + crate::flash::common::read_blocking(self.0.base, self.0.size, offset, bytes) + } + } + #[cfg(feature = "nightly")] impl $type_name<'_, Async> { pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - crate::flash::common::read_blocking(self.0.base, self.0.size, offset, bytes) + self.read_blocking(offset, bytes) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { @@ -124,10 +130,22 @@ mod alt_regions { } } - impl embedded_storage::nor_flash::ErrorType for $type_name<'_, Async> { + impl embedded_storage::nor_flash::ErrorType for $type_name<'_, MODE> { type Error = Error; } + impl embedded_storage::nor_flash::ReadNorFlash for $type_name<'_, MODE> { + const READ_SIZE: usize = crate::flash::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read_blocking(offset, bytes) + } + + fn capacity(&self) -> usize { + self.0.size as usize + } + } + #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> { const READ_SIZE: usize = crate::flash::READ_SIZE; diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 2b84ec614..47f1d16d8 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -26,7 +26,7 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking_only(p.FLASH); + let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking(p.FLASH); let mut updater = FirmwareUpdater::default(); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index a69b6327f..5db1dbb57 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new_blocking_only(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 1f55db932..5d586445c 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new_blocking_only(p.FLASH); + let mut flash = Flash::new_blocking(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index b8617c3bd..202220223 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new_blocking_only(p.FLASH); + let mut flash = Flash::new_blocking(p.FLASH); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index c66635639..4033ac590 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new_blocking_only(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index c66635639..4033ac590 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new_blocking_only(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PB2, Pull::Up); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 86936222c..141d82afd 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new_blocking_only(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PC13, Pull::Up); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 2982e8df1..5f48dbe51 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let flash = Flash::new_blocking_only(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); let mut flash = BlockingAsync::new(flash); let button = Input::new(p.PA0, Pull::Up); diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 5e8a4f2b3..f81fdbc5f 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,7 +20,7 @@ fn main() -> ! { */ let mut bl: BootLoader<2048> = BootLoader::default(); - let layout = Flash::new_blocking_only(p.FLASH).into_blocking_regions(); + let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); let mut flash = BootFlash::new(layout.bank1_region); let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index 9a31b548d..2432a29d2 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 455af930b..cadb0129d 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { // Once can also call `into_regions()` to get access to NorFlash implementations // for each of the unique characteristics. - let mut f = Flash::new_blocking_only(p.FLASH); + let mut f = Flash::new_blocking(p.FLASH); // Sector 5 test_flash(&mut f, 128 * 1024, 128 * 1024); diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index 5507e7310..f3b667555 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::{flash::Flash, interrupt}; +use embassy_stm32::flash::Flash; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new(p.FLASH, interrupt::take!(FLASH)).into_blocking_regions().bank1_region3; + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region3; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index c0c332c34..982e24516 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { // wait a bit before accessing the flash Timer::after(Duration::from_millis(300)).await; - let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank2_region; + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank2_region; info!("Reading..."); let mut buf = [0u8; 32]; diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index 57ccf7f57..f057421f8 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 71174bfbc..8046f16b9 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x26000; - let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 51bd0db4e..81e365fbe 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { const ADDR: u32 = 0x36000; - let mut f = Flash::new_blocking_only(p.FLASH).into_blocking_regions().bank1_region; + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; info!("Reading..."); let mut buf = [0u8; 8]; From 9eca19b49d462e57308e8b13f7ff03e10cfb0557 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 21:46:26 +0200 Subject: [PATCH 1217/1575] *_blocking -> blocking_* --- embassy-stm32/src/flash/asynch.rs | 4 +-- embassy-stm32/src/flash/common.rs | 42 +++++++++++++++---------------- embassy-stm32/src/flash/f0.rs | 10 ++++---- embassy-stm32/src/flash/f3.rs | 10 ++++---- embassy-stm32/src/flash/f4.rs | 18 ++++++------- embassy-stm32/src/flash/f7.rs | 10 ++++---- embassy-stm32/src/flash/h7.rs | 10 ++++---- embassy-stm32/src/flash/l.rs | 10 ++++---- embassy-stm32/src/flash/other.rs | 4 +-- 9 files changed, 59 insertions(+), 59 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 74c54ff33..de53f6c8f 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -4,7 +4,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; use super::{ - ensure_sector_aligned, family, get_sector, read_blocking, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, + blocking_read, ensure_sector_aligned, family, get_sector, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, }; @@ -112,7 +112,7 @@ foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name<'_, Async> { pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - read_blocking(self.0.base, self.0.size, offset, bytes) + blocking_read(self.0.base, self.0.size, offset, bytes) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 8ae72ebcb..54c8d6812 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -54,12 +54,12 @@ impl<'d, MODE> Flash<'d, MODE> { } pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - read_blocking(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) + blocking_read(FLASH_BASE as u32, FLASH_SIZE as u32, offset, bytes) } - pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { unsafe { - write_blocking( + blocking_write( FLASH_BASE as u32, FLASH_SIZE as u32, offset, @@ -69,8 +69,8 @@ impl<'d, MODE> Flash<'d, MODE> { } } - pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { erase_blocking(FLASH_BASE as u32, from, to, erase_sector_unlocked) } + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { blocking_erase(FLASH_BASE as u32, from, to, erase_sector_unlocked) } } } @@ -83,7 +83,7 @@ impl interrupt::Handler for InterruptHandler { } } -pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { +pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -97,7 +97,7 @@ pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) Ok(()) } -pub(super) unsafe fn write_blocking( +pub(super) unsafe fn blocking_write( base: u32, size: u32, offset: u32, @@ -135,14 +135,14 @@ pub(super) unsafe fn write_chunk_unlocked(address: u32, chunk: &[u8]) -> Result< family::lock(); }); - family::write_blocking(address, chunk.try_into().unwrap()) + family::blocking_write(address, chunk.try_into().unwrap()) } pub(super) unsafe fn write_chunk_with_critical_section(address: u32, chunk: &[u8]) -> Result<(), Error> { critical_section::with(|_| write_chunk_unlocked(address, chunk)) } -pub(super) unsafe fn erase_blocking( +pub(super) unsafe fn blocking_erase( base: u32, from: u32, to: u32, @@ -174,7 +174,7 @@ pub(super) unsafe fn erase_sector_unlocked(sector: &FlashSector) -> Result<(), E let _on_drop = OnDrop::new(|| family::lock()); - family::erase_sector_blocking(§or) + family::blocking_erase_sector(§or) } pub(super) unsafe fn erase_sector_with_critical_section(sector: &FlashSector) -> Result<(), Error> { @@ -246,29 +246,29 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_, MODE> { const ERASE_SIZE: usize = MAX_ERASE_SIZE; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write_blocking(offset, bytes) + self.blocking_write(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase_blocking(from, to) + self.blocking_erase(from, to) } } foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name<'_, MODE> { - pub fn read_blocking(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - read_blocking(self.0.base, self.0.size, offset, bytes) + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + blocking_read(self.0.base, self.0.size, offset, bytes) } } impl crate::_generated::flash_regions::$type_name<'_, Blocking> { - pub fn write_blocking(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { - unsafe { write_blocking(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) } + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + unsafe { blocking_write(self.0.base, self.0.size, offset, bytes, write_chunk_with_critical_section) } } - pub fn erase_blocking(&mut self, from: u32, to: u32) -> Result<(), Error> { - unsafe { erase_blocking(self.0.base, from, to, erase_sector_with_critical_section) } + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + unsafe { blocking_erase(self.0.base, from, to, erase_sector_with_critical_section) } } } @@ -280,7 +280,7 @@ foreach_flash_region! { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read_blocking(offset, bytes) + self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { @@ -293,11 +293,11 @@ foreach_flash_region! { const ERASE_SIZE: usize = $erase_size; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write_blocking(offset, bytes) + self.blocking_write(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase_blocking(from, to) + self.blocking_erase(from, to) } } }; diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index cd17486e6..9adf3fab2 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -36,7 +36,7 @@ pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); @@ -46,10 +46,10 @@ pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - wait_ready_blocking() + blocking_wait_ready() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -60,7 +60,7 @@ pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = wait_ready_blocking(); + let mut ret: Result<(), Error> = blocking_wait_ready(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -92,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn wait_ready_blocking() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 4ce391288..b052b4d41 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -36,7 +36,7 @@ pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); @@ -46,10 +46,10 @@ pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - wait_ready_blocking() + blocking_wait_ready() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); @@ -60,7 +60,7 @@ pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let mut ret: Result<(), Error> = wait_ready_blocking(); + let mut ret: Result<(), Error> = blocking_wait_ready(); if !pac::FLASH.sr().read().eop() { trace!("FLASH: EOP not set"); @@ -92,7 +92,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn wait_ready_blocking() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 875bb912b..5e0e96204 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -108,15 +108,15 @@ mod alt_regions { macro_rules! foreach_altflash_region { ($type_name:ident, $region:ident) => { impl $type_name<'_, MODE> { - pub fn read_blocking(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - crate::flash::common::read_blocking(self.0.base, self.0.size, offset, bytes) + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + crate::flash::common::blocking_read(self.0.base, self.0.size, offset, bytes) } } #[cfg(feature = "nightly")] impl $type_name<'_, Async> { pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { - self.read_blocking(offset, bytes) + self.blocking_read(offset, bytes) } pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { @@ -138,7 +138,7 @@ mod alt_regions { const READ_SIZE: usize = crate::flash::READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read_blocking(offset, bytes) + self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { @@ -299,9 +299,9 @@ pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> wait_ready().await } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { write_start(start_address, buf); - wait_ready_blocking() + blocking_wait_ready() } unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { @@ -340,7 +340,7 @@ pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Erro ret } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { save_data_cache_state(); pac::FLASH.cr().modify(|w| { @@ -352,7 +352,7 @@ pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = wait_ready_blocking(); + let ret: Result<(), Error> = blocking_wait_ready(); clear_all_err(); restore_data_cache_state(); ret @@ -386,7 +386,7 @@ pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { .await } -unsafe fn wait_ready_blocking() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index ab518bf89..f0c6bf81d 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -38,7 +38,7 @@ pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -48,10 +48,10 @@ pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - wait_ready_blocking() + blocking_wait_ready() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.index_in_bank) @@ -61,7 +61,7 @@ pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), E w.set_strt(true); }); - let ret: Result<(), Error> = wait_ready_blocking(); + let ret: Result<(), Error> = blocking_wait_ready(); pac::FLASH.cr().modify(|w| w.set_ser(false)); clear_all_err(); ret @@ -87,7 +87,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn wait_ready_blocking() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index d6818d594..ee824ed2b 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -43,7 +43,7 @@ pub(crate) unsafe fn enable_blocking_write() { pub(crate) unsafe fn disable_blocking_write() {} -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { // We cannot have the write setup sequence in begin_write as it depends on the address let bank = if start_address < BANK1_REGION.end() { pac::FLASH.bank(0) @@ -64,7 +64,7 @@ pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); address += val.len() as u32; - res = Some(wait_ready_blocking(bank)); + res = Some(blocking_wait_ready(bank)); bank.sr().modify(|w| { if w.eop() { w.set_eop(true); @@ -84,7 +84,7 @@ pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) res.unwrap() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); @@ -95,7 +95,7 @@ pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), E w.set_start(true); }); - let ret: Result<(), Error> = wait_ready_blocking(bank); + let ret: Result<(), Error> = blocking_wait_ready(bank); bank.cr().modify(|w| w.set_ser(false)); bank_clear_all_err(bank); ret @@ -142,7 +142,7 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { }); } -unsafe fn wait_ready_blocking(bank: pac::flash::Bank) -> Result<(), Error> { +unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index c2394e0c9..76cad6d15 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -57,7 +57,7 @@ pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); @@ -67,10 +67,10 @@ pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) fence(Ordering::SeqCst); } - wait_ready_blocking() + blocking_wait_ready() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { @@ -100,7 +100,7 @@ pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), E }); } - let ret: Result<(), Error> = wait_ready_blocking(); + let ret: Result<(), Error> = blocking_wait_ready(); #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_per(false)); @@ -153,7 +153,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -unsafe fn wait_ready_blocking() -> Result<(), Error> { +unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { let sr = pac::FLASH.sr().read(); diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index e569951f9..c007f1178 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -24,10 +24,10 @@ pub(crate) unsafe fn enable_blocking_write() { pub(crate) unsafe fn disable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn write_blocking(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { unimplemented!(); } -pub(crate) unsafe fn erase_sector_blocking(_sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { unimplemented!(); } pub(crate) unsafe fn clear_all_err() { From 344e28360ff55771379202ecd06eb0cb1db38a29 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:09:28 +0200 Subject: [PATCH 1218/1575] More blocking rename --- embassy-stm32/src/flash/common.rs | 12 ++++++------ embassy-stm32/src/flash/f0.rs | 2 +- embassy-stm32/src/flash/f3.rs | 2 +- embassy-stm32/src/flash/l.rs | 2 +- embassy-stm32/src/flash/other.rs | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 1ea65c0b6..0729771a1 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -92,7 +92,7 @@ impl interrupt::Handler for InterruptHandler { } } -pub(super) fn read_blocking(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { +pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); } @@ -255,11 +255,11 @@ impl embedded_storage::nor_flash::NorFlash for Flash<'_, MODE> { const ERASE_SIZE: usize = MAX_ERASE_SIZE; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write_blocking(offset, bytes) + self.blocking_write(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase_blocking(from, to) + self.blocking_erase(from, to) } } @@ -289,7 +289,7 @@ foreach_flash_region! { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read_blocking(offset, bytes) + self.blocking_read(offset, bytes) } fn capacity(&self) -> usize { @@ -302,11 +302,11 @@ foreach_flash_region! { const ERASE_SIZE: usize = $erase_size; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write_blocking(offset, bytes) + self.blocking_write(offset, bytes) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase_blocking(from, to) + self.blocking_erase(from, to) } } }; diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index cd17486e6..22e08e91a 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -36,7 +36,7 @@ pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 4ce391288..e05734703 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -36,7 +36,7 @@ pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for chunk in buf.chunks(2) { write_volatile(address as *mut u16, u16::from_le_bytes(chunk.try_into().unwrap())); diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index c2394e0c9..e27accb01 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -57,7 +57,7 @@ pub(crate) unsafe fn disable_blocking_write() { pac::FLASH.cr().write(|w| w.set_pg(false)); } -pub(crate) unsafe fn write_blocking(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { let mut address = start_address; for val in buf.chunks(4) { write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index e569951f9..73266177f 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -24,7 +24,7 @@ pub(crate) unsafe fn enable_blocking_write() { pub(crate) unsafe fn disable_blocking_write() { unimplemented!(); } -pub(crate) unsafe fn write_blocking(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { +pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { unimplemented!(); } pub(crate) unsafe fn erase_sector_blocking(_sector: &FlashSector) -> Result<(), Error> { From 8528455a75aa33cf774d72ba6b35a4298bca511e Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:20:05 +0200 Subject: [PATCH 1219/1575] Errata if _not_ pa12 out low --- embassy-stm32/src/flash/f4.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 5e0e96204..8c9c93696 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -463,7 +463,7 @@ pub(crate) fn assert_not_corrupted_read() { feature = "stm32f439vi", feature = "stm32f439zi", ))] - if unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && pa12_is_output_pull_low() } { + if unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } { panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11"); } @@ -490,7 +490,7 @@ pub(crate) fn assert_not_corrupted_read() { if unsafe { pac::FLASH.optcr().read().db1m() && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 - && pa12_is_output_pull_low() + && !pa12_is_output_pull_low() } { panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); } From ce331b411c3a6c69fbfefa968319f8eba5ad813f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:31:24 +0200 Subject: [PATCH 1220/1575] Only assert_not_corrupted_read if we read from the second bank --- embassy-stm32/src/flash/common.rs | 7 ++++--- embassy-stm32/src/flash/f4.rs | 15 ++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 0729771a1..35f5a464c 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -97,10 +97,11 @@ pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) return Err(Error::Size); } - #[cfg(flash_f4)] - family::assert_not_corrupted_read(); - let start_address = base + offset; + + #[cfg(flash_f4)] + family::assert_not_corrupted_read(start_address + bytes.len()); + let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); Ok(()) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 8c9c93696..a43efc51b 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -6,6 +6,7 @@ use atomic_polyfill::AtomicBool; #[cfg(feature = "nightly")] use embassy_sync::waitqueue::AtomicWaker; use pac::flash::regs::Sr; +use pac::FLASH_SIZE; use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; @@ -437,10 +438,13 @@ fn restore_data_cache_state() { } } -pub(crate) fn assert_not_corrupted_read() { +pub(crate) fn assert_not_corrupted_read(end_address: u32) { #[allow(unused)] const REVISION_3: u16 = 0x2001; + #[allow(unused)] + let second_bank_read = get_flash_regions().last().unwrap().bank == FlashBank::Bank2 && end_address > FLASH_SIZE / 2; + #[cfg(any( feature = "stm32f427ai", feature = "stm32f427ii", @@ -463,7 +467,7 @@ pub(crate) fn assert_not_corrupted_read() { feature = "stm32f439vi", feature = "stm32f439zi", ))] - if unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } { + if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } { panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11"); } @@ -487,11 +491,8 @@ pub(crate) fn assert_not_corrupted_read() { feature = "stm32f439vg", feature = "stm32f439zg", ))] - if unsafe { - pac::FLASH.optcr().read().db1m() - && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 - && !pa12_is_output_pull_low() - } { + if second_bank_read && unsafe { &&pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } + { panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); } } From b50d04336ee3608e36545908cbf45969e81c9807 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:32:57 +0200 Subject: [PATCH 1221/1575] Fix merge error --- embassy-stm32/src/flash/common.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 35f5a464c..400831e0a 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -83,15 +83,6 @@ impl interrupt::Handler for InterruptHandler { } } -/// Interrupt handler -pub struct InterruptHandler; - -impl interrupt::Handler for InterruptHandler { - unsafe fn on_interrupt() { - family::on_interrupt(); - } -} - pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); From 8938d928f8c3920a5777b0537d5d9ab3fec94586 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:36:56 +0200 Subject: [PATCH 1222/1575] Fix examples --- examples/stm32f3/src/bin/flash.rs | 10 +++++----- examples/stm32f4/src/bin/flash.rs | 4 ++-- examples/stm32f7/src/bin/flash.rs | 10 +++++----- examples/stm32h7/src/bin/flash.rs | 10 +++++----- examples/stm32l0/src/bin/flash.rs | 10 +++++----- examples/stm32l1/src/bin/flash.rs | 10 +++++----- examples/stm32wl/src/bin/flash.rs | 10 +++++----- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/stm32f3/src/bin/flash.rs b/examples/stm32f3/src/bin/flash.rs index 2432a29d2..236fb36c1 100644 --- a/examples/stm32f3/src/bin/flash.rs +++ b/examples/stm32f3/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase_blocking(ADDR, ADDR + 2048)); + unwrap!(f.blocking_erase(ADDR, ADDR + 2048)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index cadb0129d..4a77595cc 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -35,7 +35,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase_blocking(offset, offset + size)); + unwrap!(f.blocking_erase(offset, offset + size)); info!("Reading..."); let mut buf = [0u8; 32]; @@ -43,7 +43,7 @@ fn test_flash(f: &mut Flash, offset: u32, size: u32) { info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write_blocking( + unwrap!(f.blocking_write( offset, &[ 1, 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, diff --git a/examples/stm32f7/src/bin/flash.rs b/examples/stm32f7/src/bin/flash.rs index f3b667555..35d3059be 100644 --- a/examples/stm32f7/src/bin/flash.rs +++ b/examples/stm32f7/src/bin/flash.rs @@ -22,19 +22,19 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase_blocking(ADDR, ADDR + 256 * 1024)); + unwrap!(f.blocking_erase(ADDR, ADDR + 256 * 1024)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write_blocking( + unwrap!(f.blocking_write( ADDR, &[ 1, 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, @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32h7/src/bin/flash.rs b/examples/stm32h7/src/bin/flash.rs index 982e24516..f66df770b 100644 --- a/examples/stm32h7/src/bin/flash.rs +++ b/examples/stm32h7/src/bin/flash.rs @@ -22,19 +22,19 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase_blocking(ADDR, ADDR + 128 * 1024)); + unwrap!(f.blocking_erase(ADDR, ADDR + 128 * 1024)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write_blocking( + unwrap!(f.blocking_write( ADDR, &[ 1, 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, @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!( &buf[..], diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs index f057421f8..86f6c70b9 100644 --- a/examples/stm32l0/src/bin/flash.rs +++ b/examples/stm32l0/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase_blocking(ADDR, ADDR + 128)); + unwrap!(f.blocking_erase(ADDR, ADDR + 128)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs index 8046f16b9..aeb535cca 100644 --- a/examples/stm32l1/src/bin/flash.rs +++ b/examples/stm32l1/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase_blocking(ADDR, ADDR + 256)); + unwrap!(f.blocking_erase(ADDR, ADDR + 256)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 81e365fbe..5e52d49ec 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -18,23 +18,23 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.erase_blocking(ADDR, ADDR + 2048)); + unwrap!(f.blocking_erase(ADDR, ADDR + 2048)); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Writing..."); - unwrap!(f.write_blocking(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); + unwrap!(f.blocking_write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8])); info!("Reading..."); let mut buf = [0u8; 8]; - unwrap!(f.read_blocking(ADDR, &mut buf)); + unwrap!(f.blocking_read(ADDR, &mut buf)); info!("Read: {=[u8]:x}", buf); assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]); } From 41a632a56c40d0ac522a8dcf20b95ed478dc41ea Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:48:17 +0200 Subject: [PATCH 1223/1575] Formatting --- examples/stm32f4/src/bin/flash_async.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs index 675337083..6c9689d9c 100644 --- a/examples/stm32f4/src/bin/flash_async.rs +++ b/examples/stm32f4/src/bin/flash_async.rs @@ -4,10 +4,10 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_time::{Timer, Duration}; +use embassy_stm32::bind_interrupts; use embassy_stm32::flash::{Flash, InterruptHandler}; use embassy_stm32::gpio::{AnyPin, Level, Output, Pin, Speed}; -use embassy_stm32::bind_interrupts; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -82,4 +82,4 @@ async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) { 30, 31, 32 ] ); -} \ No newline at end of file +} From 88543445d8414b94720654f1e5dba900e8a75cce Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:48:54 +0200 Subject: [PATCH 1224/1575] Fix end address for assertion --- embassy-stm32/src/flash/common.rs | 2 +- embassy-stm32/src/flash/f4.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 400831e0a..c190eaf9a 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -91,7 +91,7 @@ pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) let start_address = base + offset; #[cfg(flash_f4)] - family::assert_not_corrupted_read(start_address + bytes.len()); + family::assert_not_corrupted_read(start_address + bytes.len() as u32); let flash_data = unsafe { core::slice::from_raw_parts(start_address as *const u8, bytes.len()) }; bytes.copy_from_slice(flash_data); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index a43efc51b..2dd6d85fd 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -443,7 +443,7 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { const REVISION_3: u16 = 0x2001; #[allow(unused)] - let second_bank_read = get_flash_regions().last().unwrap().bank == FlashBank::Bank2 && end_address > FLASH_SIZE / 2; + let second_bank_read = get_flash_regions().last().unwrap().bank == FlashBank::Bank2 && end_address > (FLASH_SIZE / 2) as u32; #[cfg(any( feature = "stm32f427ai", @@ -491,7 +491,7 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { feature = "stm32f439vg", feature = "stm32f439zg", ))] - if second_bank_read && unsafe { &&pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } + if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } { panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); } From 4478d8322b41b614599202711ddbf42df1c8b50b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 22:58:13 +0200 Subject: [PATCH 1225/1575] Endless rustfmt pain --- embassy-stm32/src/flash/f4.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 2dd6d85fd..8a0e2001b 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -443,7 +443,8 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { const REVISION_3: u16 = 0x2001; #[allow(unused)] - let second_bank_read = get_flash_regions().last().unwrap().bank == FlashBank::Bank2 && end_address > (FLASH_SIZE / 2) as u32; + let second_bank_read = + get_flash_regions().last().unwrap().bank == FlashBank::Bank2 && end_address > (FLASH_SIZE / 2) as u32; #[cfg(any( feature = "stm32f427ai", @@ -491,8 +492,7 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { feature = "stm32f439vg", feature = "stm32f439zg", ))] - if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } - { + if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } { panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); } } From 74104aafda4a16b7be66f8d1634fc4b663721c49 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 23:13:20 +0200 Subject: [PATCH 1226/1575] erase_sector_blocking -> blocking_erase_sector --- embassy-stm32/src/flash/f0.rs | 2 +- embassy-stm32/src/flash/f3.rs | 2 +- embassy-stm32/src/flash/f4.rs | 4 ++++ embassy-stm32/src/flash/l.rs | 2 +- embassy-stm32/src/flash/other.rs | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 22e08e91a..bf601f05c 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -49,7 +49,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) wait_ready_blocking() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index e05734703..5b566a62f 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -49,7 +49,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) wait_ready_blocking() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { pac::FLASH.cr().modify(|w| { w.set_per(true); }); diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 8a0e2001b..a9ebaa355 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -320,6 +320,8 @@ unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { save_data_cache_state(); + trace!("Erasing sector number {}", sector.snb()); + pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.snb()); @@ -344,6 +346,8 @@ pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Erro pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { save_data_cache_state(); + trace!("Blocking erasing sector number {}", sector.snb()); + pac::FLASH.cr().modify(|w| { w.set_ser(true); w.set_snb(sector.snb()) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index e27accb01..0a5b6301e 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -70,7 +70,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) wait_ready_blocking() } -pub(crate) unsafe fn erase_sector_blocking(sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { #[cfg(any(flash_l0, flash_l1))] { pac::FLASH.pecr().modify(|w| { diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index 73266177f..c007f1178 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -27,7 +27,7 @@ pub(crate) unsafe fn disable_blocking_write() { pub(crate) unsafe fn blocking_write(_start_address: u32, _buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { unimplemented!(); } -pub(crate) unsafe fn erase_sector_blocking(_sector: &FlashSector) -> Result<(), Error> { +pub(crate) unsafe fn blocking_erase_sector(_sector: &FlashSector) -> Result<(), Error> { unimplemented!(); } pub(crate) unsafe fn clear_all_err() { From e08267df54a78106f4c03958346c81ae7e169bd6 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Thu, 25 May 2023 23:51:10 +0200 Subject: [PATCH 1227/1575] Move new async to asynch module to guard for models without flash interrupt --- embassy-stm32/src/flash/asynch.rs | 31 ++++++++++++++++++++++++++++++ embassy-stm32/src/flash/common.rs | 32 ++----------------------------- embassy-stm32/src/flash/f0.rs | 4 ---- embassy-stm32/src/flash/f3.rs | 4 ---- embassy-stm32/src/flash/f7.rs | 4 ---- embassy-stm32/src/flash/h7.rs | 4 ---- embassy-stm32/src/flash/l.rs | 4 ---- embassy-stm32/src/flash/mod.rs | 4 +++- embassy-stm32/src/flash/other.rs | 4 ---- 9 files changed, 36 insertions(+), 55 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index de53f6c8f..6ad1e90d4 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,5 +1,9 @@ +use core::marker::PhantomData; + use atomic_polyfill::{fence, Ordering}; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; @@ -7,10 +11,28 @@ use super::{ blocking_read, ensure_sector_aligned, family, get_sector, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, }; +use crate::peripherals::FLASH; +use crate::{interrupt, Peripheral}; pub(super) static REGION_ACCESS: Mutex = Mutex::new(()); impl<'d> Flash<'d, Async> { + pub fn new( + p: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding + 'd, + ) -> Self { + into_ref!(p); + + let flash_irq = unsafe { crate::interrupt::FLASH::steal() }; + flash_irq.unpend(); + flash_irq.enable(); + + Self { + inner: p, + _mode: PhantomData, + } + } + pub fn into_regions(self) -> FlashLayout<'d, Async> { family::set_default_layout(); FlashLayout::new(self.inner) @@ -25,6 +47,15 @@ impl<'d> Flash<'d, Async> { } } +/// Interrupt handler +pub struct InterruptHandler; + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + family::on_interrupt(); + } +} + impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_, Async> { const READ_SIZE: usize = READ_SIZE; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index c190eaf9a..cc0e2572b 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use atomic_polyfill::{fence, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; @@ -11,29 +10,11 @@ use super::{ READ_SIZE, WRITE_SIZE, }; use crate::peripherals::FLASH; -use crate::{interrupt, Peripheral}; +use crate::Peripheral; pub struct Flash<'d, MODE = Async> { pub(crate) inner: PeripheralRef<'d, FLASH>, - _mode: PhantomData, -} - -impl<'d> Flash<'d, Async> { - pub fn new( - p: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding + 'd, - ) -> Self { - into_ref!(p); - - let flash_irq = unsafe { crate::interrupt::FLASH::steal() }; - flash_irq.unpend(); - flash_irq.enable(); - - Self { - inner: p, - _mode: PhantomData, - } - } + pub(crate) _mode: PhantomData, } impl<'d> Flash<'d, Blocking> { @@ -74,15 +55,6 @@ impl<'d, MODE> Flash<'d, MODE> { } } -/// Interrupt handler -pub struct InterruptHandler; - -impl interrupt::Handler for InterruptHandler { - unsafe fn on_interrupt() { - family::on_interrupt(); - } -} - pub(super) fn blocking_read(base: u32, size: u32, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { if offset + bytes.len() as u32 > size { return Err(Error::Size); diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index bf601f05c..e9916d14b 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -13,10 +13,6 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt() { - unimplemented!(); -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 5b566a62f..4e65f5580 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -13,10 +13,6 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt() { - unimplemented!(); -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index f0c6bf81d..e6267e178 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -12,10 +12,6 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt() { - unimplemented!(); -} - pub(crate) unsafe fn lock() { pac::FLASH.cr().modify(|w| w.set_lock(true)); } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index ee824ed2b..d09ebc0dd 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -17,10 +17,6 @@ pub fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt() { - unimplemented!(); -} - pub(crate) unsafe fn lock() { pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); if is_dual_bank() { diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 0a5b6301e..c4bbd5477 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -12,10 +12,6 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt() { - unimplemented!(); -} - pub(crate) unsafe fn lock() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_lock(true)); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 02f6c5320..2cc92f518 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,10 +1,12 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; #[cfg(all(feature = "nightly", flash_f4))] -pub mod asynch; +mod asynch; #[cfg(flash)] mod common; +#[cfg(all(feature = "nightly", flash_f4))] +pub use asynch::InterruptHandler; #[cfg(flash)] pub use common::*; diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index c007f1178..ccdcfeb7a 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -8,10 +8,6 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -pub(crate) unsafe fn on_interrupt() { - unimplemented!(); -} - pub(crate) unsafe fn lock() { unimplemented!(); } From 9115431d358628f2d0ff9f9b5d017e2f3b23472b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 00:12:22 +0200 Subject: [PATCH 1228/1575] Move nightly guard and clear data cache reset bit --- embassy-stm32/src/flash/f4.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index a9ebaa355..35548a558 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -236,6 +236,7 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } +#[cfg(feature = "nightly")] pub(crate) unsafe fn on_interrupt() { // Clear IRQ flags pac::FLASH.sr().write(|w| { @@ -243,7 +244,6 @@ pub(crate) unsafe fn on_interrupt() { w.set_eop(true); }); - #[cfg(feature = "nightly")] WAKER.wake(); } @@ -436,6 +436,7 @@ fn restore_data_cache_state() { unsafe { // Reset data cache before we enable it again pac::FLASH.acr().modify(|w| w.set_dcrst(true)); + pac::FLASH.acr().modify(|w| w.set_dcrst(false)); pac::FLASH.acr().modify(|w| w.set_dcen(true)) }; } From 35d8edbc4104be16d1b1437b32a135a411b2d0e1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 00:31:41 +0200 Subject: [PATCH 1229/1575] nightly guard async traits only --- embassy-stm32/src/flash/asynch.rs | 12 ++++++++---- embassy-stm32/src/flash/f4.rs | 11 +---------- embassy-stm32/src/flash/mod.rs | 4 ++-- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 6ad1e90d4..a0d994956 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -9,7 +9,7 @@ use embassy_sync::mutex::Mutex; use super::{ blocking_read, ensure_sector_aligned, family, get_sector, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, - MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, + WRITE_SIZE, }; use crate::peripherals::FLASH; use crate::{interrupt, Peripheral}; @@ -56,8 +56,9 @@ impl interrupt::Handler for InterruptHandler { } } +#[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_, Async> { - const READ_SIZE: usize = READ_SIZE; + const READ_SIZE: usize = super::READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { self.read(offset, bytes) @@ -68,9 +69,10 @@ impl embedded_storage_async::nor_flash::ReadNorFlash for Flash<'_, Async> { } } +#[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::NorFlash for Flash<'_, Async> { const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = MAX_ERASE_SIZE; + const ERASE_SIZE: usize = super::MAX_ERASE_SIZE; async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { self.write(offset, bytes).await @@ -157,8 +159,9 @@ foreach_flash_region! { } } + #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::ReadNorFlash for crate::_generated::flash_regions::$type_name<'_, Async> { - const READ_SIZE: usize = READ_SIZE; + const READ_SIZE: usize = super::READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { self.read(offset, bytes).await @@ -169,6 +172,7 @@ foreach_flash_region! { } } + #[cfg(feature = "nightly")] impl embedded_storage_async::nor_flash::NorFlash for crate::_generated::flash_regions::$type_name<'_, Async> { const WRITE_SIZE: usize = $write_size; const ERASE_SIZE: usize = $erase_size; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 35548a558..dd7a04aa2 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -3,7 +3,6 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; use atomic_polyfill::AtomicBool; -#[cfg(feature = "nightly")] use embassy_sync::waitqueue::AtomicWaker; use pac::flash::regs::Sr; use pac::FLASH_SIZE; @@ -20,7 +19,7 @@ mod alt_regions { use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; - #[cfg(feature = "nightly")] + use crate::flash::asynch; use crate::flash::{Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; @@ -114,7 +113,6 @@ mod alt_regions { } } - #[cfg(feature = "nightly")] impl $type_name<'_, Async> { pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { self.blocking_read(offset, bytes) @@ -185,7 +183,6 @@ mod alt_regions { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub use alt_regions::*; -#[cfg(feature = "nightly")] static WAKER: AtomicWaker = AtomicWaker::new(); static DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false); @@ -236,7 +233,6 @@ pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS } -#[cfg(feature = "nightly")] pub(crate) unsafe fn on_interrupt() { // Clear IRQ flags pac::FLASH.sr().write(|w| { @@ -256,7 +252,6 @@ pub(crate) unsafe fn unlock() { pac::FLASH.keyr().write(|w| w.set_key(0xCDEF89AB)); } -#[cfg(feature = "nightly")] pub(crate) unsafe fn enable_write() { assert_eq!(0, WRITE_SIZE % 4); save_data_cache_state(); @@ -269,7 +264,6 @@ pub(crate) unsafe fn enable_write() { }); } -#[cfg(feature = "nightly")] pub(crate) unsafe fn disable_write() { pac::FLASH.cr().write(|w| { w.set_pg(false); @@ -294,7 +288,6 @@ pub(crate) unsafe fn disable_blocking_write() { restore_data_cache_state(); } -#[cfg(feature = "nightly")] pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { write_start(start_address, buf); wait_ready().await @@ -316,7 +309,6 @@ unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { } } -#[cfg(feature = "nightly")] pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { save_data_cache_state(); @@ -372,7 +364,6 @@ pub(crate) unsafe fn clear_all_err() { }); } -#[cfg(feature = "nightly")] pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { use core::task::Poll; diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 2cc92f518..f44ef2c16 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,11 +1,11 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; -#[cfg(all(feature = "nightly", flash_f4))] +#[cfg(flash_f4)] mod asynch; #[cfg(flash)] mod common; -#[cfg(all(feature = "nightly", flash_f4))] +#[cfg(flash_f4)] pub use asynch::InterruptHandler; #[cfg(flash)] pub use common::*; From d82ba4af8a826b5e8f8273b6f314e9c799a0b99c Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 00:35:53 +0200 Subject: [PATCH 1230/1575] WHY does format on save not work --- embassy-stm32/src/flash/f4.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index dd7a04aa2..25d96ca1e 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -19,9 +19,7 @@ mod alt_regions { use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; - - use crate::flash::asynch; - use crate::flash::{Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; + use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { From 307f2365da9b24fac2a0c312106a5f9986c4b5ea Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 04:53:43 +0200 Subject: [PATCH 1231/1575] Fix blocking example --- examples/stm32f4/src/bin/flash.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32f4/src/bin/flash.rs b/examples/stm32f4/src/bin/flash.rs index 4a77595cc..93c54e943 100644 --- a/examples/stm32f4/src/bin/flash.rs +++ b/examples/stm32f4/src/bin/flash.rs @@ -4,7 +4,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Blocking, Flash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) { test_flash(&mut f, (2048 - 128) * 1024, 128 * 1024); } -fn test_flash(f: &mut Flash, offset: u32, size: u32) { +fn test_flash(f: &mut Flash<'_, Blocking>, offset: u32, size: u32) { info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); info!("Reading..."); From 2ccf9f3abd6c8ccde0e56f97cbe65e5fb4bc32ce Mon Sep 17 00:00:00 2001 From: goueslati Date: Fri, 26 May 2023 09:56:55 +0100 Subject: [PATCH 1232/1575] stm32/ipcc: static methods for IPCC --- embassy-stm32/src/ipcc.rs | 66 +++++++---------- embassy-stm32/src/tl_mbox/ble.rs | 12 +-- embassy-stm32/src/tl_mbox/evt.rs | 5 +- embassy-stm32/src/tl_mbox/mm.rs | 14 ++-- embassy-stm32/src/tl_mbox/mod.rs | 90 ++++++++++------------- embassy-stm32/src/tl_mbox/shci.rs | 6 +- embassy-stm32/src/tl_mbox/sys.rs | 20 ++--- examples/stm32wb/src/bin/tl_mbox.rs | 8 +- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 21 +++--- 9 files changed, 104 insertions(+), 138 deletions(-) diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 2b9caf8e5..ea33b32c7 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,5 +1,3 @@ -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; - use crate::ipcc::sealed::Instance; use crate::peripherals::IPCC; use crate::rcc::sealed::RccPeripheral; @@ -29,22 +27,10 @@ pub(crate) mod sealed { } } -pub struct Ipcc<'d> { - _peri: PeripheralRef<'d, IPCC>, -} +pub(crate) struct Ipcc; -impl<'d> Ipcc<'d> { - pub fn new(peri: impl Peripheral

+ 'd, _config: Config) -> Self { - Self::new_inner(peri) - } - - pub(crate) fn new_inner(peri: impl Peripheral

+ 'd) -> Self { - into_ref!(peri); - - Self { _peri: peri } - } - - pub fn init(&mut self) { +impl Ipcc { + pub(crate) fn init(_config: Config) { IPCC::enable(); IPCC::reset(); IPCC::set_cpu2(true); @@ -61,56 +47,60 @@ impl<'d> Ipcc<'d> { } } - pub fn c1_set_rx_channel(&mut self, channel: IpccChannel, enabled: bool) { + pub(crate) fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } } - pub fn c1_get_rx_channel(&self, channel: IpccChannel) -> bool { + pub(crate) fn c1_get_rx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { !regs.cpu(0).mr().read().chom(channel as usize) } } - pub fn c2_set_rx_channel(&mut self, channel: IpccChannel, enabled: bool) { + #[allow(dead_code)] + pub(crate) fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } } - pub fn c2_get_rx_channel(&self, channel: IpccChannel) -> bool { + #[allow(dead_code)] + pub(crate) fn c2_get_rx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { !regs.cpu(1).mr().read().chom(channel as usize) } } - pub fn c1_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { + pub(crate) fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } } - pub fn c1_get_tx_channel(&self, channel: IpccChannel) -> bool { + pub(crate) fn c1_get_tx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) } } - pub fn c2_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { + #[allow(dead_code)] + pub(crate) fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } } - pub fn c2_get_tx_channel(&self, channel: IpccChannel) -> bool { + #[allow(dead_code)] + pub(crate) fn c2_get_tx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled @@ -118,53 +108,51 @@ impl<'d> Ipcc<'d> { } /// clears IPCC receive channel status for CPU1 - pub fn c1_clear_flag_channel(&mut self, channel: IpccChannel) { + pub(crate) fn c1_clear_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } } + #[allow(dead_code)] /// clears IPCC receive channel status for CPU2 - pub fn c2_clear_flag_channel(&mut self, channel: IpccChannel) { + pub(crate) fn c2_clear_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) } } - pub fn c1_set_flag_channel(&mut self, channel: IpccChannel) { + pub(crate) fn c1_set_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } } - pub fn c2_set_flag_channel(&mut self, channel: IpccChannel) { + #[allow(dead_code)] + pub(crate) fn c2_set_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) } } - pub fn c1_is_active_flag(&self, channel: IpccChannel) -> bool { + pub(crate) fn c1_is_active_flag(channel: IpccChannel) -> bool { let regs = IPCC::regs(); unsafe { regs.cpu(0).sr().read().chf(channel as usize) } } - pub fn c2_is_active_flag(&self, channel: IpccChannel) -> bool { + pub(crate) fn c2_is_active_flag(channel: IpccChannel) -> bool { let regs = IPCC::regs(); unsafe { regs.cpu(1).sr().read().chf(channel as usize) } } - pub fn is_tx_pending(&self, channel: IpccChannel) -> bool { - !self.c1_is_active_flag(channel) && self.c1_get_tx_channel(channel) + pub(crate) fn is_tx_pending(channel: IpccChannel) -> bool { + !Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel) } - pub fn is_rx_pending(&self, channel: IpccChannel) -> bool { - self.c2_is_active_flag(channel) && self.c1_get_rx_channel(channel) - } - - pub fn as_mut_ptr(&self) -> *mut Self { - unsafe { &mut core::ptr::read(self) as *mut _ } + pub(crate) fn is_rx_pending(channel: IpccChannel) -> bool { + Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel) } } diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index d8bf14d4f..5cc0bb200 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -16,7 +16,7 @@ use crate::tl_mbox::cmd::CmdPacket; pub struct Ble; impl Ble { - pub(crate) fn new(ipcc: &mut Ipcc) -> Self { + pub(crate) fn new() -> Self { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); @@ -28,12 +28,12 @@ impl Ble { }); } - ipcc.c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); + Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); Ble } - pub(crate) fn evt_handler(ipcc: &mut Ipcc) { + pub(crate) fn evt_handler() { unsafe { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; @@ -48,10 +48,10 @@ impl Ble { } } - ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); + Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); } - pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { + pub(crate) fn send_cmd(buf: &[u8]) { unsafe { let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial; @@ -63,6 +63,6 @@ impl Ble { cmd_packet.cmd_serial.ty = TlPacketType::BleCmd as u8; } - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); } } diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs index 770133f29..04feb3e68 100644 --- a/embassy-stm32/src/tl_mbox/evt.rs +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -131,9 +131,6 @@ impl EvtBox { impl Drop for EvtBox { fn drop(&mut self) { - use crate::ipcc::Ipcc; - - let mut ipcc = Ipcc::new_inner(unsafe { crate::Peripherals::steal() }.IPCC); - mm::MemoryManager::evt_drop(self.ptr, &mut ipcc); + mm::MemoryManager::evt_drop(self.ptr); } } diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs index f99ffa399..6e0818cf1 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -30,27 +30,27 @@ impl MemoryManager { MemoryManager } - pub fn evt_handler(ipcc: &mut Ipcc) { - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); + pub fn evt_handler() { + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); Self::send_free_buf(); - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); } - pub fn evt_drop(evt: *mut EvtPacket, ipcc: &mut Ipcc) { + pub fn evt_drop(evt: *mut EvtPacket) { unsafe { let list_node = evt.cast(); LinkedListNode::remove_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); } - let channel_is_busy = ipcc.c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); // postpone event buffer freeing to IPCC interrupt handler if channel_is_busy { - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); } else { Self::send_free_buf(); - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); } } diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index dc6104cc3..1c927efa0 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -1,6 +1,7 @@ use core::mem::MaybeUninit; use bit_field::BitField; +use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -12,7 +13,7 @@ use self::shci::{shci_ble_init, ShciBleInitCmdParam}; use self::sys::Sys; use self::unsafe_linked_list::LinkedListNode; use crate::interrupt; -use crate::ipcc::Ipcc; +use crate::ipcc::{Config, Ipcc}; mod ble; mod channels; @@ -58,13 +59,30 @@ pub struct FusInfoTable { pub struct ReceiveInterruptHandler {} impl interrupt::Handler for ReceiveInterruptHandler { - unsafe fn on_interrupt() {} + unsafe fn on_interrupt() { + if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { + sys::Sys::evt_handler(); + } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { + ble::Ble::evt_handler(); + } else { + todo!() + } + } } pub struct TransmitInterruptHandler {} impl interrupt::Handler for TransmitInterruptHandler { - unsafe fn on_interrupt() {} + unsafe fn on_interrupt() { + if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { + // TODO: handle this case + let _ = sys::Sys::cmd_evt_handler(); + } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) { + mm::MemoryManager::evt_handler(); + } else { + todo!() + } + } } /// # Version @@ -298,9 +316,9 @@ pub struct TlMbox { impl TlMbox { /// initializes low-level transport between CPU1 and BLE stack on CPU2 pub fn init( - ipcc: &mut Ipcc, _irqs: impl interrupt::Binding + interrupt::Binding, + config: Config, ) -> TlMbox { unsafe { TL_REF_TABLE = MaybeUninit::new(RefTable { @@ -336,29 +354,18 @@ impl TlMbox { HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed(); } - ipcc.init(); + Ipcc::init(config); - let _sys = Sys::new(ipcc); - let _ble = Ble::new(ipcc); + let _sys = Sys::new(); + let _ble = Ble::new(); let _mm = MemoryManager::new(); - // rx_irq.disable(); - // tx_irq.disable(); - // - // rx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); - // tx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); - // - // rx_irq.set_handler(|ipcc| { - // let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; - // Self::interrupt_ipcc_rx_handler(ipcc); - // }); - // tx_irq.set_handler(|ipcc| { - // let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; - // Self::interrupt_ipcc_tx_handler(ipcc); - // }); - // - // rx_irq.enable(); - // tx_irq.enable(); + // enable interrupts + unsafe { crate::interrupt::IPCC_C1_RX::steal() }.unpend(); + unsafe { crate::interrupt::IPCC_C1_TX::steal() }.unpend(); + + unsafe { crate::interrupt::IPCC_C1_RX::steal() }.enable(); + unsafe { crate::interrupt::IPCC_C1_TX::steal() }.enable(); TlMbox { _sys, _ble, _mm } } @@ -374,42 +381,19 @@ impl TlMbox { } } - pub fn shci_ble_init(&self, ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { - shci_ble_init(ipcc, param); + pub fn shci_ble_init(&self, param: ShciBleInitCmdParam) { + shci_ble_init(param); } - pub fn send_ble_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { - ble::Ble::send_cmd(ipcc, buf); + pub fn send_ble_cmd(&self, buf: &[u8]) { + ble::Ble::send_cmd(buf); } - // pub fn send_sys_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { - // sys::Sys::send_cmd(ipcc, buf); + // pub fn send_sys_cmd(&self, buf: &[u8]) { + // sys::Sys::send_cmd(buf); // } pub async fn read(&self) -> EvtBox { TL_CHANNEL.recv().await } - - #[allow(dead_code)] - fn interrupt_ipcc_rx_handler(ipcc: &mut Ipcc) { - if ipcc.is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { - sys::Sys::evt_handler(ipcc); - } else if ipcc.is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { - ble::Ble::evt_handler(ipcc); - } else { - todo!() - } - } - - #[allow(dead_code)] - fn interrupt_ipcc_tx_handler(ipcc: &mut Ipcc) { - if ipcc.is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { - // TODO: handle this case - let _ = sys::Sys::cmd_evt_handler(ipcc); - } else if ipcc.is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) { - mm::MemoryManager::evt_handler(ipcc); - } else { - todo!() - } - } } diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32/src/tl_mbox/shci.rs index 61fd9e4a3..3d7e994ae 100644 --- a/embassy-stm32/src/tl_mbox/shci.rs +++ b/embassy-stm32/src/tl_mbox/shci.rs @@ -76,7 +76,7 @@ pub struct ShciBleInitCmdPacket { param: ShciBleInitCmdParam, } -pub fn shci_ble_init(ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { +pub fn shci_ble_init(param: ShciBleInitCmdParam) { let mut packet = ShciBleInitCmdPacket { header: ShciHeader::default(), param, @@ -95,7 +95,7 @@ pub fn shci_ble_init(ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { cmd_buf.cmd_serial.ty = TlPacketType::SysCmd as u8; - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); } } diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index 31ebde721..79e257148 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -12,7 +12,7 @@ use crate::ipcc::Ipcc; pub struct Sys; impl Sys { - pub(crate) fn new(ipcc: &mut Ipcc) -> Self { + pub(crate) fn new() -> Self { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); @@ -22,12 +22,12 @@ impl Sys { }); } - ipcc.c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); + Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); Sys } - pub(crate) fn evt_handler(ipcc: &mut Ipcc) { + pub(crate) fn evt_handler() { unsafe { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; @@ -43,11 +43,11 @@ impl Sys { } } - ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); + Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); } - pub(crate) fn cmd_evt_handler(ipcc: &mut Ipcc) -> CcEvt { - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); + pub(crate) fn cmd_evt_handler() -> CcEvt { + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); // ST's command response data structure is really convoluted. // @@ -68,11 +68,11 @@ impl Sys { } #[allow(dead_code)] - pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { + pub(crate) fn send_cmd(buf: &[u8]) { unsafe { // TODO: check this let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; - let cmd_serial: *mut CmdSerial = &mut (*cmd_buffer).cmd_serial; + let cmd_serial: *mut CmdSerial = &mut cmd_buffer.cmd_serial; let cmd_serial_buf = cmd_serial.cast(); core::ptr::copy(buf.as_ptr(), cmd_serial_buf, buf.len()); @@ -80,8 +80,8 @@ impl Sys { let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; cmd_packet.cmd_serial.ty = TlPacketType::SysCmd as u8; - ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); } } } diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 326e4be85..18d93a279 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::ipcc::Config; use embassy_stm32::tl_mbox::TlMbox; use embassy_stm32::{bind_interrupts, tl_mbox}; use embassy_time::{Duration, Timer}; @@ -41,13 +41,11 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let _p = embassy_stm32::init(Default::default()); info!("Hello World!"); let config = Config::default(); - let mut ipcc = Ipcc::new(p.IPCC, config); - - let mbox = TlMbox::init(&mut ipcc, Irqs); + let mbox = TlMbox::init(Irqs, config); loop { let wireless_fw_info = mbox.wireless_fw_info(); diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 7a69f26b7..41c450a53 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::ipcc::Config; use embassy_stm32::tl_mbox::TlMbox; use embassy_stm32::{bind_interrupts, tl_mbox}; use {defmt_rtt as _, panic_probe as _}; @@ -40,16 +40,11 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let _p = embassy_stm32::init(Default::default()); info!("Hello World!"); let config = Config::default(); - let mut ipcc = Ipcc::new(p.IPCC, config); - - let mbox = TlMbox::init(&mut ipcc, Irqs); - - // initialize ble stack, does not return a response - mbox.shci_ble_init(&mut ipcc, Default::default()); + let mbox = TlMbox::init(Irqs, config); info!("waiting for coprocessor to boot"); let event_box = mbox.read().await; @@ -74,10 +69,11 @@ async fn main(_spawner: Spawner) { ); } - mbox.shci_ble_init(&mut ipcc, Default::default()); + // initialize ble stack, does not return a response + mbox.shci_ble_init(Default::default()); info!("resetting BLE"); - mbox.send_ble_cmd(&mut ipcc, &[0x01, 0x03, 0x0c, 0x00, 0x00]); + mbox.send_ble_cmd(&[0x01, 0x03, 0x0c, 0x00, 0x00]); let event_box = mbox.read().await; @@ -92,7 +88,10 @@ async fn main(_spawner: Spawner) { info!( "==> kind: {:#04x}, code: {:#04x}, payload_length: {}, payload: {:#04x}", - kind, code, payload_len, payload + kind, + code, + payload_len, + payload[3..] ); loop {} From 984cd47b417a7bab986ff11b589b88fcae2940b9 Mon Sep 17 00:00:00 2001 From: goueslati Date: Fri, 26 May 2023 10:03:01 +0100 Subject: [PATCH 1233/1575] stm32/ipcc: update test --- tests/stm32/src/bin/ble.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/ble.rs index aedf9a380..db5cc611d 100644 --- a/tests/stm32/src/bin/ble.rs +++ b/tests/stm32/src/bin/ble.rs @@ -7,24 +7,24 @@ #[path = "../example_common.rs"] mod example_common; use embassy_executor::Spawner; -use embassy_stm32::interrupt; -use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::ipcc::Config; use embassy_stm32::tl_mbox::TlMbox; +use embassy_stm32::{bind_interrupts, tl_mbox}; use embassy_time::{Duration, Timer}; use example_common::*; +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; + IPCC_C1_TX => tl_mbox::TransmitInterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(config()); + let _p = embassy_stm32::init(config()); info!("Hello World!"); let config = Config::default(); - let mut ipcc = Ipcc::new(p.IPCC, config); - - let rx_irq = interrupt::take!(IPCC_C1_RX); - let tx_irq = interrupt::take!(IPCC_C1_TX); - - let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq); + let mbox = TlMbox::init(Irqs, config); loop { let wireless_fw_info = mbox.wireless_fw_info(); From 66304a102d628f2e61f9e38bb95eb11a2663335d Mon Sep 17 00:00:00 2001 From: goueslati Date: Fri, 26 May 2023 11:26:58 +0100 Subject: [PATCH 1234/1575] Revert "Merge branch 'tl_mbox' into ipcc" This reverts commit 859e539f85c7f4770050cc11f83fe1f6d040cd1d, reversing changes made to 984cd47b417a7bab986ff11b589b88fcae2940b9. --- embassy-stm32/Cargo.toml | 10 -- embassy-stm32/src/tl_mbox/ble.rs | 11 -- embassy-stm32/src/tl_mbox/channels.rs | 144 ++++++++-------------- embassy-stm32/src/tl_mbox/mac_802_15_4.rs | 82 ------------ embassy-stm32/src/tl_mbox/mm.rs | 2 +- embassy-stm32/src/tl_mbox/mod.rs | 92 ++++---------- embassy-stm32/src/tl_mbox/sys.rs | 1 + 7 files changed, 75 insertions(+), 267 deletions(-) delete mode 100644 embassy-stm32/src/tl_mbox/mac_802_15_4.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 653f328c4..0d7e03bf5 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -110,16 +110,6 @@ unstable-pac = [] # Implement embedded-hal-async traits if `nightly` is set as well. unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] -# stm32wb specific -# support for wireless stacks -ble = [] -thread = [] -lld-tests = [] -ble-lld = [] -mac-802_15_4 = [] -zigbee = [] -traces = [] - # Chip-selection features stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index 0699d1867..5cc0bb200 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -65,15 +65,4 @@ impl Ble { Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); } - - pub(crate) fn send_acl_data() { - unsafe { - (*(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer) - .acl_data_serial - .ty = TlPacketType::AclData as u8; - } - - Ipcc::c1_set_flag_channel(channels::Cpu1Channel::HciAclData.into()); - Ipcc::c1_set_tx_channel(channels::Cpu1Channel::HciAclData.into(), true); - } } diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs index 94e97230f..aaa6ce177 100644 --- a/embassy-stm32/src/tl_mbox/channels.rs +++ b/embassy-stm32/src/tl_mbox/channels.rs @@ -49,104 +49,56 @@ //! | | //! -use crate::ipcc::IpccChannel; +pub mod cpu1 { + use crate::ipcc::IpccChannel; -pub enum Cpu1Channel { - BleCmd, - SystemCmdRsp, - #[cfg(feature = "thread")] - ThreadOtCmdRsp, - #[cfg(feature = "zigbee")] - ZigbeeCmdAppli, - MmReleaseBuffer, - #[cfg(feature = "mac-802_15_4")] - Mac802_15_4cmdRsp, - #[cfg(feature = "thread")] - ThreadCliCmd, - #[cfg(feature = "lld-tests")] - LldTestsCliCmd, - #[cfg(feature = "ble-lld")] - BleLldCmd, - HciAclData, + // Not used currently but reserved + pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; + // Not used currently but reserved + pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; + // Not used currently but reserved + pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6; } -impl From for IpccChannel { - fn from(value: Cpu1Channel) -> Self { - match value { - Cpu1Channel::BleCmd => IpccChannel::Channel1, - Cpu1Channel::SystemCmdRsp => IpccChannel::Channel2, - #[cfg(feature = "thread")] - Cpu1Channel::ThreadOtCmdRsp => IpccChannel::Channel3, - #[cfg(feature = "zigbee")] - Cpu1Channel::ZigbeeCmdAppli => IpccChannel::Channel3, - #[cfg(feature = "mac-802_15_4")] - Cpu1Channel::Mac802_15_4cmdRsp => IpccChannel::Channel3, - Cpu1Channel::MmReleaseBuffer => IpccChannel::Channel4, - #[cfg(feature = "thread")] - Cpu1Channel::ThreadCliCmd => IpccChannel::Channel5, - #[cfg(feature = "lld-tests")] - Cpu1Channel::LldTestsCliCmd => IpccChannel::Channel5, - #[cfg(feature = "ble-lld")] - Cpu1Channel::BleLldCmd => IpccChannel::Channel5, - Cpu1Channel::HciAclData => IpccChannel::Channel6, - } - } -} +pub mod cpu2 { + use crate::ipcc::IpccChannel; -pub enum Cpu2Channel { - BleEvent, - SystemEvent, - #[cfg(feature = "thread")] - ThreadNotifAck, - #[cfg(feature = "zigbee")] - ZigbeeAppliNotifAck, - #[cfg(feature = "mac-802_15_4")] - Mac802_15_4NotifAck, - #[cfg(feature = "lld-tests")] - LldTestsM0Cmd, - #[cfg(feature = "ble-lld")] - BleLldM0Cmd, - #[cfg(feature = "traces")] - Traces, - #[cfg(feature = "thread")] - ThreadCliNotifAck, - #[cfg(feature = "lld-tests")] - LldTestsCliRsp, - #[cfg(feature = "ble-lld")] - BleLldCliRsp, - #[cfg(feature = "ble-lld")] - BleLldRsp, - #[cfg(feature = "zigbee")] - ZigbeeM0Request, -} - -impl From for IpccChannel { - fn from(value: Cpu2Channel) -> Self { - match value { - Cpu2Channel::BleEvent => IpccChannel::Channel1, - Cpu2Channel::SystemEvent => IpccChannel::Channel2, - #[cfg(feature = "thread")] - Cpu2Channel::ThreadNotifAck => IpccChannel::Channel3, - #[cfg(feature = "zigbee")] - Cpu2Channel::ZigbeeAppliNotifAck => IpccChannel::Channel3, - #[cfg(feature = "mac-802_15_4")] - Cpu2Channel::Mac802_15_4NotifAck => IpccChannel::Channel3, - #[cfg(feature = "lld-tests")] - Cpu2Channel::LldTestsM0Cmd => IpccChannel::Channel3, - #[cfg(feature = "ble-lld")] - Cpu2Channel::BleLldM0Cmd => IpccChannel::Channel3, - #[cfg(feature = "traces")] - Cpu2Channel::Traces => IpccChannel::Channel4, - #[cfg(feature = "thread")] - Cpu2Channel::ThreadCliNotifAck => IpccChannel::Channel5, - #[cfg(feature = "lld-tests")] - Cpu2Channel::LldTestsCliRsp => IpccChannel::Channel5, - #[cfg(feature = "ble-lld")] - Cpu2Channel::BleLldCliRsp => IpccChannel::Channel5, - #[cfg(feature = "ble-lld")] - Cpu2Channel::BleLldRsp => IpccChannel::Channel5, - #[cfg(feature = "zigbee")] - Cpu2Channel::ZigbeeM0Request => IpccChannel::Channel5, - } - } + pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; + pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; + #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5; } diff --git a/embassy-stm32/src/tl_mbox/mac_802_15_4.rs b/embassy-stm32/src/tl_mbox/mac_802_15_4.rs deleted file mode 100644 index 19f951130..000000000 --- a/embassy-stm32/src/tl_mbox/mac_802_15_4.rs +++ /dev/null @@ -1,82 +0,0 @@ -use core::mem::MaybeUninit; - -use embassy_futures::block_on; - -use super::cmd::{CmdPacket, CmdSerial}; -use super::consts::TlPacketType; -use super::evt::{EvtBox, EvtPacket}; -use super::unsafe_linked_list::LinkedListNode; -use super::{ - channels, Mac802_15_4Table, EVT_QUEUE, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_CHANNEL, - TL_MAC_802_15_4_TABLE, TL_REF_TABLE, -}; -use crate::ipcc::Ipcc; - -pub struct Mac802_15_4; - -impl Mac802_15_4 { - pub(crate) fn init(ipcc: &mut Ipcc) -> Self { - unsafe { - LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); - - TL_MAC_802_15_4_TABLE = MaybeUninit::new(Mac802_15_4Table { - pcmd_rsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), - pnotack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), - evt_queue: EVT_QUEUE.as_ptr().cast(), - }); - } - - ipcc.c1_set_rx_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into(), true); - - Self - } - - pub(crate) fn notif_evt_handler(ipcc: &mut Ipcc) { - unsafe { - let notif_buffer: *mut EvtPacket = (*TL_REF_TABLE.assume_init().mac_802_15_4_table).pnotack_buffer.cast(); - let event = EvtBox::new(notif_buffer); - - block_on(TL_CHANNEL.send(event)); - } - - ipcc.c1_set_rx_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into(), false); - } - - pub(crate) fn cmd_evt_handler(ipcc: &mut Ipcc) { - unsafe { - let _notif_buffer = (*TL_REF_TABLE.assume_init().mac_802_15_4_table).pcmd_rsp_buffer; - - // NOTE: ST's HAL does nothing with this buffer, ?????? - } - - ipcc.c1_set_tx_channel(channels::Cpu1Channel::Mac802_15_4cmdRsp.into(), false); - } - - pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { - unsafe { - let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().mac_802_15_4_table).pcmd_rsp_buffer.cast(); - let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial; - let pcmd_serial_buf: *mut u8 = pcmd_serial.cast(); - - core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); - - let cmd_packet: &mut CmdPacket = - &mut *(*TL_REF_TABLE.assume_init().mac_802_15_4_table).pcmd_rsp_buffer.cast(); - cmd_packet.cmd_serial.ty = TlPacketType::OtCmd as u8; - } - - ipcc.c1_set_flag_channel(channels::Cpu1Channel::Mac802_15_4cmdRsp.into()); - ipcc.c1_set_tx_channel(channels::Cpu1Channel::Mac802_15_4cmdRsp.into(), true); - } - - pub(crate) fn send_ack(ipcc: &mut Ipcc) { - // TODO - unsafe { - let packet: &mut CmdPacket = &mut *(*TL_REF_TABLE.assume_init().mac_802_15_4_table).pnotack_buffer.cast(); - packet.cmd_serial.ty = TlPacketType::OtAck as u8; - } - - ipcc.c1_clear_flag_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into()); - ipcc.c1_set_rx_channel(channels::Cpu2Channel::Mac802_15_4NotifAck.into(), true); - } -} diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs index cb161ce58..6e0818cf1 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -11,7 +11,7 @@ use crate::ipcc::Ipcc; pub struct MemoryManager; impl MemoryManager { - pub fn init() -> Self { + pub fn new() -> Self { unsafe { LinkedListNode::init_head(FREE_BUFF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index fe366f6d8..1c927efa0 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -5,12 +5,9 @@ use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; -#[cfg(feature = "ble")] use self::ble::Ble; use self::cmd::{AclDataPacket, CmdPacket}; use self::evt::{CsEvt, EvtBox}; -#[cfg(feature = "mac-802_15_4")] -use self::mac_802_15_4::Mac802_15_4; use self::mm::MemoryManager; use self::shci::{shci_ble_init, ShciBleInitCmdParam}; use self::sys::Sys; @@ -18,6 +15,7 @@ use self::unsafe_linked_list::LinkedListNode; use crate::interrupt; use crate::ipcc::{Config, Ipcc}; +mod ble; mod channels; mod cmd; mod consts; @@ -27,11 +25,6 @@ mod shci; mod sys; mod unsafe_linked_list; -#[cfg(feature = "ble")] -mod ble; -#[cfg(feature = "mac-802_15_4")] -mod mac_802_15_4; - pub type PacketHeader = LinkedListNode; const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); @@ -219,8 +212,8 @@ struct TracesTable { #[repr(C, packed)] struct Mac802_15_4Table { - pcmd_rsp_buffer: *mut u8, - pnotack_buffer: *mut u8, + pcmd_rsp_buffer: *const u8, + pnotack_buffer: *const u8, evt_queue: *const u8, } @@ -240,7 +233,6 @@ pub struct RefTable { ble_lld_table: *const BleLldTable, } -// -------------------- reference table -------------------- #[link_section = "TL_REF_TABLE"] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); @@ -274,50 +266,38 @@ static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::u #[link_section = "MB_MEM1"] static mut TL_ZIGBEE_TABLE: MaybeUninit = MaybeUninit::uninit(); -// -------------------- tables -------------------- +#[allow(dead_code)] // Not used currently but reserved #[link_section = "MB_MEM1"] static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +// not in shared RAM +static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); + #[link_section = "MB_MEM2"] static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = MaybeUninit::uninit(); -#[link_section = "MB_MEM1"] +#[link_section = "MB_MEM2"] static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -// not in shared RAM -static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); - -// -------------------- app tables -------------------- -#[link_section = "MB_MEM2"] -static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); - #[link_section = "MB_MEM2"] static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); +#[link_section = "MB_MEM2"] +static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); + #[link_section = "MB_MEM2"] static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = MaybeUninit::uninit(); -#[cfg(feature = "mac-802_15_4")] -#[link_section = "MB_MEM2"] -static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); - -#[cfg(feature = "mac-802_15_4")] -#[link_section = "MB_MEM2"] -static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = - MaybeUninit::uninit(); - -#[cfg(feature = "ble")] #[link_section = "MB_MEM2"] static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = MaybeUninit::uninit(); -#[cfg(feature = "ble")] -#[link_section = "MB_MEM1"] +#[link_section = "MB_MEM2"] static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] @@ -327,14 +307,10 @@ static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251 // TODO: get a better size, this is a placeholder pub(crate) static TL_CHANNEL: Channel = Channel::new(); -pub struct TlMbox; - -pub enum MailboxTarget { - Sys, - #[cfg(feature = "ble")] - Ble, - #[cfg(feature = "mac-802_15_4")] - Mac802_15_4, +pub struct TlMbox { + _sys: Sys, + _ble: Ble, + _mm: MemoryManager, } impl TlMbox { @@ -380,14 +356,9 @@ impl TlMbox { Ipcc::init(config); - Sys::init(); - MemoryManager::init(); - - #[cfg(feature = "ble")] - Ble::init(); - - #[cfg(feature = "mac-802_15_4")] - Mac802_15_4::init(); + let _sys = Sys::new(); + let _ble = Ble::new(); + let _mm = MemoryManager::new(); // enable interrupts unsafe { crate::interrupt::IPCC_C1_RX::steal() }.unpend(); @@ -396,7 +367,7 @@ impl TlMbox { unsafe { crate::interrupt::IPCC_C1_RX::steal() }.enable(); unsafe { crate::interrupt::IPCC_C1_TX::steal() }.enable(); - Self + TlMbox { _sys, _ble, _mm } } pub fn wireless_fw_info(&self) -> Option { @@ -410,30 +381,17 @@ impl TlMbox { } } - #[cfg(feature = "ble")] pub fn shci_ble_init(&self, param: ShciBleInitCmdParam) { shci_ble_init(param); } - pub fn send_cmd(&self, buf: &[u8], target: MailboxTarget) { - match target { - MailboxTarget::Sys => Sys::send_cmd(buf), - #[cfg(feature = "ble")] - MailboxTarget::Ble => Ble::send_cmd(buf), - #[cfg(feature = "mac-802_15_4")] - MailboxTarget::Mac802_15_4 => Mac802_15_4::send_cmd(buf), - } + pub fn send_ble_cmd(&self, buf: &[u8]) { + ble::Ble::send_cmd(buf); } - pub fn send_ack(&self, target: MailboxTarget) { - match target { - #[cfg(feature = "ble")] - MailboxTarget::Ble => Ble::send_acl_data(), - #[cfg(feature = "mac-802_15_4")] - MailboxTarget::Mac802_15_4 => Mac802_15_4::send_ack(), - MailboxTarget::Sys => { /* does nothing */ } - } - } + // pub fn send_sys_cmd(&self, buf: &[u8]) { + // sys::Sys::send_cmd(buf); + // } pub async fn read(&self) -> EvtBox { TL_CHANNEL.recv().await diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index 4ffbe8e6a..79e257148 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -70,6 +70,7 @@ impl Sys { #[allow(dead_code)] pub(crate) fn send_cmd(buf: &[u8]) { unsafe { + // TODO: check this let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; let cmd_serial: *mut CmdSerial = &mut cmd_buffer.cmd_serial; let cmd_serial_buf = cmd_serial.cast(); From 3081ecf301a54f8ed3d0f72350dd21f8ac9e1b18 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 May 2023 13:07:32 +0200 Subject: [PATCH 1235/1575] sync: do will_wake check in MultiWakerRegistration. --- embassy-sync/src/pubsub/mod.rs | 34 ++-------------- embassy-sync/src/waitqueue/multi_waker.rs | 49 +++++++++++++++++------ 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 59e701c58..6afd54af5 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -4,7 +4,7 @@ use core::cell::RefCell; use core::fmt::Debug; -use core::task::{Context, Poll, Waker}; +use core::task::{Context, Poll}; use heapless::Deque; @@ -179,7 +179,7 @@ impl { if let Some(cx) = cx { - s.register_subscriber_waker(cx.waker()); + s.subscriber_wakers.register(cx.waker()); } Poll::Pending } @@ -206,7 +206,7 @@ impl { if let Some(cx) = cx { - s.register_publisher_waker(cx.waker()); + s.publisher_wakers.register(cx.waker()); } Err(message) } @@ -335,34 +335,6 @@ impl PubSubSta Some(WaitResult::Message(message)) } - fn register_subscriber_waker(&mut self, waker: &Waker) { - match self.subscriber_wakers.register(waker) { - Ok(()) => {} - Err(_) => { - // All waker slots were full. This can only happen when there was a subscriber that now has dropped. - // We need to throw it away. It's a bit inefficient, but we can wake everything. - // Any future that is still active will simply reregister. - // This won't happen a lot, so it's ok. - self.subscriber_wakers.wake(); - self.subscriber_wakers.register(waker).unwrap(); - } - } - } - - fn register_publisher_waker(&mut self, waker: &Waker) { - match self.publisher_wakers.register(waker) { - Ok(()) => {} - Err(_) => { - // All waker slots were full. This can only happen when there was a publisher that now has dropped. - // We need to throw it away. It's a bit inefficient, but we can wake everything. - // Any future that is still active will simply reregister. - // This won't happen a lot, so it's ok. - self.publisher_wakers.wake(); - self.publisher_wakers.register(waker).unwrap(); - } - } - } - fn unregister_subscriber(&mut self, subscriber_next_message_id: u64) { self.subscriber_count -= 1; diff --git a/embassy-sync/src/waitqueue/multi_waker.rs b/embassy-sync/src/waitqueue/multi_waker.rs index 325d2cb3a..824d192da 100644 --- a/embassy-sync/src/waitqueue/multi_waker.rs +++ b/embassy-sync/src/waitqueue/multi_waker.rs @@ -1,33 +1,58 @@ use core::task::Waker; -use super::WakerRegistration; +use heapless::Vec; /// Utility struct to register and wake multiple wakers. pub struct MultiWakerRegistration { - wakers: [WakerRegistration; N], + wakers: Vec, } impl MultiWakerRegistration { /// Create a new empty instance pub const fn new() -> Self { - const WAKER: WakerRegistration = WakerRegistration::new(); - Self { wakers: [WAKER; N] } + Self { wakers: Vec::new() } } /// Register a waker. If the buffer is full the function returns it in the error - pub fn register<'a>(&mut self, w: &'a Waker) -> Result<(), &'a Waker> { - if let Some(waker_slot) = self.wakers.iter_mut().find(|waker_slot| !waker_slot.occupied()) { - waker_slot.register(w); - Ok(()) - } else { - Err(w) + pub fn register<'a>(&mut self, w: &'a Waker) { + // If we already have some waker that wakes the same task as `w`, do nothing. + // This avoids cloning wakers, and avoids unnecessary mass-wakes. + for w2 in &self.wakers { + if w.will_wake(w2) { + return; + } + } + + if self.wakers.is_full() { + // All waker slots were full. It's a bit inefficient, but we can wake everything. + // Any future that is still active will simply reregister. + // This won't happen a lot, so it's ok. + self.wake(); + } + + if self.wakers.push(w.clone()).is_err() { + // This can't happen unless N=0 + // (Either `wakers` wasn't full, or it was in which case `wake()` empied it) + panic!("tried to push a waker to a zero-length MultiWakerRegistration") } } /// Wake all registered wakers. This clears the buffer pub fn wake(&mut self) { - for waker_slot in self.wakers.iter_mut() { - waker_slot.wake() + // heapless::Vec has no `drain()`, do it unsafely ourselves... + + // First set length to 0, without dropping the contents. + // This is necessary for soundness: if wake() panics and we're using panic=unwind. + // Setting len=0 upfront ensures other code can't observe the vec in an inconsistent state. + // (it'll leak wakers, but that's not UB) + let len = self.wakers.len(); + unsafe { self.wakers.set_len(0) } + + for i in 0..len { + // Move a waker out of the vec. + let waker = unsafe { self.wakers.as_mut_ptr().add(i).read() }; + // Wake it by value, which consumes (drops) it. + waker.wake(); } } } From fee89ed7c7f79d7e70421e7f0915c13bcfdefb2f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 15:23:36 +0200 Subject: [PATCH 1236/1575] Remove ability to set alt layout - it does not work. --- embassy-stm32/src/flash/asynch.rs | 2 +- embassy-stm32/src/flash/common.rs | 2 +- embassy-stm32/src/flash/f0.rs | 4 +++- embassy-stm32/src/flash/f3.rs | 4 +++- embassy-stm32/src/flash/f4.rs | 35 ++++++++----------------------- embassy-stm32/src/flash/f7.rs | 4 +++- embassy-stm32/src/flash/h7.rs | 4 +++- embassy-stm32/src/flash/l.rs | 4 +++- embassy-stm32/src/flash/other.rs | 4 +++- 9 files changed, 29 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index a0d994956..3c3ece99a 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -34,7 +34,7 @@ impl<'d> Flash<'d, Async> { } pub fn into_regions(self) -> FlashLayout<'d, Async> { - family::set_default_layout(); + assert!(family::is_default_layout()); FlashLayout::new(self.inner) } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index cc0e2572b..6c1912900 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -30,7 +30,7 @@ impl<'d> Flash<'d, Blocking> { impl<'d, MODE> Flash<'d, MODE> { pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { - family::set_default_layout(); + assert!(family::is_default_layout()); FlashLayout::new(self.inner) } diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index e9916d14b..02bd4cc1f 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -7,7 +7,9 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub const fn set_default_layout() {} +pub const fn is_default_layout() -> bool { + true +} pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 4e65f5580..b093a7837 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -7,7 +7,9 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub const fn set_default_layout() {} +pub const fn is_default_layout() -> bool { + true +} pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 25d96ca1e..5e1fc696f 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -69,7 +69,7 @@ mod alt_regions { impl<'d> Flash<'d> { pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> { - super::set_alt_layout(); + assert!(!super::is_default_layout()); // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all async flash region operations are protected with a mutex. @@ -86,7 +86,7 @@ mod alt_regions { } pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> { - super::set_alt_layout(); + assert!(!super::is_default_layout()); // SAFETY: We never expose the cloned peripheral references, and their instance is not public. // Also, all blocking flash region operations are protected with a cs. @@ -191,38 +191,21 @@ impl FlashSector { } #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -pub fn set_default_layout() { - unsafe { - pac::FLASH.optkeyr().write(|w| w.set_optkey(0x08192A3B)); - pac::FLASH.optkeyr().write(|w| w.set_optkey(0x4C5D6E7F)); - pac::FLASH.optcr().modify(|r| { - r.set_db1m(false); - r.set_optlock(true) - }); - }; +pub(crate) fn is_default_layout() -> bool { + unsafe { !pac::FLASH.optcr().read().db1m() } } #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] -pub const fn set_default_layout() {} - -#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] -fn set_alt_layout() { - unsafe { - pac::FLASH.optkeyr().write(|w| w.set_optkey(0x08192A3B)); - pac::FLASH.optkeyr().write(|w| w.set_optkey(0x4C5D6E7F)); - pac::FLASH.optcr().modify(|r| { - r.set_db1m(true); - r.set_optlock(true) - }); - }; +pub(crate) const fn is_default_layout() -> bool { + true } #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub fn get_flash_regions() -> &'static [&'static FlashRegion] { - if unsafe { pac::FLASH.optcr().read().db1m() } { - &ALT_FLASH_REGIONS - } else { + if is_default_layout() { &FLASH_REGIONS + } else { + &ALT_FLASH_REGIONS } } diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index e6267e178..1a6d6bb03 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -6,7 +6,9 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub const fn set_default_layout() {} +pub const fn is_default_layout() -> bool { + true +} pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index d09ebc0dd..9baf059ee 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -7,7 +7,9 @@ use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub const fn set_default_layout() {} +pub const fn is_default_layout() -> bool { + true +} const fn is_dual_bank() -> bool { FLASH_REGIONS.len() == 2 diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index c4bbd5477..deefd05ed 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -6,7 +6,9 @@ use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; -pub const fn set_default_layout() {} +pub const fn is_default_layout() -> bool { + true +} pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index ccdcfeb7a..a7e8d1d57 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs @@ -2,7 +2,9 @@ use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; -pub const fn set_default_layout() {} +pub const fn is_default_layout() -> bool { + true +} pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { &FLASH_REGIONS From f501a1ba2cea0d7e4fdcdc2f289bce65f87528e3 Mon Sep 17 00:00:00 2001 From: Linus Harberg Date: Fri, 26 May 2023 16:40:10 +0200 Subject: [PATCH 1237/1575] Disable ppi group first to avoid BufferedUarte not getting dropped properly --- embassy-nrf/src/buffered_uarte.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c41d8398c..4d053c023 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -646,6 +646,8 @@ mod _embedded_io { impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarte<'a, U, T> { fn drop(&mut self) { + self._ppi_group.disable_all(); + let r = U::regs(); self.timer.stop(); From f4736457f5f6d0d72d27d5d560bf048ca2df359f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 May 2023 16:50:19 +0200 Subject: [PATCH 1238/1575] nrf/timer: use low power counter mode. The regular one permanently requests HFCLK, while the low power one only does so while counting, for 1 clock cycle. The regular mode is "deprecated" too. --- embassy-nrf/src/timer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index e9d2132c1..2a0e16a50 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -124,7 +124,7 @@ impl<'d, T: Instance> Timer<'d, T> { this.stop(); if is_counter { - regs.mode.write(|w| w.mode().counter()); + regs.mode.write(|w| w.mode().low_power_counter()); } else { regs.mode.write(|w| w.mode().timer()); } From 62e799da09e144c9bd2bc3935011913e62c86d16 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 21:40:12 +0200 Subject: [PATCH 1239/1575] Create flash partition for shared flash access --- embassy-embedded-hal/Cargo.toml | 1 + .../src/adapter/yielding_async.rs | 62 +------- .../src/{flash.rs => flash/concat_flash.rs} | 86 ++-------- embassy-embedded-hal/src/flash/mem_flash.rs | 127 +++++++++++++++ embassy-embedded-hal/src/flash/mod.rs | 9 ++ embassy-embedded-hal/src/flash/partition.rs | 150 ++++++++++++++++++ 6 files changed, 308 insertions(+), 127 deletions(-) rename embassy-embedded-hal/src/{flash.rs => flash/concat_flash.rs} (72%) create mode 100644 embassy-embedded-hal/src/flash/mem_flash.rs create mode 100644 embassy-embedded-hal/src/flash/mod.rs create mode 100644 embassy-embedded-hal/src/flash/partition.rs diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index ad2f14568..35c70bb63 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -31,4 +31,5 @@ nb = "1.0.0" defmt = { version = "0.3", optional = true } [dev-dependencies] +critical-section = { version = "1.1.1", features = ["std"] } futures-test = "0.3.17" diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs index 96d5cca8e..f51e4076f 100644 --- a/embassy-embedded-hal/src/adapter/yielding_async.rs +++ b/embassy-embedded-hal/src/adapter/yielding_async.rs @@ -167,66 +167,18 @@ mod tests { use embedded_storage_async::nor_flash::NorFlash; use super::*; - - extern crate std; - - #[derive(Default)] - struct FakeFlash(Vec<(u32, u32)>); - - impl embedded_storage::nor_flash::ErrorType for FakeFlash { - type Error = std::convert::Infallible; - } - - impl embedded_storage_async::nor_flash::ReadNorFlash for FakeFlash { - const READ_SIZE: usize = 1; - - async fn read(&mut self, _offset: u32, _bytes: &mut [u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - fn capacity(&self) -> usize { - unimplemented!() - } - } - - impl embedded_storage_async::nor_flash::NorFlash for FakeFlash { - const WRITE_SIZE: usize = 4; - const ERASE_SIZE: usize = 128; - - async fn write(&mut self, _offset: u32, _bytes: &[u8]) -> Result<(), Self::Error> { - unimplemented!() - } - - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.0.push((from, to)); - Ok(()) - } - } + use crate::flash::mem_flash::MemFlash; #[futures_test::test] async fn can_erase() { - let fake = FakeFlash::default(); - let mut yielding = YieldingAsync::new(fake); + let flash = MemFlash::<1024, 128, 4>::new(0x00); + let mut yielding = YieldingAsync::new(flash); yielding.erase(0, 256).await.unwrap(); - let fake = yielding.wrapped; - assert_eq!(2, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - } - - #[futures_test::test] - async fn can_erase_wrong_erase_size() { - let fake = FakeFlash::default(); - let mut yielding = YieldingAsync::new(fake); - - yielding.erase(0, 257).await.unwrap(); - - let fake = yielding.wrapped; - assert_eq!(3, fake.0.len()); - assert_eq!((0, 128), fake.0[0]); - assert_eq!((128, 256), fake.0[1]); - assert_eq!((256, 257), fake.0[2]); + let flash = yielding.wrapped; + assert_eq!(2, flash.erases.len()); + assert_eq!((0, 128), flash.erases[0]); + assert_eq!((128, 256), flash.erases[1]); } } diff --git a/embassy-embedded-hal/src/flash.rs b/embassy-embedded-hal/src/flash/concat_flash.rs similarity index 72% rename from embassy-embedded-hal/src/flash.rs rename to embassy-embedded-hal/src/flash/concat_flash.rs index 9a6e4bd92..1ea84269c 100644 --- a/embassy-embedded-hal/src/flash.rs +++ b/embassy-embedded-hal/src/flash/concat_flash.rs @@ -1,5 +1,3 @@ -//! Utilities related to flash. - use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, ReadNorFlash}; #[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; @@ -192,18 +190,21 @@ where #[cfg(test)] mod tests { - use super::*; + use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; + + use super::ConcatFlash; + use crate::flash::mem_flash::MemFlash; #[test] fn can_write_and_read_across_flashes() { - let first = MemFlash::<64, 16, 4>::new(); - let second = MemFlash::<64, 64, 4>::new(); + let first = MemFlash::<64, 16, 4>::default(); + let second = MemFlash::<64, 64, 4>::default(); let mut f = ConcatFlash::new(first, second); f.write(60, &[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]).unwrap(); - assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0 .0[60..]); - assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1 .0[0..4]); + assert_eq!(&[0x11, 0x22, 0x33, 0x44], &f.0.mem[60..]); + assert_eq!(&[0x55, 0x66, 0x77, 0x88], &f.1.mem[0..4]); let mut read_buf = [0; 8]; f.read(60, &mut read_buf).unwrap(); @@ -213,74 +214,15 @@ mod tests { #[test] fn can_erase_across_flashes() { - let mut first = MemFlash::<128, 16, 4>::new(); - let mut second = MemFlash::<128, 64, 4>::new(); - first.0.fill(0x00); - second.0.fill(0x00); - + let first = MemFlash::<128, 16, 4>::new(0x00); + let second = MemFlash::<128, 64, 4>::new(0x00); let mut f = ConcatFlash::new(first, second); f.erase(64, 192).unwrap(); - assert_eq!(&[0x00; 64], &f.0 .0[0..64]); - assert_eq!(&[0xff; 64], &f.0 .0[64..128]); - assert_eq!(&[0xff; 64], &f.1 .0[0..64]); - assert_eq!(&[0x00; 64], &f.1 .0[64..128]); - } - - pub struct MemFlash([u8; SIZE]); - - impl MemFlash { - pub const fn new() -> Self { - Self([0xff; SIZE]) - } - } - - impl ErrorType - for MemFlash - { - type Error = core::convert::Infallible; - } - - impl ReadNorFlash - for MemFlash - { - const READ_SIZE: usize = 1; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - let len = bytes.len(); - bytes.copy_from_slice(&self.0[offset as usize..offset as usize + len]); - Ok(()) - } - - fn capacity(&self) -> usize { - SIZE - } - } - - impl NorFlash - for MemFlash - { - const WRITE_SIZE: usize = WRITE_SIZE; - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let from = from as usize; - let to = to as usize; - assert_eq!(0, from % ERASE_SIZE); - assert_eq!(0, to % ERASE_SIZE); - self.0[from..to].fill(0xff); - Ok(()) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - let offset = offset as usize; - assert_eq!(0, bytes.len() % WRITE_SIZE); - assert_eq!(0, offset % WRITE_SIZE); - assert!(offset + bytes.len() <= SIZE); - - self.0[offset..offset + bytes.len()].copy_from_slice(bytes); - Ok(()) - } + assert_eq!(&[0x00; 64], &f.0.mem[0..64]); + assert_eq!(&[0xff; 64], &f.0.mem[64..128]); + assert_eq!(&[0xff; 64], &f.1.mem[0..64]); + assert_eq!(&[0x00; 64], &f.1.mem[64..128]); } } diff --git a/embassy-embedded-hal/src/flash/mem_flash.rs b/embassy-embedded-hal/src/flash/mem_flash.rs new file mode 100644 index 000000000..4e10627df --- /dev/null +++ b/embassy-embedded-hal/src/flash/mem_flash.rs @@ -0,0 +1,127 @@ +use alloc::vec::Vec; + +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; + +extern crate alloc; + +pub(crate) struct MemFlash { + pub mem: [u8; SIZE], + pub writes: Vec<(u32, usize)>, + pub erases: Vec<(u32, u32)>, +} + +impl MemFlash { + #[allow(unused)] + pub const fn new(fill: u8) -> Self { + Self { + mem: [fill; SIZE], + writes: Vec::new(), + erases: Vec::new(), + } + } + + fn read(&mut self, offset: u32, bytes: &mut [u8]) { + let len = bytes.len(); + bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); + } + + fn write(&mut self, offset: u32, bytes: &[u8]) { + self.writes.push((offset, bytes.len())); + let offset = offset as usize; + assert_eq!(0, bytes.len() % WRITE_SIZE); + assert_eq!(0, offset % WRITE_SIZE); + assert!(offset + bytes.len() <= SIZE); + + self.mem[offset..offset + bytes.len()].copy_from_slice(bytes); + } + + fn erase(&mut self, from: u32, to: u32) { + self.erases.push((from, to)); + let from = from as usize; + let to = to as usize; + assert_eq!(0, from % ERASE_SIZE); + assert_eq!(0, to % ERASE_SIZE); + self.mem[from..to].fill(0xff); + } +} + +impl Default + for MemFlash +{ + fn default() -> Self { + Self::new(0xff) + } +} + +impl ErrorType + for MemFlash +{ + type Error = core::convert::Infallible; +} + +impl ReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes); + Ok(()) + } + + fn capacity(&self) -> usize { + SIZE + } +} + +impl NorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes); + Ok(()) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to); + Ok(()) + } +} + +#[cfg(feature = "nightly")] +impl AsyncReadNorFlash + for MemFlash +{ + const READ_SIZE: usize = 1; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes); + Ok(()) + } + + fn capacity(&self) -> usize { + SIZE + } +} + +#[cfg(feature = "nightly")] +impl AsyncNorFlash + for MemFlash +{ + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes); + Ok(()) + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to); + Ok(()) + } +} diff --git a/embassy-embedded-hal/src/flash/mod.rs b/embassy-embedded-hal/src/flash/mod.rs new file mode 100644 index 000000000..c80dd6aac --- /dev/null +++ b/embassy-embedded-hal/src/flash/mod.rs @@ -0,0 +1,9 @@ +//! Utilities related to flash. + +mod concat_flash; +#[cfg(test)] +pub(crate) mod mem_flash; +mod partition; + +pub use concat_flash::ConcatFlash; +pub use partition::Partition; diff --git a/embassy-embedded-hal/src/flash/partition.rs b/embassy-embedded-hal/src/flash/partition.rs new file mode 100644 index 000000000..084425e95 --- /dev/null +++ b/embassy-embedded-hal/src/flash/partition.rs @@ -0,0 +1,150 @@ +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::mutex::Mutex; +use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind}; +#[cfg(feature = "nightly")] +use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; + +/// A logical partition of an underlying shared flash +/// +/// A partition holds an offset and a size of the flash, +/// and is restricted to operate with that range. +/// There is no guarantee that muliple partitions on the same flash +/// operate on mutually exclusive ranges - such a separation is up to +/// the user to guarantee. +pub struct Partition<'a, M: RawMutex, T> { + flash: &'a Mutex, + offset: u32, + size: u32, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + Partition, + OutOfBounds, + Flash(T), +} + +impl<'a, M: RawMutex, T> Partition<'a, M, T> { + /// Create a new partition + pub const fn new(flash: &'a Mutex, offset: u32, size: u32) -> Self { + Self { flash, offset, size } + } +} + +impl NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match self { + Error::Partition => NorFlashErrorKind::Other, + Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, + Error::Flash(f) => f.kind(), + } + } +} + +impl ErrorType for Partition<'_, M, T> { + type Error = Error; +} + +#[cfg(feature = "nightly")] +impl ReadNorFlash for Partition<'_, M, T> { + const READ_SIZE: usize = T::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if self.offset % T::READ_SIZE as u32 != 0 || self.size % T::READ_SIZE as u32 != 0 { + return Err(Error::Partition); + } + if offset + bytes.len() as u32 > self.size { + return Err(Error::OutOfBounds); + } + + let mut flash = self.flash.lock().await; + flash.read(self.offset + offset, bytes).await.map_err(Error::Flash) + } + + fn capacity(&self) -> usize { + self.size as usize + } +} + +#[cfg(feature = "nightly")] +impl NorFlash for Partition<'_, M, T> { + const WRITE_SIZE: usize = T::WRITE_SIZE; + const ERASE_SIZE: usize = T::ERASE_SIZE; + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if self.offset % T::WRITE_SIZE as u32 != 0 || self.size % T::WRITE_SIZE as u32 != 0 { + return Err(Error::Partition); + } + if offset + bytes.len() as u32 > self.size { + return Err(Error::OutOfBounds); + } + + let mut flash = self.flash.lock().await; + flash.write(self.offset + offset, bytes).await.map_err(Error::Flash) + } + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if self.offset % T::ERASE_SIZE as u32 != 0 || self.size % T::ERASE_SIZE as u32 != 0 { + return Err(Error::Partition); + } + if to > self.size { + return Err(Error::OutOfBounds); + } + + let mut flash = self.flash.lock().await; + flash + .erase(self.offset + from, self.offset + to) + .await + .map_err(Error::Flash) + } +} + +#[cfg(test)] +mod tests { + use embassy_sync::blocking_mutex::raw::NoopRawMutex; + + use super::*; + use crate::flash::mem_flash::MemFlash; + + #[futures_test::test] + async fn can_read() { + let mut flash = MemFlash::<1024, 128, 4>::default(); + flash.mem[12..20].fill(0xAA); + + let flash = Mutex::::new(flash); + let mut partition = Partition::new(&flash, 8, 12); + + let mut read_buf = [0; 8]; + partition.read(4, &mut read_buf).await.unwrap(); + + assert!(read_buf.iter().position(|&x| x != 0xAA).is_none()); + } + + #[futures_test::test] + async fn can_write() { + let flash = MemFlash::<1024, 128, 4>::default(); + + let flash = Mutex::::new(flash); + let mut partition = Partition::new(&flash, 8, 12); + + let write_buf = [0xAA; 8]; + partition.write(4, &write_buf).await.unwrap(); + + let flash = flash.try_lock().unwrap(); + assert!(flash.mem[12..20].iter().position(|&x| x != 0xAA).is_none()); + } + + #[futures_test::test] + async fn can_erase() { + let flash = MemFlash::<1024, 128, 4>::new(0x00); + + let flash = Mutex::::new(flash); + let mut partition = Partition::new(&flash, 128, 256); + + partition.erase(0, 128).await.unwrap(); + + let flash = flash.try_lock().unwrap(); + assert!(flash.mem[128..256].iter().position(|&x| x != 0xFF).is_none()); + } +} From e495473fc341e1403a004901e0d0a575f33350e0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 22:07:23 +0200 Subject: [PATCH 1240/1575] Remove runtime offset and size assertions --- embassy-embedded-hal/src/flash/mem_flash.rs | 1 + embassy-embedded-hal/src/flash/mod.rs | 2 ++ embassy-embedded-hal/src/flash/partition.rs | 35 +++++++++------------ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/embassy-embedded-hal/src/flash/mem_flash.rs b/embassy-embedded-hal/src/flash/mem_flash.rs index 4e10627df..afb0d1a15 100644 --- a/embassy-embedded-hal/src/flash/mem_flash.rs +++ b/embassy-embedded-hal/src/flash/mem_flash.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; +#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; extern crate alloc; diff --git a/embassy-embedded-hal/src/flash/mod.rs b/embassy-embedded-hal/src/flash/mod.rs index c80dd6aac..0210b198d 100644 --- a/embassy-embedded-hal/src/flash/mod.rs +++ b/embassy-embedded-hal/src/flash/mod.rs @@ -3,7 +3,9 @@ mod concat_flash; #[cfg(test)] pub(crate) mod mem_flash; +#[cfg(feature = "nightly")] mod partition; pub use concat_flash::ConcatFlash; +#[cfg(feature = "nightly")] pub use partition::Partition; diff --git a/embassy-embedded-hal/src/flash/partition.rs b/embassy-embedded-hal/src/flash/partition.rs index 084425e95..66d93c0ea 100644 --- a/embassy-embedded-hal/src/flash/partition.rs +++ b/embassy-embedded-hal/src/flash/partition.rs @@ -1,7 +1,6 @@ use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind}; -#[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; /// A logical partition of an underlying shared flash @@ -11,7 +10,7 @@ use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; /// There is no guarantee that muliple partitions on the same flash /// operate on mutually exclusive ranges - such a separation is up to /// the user to guarantee. -pub struct Partition<'a, M: RawMutex, T> { +pub struct Partition<'a, M: RawMutex, T: NorFlash> { flash: &'a Mutex, offset: u32, size: u32, @@ -20,14 +19,20 @@ pub struct Partition<'a, M: RawMutex, T> { #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - Partition, OutOfBounds, Flash(T), } -impl<'a, M: RawMutex, T> Partition<'a, M, T> { +impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> { /// Create a new partition pub const fn new(flash: &'a Mutex, offset: u32, size: u32) -> Self { + if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0 + { + panic!("Partition offset must be a multiple of read, write and erase size"); + } + if size % T::READ_SIZE as u32 != 0 || size % T::WRITE_SIZE as u32 != 0 || size % T::ERASE_SIZE as u32 != 0 { + panic!("Partition size must be a multiple of read, write and erase size"); + } Self { flash, offset, size } } } @@ -35,25 +40,21 @@ impl<'a, M: RawMutex, T> Partition<'a, M, T> { impl NorFlashError for Error { fn kind(&self) -> NorFlashErrorKind { match self { - Error::Partition => NorFlashErrorKind::Other, Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, Error::Flash(f) => f.kind(), } } } -impl ErrorType for Partition<'_, M, T> { +impl ErrorType for Partition<'_, M, T> { type Error = Error; } #[cfg(feature = "nightly")] -impl ReadNorFlash for Partition<'_, M, T> { +impl ReadNorFlash for Partition<'_, M, T> { const READ_SIZE: usize = T::READ_SIZE; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - if self.offset % T::READ_SIZE as u32 != 0 || self.size % T::READ_SIZE as u32 != 0 { - return Err(Error::Partition); - } if offset + bytes.len() as u32 > self.size { return Err(Error::OutOfBounds); } @@ -73,9 +74,6 @@ impl NorFlash for Partition<'_, M, T> { const ERASE_SIZE: usize = T::ERASE_SIZE; async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - if self.offset % T::WRITE_SIZE as u32 != 0 || self.size % T::WRITE_SIZE as u32 != 0 { - return Err(Error::Partition); - } if offset + bytes.len() as u32 > self.size { return Err(Error::OutOfBounds); } @@ -85,9 +83,6 @@ impl NorFlash for Partition<'_, M, T> { } async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - if self.offset % T::ERASE_SIZE as u32 != 0 || self.size % T::ERASE_SIZE as u32 != 0 { - return Err(Error::Partition); - } if to > self.size { return Err(Error::OutOfBounds); } @@ -110,10 +105,10 @@ mod tests { #[futures_test::test] async fn can_read() { let mut flash = MemFlash::<1024, 128, 4>::default(); - flash.mem[12..20].fill(0xAA); + flash.mem[132..132 + 8].fill(0xAA); let flash = Mutex::::new(flash); - let mut partition = Partition::new(&flash, 8, 12); + let mut partition = Partition::new(&flash, 128, 256); let mut read_buf = [0; 8]; partition.read(4, &mut read_buf).await.unwrap(); @@ -126,13 +121,13 @@ mod tests { let flash = MemFlash::<1024, 128, 4>::default(); let flash = Mutex::::new(flash); - let mut partition = Partition::new(&flash, 8, 12); + let mut partition = Partition::new(&flash, 128, 256); let write_buf = [0xAA; 8]; partition.write(4, &write_buf).await.unwrap(); let flash = flash.try_lock().unwrap(); - assert!(flash.mem[12..20].iter().position(|&x| x != 0xAA).is_none()); + assert!(flash.mem[132..132 + 8].iter().position(|&x| x != 0xAA).is_none()); } #[futures_test::test] From cb5df138d6048878a4311ecec63a632c7e20e34a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 26 May 2023 23:48:49 +0200 Subject: [PATCH 1241/1575] Use found divider instead of re-reading brr --- embassy-stm32/src/usart/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 565416c72..061c859a8 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -836,7 +836,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: #[cfg(not(usart_v1))] let mut over8 = false; - let mut found = false; + let mut found = None; for &(presc, _presc_val) in &DIVS { let denom = (config.baudrate * presc as u32) as u64; let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; @@ -858,26 +858,26 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); } - found = true; + found = Some(div); break; } panic!("USART: baudrate too high"); } if div < brr_max { + let div = div as u32; unsafe { - r.brr().write_value(regs::Brr(div as u32)); + r.brr().write_value(regs::Brr(div)); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); } - found = true; + found = Some(div); break; } } - assert!(found, "USART: baudrate too low"); + let div = found.expect("USART: baudrate too low"); - let brr = unsafe { r.brr().read().brr() as u32 }; #[cfg(not(usart_v1))] let oversampling = if over8 { "8 bit" } else { "16 bit" }; #[cfg(usart_v1)] @@ -886,7 +886,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", oversampling, config.baudrate, - pclk_freq.0 / brr + pclk_freq.0 / div ); unsafe { From 6d8f409018f9fabd23e80e07a20b357989c4d841 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 27 May 2023 10:29:21 +0200 Subject: [PATCH 1242/1575] Add BlockingPartition --- embassy-embedded-hal/src/flash/mod.rs | 5 +- .../{partition.rs => partition/asynch.rs} | 22 +-- .../src/flash/partition/blocking.rs | 125 ++++++++++++++++++ .../src/flash/partition/mod.rs | 30 +++++ 4 files changed, 159 insertions(+), 23 deletions(-) rename embassy-embedded-hal/src/flash/{partition.rs => partition/asynch.rs} (89%) create mode 100644 embassy-embedded-hal/src/flash/partition/blocking.rs create mode 100644 embassy-embedded-hal/src/flash/partition/mod.rs diff --git a/embassy-embedded-hal/src/flash/mod.rs b/embassy-embedded-hal/src/flash/mod.rs index 0210b198d..7e4ef290f 100644 --- a/embassy-embedded-hal/src/flash/mod.rs +++ b/embassy-embedded-hal/src/flash/mod.rs @@ -3,9 +3,6 @@ mod concat_flash; #[cfg(test)] pub(crate) mod mem_flash; -#[cfg(feature = "nightly")] -mod partition; +pub mod partition; pub use concat_flash::ConcatFlash; -#[cfg(feature = "nightly")] -pub use partition::Partition; diff --git a/embassy-embedded-hal/src/flash/partition.rs b/embassy-embedded-hal/src/flash/partition/asynch.rs similarity index 89% rename from embassy-embedded-hal/src/flash/partition.rs rename to embassy-embedded-hal/src/flash/partition/asynch.rs index 66d93c0ea..141e0d9fc 100644 --- a/embassy-embedded-hal/src/flash/partition.rs +++ b/embassy-embedded-hal/src/flash/partition/asynch.rs @@ -1,8 +1,10 @@ use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; -use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind}; +use embedded_storage::nor_flash::ErrorType; use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; +use super::Error; + /// A logical partition of an underlying shared flash /// /// A partition holds an offset and a size of the flash, @@ -16,13 +18,6 @@ pub struct Partition<'a, M: RawMutex, T: NorFlash> { size: u32, } -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - OutOfBounds, - Flash(T), -} - impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> { /// Create a new partition pub const fn new(flash: &'a Mutex, offset: u32, size: u32) -> Self { @@ -37,20 +32,10 @@ impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> { } } -impl NorFlashError for Error { - fn kind(&self) -> NorFlashErrorKind { - match self { - Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, - Error::Flash(f) => f.kind(), - } - } -} - impl ErrorType for Partition<'_, M, T> { type Error = Error; } -#[cfg(feature = "nightly")] impl ReadNorFlash for Partition<'_, M, T> { const READ_SIZE: usize = T::READ_SIZE; @@ -68,7 +53,6 @@ impl ReadNorFlash for Partition<'_, M, T> { } } -#[cfg(feature = "nightly")] impl NorFlash for Partition<'_, M, T> { const WRITE_SIZE: usize = T::WRITE_SIZE; const ERASE_SIZE: usize = T::ERASE_SIZE; diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs new file mode 100644 index 000000000..1a4c80f92 --- /dev/null +++ b/embassy-embedded-hal/src/flash/partition/blocking.rs @@ -0,0 +1,125 @@ +use embassy_sync::blocking_mutex::raw::RawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; + +use super::Error; + +/// A logical partition of an underlying shared flash +/// +/// A partition holds an offset and a size of the flash, +/// and is restricted to operate with that range. +/// There is no guarantee that muliple partitions on the same flash +/// operate on mutually exclusive ranges - such a separation is up to +/// the user to guarantee. +pub struct BlockingPartition<'a, M: RawMutex, T: NorFlash> { + flash: &'a Mutex, + offset: u32, + size: u32, +} + +impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> { + /// Create a new partition + pub const fn new(flash: &'a Mutex, offset: u32, size: u32) -> Self { + if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0 + { + panic!("Partition offset must be a multiple of read, write and erase size"); + } + if size % T::READ_SIZE as u32 != 0 || size % T::WRITE_SIZE as u32 != 0 || size % T::ERASE_SIZE as u32 != 0 { + panic!("Partition size must be a multiple of read, write and erase size"); + } + Self { flash, offset, size } + } +} + +impl ErrorType for BlockingPartition<'_, M, T> { + type Error = Error; +} + +impl ReadNorFlash for BlockingPartition<'_, M, T> { + const READ_SIZE: usize = T::READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if offset + bytes.len() as u32 > self.size { + return Err(Error::OutOfBounds); + } + + self.flash + .lock(|flash| flash.read(self.offset + offset, bytes).map_err(Error::Flash)) + } + + fn capacity(&self) -> usize { + self.size as usize + } +} + +impl NorFlash for BlockingPartition<'_, M, T> { + const WRITE_SIZE: usize = T::WRITE_SIZE; + const ERASE_SIZE: usize = T::ERASE_SIZE; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if offset + bytes.len() as u32 > self.size { + return Err(Error::OutOfBounds); + } + + self.flash + .lock(|flash| flash.write(self.offset + offset, bytes).map_err(Error::Flash)) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if to > self.size { + return Err(Error::OutOfBounds); + } + + self.flash + .lock(|flash| flash.erase(self.offset + from, self.offset + to).map_err(Error::Flash)) + } +} + +#[cfg(test)] +mod tests { + use embassy_sync::blocking_mutex::raw::NoopRawMutex; + + use super::*; + use crate::flash::mem_flash::MemFlash; + + #[test] + fn can_read() { + let mut flash = MemFlash::<1024, 128, 4>::default(); + flash.mem[132..132 + 8].fill(0xAA); + + let flash = Mutex::::new(flash); + let mut partition = BlockingPartition::new(&flash, 128, 256); + + let mut read_buf = [0; 8]; + partition.read(4, &mut read_buf).unwrap(); + + assert!(read_buf.iter().position(|&x| x != 0xAA).is_none()); + } + + #[test] + fn can_write() { + let flash = MemFlash::<1024, 128, 4>::default(); + + let flash = Mutex::::new(flash); + let mut partition = BlockingPartition::new(&flash, 128, 256); + + let write_buf = [0xAA; 8]; + partition.write(4, &write_buf).unwrap(); + + let flash = flash.into_inner(); + assert!(flash.mem[132..132 + 8].iter().position(|&x| x != 0xAA).is_none()); + } + + #[test] + fn can_erase() { + let flash = MemFlash::<1024, 128, 4>::new(0x00); + + let flash = Mutex::::new(flash); + let mut partition = BlockingPartition::new(&flash, 128, 256); + + partition.erase(0, 128).unwrap(); + + let flash = flash.into_inner(); + assert!(flash.mem[128..256].iter().position(|&x| x != 0xFF).is_none()); + } +} diff --git a/embassy-embedded-hal/src/flash/partition/mod.rs b/embassy-embedded-hal/src/flash/partition/mod.rs new file mode 100644 index 000000000..a12e49ce1 --- /dev/null +++ b/embassy-embedded-hal/src/flash/partition/mod.rs @@ -0,0 +1,30 @@ +//! Flash Partition utilities + +use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; + +#[cfg(feature = "nightly")] +mod asynch; +mod blocking; + +#[cfg(feature = "nightly")] +pub use asynch::Partition; +pub use blocking::BlockingPartition; + +/// Partition error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The requested flash area is outside the partition + OutOfBounds, + /// Underlying flash error + Flash(T), +} + +impl NorFlashError for Error { + fn kind(&self) -> NorFlashErrorKind { + match self { + Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, + Error::Flash(f) => f.kind(), + } + } +} From 85ce44f78e6d269675085ce6aa54c6353ed13ec7 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 27 May 2023 17:02:54 +0200 Subject: [PATCH 1243/1575] Use RefCell in blocking mutex --- .../src/flash/partition/blocking.rs | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs index 1a4c80f92..dc52e292a 100644 --- a/embassy-embedded-hal/src/flash/partition/blocking.rs +++ b/embassy-embedded-hal/src/flash/partition/blocking.rs @@ -1,3 +1,5 @@ +use core::cell::RefCell; + use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; @@ -12,14 +14,14 @@ use super::Error; /// operate on mutually exclusive ranges - such a separation is up to /// the user to guarantee. pub struct BlockingPartition<'a, M: RawMutex, T: NorFlash> { - flash: &'a Mutex, + flash: &'a Mutex>, offset: u32, size: u32, } impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> { /// Create a new partition - pub const fn new(flash: &'a Mutex, offset: u32, size: u32) -> Self { + pub const fn new(flash: &'a Mutex>, offset: u32, size: u32) -> Self { if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0 { panic!("Partition offset must be a multiple of read, write and erase size"); @@ -43,8 +45,12 @@ impl ReadNorFlash for BlockingPartition<'_, M, T> { return Err(Error::OutOfBounds); } - self.flash - .lock(|flash| flash.read(self.offset + offset, bytes).map_err(Error::Flash)) + self.flash.lock(|flash| { + flash + .borrow_mut() + .read(self.offset + offset, bytes) + .map_err(Error::Flash) + }) } fn capacity(&self) -> usize { @@ -61,8 +67,12 @@ impl NorFlash for BlockingPartition<'_, M, T> { return Err(Error::OutOfBounds); } - self.flash - .lock(|flash| flash.write(self.offset + offset, bytes).map_err(Error::Flash)) + self.flash.lock(|flash| { + flash + .borrow_mut() + .write(self.offset + offset, bytes) + .map_err(Error::Flash) + }) } fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { @@ -70,8 +80,12 @@ impl NorFlash for BlockingPartition<'_, M, T> { return Err(Error::OutOfBounds); } - self.flash - .lock(|flash| flash.erase(self.offset + from, self.offset + to).map_err(Error::Flash)) + self.flash.lock(|flash| { + flash + .borrow_mut() + .erase(self.offset + from, self.offset + to) + .map_err(Error::Flash) + }) } } @@ -87,7 +101,7 @@ mod tests { let mut flash = MemFlash::<1024, 128, 4>::default(); flash.mem[132..132 + 8].fill(0xAA); - let flash = Mutex::::new(flash); + let flash = Mutex::::new(RefCell::new(flash)); let mut partition = BlockingPartition::new(&flash, 128, 256); let mut read_buf = [0; 8]; @@ -100,13 +114,13 @@ mod tests { fn can_write() { let flash = MemFlash::<1024, 128, 4>::default(); - let flash = Mutex::::new(flash); + let flash = Mutex::::new(RefCell::new(flash)); let mut partition = BlockingPartition::new(&flash, 128, 256); let write_buf = [0xAA; 8]; partition.write(4, &write_buf).unwrap(); - let flash = flash.into_inner(); + let flash = flash.into_inner().take(); assert!(flash.mem[132..132 + 8].iter().position(|&x| x != 0xAA).is_none()); } @@ -114,12 +128,12 @@ mod tests { fn can_erase() { let flash = MemFlash::<1024, 128, 4>::new(0x00); - let flash = Mutex::::new(flash); + let flash = Mutex::::new(RefCell::new(flash)); let mut partition = BlockingPartition::new(&flash, 128, 256); partition.erase(0, 128).unwrap(); - let flash = flash.into_inner(); + let flash = flash.into_inner().take(); assert!(flash.mem[128..256].iter().position(|&x| x != 0xFF).is_none()); } } From c19967dcf24d5223de5fd9b390371dc24aeccc1d Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 27 May 2023 15:03:25 -0500 Subject: [PATCH 1244/1575] stm32/ipcc: extract tl_mbox linker file to embassy-stm32 --- embassy-stm32/build.rs | 10 ++++++ .../tl_mbox.x.in | 14 ++------ examples/stm32wb/Cargo.toml | 2 +- examples/stm32wb/build.rs | 36 ++++--------------- examples/stm32wb/memory.x | 35 ------------------ 5 files changed, 20 insertions(+), 77 deletions(-) rename tests/stm32/memory_ble.x => embassy-stm32/tl_mbox.x.in (58%) delete mode 100644 examples/stm32wb/memory.x diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 540981727..9207cf3f4 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -910,6 +910,16 @@ fn main() { println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]); } + // ======== + // stm32wb tl_mbox link sections + + if chip_name.starts_with("stm32wb") { + let out_file = out_dir.join("tl_mbox.x").to_string_lossy().to_string(); + fs::write(out_file, fs::read_to_string("tl_mbox.x.in").unwrap()).unwrap(); + println!("cargo:rustc-link-search={}", out_dir.display()); + println!("cargo:rerun-if-changed=tl_mbox.x.in"); + } + // ======= // Features for targeting groups of chips diff --git a/tests/stm32/memory_ble.x b/embassy-stm32/tl_mbox.x.in similarity index 58% rename from tests/stm32/memory_ble.x rename to embassy-stm32/tl_mbox.x.in index 4332e2c72..b6eecb429 100644 --- a/tests/stm32/memory_ble.x +++ b/embassy-stm32/tl_mbox.x.in @@ -1,21 +1,13 @@ -/* - Memory size for STM32WB55xG with 512K FLASH -*/ - -MEMORY +MEMORY { - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K - RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K } -/* Place stack at the end of SRAM1 */ -_stack_start = ORIGIN(RAM) + LENGTH(RAM); - /* * Scatter the mailbox interface memory sections in shared memory */ -SECTIONS { +SECTIONS +{ TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 3c7e3e874..8cfac772a 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "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", "stm32wb55rg", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wb/build.rs b/examples/stm32wb/build.rs index 30691aa97..29b3a9b2a 100644 --- a/examples/stm32wb/build.rs +++ b/examples/stm32wb/build.rs @@ -1,35 +1,11 @@ -//! 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"); +use std::error::Error; +fn main() -> Result<(), Box> { println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rerun-if-changed=link.x"); + println!("cargo:rustc-link-arg-bins=-Ttl_mbox.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + + Ok(()) } diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x deleted file mode 100644 index e1f0530bd..000000000 --- a/examples/stm32wb/memory.x +++ /dev/null @@ -1,35 +0,0 @@ -/* - The size of this file must be exactly the same as in other memory_xx.x files. - Memory size for STM32WB55xC with 256K FLASH -*/ - -MEMORY -{ - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K - RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K -} - -/* - Memory size for STM32WB55xG with 512K FLASH - - MEMORY - { - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K - RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 - RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K - } -*/ - -/* Place stack at the end of SRAM1 */ -_stack_start = ORIGIN(RAM) + LENGTH(RAM); - -/* - * Scatter the mailbox interface memory sections in shared memory - */ -SECTIONS { - TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED - - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED -} From 7e501855fc2ee98bef6be56244c4587610dbdc32 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 27 May 2023 15:05:07 -0500 Subject: [PATCH 1245/1575] stm32/ipcc: move into tl_mbox --- embassy-stm32/src/lib.rs | 2 -- embassy-stm32/src/{ => tl_mbox}/ipcc.rs | 40 ++++++++++++------------- 2 files changed, 20 insertions(+), 22 deletions(-) rename embassy-stm32/src/{ => tl_mbox}/ipcc.rs (75%) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index c9df5c1b2..b9d7a15c7 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -41,8 +41,6 @@ pub mod crc; pub mod flash; #[cfg(all(spi_v1, rcc_f4))] pub mod i2s; -#[cfg(stm32wb)] -pub mod ipcc; pub mod pwm; #[cfg(quadspi)] pub mod qspi; diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/tl_mbox/ipcc.rs similarity index 75% rename from embassy-stm32/src/ipcc.rs rename to embassy-stm32/src/tl_mbox/ipcc.rs index ea33b32c7..d1ac731ed 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/tl_mbox/ipcc.rs @@ -1,4 +1,4 @@ -use crate::ipcc::sealed::Instance; +use self::sealed::Instance; use crate::peripherals::IPCC; use crate::rcc::sealed::RccPeripheral; @@ -20,17 +20,17 @@ pub enum IpccChannel { Channel6 = 5, } -pub(crate) mod sealed { +pub mod sealed { pub trait Instance: crate::rcc::RccPeripheral { fn regs() -> crate::pac::ipcc::Ipcc; fn set_cpu2(enabled: bool); } } -pub(crate) struct Ipcc; +pub struct Ipcc; impl Ipcc { - pub(crate) fn init(_config: Config) { + pub fn enable(_config: Config) { IPCC::enable(); IPCC::reset(); IPCC::set_cpu2(true); @@ -47,14 +47,14 @@ impl Ipcc { } } - pub(crate) fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) { + pub fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } } - pub(crate) fn c1_get_rx_channel(channel: IpccChannel) -> bool { + pub fn c1_get_rx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled @@ -62,7 +62,7 @@ impl Ipcc { } #[allow(dead_code)] - pub(crate) fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) { + pub fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled @@ -70,21 +70,21 @@ impl Ipcc { } #[allow(dead_code)] - pub(crate) fn c2_get_rx_channel(channel: IpccChannel) -> bool { + pub fn c2_get_rx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { !regs.cpu(1).mr().read().chom(channel as usize) } } - pub(crate) fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) { + pub fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } } - pub(crate) fn c1_get_tx_channel(channel: IpccChannel) -> bool { + pub fn c1_get_tx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled @@ -92,7 +92,7 @@ impl Ipcc { } #[allow(dead_code)] - pub(crate) fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) { + pub fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled @@ -100,7 +100,7 @@ impl Ipcc { } #[allow(dead_code)] - pub(crate) fn c2_get_tx_channel(channel: IpccChannel) -> bool { + pub fn c2_get_tx_channel(channel: IpccChannel) -> bool { let regs = IPCC::regs(); // If bit is set to 1 then interrupt is disabled @@ -108,7 +108,7 @@ impl Ipcc { } /// clears IPCC receive channel status for CPU1 - pub(crate) fn c1_clear_flag_channel(channel: IpccChannel) { + pub fn c1_clear_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } @@ -116,42 +116,42 @@ impl Ipcc { #[allow(dead_code)] /// clears IPCC receive channel status for CPU2 - pub(crate) fn c2_clear_flag_channel(channel: IpccChannel) { + pub fn c2_clear_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) } } - pub(crate) fn c1_set_flag_channel(channel: IpccChannel) { + pub fn c1_set_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } } #[allow(dead_code)] - pub(crate) fn c2_set_flag_channel(channel: IpccChannel) { + pub fn c2_set_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) } } - pub(crate) fn c1_is_active_flag(channel: IpccChannel) -> bool { + pub fn c1_is_active_flag(channel: IpccChannel) -> bool { let regs = IPCC::regs(); unsafe { regs.cpu(0).sr().read().chf(channel as usize) } } - pub(crate) fn c2_is_active_flag(channel: IpccChannel) -> bool { + pub fn c2_is_active_flag(channel: IpccChannel) -> bool { let regs = IPCC::regs(); unsafe { regs.cpu(1).sr().read().chf(channel as usize) } } - pub(crate) fn is_tx_pending(channel: IpccChannel) -> bool { + pub fn is_tx_pending(channel: IpccChannel) -> bool { !Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel) } - pub(crate) fn is_rx_pending(channel: IpccChannel) -> bool { + pub fn is_rx_pending(channel: IpccChannel) -> bool { Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel) } } From 37e104a6b380c3c7ec20346a44b11050476a6116 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 27 May 2023 15:05:23 -0500 Subject: [PATCH 1246/1575] stm32/ipcc: refactor tl_mbox --- embassy-stm32/src/tl_mbox/ble.rs | 14 +++----- embassy-stm32/src/tl_mbox/channels.rs | 4 +-- embassy-stm32/src/tl_mbox/evt.rs | 4 +-- embassy-stm32/src/tl_mbox/mm.rs | 10 ++---- embassy-stm32/src/tl_mbox/mod.rs | 46 +++++++++++++++++++-------- embassy-stm32/src/tl_mbox/shci.rs | 2 +- embassy-stm32/src/tl_mbox/sys.rs | 16 ++++------ 7 files changed, 51 insertions(+), 45 deletions(-) diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index 5cc0bb200..062377999 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -1,5 +1,3 @@ -use core::mem::MaybeUninit; - use embassy_futures::block_on; use super::cmd::CmdSerial; @@ -10,17 +8,17 @@ use super::{ channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_CHANNEL, TL_REF_TABLE, }; -use crate::ipcc::Ipcc; use crate::tl_mbox::cmd::CmdPacket; +use crate::tl_mbox::ipcc::Ipcc; pub struct Ble; impl Ble { - pub(crate) fn new() -> Self { + pub fn enable() { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); - TL_BLE_TABLE = MaybeUninit::new(BleTable { + TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable { pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), pcs_buffer: CS_BUFFER.as_mut_ptr().cast(), pevt_queue: EVT_QUEUE.as_ptr().cast(), @@ -29,11 +27,9 @@ impl Ble { } Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); - - Ble } - pub(crate) fn evt_handler() { + pub fn evt_handler() { unsafe { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; @@ -51,7 +47,7 @@ impl Ble { Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); } - pub(crate) fn send_cmd(buf: &[u8]) { + pub fn send_cmd(buf: &[u8]) { unsafe { let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial; diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs index aaa6ce177..25a065ba4 100644 --- a/embassy-stm32/src/tl_mbox/channels.rs +++ b/embassy-stm32/src/tl_mbox/channels.rs @@ -50,7 +50,7 @@ //! pub mod cpu1 { - use crate::ipcc::IpccChannel; + use crate::tl_mbox::ipcc::IpccChannel; // Not used currently but reserved pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; @@ -75,7 +75,7 @@ pub mod cpu1 { } pub mod cpu2 { - use crate::ipcc::IpccChannel; + use crate::tl_mbox::ipcc::IpccChannel; pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs index 04feb3e68..47a8b72fd 100644 --- a/embassy-stm32/src/tl_mbox/evt.rs +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -3,7 +3,7 @@ use core::mem::MaybeUninit; use super::cmd::{AclDataPacket, AclDataSerial}; use super::consts::TlPacketType; use super::{PacketHeader, TL_EVT_HEADER_SIZE}; -use crate::tl_mbox::mm; +use crate::tl_mbox::mm::MemoryManager; /// the payload of [`Evt`] for a command status event #[derive(Copy, Clone)] @@ -131,6 +131,6 @@ impl EvtBox { impl Drop for EvtBox { fn drop(&mut self) { - mm::MemoryManager::evt_drop(self.ptr); + MemoryManager::evt_drop(self.ptr); } } diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs index 6e0818cf1..e28a6aa0c 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -1,22 +1,20 @@ -use core::mem::MaybeUninit; - use super::evt::EvtPacket; use super::unsafe_linked_list::LinkedListNode; use super::{ channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, TL_REF_TABLE, }; -use crate::ipcc::Ipcc; +use crate::tl_mbox::ipcc::Ipcc; pub struct MemoryManager; impl MemoryManager { - pub fn new() -> Self { + pub fn enable() { unsafe { LinkedListNode::init_head(FREE_BUFF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); - TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable { + TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), ble_pool: EVT_POOL.as_ptr().cast(), @@ -26,8 +24,6 @@ impl MemoryManager { traces_pool_size: 0, }); } - - MemoryManager } pub fn evt_handler() { diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index 1c927efa0..616f7dc56 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -1,7 +1,9 @@ use core::mem::MaybeUninit; +use atomic_polyfill::{compiler_fence, Ordering}; use bit_field::BitField; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -13,13 +15,16 @@ use self::shci::{shci_ble_init, ShciBleInitCmdParam}; use self::sys::Sys; use self::unsafe_linked_list::LinkedListNode; use crate::interrupt; -use crate::ipcc::{Config, Ipcc}; +use crate::peripherals::IPCC; +pub use crate::tl_mbox::ipcc::Config; +use crate::tl_mbox::ipcc::Ipcc; mod ble; mod channels; mod cmd; mod consts; mod evt; +mod ipcc; mod mm; mod shci; mod sys; @@ -60,6 +65,8 @@ pub struct ReceiveInterruptHandler {} impl interrupt::Handler for ReceiveInterruptHandler { unsafe fn on_interrupt() { + // info!("ipcc rx interrupt"); + if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { sys::Sys::evt_handler(); } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { @@ -74,6 +81,8 @@ pub struct TransmitInterruptHandler {} impl interrupt::Handler for TransmitInterruptHandler { unsafe fn on_interrupt() { + // info!("ipcc tx interrupt"); + if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { // TODO: handle this case let _ = sys::Sys::cmd_evt_handler(); @@ -307,21 +316,24 @@ static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251 // TODO: get a better size, this is a placeholder pub(crate) static TL_CHANNEL: Channel = Channel::new(); -pub struct TlMbox { - _sys: Sys, - _ble: Ble, - _mm: MemoryManager, +pub struct TlMbox<'d> { + _ipcc: PeripheralRef<'d, IPCC>, } -impl TlMbox { +impl<'d> TlMbox<'d> { /// initializes low-level transport between CPU1 and BLE stack on CPU2 - pub fn init( + pub fn new( + ipcc: impl Peripheral

+ 'd, _irqs: impl interrupt::Binding + interrupt::Binding, config: Config, - ) -> TlMbox { + ) -> Self { + into_ref!(ipcc); + unsafe { - TL_REF_TABLE = MaybeUninit::new(RefTable { + compiler_fence(Ordering::AcqRel); + + TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), ble_table: TL_BLE_TABLE.as_ptr(), thread_table: TL_THREAD_TABLE.as_ptr(), @@ -334,6 +346,10 @@ impl TlMbox { ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), }); + // info!("TL_REF_TABLE addr: {:x}", TL_REF_TABLE.as_ptr() as usize); + + compiler_fence(Ordering::AcqRel); + TL_SYS_TABLE = MaybeUninit::zeroed(); TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed(); TL_BLE_TABLE = MaybeUninit::zeroed(); @@ -352,13 +368,15 @@ impl TlMbox { CS_BUFFER = MaybeUninit::zeroed(); BLE_CMD_BUFFER = MaybeUninit::zeroed(); HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed(); + + compiler_fence(Ordering::AcqRel); } - Ipcc::init(config); + Ipcc::enable(config); - let _sys = Sys::new(); - let _ble = Ble::new(); - let _mm = MemoryManager::new(); + Sys::enable(); + Ble::enable(); + MemoryManager::enable(); // enable interrupts unsafe { crate::interrupt::IPCC_C1_RX::steal() }.unpend(); @@ -367,7 +385,7 @@ impl TlMbox { unsafe { crate::interrupt::IPCC_C1_RX::steal() }.enable(); unsafe { crate::interrupt::IPCC_C1_TX::steal() }.enable(); - TlMbox { _sys, _ble, _mm } + Self { _ipcc: ipcc } } pub fn wireless_fw_info(&self) -> Option { diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32/src/tl_mbox/shci.rs index 3d7e994ae..6b5b2dd19 100644 --- a/embassy-stm32/src/tl_mbox/shci.rs +++ b/embassy-stm32/src/tl_mbox/shci.rs @@ -3,7 +3,7 @@ use super::cmd::CmdPacket; use super::consts::TlPacketType; use super::{channels, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE}; -use crate::ipcc::Ipcc; +use crate::tl_mbox::ipcc::Ipcc; const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index 79e257148..9685fb920 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -1,5 +1,3 @@ -use core::mem::MaybeUninit; - use embassy_futures::block_on; use super::cmd::{CmdPacket, CmdSerial}; @@ -7,27 +5,25 @@ use super::consts::TlPacketType; use super::evt::{CcEvt, EvtBox, EvtSerial}; use super::unsafe_linked_list::LinkedListNode; use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_CHANNEL, TL_REF_TABLE, TL_SYS_TABLE}; -use crate::ipcc::Ipcc; +use crate::tl_mbox::ipcc::Ipcc; pub struct Sys; impl Sys { - pub(crate) fn new() -> Self { + pub fn enable() { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); - TL_SYS_TABLE = MaybeUninit::new(SysTable { + TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), }); } Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); - - Sys } - pub(crate) fn evt_handler() { + pub fn evt_handler() { unsafe { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; @@ -46,7 +42,7 @@ impl Sys { Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); } - pub(crate) fn cmd_evt_handler() -> CcEvt { + pub fn cmd_evt_handler() -> CcEvt { Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); // ST's command response data structure is really convoluted. @@ -68,7 +64,7 @@ impl Sys { } #[allow(dead_code)] - pub(crate) fn send_cmd(buf: &[u8]) { + pub fn send_cmd(buf: &[u8]) { unsafe { // TODO: check this let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; From 09d52638b551a37c8b032ffb6daaa1abd2efa231 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 27 May 2023 15:05:50 -0500 Subject: [PATCH 1247/1575] stm32/ipcc: refactor examples and tests --- examples/stm32wb/src/bin/tl_mbox.rs | 14 ++++++++------ examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 10 +++++----- tests/stm32/.cargo/config.toml | 6 ++++-- tests/stm32/Cargo.toml | 6 +++--- tests/stm32/build.rs | 13 +++++++++---- tests/stm32/src/bin/{ble.rs => tl_mbox.rs} | 7 +++---- 6 files changed, 32 insertions(+), 24 deletions(-) rename tests/stm32/src/bin/{ble.rs => tl_mbox.rs} (89%) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 18d93a279..8f4e70af0 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -4,8 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::ipcc::Config; -use embassy_stm32::tl_mbox::TlMbox; +use embassy_stm32::tl_mbox::{Config, TlMbox}; use embassy_stm32::{bind_interrupts, tl_mbox}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -41,16 +40,16 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let _p = embassy_stm32::init(Default::default()); + let p = embassy_stm32::init(Default::default()); info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(Irqs, config); + let mbox = TlMbox::new(p.IPCC, Irqs, config); loop { let wireless_fw_info = mbox.wireless_fw_info(); match wireless_fw_info { - None => error!("not yet initialized"), + None => info!("not yet initialized"), Some(fw_info) => { let version_major = fw_info.version_major(); let version_minor = fw_info.version_minor(); @@ -68,6 +67,9 @@ async fn main(_spawner: Spawner) { } } - Timer::after(Duration::from_millis(500)).await; + Timer::after(Duration::from_millis(50)).await; } + + info!("Test OK"); + cortex_m::asm::bkpt(); } diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 41c450a53..1724d946f 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -4,8 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::ipcc::Config; -use embassy_stm32::tl_mbox::TlMbox; +use embassy_stm32::tl_mbox::{Config, TlMbox}; use embassy_stm32::{bind_interrupts, tl_mbox}; use {defmt_rtt as _, panic_probe as _}; @@ -40,11 +39,11 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let _p = embassy_stm32::init(Default::default()); + let p = embassy_stm32::init(Default::default()); info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(Irqs, config); + let mbox = TlMbox::new(p.IPCC, Irqs, config); info!("waiting for coprocessor to boot"); let event_box = mbox.read().await; @@ -94,5 +93,6 @@ async fn main(_spawner: Spawner) { payload[3..] ); - loop {} + info!("Test OK"); + cortex_m::asm::bkpt(); } diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml index 29c4799a1..48588f0b7 100644 --- a/tests/stm32/.cargo/config.toml +++ b/tests/stm32/.cargo/config.toml @@ -3,8 +3,10 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" +#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" #runner = "teleprobe local run --chip STM32F103C8 --elf" +#runner = "teleprobe local run --chip STM32WB55RG --elf" +runner = "probe-run --chip STM32WB55RG" rustflags = [ # Code-size optimizations. @@ -14,7 +16,7 @@ rustflags = [ ] [build] -target = "thumbv7m-none-eabi" +target = "thumbv7em-none-eabi" [env] DEFMT_LOG = "trace" diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bd8d90abe..23b5f9caa 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,7 +12,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board @@ -45,8 +45,8 @@ chrono = { version = "^0.4", default-features = false, optional = true} # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. [[bin]] -name = "ble" -path = "src/bin/ble.rs" +name = "tl_mbox" +path = "src/bin/tl_mbox.rs" required-features = [ "ble",] [[bin]] diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index ca76b70bb..b4583147e 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -9,17 +9,22 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-arg-bins=--nmagic"); // too little RAM to run from RAM. - if cfg!(any(feature = "stm32f103c8", feature = "stm32c031c6")) { + if cfg!(any( + feature = "stm32f103c8", + feature = "stm32c031c6", + feature = "stm32wb55rg" + )) { println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rerun-if-changed=link.x"); - } else if cfg!(feature = "stm32wb55rg") { - println!("cargo:rustc-link-arg-bins=-Tlink.x"); - fs::write(out.join("memory.x"), include_bytes!("memory_ble.x")).unwrap(); } else { println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); println!("cargo:rerun-if-changed=link_ram.x"); } + if cfg!(feature = "stm32wb55rg") { + println!("cargo:rustc-link-arg-bins=-Ttl_mbox.x"); + } + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); Ok(()) diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/tl_mbox.rs similarity index 89% rename from tests/stm32/src/bin/ble.rs rename to tests/stm32/src/bin/tl_mbox.rs index db5cc611d..626e7ac6f 100644 --- a/tests/stm32/src/bin/ble.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -7,8 +7,7 @@ #[path = "../example_common.rs"] mod example_common; use embassy_executor::Spawner; -use embassy_stm32::ipcc::Config; -use embassy_stm32::tl_mbox::TlMbox; +use embassy_stm32::tl_mbox::{Config, TlMbox}; use embassy_stm32::{bind_interrupts, tl_mbox}; use embassy_time::{Duration, Timer}; use example_common::*; @@ -20,11 +19,11 @@ bind_interrupts!(struct Irqs{ #[embassy_executor::main] async fn main(_spawner: Spawner) { - let _p = embassy_stm32::init(config()); + let p = embassy_stm32::init(config()); info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(Irqs, config); + let mbox = TlMbox::new(p.IPCC, Irqs, config); loop { let wireless_fw_info = mbox.wireless_fw_info(); From 5d7301e510a0882d004cdaee337280816d490afd Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 27 May 2023 15:08:30 -0500 Subject: [PATCH 1248/1575] tests/stm32: revert cfg changes --- tests/stm32/.cargo/config.toml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml index 48588f0b7..426d6e67f 100644 --- a/tests/stm32/.cargo/config.toml +++ b/tests/stm32/.cargo/config.toml @@ -3,10 +3,8 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" +runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" #runner = "teleprobe local run --chip STM32F103C8 --elf" -#runner = "teleprobe local run --chip STM32WB55RG --elf" -runner = "probe-run --chip STM32WB55RG" rustflags = [ # Code-size optimizations. @@ -16,7 +14,7 @@ rustflags = [ ] [build] -target = "thumbv7em-none-eabi" +target = "thumbv7m-none-eabi" [env] -DEFMT_LOG = "trace" +DEFMT_LOG = "trace" \ No newline at end of file From bd6a1d38d208f770d15a70a89112fc74046a96ef Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 29 May 2023 09:16:50 -0500 Subject: [PATCH 1249/1575] stm32/tests: disable sdmmc test for now --- tests/stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bd8d90abe..6e7a4a57d 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,7 +7,7 @@ autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono", "not-gpdma"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "not-gpdma"] # Nucleo "sdmmc" stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo From 7b2a39a6fb62e0a1483b31e2d24dec71013558e7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 May 2023 23:05:18 +0200 Subject: [PATCH 1250/1575] Switch to Bender for CI. --- .github/ci/rust.sh | 21 ++++++++++ .github/workflows/rust.yml | 80 -------------------------------------- ci.sh | 1 - 3 files changed, 21 insertions(+), 81 deletions(-) create mode 100755 .github/ci/rust.sh delete mode 100644 .github/workflows/rust.yml diff --git a/.github/ci/rust.sh b/.github/ci/rust.sh new file mode 100755 index 000000000..af7f336c1 --- /dev/null +++ b/.github/ci/rust.sh @@ -0,0 +1,21 @@ +#!/bin/bash +## on push branch=main +## on push branch~=gh-readonly-queue/main/.* +## on pull_request + +set -euo pipefail + +echo Hello World! + +export RUSTUP_HOME=/ci/cache/rustup +export CARGO_HOME=/ci/cache/cargo +export CARGO_TARGET_DIR=/ci/cache/target +if [ -f /ci/secrets/teleprobe-token.txt ]; then + echo Got teleprobe token! + export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) +fi + +hashtime restore /ci/cache/filetime.json || true +hashtime save /ci/cache/filetime.json + +./ci.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index 0cbca31b8..000000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Rust - -on: - push: - branches: [staging, trying, master] - pull_request: - branches: [master] - -env: - CARGO_TERM_COLOR: always - -jobs: - all: - runs-on: ubuntu-latest - needs: [build-nightly, build-stable, test] - steps: - - name: Done - run: exit 0 - build-nightly: - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Cache multiple paths - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target_ci - key: rust3-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} - - name: build - env: - TELEPROBE_TOKEN: ${{ secrets.TELEPROBE_TOKEN }} - run: | - curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.3.0/cargo-batch - chmod +x /usr/local/bin/cargo-batch - ./ci.sh - rm -rf target_ci/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* - build-stable: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Cache multiple paths - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target_ci_stable - key: rust-stable-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} - - name: build - run: | - curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.3.0/cargo-batch - chmod +x /usr/local/bin/cargo-batch - ./ci_stable.sh - rm -rf target_ci_stable/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* - - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Test boot - working-directory: ./embassy-boot/boot - run: cargo test && cargo test --features nightly && cargo test --features "ed25519-dalek,nightly" && cargo test --features "ed25519-salty,nightly" - - - name: Test sync - working-directory: ./embassy-sync - run: cargo test diff --git a/ci.sh b/ci.sh index 6d906f5f9..11c569328 100755 --- a/ci.sh +++ b/ci.sh @@ -2,7 +2,6 @@ set -euo pipefail -export CARGO_TARGET_DIR=$PWD/target_ci export RUSTFLAGS=-Dwarnings export DEFMT_LOG=trace From 6cb6e575920613c2aefca22c06764a098521cf5b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 29 May 2023 18:49:50 +0200 Subject: [PATCH 1251/1575] CI fixes. --- .github/ci/rust.sh | 1 - .github/workflows/doc.yml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ci/rust.sh b/.github/ci/rust.sh index af7f336c1..db1fc8538 100755 --- a/.github/ci/rust.sh +++ b/.github/ci/rust.sh @@ -1,5 +1,4 @@ #!/bin/bash -## on push branch=main ## on push branch~=gh-readonly-queue/main/.* ## on pull_request diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index b4e225e64..a69a49718 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -2,7 +2,7 @@ name: Docs on: push: - branches: [master] + branches: [main] env: BUILDER_THREADS: '1' From 46961cfdf72a3b5d54b241a41d9f2496c6dc6229 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 29 May 2023 19:46:28 +0200 Subject: [PATCH 1252/1575] Fix tests. --- .../src/shared_bus/blocking/i2c.rs | 5 ++--- .../src/shared_bus/blocking/spi.rs | 5 ++--- embassy-hal-common/src/atomic_ring_buffer.rs | 2 -- embassy-nrf/src/time_driver.rs | 2 +- embassy-stm32/src/flash/common.rs | 2 +- embassy-stm32/src/pwm/complementary_pwm.rs | 14 +++++++------- embassy-time/Cargo.toml | 2 +- embassy-time/src/driver.rs | 2 +- embassy-time/src/queue_generic.rs | 15 +++++++++------ embassy-time/src/timer.rs | 1 - 10 files changed, 24 insertions(+), 26 deletions(-) diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index 1fe520e6c..af73df059 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -2,13 +2,12 @@ //! //! # Example (nrf52) //! -//! ```rust +//! ```rust,ignore //! use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; //! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; //! //! static I2C_BUS: StaticCell>>> = StaticCell::new(); -//! let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -//! let i2c = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, Config::default()); +//! let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, Config::default()); //! let i2c_bus = NoopMutex::new(RefCell::new(i2c)); //! let i2c_bus = I2C_BUS.init(i2c_bus); //! diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 7982ffb6e..22e013be9 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -2,13 +2,12 @@ //! //! # Example (nrf52) //! -//! ```rust +//! ```rust,ignore //! use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; //! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; //! //! static SPI_BUS: StaticCell>>> = StaticCell::new(); -//! let irq = interrupt::take!(SPIM3); -//! let spi = Spim::new_txonly(p.SPI3, irq, p.P0_15, p.P0_18, Config::default()); +//! let spi = Spim::new_txonly(p.SPI3, Irqs, p.P0_15, p.P0_18, Config::default()); //! let spi_bus = NoopMutex::new(RefCell::new(spi)); //! let spi_bus = SPI_BUS.init(spi_bus); //! diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs index 0eb39cb33..ea84925c4 100644 --- a/embassy-hal-common/src/atomic_ring_buffer.rs +++ b/embassy-hal-common/src/atomic_ring_buffer.rs @@ -458,8 +458,6 @@ mod tests { #[test] fn push_slices() { - init(); - let mut b = [0; 4]; let rb = RingBuffer::new(); unsafe { diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index bc2c8a3c1..c82c238cc 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -67,7 +67,7 @@ fn compare_n(n: usize) -> u32 { 1 << (n + 16) } -#[cfg(tests)] +#[cfg(test)] mod test { use super::*; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 6c1912900..c6cdc574b 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -163,7 +163,7 @@ pub(super) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector bank_offset = 0; } - if address < region.end() { + if address >= region.base && address < region.end() { let index_in_region = (address - region.base) / region.erase_size; return FlashSector { bank: region.bank, diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index 3f8b43cfa..cfb79947c 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -209,39 +209,39 @@ mod tests { #[test] fn test_compute_dead_time_value() { - struct test_run { + struct TestRun { value: u16, ckd: Ckd, bits: u8, } let fn_results = [ - test_run { + TestRun { value: 1, ckd: Ckd::DIV1, bits: 1, }, - test_run { + TestRun { value: 125, ckd: Ckd::DIV1, bits: 125, }, - test_run { + TestRun { value: 245, ckd: Ckd::DIV1, bits: 64 + 245 / 2, }, - test_run { + TestRun { value: 255, ckd: Ckd::DIV2, bits: 127, }, - test_run { + TestRun { value: 400, ckd: Ckd::DIV1, bits: 32 + (400u16 / 8) as u8, }, - test_run { + TestRun { value: 600, ckd: Ckd::DIV4, bits: 64 + (600u16 / 8) as u8, diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c4edbd38a..857da5467 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -169,4 +169,4 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" critical-section = { version = "1.1", features = ["std"] } - +embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly"] } diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs index 5c2ad3b23..d6436369b 100644 --- a/embassy-time/src/driver.rs +++ b/embassy-time/src/driver.rs @@ -49,7 +49,7 @@ //! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { //! todo!() //! } -//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { +//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { //! todo!() //! } //! } diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 64a8af4bc..4795eb2f3 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -183,7 +183,6 @@ mod tests { use serial_test::serial; - use super::InnerQueue; use crate::driver::{AlarmHandle, Driver}; use crate::queue_generic::QUEUE; use crate::Instant; @@ -317,14 +316,18 @@ mod tests { fn setup() { DRIVER.reset(); - - QUEUE.inner.lock(|inner| { - *inner.borrow_mut() = InnerQueue::new(); - }); + critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None); } fn queue_len() -> usize { - QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) + critical_section::with(|cs| { + QUEUE + .inner + .borrow_ref(cs) + .as_ref() + .map(|inner| inner.queue.iter().count()) + .unwrap_or(0) + }) } #[test] diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 52620d233..d3d1f9f5f 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -109,7 +109,6 @@ impl Future for Timer { /// # #![feature(type_alias_impl_trait)] /// # /// use embassy_time::{Duration, Ticker}; -/// use futures::StreamExt; /// # fn foo(){} /// /// #[embassy_executor::task] From 421ee4dfbfbbdc007265498d4f529687c16d89af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 29 May 2023 19:46:46 +0200 Subject: [PATCH 1253/1575] ci: add stable build, add tests. --- .github/ci/build-stable.sh | 16 ++++++++++++++++ .github/ci/{rust.sh => build.sh} | 2 -- .github/ci/test.sh | 28 ++++++++++++++++++++++++++++ ci_stable.sh | 3 --- 4 files changed, 44 insertions(+), 5 deletions(-) create mode 100755 .github/ci/build-stable.sh rename .github/ci/{rust.sh => build.sh} (95%) create mode 100755 .github/ci/test.sh diff --git a/.github/ci/build-stable.sh b/.github/ci/build-stable.sh new file mode 100755 index 000000000..0dadd6102 --- /dev/null +++ b/.github/ci/build-stable.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## on push branch~=gh-readonly-queue/main/.* +## on pull_request + +set -euo pipefail + +export RUSTUP_HOME=/ci/cache/rustup +export CARGO_HOME=/ci/cache/cargo +export CARGO_TARGET_DIR=/ci/cache/target + +hashtime restore /ci/cache/filetime.json || true +hashtime save /ci/cache/filetime.json + +sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml + +./ci_stable.sh diff --git a/.github/ci/rust.sh b/.github/ci/build.sh similarity index 95% rename from .github/ci/rust.sh rename to .github/ci/build.sh index db1fc8538..1c3a7f3b0 100755 --- a/.github/ci/rust.sh +++ b/.github/ci/build.sh @@ -4,8 +4,6 @@ set -euo pipefail -echo Hello World! - export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target diff --git a/.github/ci/test.sh b/.github/ci/test.sh new file mode 100755 index 000000000..a7140cfd9 --- /dev/null +++ b/.github/ci/test.sh @@ -0,0 +1,28 @@ +#!/bin/bash +## on push branch~=gh-readonly-queue/main/.* +## on pull_request + +set -euo pipefail + +export RUSTUP_HOME=/ci/cache/rustup +export CARGO_HOME=/ci/cache/cargo +export CARGO_TARGET_DIR=/ci/cache/target + +hashtime restore /ci/cache/filetime.json || true +hashtime save /ci/cache/filetime.json + +cargo test --manifest-path ./embassy-sync/Cargo.toml +cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml +cargo test --manifest-path ./embassy-hal-common/Cargo.toml +cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue + +cargo test --manifest-path ./embassy-boot/boot/Cargo.toml +cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly +cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-dalek +cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-salty + +#cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nightly,nrf52840,time-driver-rtc1 ## broken doctests + +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti +cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti diff --git a/ci_stable.sh b/ci_stable.sh index 55a2f89a0..a67087ffb 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -2,12 +2,9 @@ set -euo pipefail -export CARGO_TARGET_DIR=$PWD/target_ci_stable export RUSTFLAGS=-Dwarnings export DEFMT_LOG=trace -sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml - cargo batch \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ From 1acbc5b1a90985df83195ad23f9665b3227b3805 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 27 May 2023 19:26:45 +0200 Subject: [PATCH 1254/1575] Remove the usage of the local Partition type in BootLoader --- embassy-boot/boot/Cargo.toml | 1 + embassy-boot/boot/src/boot_loader.rs | 344 +++++++++------------------ 2 files changed, 114 insertions(+), 231 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index f641d5e1c..3fdf69c5e 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -39,6 +39,7 @@ env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } sha1 = "0.10.5" +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } [dev-dependencies.ed25519-dalek] default_features = false diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index b959de2c4..50eb5e668 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -1,6 +1,8 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use crate::{State, BOOT_MAGIC, SWAP_MAGIC}; + +const STATE_ERASE_VALUE: u8 = 0xFF; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -30,65 +32,39 @@ where } } -/// Trait defining the flash handles used for active and DFU partition. -pub trait FlashConfig { - /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. - const STATE_ERASE_VALUE: u8 = 0xFF; - /// Flash type used for the state partition. - type STATE: NorFlash; - /// Flash type used for the active partition. - type ACTIVE: NorFlash; - /// Flash type used for the dfu partition. - type DFU: NorFlash; - - /// Return flash instance used to write/read to/from active partition. - fn active(&mut self) -> &mut Self::ACTIVE; - /// Return flash instance used to write/read to/from dfu partition. - fn dfu(&mut self) -> &mut Self::DFU; - /// Return flash instance used to write/read to/from bootloader state. - fn state(&mut self) -> &mut Self::STATE; -} - -trait FlashConfigEx { - fn page_size() -> u32; -} - -impl FlashConfigEx for T { - /// Get the page size which is the "unit of operation" within the bootloader. - fn page_size() -> u32 { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 - } -} - /// BootLoader works with any flash implementing embedded_storage. -pub struct BootLoader { - // Page with current state of bootloader. The state partition has the following format: - // All ranges are in multiples of WRITE_SIZE bytes. - // | Range | Description | - // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | - // | 2..2 + N | Progress index used while swapping or reverting | - state: Partition, - // Location of the partition which will be booted from - active: Partition, - // Location of the partition which will be swapped in when requested - dfu: Partition, +pub struct BootLoader { + /// Flash type used for the active partition - the partition which will be booted from. + active: ACTIVE, + /// Flash type used for the dfu partition - he partition which will be swapped in when requested. + dfu: DFU, + /// Flash type used for the state partition. + /// + /// The state partition has the following format: + /// All ranges are in multiples of WRITE_SIZE bytes. + /// | Range | Description | + /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | + /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | + /// | 2..2 + N | Progress index used while swapping or reverting + state: STATE, } -impl BootLoader { - /// Create a new instance of a bootloader with the given partitions. +impl BootLoader { + /// Get the page size which is the "unit of operation" within the bootloader. + const PAGE_SIZE: u32 = if ACTIVE::ERASE_SIZE > DFU::ERASE_SIZE { + ACTIVE::ERASE_SIZE as u32 + } else { + DFU::ERASE_SIZE as u32 + }; + + /// Create a new instance of a bootloader with the flash partitions. /// /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. - pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { Self { active, dfu, state } } - /// Return the offset of the active partition into the active flash. - pub fn boot_address(&self) -> usize { - self.active.from as usize - } - /// Perform necessary boot preparations like swapping images. /// /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap @@ -175,195 +151,174 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { + pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(0, P::page_size() % aligned_buf.len() as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); - assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); - assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); + assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32); + assert_eq!(0, Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32); + assert_eq!(0, Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32); + assert_eq!(0, Self::PAGE_SIZE % DFU::WRITE_SIZE as u32); + assert_eq!(0, Self::PAGE_SIZE % DFU::ERASE_SIZE as u32); + assert!(aligned_buf.len() >= STATE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); + + assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p, aligned_buf)?; + let state = self.read_state(aligned_buf)?; if state == State::Swap { // // Check if we already swapped. If we're in the swap state, this means we should revert // since the app has failed to mark boot as successful // - if !self.is_swapped(p, aligned_buf)? { + if !self.is_swapped(aligned_buf)? { trace!("Swapping"); - self.swap(p, aligned_buf)?; + self.swap(aligned_buf)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p, aligned_buf)?; + self.revert(aligned_buf)?; - let state_flash = p.state(); - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; // Invalidate progress - state_word.fill(!P::STATE_ERASE_VALUE); - self.state - .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + state_word.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, state_word)?; // Clear magic and progress - self.state.wipe_blocking(state_flash)?; + self.state.erase(0, self.state.capacity() as u32)?; // Set magic state_word.fill(BOOT_MAGIC); - self.state.write_blocking(state_flash, 0, state_word)?; + self.state.write(0, state_word)?; } } Ok(state) } - fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { - let page_count = (self.active.size() / P::page_size()) as usize; - let progress = self.current_progress(p, aligned_buf)?; + fn is_swapped(&mut self, aligned_buf: &mut [u8]) -> Result { + let page_count = self.active.capacity() / Self::PAGE_SIZE as usize; + let progress = self.current_progress(aligned_buf)?; Ok(progress >= page_count * 2) } - fn current_progress(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let write_size = P::STATE::WRITE_SIZE as u32; - let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; - let state_flash = config.state(); + fn current_progress(&mut self, aligned_buf: &mut [u8]) -> Result { + let write_size = STATE::WRITE_SIZE as u32; + let max_index = ((self.state.capacity() - STATE::WRITE_SIZE) / STATE::WRITE_SIZE) - 2; let state_word = &mut aligned_buf[..write_size as usize]; - self.state.read_blocking(state_flash, write_size, state_word)?; - if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { + self.state.read(write_size, state_word)?; + if state_word.iter().any(|&b| b != STATE_ERASE_VALUE) { // Progress is invalid return Ok(max_index); } for index in 0..max_index { - self.state - .read_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?; + self.state.read((2 + index) as u32 * write_size, state_word)?; - if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { + if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) { return Ok(index); } } Ok(max_index) } - fn update_progress( - &mut self, - progress_index: usize, - p: &mut P, - aligned_buf: &mut [u8], - ) -> Result<(), BootError> { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - state_word.fill(!P::STATE_ERASE_VALUE); - self.state.write_blocking( - p.state(), - (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32, - state_word, - )?; + fn update_progress(&mut self, progress_index: usize, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; + state_word.fill(!STATE_ERASE_VALUE); + self.state + .write((2 + progress_index) as u32 * STATE::WRITE_SIZE as u32, state_word)?; Ok(()) } - fn copy_page_once_to_active( + fn copy_page_once_to_active( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.active - .erase_blocking(p.active(), to_offset, to_offset + page_size)?; + self.active.erase(to_offset, to_offset + page_size)?; for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { - self.dfu - .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; - self.active - .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; + self.dfu.read(from_offset + offset_in_page as u32, aligned_buf)?; + self.active.write(to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(progress_index, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn copy_page_once_to_dfu( + fn copy_page_once_to_dfu( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?; + self.dfu.erase(to_offset as u32, to_offset + page_size)?; for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { - self.active - .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; - self.dfu - .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; + self.active.read(from_offset + offset_in_page as u32, aligned_buf)?; + self.dfu.write(to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(progress_index, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + fn swap(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; for page_num in 0..page_count { let progress_index = (page_num * 2) as usize; // Copy active page to the 'next' DFU page. - let active_from_offset = (page_count - 1 - page_num) * page_size; - let dfu_to_offset = (page_count - page_num) * page_size; + let active_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; + let dfu_to_offset = (page_count - page_num) * Self::PAGE_SIZE; //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; // Copy DFU page to the active page - let active_to_offset = (page_count - 1 - page_num) * page_size; - let dfu_from_offset = (page_count - 1 - page_num) * page_size; + let active_to_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; + let dfu_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; } Ok(()) } - fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + fn revert(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; for page_num in 0..page_count { let progress_index = (page_count * 2 + page_num * 2) as usize; // Copy the bad active page to the DFU page - let active_from_offset = page_num * page_size; - let dfu_to_offset = page_num * page_size; - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; + let active_from_offset = page_num * Self::PAGE_SIZE; + let dfu_to_offset = page_num * Self::PAGE_SIZE; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; // Copy the DFU page back to the active page - let active_to_offset = page_num * page_size; - let dfu_from_offset = (page_num + 1) * page_size; - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + let active_to_offset = page_num * Self::PAGE_SIZE; + let dfu_from_offset = (page_num + 1) * Self::PAGE_SIZE; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; } Ok(()) } - fn read_state(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - self.state.read_blocking(config.state(), 0, state_word)?; + fn read_state(&mut self, aligned_buf: &mut [u8]) -> Result { + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; + self.state.read(0, state_word)?; if !state_word.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -373,11 +328,16 @@ impl BootLoader { } } -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { - assert_eq!(active.size() % page_size, 0); - assert_eq!(dfu.size() % page_size, 0); - assert!(dfu.size() - active.size() >= page_size); - assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); +fn assert_partitions( + active: &ACTIVE, + dfu: &DFU, + state: &STATE, + page_size: u32, +) { + assert_eq!(active.capacity() as u32 % page_size, 0); + assert_eq!(dfu.capacity() as u32 % page_size, 0); + assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); + assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); } /// A flash wrapper implementing the Flash and embedded_storage traits. @@ -436,98 +396,20 @@ where } } -/// Convenience provider that uses a single flash for all partitions. -pub struct SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - flash: &'a mut F, -} - -impl<'a, F> SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - /// Create a provider for a single flash. - pub fn new(flash: &'a mut F) -> Self { - Self { flash } - } -} - -impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - type STATE = F; - type ACTIVE = F; - type DFU = F; - - fn active(&mut self) -> &mut Self::STATE { - self.flash - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - self.flash - } - fn state(&mut self) -> &mut Self::DFU { - self.flash - } -} - -/// Convenience flash provider that uses separate flash instances for each partition. -pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - active: &'a mut ACTIVE, - state: &'a mut STATE, - dfu: &'a mut DFU, -} - -impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - /// Create a new flash provider with separate configuration for all three partitions. - pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { - Self { active, state, dfu } - } -} - -impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - type STATE = STATE; - type ACTIVE = ACTIVE; - type DFU = DFU; - - fn active(&mut self) -> &mut Self::ACTIVE { - self.active - } - fn dfu(&mut self) -> &mut Self::DFU { - self.dfu - } - fn state(&mut self) -> &mut Self::STATE { - self.state - } -} - #[cfg(test)] mod tests { use super::*; + use crate::mem_flash::MemFlash; #[test] #[should_panic] fn test_range_asserts() { - const ACTIVE: Partition = Partition::new(4096, 4194304); - const DFU: Partition = Partition::new(4194304, 2 * 4194304); - const STATE: Partition = Partition::new(0, 4096); - assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + const ACTIVE_SIZE: usize = 4194304 - 4096; + const DFU_SIZE: usize = 4194304; + const STATE_SIZE: usize = 4096; + static ACTIVE: MemFlash = MemFlash::new(0xFF); + static DFU: MemFlash = MemFlash::new(0xFF); + static STATE: MemFlash = MemFlash::new(0xFF); + assert_partitions(&ACTIVE, &DFU, &STATE, 4096); } } From c844894a6e25bbf38c10ed7e60ee554e565b56b1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 29 May 2023 21:29:13 +0200 Subject: [PATCH 1255/1575] Split the FirmwareUpdater into blocking and async --- embassy-boot/boot/src/firmware_updater.rs | 543 ------------------ .../boot/src/firmware_updater/asynch.rs | 251 ++++++++ .../boot/src/firmware_updater/blocking.rs | 221 +++++++ embassy-boot/boot/src/firmware_updater/mod.rs | 71 +++ 4 files changed, 543 insertions(+), 543 deletions(-) delete mode 100644 embassy-boot/boot/src/firmware_updater.rs create mode 100644 embassy-boot/boot/src/firmware_updater/asynch.rs create mode 100644 embassy-boot/boot/src/firmware_updater/blocking.rs create mode 100644 embassy-boot/boot/src/firmware_updater/mod.rs diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs deleted file mode 100644 index aeea206f9..000000000 --- a/embassy-boot/boot/src/firmware_updater.rs +++ /dev/null @@ -1,543 +0,0 @@ -use digest::Digest; -use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; -#[cfg(feature = "nightly")] -use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; - -use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; - -/// Errors returned by FirmwareUpdater -#[derive(Debug)] -pub enum FirmwareUpdaterError { - /// Error from flash. - Flash(NorFlashErrorKind), - /// Signature errors. - Signature(signature::Error), -} - -#[cfg(feature = "defmt")] -impl defmt::Format for FirmwareUpdaterError { - fn format(&self, fmt: defmt::Formatter) { - match self { - FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), - FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), - } - } -} - -impl From for FirmwareUpdaterError -where - E: NorFlashError, -{ - fn from(error: E) -> Self { - FirmwareUpdaterError::Flash(error.kind()) - } -} - -/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to -/// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { - state: Partition, - dfu: Partition, -} - -#[cfg(target_os = "none")] -impl Default for FirmwareUpdater { - fn default() -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - FirmwareUpdater::new(dfu, state) - } -} - -impl FirmwareUpdater { - /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub const fn new(dfu: Partition, state: Partition) -> Self { - Self { dfu, state } - } - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - #[cfg(feature = "nightly")] - pub async fn get_state( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read(state_flash, 0, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(all(feature = "_verify", feature = "nightly"))] - pub async fn verify_and_mark_updated( - &mut self, - _state_and_dfu_flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: u32, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; - - use crate::digest_adapters::ed25519_dalek::Sha512; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; - - public_key.verify(&message, &signature).map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Signature}; - - use crate::digest_adapters::salty::Sha512; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; - - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await - } - - /// Verify the update in DFU with any digest. - #[cfg(feature = "nightly")] - pub async fn hash( - &mut self, - dfu_flash: &mut F, - update_len: u32, - chunk_buf: &mut [u8], - output: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let mut digest = D::new(); - for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read(dfu_flash, offset, chunk_buf).await?; - let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); - digest.update(&chunk_buf[..len]); - } - output.copy_from_slice(digest.finalize().as_slice()); - Ok(()) - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(all(feature = "nightly", not(feature = "_verify")))] - pub async fn mark_updated( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, state_flash).await - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(feature = "nightly")] - pub async fn mark_booted( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, state_flash).await - } - - #[cfg(feature = "nightly")] - async fn set_magic( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read(state_flash, 0, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; - } - - // Clear magic and progress - self.state.wipe(state_flash).await?; - - // Set magic - aligned.fill(magic); - self.state.write(state_flash, 0, aligned).await?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - #[cfg(feature = "nightly")] - pub async fn write_firmware( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - self.dfu - .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) - .await?; - - self.dfu.write(dfu_flash, offset as u32, data).await?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning its `Partition`. - /// - /// Using this instead of `write_firmware` allows for an optimized API in - /// exchange for added complexity. - #[cfg(feature = "nightly")] - pub async fn prepare_update( - &mut self, - dfu_flash: &mut F, - ) -> Result { - self.dfu.wipe(dfu_flash).await?; - - Ok(self.dfu) - } - - // - // Blocking API - // - - /// Obtain the current state. - /// - /// This is useful to check if the bootloader has just done a swap, in order - /// to do verifications and self-tests of the new image before calling - /// `mark_booted`. - pub fn get_state_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read_blocking(state_flash, 0, aligned)?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } - } - - /// Verify the DFU given a public key. If there is an error then DO NOT - /// proceed with updating the firmware as it must be signed with a - /// corresponding private key (otherwise it could be malicious firmware). - /// - /// Mark to trigger firmware swap on next boot if verify suceeds. - /// - /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have - /// been generated from a SHA-512 digest of the firmware bytes. - /// - /// If no signature feature is set then this method will always return a - /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. - #[cfg(feature = "_verify")] - pub fn verify_and_mark_updated_blocking( - &mut self, - _state_and_dfu_flash: &mut F, - _public_key: &[u8], - _signature: &[u8], - _update_len: u32, - _aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); - - #[cfg(feature = "ed25519-dalek")] - { - use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; - - use crate::digest_adapters::ed25519_dalek::Sha512; - - let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); - - let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; - let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; - - public_key.verify(&message, &signature).map_err(into_signature_error)? - } - #[cfg(feature = "ed25519-salty")] - { - use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; - use salty::{PublicKey, Signature}; - - use crate::digest_adapters::salty::Sha512; - - fn into_signature_error(_: E) -> FirmwareUpdaterError { - FirmwareUpdaterError::Signature(signature::Error::default()) - } - - let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; - let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; - let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; - let signature = Signature::try_from(&signature).map_err(into_signature_error)?; - - let mut message = [0; 64]; - self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; - - let r = public_key.verify(&message, &signature); - trace!( - "Verifying with public key {}, signature {} and message {} yields ok: {}", - public_key.to_bytes(), - signature.to_bytes(), - message, - r.is_ok() - ); - r.map_err(into_signature_error)? - } - - self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) - } - - /// Verify the update in DFU with any digest. - pub fn hash_blocking( - &mut self, - dfu_flash: &mut F, - update_len: u32, - chunk_buf: &mut [u8], - output: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - let mut digest = D::new(); - for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; - let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); - digest.update(&chunk_buf[..len]); - } - output.copy_from_slice(digest.finalize().as_slice()); - Ok(()) - } - - /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(not(feature = "_verify"))] - pub fn mark_updated_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) - } - - /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) - } - - fn set_magic_blocking( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read_blocking(state_flash, 0, aligned)?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; - } - - // Clear magic and progress - self.state.wipe_blocking(state_flash)?; - - // Set magic - aligned.fill(magic); - self.state.write_blocking(state_flash, 0, aligned)?; - } - Ok(()) - } - - /// Write data to a flash page. - /// - /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. - /// - /// # Safety - /// - /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); - - self.dfu - .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; - - self.dfu.write_blocking(dfu_flash, offset as u32, data)?; - - Ok(()) - } - - /// Prepare for an incoming DFU update by erasing the entire DFU area and - /// returning its `Partition`. - /// - /// Using this instead of `write_firmware_blocking` allows for an optimized - /// API in exchange for added complexity. - pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { - self.dfu.wipe_blocking(flash)?; - - Ok(self.dfu) - } -} - -#[cfg(test)] -mod tests { - use futures::executor::block_on; - use sha1::{Digest, Sha1}; - - use super::*; - use crate::mem_flash::MemFlash; - - #[test] - #[cfg(feature = "nightly")] - fn can_verify_sha1() { - const STATE: Partition = Partition::new(0, 4096); - const DFU: Partition = Partition::new(65536, 131072); - - let mut flash = MemFlash::<131072, 4096, 8>::default(); - - let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; - let mut to_write = [0; 4096]; - to_write[..7].copy_from_slice(update.as_slice()); - - let mut updater = FirmwareUpdater::new(DFU, STATE); - block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); - let mut chunk_buf = [0; 2]; - let mut hash = [0; 20]; - block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); - - assert_eq!(Sha1::digest(update).as_slice(), hash); - } -} diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs new file mode 100644 index 000000000..bdd03bff4 --- /dev/null +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -0,0 +1,251 @@ +use digest::Digest; +use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; + +use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; + +impl FirmwareUpdater { + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result { + self.state.read(state_flash, 0, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(all(feature = "_verify", feature = "nightly"))] + pub async fn verify_and_mark_updated( + &mut self, + _state_and_dfu_flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: u32, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_update_len <= self.dfu.size()); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) + .await?; + + public_key.verify(&message, &signature).map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) + .await?; + + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await + } + + /// Verify the update in DFU with any digest. + pub async fn hash( + &mut self, + dfu_flash: &mut F, + update_len: u32, + chunk_buf: &mut [u8], + output: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read(dfu_flash, offset, chunk_buf).await?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); + } + output.copy_from_slice(digest.finalize().as_slice()); + Ok(()) + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(all(feature = "nightly", not(feature = "_verify")))] + pub async fn mark_updated( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC, state_flash).await + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_booted( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC, state_flash).await + } + + async fn set_magic( + &mut self, + aligned: &mut [u8], + magic: u8, + state_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + self.state.read(state_flash, 0, aligned).await?; + + if aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; + + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; + } + + // Clear magic and progress + self.state.wipe(state_flash).await?; + + // Set magic + aligned.fill(magic); + self.state.write(state_flash, 0, aligned).await?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + dfu_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + self.dfu + .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) + .await?; + + self.dfu.write(dfu_flash, offset as u32, data).await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning its `Partition`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub async fn prepare_update( + &mut self, + dfu_flash: &mut F, + ) -> Result { + self.dfu.wipe(dfu_flash).await?; + + Ok(self.dfu) + } +} + +#[cfg(test)] +mod tests { + use futures::executor::block_on; + use sha1::{Digest, Sha1}; + + use super::*; + use crate::mem_flash::MemFlash; + + #[test] + fn can_verify_sha1() { + const STATE: Partition = Partition::new(0, 4096); + const DFU: Partition = Partition::new(65536, 131072); + + let mut flash = MemFlash::<131072, 4096, 8>::default(); + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = FirmwareUpdater::new(DFU, STATE); + block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); + } +} diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs new file mode 100644 index 000000000..50caaf08c --- /dev/null +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -0,0 +1,221 @@ +use digest::Digest; +use embedded_storage::nor_flash::NorFlash; + +use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; + +impl FirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. + pub const fn new(dfu: Partition, state: Partition) -> Self { + Self { dfu, state } + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state_blocking( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result { + self.state.read_blocking(state_flash, 0, aligned)?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Verify the DFU given a public key. If there is an error then DO NOT + /// proceed with updating the firmware as it must be signed with a + /// corresponding private key (otherwise it could be malicious firmware). + /// + /// Mark to trigger firmware swap on next boot if verify suceeds. + /// + /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have + /// been generated from a SHA-512 digest of the firmware bytes. + /// + /// If no signature feature is set then this method will always return a + /// signature error. + /// + /// # Safety + /// + /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + #[cfg(feature = "_verify")] + pub fn verify_and_mark_updated_blocking( + &mut self, + _state_and_dfu_flash: &mut F, + _public_key: &[u8], + _signature: &[u8], + _update_len: u32, + _aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(_aligned.len(), F::WRITE_SIZE); + assert!(_update_len <= self.dfu.size()); + + #[cfg(feature = "ed25519-dalek")] + { + use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; + + use crate::digest_adapters::ed25519_dalek::Sha512; + + let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into()); + + let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; + let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; + + public_key.verify(&message, &signature).map_err(into_signature_error)? + } + #[cfg(feature = "ed25519-salty")] + { + use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH}; + use salty::{PublicKey, Signature}; + + use crate::digest_adapters::salty::Sha512; + + fn into_signature_error(_: E) -> FirmwareUpdaterError { + FirmwareUpdaterError::Signature(signature::Error::default()) + } + + let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?; + let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?; + let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?; + let signature = Signature::try_from(&signature).map_err(into_signature_error)?; + + let mut message = [0; 64]; + self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; + + let r = public_key.verify(&message, &signature); + trace!( + "Verifying with public key {}, signature {} and message {} yields ok: {}", + public_key.to_bytes(), + signature.to_bytes(), + message, + r.is_ok() + ); + r.map_err(into_signature_error)? + } + + self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) + } + + /// Verify the update in DFU with any digest. + pub fn hash_blocking( + &mut self, + dfu_flash: &mut F, + update_len: u32, + chunk_buf: &mut [u8], + output: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + let mut digest = D::new(); + for offset in (0..update_len).step_by(chunk_buf.len()) { + self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; + let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); + digest.update(&chunk_buf[..len]); + } + output.copy_from_slice(digest.finalize().as_slice()); + Ok(()) + } + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub fn mark_updated_blocking( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted_blocking( + &mut self, + state_flash: &mut F, + aligned: &mut [u8], + ) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) + } + + fn set_magic_blocking( + &mut self, + aligned: &mut [u8], + magic: u8, + state_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + self.state.read_blocking(state_flash, 0, aligned)?; + + if aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; + + // FIXME: Do not make this assumption. + const STATE_ERASE_VALUE: u8 = 0xFF; + + if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + aligned.fill(!STATE_ERASE_VALUE); + self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; + } + + // Clear magic and progress + self.state.wipe_blocking(state_flash)?; + + // Set magic + aligned.fill(magic); + self.state.write_blocking(state_flash, 0, aligned)?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + dfu_flash: &mut F, + ) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= F::ERASE_SIZE); + + self.dfu + .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; + + self.dfu.write_blocking(dfu_flash, offset as u32, data)?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning its `Partition`. + /// + /// Using this instead of `write_firmware_blocking` allows for an optimized + /// API in exchange for added complexity. + pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { + self.dfu.wipe_blocking(flash)?; + + Ok(self.dfu) + } +} diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs new file mode 100644 index 000000000..e09f5eb6c --- /dev/null +++ b/embassy-boot/boot/src/firmware_updater/mod.rs @@ -0,0 +1,71 @@ +#[cfg(feature = "nightly")] +mod asynch; +mod blocking; + +use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; + +use crate::Partition; + +/// Errors returned by FirmwareUpdater +#[derive(Debug)] +pub enum FirmwareUpdaterError { + /// Error from flash. + Flash(NorFlashErrorKind), + /// Signature errors. + Signature(signature::Error), +} + +#[cfg(feature = "defmt")] +impl defmt::Format for FirmwareUpdaterError { + fn format(&self, fmt: defmt::Formatter) { + match self { + FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), + FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), + } + } +} + +impl From for FirmwareUpdaterError +where + E: NorFlashError, +{ + fn from(error: E) -> Self { + FirmwareUpdaterError::Flash(error.kind()) + } +} + +/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to +/// 'mess up' the internal bootloader state +pub struct FirmwareUpdater { + state: Partition, + dfu: Partition, +} + +#[cfg(target_os = "none")] +impl Default for FirmwareUpdater { + fn default() -> Self { + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + let dfu = unsafe { + Partition::new( + &__bootloader_dfu_start as *const u32 as u32, + &__bootloader_dfu_end as *const u32 as u32, + ) + }; + let state = unsafe { + Partition::new( + &__bootloader_state_start as *const u32 as u32, + &__bootloader_state_end as *const u32 as u32, + ) + }; + + trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); + trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); + FirmwareUpdater::new(dfu, state) + } +} From aba0f8fd6cd51cad65480689bc9254df4f071175 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 29 May 2023 14:49:43 -0500 Subject: [PATCH 1256/1575] stm32/uart: refactor rx ringbuffer - remove some race conditions - allow full use of rx buffer --- .vscode/.gitignore | 3 +- embassy-stm32/src/dma/bdma.rs | 40 +- embassy-stm32/src/dma/dma.rs | 40 +- embassy-stm32/src/dma/ringbuffer.rs | 529 ++++++++++-------- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/usart/mod.rs | 50 +- .../{rx_ringbuffered.rs => ringbuffered.rs} | 182 +++--- 7 files changed, 422 insertions(+), 424 deletions(-) rename embassy-stm32/src/usart/{rx_ringbuffered.rs => ringbuffered.rs} (63%) diff --git a/.vscode/.gitignore b/.vscode/.gitignore index 9fbb9ec95..8c3dd8a31 100644 --- a/.vscode/.gitignore +++ b/.vscode/.gitignore @@ -1,3 +1,4 @@ *.cortex-debug.*.json launch.json -tasks.json \ No newline at end of file +tasks.json +*.cfg diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 0202ec379..9dafa26d0 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -111,24 +111,18 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); } - let mut wake = false; - if isr.htif(channel_num) && cr.read().htie() { // Acknowledge half transfer complete interrupt dma.ifcr().write(|w| w.set_htif(channel_num, true)); - wake = true; - } - - if isr.tcif(channel_num) && cr.read().tcie() { + } else if isr.tcif(channel_num) && cr.read().tcie() { // Acknowledge transfer complete interrupt dma.ifcr().write(|w| w.set_tcif(channel_num, true)); STATE.complete_count[index].fetch_add(1, Ordering::Release); - wake = true; + } else { + return; } - if wake { - STATE.ch_wakers[index].wake(); - } + STATE.ch_wakers[index].wake(); } #[cfg(any(bdma_v2, dmamux))] @@ -371,7 +365,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { - fn ndtr(&self) -> usize { + fn get_remaining_transfers(&self) -> usize { let ch = self.0.regs().ch(self.0.num()); unsafe { ch.ndtr().read() }.ndt() as usize } @@ -457,21 +451,17 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } /// Read bytes from the ring buffer + /// Return a tuple of the length read and the length remaining in the buffer + /// If not all of the bytes were read, then there will be some bytes in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, buf: &mut [W]) -> Result { + pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } - pub fn is_empty(&self) -> bool { - self.ringbuf.is_empty() - } - - pub fn len(&self) -> usize { - self.ringbuf.len() - } - - pub fn capacity(&self) -> usize { - self.ringbuf.dma_buf.len() + /// The capacity of the ringbuffer + pub fn cap(&self) -> usize { + self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { @@ -506,12 +496,6 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let ch = self.channel.regs().ch(self.channel.num()); unsafe { ch.cr().read() }.en() } - - /// Synchronize the position of the ring buffer to the actual DMA controller position - pub fn reload_position(&mut self) { - let ch = self.channel.regs().ch(self.channel.num()); - self.ringbuf.ndtr = unsafe { ch.ndtr().read() }.ndt() as usize; - } } impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 7b17d9e49..47b749ece 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -187,24 +187,18 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); } - let mut wake = false; - if isr.htif(channel_num % 4) && cr.read().htie() { // Acknowledge half transfer complete interrupt dma.ifcr(channel_num / 4).write(|w| w.set_htif(channel_num % 4, true)); - wake = true; - } - - if isr.tcif(channel_num % 4) && cr.read().tcie() { + } else if isr.tcif(channel_num % 4) && cr.read().tcie() { // Acknowledge transfer complete interrupt dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); STATE.complete_count[index].fetch_add(1, Ordering::Release); - wake = true; + } else { + return; } - if wake { - STATE.ch_wakers[index].wake(); - } + STATE.ch_wakers[index].wake(); } #[cfg(any(dma_v2, dmamux))] @@ -612,7 +606,7 @@ impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { - fn ndtr(&self) -> usize { + fn get_remaining_transfers(&self) -> usize { let ch = self.0.regs().st(self.0.num()); unsafe { ch.ndtr().read() }.ndt() as usize } @@ -713,21 +707,17 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } /// Read bytes from the ring buffer + /// Return a tuple of the length read and the length remaining in the buffer + /// If not all of the bytes were read, then there will be some bytes in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, buf: &mut [W]) -> Result { + pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } - pub fn is_empty(&self) -> bool { - self.ringbuf.is_empty() - } - - pub fn len(&self) -> usize { - self.ringbuf.len() - } - - pub fn capacity(&self) -> usize { - self.ringbuf.dma_buf.len() + // The capacity of the ringbuffer + pub fn cap(&self) -> usize { + self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { @@ -766,12 +756,6 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let ch = self.channel.regs().st(self.channel.num()); unsafe { ch.cr().read() }.en() } - - /// Synchronize the position of the ring buffer to the actual DMA controller position - pub fn reload_position(&mut self) { - let ch = self.channel.regs().st(self.channel.num()); - self.ringbuf.ndtr = unsafe { ch.ndtr().read() }.ndt() as usize; - } } impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 38cc87ae9..72a84a57f 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -25,14 +25,13 @@ use super::word::Word; /// +-----------------------------------------+ +-----------------------------------------+ /// ^ ^ ^ ^ ^ ^ /// | | | | | | -/// +- first --+ | +- end ------+ | +/// +- start --+ | +- end ------+ | /// | | | | -/// +- end --------------------+ +- first ----------------+ +/// +- end --------------------+ +- start ----------------+ /// ``` pub struct DmaRingBuffer<'a, W: Word> { pub(crate) dma_buf: &'a mut [W], - first: usize, - pub ndtr: usize, + start: usize, } #[derive(Debug, PartialEq)] @@ -41,7 +40,7 @@ pub struct OverrunError; pub trait DmaCtrl { /// Get the NDTR register value, i.e. the space left in the underlying /// buffer until the dma writer wraps. - fn ndtr(&self) -> usize; + fn get_remaining_transfers(&self) -> usize; /// Get the transfer completed counter. /// This counter is incremented by the dma controller when NDTR is reloaded, @@ -54,151 +53,131 @@ pub trait DmaCtrl { impl<'a, W: Word> DmaRingBuffer<'a, W> { pub fn new(dma_buf: &'a mut [W]) -> Self { - let ndtr = dma_buf.len(); - Self { - dma_buf, - first: 0, - ndtr, - } + Self { dma_buf, start: 0 } } /// Reset the ring buffer to its initial state pub fn clear(&mut self, mut dma: impl DmaCtrl) { - self.first = 0; - self.ndtr = self.dma_buf.len(); + self.start = 0; dma.reset_complete_count(); } - /// The buffer end position - fn end(&self) -> usize { - self.dma_buf.len() - self.ndtr + /// The capacity of the ringbuffer + pub const fn cap(&self) -> usize { + self.dma_buf.len() } - /// Returns whether the buffer is empty - pub fn is_empty(&self) -> bool { - self.first == self.end() - } - - /// The current number of bytes in the buffer - /// This may change at any time if dma is currently active - pub fn len(&self) -> usize { - // Read out a stable end (the dma periheral can change it at anytime) - let end = self.end(); - if self.first <= end { - // No wrap - end - self.first - } else { - self.dma_buf.len() - self.first + end - } + /// The current position of the ringbuffer + fn pos(&self, remaining_transfers: usize) -> usize { + self.cap() - remaining_transfers } /// Read bytes from the ring buffer + /// Return a tuple of the length read and the length remaining in the buffer + /// If not all of the bytes were read, then there will be some bytes in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result { - let end = self.end(); + pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + /* + This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check + after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed + to fire in the same clock cycle that a register is read, so checking get_complete_count early does + not yield relevant information. - compiler_fence(Ordering::SeqCst); + Therefore, the only variable we really need to know is ndtr. If the dma has overrun by more than a full + buffer, we will do a bit more work than we have to, but algorithms should not be optimized for error + conditions. - if self.first == end { - // The buffer is currently empty - - if dma.get_complete_count() > 0 { - // The DMA has written such that the ring buffer wraps at least once - self.ndtr = dma.ndtr(); - if self.end() > self.first || dma.get_complete_count() > 1 { - return Err(OverrunError); - } - } - - Ok(0) - } else if self.first < end { + After we've done our work, we confirm that we haven't overrun more than a full buffer, and also that + the dma has not overrun within the data we could have copied. We check the data we could have copied + rather than the data we actually copied because it costs nothing and confirms an error condition + earlier. + */ + let end = self.pos(dma.get_remaining_transfers()); + if self.start == end && dma.get_complete_count() == 0 { + // No bytes are available in the buffer + Ok((0, self.cap())) + } else if self.start < end { // The available, unread portion in the ring buffer DOES NOT wrap - - if dma.get_complete_count() > 1 { - return Err(OverrunError); - } - // Copy out the bytes from the dma buffer - let len = self.copy_to(buf, self.first..end); + let len = self.copy_to(buf, self.start..end); compiler_fence(Ordering::SeqCst); - match dma.get_complete_count() { - 0 => { - // The DMA writer has not wrapped before nor after the copy - } - 1 => { - // The DMA writer has written such that the ring buffer now wraps - self.ndtr = dma.ndtr(); - if self.end() > self.first || dma.get_complete_count() > 1 { - // The bytes that we have copied out have overflowed - // as the writer has now both wrapped and is currently writing - // within the region that we have just copied out - return Err(OverrunError); - } - } - _ => { - return Err(OverrunError); - } - } + /* + first, check if the dma has wrapped at all if it's after end + or more than once if it's before start - self.first = (self.first + len) % self.dma_buf.len(); - Ok(len) + this is in a critical section to try to reduce mushy behavior. + it's not ideal but it's the best we can do + + then, get the current position of of the dma write and check + if it's inside data we could have copied + */ + let (pos, complete_count) = + critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count())); + if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { + Err(OverrunError) + } else { + self.start = (self.start + len) % self.cap(); + + Ok((len, self.cap() - self.start)) + } + } else if self.start + buf.len() < self.cap() { + // The available, unread portion in the ring buffer DOES wrap + // The DMA writer has wrapped since we last read and is currently + // writing (or the next byte added will be) in the beginning of the ring buffer. + + // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. + + // Copy out from the dma buffer + let len = self.copy_to(buf, self.start..self.cap()); + + compiler_fence(Ordering::SeqCst); + + /* + first, check if the dma has wrapped around more than once + + then, get the current position of of the dma write and check + if it's inside data we could have copied + */ + let pos = self.pos(dma.get_remaining_transfers()); + if pos > self.start || pos < end || dma.get_complete_count() > 1 { + Err(OverrunError) + } else { + self.start = (self.start + len) % self.cap(); + + Ok((len, self.start + end)) + } } else { // The available, unread portion in the ring buffer DOES wrap // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - let complete_count = dma.get_complete_count(); - if complete_count > 1 { - return Err(OverrunError); - } + // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, + // so the next read will not have any unread tail bytes in the ring buffer. - // If the unread portion wraps then the writer must also have wrapped - assert!(complete_count == 1); + // Copy out from the dma buffer + let tail = self.copy_to(buf, self.start..self.cap()); + let head = self.copy_to(&mut buf[tail..], 0..end); - if self.first + buf.len() < self.dma_buf.len() { - // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. + compiler_fence(Ordering::SeqCst); - // Copy out from the dma buffer - let len = self.copy_to(buf, self.first..self.dma_buf.len()); + /* + first, check if the dma has wrapped around more than once - compiler_fence(Ordering::SeqCst); - - // We have now copied out the data from dma_buf - // Make sure that the just read part was not overwritten during the copy - self.ndtr = dma.ndtr(); - if self.end() > self.first || dma.get_complete_count() > 1 { - // The writer has entered the data that we have just read since we read out `end` in the beginning and until now. - return Err(OverrunError); - } - - self.first = (self.first + len) % self.dma_buf.len(); - Ok(len) + then, get the current position of of the dma write and check + if it's inside data we could have copied + */ + let pos = self.pos(dma.get_remaining_transfers()); + if pos > self.start || pos < end || dma.reset_complete_count() > 1 { + Err(OverrunError) } else { - // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, - // so the next read will not have any unread tail bytes in the ring buffer. - - // Copy out from the dma buffer - let tail = self.copy_to(buf, self.first..self.dma_buf.len()); - let head = self.copy_to(&mut buf[tail..], 0..end); - - compiler_fence(Ordering::SeqCst); - - // We have now copied out the data from dma_buf - // Reset complete counter and make sure that the just read part was not overwritten during the copy - self.ndtr = dma.ndtr(); - let complete_count = dma.reset_complete_count(); - if self.end() > self.first || complete_count > 1 { - return Err(OverrunError); - } - - self.first = head; - Ok(tail + head) + self.start = head; + Ok((tail + head, self.cap() - self.start)) } } } - /// Copy from the dma buffer at `data_range` into `buf` fn copy_to(&mut self, buf: &mut [W], data_range: Range) -> usize { // Limit the number of bytes that can be copied @@ -218,203 +197,289 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { length } } - #[cfg(test)] mod tests { use core::array; - use core::cell::RefCell; + use std::{cell, vec}; use super::*; - struct TestCtrl { - next_ndtr: RefCell>, - complete_count: usize, + #[allow(dead_code)] + #[derive(PartialEq, Debug)] + enum TestCircularTransferRequest { + GetCompleteCount(usize), + ResetCompleteCount(usize), + PositionRequest(usize), } - impl TestCtrl { - pub const fn new() -> Self { - Self { - next_ndtr: RefCell::new(None), - complete_count: 0, + struct TestCircularTransfer { + len: usize, + requests: cell::RefCell>, + } + + impl DmaCtrl for &mut TestCircularTransfer { + fn get_remaining_transfers(&self) -> usize { + match self.requests.borrow_mut().pop().unwrap() { + TestCircularTransferRequest::PositionRequest(pos) => { + let len = self.len; + + assert!(len >= pos); + + len - pos + } + _ => unreachable!(), } } - pub fn set_next_ndtr(&mut self, ndtr: usize) { - self.next_ndtr.borrow_mut().replace(ndtr); - } - } - - impl DmaCtrl for &mut TestCtrl { - fn ndtr(&self) -> usize { - self.next_ndtr.borrow_mut().unwrap() - } - fn get_complete_count(&self) -> usize { - self.complete_count + match self.requests.borrow_mut().pop().unwrap() { + TestCircularTransferRequest::GetCompleteCount(complete_count) => complete_count, + _ => unreachable!(), + } } fn reset_complete_count(&mut self) -> usize { - let old = self.complete_count; - self.complete_count = 0; - old + match self.requests.get_mut().pop().unwrap() { + TestCircularTransferRequest::ResetCompleteCount(complete_count) => complete_count, + _ => unreachable!(), + } + } + } + + impl TestCircularTransfer { + pub fn new(len: usize) -> Self { + Self { + requests: cell::RefCell::new(vec![]), + len: len, + } + } + + pub fn setup(&self, mut requests: vec::Vec) { + requests.reverse(); + self.requests.replace(requests); } } #[test] - fn empty() { + fn empty_and_read_not_started() { let mut dma_buf = [0u8; 16]; let ringbuf = DmaRingBuffer::new(&mut dma_buf); - assert!(ringbuf.is_empty()); - assert_eq!(0, ringbuf.len()); + assert_eq!(0, ringbuf.start); } #[test] fn can_read() { + let mut dma = TestCircularTransfer::new(16); + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); - ringbuf.ndtr = 6; - assert!(!ringbuf.is_empty()); - assert_eq!(10, ringbuf.len()); + assert_eq!(0, ringbuf.start); + assert_eq!(16, ringbuf.len()); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(8), + TestCircularTransferRequest::PositionRequest(10), + TestCircularTransferRequest::GetCompleteCount(0), + ]); let mut buf = [0; 2]; - assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); assert_eq!([0, 1], buf); - assert_eq!(8, ringbuf.len()); + assert_eq!(2, ringbuf.start); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(10), + TestCircularTransferRequest::PositionRequest(12), + TestCircularTransferRequest::GetCompleteCount(0), + ]); let mut buf = [0; 2]; - assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); assert_eq!([2, 3], buf); - assert_eq!(6, ringbuf.len()); + assert_eq!(4, ringbuf.start); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(12), + TestCircularTransferRequest::PositionRequest(14), + TestCircularTransferRequest::GetCompleteCount(0), + ]); let mut buf = [0; 8]; - assert_eq!(6, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!(8, ringbuf.read(&mut dma, &mut buf).unwrap().0); assert_eq!([4, 5, 6, 7, 8, 9], buf[..6]); - assert_eq!(0, ringbuf.len()); - - let mut buf = [0; 2]; - assert_eq!(0, ringbuf.read(&mut ctrl, &mut buf).unwrap()); + assert_eq!(12, ringbuf.start); } #[test] fn can_read_with_wrap() { + let mut dma = TestCircularTransfer::new(16); + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); - ringbuf.first = 12; - ringbuf.ndtr = 10; - // The dma controller has written 4 + 6 bytes and has reloaded NDTR - ctrl.complete_count = 1; - ctrl.set_next_ndtr(10); + assert_eq!(0, ringbuf.start); + assert_eq!(16, ringbuf.len()); - assert!(!ringbuf.is_empty()); - assert_eq!(6 + 4, ringbuf.len()); + /* + Read to close to the end of the buffer + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(14), + TestCircularTransferRequest::PositionRequest(16), + TestCircularTransferRequest::GetCompleteCount(0), + ]); + let mut buf = [0; 14]; + assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(14, ringbuf.start); - let mut buf = [0; 2]; - assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); - assert_eq!([12, 13], buf); - assert_eq!(6 + 2, ringbuf.len()); - - let mut buf = [0; 4]; - assert_eq!(4, ringbuf.read(&mut ctrl, &mut buf).unwrap()); - assert_eq!([14, 15, 0, 1], buf); - assert_eq!(4, ringbuf.len()); + /* + Now, read around the buffer + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(6), + TestCircularTransferRequest::PositionRequest(8), + TestCircularTransferRequest::ResetCompleteCount(1), + ]); + let mut buf = [0; 6]; + assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(4, ringbuf.start); } #[test] fn can_read_when_dma_writer_is_wrapped_and_read_does_not_wrap() { + let mut dma = TestCircularTransfer::new(16); + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); - ringbuf.first = 2; - ringbuf.ndtr = 6; - // The dma controller has written 6 + 2 bytes and has reloaded NDTR - ctrl.complete_count = 1; - ctrl.set_next_ndtr(14); + assert_eq!(0, ringbuf.start); + assert_eq!(16, ringbuf.len()); + /* + Read to close to the end of the buffer + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(14), + TestCircularTransferRequest::PositionRequest(16), + TestCircularTransferRequest::GetCompleteCount(0), + ]); + let mut buf = [0; 14]; + assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(14, ringbuf.start); + + /* + Now, read to the end of the buffer + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(6), + TestCircularTransferRequest::PositionRequest(8), + TestCircularTransferRequest::ResetCompleteCount(1), + ]); let mut buf = [0; 2]; - assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); - assert_eq!([2, 3], buf); - - assert_eq!(1, ctrl.complete_count); // The interrupt flag IS NOT cleared + assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(0, ringbuf.start); } #[test] - fn can_read_when_dma_writer_is_wrapped_and_read_wraps() { + fn can_read_when_dma_writer_wraps_once_with_same_ndtr() { + let mut dma = TestCircularTransfer::new(16); + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); - ringbuf.first = 12; - ringbuf.ndtr = 10; - // The dma controller has written 6 + 2 bytes and has reloaded NDTR - ctrl.complete_count = 1; - ctrl.set_next_ndtr(14); + assert_eq!(0, ringbuf.start); + assert_eq!(16, ringbuf.len()); - let mut buf = [0; 10]; - assert_eq!(10, ringbuf.read(&mut ctrl, &mut buf).unwrap()); - assert_eq!([12, 13, 14, 15, 0, 1, 2, 3, 4, 5], buf); + /* + Read to about the middle of the buffer + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(6), + TestCircularTransferRequest::PositionRequest(6), + TestCircularTransferRequest::GetCompleteCount(0), + ]); + let mut buf = [0; 6]; + assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(6, ringbuf.start); - assert_eq!(0, ctrl.complete_count); // The interrupt flag IS cleared - } - - #[test] - fn cannot_read_when_dma_writer_wraps_with_same_ndtr() { - let mut dma_buf = [0u8; 16]; - let mut ctrl = TestCtrl::new(); - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); - ringbuf.first = 6; - ringbuf.ndtr = 10; - ctrl.set_next_ndtr(9); - - assert!(ringbuf.is_empty()); // The ring buffer thinks that it is empty - - // The dma controller has written exactly 16 bytes - ctrl.complete_count = 1; - - let mut buf = [0; 2]; - assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); - - assert_eq!(1, ctrl.complete_count); // The complete counter is not reset + /* + Now, wrap the DMA controller around + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(6), + TestCircularTransferRequest::GetCompleteCount(1), + TestCircularTransferRequest::PositionRequest(6), + TestCircularTransferRequest::GetCompleteCount(1), + ]); + let mut buf = [0; 6]; + assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(12, ringbuf.start); } #[test] fn cannot_read_when_dma_writer_overwrites_during_not_wrapping_read() { + let mut dma = TestCircularTransfer::new(16); + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); - ringbuf.first = 2; - ringbuf.ndtr = 6; - // The dma controller has written 6 + 3 bytes and has reloaded NDTR - ctrl.complete_count = 1; - ctrl.set_next_ndtr(13); + assert_eq!(0, ringbuf.start); + assert_eq!(16, ringbuf.len()); - let mut buf = [0; 2]; - assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); + /* + Read a few bytes + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(2), + TestCircularTransferRequest::PositionRequest(2), + TestCircularTransferRequest::GetCompleteCount(0), + ]); + let mut buf = [0; 6]; + assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(2, ringbuf.start); - assert_eq!(1, ctrl.complete_count); // The complete counter is not reset + /* + Now, overtake the reader + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(4), + TestCircularTransferRequest::PositionRequest(6), + TestCircularTransferRequest::GetCompleteCount(1), + ]); + let mut buf = [0; 6]; + assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err()); } #[test] fn cannot_read_when_dma_writer_overwrites_during_wrapping_read() { + let mut dma = TestCircularTransfer::new(16); + let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ctrl = TestCtrl::new(); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); - ringbuf.first = 12; - ringbuf.ndtr = 10; - // The dma controller has written 6 + 13 bytes and has reloaded NDTR - ctrl.complete_count = 1; - ctrl.set_next_ndtr(3); + assert_eq!(0, ringbuf.start); + assert_eq!(16, ringbuf.len()); - let mut buf = [0; 2]; - assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); + /* + Read to close to the end of the buffer + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(14), + TestCircularTransferRequest::PositionRequest(16), + TestCircularTransferRequest::GetCompleteCount(0), + ]); + let mut buf = [0; 14]; + assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); + assert_eq!(14, ringbuf.start); - assert_eq!(1, ctrl.complete_count); // The complete counter is not reset + /* + Now, overtake the reader + */ + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(8), + TestCircularTransferRequest::PositionRequest(10), + TestCircularTransferRequest::ResetCompleteCount(2), + ]); + let mut buf = [0; 6]; + assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err()); } } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index c9df5c1b2..1258a65f1 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] // This must go FIRST so that all the other modules see its macros. diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 061c859a8..05ccb8749 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -13,6 +13,12 @@ use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; #[cfg(not(any(usart_v1, usart_v2)))] +#[allow(unused_imports)] +use crate::pac::usart::regs::Isr as Sr; +#[cfg(any(usart_v1, usart_v2))] +#[allow(unused_imports)] +use crate::pac::usart::regs::Sr; +#[cfg(not(any(usart_v1, usart_v2)))] use crate::pac::usart::Lpuart as Regs; #[cfg(any(usart_v1, usart_v2))] use crate::pac::usart::Usart as Regs; @@ -32,7 +38,6 @@ impl interrupt::Handler for InterruptHandler let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; - let mut wake = false; let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); if has_errors { // clear all interrupts and DMA Rx Request @@ -52,35 +57,24 @@ impl interrupt::Handler for InterruptHandler w.set_dmar(false); }); } + } else if cr1.idleie() && sr.idle() { + // IDLE detected: no more data will come + unsafe { + r.cr1().modify(|w| { + // disable idle line detection + w.set_idleie(false); + }); + } + } else if cr1.rxneie() { + // We cannot check the RXNE flag as it is auto-cleared by the DMA controller - wake = true; + // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection } else { - if cr1.idleie() && sr.idle() { - // IDLE detected: no more data will come - unsafe { - r.cr1().modify(|w| { - // disable idle line detection - w.set_idleie(false); - }); - } - - wake = true; - } - - if cr1.rxneie() { - // We cannot check the RXNE flag as it is auto-cleared by the DMA controller - - // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection - - wake = true; - } + return; } - if wake { - compiler_fence(Ordering::SeqCst); - - s.rx_waker.wake(); - } + compiler_fence(Ordering::SeqCst); + s.rx_waker.wake(); } } @@ -1109,9 +1103,9 @@ pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler; mod buffered; #[cfg(not(gpdma))] -mod rx_ringbuffered; +mod ringbuffered; #[cfg(not(gpdma))] -pub use rx_ringbuffered::RingBufferedUartRx; +pub use ringbuffered::RingBufferedUartRx; use self::sealed::Kind; diff --git a/embassy-stm32/src/usart/rx_ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs similarity index 63% rename from embassy-stm32/src/usart/rx_ringbuffered.rs rename to embassy-stm32/src/usart/ringbuffered.rs index 33b750497..511b71c7f 100644 --- a/embassy-stm32/src/usart/rx_ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -2,13 +2,12 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; use embassy_hal_common::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; -use crate::dma::ringbuffer::OverrunError; use crate::dma::RingBuffer; +use crate::usart::{Regs, Sr}; pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma> { _peri: PeripheralRef<'d, T>, @@ -24,7 +23,9 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { let request = self.rx_dma.request(); let opts = Default::default(); + let ring_buf = unsafe { RingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; + RingBufferedUartRx { _peri: self._peri, ring_buf, @@ -42,11 +43,18 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD Ok(()) } + fn stop(&mut self, err: Error) -> Result { + self.teardown_uart(); + + Err(err) + } + /// Start uart background receive fn setup_uart(&mut self) { // fence before starting DMA. compiler_fence(Ordering::SeqCst); + // start the dma controller self.ring_buf.start(); let r = T::regs(); @@ -58,8 +66,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD w.set_rxneie(false); // enable parity interrupt if not ParityNone w.set_peie(w.pce()); - // disable idle line interrupt - w.set_idleie(false); + // enable idle line interrupt + w.set_idleie(true); }); r.cr3().modify(|w| { // enable Error Interrupt: (Frame error, Noise error, Overrun error) @@ -72,6 +80,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD /// Stop uart background receive fn teardown_uart(&mut self) { + self.ring_buf.request_stop(); + let r = T::regs(); // clear all interrupts and DMA Rx Request // SAFETY: only clears Rx related flags @@ -93,9 +103,6 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD } compiler_fence(Ordering::SeqCst); - - self.ring_buf.request_stop(); - while self.ring_buf.is_running() {} } /// Read bytes that are readily available in the ring buffer. @@ -111,96 +118,49 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD // Start background receive if it was not already started // SAFETY: read only - let is_started = unsafe { r.cr3().read().dmar() }; - if !is_started { - self.start()?; - } + match unsafe { r.cr3().read().dmar() } { + false => self.start()?, + _ => {} + }; - // SAFETY: read only and we only use Rx related flags - let s = unsafe { sr(r).read() }; - let has_errors = s.pe() || s.fe() || s.ne() || s.ore(); - if has_errors { - self.teardown_uart(); - - if s.pe() { - return Err(Error::Parity); - } else if s.fe() { - return Err(Error::Framing); - } else if s.ne() { - return Err(Error::Noise); - } else { - return Err(Error::Overrun); - } - } - - self.ring_buf.reload_position(); - match self.ring_buf.read(buf) { - Ok(len) if len == 0 => {} - Ok(len) => { - assert!(len > 0); - return Ok(len); - } - Err(OverrunError) => { - // Stop any transfer from now on - // The user must re-start to receive any more data - self.teardown_uart(); - return Err(Error::Overrun); - } - } + check_for_errors(clear_idle_flag(T::regs()))?; loop { - self.wait_for_data_or_idle().await?; + match self.ring_buf.read(buf) { + Ok((0, _)) => {} + Ok((len, _)) => { + return Ok(len); + } + Err(_) => { + return self.stop(Error::Overrun); + } + } - self.ring_buf.reload_position(); - if !self.ring_buf.is_empty() { - break; + match self.wait_for_data_or_idle().await { + Ok(_) => {} + Err(err) => { + return self.stop(err); + } } } - - let len = self.ring_buf.read(buf).map_err(|_err| Error::Overrun)?; - assert!(len > 0); - - Ok(len) } /// Wait for uart idle or dma half-full or full async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { - let r = T::regs(); - - // make sure USART state is restored to neutral state - let _on_drop = OnDrop::new(move || { - // SAFETY: only clears Rx related flags - unsafe { - r.cr1().modify(|w| { - // disable idle line interrupt - w.set_idleie(false); - }); - } - }); - - // SAFETY: only sets Rx related flags - unsafe { - r.cr1().modify(|w| { - // enable idle line interrupt - w.set_idleie(true); - }); - } - compiler_fence(Ordering::SeqCst); + let mut dma_init = false; // Future which completes when there is dma is half full or full let dma = poll_fn(|cx| { self.ring_buf.set_waker(cx.waker()); - compiler_fence(Ordering::SeqCst); + let status = match dma_init { + false => Poll::Pending, + true => Poll::Ready(()), + }; - self.ring_buf.reload_position(); - if !self.ring_buf.is_empty() { - // Some data is now available - Poll::Ready(()) - } else { - Poll::Pending - } + dma_init = true; + status }); // Future which completes when idle line is detected @@ -210,28 +170,11 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD compiler_fence(Ordering::SeqCst); - // SAFETY: read only and we only use Rx related flags - let sr = unsafe { sr(r).read() }; + // Critical section is needed so that IDLE isn't set after + // our read but before we clear it. + let sr = critical_section::with(|_| clear_idle_flag(T::regs())); - // SAFETY: only clears Rx related flags - unsafe { - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); - } - - let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore(); - if has_errors { - if sr.pe() { - return Poll::Ready(Err(Error::Parity)); - } else if sr.fe() { - return Poll::Ready(Err(Error::Framing)); - } else if sr.ne() { - return Poll::Ready(Err(Error::Noise)); - } else { - return Poll::Ready(Err(Error::Overrun)); - } - } + check_for_errors(sr)?; if sr.idle() { // Idle line is detected @@ -243,11 +186,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD match select(dma, uart).await { Either::Left(((), _)) => Ok(()), - Either::Right((Ok(()), _)) => Ok(()), - Either::Right((Err(e), _)) => { - self.teardown_uart(); - Err(e) - } + Either::Right((result, _)) => result, } } } @@ -257,6 +196,37 @@ impl> Drop for RingBufferedUartRx<'_, T self.teardown_uart(); } } +/// Return an error result if the Sr register has errors +fn check_for_errors(s: Sr) -> Result<(), Error> { + if s.pe() { + Err(Error::Parity) + } else if s.fe() { + Err(Error::Framing) + } else if s.ne() { + Err(Error::Noise) + } else if s.ore() { + Err(Error::Overrun) + } else { + Ok(()) + } +} + +/// Clear IDLE and return the Sr register +fn clear_idle_flag(r: Regs) -> Sr { + unsafe { + // SAFETY: read only and we only use Rx related flags + + let sr = sr(r).read(); + + // This read also clears the error and idle interrupt flags on v1. + rdr(r).read_volatile(); + clear_interrupt_flags(r, sr); + + r.cr1().modify(|w| w.set_idleie(true)); + + sr + } +} #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { From 94046f30ffefc96a7b110ed1d9bb60d64cb4780f Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 27 May 2023 19:26:45 +0200 Subject: [PATCH 1257/1575] Remove the usage of the local Partition type in BootLoader --- embassy-boot/boot/Cargo.toml | 1 + embassy-boot/boot/src/boot_loader.rs | 344 +++++++++------------------ 2 files changed, 114 insertions(+), 231 deletions(-) diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index f641d5e1c..3fdf69c5e 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -39,6 +39,7 @@ env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } sha1 = "0.10.5" +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } [dev-dependencies.ed25519-dalek] default_features = false diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index b959de2c4..50eb5e668 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -1,6 +1,8 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; -use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use crate::{State, BOOT_MAGIC, SWAP_MAGIC}; + +const STATE_ERASE_VALUE: u8 = 0xFF; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -30,65 +32,39 @@ where } } -/// Trait defining the flash handles used for active and DFU partition. -pub trait FlashConfig { - /// The erase value of the state flash. Typically the default of 0xFF is used, but some flashes use a different value. - const STATE_ERASE_VALUE: u8 = 0xFF; - /// Flash type used for the state partition. - type STATE: NorFlash; - /// Flash type used for the active partition. - type ACTIVE: NorFlash; - /// Flash type used for the dfu partition. - type DFU: NorFlash; - - /// Return flash instance used to write/read to/from active partition. - fn active(&mut self) -> &mut Self::ACTIVE; - /// Return flash instance used to write/read to/from dfu partition. - fn dfu(&mut self) -> &mut Self::DFU; - /// Return flash instance used to write/read to/from bootloader state. - fn state(&mut self) -> &mut Self::STATE; -} - -trait FlashConfigEx { - fn page_size() -> u32; -} - -impl FlashConfigEx for T { - /// Get the page size which is the "unit of operation" within the bootloader. - fn page_size() -> u32 { - core::cmp::max(T::ACTIVE::ERASE_SIZE, T::DFU::ERASE_SIZE) as u32 - } -} - /// BootLoader works with any flash implementing embedded_storage. -pub struct BootLoader { - // Page with current state of bootloader. The state partition has the following format: - // All ranges are in multiples of WRITE_SIZE bytes. - // | Range | Description | - // | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - // | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | - // | 2..2 + N | Progress index used while swapping or reverting | - state: Partition, - // Location of the partition which will be booted from - active: Partition, - // Location of the partition which will be swapped in when requested - dfu: Partition, +pub struct BootLoader { + /// Flash type used for the active partition - the partition which will be booted from. + active: ACTIVE, + /// Flash type used for the dfu partition - he partition which will be swapped in when requested. + dfu: DFU, + /// Flash type used for the state partition. + /// + /// The state partition has the following format: + /// All ranges are in multiples of WRITE_SIZE bytes. + /// | Range | Description | + /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | + /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | + /// | 2..2 + N | Progress index used while swapping or reverting + state: STATE, } -impl BootLoader { - /// Create a new instance of a bootloader with the given partitions. +impl BootLoader { + /// Get the page size which is the "unit of operation" within the bootloader. + const PAGE_SIZE: u32 = if ACTIVE::ERASE_SIZE > DFU::ERASE_SIZE { + ACTIVE::ERASE_SIZE as u32 + } else { + DFU::ERASE_SIZE as u32 + }; + + /// Create a new instance of a bootloader with the flash partitions. /// /// - All partitions must be aligned with the PAGE_SIZE const generic parameter. /// - The dfu partition must be at least PAGE_SIZE bigger than the active partition. - pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { + pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { Self { active, dfu, state } } - /// Return the offset of the active partition into the active flash. - pub fn boot_address(&self) -> usize { - self.active.from as usize - } - /// Perform necessary boot preparations like swapping images. /// /// The DFU partition is assumed to be 1 page bigger than the active partition for the swap @@ -175,195 +151,174 @@ impl BootLoader { /// | DFU | 3 | 3 | 2 | 1 | 3 | /// +-----------+--------------+--------+--------+--------+--------+ /// - pub fn prepare_boot(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { + pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(0, P::page_size() % aligned_buf.len() as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::ACTIVE::ERASE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::WRITE_SIZE as u32); - assert_eq!(0, P::page_size() % P::DFU::ERASE_SIZE as u32); - assert!(aligned_buf.len() >= P::STATE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::ACTIVE::WRITE_SIZE); - assert_eq!(0, aligned_buf.len() % P::DFU::WRITE_SIZE); - assert_partitions(self.active, self.dfu, self.state, P::page_size(), P::STATE::WRITE_SIZE); + assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32); + assert_eq!(0, Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32); + assert_eq!(0, Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32); + assert_eq!(0, Self::PAGE_SIZE % DFU::WRITE_SIZE as u32); + assert_eq!(0, Self::PAGE_SIZE % DFU::ERASE_SIZE as u32); + assert!(aligned_buf.len() >= STATE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); + assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); + + assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE); // Copy contents from partition N to active - let state = self.read_state(p, aligned_buf)?; + let state = self.read_state(aligned_buf)?; if state == State::Swap { // // Check if we already swapped. If we're in the swap state, this means we should revert // since the app has failed to mark boot as successful // - if !self.is_swapped(p, aligned_buf)? { + if !self.is_swapped(aligned_buf)? { trace!("Swapping"); - self.swap(p, aligned_buf)?; + self.swap(aligned_buf)?; trace!("Swapping done"); } else { trace!("Reverting"); - self.revert(p, aligned_buf)?; + self.revert(aligned_buf)?; - let state_flash = p.state(); - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; // Invalidate progress - state_word.fill(!P::STATE_ERASE_VALUE); - self.state - .write_blocking(state_flash, P::STATE::WRITE_SIZE as u32, state_word)?; + state_word.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, state_word)?; // Clear magic and progress - self.state.wipe_blocking(state_flash)?; + self.state.erase(0, self.state.capacity() as u32)?; // Set magic state_word.fill(BOOT_MAGIC); - self.state.write_blocking(state_flash, 0, state_word)?; + self.state.write(0, state_word)?; } } Ok(state) } - fn is_swapped(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result { - let page_count = (self.active.size() / P::page_size()) as usize; - let progress = self.current_progress(p, aligned_buf)?; + fn is_swapped(&mut self, aligned_buf: &mut [u8]) -> Result { + let page_count = self.active.capacity() / Self::PAGE_SIZE as usize; + let progress = self.current_progress(aligned_buf)?; Ok(progress >= page_count * 2) } - fn current_progress(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let write_size = P::STATE::WRITE_SIZE as u32; - let max_index = (((self.state.size() - write_size) / write_size) - 2) as usize; - let state_flash = config.state(); + fn current_progress(&mut self, aligned_buf: &mut [u8]) -> Result { + let write_size = STATE::WRITE_SIZE as u32; + let max_index = ((self.state.capacity() - STATE::WRITE_SIZE) / STATE::WRITE_SIZE) - 2; let state_word = &mut aligned_buf[..write_size as usize]; - self.state.read_blocking(state_flash, write_size, state_word)?; - if state_word.iter().any(|&b| b != P::STATE_ERASE_VALUE) { + self.state.read(write_size, state_word)?; + if state_word.iter().any(|&b| b != STATE_ERASE_VALUE) { // Progress is invalid return Ok(max_index); } for index in 0..max_index { - self.state - .read_blocking(state_flash, (2 + index) as u32 * write_size, state_word)?; + self.state.read((2 + index) as u32 * write_size, state_word)?; - if state_word.iter().any(|&b| b == P::STATE_ERASE_VALUE) { + if state_word.iter().any(|&b| b == STATE_ERASE_VALUE) { return Ok(index); } } Ok(max_index) } - fn update_progress( - &mut self, - progress_index: usize, - p: &mut P, - aligned_buf: &mut [u8], - ) -> Result<(), BootError> { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - state_word.fill(!P::STATE_ERASE_VALUE); - self.state.write_blocking( - p.state(), - (2 + progress_index) as u32 * P::STATE::WRITE_SIZE as u32, - state_word, - )?; + fn update_progress(&mut self, progress_index: usize, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; + state_word.fill(!STATE_ERASE_VALUE); + self.state + .write((2 + progress_index) as u32 * STATE::WRITE_SIZE as u32, state_word)?; Ok(()) } - fn copy_page_once_to_active( + fn copy_page_once_to_active( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.active - .erase_blocking(p.active(), to_offset, to_offset + page_size)?; + self.active.erase(to_offset, to_offset + page_size)?; for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { - self.dfu - .read_blocking(p.dfu(), from_offset + offset_in_page as u32, aligned_buf)?; - self.active - .write_blocking(p.active(), to_offset + offset_in_page as u32, aligned_buf)?; + self.dfu.read(from_offset + offset_in_page as u32, aligned_buf)?; + self.active.write(to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(progress_index, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn copy_page_once_to_dfu( + fn copy_page_once_to_dfu( &mut self, progress_index: usize, from_offset: u32, to_offset: u32, - p: &mut P, aligned_buf: &mut [u8], ) -> Result<(), BootError> { - if self.current_progress(p, aligned_buf)? <= progress_index { - let page_size = P::page_size() as u32; + if self.current_progress(aligned_buf)? <= progress_index { + let page_size = Self::PAGE_SIZE as u32; - self.dfu - .erase_blocking(p.dfu(), to_offset as u32, to_offset + page_size)?; + self.dfu.erase(to_offset as u32, to_offset + page_size)?; for offset_in_page in (0..page_size).step_by(aligned_buf.len()) { - self.active - .read_blocking(p.active(), from_offset + offset_in_page as u32, aligned_buf)?; - self.dfu - .write_blocking(p.dfu(), to_offset + offset_in_page as u32, aligned_buf)?; + self.active.read(from_offset + offset_in_page as u32, aligned_buf)?; + self.dfu.write(to_offset + offset_in_page as u32, aligned_buf)?; } - self.update_progress(progress_index, p, aligned_buf)?; + self.update_progress(progress_index, aligned_buf)?; } Ok(()) } - fn swap(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + fn swap(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; for page_num in 0..page_count { let progress_index = (page_num * 2) as usize; // Copy active page to the 'next' DFU page. - let active_from_offset = (page_count - 1 - page_num) * page_size; - let dfu_to_offset = (page_count - page_num) * page_size; + let active_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; + let dfu_to_offset = (page_count - page_num) * Self::PAGE_SIZE; //trace!("Copy active {} to dfu {}", active_from_offset, dfu_to_offset); - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; // Copy DFU page to the active page - let active_to_offset = (page_count - 1 - page_num) * page_size; - let dfu_from_offset = (page_count - 1 - page_num) * page_size; + let active_to_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; + let dfu_from_offset = (page_count - 1 - page_num) * Self::PAGE_SIZE; //trace!("Copy dfy {} to active {}", dfu_from_offset, active_to_offset); - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; } Ok(()) } - fn revert(&mut self, p: &mut P, aligned_buf: &mut [u8]) -> Result<(), BootError> { - let page_size = P::page_size(); - let page_count = self.active.size() / page_size; + fn revert(&mut self, aligned_buf: &mut [u8]) -> Result<(), BootError> { + let page_count = self.active.capacity() as u32 / Self::PAGE_SIZE; for page_num in 0..page_count { let progress_index = (page_count * 2 + page_num * 2) as usize; // Copy the bad active page to the DFU page - let active_from_offset = page_num * page_size; - let dfu_to_offset = page_num * page_size; - self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, p, aligned_buf)?; + let active_from_offset = page_num * Self::PAGE_SIZE; + let dfu_to_offset = page_num * Self::PAGE_SIZE; + self.copy_page_once_to_dfu(progress_index, active_from_offset, dfu_to_offset, aligned_buf)?; // Copy the DFU page back to the active page - let active_to_offset = page_num * page_size; - let dfu_from_offset = (page_num + 1) * page_size; - self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, p, aligned_buf)?; + let active_to_offset = page_num * Self::PAGE_SIZE; + let dfu_from_offset = (page_num + 1) * Self::PAGE_SIZE; + self.copy_page_once_to_active(progress_index + 1, dfu_from_offset, active_to_offset, aligned_buf)?; } Ok(()) } - fn read_state(&mut self, config: &mut P, aligned_buf: &mut [u8]) -> Result { - let state_word = &mut aligned_buf[..P::STATE::WRITE_SIZE]; - self.state.read_blocking(config.state(), 0, state_word)?; + fn read_state(&mut self, aligned_buf: &mut [u8]) -> Result { + let state_word = &mut aligned_buf[..STATE::WRITE_SIZE]; + self.state.read(0, state_word)?; if !state_word.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -373,11 +328,16 @@ impl BootLoader { } } -fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: u32, state_write_size: usize) { - assert_eq!(active.size() % page_size, 0); - assert_eq!(dfu.size() % page_size, 0); - assert!(dfu.size() - active.size() >= page_size); - assert!(2 + 2 * (active.size() / page_size) <= state.size() / state_write_size as u32); +fn assert_partitions( + active: &ACTIVE, + dfu: &DFU, + state: &STATE, + page_size: u32, +) { + assert_eq!(active.capacity() as u32 % page_size, 0); + assert_eq!(dfu.capacity() as u32 % page_size, 0); + assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); + assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); } /// A flash wrapper implementing the Flash and embedded_storage traits. @@ -436,98 +396,20 @@ where } } -/// Convenience provider that uses a single flash for all partitions. -pub struct SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - flash: &'a mut F, -} - -impl<'a, F> SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - /// Create a provider for a single flash. - pub fn new(flash: &'a mut F) -> Self { - Self { flash } - } -} - -impl<'a, F> FlashConfig for SingleFlashConfig<'a, F> -where - F: NorFlash, -{ - type STATE = F; - type ACTIVE = F; - type DFU = F; - - fn active(&mut self) -> &mut Self::STATE { - self.flash - } - fn dfu(&mut self) -> &mut Self::ACTIVE { - self.flash - } - fn state(&mut self) -> &mut Self::DFU { - self.flash - } -} - -/// Convenience flash provider that uses separate flash instances for each partition. -pub struct MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - active: &'a mut ACTIVE, - state: &'a mut STATE, - dfu: &'a mut DFU, -} - -impl<'a, ACTIVE, STATE, DFU> MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - /// Create a new flash provider with separate configuration for all three partitions. - pub fn new(active: &'a mut ACTIVE, state: &'a mut STATE, dfu: &'a mut DFU) -> Self { - Self { active, state, dfu } - } -} - -impl<'a, ACTIVE, STATE, DFU> FlashConfig for MultiFlashConfig<'a, ACTIVE, STATE, DFU> -where - ACTIVE: NorFlash, - STATE: NorFlash, - DFU: NorFlash, -{ - type STATE = STATE; - type ACTIVE = ACTIVE; - type DFU = DFU; - - fn active(&mut self) -> &mut Self::ACTIVE { - self.active - } - fn dfu(&mut self) -> &mut Self::DFU { - self.dfu - } - fn state(&mut self) -> &mut Self::STATE { - self.state - } -} - #[cfg(test)] mod tests { use super::*; + use crate::mem_flash::MemFlash; #[test] #[should_panic] fn test_range_asserts() { - const ACTIVE: Partition = Partition::new(4096, 4194304); - const DFU: Partition = Partition::new(4194304, 2 * 4194304); - const STATE: Partition = Partition::new(0, 4096); - assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + const ACTIVE_SIZE: usize = 4194304 - 4096; + const DFU_SIZE: usize = 4194304; + const STATE_SIZE: usize = 4096; + static ACTIVE: MemFlash = MemFlash::new(0xFF); + static DFU: MemFlash = MemFlash::new(0xFF); + static STATE: MemFlash = MemFlash::new(0xFF); + assert_partitions(&ACTIVE, &DFU, &STATE, 4096); } } From 1a31b03976d73496f649165d9f92c4e19bef93fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 29 May 2023 21:30:28 +0200 Subject: [PATCH 1258/1575] ci: fix nrf, rp tests. --- .github/ci/test.sh | 4 ++- embassy-boot/nrf/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 33 ++++++++++++++++------- embassy-nrf/README.md | 2 +- embassy-nrf/src/qdec.rs | 13 +++++++-- embassy-nrf/src/temp.rs | 13 ++++++++- embassy-rp/Cargo.toml | 8 +++++- embassy-rp/src/flash.rs | 1 + embassy-rp/src/intrinsics.rs | 19 ++++++------- embassy-rp/src/multicore.rs | 25 ++++++++++++++--- embassy-rp/src/rtc/mod.rs | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 4 +-- examples/boot/bootloader/rp/Cargo.toml | 4 +-- examples/boot/bootloader/stm32/Cargo.toml | 4 +-- 14 files changed, 98 insertions(+), 36 deletions(-) diff --git a/.github/ci/test.sh b/.github/ci/test.sh index a7140cfd9..d014e4bd7 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -21,7 +21,9 @@ cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-dalek cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-salty -#cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nightly,nrf52840,time-driver-rtc1 ## broken doctests +cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nightly,nrf52840,time-driver-rtc1,gpiote + +cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features nightly,time-driver cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index e46736889..8186a9958 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml @@ -17,7 +17,7 @@ target = "thumbv7em-none-eabi" defmt = { version = "0.3", optional = true } embassy-sync = { path = "../../embassy-sync" } -embassy-nrf = { path = "../../embassy-nrf", default-features = false } +embassy-nrf = { path = "../../embassy-nrf" } embassy-boot = { path = "../boot", default-features = false } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 15d0f6872..83900d4d0 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -16,6 +16,18 @@ flavors = [ ] [features] +default = [ + "nrf52805-pac?/rt", + "nrf52810-pac?/rt", + "nrf52811-pac?/rt", + "nrf52820-pac?/rt", + "nrf52832-pac?/rt", + "nrf52833-pac?/rt", + "nrf52840-pac?/rt", + "nrf5340-app-pac?/rt", + "nrf5340-net-pac?/rt", + "nrf9160-pac?/rt", +] time = ["dep:embassy-time"] @@ -103,13 +115,14 @@ embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } cfg-if = "1.0.0" -nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf52810-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf52811-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf52820-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf52832-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf52833-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf52840-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf5340-app-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf5340-net-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } -nrf9160-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } +nrf52805-pac = { version = "0.12.0", optional = true } +nrf52810-pac = { version = "0.12.0", optional = true } +nrf52811-pac = { version = "0.12.0", optional = true } +nrf52820-pac = { version = "0.12.0", optional = true } +nrf52832-pac = { version = "0.12.0", optional = true } +nrf52833-pac = { version = "0.12.0", optional = true } +nrf52840-pac = { version = "0.12.0", optional = true } +nrf5340-app-pac = { version = "0.12.0", optional = true } +nrf5340-net-pac = { version = "0.12.0", optional = true } +nrf9160-pac = { version = "0.12.0", optional = true } + diff --git a/embassy-nrf/README.md b/embassy-nrf/README.md index a31cfae68..129ec0c01 100644 --- a/embassy-nrf/README.md +++ b/embassy-nrf/README.md @@ -13,7 +13,7 @@ with peripherals. It takes care of sending/receiving data over a variety of bus However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: -```no_run +```rust,ignore // As we pass a slice to the function whose contents will not ever change, // the compiler writes it into the flash and thus the pointer to it will // reference static memory. Since EasyDMA requires slices to reside in RAM, diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 4d2a09198..c845492fc 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -154,10 +154,19 @@ impl<'d, T: Instance> Qdec<'d, T> { /// # Example /// /// ```no_run - /// let irq = interrupt::take!(QDEC); + /// use embassy_nrf::qdec::{self, Qdec}; + /// use embassy_nrf::{bind_interrupts, peripherals}; + /// + /// bind_interrupts!(struct Irqs { + /// QDEC => qdec::InterruptHandler; + /// }); + /// + /// # async { + /// # let p: embassy_nrf::Peripherals = todo!(); /// let config = qdec::Config::default(); - /// let mut q = Qdec::new(p.QDEC, p.P0_31, p.P0_30, config); + /// let mut q = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config); /// let delta = q.read().await; + /// # }; /// ``` pub async fn read(&mut self) -> i16 { let t = T::regs(); diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 3a75ec629..0653710af 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -56,8 +56,19 @@ impl<'d> Temp<'d> { /// # Example /// /// ```no_run - /// let mut t = Temp::new(p.TEMP, interrupt::take!(TEMP)); + /// use embassy_nrf::{bind_interrupts, temp}; + /// use embassy_nrf::temp::Temp; + /// use embassy_time::{Duration, Timer}; + /// + /// bind_interrupts!(struct Irqs { + /// TEMP => temp::InterruptHandler; + /// }); + /// + /// # async { + /// # let p: embassy_nrf::Peripherals = todo!(); + /// let mut t = Temp::new(p.TEMP, Irqs); /// let v: u16 = t.read().await.to_num::(); + /// # }; /// ``` pub async fn read(&mut self) -> I30F2 { // In case the future is dropped, stop the task and reset events. diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index e395a994f..e032dfdae 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -13,6 +13,8 @@ flavors = [ ] [features] +default = [ "rp-pac/rt" ] + defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] # critical section that is safe for multicore use @@ -70,7 +72,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "4", features = ["rt"] } +rp-pac = { version = "4" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} @@ -81,3 +83,7 @@ paste = "1.0" pio-proc = {version= "0.2" } pio = {version= "0.2.1" } rp2040-boot2 = "0.3" + +[dev-dependencies] +embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } +static_cell = "1.0" diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 929bd028c..0410429e0 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -575,6 +575,7 @@ mod ram_helpers { #[inline(never)] #[link_section = ".data.ram_func"] unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { + #[cfg(target_arch = "arm")] core::arch::asm!( "mov r10, r0", // cmd "mov r5, r1", // ptrs diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 3baabb287..5b9c127ba 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -61,16 +61,17 @@ macro_rules! intrinsics_aliases { /// Like the compiler-builtins macro, it accepts a series of functions that /// looks like normal Rust code: /// -/// intrinsics! { -/// extern "C" fn foo(a: i32) -> u32 { -/// // ... -/// } -/// -/// #[nonstandard_attribute] -/// extern "C" fn bar(a: i32) -> u32 { -/// // ... -/// } +/// ```rust,ignore +/// intrinsics! { +/// extern "C" fn foo(a: i32) -> u32 { +/// // ... /// } +/// #[nonstandard_attribute] +/// extern "C" fn bar(a: i32) -> u32 { +/// // ... +/// } +/// } +/// ``` /// /// Each function can also be decorated with nonstandard attributes to control /// additional behaviour: diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index bbc775105..a13209f74 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -9,22 +9,41 @@ //! the `embassy-sync` primitives and `CriticalSectionRawMutex`. //! //! # Usage +//! //! ```no_run +//! # #![feature(type_alias_impl_trait)] +//! use embassy_rp::multicore::Stack; +//! use static_cell::StaticCell; +//! use embassy_executor::Executor; +//! //! static mut CORE1_STACK: Stack<4096> = Stack::new(); //! static EXECUTOR0: StaticCell = StaticCell::new(); //! static EXECUTOR1: StaticCell = StaticCell::new(); //! +//! # // workaround weird error: `main` function not found in crate `rust_out` +//! # let _ = (); +//! +//! #[embassy_executor::task] +//! async fn core0_task() { +//! // ... +//! } +//! +//! #[embassy_executor::task] +//! async fn core1_task() { +//! // ... +//! } +//! //! #[cortex_m_rt::entry] //! fn main() -> ! { //! let p = embassy_rp::init(Default::default()); //! -//! spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { +//! embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { //! let executor1 = EXECUTOR1.init(Executor::new()); -//! executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); +//! executor1.run(|spawner| spawner.spawn(core1_task()).unwrap()); //! }); //! //! let executor0 = EXECUTOR0.init(Executor::new()); -//! executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); +//! executor0.run(|spawner| spawner.spawn(core0_task()).unwrap()) //! } //! ``` diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index c213ad174..e1d886d4a 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -121,7 +121,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { /// # #[cfg(not(feature = "chrono"))] /// # fn main() { /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; - /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; + /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; /// let now = real_time_clock.now().unwrap(); /// real_time_clock.schedule_alarm( /// DateTimeFilter::default() diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index cd0be5b48..8c2fb4c5f 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0" defmt = { 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 } +embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] } +embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } cfg-if = "1.0.0" diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index b4167bcd8..bf9226993 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0" 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 } +embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] } +embassy-boot-rp = { path = "../../../../embassy-boot/rp" } embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index f2675aa73..fbc80b34c 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0" defmt = { 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 } +embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] } +embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" From da0be7114f87053fe8b38cf64e4d42badd8b4786 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 29 May 2023 15:14:43 -0500 Subject: [PATCH 1259/1575] stm32/uart: fix dma ringbuf tests --- embassy-stm32/src/dma/ringbuffer.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 72a84a57f..a2bde986f 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -276,7 +276,7 @@ mod tests { let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.len()); + assert_eq!(16, ringbuf.cap()); dma.setup(vec![ TestCircularTransferRequest::PositionRequest(8), @@ -317,7 +317,7 @@ mod tests { let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.len()); + assert_eq!(16, ringbuf.cap()); /* Read to close to the end of the buffer @@ -352,7 +352,7 @@ mod tests { let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.len()); + assert_eq!(16, ringbuf.cap()); /* Read to close to the end of the buffer @@ -387,7 +387,7 @@ mod tests { let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.len()); + assert_eq!(16, ringbuf.cap()); /* Read to about the middle of the buffer @@ -423,7 +423,7 @@ mod tests { let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.len()); + assert_eq!(16, ringbuf.cap()); /* Read a few bytes @@ -457,7 +457,7 @@ mod tests { let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.len()); + assert_eq!(16, ringbuf.cap()); /* Read to close to the end of the buffer From 42a5b14724ffc9efe3a011fb5a23d77b66af3a1a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 29 May 2023 22:50:30 +0200 Subject: [PATCH 1260/1575] Remove unneeded default-features=false. --- .../ROOT/examples/layer-by-layer/blinky-async/Cargo.toml | 4 ++-- .../ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index 523de0ddf..a7236ed5e 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" cortex-m-rt = "0.7" -embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } -embassy-executor = { version = "0.2.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] } +embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] } +embassy-executor = { version = "0.2.0", features = ["nightly", "arch-cortex-m", "executor-thread"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml index f86361dd5..c15de2db2 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7" cortex-m-rt = "0.7" -embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x"], default-features = false } +embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x"] } defmt = "0.3.0" defmt-rtt = "0.3.0" From 020e956f1ba5c0b3baf75b02f286218f661e1c02 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 30 May 2023 00:10:36 +0200 Subject: [PATCH 1261/1575] ci: run HIL tests in parallel. --- .github/ci/build.sh | 1 + ci.sh | 43 +++++-------------- tests/nrf/.cargo/config.toml | 2 +- tests/nrf/Cargo.toml | 2 + tests/nrf/build.rs | 1 + tests/nrf/src/bin/buffered_uart.rs | 2 + tests/nrf/src/bin/buffered_uart_spam.rs | 2 + tests/nrf/src/bin/timer.rs | 2 + tests/nrf/src/common.rs | 1 + tests/rp/.cargo/config.toml | 4 +- tests/rp/Cargo.toml | 2 + tests/rp/build.rs | 1 + tests/rp/src/bin/dma_copy_async.rs | 2 + tests/rp/src/bin/flash.rs | 2 + tests/rp/src/bin/float.rs | 2 + tests/rp/src/bin/gpio.rs | 2 + tests/rp/src/bin/gpio_async.rs | 2 + tests/rp/src/bin/gpio_multicore.rs | 2 + tests/rp/src/bin/multicore.rs | 2 + tests/rp/src/bin/pwm.rs | 2 + tests/rp/src/bin/spi.rs | 2 + tests/rp/src/bin/spi_async.rs | 2 + tests/rp/src/bin/uart.rs | 2 + tests/rp/src/bin/uart_buffered.rs | 2 + tests/rp/src/bin/uart_dma.rs | 2 + tests/rp/src/bin/uart_upgrade.rs | 2 + tests/rp/src/common.rs | 1 + tests/stm32/.cargo/config.toml | 2 +- tests/stm32/Cargo.toml | 2 + tests/stm32/build.rs | 1 + tests/stm32/src/bin/gpio.rs | 6 +-- tests/stm32/src/bin/rtc.rs | 8 ++-- tests/stm32/src/bin/sdmmc.rs | 2 + tests/stm32/src/bin/spi.rs | 6 +-- tests/stm32/src/bin/spi_dma.rs | 6 +-- tests/stm32/src/bin/timer.rs | 6 +-- tests/stm32/src/bin/tl_mbox.rs | 10 ++--- tests/stm32/src/bin/usart.rs | 6 +-- tests/stm32/src/bin/usart_dma.rs | 6 +-- tests/stm32/src/bin/usart_rx_ringbuffered.rs | 6 +-- tests/stm32/src/common.rs | 44 ++++++++++++++++++++ tests/stm32/src/example_common.rs | 25 ----------- 42 files changed, 136 insertions(+), 92 deletions(-) create mode 100644 tests/nrf/src/common.rs create mode 100644 tests/rp/src/common.rs create mode 100644 tests/stm32/src/common.rs delete mode 100644 tests/stm32/src/example_common.rs diff --git a/.github/ci/build.sh b/.github/ci/build.sh index 1c3a7f3b0..30ca1e6f0 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -9,6 +9,7 @@ export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target if [ -f /ci/secrets/teleprobe-token.txt ]; then echo Got teleprobe token! + export TELEPROBE_HOST=https://teleprobe.embassy.dev export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) fi diff --git a/ci.sh b/ci.sh index 11c569328..1eafda3a3 100755 --- a/ci.sh +++ b/ci.sh @@ -12,7 +12,7 @@ if [ $TARGET = "x86_64-unknown-linux-gnu" ]; then BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --out-dir out/examples/std" fi -find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2018 +find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2021 cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \ @@ -127,45 +127,24 @@ cargo batch \ --- 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 \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/nucleo-stm32f429zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/nucleo-stm32g491re \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/nucleo-stm32c031c6 \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/nucleo-stm32h563zi \ - --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/stm32h755zi \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \ --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ --- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \ --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ $BUILD_EXTRA - -function run_elf { - echo Running target=$1 elf=$2 - STATUSCODE=$( - curl \ - -sS \ - --output /dev/stderr \ - --write-out "%{http_code}" \ - -H "Authorization: Bearer $TELEPROBE_TOKEN" \ - https://teleprobe.embassy.dev/targets/$1/run --data-binary @$2 - ) - echo - echo HTTP Status code: $STATUSCODE - test "$STATUSCODE" -eq 200 -} - if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests exit fi -for board in $(ls out/tests); do - echo Running tests for board: $board - for elf in $(ls out/tests/$board); do - run_elf $board out/tests/$board/$elf - done -done +teleprobe client run -r out/tests \ No newline at end of file diff --git a/tests/nrf/.cargo/config.toml b/tests/nrf/.cargo/config.toml index 4eec189d4..03995f963 100644 --- a/tests/nrf/.cargo/config.toml +++ b/tests/nrf/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] #runner = "teleprobe local run --chip nRF52840_xxAA --elf" -runner = "teleprobe client run --target nrf52840-dk --elf" +runner = "teleprobe client run" [build] target = "thumbv7em-none-eabi" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index ac38229a6..9735c87d9 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -5,6 +5,8 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +teleprobe-meta = "1" + embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } diff --git a/tests/nrf/build.rs b/tests/nrf/build.rs index 6f4872249..93e2a28cf 100644 --- a/tests/nrf/build.rs +++ b/tests/nrf/build.rs @@ -11,6 +11,7 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + println!("cargo:rustc-link-arg-bins=-Tteleprobe.x"); Ok(()) } diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs index e73d4f0b0..72a4cb4ef 100644 --- a/tests/nrf/src/bin/buffered_uart.rs +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 74eda6d01..50960206f 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use core::mem; use core::ptr::NonNull; diff --git a/tests/nrf/src/bin/timer.rs b/tests/nrf/src/bin/timer.rs index 9b9b5fb28..607c5bbf1 100644 --- a/tests/nrf/src/bin/timer.rs +++ b/tests/nrf/src/bin/timer.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert, info}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/common.rs b/tests/nrf/src/common.rs new file mode 100644 index 000000000..1a05ac1c5 --- /dev/null +++ b/tests/nrf/src/common.rs @@ -0,0 +1 @@ +teleprobe_meta::target!(b"nrf52840-dk"); diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index e1744c703..bc92e788b 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -5,8 +5,8 @@ #build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -#runner = "teleprobe client run --target rpi-pico --elf" -runner = "teleprobe local run --chip RP2040 --elf" +runner = "teleprobe client run" +#runner = "teleprobe local run --chip RP2040 --elf" rustflags = [ # Code-size optimizations. diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 43167166e..1786baee3 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -5,6 +5,8 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +teleprobe-meta = "1" + embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } diff --git a/tests/rp/build.rs b/tests/rp/build.rs index 6f4872249..93e2a28cf 100644 --- a/tests/rp/build.rs +++ b/tests/rp/build.rs @@ -11,6 +11,7 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + println!("cargo:rustc-link-arg-bins=-Tteleprobe.x"); Ok(()) } diff --git a/tests/rp/src/bin/dma_copy_async.rs b/tests/rp/src/bin/dma_copy_async.rs index c53f644bd..2c0b559a9 100644 --- a/tests/rp/src/bin/dma_copy_async.rs +++ b/tests/rp/src/bin/dma_copy_async.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 00bebe2b6..cf9b86df5 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs index 6715271e6..6a982507a 100644 --- a/tests/rp/src/bin/float.rs +++ b/tests/rp/src/bin/float.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index 80e92d0fd..51112d319 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index f20b8fcbd..532494de5 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index 6c13ccaae..780112bc1 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{info, unwrap}; use embassy_executor::Executor; diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index da78e887a..114889dec 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{info, unwrap}; use embassy_executor::Executor; diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs index b8cbe74c8..c71d21ef9 100644 --- a/tests/rp/src/bin/pwm.rs +++ b/tests/rp/src/bin/pwm.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert, assert_eq, assert_ne, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi.rs b/tests/rp/src/bin/spi.rs index 478d62ee0..84dfa5a2c 100644 --- a/tests/rp/src/bin/spi.rs +++ b/tests/rp/src/bin/spi.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index 2e22c9de7..a4080b03d 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs @@ -4,6 +4,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs index 80c18c02e..2331c7d36 100644 --- a/tests/rp/src/bin/uart.rs +++ b/tests/rp/src/bin/uart.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index 1dcf57d07..e74e9986c 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, panic, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index 75be76eda..fee6c825d 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs index 8605bb1c5..760e53954 100644 --- a/tests/rp/src/bin/uart_upgrade.rs +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/common.rs b/tests/rp/src/common.rs new file mode 100644 index 000000000..955674f27 --- /dev/null +++ b/tests/rp/src/common.rs @@ -0,0 +1 @@ +teleprobe_meta::target!(b"rpi-pico"); diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml index 426d6e67f..07761b01c 100644 --- a/tests/stm32/.cargo/config.toml +++ b/tests/stm32/.cargo/config.toml @@ -3,7 +3,7 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" +runner = "teleprobe client run" #runner = "teleprobe local run --chip STM32F103C8 --elf" rustflags = [ diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 4c0597746..f1b0ba121 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -22,6 +22,8 @@ ble = [] not-gpdma = [] [dependencies] +teleprobe-meta = "1" + embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index b4583147e..2e71954d7 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -26,6 +26,7 @@ fn main() -> Result<(), Box> { } println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + println!("cargo:rustc-link-arg-bins=-Tteleprobe.x"); Ok(()) } diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 8b99b08a5..67f44317e 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -1,13 +1,13 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -#[path = "../example_common.rs"] -mod example_common; +use common::*; use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull, Speed}; -use example_common::*; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index ccf2ca609..32d35c42c 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -1,18 +1,16 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -// required-features: chrono - -#[path = "../example_common.rs"] -mod example_common; use chrono::{NaiveDate, NaiveDateTime}; +use common::*; use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::pac; use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_time::{Duration, Timer}; -use example_common::*; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs index 7d96cf41e..515025386 100644 --- a/tests/stm32/src/bin/sdmmc.rs +++ b/tests/stm32/src/bin/sdmmc.rs @@ -2,6 +2,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index a87ac3237..819ecae3c 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -1,15 +1,15 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -#[path = "../example_common.rs"] -mod example_common; +use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; -use example_common::*; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 74776ebf8..78aad24e1 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -1,14 +1,14 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -#[path = "../example_common.rs"] -mod example_common; +use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; -use example_common::*; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/timer.rs b/tests/stm32/src/bin/timer.rs index e00e43bf1..f8b453cda 100644 --- a/tests/stm32/src/bin/timer.rs +++ b/tests/stm32/src/bin/timer.rs @@ -1,13 +1,13 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -#[path = "../example_common.rs"] -mod example_common; +use common::*; use defmt::assert; use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Timer}; -use example_common::*; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 626e7ac6f..fab9f0e1b 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -1,16 +1,16 @@ +// required-features: ble + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -// required-features: ble - -#[path = "../example_common.rs"] -mod example_common; +use common::*; use embassy_executor::Spawner; use embassy_stm32::tl_mbox::{Config, TlMbox}; use embassy_stm32::{bind_interrupts, tl_mbox}; use embassy_time::{Duration, Timer}; -use example_common::*; bind_interrupts!(struct Irqs{ IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 415c7afa9..394005b82 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -1,16 +1,16 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -#[path = "../example_common.rs"] -mod example_common; +use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Error, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_time::{Duration, Instant}; -use example_common::*; #[cfg(any( feature = "stm32f103c8", diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index 7f002b97e..50dd2893e 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -1,15 +1,15 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -#[path = "../example_common.rs"] -mod example_common; +use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; -use example_common::*; #[cfg(any( feature = "stm32f103c8", diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 3a34773f7..c8dd2643b 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -3,15 +3,15 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; -#[path = "../example_common.rs"] -mod example_common; +use common::*; use defmt::{assert_eq, panic}; use embassy_executor::Spawner; use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_time::{Duration, Timer}; -use example_common::*; use rand_chacha::ChaCha8Rng; use rand_core::{RngCore, SeedableRng}; diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs new file mode 100644 index 000000000..3d2a9b8ef --- /dev/null +++ b/tests/stm32/src/common.rs @@ -0,0 +1,44 @@ +#![macro_use] + +pub use defmt::*; +#[allow(unused)] +use embassy_stm32::time::Hertz; +use embassy_stm32::Config; +use {defmt_rtt as _, panic_probe as _}; + +#[cfg(feature = "stm32f103c8")] +teleprobe_meta::target!(b"bluepill-stm32f103c8"); +#[cfg(feature = "stm32g491re")] +teleprobe_meta::target!(b"nucleo-stm32g491re"); +#[cfg(feature = "stm32g071rb")] +teleprobe_meta::target!(b"nucleo-stm32g071rb"); +#[cfg(feature = "stm32f429zi")] +teleprobe_meta::target!(b"nucleo-stm32f429zi"); +#[cfg(feature = "stm32wb55rg")] +teleprobe_meta::target!(b"nucleo-stm32wb55rg"); +#[cfg(feature = "stm32h755zi")] +teleprobe_meta::target!(b"nucleo-stm32h755zi"); +#[cfg(feature = "stm32u585ai")] +teleprobe_meta::target!(b"iot-stm32u585ai"); +#[cfg(feature = "stm32h563zi")] +teleprobe_meta::target!(b"nucleo-stm32h563zi"); +#[cfg(feature = "stm32c031c6")] +teleprobe_meta::target!(b"nucleo-stm32c031c6"); + +pub fn config() -> Config { + #[allow(unused_mut)] + let mut config = Config::default(); + + #[cfg(feature = "stm32h755zi")] + { + config.rcc.sys_ck = Some(Hertz(400_000_000)); + config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); + } + + #[cfg(feature = "stm32u585ai")] + { + config.rcc.mux = embassy_stm32::rcc::ClockSrc::MSI(embassy_stm32::rcc::MSIRange::Range48mhz); + } + + config +} diff --git a/tests/stm32/src/example_common.rs b/tests/stm32/src/example_common.rs deleted file mode 100644 index 3d150da60..000000000 --- a/tests/stm32/src/example_common.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![macro_use] - -pub use defmt::*; -#[allow(unused)] -use embassy_stm32::time::Hertz; -use embassy_stm32::Config; -use {defmt_rtt as _, panic_probe as _}; - -pub fn config() -> Config { - #[allow(unused_mut)] - let mut config = Config::default(); - - #[cfg(feature = "stm32h755zi")] - { - config.rcc.sys_ck = Some(Hertz(400_000_000)); - config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); - } - - #[cfg(feature = "stm32u585ai")] - { - config.rcc.mux = embassy_stm32::rcc::ClockSrc::MSI(embassy_stm32::rcc::MSIRange::Range48mhz); - } - - config -} From f8d35806dc773a6310a60115fbf90f48fe409207 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 29 May 2023 19:09:52 -0500 Subject: [PATCH 1262/1575] stm32/can: move to irq binding use embassy channel --- embassy-stm32/Cargo.toml | 1 - embassy-stm32/src/can/bxcan.rs | 184 +++++++++++++++++++-------------- 2 files changed, 105 insertions(+), 80 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 5d4b26a36..4e29bb32f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -69,7 +69,6 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" -heapless = { version = "0.7.5", default-features = false } [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 734efdc02..8ae1dcb94 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -1,17 +1,73 @@ use core::future::poll_fn; +use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; +use futures::FutureExt; use crate::gpio::sealed::AFType; use crate::interrupt::InterruptExt; use crate::pac::can::vals::{Lec, RirIde}; use crate::rcc::RccPeripheral; use crate::time::Hertz; -use crate::{peripherals, Peripheral}; +use crate::{interrupt, peripherals, Peripheral}; + +/// Interrupt handler. +pub struct TxInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for TxInterruptHandler { + unsafe fn on_interrupt() { + T::regs().tsr().write(|v| { + v.set_rqcp(0, true); + v.set_rqcp(1, true); + v.set_rqcp(2, true); + }); + + T::state().tx_waker.wake(); + } +} + +pub struct Rx0InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for Rx0InterruptHandler { + unsafe fn on_interrupt() { + Can::::receive_fifo(RxFifo::Fifo0); + } +} + +pub struct Rx1InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for Rx1InterruptHandler { + unsafe fn on_interrupt() { + Can::::receive_fifo(RxFifo::Fifo1); + } +} + +pub struct SceInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for SceInterruptHandler { + unsafe fn on_interrupt() { + let msr = T::regs().msr(); + let msr_val = msr.read(); + + if msr_val.erri() { + msr.modify(|v| v.set_erri(true)); + T::state().err_waker.wake(); + } + } +} pub struct Can<'d, T: Instance> { can: bxcan::Can>, @@ -64,12 +120,13 @@ impl<'d, T: Instance> Can<'d, T> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - tx_irq: impl Peripheral

+ 'd, - rx0_irq: impl Peripheral

+ 'd, - rx1_irq: impl Peripheral

+ 'd, - sce_irq: impl Peripheral

+ 'd, + _irqs: impl interrupt::Binding> + + interrupt::Binding> + + interrupt::Binding> + + interrupt::Binding> + + 'd, ) -> Self { - into_ref!(peri, rx, tx, tx_irq, rx0_irq, rx1_irq, sce_irq); + into_ref!(peri, rx, tx); unsafe { rx.set_as_af(rx.af_num(), AFType::Input); @@ -79,21 +136,19 @@ impl<'d, T: Instance> Can<'d, T> { T::enable(); T::reset(); - tx_irq.unpend(); - tx_irq.set_handler(Self::tx_interrupt); - tx_irq.enable(); + unsafe { + T::TXInterrupt::steal().unpend(); + T::TXInterrupt::steal().enable(); - rx0_irq.unpend(); - rx0_irq.set_handler(Self::rx0_interrupt); - rx0_irq.enable(); + T::RX0Interrupt::steal().unpend(); + T::RX0Interrupt::steal().enable(); - rx1_irq.unpend(); - rx1_irq.set_handler(Self::rx1_interrupt); - rx1_irq.enable(); + T::RX1Interrupt::steal().unpend(); + T::RX1Interrupt::steal().enable(); - sce_irq.unpend(); - sce_irq.set_handler(Self::sce_interrupt); - sce_irq.enable(); + T::SCEInterrupt::steal().unpend(); + T::SCEInterrupt::steal().enable(); + } let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); Self { can } @@ -133,12 +188,11 @@ impl<'d, T: Instance> Can<'d, T> { pub async fn receive_frame_or_error(&mut self) -> FrameOrError { poll_fn(|cx| { - if let Some(frame) = T::state().rx_queue.dequeue() { + if let Poll::Ready(frame) = T::state().rx_queue.recv().poll_unpin(cx) { return Poll::Ready(FrameOrError::Frame(frame)); } else if let Some(err) = self.curr_error() { return Poll::Ready(FrameOrError::Error(err)); } - T::state().rx_waker.register(cx.waker()); T::state().err_waker.register(cx.waker()); Poll::Pending }) @@ -159,69 +213,42 @@ impl<'d, T: Instance> Can<'d, T> { None } - unsafe fn sce_interrupt(_: *mut ()) { - let msr = T::regs().msr(); - let msr_val = msr.read(); - - if msr_val.erri() { - msr.modify(|v| v.set_erri(true)); - T::state().err_waker.wake(); - return; - } - } - - unsafe fn tx_interrupt(_: *mut ()) { - T::regs().tsr().write(|v| { - v.set_rqcp(0, true); - v.set_rqcp(1, true); - v.set_rqcp(2, true); - }); - T::state().tx_waker.wake(); - } - - unsafe fn rx0_interrupt(_: *mut ()) { - Self::receive_fifo(RxFifo::Fifo0); - } - - unsafe fn rx1_interrupt(_: *mut ()) { - Self::receive_fifo(RxFifo::Fifi1); - } - unsafe fn receive_fifo(fifo: RxFifo) { let state = T::state(); let regs = T::regs(); let fifo_idx = match fifo { RxFifo::Fifo0 => 0usize, - RxFifo::Fifi1 => 1usize, + RxFifo::Fifo1 => 1usize, }; let rfr = regs.rfr(fifo_idx); let fifo = regs.rx(fifo_idx); - // If there are no pending messages, there is nothing to do - if rfr.read().fmp() == 0 { - return; + loop { + // If there are no pending messages, there is nothing to do + if rfr.read().fmp() == 0 { + return; + } + + let rir = fifo.rir().read(); + let id = if rir.ide() == RirIde::STANDARD { + Id::from(StandardId::new_unchecked(rir.stid())) + } else { + Id::from(ExtendedId::new_unchecked(rir.exid())) + }; + let data_len = fifo.rdtr().read().dlc() as usize; + let mut data: [u8; 8] = [0; 8]; + data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); + + let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); + + rfr.modify(|v| v.set_rfom(true)); + + /* + NOTE: consensus was reached that if rx_queue is full, packets should be dropped + */ + let _ = state.rx_queue.try_send(frame); } - - let rir = fifo.rir().read(); - let id = if rir.ide() == RirIde::STANDARD { - Id::from(StandardId::new_unchecked(rir.stid())) - } else { - Id::from(ExtendedId::new_unchecked(rir.exid())) - }; - let data_len = fifo.rdtr().read().dlc() as usize; - let mut data: [u8; 8] = [0; 8]; - data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - - let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); - - rfr.modify(|v| v.set_rfom(true)); - - match state.rx_queue.enqueue(frame) { - Ok(_) => {} - Err(_) => defmt::error!("RX queue overflow"), - } - state.rx_waker.wake(); } pub fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option { @@ -318,7 +345,7 @@ impl<'d, T: Instance> Can<'d, T> { enum RxFifo { Fifo0, - Fifi1, + Fifo1, } impl<'d, T: Instance> Drop for Can<'d, T> { @@ -345,23 +372,22 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { } pub(crate) mod sealed { + use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; + use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; - use heapless::mpmc::Q8; pub struct State { pub tx_waker: AtomicWaker, - pub rx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Q8, + pub rx_queue: Channel, } impl State { pub const fn new() -> Self { Self { tx_waker: AtomicWaker::new(), - rx_waker: AtomicWaker::new(), err_waker: AtomicWaker::new(), - rx_queue: Q8::new(), + rx_queue: Channel::new(), } } } From 5205b5b09588d6e0006e5479764ee94b113b03b9 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:36:42 +0200 Subject: [PATCH 1263/1575] Split FirmwareUpdater into async and blocking types --- .../boot/src/firmware_updater/asynch.rs | 176 +++++++++-------- .../boot/src/firmware_updater/blocking.rs | 187 +++++++++++------- embassy-boot/boot/src/firmware_updater/mod.rs | 51 ++--- embassy-boot/boot/src/lib.rs | 4 +- 4 files changed, 236 insertions(+), 182 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index bdd03bff4..d0780bdf1 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -1,20 +1,68 @@ use digest::Digest; -use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; +use embassy_embedded_hal::flash::partition::Partition; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embedded_storage_async::nor_flash::NorFlash; -use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use super::FirmwareUpdaterConfig; +use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; + +/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to +/// 'mess up' the internal bootloader state +pub struct FirmwareUpdater { + dfu: DFU, + state: STATE, +} + +impl<'a, FLASH: NorFlash> + FirmwareUpdaterConfig, Partition<'a, NoopRawMutex, FLASH>> +{ + /// Create a firmware updater config from the flash and address symbols defined in the linkerfile + #[cfg(target_os = "none")] + pub fn from_linkerfile(flash: &'a Mutex) -> Self { + use embassy_sync::mutex::Mutex; + + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + let dfu = unsafe { + let start = &__bootloader_dfu_start as *const u32 as u32; + let end = &__bootloader_dfu_end as *const u32 as u32; + trace!("DFU: 0x{:x} - 0x{:x}", start, end); + + Partition::new(flash, start, end - start) + }; + let state = unsafe { + let start = &__bootloader_state_start as *const u32 as u32; + let end = &__bootloader_state_end as *const u32 as u32; + trace!("STATE: 0x{:x} - 0x{:x}", start, end); + + Partition::new(flash, start, end - start) + }; + + Self { dfu, state } + } +} + +impl FirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. + pub fn new(config: FirmwareUpdaterConfig) -> Self { + Self { + dfu: config.dfu, + state: config.state, + } + } -impl FirmwareUpdater { /// Obtain the current state. /// /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub async fn get_state( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read(state_flash, 0, aligned).await?; + pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result { + self.state.read(0, aligned).await?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -37,19 +85,18 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from /// and written to. - #[cfg(all(feature = "_verify", feature = "nightly"))] - pub async fn verify_and_mark_updated( + #[cfg(feature = "_verify")] + pub async fn verify_and_mark_updated( &mut self, - _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); + assert_eq!(_aligned.len(), STATE::WRITE_SIZE); + assert!(_update_len <= self.dfu.capacity() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -63,8 +110,7 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; + self.hash::(_update_len, _aligned, &mut message).await?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -85,8 +131,7 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message) - .await?; + self.hash::(_update_len, _aligned, &mut message).await?; let r = public_key.verify(&message, &signature); trace!( @@ -99,20 +144,19 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await + self.set_magic(_aligned, SWAP_MAGIC).await } /// Verify the update in DFU with any digest. - pub async fn hash( + pub async fn hash( &mut self, - dfu_flash: &mut F, update_len: u32, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read(dfu_flash, offset, chunk_buf).await?; + self.dfu.read(offset, chunk_buf).await?; let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); digest.update(&chunk_buf[..len]); } @@ -124,60 +168,44 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - #[cfg(all(feature = "nightly", not(feature = "_verify")))] - pub async fn mark_updated( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC, state_flash).await + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + #[cfg(not(feature = "_verify"))] + pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC).await } /// Mark firmware boot successful and stop rollback on reset. /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC, state_flash).await + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC).await } - async fn set_magic( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read(state_flash, 0, aligned).await?; + async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, aligned).await?; if aligned.iter().any(|&b| b != magic) { // Read progress validity - self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; + self.state.read(STATE::WRITE_SIZE as u32, aligned).await?; if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { // The current progress validity marker is invalid } else { // Invalidate progress aligned.fill(!STATE_ERASE_VALUE); - self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?; + self.state.write(STATE::WRITE_SIZE as u32, aligned).await?; } // Clear magic and progress - self.state.wipe(state_flash).await?; + self.state.erase(0, self.state.capacity() as u32).await?; // Set magic aligned.fill(magic); - self.state.write(state_flash, 0, aligned).await?; + self.state.write(0, aligned).await?; } Ok(()) } @@ -189,19 +217,12 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); + pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= DFU::ERASE_SIZE); - self.dfu - .erase(dfu_flash, offset as u32, (offset + data.len()) as u32) - .await?; + self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; - self.dfu.write(dfu_flash, offset as u32, data).await?; + self.dfu.write(offset as u32, data).await?; Ok(()) } @@ -211,18 +232,18 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub async fn prepare_update( - &mut self, - dfu_flash: &mut F, - ) -> Result { - self.dfu.wipe(dfu_flash).await?; + pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.dfu.erase(0, self.dfu.capacity() as u32).await?; - Ok(self.dfu) + Ok(&mut self.dfu) } } #[cfg(test)] mod tests { + use embassy_embedded_hal::flash::partition::Partition; + use embassy_sync::blocking_mutex::raw::NoopRawMutex; + use embassy_sync::mutex::Mutex; use futures::executor::block_on; use sha1::{Digest, Sha1}; @@ -231,20 +252,19 @@ mod tests { #[test] fn can_verify_sha1() { - const STATE: Partition = Partition::new(0, 4096); - const DFU: Partition = Partition::new(65536, 131072); - - let mut flash = MemFlash::<131072, 4096, 8>::default(); + let flash = Mutex::::new(MemFlash::<131072, 4096, 8>::default()); + let state = Partition::new(&flash, 0, 4096); + let dfu = Partition::new(&flash, 65536, 65536); let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); - let mut updater = FirmwareUpdater::new(DFU, STATE); - block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); + block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; - block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); + block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); assert_eq!(Sha1::digest(update).as_slice(), hash); } diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index 50caaf08c..c44126149 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -1,25 +1,70 @@ use digest::Digest; +use embassy_embedded_hal::flash::partition::BlockingPartition; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage::nor_flash::NorFlash; -use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; +use super::FirmwareUpdaterConfig; +use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; + +/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to +/// 'mess up' the internal bootloader state +pub struct BlockingFirmwareUpdater { + dfu: DFU, + state: STATE, +} + +impl<'a, FLASH: NorFlash> + FirmwareUpdaterConfig, BlockingPartition<'a, NoopRawMutex, FLASH>> +{ + /// Create a firmware updater config from the flash and address symbols defined in the linkerfile + #[cfg(target_os = "none")] + pub fn from_linkerfile_blocking(flash: &'a Mutex>) -> Self { + use core::cell::RefCell; + + use embassy_sync::blocking_mutex::Mutex; + + extern "C" { + static __bootloader_state_start: u32; + static __bootloader_state_end: u32; + static __bootloader_dfu_start: u32; + static __bootloader_dfu_end: u32; + } + + let dfu = unsafe { + let start = &__bootloader_dfu_start as *const u32 as u32; + let end = &__bootloader_dfu_end as *const u32 as u32; + trace!("DFU: 0x{:x} - 0x{:x}", start, end); + + BlockingPartition::new(flash, start, end - start) + }; + let state = unsafe { + let start = &__bootloader_state_start as *const u32 as u32; + let end = &__bootloader_state_end as *const u32 as u32; + trace!("STATE: 0x{:x} - 0x{:x}", start, end); + + BlockingPartition::new(flash, start, end - start) + }; -impl FirmwareUpdater { - /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub const fn new(dfu: Partition, state: Partition) -> Self { Self { dfu, state } } +} + +impl BlockingFirmwareUpdater { + /// Create a firmware updater instance with partition ranges for the update and state partitions. + pub fn new(config: FirmwareUpdaterConfig) -> Self { + Self { + dfu: config.dfu, + state: config.state, + } + } /// Obtain the current state. /// /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub fn get_state_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result { - self.state.read_blocking(state_flash, 0, aligned)?; + pub fn get_state(&mut self, aligned: &mut [u8]) -> Result { + self.state.read(0, aligned)?; if !aligned.iter().any(|&b| b != SWAP_MAGIC) { Ok(State::Swap) @@ -42,19 +87,18 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from /// and written to. #[cfg(feature = "_verify")] - pub fn verify_and_mark_updated_blocking( + pub fn verify_and_mark_updated( &mut self, - _state_and_dfu_flash: &mut F, _public_key: &[u8], _signature: &[u8], _update_len: u32, _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), F::WRITE_SIZE); - assert!(_update_len <= self.dfu.size()); + assert_eq!(_aligned.len(), STATE::WRITE_SIZE); + assert!(_update_len <= self.dfu.capacity() as u32); #[cfg(feature = "ed25519-dalek")] { @@ -68,7 +112,7 @@ impl FirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; + self.hash::(_update_len, _aligned, &mut message)?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -89,7 +133,7 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?; + self.hash::(_update_len, _aligned, &mut message)?; let r = public_key.verify(&message, &signature); trace!( @@ -102,20 +146,19 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash) + self.set_magic(_aligned, SWAP_MAGIC) } /// Verify the update in DFU with any digest. - pub fn hash_blocking( + pub fn hash( &mut self, - dfu_flash: &mut F, update_len: u32, chunk_buf: &mut [u8], output: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { let mut digest = D::new(); for offset in (0..update_len).step_by(chunk_buf.len()) { - self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?; + self.dfu.read(offset, chunk_buf)?; let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len()); digest.update(&chunk_buf[..len]); } @@ -127,60 +170,44 @@ impl FirmwareUpdater { /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. #[cfg(not(feature = "_verify"))] - pub fn mark_updated_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash) + pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, SWAP_MAGIC) } /// Mark firmware boot successful and stop rollback on reset. /// /// # Safety /// - /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted_blocking( - &mut self, - state_flash: &mut F, - aligned: &mut [u8], - ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), F::WRITE_SIZE); - self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash) + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.set_magic(aligned, BOOT_MAGIC) } - fn set_magic_blocking( - &mut self, - aligned: &mut [u8], - magic: u8, - state_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - self.state.read_blocking(state_flash, 0, aligned)?; + fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, aligned)?; if aligned.iter().any(|&b| b != magic) { // Read progress validity - self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; - - // FIXME: Do not make this assumption. - const STATE_ERASE_VALUE: u8 = 0xFF; + self.state.read(STATE::WRITE_SIZE as u32, aligned)?; if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { // The current progress validity marker is invalid } else { // Invalidate progress aligned.fill(!STATE_ERASE_VALUE); - self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?; + self.state.write(STATE::WRITE_SIZE as u32, aligned)?; } // Clear magic and progress - self.state.wipe_blocking(state_flash)?; + self.state.erase(0, self.state.capacity() as u32)?; // Set magic aligned.fill(magic); - self.state.write_blocking(state_flash, 0, aligned)?; + self.state.write(0, aligned)?; } Ok(()) } @@ -192,18 +219,12 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( - &mut self, - offset: usize, - data: &[u8], - dfu_flash: &mut F, - ) -> Result<(), FirmwareUpdaterError> { - assert!(data.len() >= F::ERASE_SIZE); + pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { + assert!(data.len() >= DFU::ERASE_SIZE); - self.dfu - .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; + self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; - self.dfu.write_blocking(dfu_flash, offset as u32, data)?; + self.dfu.write(offset as u32, data)?; Ok(()) } @@ -211,11 +232,45 @@ impl FirmwareUpdater { /// Prepare for an incoming DFU update by erasing the entire DFU area and /// returning its `Partition`. /// - /// Using this instead of `write_firmware_blocking` allows for an optimized - /// API in exchange for added complexity. - pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { - self.dfu.wipe_blocking(flash)?; + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.dfu.erase(0, self.dfu.capacity() as u32)?; - Ok(self.dfu) + Ok(&mut self.dfu) + } +} + +#[cfg(test)] +mod tests { + use core::cell::RefCell; + + use embassy_embedded_hal::flash::partition::BlockingPartition; + use embassy_sync::blocking_mutex::raw::NoopRawMutex; + use embassy_sync::blocking_mutex::Mutex; + use sha1::{Digest, Sha1}; + + use super::*; + use crate::mem_flash::MemFlash; + + #[test] + fn can_verify_sha1() { + let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); + let state = BlockingPartition::new(&flash, 0, 4096); + let dfu = BlockingPartition::new(&flash, 65536, 65536); + + let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; + let mut to_write = [0; 4096]; + to_write[..7].copy_from_slice(update.as_slice()); + + let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); + updater.write_firmware(0, to_write.as_slice()).unwrap(); + let mut chunk_buf = [0; 2]; + let mut hash = [0; 20]; + updater + .hash::(update.len() as u32, &mut chunk_buf, &mut hash) + .unwrap(); + + assert_eq!(Sha1::digest(update).as_slice(), hash); } } diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs index e09f5eb6c..a37984a3a 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs @@ -2,9 +2,22 @@ mod asynch; mod blocking; +#[cfg(feature = "nightly")] +pub use asynch::FirmwareUpdater; +pub use blocking::BlockingFirmwareUpdater; use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; -use crate::Partition; +/// Firmware updater flash configuration holding the two flashes used by the updater +/// +/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use. +/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition +/// the provided flash according to symbols defined in the linkerfile. +pub struct FirmwareUpdaterConfig { + /// The dfu flash partition + pub dfu: DFU, + /// The state flash partition + pub state: STATE, +} /// Errors returned by FirmwareUpdater #[derive(Debug)] @@ -33,39 +46,3 @@ where FirmwareUpdaterError::Flash(error.kind()) } } - -/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to -/// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { - state: Partition, - dfu: Partition, -} - -#[cfg(target_os = "none")] -impl Default for FirmwareUpdater { - fn default() -> Self { - extern "C" { - static __bootloader_state_start: u32; - static __bootloader_state_end: u32; - static __bootloader_dfu_start: u32; - static __bootloader_dfu_end: u32; - } - - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); - trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); - FirmwareUpdater::new(dfu, state) - } -} diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4521fecb0..af2d3ce02 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -11,8 +11,10 @@ mod mem_flash; mod partition; pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; -pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; pub use partition::Partition; +#[cfg(feature = "nightly")] +pub use firmware_updater::FirmwareUpdater; +pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; pub(crate) const BOOT_MAGIC: u8 = 0xD0; pub(crate) const SWAP_MAGIC: u8 = 0xF0; From c5ec453ec1e87c2f5c5047bf357ae99298aa6d12 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:38:00 +0200 Subject: [PATCH 1264/1575] Add bootloader helper for creating config from linkerfile symbols --- embassy-boot/boot/src/boot_loader.rs | 136 ++++++++++++++------------- embassy-boot/boot/src/lib.rs | 2 +- 2 files changed, 72 insertions(+), 66 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index 50eb5e668..bdf7bd7fd 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -1,8 +1,11 @@ -use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; +use core::cell::RefCell; -use crate::{State, BOOT_MAGIC, SWAP_MAGIC}; +use embassy_embedded_hal::flash::partition::BlockingPartition; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; -const STATE_ERASE_VALUE: u8 = 0xFF; +use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -32,14 +35,69 @@ where } } +/// Bootloader flash configuration holding the three flashes used by the bootloader +/// +/// If only a single flash is actually used, then that flash should be partitioned into three partitions before use. +/// The easiest way to do this is to use [`BootLoaderConfig::from_linkerfile_blocking`] which will partition +/// the provided flash according to symbols defined in the linkerfile. +pub struct BootLoaderConfig { + /// Flash type used for the active partition - the partition which will be booted from. + pub active: ACTIVE, + /// Flash type used for the dfu partition - the partition which will be swapped in when requested. + pub dfu: DFU, + /// Flash type used for the state partition. + pub state: STATE, +} + +impl<'a, FLASH: NorFlash> + BootLoaderConfig< + BlockingPartition<'a, NoopRawMutex, FLASH>, + BlockingPartition<'a, NoopRawMutex, FLASH>, + BlockingPartition<'a, NoopRawMutex, FLASH>, + > +{ + /// Create a bootloader config from the flash and address symbols defined in the linkerfile + // #[cfg(target_os = "none")] + pub fn from_linkerfile_blocking(flash: &'a Mutex>) -> 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 { + let start = &__bootloader_active_start as *const u32 as u32; + let end = &__bootloader_active_end as *const u32 as u32; + trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end); + + BlockingPartition::new(flash, start, end - start) + }; + let dfu = unsafe { + let start = &__bootloader_dfu_start as *const u32 as u32; + let end = &__bootloader_dfu_end as *const u32 as u32; + trace!("DFU: 0x{:x} - 0x{:x}", start, end); + + BlockingPartition::new(flash, start, end - start) + }; + let state = unsafe { + let start = &__bootloader_state_start as *const u32 as u32; + let end = &__bootloader_state_end as *const u32 as u32; + trace!("STATE: 0x{:x} - 0x{:x}", start, end); + + BlockingPartition::new(flash, start, end - start) + }; + + Self { active, dfu, state } + } +} + /// BootLoader works with any flash implementing embedded_storage. pub struct BootLoader { - /// Flash type used for the active partition - the partition which will be booted from. active: ACTIVE, - /// Flash type used for the dfu partition - he partition which will be swapped in when requested. dfu: DFU, - /// Flash type used for the state partition. - /// /// The state partition has the following format: /// All ranges are in multiples of WRITE_SIZE bytes. /// | Range | Description | @@ -61,8 +119,12 @@ impl BootLoader Self { - Self { active, dfu, state } + pub fn new(config: BootLoaderConfig) -> Self { + Self { + active: config.active, + dfu: config.dfu, + state: config.state, + } } /// Perform necessary boot preparations like swapping images. @@ -340,62 +402,6 @@ fn assert_partitions( assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); } -/// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash -where - F: NorFlash, -{ - flash: F, -} - -impl BootFlash -where - F: NorFlash, -{ - /// Create a new instance of a bootable flash - pub fn new(flash: F) -> Self { - Self { flash } - } -} - -impl ErrorType for BootFlash -where - F: NorFlash, -{ - type Error = F::Error; -} - -impl NorFlash for BootFlash -where - F: NorFlash, -{ - const WRITE_SIZE: usize = F::WRITE_SIZE; - const ERASE_SIZE: usize = F::ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(&mut self.flash, from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(&mut self.flash, offset, bytes) - } -} - -impl ReadNorFlash for BootFlash -where - F: NorFlash, -{ - const READ_SIZE: usize = F::READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(&mut self.flash, offset, bytes) - } - - fn capacity(&self) -> usize { - F::capacity(&self.flash) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index af2d3ce02..f2034fa8a 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -10,8 +10,8 @@ mod firmware_updater; mod mem_flash; mod partition; -pub use boot_loader::{BootError, BootFlash, BootLoader, FlashConfig, MultiFlashConfig, SingleFlashConfig}; pub use partition::Partition; +pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; #[cfg(feature = "nightly")] pub use firmware_updater::FirmwareUpdater; pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; From 1cd87f0028c8a0f9b87a09016a22179fb05ced33 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:40:04 +0200 Subject: [PATCH 1265/1575] Cleanup MemFlash --- embassy-boot/boot/src/mem_flash.rs | 108 +++++++++++++++-------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/embassy-boot/boot/src/mem_flash.rs b/embassy-boot/boot/src/mem_flash.rs index 3ba84a92c..2728e9720 100644 --- a/embassy-boot/boot/src/mem_flash.rs +++ b/embassy-boot/boot/src/mem_flash.rs @@ -34,6 +34,52 @@ impl MemFla } } + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), MemFlashError> { + let len = bytes.len(); + bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); + Ok(()) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { + let offset = offset as usize; + assert!(bytes.len() % WRITE_SIZE == 0); + assert!(offset % WRITE_SIZE == 0); + assert!(offset + bytes.len() <= SIZE); + + if let Some(pending_successes) = self.pending_write_successes { + if pending_successes > 0 { + self.pending_write_successes = Some(pending_successes - 1); + } else { + return Err(MemFlashError); + } + } + + for ((offset, mem_byte), new_byte) in self + .mem + .iter_mut() + .enumerate() + .skip(offset) + .take(bytes.len()) + .zip(bytes) + { + assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); + *mem_byte = *new_byte; + } + + Ok(()) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), MemFlashError> { + let from = from as usize; + let to = to as usize; + assert!(from % ERASE_SIZE == 0); + assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); + for i in from..to { + self.mem[i] = 0xFF; + } + Ok(()) + } + pub fn program(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MemFlashError> { let offset = offset as usize; assert!(bytes.len() % WRITE_SIZE == 0); @@ -44,12 +90,6 @@ impl MemFla Ok(()) } - - pub fn assert_eq(&self, offset: u32, expectation: &[u8]) { - for i in 0..expectation.len() { - assert_eq!(self.mem[offset as usize + i], expectation[i], "Index {}", i); - } - } } impl Default @@ -78,9 +118,7 @@ impl ReadNo const READ_SIZE: usize = 1; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - let len = bytes.len(); - bytes.copy_from_slice(&self.mem[offset as usize..offset as usize + len]); - Ok(()) + self.read(offset, bytes) } fn capacity(&self) -> usize { @@ -94,44 +132,12 @@ impl NorFla const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - let from = from as usize; - let to = to as usize; - assert!(from % ERASE_SIZE == 0); - assert!(to % ERASE_SIZE == 0, "To: {}, erase size: {}", to, ERASE_SIZE); - for i in from..to { - self.mem[i] = 0xFF; - } - Ok(()) + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) } - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - let offset = offset as usize; - assert!(bytes.len() % WRITE_SIZE == 0); - assert!(offset % WRITE_SIZE == 0); - assert!(offset + bytes.len() <= SIZE); - - if let Some(pending_successes) = self.pending_write_successes { - if pending_successes > 0 { - self.pending_write_successes = Some(pending_successes - 1); - } else { - return Err(MemFlashError); - } - } - - for ((offset, mem_byte), new_byte) in self - .mem - .iter_mut() - .enumerate() - .skip(offset) - .take(bytes.len()) - .zip(bytes) - { - assert_eq!(0xFF, *mem_byte, "Offset {} is not erased", offset); - *mem_byte = *new_byte; - } - - Ok(()) + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) } } @@ -142,11 +148,11 @@ impl AsyncR const READ_SIZE: usize = 1; async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - ::read(self, offset, bytes) + self.read(offset, bytes) } fn capacity(&self) -> usize { - ::capacity(self) + SIZE } } @@ -157,11 +163,11 @@ impl AsyncN const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; - async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - ::erase(self, from, to) + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) } - async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - ::write(self, offset, bytes) + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) } } From b23e40f72242a9f4759d8a6d4a924c8c9d041c67 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:41:10 +0200 Subject: [PATCH 1266/1575] Add TestFlash helper --- embassy-boot/boot/src/lib.rs | 6 ++ embassy-boot/boot/src/test_flash/asynch.rs | 57 +++++++++++++++++ embassy-boot/boot/src/test_flash/blocking.rs | 65 ++++++++++++++++++++ embassy-boot/boot/src/test_flash/mod.rs | 7 +++ 4 files changed, 135 insertions(+) create mode 100644 embassy-boot/boot/src/test_flash/asynch.rs create mode 100644 embassy-boot/boot/src/test_flash/blocking.rs create mode 100644 embassy-boot/boot/src/test_flash/mod.rs diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index f2034fa8a..c76087ff1 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -7,10 +7,16 @@ mod fmt; mod boot_loader; mod digest_adapters; mod firmware_updater; +#[cfg(test)] mod mem_flash; mod partition; +#[cfg(test)] +mod test_flash; pub use partition::Partition; +// The expected value of the flash after an erase +// TODO: Use the value provided by NorFlash when available +pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; #[cfg(feature = "nightly")] pub use firmware_updater::FirmwareUpdater; diff --git a/embassy-boot/boot/src/test_flash/asynch.rs b/embassy-boot/boot/src/test_flash/asynch.rs new file mode 100644 index 000000000..b5b3c2538 --- /dev/null +++ b/embassy-boot/boot/src/test_flash/asynch.rs @@ -0,0 +1,57 @@ +use embassy_embedded_hal::flash::partition::Partition; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::mutex::Mutex; +use embedded_storage_async::nor_flash::NorFlash; + +pub struct AsyncTestFlash +where + ACTIVE: NorFlash, + DFU: NorFlash, + STATE: NorFlash, +{ + active: Mutex, + dfu: Mutex, + state: Mutex, +} + +impl AsyncTestFlash +where + ACTIVE: NorFlash, + DFU: NorFlash, + STATE: NorFlash, +{ + pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + Self { + active: Mutex::new(active), + dfu: Mutex::new(dfu), + state: Mutex::new(state), + } + } + + pub fn active(&self) -> Partition { + Self::create_partition(&self.active) + } + + pub fn dfu(&self) -> Partition { + Self::create_partition(&self.dfu) + } + + pub fn state(&self) -> Partition { + Self::create_partition(&self.state) + } + + fn create_partition(mutex: &Mutex) -> Partition { + Partition::new(mutex, 0, mutex.try_lock().unwrap().capacity() as u32) + } +} + +impl AsyncTestFlash +where + ACTIVE: NorFlash + embedded_storage::nor_flash::NorFlash, + DFU: NorFlash + embedded_storage::nor_flash::NorFlash, + STATE: NorFlash + embedded_storage::nor_flash::NorFlash, +{ + pub fn into_blocking(self) -> super::BlockingTestFlash { + super::BlockingTestFlash::new(self.active.into_inner(), self.dfu.into_inner(), self.state.into_inner()) + } +} diff --git a/embassy-boot/boot/src/test_flash/blocking.rs b/embassy-boot/boot/src/test_flash/blocking.rs new file mode 100644 index 000000000..77876a218 --- /dev/null +++ b/embassy-boot/boot/src/test_flash/blocking.rs @@ -0,0 +1,65 @@ +use core::cell::RefCell; + +use embassy_embedded_hal::flash::partition::BlockingPartition; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embedded_storage::nor_flash::NorFlash; + +pub struct BlockingTestFlash +where + ACTIVE: NorFlash, + DFU: NorFlash, + STATE: NorFlash, +{ + active: Mutex>, + dfu: Mutex>, + state: Mutex>, +} + +impl BlockingTestFlash +where + ACTIVE: NorFlash, + DFU: NorFlash, + STATE: NorFlash, +{ + pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + Self { + active: Mutex::new(RefCell::new(active)), + dfu: Mutex::new(RefCell::new(dfu)), + state: Mutex::new(RefCell::new(state)), + } + } + + pub fn active(&self) -> BlockingPartition { + Self::create_partition(&self.active) + } + + pub fn dfu(&self) -> BlockingPartition { + Self::create_partition(&self.dfu) + } + + pub fn state(&self) -> BlockingPartition { + Self::create_partition(&self.state) + } + + pub fn create_partition( + mutex: &Mutex>, + ) -> BlockingPartition { + BlockingPartition::new(mutex, 0, mutex.lock(|f| f.borrow().capacity()) as u32) + } +} + +impl BlockingTestFlash +where + ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash, + DFU: NorFlash + embedded_storage_async::nor_flash::NorFlash, + STATE: NorFlash + embedded_storage_async::nor_flash::NorFlash, +{ + pub fn into_async(self) -> super::AsyncTestFlash { + super::AsyncTestFlash::new( + self.active.into_inner().into_inner(), + self.dfu.into_inner().into_inner(), + self.state.into_inner().into_inner(), + ) + } +} diff --git a/embassy-boot/boot/src/test_flash/mod.rs b/embassy-boot/boot/src/test_flash/mod.rs new file mode 100644 index 000000000..a0672322e --- /dev/null +++ b/embassy-boot/boot/src/test_flash/mod.rs @@ -0,0 +1,7 @@ +#[cfg(feature = "nightly")] +mod asynch; +mod blocking; + +#[cfg(feature = "nightly")] +pub(crate) use asynch::AsyncTestFlash; +pub(crate) use blocking::BlockingTestFlash; From 551f76c70067bfa14b159a198cdb92f810f40607 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:44:12 +0200 Subject: [PATCH 1267/1575] Remove legacy Partition type and use the one from embedded-hal --- embassy-boot/boot/Cargo.toml | 7 +- embassy-boot/boot/src/lib.rs | 2 - embassy-boot/boot/src/partition.rs | 144 ----------------------------- 3 files changed, 4 insertions(+), 149 deletions(-) delete mode 100644 embassy-boot/boot/src/partition.rs diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 3fdf69c5e..415d7960f 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml @@ -27,9 +27,10 @@ defmt = { version = "0.3", optional = true } digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true } +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embedded-storage = "0.3.0" -embedded-storage-async = { version = "0.4.0", optional = true} +embedded-storage-async = { version = "0.4.0", optional = true } salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true } signature = { version = "1.6.4", default-features = false } @@ -39,7 +40,7 @@ env_logger = "0.9" rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version futures = { version = "0.3", features = ["executor"] } sha1 = "0.10.5" -embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } +critical-section = { version = "1.1.1", features = ["std"] } [dev-dependencies.ed25519-dalek] default_features = false @@ -49,7 +50,7 @@ features = ["rand", "std", "u32_backend"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] -nightly = ["dep:embedded-storage-async"] +nightly = ["dep:embedded-storage-async", "embassy-embedded-hal/nightly"] #Internal features _verify = [] diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index c76087ff1..15d3a4f21 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -9,11 +9,9 @@ mod digest_adapters; mod firmware_updater; #[cfg(test)] mod mem_flash; -mod partition; #[cfg(test)] mod test_flash; -pub use partition::Partition; // The expected value of the flash after an erase // TODO: Use the value provided by NorFlash when available pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; diff --git a/embassy-boot/boot/src/partition.rs b/embassy-boot/boot/src/partition.rs deleted file mode 100644 index 7b56a8240..000000000 --- a/embassy-boot/boot/src/partition.rs +++ /dev/null @@ -1,144 +0,0 @@ -use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; -#[cfg(feature = "nightly")] -use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; - -/// A region in flash used by the bootloader. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Partition { - /// The offset into the flash where the partition starts. - pub from: u32, - /// The offset into the flash where the partition ends. - pub to: u32, -} - -impl Partition { - /// Create a new partition with the provided range - pub const fn new(from: u32, to: u32) -> Self { - Self { from, to } - } - - /// Return the size of the partition - pub const fn size(&self) -> u32 { - self.to - self.from - } - - /// Read from the partition on the provided flash - #[cfg(feature = "nightly")] - pub async fn read( - &self, - flash: &mut F, - offset: u32, - bytes: &mut [u8], - ) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.read(offset, bytes).await - } - - /// Write to the partition on the provided flash - #[cfg(feature = "nightly")] - pub async fn write(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.write(offset, bytes).await?; - trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); - Ok(()) - } - - /// Erase part of the partition on the provided flash - #[cfg(feature = "nightly")] - pub async fn erase(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { - let from = self.from as u32 + from; - let to = self.from as u32 + to; - flash.erase(from, to).await?; - trace!("Erased from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Erase the entire partition - #[cfg(feature = "nightly")] - pub(crate) async fn wipe(&self, flash: &mut F) -> Result<(), F::Error> { - let from = self.from as u32; - let to = self.to as u32; - flash.erase(from, to).await?; - trace!("Wiped from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Read from the partition on the provided flash - pub fn read_blocking(&self, flash: &mut F, offset: u32, bytes: &mut [u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.read(offset, bytes) - } - - /// Write to the partition on the provided flash - pub fn write_blocking(&self, flash: &mut F, offset: u32, bytes: &[u8]) -> Result<(), F::Error> { - let offset = self.from as u32 + offset; - flash.write(offset, bytes)?; - trace!("Wrote from 0x{:x} len {}", offset, bytes.len()); - Ok(()) - } - - /// Erase part of the partition on the provided flash - pub fn erase_blocking(&self, flash: &mut F, from: u32, to: u32) -> Result<(), F::Error> { - let from = self.from as u32 + from; - let to = self.from as u32 + to; - flash.erase(from, to)?; - trace!("Erased from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } - - /// Erase the entire partition - pub(crate) fn wipe_blocking(&self, flash: &mut F) -> Result<(), F::Error> { - let from = self.from as u32; - let to = self.to as u32; - flash.erase(from, to)?; - trace!("Wiped from 0x{:x} to 0x{:x}", from, to); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use crate::mem_flash::MemFlash; - use crate::Partition; - - #[test] - fn can_erase() { - let mut flash = MemFlash::<1024, 64, 4>::new(0x00); - let partition = Partition::new(256, 512); - - partition.erase_blocking(&mut flash, 64, 192).unwrap(); - - for (index, byte) in flash.mem.iter().copied().enumerate().take(256 + 64) { - assert_eq!(0x00, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64).take(128) { - assert_eq!(0xFF, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(256 + 64 + 128) { - assert_eq!(0x00, byte, "Index {}", index); - } - } - - #[test] - fn can_wipe() { - let mut flash = MemFlash::<1024, 64, 4>::new(0x00); - let partition = Partition::new(256, 512); - - partition.wipe_blocking(&mut flash).unwrap(); - - for (index, byte) in flash.mem.iter().copied().enumerate().take(256) { - assert_eq!(0x00, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(256).take(256) { - assert_eq!(0xFF, byte, "Index {}", index); - } - - for (index, byte) in flash.mem.iter().copied().enumerate().skip(512) { - assert_eq!(0x00, byte, "Index {}", index); - } - } -} From c6a984f506530bc08464800abc332be9c49ac198 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:55:49 +0200 Subject: [PATCH 1268/1575] Align tests --- embassy-boot/boot/src/boot_loader.rs | 2 +- embassy-boot/boot/src/lib.rs | 238 +++++++++++-------- embassy-boot/boot/src/test_flash/asynch.rs | 17 +- embassy-boot/boot/src/test_flash/blocking.rs | 22 +- 4 files changed, 162 insertions(+), 117 deletions(-) diff --git a/embassy-boot/boot/src/boot_loader.rs b/embassy-boot/boot/src/boot_loader.rs index bdf7bd7fd..a8c19197b 100644 --- a/embassy-boot/boot/src/boot_loader.rs +++ b/embassy-boot/boot/src/boot_loader.rs @@ -40,7 +40,7 @@ where /// If only a single flash is actually used, then that flash should be partitioned into three partitions before use. /// The easiest way to do this is to use [`BootLoaderConfig::from_linkerfile_blocking`] which will partition /// the provided flash according to symbols defined in the linkerfile. -pub struct BootLoaderConfig { +pub struct BootLoaderConfig { /// Flash type used for the active partition - the partition which will be booted from. pub active: ACTIVE, /// Flash type used for the dfu partition - the partition which will be swapped in when requested. diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 15d3a4f21..d13eafdd0 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -51,10 +51,18 @@ impl AsMut<[u8]> for AlignedBuffer { #[cfg(test)] mod tests { + use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; + #[cfg(feature = "nightly")] + use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; use futures::executor::block_on; use super::*; + use crate::boot_loader::BootLoaderConfig; + use crate::firmware_updater::FirmwareUpdaterConfig; use crate::mem_flash::MemFlash; + #[cfg(feature = "nightly")] + use crate::test_flash::AsyncTestFlash; + use crate::test_flash::BlockingTestFlash; /* #[test] @@ -73,147 +81,173 @@ mod tests { #[test] fn test_boot_state() { - const STATE: Partition = Partition::new(0, 4096); - const ACTIVE: Partition = Partition::new(4096, 61440); - const DFU: Partition = Partition::new(61440, 122880); + let flash = BlockingTestFlash::new(BootLoaderConfig { + active: MemFlash::<57344, 4096, 4>::default(), + dfu: MemFlash::<61440, 4096, 4>::default(), + state: MemFlash::<4096, 4096, 4>::default(), + }); - let mut flash = MemFlash::<131072, 4096, 4>::default(); - flash.mem[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); - let mut flash = SingleFlashConfig::new(&mut flash); + flash.state().write(0, &[BOOT_MAGIC; 4]).unwrap(); - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + let mut bootloader = BootLoader::new(BootLoaderConfig { + active: flash.active(), + dfu: flash.dfu(), + state: flash.state(), + }); let mut page = [0; 4096]; - assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash, &mut page).unwrap()); + assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap()); } #[test] #[cfg(all(feature = "nightly", not(feature = "_verify")))] fn test_swap_state() { - const STATE: Partition = Partition::new(0, 4096); - const ACTIVE: Partition = Partition::new(4096, 61440); - const DFU: Partition = Partition::new(61440, 122880); - let mut flash = MemFlash::<131072, 4096, 4>::random(); + const FIRMWARE_SIZE: usize = 57344; + let flash = AsyncTestFlash::new(BootLoaderConfig { + active: MemFlash::::default(), + dfu: MemFlash::<61440, 4096, 4>::default(), + state: MemFlash::<4096, 4096, 4>::default(), + }); - let original = [rand::random::(); ACTIVE.size() as usize]; - let update = [rand::random::(); ACTIVE.size() as usize]; + const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; + const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; let mut aligned = [0; 4]; - flash.program(ACTIVE.from, &original).unwrap(); + block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); + block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); - let mut updater = FirmwareUpdater::new(DFU, STATE); - block_on(updater.write_firmware(0, &update, &mut flash)).unwrap(); - block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated(&mut aligned)).unwrap(); + + let flash = flash.into_blocking(); + let mut bootloader = BootLoader::new(BootLoaderConfig { + active: flash.active(), + dfu: flash.dfu(), + state: flash.state(), + }); let mut page = [0; 1024]; - assert_eq!( - State::Swap, - bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) - .unwrap() - ); + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - flash.assert_eq(ACTIVE.from, &update); + let mut read_buf = [0; FIRMWARE_SIZE]; + flash.active().read(0, &mut read_buf).unwrap(); + assert_eq!(UPDATE, read_buf); // First DFU page is untouched - flash.assert_eq(DFU.from + 4096, &original); + flash.dfu().read(4096, &mut read_buf).unwrap(); + assert_eq!(ORIGINAL, read_buf); // Running again should cause a revert - assert_eq!( - State::Swap, - bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) - .unwrap() - ); + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - flash.assert_eq(ACTIVE.from, &original); - // Last page is untouched - flash.assert_eq(DFU.from, &update); + let mut read_buf = [0; FIRMWARE_SIZE]; + flash.active().read(0, &mut read_buf).unwrap(); + assert_eq!(ORIGINAL, read_buf); + // Last DFU page is untouched + flash.dfu().read(0, &mut read_buf).unwrap(); + assert_eq!(UPDATE, read_buf); // Mark as booted - block_on(updater.mark_booted(&mut flash, &mut aligned)).unwrap(); - assert_eq!( - State::Boot, - bootloader - .prepare_boot(&mut SingleFlashConfig::new(&mut flash), &mut page) - .unwrap() - ); + let flash = flash.into_async(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }); + block_on(updater.mark_booted(&mut aligned)).unwrap(); + + let flash = flash.into_blocking(); + let mut bootloader = BootLoader::new(BootLoaderConfig { + active: flash.active(), + dfu: flash.dfu(), + state: flash.state(), + }); + assert_eq!(State::Boot, bootloader.prepare_boot(&mut page).unwrap()); } #[test] #[cfg(all(feature = "nightly", not(feature = "_verify")))] - fn test_separate_flash_active_page_biggest() { - const STATE: Partition = Partition::new(2048, 4096); - const ACTIVE: Partition = Partition::new(4096, 16384); - const DFU: Partition = Partition::new(0, 16384); + fn test_swap_state_active_page_biggest() { + const FIRMWARE_SIZE: usize = 12288; + let flash = AsyncTestFlash::new(BootLoaderConfig { + active: MemFlash::<12288, 4096, 8>::random(), + dfu: MemFlash::<16384, 2048, 8>::random(), + state: MemFlash::<2048, 128, 4>::random(), + }); - let mut active = MemFlash::<16384, 4096, 8>::random(); - let mut dfu = MemFlash::<16384, 2048, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random(); + const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; + const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; let mut aligned = [0; 4]; - let original = [rand::random::(); ACTIVE.size() as usize]; - let update = [rand::random::(); ACTIVE.size() as usize]; + block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); + block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - active.program(ACTIVE.from, &original).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated(&mut aligned)).unwrap(); - let mut updater = FirmwareUpdater::new(DFU, STATE); + let flash = flash.into_blocking(); + let mut bootloader = BootLoader::new(BootLoaderConfig { + active: flash.active(), + dfu: flash.dfu(), + state: flash.state(), + }); - block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); - block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); - - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); let mut page = [0; 4096]; + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - assert_eq!( - State::Swap, - bootloader - .prepare_boot(&mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu), &mut page) - .unwrap() - ); - - active.assert_eq(ACTIVE.from, &update); + let mut read_buf = [0; FIRMWARE_SIZE]; + flash.active().read(0, &mut read_buf).unwrap(); + assert_eq!(UPDATE, read_buf); // First DFU page is untouched - dfu.assert_eq(DFU.from + 4096, &original); + flash.dfu().read(4096, &mut read_buf).unwrap(); + assert_eq!(ORIGINAL, read_buf); } #[test] #[cfg(all(feature = "nightly", not(feature = "_verify")))] - fn test_separate_flash_dfu_page_biggest() { - const STATE: Partition = Partition::new(2048, 4096); - const ACTIVE: Partition = Partition::new(4096, 16384); - const DFU: Partition = Partition::new(0, 16384); + fn test_swap_state_dfu_page_biggest() { + const FIRMWARE_SIZE: usize = 12288; + let flash = AsyncTestFlash::new(BootLoaderConfig { + active: MemFlash::::random(), + dfu: MemFlash::<16384, 4096, 8>::random(), + state: MemFlash::<2048, 128, 4>::random(), + }); + const ORIGINAL: [u8; FIRMWARE_SIZE] = [0x55; FIRMWARE_SIZE]; + const UPDATE: [u8; FIRMWARE_SIZE] = [0xAA; FIRMWARE_SIZE]; let mut aligned = [0; 4]; - let mut active = MemFlash::<16384, 2048, 4>::random(); - let mut dfu = MemFlash::<16384, 4096, 8>::random(); - let mut state = MemFlash::<4096, 128, 4>::random(); - let original = [rand::random::(); ACTIVE.size() as usize]; - let update = [rand::random::(); ACTIVE.size() as usize]; + block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); + block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - active.program(ACTIVE.from, &original).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated(&mut aligned)).unwrap(); - let mut updater = FirmwareUpdater::new(DFU, STATE); - - block_on(updater.write_firmware(0, &update, &mut dfu)).unwrap(); - block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); - - let mut bootloader: BootLoader = BootLoader::new(ACTIVE, DFU, STATE); + let flash = flash.into_blocking(); + let mut bootloader = BootLoader::new(BootLoaderConfig { + active: flash.active(), + dfu: flash.dfu(), + state: flash.state(), + }); let mut page = [0; 4096]; - assert_eq!( - State::Swap, - bootloader - .prepare_boot( - &mut MultiFlashConfig::new(&mut active, &mut state, &mut dfu,), - &mut page - ) - .unwrap() - ); + assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); - active.assert_eq(ACTIVE.from, &update); + let mut read_buf = [0; FIRMWARE_SIZE]; + flash.active().read(0, &mut read_buf).unwrap(); + assert_eq!(UPDATE, read_buf); // First DFU page is untouched - dfu.assert_eq(DFU.from + 4096, &original); + flash.dfu().read(4096, &mut read_buf).unwrap(); + assert_eq!(ORIGINAL, read_buf); } #[test] @@ -239,25 +273,25 @@ mod tests { let public_key: PublicKey = keypair.public; // Setup flash - - const STATE: Partition = Partition::new(0, 4096); - const DFU: Partition = Partition::new(4096, 8192); - let mut flash = MemFlash::<8192, 4096, 4>::default(); + let flash = BlockingTestFlash::new(BootLoaderConfig { + active: MemFlash::<0, 0, 0>::default(), + dfu: MemFlash::<4096, 4096, 4>::default(), + state: MemFlash::<4096, 4096, 4>::default(), + }); let firmware_len = firmware.len(); let mut write_buf = [0; 4096]; write_buf[0..firmware_len].copy_from_slice(firmware); - DFU.write_blocking(&mut flash, 0, &write_buf).unwrap(); + flash.dfu().write(0, &write_buf).unwrap(); // On with the test - - let mut updater = FirmwareUpdater::new(DFU, STATE); + let flash = flash.into_async(); + let mut updater = FirmwareUpdater::new(flash.dfu(), flash.state()); let mut aligned = [0; 4]; assert!(block_on(updater.verify_and_mark_updated( - &mut flash, &public_key.to_bytes(), &signature.to_bytes(), firmware_len as u32, diff --git a/embassy-boot/boot/src/test_flash/asynch.rs b/embassy-boot/boot/src/test_flash/asynch.rs index b5b3c2538..3ac9e71ab 100644 --- a/embassy-boot/boot/src/test_flash/asynch.rs +++ b/embassy-boot/boot/src/test_flash/asynch.rs @@ -3,6 +3,8 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::mutex::Mutex; use embedded_storage_async::nor_flash::NorFlash; +use crate::BootLoaderConfig; + pub struct AsyncTestFlash where ACTIVE: NorFlash, @@ -20,11 +22,11 @@ where DFU: NorFlash, STATE: NorFlash, { - pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + pub fn new(config: BootLoaderConfig) -> Self { Self { - active: Mutex::new(active), - dfu: Mutex::new(dfu), - state: Mutex::new(state), + active: Mutex::new(config.active), + dfu: Mutex::new(config.dfu), + state: Mutex::new(config.state), } } @@ -52,6 +54,11 @@ where STATE: NorFlash + embedded_storage::nor_flash::NorFlash, { pub fn into_blocking(self) -> super::BlockingTestFlash { - super::BlockingTestFlash::new(self.active.into_inner(), self.dfu.into_inner(), self.state.into_inner()) + let config = BootLoaderConfig { + active: self.active.into_inner(), + dfu: self.dfu.into_inner(), + state: self.state.into_inner(), + }; + super::BlockingTestFlash::new(config) } } diff --git a/embassy-boot/boot/src/test_flash/blocking.rs b/embassy-boot/boot/src/test_flash/blocking.rs index 77876a218..ba33c9208 100644 --- a/embassy-boot/boot/src/test_flash/blocking.rs +++ b/embassy-boot/boot/src/test_flash/blocking.rs @@ -5,6 +5,8 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embedded_storage::nor_flash::NorFlash; +use crate::BootLoaderConfig; + pub struct BlockingTestFlash where ACTIVE: NorFlash, @@ -22,11 +24,11 @@ where DFU: NorFlash, STATE: NorFlash, { - pub fn new(active: ACTIVE, dfu: DFU, state: STATE) -> Self { + pub fn new(config: BootLoaderConfig) -> Self { Self { - active: Mutex::new(RefCell::new(active)), - dfu: Mutex::new(RefCell::new(dfu)), - state: Mutex::new(RefCell::new(state)), + active: Mutex::new(RefCell::new(config.active)), + dfu: Mutex::new(RefCell::new(config.dfu)), + state: Mutex::new(RefCell::new(config.state)), } } @@ -49,6 +51,7 @@ where } } +#[cfg(feature = "nightly")] impl BlockingTestFlash where ACTIVE: NorFlash + embedded_storage_async::nor_flash::NorFlash, @@ -56,10 +59,11 @@ where STATE: NorFlash + embedded_storage_async::nor_flash::NorFlash, { pub fn into_async(self) -> super::AsyncTestFlash { - super::AsyncTestFlash::new( - self.active.into_inner().into_inner(), - self.dfu.into_inner().into_inner(), - self.state.into_inner().into_inner(), - ) + let config = BootLoaderConfig { + active: self.active.into_inner().into_inner(), + dfu: self.dfu.into_inner().into_inner(), + state: self.state.into_inner().into_inner(), + }; + super::AsyncTestFlash::new(config) } } From 54bbb4400d02e40d142d3f2cd9186c7892374d83 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:56:35 +0200 Subject: [PATCH 1269/1575] Align nrf --- embassy-boot/nrf/src/lib.rs | 71 +++++++++---------------------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 710798bdb..e26b07c9f 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -3,74 +3,37 @@ #![doc = include_str!("../README.md")] mod fmt; -pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig}; +#[cfg(feature = "nightly")] +pub use embassy_boot::FirmwareUpdater; +pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, +pub struct BootLoader { + boot: embassy_boot::BootLoader, aligned_buf: AlignedBuffer, } -#[cfg(target_os = "none")] -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 u32, - &__bootloader_active_end as *const u32 as u32, - ) - }; - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - 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) - } -} - -impl BootLoader { +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 { + pub fn new(config: BootLoaderConfig) -> Self { Self { - boot: embassy_boot::BootLoader::new(active, dfu, state), + boot: embassy_boot::BootLoader::new(config), aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. - pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, &mut self.aligned_buf.0) { - Ok(_) => self.boot.boot_address(), - Err(_) => panic!("boot prepare error!"), - } + pub fn prepare(&mut self) { + self.boot + .prepare_boot(&mut self.aligned_buf.0) + .expect("Boot prepare error"); } /// Boots the application without softdevice mechanisms. @@ -79,10 +42,10 @@ impl BootLoader { /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] - pub unsafe fn load(&mut self, start: usize) -> ! { + pub unsafe fn load(&mut self, start: u32) -> ! { let mut p = cortex_m::Peripherals::steal(); p.SCB.invalidate_icache(); - p.SCB.vtor.write(start as u32); + p.SCB.vtor.write(start); cortex_m::asm::bootload(start as *const u32) } @@ -92,7 +55,7 @@ impl BootLoader { /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(feature = "softdevice")] - pub unsafe fn load(&mut self, _app: usize) -> ! { + pub unsafe fn load(&mut self, _app: u32) -> ! { use nrf_softdevice_mbr as mbr; const NRF_SUCCESS: u32 = 0; From 24dee870a862d16fa2dc9be13d3f10dc3941b54b Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:57:03 +0200 Subject: [PATCH 1270/1575] Align rp --- embassy-boot/rp/src/lib.rs | 69 +++++++++----------------------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index fb9bc3242..e825a7d13 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -3,7 +3,9 @@ #![doc = include_str!("../README.md")] mod fmt; -pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; +#[cfg(feature = "nightly")] +pub use embassy_boot::FirmwareUpdater; +pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; use embassy_rp::flash::{Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; @@ -11,27 +13,28 @@ use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, +pub struct BootLoader { + boot: embassy_boot::BootLoader, aligned_buf: AlignedBuffer, } -impl BootLoader { +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 { + pub fn new(config: BootLoaderConfig) -> Self { Self { - boot: embassy_boot::BootLoader::new(active, dfu, state), + boot: embassy_boot::BootLoader::new(config), aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. - pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { - Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(), - Err(_) => panic!("boot prepare error!"), - } + pub fn prepare(&mut self) { + self.boot + .prepare_boot(self.aligned_buf.as_mut()) + .expect("Boot prepare error"); } /// Boots the application. @@ -39,58 +42,18 @@ impl BootLoader { /// # 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) -> ! { + pub unsafe fn load(&mut self, start: u32) -> ! { 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); + p.SCB.vtor.write(start); cortex_m::asm::bootload(start as *const u32) } } -#[cfg(target_os = "none")] -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 u32, - &__bootloader_active_end as *const u32 as u32, - ) - }; - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - 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) - } -} - /// A flash implementation that will feed a watchdog when touching flash. pub struct WatchdogFlash<'d, const SIZE: usize> { flash: Flash<'d, FLASH, SIZE>, From 887ecef3690b55234e640ae16d33a2d79a822bf0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:57:19 +0200 Subject: [PATCH 1271/1575] Align stm32 --- embassy-boot/stm32/src/lib.rs | 70 +++++++++-------------------------- 1 file changed, 17 insertions(+), 53 deletions(-) diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index ccf136c74..b47b2db43 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -3,30 +3,34 @@ #![doc = include_str!("../README.md")] mod fmt; -pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State}; +#[cfg(feature = "nightly")] +pub use embassy_boot::FirmwareUpdater; +pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; +use embedded_storage::nor_flash::NorFlash; /// A bootloader for STM32 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, +pub struct BootLoader { + boot: embassy_boot::BootLoader, aligned_buf: AlignedBuffer, } -impl BootLoader { +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 { + pub fn new(config: BootLoaderConfig) -> Self { Self { - boot: embassy_boot::BootLoader::new(active, dfu, state), + boot: embassy_boot::BootLoader::new(config), aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), } } /// Inspect the bootloader state and perform actions required before booting, such as swapping /// firmware. - pub fn prepare(&mut self, flash: &mut F) -> usize { - match self.boot.prepare_boot(flash, self.aligned_buf.as_mut()) { - Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(), - Err(_) => panic!("boot prepare error!"), - } + pub fn prepare(&mut self) { + self.boot + .prepare_boot(self.aligned_buf.as_mut()) + .expect("Boot prepare error"); } /// Boots the application. @@ -34,54 +38,14 @@ impl BootLoader { /// # 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) -> ! { + pub unsafe fn load(&mut self, start: u32) -> ! { 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); + p.SCB.vtor.write(start); cortex_m::asm::bootload(start as *const u32) } } - -#[cfg(target_os = "none")] -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 u32, - &__bootloader_active_end as *const u32 as u32, - ) - }; - let dfu = unsafe { - Partition::new( - &__bootloader_dfu_start as *const u32 as u32, - &__bootloader_dfu_end as *const u32 as u32, - ) - }; - let state = unsafe { - Partition::new( - &__bootloader_state_start as *const u32 as u32, - &__bootloader_state_end as *const u32 as u32, - ) - }; - - 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) - } -} From c2aca45b8d3785007da20ce007d6a6e352fac1a0 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 13:57:40 +0200 Subject: [PATCH 1272/1575] Add offset and size accessors to Partition --- embassy-embedded-hal/src/flash/partition/asynch.rs | 10 ++++++++++ embassy-embedded-hal/src/flash/partition/blocking.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/embassy-embedded-hal/src/flash/partition/asynch.rs b/embassy-embedded-hal/src/flash/partition/asynch.rs index 141e0d9fc..5920436dd 100644 --- a/embassy-embedded-hal/src/flash/partition/asynch.rs +++ b/embassy-embedded-hal/src/flash/partition/asynch.rs @@ -30,6 +30,16 @@ impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> { } Self { flash, offset, size } } + + /// Get the partition offset within the flash + pub const fn offset(&self) -> u32 { + self.offset + } + + /// Get the partition size + pub const fn size(&self) -> u32 { + self.size + } } impl ErrorType for Partition<'_, M, T> { diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs index dc52e292a..2ddbe3de0 100644 --- a/embassy-embedded-hal/src/flash/partition/blocking.rs +++ b/embassy-embedded-hal/src/flash/partition/blocking.rs @@ -31,6 +31,16 @@ impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> { } Self { flash, offset, size } } + + /// Get the partition offset within the flash + pub const fn offset(&self) -> u32 { + self.offset + } + + /// Get the partition size + pub const fn size(&self) -> u32 { + self.size + } } impl ErrorType for BlockingPartition<'_, M, T> { From 36e00caf4dc70905b735531c0d5634addd026954 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:03:31 +0200 Subject: [PATCH 1273/1575] Align examples --- embassy-boot/nrf/src/lib.rs | 4 +++- embassy-boot/rp/src/lib.rs | 4 +++- embassy-boot/stm32/src/lib.rs | 4 +++- examples/boot/application/nrf/src/bin/a.rs | 12 ++++++----- examples/boot/application/rp/Cargo.toml | 1 + examples/boot/application/rp/src/bin/a.rs | 19 ++++++++++------- .../boot/application/stm32f3/src/bin/a.rs | 12 ++++++----- .../boot/application/stm32f7/src/bin/a.rs | 14 +++++++------ .../boot/application/stm32h7/src/bin/a.rs | 15 ++++++------- .../boot/application/stm32l1/src/bin/a.rs | 11 +++++----- .../boot/application/stm32l4/src/bin/a.rs | 9 ++++---- .../boot/application/stm32wl/src/bin/a.rs | 9 ++++---- examples/boot/bootloader/nrf/Cargo.toml | 1 + examples/boot/bootloader/nrf/src/main.rs | 21 ++++++++++++------- examples/boot/bootloader/rp/Cargo.toml | 1 + examples/boot/bootloader/rp/src/main.rs | 16 +++++++++----- examples/boot/bootloader/stm32/Cargo.toml | 1 + examples/boot/bootloader/stm32/src/main.rs | 19 +++++++++++------ 18 files changed, 108 insertions(+), 65 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index e26b07c9f..bb702073c 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -42,7 +42,9 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] - pub unsafe fn load(&mut self, start: u32) -> ! { + pub unsafe fn load(self, start: u32) -> ! { + core::mem::drop(self.boot); + let mut p = cortex_m::Peripherals::steal(); p.SCB.invalidate_icache(); p.SCB.vtor.write(start); diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index e825a7d13..25329f9e9 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -42,7 +42,9 @@ impl /// # 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: u32) -> ! { + pub unsafe fn load(self, start: u32) -> ! { + core::mem::drop(self.boot); + trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index b47b2db43..069de0d1a 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -38,7 +38,9 @@ impl /// # 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: u32) -> ! { + pub unsafe fn load(self, start: u32) -> ! { + core::mem::drop(self.boot); + trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 090a05b23..06c237781 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -3,12 +3,13 @@ #![macro_use] #![feature(type_alias_impl_trait)] -use embassy_boot_nrf::FirmwareUpdater; +use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::nvmc::Nvmc; use embassy_nrf::wdt::{self, Watchdog}; +use embassy_sync::mutex::Mutex; use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); @@ -45,9 +46,10 @@ async fn main(_spawner: Spawner) { }; let nvmc = Nvmc::new(p.NVMC); - let mut nvmc = BlockingAsync::new(nvmc); + let nvmc = Mutex::new(BlockingAsync::new(nvmc)); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); + let mut updater = FirmwareUpdater::new(config); loop { led.set_low(); button.wait_for_any_edge().await; @@ -56,11 +58,11 @@ async fn main(_spawner: Spawner) { for chunk in APP_B.chunks(4096) { let mut buf: [u8; 4096] = [0; 4096]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut nvmc, 4096).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = [0; 4]; - updater.mark_updated(&mut nvmc, &mut magic).await.unwrap(); + updater.mark_updated(&mut magic).await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 64c2b8925..4a2c5dd8f 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -20,6 +20,7 @@ embedded-hal = { version = "0.2.6" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" +embedded-storage = "0.3.0" [features] default = ["panic-reset"] diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 47f1d16d8..3fa908b63 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -9,6 +9,9 @@ use embassy_rp::flash::Flash; use embassy_rp::gpio::{Level, Output}; use embassy_rp::watchdog::Watchdog; use embassy_time::{Duration, Timer}; +use embassy_sync::blocking_mutex::Mutex; +use core::cell::RefCell; +use embedded_storage::nor_flash::NorFlash; #[cfg(feature = "panic-probe")] use panic_probe as _; #[cfg(feature = "panic-reset")] @@ -26,9 +29,11 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let mut flash: Flash<_, FLASH_SIZE> = Flash::new_blocking(p.FLASH); + let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + let flash = Mutex::new(RefCell::new(flash)); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let mut updater = BlockingFirmwareUpdater::new(config); Timer::after(Duration::from_secs(5)).await; watchdog.feed(); @@ -36,8 +41,8 @@ async fn main(_s: Spawner) { 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) + let writer = updater + .prepare_update() .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) .unwrap(); defmt::info!("writer created, starting write"); @@ -45,13 +50,13 @@ async fn main(_s: Spawner) { 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) + .write(offset, &buf.0[..]) .unwrap(); - offset += chunk.len(); + offset += chunk.len() as u32; } watchdog.feed(); defmt::info!("firmware written, marking update"); - updater.mark_updated_blocking(&mut flash, &mut buf.0[..1]).unwrap(); + updater.mark_updated(&mut buf.0[..1]).unwrap(); Timer::after(Duration::from_secs(2)).await; led.set_low(); defmt::info!("update marked, resetting"); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 5db1dbb57..6a5c276fd 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -4,7 +4,8 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; +use embassy_sync::mutex::Mutex; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; @@ -18,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PC13, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI13); @@ -26,17 +27,18 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PA5, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 5d586445c..530683cac 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new_blocking(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); + let flash = Mutex::new(RefCell::new(flash)); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -24,20 +25,21 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB7, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); - let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let mut updater = BlockingFirmwareUpdater::new(config); + let mut writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); writer - .write_block_blocking(offset, buf.as_ref(), &mut flash, chunk.len()) + .write(offset, buf.as_ref()) .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); + updater.mark_updated(magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 202220223..e5f94310c 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; @@ -16,7 +16,8 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut flash = Flash::new_blocking(p.FLASH); + let flash = Flash::new_blocking(p.FLASH); + let flash = Mutex::new(RefCell::new(flash)); let button = Input::new(p.PC13, Pull::Down); let mut button = ExtiInput::new(button, p.EXTI13); @@ -24,21 +25,21 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); - - let mut writer = updater.prepare_update_blocking(&mut flash).unwrap(); + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let mut updater = BlockingFirmwareUpdater::new(config); + let mut writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); writer - .write_block_blocking(offset, buf.as_ref(), &mut flash, 4096) + .write(offset, buf.as_ref()) .unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated_blocking(&mut flash, magic.as_mut()).unwrap(); + updater.mark_updated(magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 4033ac590..00ddda636 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; @@ -19,7 +19,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PB2, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI2); @@ -28,18 +28,19 @@ async fn main(_spawner: Spawner) { led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 128).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 141d82afd..54579e4ac 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -4,7 +4,7 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PC13, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI13); @@ -26,13 +26,14 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 5f48dbe51..0c6fa05f9 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let mut flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PA0, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI0); @@ -26,7 +26,8 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB9, Level::Low, Speed::Low); led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; //defmt::info!("Starting update"); let mut offset = 0; @@ -34,11 +35,11 @@ async fn main(_spawner: Spawner) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); // defmt::info!("Writing chunk at 0x{:x}", offset); - updater.write_firmware(offset, &buf, &mut flash, 2048).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); //defmt::info!("Marked as updated"); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 8c2fb4c5f..40656f359 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] } embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +embassy-sync = { path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } cfg-if = "1.0.0" diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 8818a23b8..72c95c02a 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -1,12 +1,15 @@ #![no_std] #![no_main] +use core::cell::RefCell; + use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_nrf::*; use embassy_nrf::nvmc::Nvmc; use embassy_nrf::wdt; +use embassy_sync::blocking_mutex::Mutex; #[entry] fn main() -> ! { @@ -20,19 +23,21 @@ fn main() -> ! { } */ - let mut bl = BootLoader::default(); - let mut wdt_config = wdt::Config::default(); wdt_config.timeout_ticks = 32768 * 5; // timeout seconds wdt_config.run_during_sleep = true; wdt_config.run_during_debug_halt = false; - let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::new(WatchdogFlash::start( - Nvmc::new(p.NVMC), - p.WDT, - wdt_config, - )))); - unsafe { bl.load(start) } + let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); + let flash = Mutex::new(RefCell::new(flash)); + + let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let active_offset = config.active.offset(); + let mut bl: BootLoader<_, _, _> = BootLoader::new(config); + + bl.prepare(); + + unsafe { bl.load(active_offset) } } #[no_mangle] diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index bf9226993..8d60f18be 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -11,6 +11,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] } embassy-boot-rp = { path = "../../../../embassy-boot/rp" } +embassy-sync = { path = "../../../../embassy-sync" } embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 8129591fa..6a81db804 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -1,10 +1,13 @@ #![no_std] #![no_main] +use core::cell::RefCell; + use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_rp::*; +use embassy_sync::blocking_mutex::Mutex; use embassy_time::Duration; const FLASH_SIZE: usize = 2 * 1024 * 1024; @@ -21,13 +24,16 @@ fn main() -> ! { } */ - let mut bl: BootLoader = BootLoader::default(); let flash = WatchdogFlash::::start(p.FLASH, p.WATCHDOG, Duration::from_secs(8)); - let mut flash = BootFlash::new(flash); - let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); - core::mem::drop(flash); + let flash = Mutex::new(RefCell::new(flash)); - unsafe { bl.load(start) } + let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let active_offset = config.active.offset(); + let mut bl: BootLoader<_, _, _> = BootLoader::new(config); + + bl.prepare(); + + unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } } #[no_mangle] diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index fbc80b34c..6436f2fee 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -12,6 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] } embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +embassy-sync = { path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" embedded-storage-async = "0.4.0" diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index f81fdbc5f..262eed200 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -1,11 +1,14 @@ #![no_std] #![no_main] +use core::cell::RefCell; + use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::Flash; +use embassy_stm32::flash::{Flash, BANK1_REGION}; +use embassy_sync::blocking_mutex::Mutex; #[entry] fn main() -> ! { @@ -19,12 +22,16 @@ fn main() -> ! { } */ - let mut bl: BootLoader<2048> = BootLoader::default(); let layout = Flash::new_blocking(p.FLASH).into_blocking_regions(); - let mut flash = BootFlash::new(layout.bank1_region); - let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); - core::mem::drop(flash); - unsafe { bl.load(start) } + let flash = Mutex::new(RefCell::new(layout.bank1_region)); + + let config = BootLoaderConfig::from_linkerfile_blocking(&flash); + let active_offset = config.active.offset(); + let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); + + bl.prepare(); + + unsafe { bl.load(BANK1_REGION.base + active_offset) } } #[no_mangle] From b527cc98af97e43186a9676b78b4b7f7ece1fa86 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:05:38 +0200 Subject: [PATCH 1274/1575] Formatting --- examples/boot/application/rp/src/bin/a.rs | 9 ++++----- examples/boot/application/stm32f3/src/bin/a.rs | 2 +- examples/boot/application/stm32f7/src/bin/a.rs | 4 +--- examples/boot/application/stm32h7/src/bin/a.rs | 4 +--- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 3fa908b63..69850069b 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -2,15 +2,16 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::cell::RefCell; + 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_rp::watchdog::Watchdog; -use embassy_time::{Duration, Timer}; use embassy_sync::blocking_mutex::Mutex; -use core::cell::RefCell; +use embassy_time::{Duration, Timer}; use embedded_storage::nor_flash::NorFlash; #[cfg(feature = "panic-probe")] use panic_probe as _; @@ -49,9 +50,7 @@ async fn main(_s: Spawner) { for chunk in APP_B.chunks(4096) { buf.0[..chunk.len()].copy_from_slice(chunk); defmt::info!("writing block at offset {}", offset); - writer - .write(offset, &buf.0[..]) - .unwrap(); + writer.write(offset, &buf.0[..]).unwrap(); offset += chunk.len() as u32; } watchdog.feed(); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 6a5c276fd..c94676f09 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -5,12 +5,12 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; -use embassy_sync::mutex::Mutex; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::mutex::Mutex; use panic_reset as _; static APP_B: &[u8] = include_bytes!("../../b.bin"); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 530683cac..fc2702c91 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -33,9 +33,7 @@ async fn main(_spawner: Spawner) { let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); - writer - .write(offset, buf.as_ref()) - .unwrap(); + writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index e5f94310c..1a54464d0 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -33,9 +33,7 @@ async fn main(_spawner: Spawner) { let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); - writer - .write(offset, buf.as_ref()) - .unwrap(); + writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len(); } let mut magic = AlignedBuffer([0; WRITE_SIZE]); From b703db4c09d1d4f0c4296a7fd7b808d96f29e1a2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:07:35 +0200 Subject: [PATCH 1275/1575] Fix verify test --- embassy-boot/boot/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index d13eafdd0..45a87bd0e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -287,7 +287,10 @@ mod tests { // On with the test let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new(flash.dfu(), flash.state()); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }); let mut aligned = [0; 4]; From c22d2b5b5bbc5e3c7d3a039e90b50d39809a10f2 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Tue, 30 May 2023 14:13:53 +0200 Subject: [PATCH 1276/1575] Remove unused use's --- embassy-boot/boot/src/firmware_updater/asynch.rs | 8 ++++---- embassy-boot/boot/src/firmware_updater/blocking.rs | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index d0780bdf1..0b3f88313 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -1,5 +1,7 @@ use digest::Digest; +#[cfg(target_os = "none")] use embassy_embedded_hal::flash::partition::Partition; +#[cfg(target_os = "none")] use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage_async::nor_flash::NorFlash; @@ -13,14 +15,12 @@ pub struct FirmwareUpdater { state: STATE, } +#[cfg(target_os = "none")] impl<'a, FLASH: NorFlash> FirmwareUpdaterConfig, Partition<'a, NoopRawMutex, FLASH>> { /// Create a firmware updater config from the flash and address symbols defined in the linkerfile - #[cfg(target_os = "none")] - pub fn from_linkerfile(flash: &'a Mutex) -> Self { - use embassy_sync::mutex::Mutex; - + pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex) -> Self { extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index c44126149..551150c4f 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -1,5 +1,7 @@ use digest::Digest; +#[cfg(target_os = "none")] use embassy_embedded_hal::flash::partition::BlockingPartition; +#[cfg(target_os = "none")] use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage::nor_flash::NorFlash; @@ -13,16 +15,14 @@ pub struct BlockingFirmwareUpdater { state: STATE, } +#[cfg(target_os = "none")] impl<'a, FLASH: NorFlash> FirmwareUpdaterConfig, BlockingPartition<'a, NoopRawMutex, FLASH>> { /// Create a firmware updater config from the flash and address symbols defined in the linkerfile - #[cfg(target_os = "none")] - pub fn from_linkerfile_blocking(flash: &'a Mutex>) -> Self { - use core::cell::RefCell; - - use embassy_sync::blocking_mutex::Mutex; - + pub fn from_linkerfile_blocking( + flash: &'a embassy_sync::blocking_mutex::Mutex>, + ) -> Self { extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; From 36bd6c817ecd4bbe48d740d91c24473aae16c0fb Mon Sep 17 00:00:00 2001 From: George Elliott-Hunter Date: Tue, 30 May 2023 20:27:06 +0200 Subject: [PATCH 1277/1575] Add [profile.release] debug = true to all examples --- examples/.cargo/config.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examples/.cargo/config.toml diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml new file mode 100644 index 000000000..84d266320 --- /dev/null +++ b/examples/.cargo/config.toml @@ -0,0 +1,3 @@ +[profile.release] +# Allows defmt to display log locations even in release +debug = true \ No newline at end of file From c327c6cd6fc3c11cfaf83cf64591940d401c5f6b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 30 May 2023 22:42:49 +0200 Subject: [PATCH 1278/1575] cyw43: move crate to subdir. --- .github/workflows/rust.yml | 29 - .gitignore | 2 - .vscode/extensions.json | 11 - .vscode/settings.json | 17 - LICENSE-APACHE | 201 -- LICENSE-MIT | 25 - ci.sh | 25 - {firmware => cyw43-firmware}/43439A0.bin | Bin {firmware => cyw43-firmware}/43439A0_clm.bin | Bin .../LICENSE-permissive-binary-license-1.0.txt | 0 {firmware => cyw43-firmware}/README.md | 0 cyw43-pio/Cargo.toml | 2 +- Cargo.toml => cyw43/Cargo.toml | 0 README.md => cyw43/README.md | 0 {src => cyw43/src}/bus.rs | 0 {src => cyw43/src}/consts.rs | 0 {src => cyw43/src}/control.rs | 0 {src => cyw43/src}/countries.rs | 0 {src => cyw43/src}/events.rs | 0 {src => cyw43/src}/fmt.rs | 0 {src => cyw43/src}/ioctl.rs | 0 {src => cyw43/src}/lib.rs | 0 {src => cyw43/src}/nvram.rs | 0 {src => cyw43/src}/runner.rs | 0 {src => cyw43/src}/structs.rs | 0 examples/rpi-pico-w/Cargo.lock | 1707 +++++++++++++++++ examples/rpi-pico-w/Cargo.toml | 2 +- examples/rpi-pico-w/src/bin/tcp_server.rs | 25 +- examples/rpi-pico-w/src/bin/tcp_server_ap.rs | 20 +- examples/rpi-pico-w/src/bin/wifi_scan.rs | 20 +- rust-toolchain.toml | 8 - rustfmt.toml | 3 - 32 files changed, 1761 insertions(+), 336 deletions(-) delete mode 100644 .github/workflows/rust.yml delete mode 100644 .gitignore delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/settings.json delete mode 100644 LICENSE-APACHE delete mode 100644 LICENSE-MIT delete mode 100755 ci.sh rename {firmware => cyw43-firmware}/43439A0.bin (100%) rename {firmware => cyw43-firmware}/43439A0_clm.bin (100%) rename {firmware => cyw43-firmware}/LICENSE-permissive-binary-license-1.0.txt (100%) rename {firmware => cyw43-firmware}/README.md (100%) rename Cargo.toml => cyw43/Cargo.toml (100%) rename README.md => cyw43/README.md (100%) rename {src => cyw43/src}/bus.rs (100%) rename {src => cyw43/src}/consts.rs (100%) rename {src => cyw43/src}/control.rs (100%) rename {src => cyw43/src}/countries.rs (100%) rename {src => cyw43/src}/events.rs (100%) rename {src => cyw43/src}/fmt.rs (100%) rename {src => cyw43/src}/ioctl.rs (100%) rename {src => cyw43/src}/lib.rs (100%) rename {src => cyw43/src}/nvram.rs (100%) rename {src => cyw43/src}/runner.rs (100%) rename {src => cyw43/src}/structs.rs (100%) create mode 100644 examples/rpi-pico-w/Cargo.lock delete mode 100644 rust-toolchain.toml delete mode 100644 rustfmt.toml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index 2cd3ba5d7..000000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Rust - -on: - push: - branches: [master] - pull_request: - branches: [master] - merge_group: - -env: - CARGO_TERM_COLOR: always - -jobs: - build-nightly: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Check fmt - run: cargo fmt -- --check - - name: Build - run: ./ci.sh diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 1e7caa9ea..000000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -Cargo.lock -target/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index a8bb78adb..000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - // List of extensions which should be recommended for users of this workspace. - "recommendations": [ - "rust-lang.rust-analyzer", - "tamasfe.even-better-toml", - ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index dd479929e..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "editor.formatOnSave": true, - "[toml]": { - "editor.formatOnSave": false - }, - "rust-analyzer.cargo.target": "thumbv6m-none-eabi", - "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.check.allTargets": false, - "rust-analyzer.check.noDefaultFeatures": true, - "rust-analyzer.linkedProjects": [ - "examples/rpi-pico-w/Cargo.toml", - ], - "rust-analyzer.server.extraEnv": { - "WIFI_NETWORK": "foo", - "WIFI_PASSWORD": "foo", - } -} \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index ea4fa15c9..000000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright 2019-2022 Embassy project contributors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index 87c052836..000000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019-2022 Embassy project contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/ci.sh b/ci.sh deleted file mode 100755 index 916838200..000000000 --- a/ci.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -euxo pipefail -cd $(dirname $0) - -export CARGO_TARGET_DIR=$(pwd)/target -export DEFMT_LOG=trace - -# build examples -#================== - -(cd examples/rpi-pico-w; WIFI_NETWORK=foo WIFI_PASSWORD=bar cargo build --release) - - -# build with log/defmt combinations -#===================================== - -cargo build --target thumbv6m-none-eabi --features '' -cargo build --target thumbv6m-none-eabi --features 'log' -cargo build --target thumbv6m-none-eabi --features 'defmt' -cargo build --target thumbv6m-none-eabi --features 'log,firmware-logs' -cargo build --target thumbv6m-none-eabi --features 'defmt,firmware-logs' - -(cd cyw43-pio; cargo build --target thumbv6m-none-eabi --features '') -(cd cyw43-pio; cargo build --target thumbv6m-none-eabi --features 'overclock') diff --git a/firmware/43439A0.bin b/cyw43-firmware/43439A0.bin similarity index 100% rename from firmware/43439A0.bin rename to cyw43-firmware/43439A0.bin diff --git a/firmware/43439A0_clm.bin b/cyw43-firmware/43439A0_clm.bin similarity index 100% rename from firmware/43439A0_clm.bin rename to cyw43-firmware/43439A0_clm.bin diff --git a/firmware/LICENSE-permissive-binary-license-1.0.txt b/cyw43-firmware/LICENSE-permissive-binary-license-1.0.txt similarity index 100% rename from firmware/LICENSE-permissive-binary-license-1.0.txt rename to cyw43-firmware/LICENSE-permissive-binary-license-1.0.txt diff --git a/firmware/README.md b/cyw43-firmware/README.md similarity index 100% rename from firmware/README.md rename to cyw43-firmware/README.md diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 2238f7617..a7af2c8e2 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" overclock = [] [dependencies] -cyw43 = { path = "../" } +cyw43 = { path = "../cyw43" } embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] } pio-proc = "0.2" pio = "0.2.1" diff --git a/Cargo.toml b/cyw43/Cargo.toml similarity index 100% rename from Cargo.toml rename to cyw43/Cargo.toml diff --git a/README.md b/cyw43/README.md similarity index 100% rename from README.md rename to cyw43/README.md diff --git a/src/bus.rs b/cyw43/src/bus.rs similarity index 100% rename from src/bus.rs rename to cyw43/src/bus.rs diff --git a/src/consts.rs b/cyw43/src/consts.rs similarity index 100% rename from src/consts.rs rename to cyw43/src/consts.rs diff --git a/src/control.rs b/cyw43/src/control.rs similarity index 100% rename from src/control.rs rename to cyw43/src/control.rs diff --git a/src/countries.rs b/cyw43/src/countries.rs similarity index 100% rename from src/countries.rs rename to cyw43/src/countries.rs diff --git a/src/events.rs b/cyw43/src/events.rs similarity index 100% rename from src/events.rs rename to cyw43/src/events.rs diff --git a/src/fmt.rs b/cyw43/src/fmt.rs similarity index 100% rename from src/fmt.rs rename to cyw43/src/fmt.rs diff --git a/src/ioctl.rs b/cyw43/src/ioctl.rs similarity index 100% rename from src/ioctl.rs rename to cyw43/src/ioctl.rs diff --git a/src/lib.rs b/cyw43/src/lib.rs similarity index 100% rename from src/lib.rs rename to cyw43/src/lib.rs diff --git a/src/nvram.rs b/cyw43/src/nvram.rs similarity index 100% rename from src/nvram.rs rename to cyw43/src/nvram.rs diff --git a/src/runner.rs b/cyw43/src/runner.rs similarity index 100% rename from src/runner.rs rename to cyw43/src/runner.rs diff --git a/src/structs.rs b/cyw43/src/structs.rs similarity index 100% rename from src/structs.rs rename to cyw43/src/structs.rs diff --git a/examples/rpi-pico-w/Cargo.lock b/examples/rpi-pico-w/Cargo.lock new file mode 100644 index 000000000..16095835b --- /dev/null +++ b/examples/rpi-pico-w/Cargo.lock @@ -0,0 +1,1707 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "as-slice" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" +dependencies = [ + "generic-array 0.12.4", + "generic-array 0.13.3", + "generic-array 0.14.7", + "stable_deref_trait", +] + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section 1.1.1", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c314e70d181aa6053b26e3f7fbf86d1dfff84f816a6175b967666b3506ef7289" +dependencies = [ + "critical-section 1.1.1", +] + +[[package]] +name = "atomic-pool" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c5fc22e05ec2884db458bf307dc7b278c9428888d2b6e6fad9c0ae7804f5f6" +dependencies = [ + "as-slice 0.1.5", + "as-slice 0.2.1", + "atomic-polyfill 1.0.2", + "stable_deref_trait", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version 0.2.3", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal 0.2.5", + "bitfield", + "critical-section 1.1.1", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "crc-any" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774646b687f63643eb0f4bf13dc263cb581c8c9e57973b6ddf78bda3994d88df" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "critical-section" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1706d332edc22aef4d9f23a6bb1c92360a403013c291af51247a737472dcae6" +dependencies = [ + "bare-metal 1.0.0", + "critical-section 1.1.1", +] + +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "cyw43" +version = "0.1.0" +dependencies = [ + "atomic-polyfill 0.1.11", + "cortex-m", + "cortex-m-rt", + "defmt", + "embassy-futures", + "embassy-net-driver-channel", + "embassy-sync", + "embassy-time", + "embedded-hal 1.0.0-alpha.10", + "futures", + "num_enum", +] + +[[package]] +name = "cyw43-example-rpi-pico-w" +version = "0.1.0" +dependencies = [ + "atomic-polyfill 0.1.11", + "cortex-m", + "cortex-m-rt", + "cyw43", + "cyw43-pio", + "defmt", + "defmt-rtt", + "embassy-executor", + "embassy-net", + "embassy-rp", + "embassy-time", + "embedded-io", + "futures", + "heapless", + "panic-probe", + "static_cell", +] + +[[package]] +name = "cyw43-pio" +version = "0.1.0" +dependencies = [ + "cyw43", + "defmt", + "embassy-rp", + "fixed", + "pio", + "pio-proc", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956673bd3cb347512bf988d1e8d89ac9a82b64f6eec54d3c01c3529dac019882" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4abc4821bd84d3d8f49945ddb24d029be9385ed9b77c99bf2f6296847a6a9f0" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "defmt-parser" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2cbbbd58847d508d97629b32cd9730a2d28532f71e219714614406029f18b1" +dependencies = [ + "critical-section 0.2.8", + "defmt", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "embassy-cortex-m" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "atomic-polyfill 1.0.2", + "cfg-if", + "cortex-m", + "critical-section 1.1.1", + "embassy-executor", + "embassy-hal-common", + "embassy-macros", + "embassy-sync", +] + +[[package]] +name = "embassy-embedded-hal" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "embassy-sync", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0-alpha.10", + "embedded-hal-async", + "embedded-storage", + "embedded-storage-async", + "nb 1.1.0", +] + +[[package]] +name = "embassy-executor" +version = "0.2.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "atomic-polyfill 1.0.2", + "cortex-m", + "critical-section 1.1.1", + "defmt", + "embassy-macros", + "embassy-time", + "futures-util", + "static_cell", +] + +[[package]] +name = "embassy-futures" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" + +[[package]] +name = "embassy-hal-common" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "defmt", + "num-traits", +] + +[[package]] +name = "embassy-macros" +version = "0.2.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "embassy-net" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "as-slice 0.2.1", + "atomic-polyfill 1.0.2", + "atomic-pool", + "defmt", + "embassy-hal-common", + "embassy-net-driver", + "embassy-sync", + "embassy-time", + "embedded-io", + "embedded-nal-async", + "futures", + "generic-array 0.14.7", + "heapless", + "managed", + "smoltcp", + "stable_deref_trait", +] + +[[package]] +name = "embassy-net-driver" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "defmt", +] + +[[package]] +name = "embassy-net-driver-channel" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "embassy-futures", + "embassy-net-driver", + "embassy-sync", +] + +[[package]] +name = "embassy-rp" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "atomic-polyfill 1.0.2", + "cfg-if", + "cortex-m", + "cortex-m-rt", + "critical-section 1.1.1", + "defmt", + "embassy-cortex-m", + "embassy-embedded-hal", + "embassy-executor", + "embassy-futures", + "embassy-hal-common", + "embassy-sync", + "embassy-time", + "embassy-usb-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0-alpha.10", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-storage", + "fixed", + "futures", + "nb 1.1.0", + "paste", + "pio", + "pio-proc", + "rand_core", + "rp-pac", + "rp2040-boot2", +] + +[[package]] +name = "embassy-sync" +version = "0.2.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "cfg-if", + "critical-section 1.1.1", + "embedded-io", + "futures-util", + "heapless", +] + +[[package]] +name = "embassy-time" +version = "0.1.1" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "atomic-polyfill 1.0.2", + "cfg-if", + "critical-section 1.1.1", + "defmt", + "embedded-hal 0.2.7", + "futures-util", + "heapless", +] + +[[package]] +name = "embassy-usb-driver" +version = "0.1.0" +source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" +dependencies = [ + "defmt", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0-alpha.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c4d073f5d91c66e629b216818a4c9747eeda0debedf2deda9a0a947e4e93b" + +[[package]] +name = "embedded-hal-async" +version = "0.2.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8042370aa7af48de36d5312cda14c18ed8ca6b7ce64f5a07832fedc9dc83063f" +dependencies = [ + "embedded-hal 1.0.0-alpha.10", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465fffd56a95bbc105c17965bca1c1d5815027b1cc6bb183bc05d04563d065c" +dependencies = [ + "embedded-hal 1.0.0-alpha.10", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" +dependencies = [ + "defmt", +] + +[[package]] +name = "embedded-nal" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9efecb57ab54fa918730f2874d7d37647169c50fa1357fecb81abee840b113" +dependencies = [ + "heapless", + "nb 1.1.0", + "no-std-net 0.5.0", +] + +[[package]] +name = "embedded-nal-async" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ce84f518ca912777ec143db235f4d615e3bf8d4e46d507d6ef12daf5b1df98" +dependencies = [ + "embedded-io", + "embedded-nal", + "heapless", + "no-std-net 0.6.0", +] + +[[package]] +name = "embedded-storage" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "156d7a2fdd98ebbf9ae579cbceca3058cff946e13f8e17b90e3511db0508c723" + +[[package]] +name = "embedded-storage-async" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052997a894670d0cde873faa7405bc98e2fd29f569d2acd568561bc1c396b35a" +dependencies = [ + "embedded-storage", +] + +[[package]] +name = "ena" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +dependencies = [ + "log", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fixed" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79386fdcec5e0fde91b1a6a5bcd89677d1f9304f7f986b154a1b9109038854d9" +dependencies = [ + "az", + "bytemuck", + "half", + "typenum", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill 0.1.11", + "defmt", + "hash32", + "rustc_version 0.4.0", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "lalrpop" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +dependencies = [ + "ascii-canvas", + "bit-set", + "diff", + "ena", + "is-terminal", + "itertools", + "lalrpop-util", + "petgraph", + "regex", + "regex-syntax 0.6.29", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" +dependencies = [ + "regex", +] + +[[package]] +name = "libc" +version = "0.2.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "no-std-net" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bcece43b12349917e096cddfa66107277f123e6c96a5aea78711dc601a47152" + +[[package]] +name = "no-std-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "once_cell" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" + +[[package]] +name = "panic-probe" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9" +dependencies = [ + "cortex-m", + "defmt", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pio" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "pio-parser" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77532c2b8279aef98dfc7207ef15298a5a3d6b6cc76ccc8b65913d69f3a8dd6b" +dependencies = [ + "lalrpop", + "lalrpop-util", + "pio", + "regex-syntax 0.6.29", +] + +[[package]] +name = "pio-proc" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b04dc870fb3a4fd8b3e4ca8c61b53bc8ac4eb78b66805d2b3c2e5c4829e0d7a" +dependencies = [ + "codespan-reporting", + "lalrpop-util", + "pio", + "pio-parser", + "proc-macro-error", + "proc-macro2", + "quote", + "regex-syntax 0.6.29", + "syn 1.0.109", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "rp-pac" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76e426cd8377db668fba1fe885028788b126b7cef91059cd478de8b076c2915" +dependencies = [ + "cortex-m", + "cortex-m-rt", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.17", +] + +[[package]] +name = "rustix" +version = "0.37.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "smoltcp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9786ac45091b96f946693e05bfa4d8ca93e2d3341237d97a380107a6b38dea" +dependencies = [ + "bitflags", + "byteorder", + "cfg-if", + "defmt", + "heapless", + "managed", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_cell" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c37c250d21f53fa7165e76e5401d7e6539c211a8d2cf449e3962956a5cc2ce" +dependencies = [ + "atomic-polyfill 1.0.2", +] + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" +dependencies = [ + "vcell", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml index 525e934f4..d3ce3085e 100644 --- a/examples/rpi-pico-w/Cargo.toml +++ b/examples/rpi-pico-w/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] -cyw43 = { path = "../../", features = ["defmt", "firmware-logs"] } +cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } diff --git a/examples/rpi-pico-w/src/bin/tcp_server.rs b/examples/rpi-pico-w/src/bin/tcp_server.rs index 8accc469f..6a87e7c53 100644 --- a/examples/rpi-pico-w/src/bin/tcp_server.rs +++ b/examples/rpi-pico-w/src/bin/tcp_server.rs @@ -28,7 +28,11 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, + runner: cyw43::Runner< + 'static, + Output<'static, PIN_23>, + PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>, + >, ) -> ! { runner.run().await } @@ -44,8 +48,8 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); - let fw = include_bytes!("../../../../firmware/43439A0.bin"); - let clm = include_bytes!("../../../../firmware/43439A0_clm.bin"); + let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); + let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: @@ -57,7 +61,15 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); let mut pio = Pio::new(p.PIO0); - let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); + let spi = PioSpi::new( + &mut pio.common, + pio.sm0, + pio.irq0, + cs, + p.PIN_24, + p.PIN_29, + p.DMA_CH0, + ); let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; @@ -90,7 +102,10 @@ async fn main(spawner: Spawner) { loop { //control.join_open(env!("WIFI_NETWORK")).await; - match control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await { + match control + .join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")) + .await + { Ok(_) => break, Err(err) => { info!("join failed with status={}", err.status); diff --git a/examples/rpi-pico-w/src/bin/tcp_server_ap.rs b/examples/rpi-pico-w/src/bin/tcp_server_ap.rs index ee2c32379..24ff7767f 100644 --- a/examples/rpi-pico-w/src/bin/tcp_server_ap.rs +++ b/examples/rpi-pico-w/src/bin/tcp_server_ap.rs @@ -28,7 +28,11 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, + runner: cyw43::Runner< + 'static, + Output<'static, PIN_23>, + PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>, + >, ) -> ! { runner.run().await } @@ -44,8 +48,8 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); - let fw = include_bytes!("../../../../firmware/43439A0.bin"); - let clm = include_bytes!("../../../../firmware/43439A0_clm.bin"); + let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); + let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: @@ -57,7 +61,15 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); let mut pio = Pio::new(p.PIO0); - let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); + let spi = PioSpi::new( + &mut pio.common, + pio.sm0, + pio.irq0, + cs, + p.PIN_24, + p.PIN_29, + p.DMA_CH0, + ); let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; diff --git a/examples/rpi-pico-w/src/bin/wifi_scan.rs b/examples/rpi-pico-w/src/bin/wifi_scan.rs index a2a44f99d..8fb6c65aa 100644 --- a/examples/rpi-pico-w/src/bin/wifi_scan.rs +++ b/examples/rpi-pico-w/src/bin/wifi_scan.rs @@ -26,7 +26,11 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, + runner: cyw43::Runner< + 'static, + Output<'static, PIN_23>, + PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>, + >, ) -> ! { runner.run().await } @@ -42,8 +46,8 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); - let fw = include_bytes!("../../../../firmware/43439A0.bin"); - let clm = include_bytes!("../../../../firmware/43439A0_clm.bin"); + let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); + let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: @@ -55,7 +59,15 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); let mut pio = Pio::new(p.PIO0); - let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); + let spi = PioSpi::new( + &mut pio.common, + pio.sm0, + pio.irq0, + cs, + p.PIN_24, + p.PIN_29, + p.DMA_CH0, + ); let state = singleton!(cyw43::State::new()); let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 2582e88f5..000000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,8 +0,0 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history -[toolchain] -channel = "nightly-2023-04-18" -components = [ "rust-src", "rustfmt" ] -targets = [ - "thumbv6m-none-eabi", -] diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 3639f4386..000000000 --- a/rustfmt.toml +++ /dev/null @@ -1,3 +0,0 @@ -group_imports = "StdExternalCrate" -imports_granularity = "Module" -max_width=120 \ No newline at end of file From 3f35a8876ee65d030e32ab2444bc0abb29d88382 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 30 May 2023 23:22:34 +0200 Subject: [PATCH 1279/1575] cyw43: adapt build to main embassy repo. --- ci.sh | 11 + cyw43-pio/Cargo.toml | 6 +- cyw43/Cargo.toml | 20 +- examples/rp/Cargo.toml | 3 + .../src/bin/wifi_ap_tcp_server.rs} | 19 +- .../{rpi-pico-w => rp}/src/bin/wifi_scan.rs | 16 +- .../src/bin/wifi_tcp_server.rs} | 24 +- examples/rpi-pico-w/.cargo/config.toml | 8 - examples/rpi-pico-w/Cargo.lock | 1707 ----------------- examples/rpi-pico-w/Cargo.toml | 67 - examples/rpi-pico-w/build.rs | 36 - examples/rpi-pico-w/memory.x | 5 - 12 files changed, 32 insertions(+), 1890 deletions(-) rename examples/{rpi-pico-w/src/bin/tcp_server_ap.rs => rp/src/bin/wifi_ap_tcp_server.rs} (91%) rename examples/{rpi-pico-w => rp}/src/bin/wifi_scan.rs (88%) rename examples/{rpi-pico-w/src/bin/tcp_server.rs => rp/src/bin/wifi_tcp_server.rs} (89%) delete mode 100644 examples/rpi-pico-w/.cargo/config.toml delete mode 100644 examples/rpi-pico-w/Cargo.lock delete mode 100644 examples/rpi-pico-w/Cargo.toml delete mode 100644 examples/rpi-pico-w/build.rs delete mode 100644 examples/rpi-pico-w/memory.x diff --git a/ci.sh b/ci.sh index 1eafda3a3..8a3669f07 100755 --- a/ci.sh +++ b/ci.sh @@ -5,6 +5,10 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings export DEFMT_LOG=trace +# needed by wifi examples +export WIFI_NETWORK=x +export WIFI_PASSWORD=x + TARGET=$(rustc -vV | sed -n 's|host: ||p') BUILD_EXTRA="" @@ -82,6 +86,13 @@ cargo batch \ --- 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-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\ + --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \ + --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \ + --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs' \ + --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \ + --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features '' \ + --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'overclock' \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,nightly \ --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,nightly \ --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index a7af2c8e2..6e9e784a0 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -9,9 +9,9 @@ edition = "2021" overclock = [] [dependencies] -cyw43 = { path = "../cyw43" } -embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] } +cyw43 = { version = "0.1.0", path = "../cyw43" } +embassy-rp = { version = "0.1.0", path = "../embassy-rp" } pio-proc = "0.2" pio = "0.2.1" fixed = "1.23.1" -defmt = { version = "0.3", optional = true } \ No newline at end of file +defmt = { version = "0.3", optional = true } diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 2bb2b8d93..c7f8816f5 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -11,10 +11,10 @@ log = ["dep:log"] firmware-logs = [] [dependencies] -embassy-time = { version = "0.1.0" } -embassy-sync = { version = "0.2.0" } -embassy-futures = { version = "0.1.0" } -embassy-net-driver-channel = { version = "0.1.0" } +embassy-time = { version = "0.1.0", path = "../embassy-time"} +embassy-sync = { version = "0.2.0", path = "../embassy-sync"} +embassy-futures = { version = "0.1.0", path = "../embassy-futures"} +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} atomic-polyfill = "0.1.5" defmt = { version = "0.3", optional = true } @@ -26,15 +26,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } num_enum = { version = "0.5.7", default-features = false } - -[patch.crates-io] -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } - -[workspace] -members = ["cyw43-pio"] -default-members = ["cyw43-pio", "."] -exclude = ["examples"] \ No newline at end of file diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index ffeb69f15..f77377a6f 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -19,6 +19,8 @@ embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["ti lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } +cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } +cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } defmt = "0.3" defmt-rtt = "0.4" @@ -36,6 +38,7 @@ st7789 = "0.6.1" display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" +heapless = "0.7.15" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = "0.2.0-alpha.1" diff --git a/examples/rpi-pico-w/src/bin/tcp_server_ap.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs similarity index 91% rename from examples/rpi-pico-w/src/bin/tcp_server_ap.rs rename to examples/rp/src/bin/wifi_ap_tcp_server.rs index 24ff7767f..15264524e 100644 --- a/examples/rpi-pico-w/src/bin/tcp_server_ap.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -14,6 +14,7 @@ use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::Pio; +use embassy_time::Duration; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -28,11 +29,7 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner< - 'static, - Output<'static, PIN_23>, - PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>, - >, + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, ) -> ! { runner.run().await } @@ -61,15 +58,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); let mut pio = Pio::new(p.PIO0); - let spi = PioSpi::new( - &mut pio.common, - pio.sm0, - pio.irq0, - cs, - p.PIN_24, - p.PIN_29, - p.DMA_CH0, - ); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; @@ -111,7 +100,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(Duration::from_secs(10))); control.gpio_set(0, false).await; info!("Listening on TCP:1234..."); diff --git a/examples/rpi-pico-w/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs similarity index 88% rename from examples/rpi-pico-w/src/bin/wifi_scan.rs rename to examples/rp/src/bin/wifi_scan.rs index 8fb6c65aa..aa5e5a399 100644 --- a/examples/rpi-pico-w/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -26,11 +26,7 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner< - 'static, - Output<'static, PIN_23>, - PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>, - >, + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, ) -> ! { runner.run().await } @@ -59,15 +55,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); let mut pio = Pio::new(p.PIO0); - let spi = PioSpi::new( - &mut pio.common, - pio.sm0, - pio.irq0, - cs, - p.PIN_24, - p.PIN_29, - p.DMA_CH0, - ); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = singleton!(cyw43::State::new()); let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; diff --git a/examples/rpi-pico-w/src/bin/tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs similarity index 89% rename from examples/rpi-pico-w/src/bin/tcp_server.rs rename to examples/rp/src/bin/wifi_tcp_server.rs index 6a87e7c53..eafa25f68 100644 --- a/examples/rpi-pico-w/src/bin/tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -14,6 +14,7 @@ use embassy_net::{Config, Stack, StackResources}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::Pio; +use embassy_time::Duration; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -28,11 +29,7 @@ macro_rules! singleton { #[embassy_executor::task] async fn wifi_task( - runner: cyw43::Runner< - 'static, - Output<'static, PIN_23>, - PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>, - >, + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, ) -> ! { runner.run().await } @@ -61,15 +58,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); let mut pio = Pio::new(p.PIO0); - let spi = PioSpi::new( - &mut pio.common, - pio.sm0, - pio.irq0, - cs, - p.PIN_24, - p.PIN_29, - p.DMA_CH0, - ); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = singleton!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; @@ -102,10 +91,7 @@ async fn main(spawner: Spawner) { loop { //control.join_open(env!("WIFI_NETWORK")).await; - match control - .join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")) - .await - { + match control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await { Ok(_) => break, Err(err) => { info!("join failed with status={}", err.status); @@ -121,7 +107,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(Duration::from_secs(10))); control.gpio_set(0, false).await; info!("Listening on TCP:1234..."); diff --git a/examples/rpi-pico-w/.cargo/config.toml b/examples/rpi-pico-w/.cargo/config.toml deleted file mode 100644 index f1ed8af96..000000000 --- a/examples/rpi-pico-w/.cargo/config.toml +++ /dev/null @@ -1,8 +0,0 @@ -[target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-rs-cli run --chip RP2040" - -[build] -target = "thumbv6m-none-eabi" - -[env] -DEFMT_LOG = "debug" diff --git a/examples/rpi-pico-w/Cargo.lock b/examples/rpi-pico-w/Cargo.lock deleted file mode 100644 index 16095835b..000000000 --- a/examples/rpi-pico-w/Cargo.lock +++ /dev/null @@ -1,1707 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" -dependencies = [ - "memchr", -] - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "as-slice" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" -dependencies = [ - "generic-array 0.12.4", - "generic-array 0.13.3", - "generic-array 0.14.7", - "stable_deref_trait", -] - -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - -[[package]] -name = "atomic-polyfill" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" -dependencies = [ - "critical-section 1.1.1", -] - -[[package]] -name = "atomic-polyfill" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c314e70d181aa6053b26e3f7fbf86d1dfff84f816a6175b967666b3506ef7289" -dependencies = [ - "critical-section 1.1.1", -] - -[[package]] -name = "atomic-pool" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c5fc22e05ec2884db458bf307dc7b278c9428888d2b6e6fad9c0ae7804f5f6" -dependencies = [ - "as-slice 0.1.5", - "as-slice 0.2.1", - "atomic-polyfill 1.0.2", - "stable_deref_trait", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - -[[package]] -name = "bare-metal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" -dependencies = [ - "rustc_version 0.2.3", -] - -[[package]] -name = "bare-metal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitfield" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "cortex-m" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" -dependencies = [ - "bare-metal 0.2.5", - "bitfield", - "critical-section 1.1.1", - "embedded-hal 0.2.7", - "volatile-register", -] - -[[package]] -name = "cortex-m-rt" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" -dependencies = [ - "cortex-m-rt-macros", -] - -[[package]] -name = "cortex-m-rt-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "crc-any" -version = "2.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774646b687f63643eb0f4bf13dc263cb581c8c9e57973b6ddf78bda3994d88df" -dependencies = [ - "debug-helper", -] - -[[package]] -name = "critical-section" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1706d332edc22aef4d9f23a6bb1c92360a403013c291af51247a737472dcae6" -dependencies = [ - "bare-metal 1.0.0", - "critical-section 1.1.1", -] - -[[package]] -name = "critical-section" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "cyw43" -version = "0.1.0" -dependencies = [ - "atomic-polyfill 0.1.11", - "cortex-m", - "cortex-m-rt", - "defmt", - "embassy-futures", - "embassy-net-driver-channel", - "embassy-sync", - "embassy-time", - "embedded-hal 1.0.0-alpha.10", - "futures", - "num_enum", -] - -[[package]] -name = "cyw43-example-rpi-pico-w" -version = "0.1.0" -dependencies = [ - "atomic-polyfill 0.1.11", - "cortex-m", - "cortex-m-rt", - "cyw43", - "cyw43-pio", - "defmt", - "defmt-rtt", - "embassy-executor", - "embassy-net", - "embassy-rp", - "embassy-time", - "embedded-io", - "futures", - "heapless", - "panic-probe", - "static_cell", -] - -[[package]] -name = "cyw43-pio" -version = "0.1.0" -dependencies = [ - "cyw43", - "defmt", - "embassy-rp", - "fixed", - "pio", - "pio-proc", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "debug-helper" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" - -[[package]] -name = "defmt" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956673bd3cb347512bf988d1e8d89ac9a82b64f6eec54d3c01c3529dac019882" -dependencies = [ - "bitflags", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4abc4821bd84d3d8f49945ddb24d029be9385ed9b77c99bf2f6296847a6a9f0" -dependencies = [ - "defmt-parser", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "defmt-parser" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" -dependencies = [ - "thiserror", -] - -[[package]] -name = "defmt-rtt" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2cbbbd58847d508d97629b32cd9730a2d28532f71e219714614406029f18b1" -dependencies = [ - "critical-section 0.2.8", - "defmt", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "embassy-cortex-m" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "atomic-polyfill 1.0.2", - "cfg-if", - "cortex-m", - "critical-section 1.1.1", - "embassy-executor", - "embassy-hal-common", - "embassy-macros", - "embassy-sync", -] - -[[package]] -name = "embassy-embedded-hal" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "embassy-sync", - "embedded-hal 0.2.7", - "embedded-hal 1.0.0-alpha.10", - "embedded-hal-async", - "embedded-storage", - "embedded-storage-async", - "nb 1.1.0", -] - -[[package]] -name = "embassy-executor" -version = "0.2.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "atomic-polyfill 1.0.2", - "cortex-m", - "critical-section 1.1.1", - "defmt", - "embassy-macros", - "embassy-time", - "futures-util", - "static_cell", -] - -[[package]] -name = "embassy-futures" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" - -[[package]] -name = "embassy-hal-common" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "defmt", - "num-traits", -] - -[[package]] -name = "embassy-macros" -version = "0.2.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "embassy-net" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "as-slice 0.2.1", - "atomic-polyfill 1.0.2", - "atomic-pool", - "defmt", - "embassy-hal-common", - "embassy-net-driver", - "embassy-sync", - "embassy-time", - "embedded-io", - "embedded-nal-async", - "futures", - "generic-array 0.14.7", - "heapless", - "managed", - "smoltcp", - "stable_deref_trait", -] - -[[package]] -name = "embassy-net-driver" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "defmt", -] - -[[package]] -name = "embassy-net-driver-channel" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "embassy-futures", - "embassy-net-driver", - "embassy-sync", -] - -[[package]] -name = "embassy-rp" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "atomic-polyfill 1.0.2", - "cfg-if", - "cortex-m", - "cortex-m-rt", - "critical-section 1.1.1", - "defmt", - "embassy-cortex-m", - "embassy-embedded-hal", - "embassy-executor", - "embassy-futures", - "embassy-hal-common", - "embassy-sync", - "embassy-time", - "embassy-usb-driver", - "embedded-hal 0.2.7", - "embedded-hal 1.0.0-alpha.10", - "embedded-hal-async", - "embedded-hal-nb", - "embedded-io", - "embedded-storage", - "fixed", - "futures", - "nb 1.1.0", - "paste", - "pio", - "pio-proc", - "rand_core", - "rp-pac", - "rp2040-boot2", -] - -[[package]] -name = "embassy-sync" -version = "0.2.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "cfg-if", - "critical-section 1.1.1", - "embedded-io", - "futures-util", - "heapless", -] - -[[package]] -name = "embassy-time" -version = "0.1.1" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "atomic-polyfill 1.0.2", - "cfg-if", - "critical-section 1.1.1", - "defmt", - "embedded-hal 0.2.7", - "futures-util", - "heapless", -] - -[[package]] -name = "embassy-usb-driver" -version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=82f7e104d90a6628d1873017ea5ef6a7afb3b3f7#82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" -dependencies = [ - "defmt", -] - -[[package]] -name = "embedded-hal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" -dependencies = [ - "nb 0.1.3", - "void", -] - -[[package]] -name = "embedded-hal" -version = "1.0.0-alpha.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65c4d073f5d91c66e629b216818a4c9747eeda0debedf2deda9a0a947e4e93b" - -[[package]] -name = "embedded-hal-async" -version = "0.2.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8042370aa7af48de36d5312cda14c18ed8ca6b7ce64f5a07832fedc9dc83063f" -dependencies = [ - "embedded-hal 1.0.0-alpha.10", -] - -[[package]] -name = "embedded-hal-nb" -version = "1.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465fffd56a95bbc105c17965bca1c1d5815027b1cc6bb183bc05d04563d065c" -dependencies = [ - "embedded-hal 1.0.0-alpha.10", - "nb 1.1.0", -] - -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" -dependencies = [ - "defmt", -] - -[[package]] -name = "embedded-nal" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9efecb57ab54fa918730f2874d7d37647169c50fa1357fecb81abee840b113" -dependencies = [ - "heapless", - "nb 1.1.0", - "no-std-net 0.5.0", -] - -[[package]] -name = "embedded-nal-async" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ce84f518ca912777ec143db235f4d615e3bf8d4e46d507d6ef12daf5b1df98" -dependencies = [ - "embedded-io", - "embedded-nal", - "heapless", - "no-std-net 0.6.0", -] - -[[package]] -name = "embedded-storage" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "156d7a2fdd98ebbf9ae579cbceca3058cff946e13f8e17b90e3511db0508c723" - -[[package]] -name = "embedded-storage-async" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052997a894670d0cde873faa7405bc98e2fd29f569d2acd568561bc1c396b35a" -dependencies = [ - "embedded-storage", -] - -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fixed" -version = "1.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79386fdcec5e0fde91b1a6a5bcd89677d1f9304f7f986b154a1b9109038854d9" -dependencies = [ - "az", - "bytemuck", - "half", - "typenum", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-core", - "futures-macro", - "futures-sink", - "futures-task", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "half" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" -dependencies = [ - "crunchy", -] - -[[package]] -name = "hash32" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heapless" -version = "0.7.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" -dependencies = [ - "atomic-polyfill 0.1.11", - "defmt", - "hash32", - "rustc_version 0.4.0", - "spin", - "stable_deref_trait", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "lalrpop" -version = "0.19.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" -dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax 0.6.29", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "lalrpop-util" -version = "0.19.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" -dependencies = [ - "regex", -] - -[[package]] -name = "libc" -version = "0.2.144" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" - -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" - -[[package]] -name = "managed" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "nb" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" -dependencies = [ - "nb 1.1.0", -] - -[[package]] -name = "nb" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "no-std-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bcece43b12349917e096cddfa66107277f123e6c96a5aea78711dc601a47152" - -[[package]] -name = "no-std-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "once_cell" -version = "1.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" - -[[package]] -name = "panic-probe" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa6fa5645ef5a760cd340eaa92af9c1ce131c8c09e7f8926d8a24b59d26652b9" -dependencies = [ - "cortex-m", - "defmt", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "petgraph" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pio" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3" -dependencies = [ - "arrayvec", - "num_enum", - "paste", -] - -[[package]] -name = "pio-parser" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77532c2b8279aef98dfc7207ef15298a5a3d6b6cc76ccc8b65913d69f3a8dd6b" -dependencies = [ - "lalrpop", - "lalrpop-util", - "pio", - "regex-syntax 0.6.29", -] - -[[package]] -name = "pio-proc" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b04dc870fb3a4fd8b3e4ca8c61b53bc8ac4eb78b66805d2b3c2e5c4829e0d7a" -dependencies = [ - "codespan-reporting", - "lalrpop-util", - "pio", - "pio-parser", - "proc-macro-error", - "proc-macro2", - "quote", - "regex-syntax 0.6.29", - "syn 1.0.109", -] - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.2", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" - -[[package]] -name = "rp-pac" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76e426cd8377db668fba1fe885028788b126b7cef91059cd478de8b076c2915" -dependencies = [ - "cortex-m", - "cortex-m-rt", -] - -[[package]] -name = "rp2040-boot2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" -dependencies = [ - "crc-any", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.17", -] - -[[package]] -name = "rustix" -version = "0.37.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "smoltcp" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9786ac45091b96f946693e05bfa4d8ca93e2d3341237d97a380107a6b38dea" -dependencies = [ - "bitflags", - "byteorder", - "cfg-if", - "defmt", - "heapless", - "managed", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_cell" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c37c250d21f53fa7165e76e5401d7e6539c211a8d2cf449e3962956a5cc2ce" -dependencies = [ - "atomic-polyfill 1.0.2", -] - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.18", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-ident" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "vcell" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "volatile-register" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" -dependencies = [ - "vcell", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/examples/rpi-pico-w/Cargo.toml b/examples/rpi-pico-w/Cargo.toml deleted file mode 100644 index d3ce3085e..000000000 --- a/examples/rpi-pico-w/Cargo.toml +++ /dev/null @@ -1,67 +0,0 @@ -[package] -name = "cyw43-example-rpi-pico-w" -version = "0.1.0" -edition = "2021" - - -[dependencies] -cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } -embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } -embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } -embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } -atomic-polyfill = "0.1.5" -static_cell = "1.0" - -defmt = "0.3.4" -defmt-rtt = "0.3" -panic-probe = { version = "0.3", features = ["print-defmt"] } - -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -cortex-m-rt = "0.7.0" -futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } - -embedded-io = { version = "0.4.0", features = ["async", "defmt"] } -heapless = "0.7.15" - - -[patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "82f7e104d90a6628d1873017ea5ef6a7afb3b3f7" } - -[profile.dev] -debug = 2 -debug-assertions = true -opt-level = 1 -overflow-checks = true - -[profile.release] -codegen-units = 1 -debug = 1 -debug-assertions = false -incremental = false -lto = 'fat' -opt-level = 's' -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/examples/rpi-pico-w/build.rs b/examples/rpi-pico-w/build.rs deleted file mode 100644 index 3f915f931..000000000 --- a/examples/rpi-pico-w/build.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! 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=-Tlink-rp.x"); - println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); -} diff --git a/examples/rpi-pico-w/memory.x b/examples/rpi-pico-w/memory.x deleted file mode 100644 index eb8c1731d..000000000 --- a/examples/rpi-pico-w/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY { - BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 - FLASH : ORIGIN = 0x10000100, LENGTH = 1024K - 0x100 - RAM : ORIGIN = 0x20000000, LENGTH = 256K -} \ No newline at end of file From 7f0e778145e7a0f7281d9b37e59473fddf232097 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 May 2023 00:54:20 +0200 Subject: [PATCH 1280/1575] move embassy-net-w5500 to subdir. --- .github/workflows/rust.yml | 29 --- .gitignore | 4 - .vscode/settings.json | 13 -- LICENSE-APACHE | 201 ------------------ LICENSE-MIT | 25 --- ci.sh | 18 -- Cargo.toml => embassy-net-w5500/Cargo.toml | 0 README.md => embassy-net-w5500/README.md | 0 {src => embassy-net-w5500/src}/device.rs | 0 {src => embassy-net-w5500/src}/lib.rs | 0 {src => embassy-net-w5500/src}/socket.rs | 0 {src => embassy-net-w5500/src}/spi.rs | 0 examples/.cargo/config.toml | 8 - examples/Cargo.toml | 70 ------ examples/README.md | 33 --- examples/build.rs | 36 ---- examples/memory.x | 5 - .../src/bin/ethernet_w5500_multisocket.rs} | 4 + .../src/bin/ethernet_w5500_tcp_client.rs} | 4 + .../src/bin/ethernet_w5500_tcp_server.rs} | 5 + .../src/bin/ethernet_w5500_udp.rs} | 4 + rust-toolchain.toml | 8 - 22 files changed, 17 insertions(+), 450 deletions(-) delete mode 100644 .github/workflows/rust.yml delete mode 100644 .gitignore delete mode 100644 .vscode/settings.json delete mode 100644 LICENSE-APACHE delete mode 100644 LICENSE-MIT delete mode 100755 ci.sh rename Cargo.toml => embassy-net-w5500/Cargo.toml (100%) rename README.md => embassy-net-w5500/README.md (100%) rename {src => embassy-net-w5500/src}/device.rs (100%) rename {src => embassy-net-w5500/src}/lib.rs (100%) rename {src => embassy-net-w5500/src}/socket.rs (100%) rename {src => embassy-net-w5500/src}/spi.rs (100%) delete mode 100644 examples/.cargo/config.toml delete mode 100644 examples/Cargo.toml delete mode 100644 examples/README.md delete mode 100644 examples/build.rs delete mode 100644 examples/memory.x rename examples/{src/bin/multisocket.rs => rp/src/bin/ethernet_w5500_multisocket.rs} (94%) rename examples/{src/bin/tcp-client.rs => rp/src/bin/ethernet_w5500_tcp_client.rs} (93%) rename examples/{src/bin/tcp-server.rs => rp/src/bin/ethernet_w5500_tcp_server.rs} (93%) rename examples/{src/bin/udp.rs => rp/src/bin/ethernet_w5500_udp.rs} (94%) delete mode 100644 rust-toolchain.toml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml deleted file mode 100644 index dfa96dd0e..000000000 --- a/.github/workflows/rust.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Rust - -on: - push: - branches: [main] - pull_request: - branches: [main] - merge_group: - -env: - CARGO_TERM_COLOR: always - -jobs: - build-nightly: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Check fmt - run: cargo fmt -- --check - - name: Build - run: ./ci.sh diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a2ac3d82e..000000000 --- a/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.iml -**/target -**/*.rs.bk -Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 231c407ad..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "editor.formatOnSave": true, - "[toml]": { - "editor.formatOnSave": false - }, - "rust-analyzer.check.allTargets": false, - "rust-analyzer.check.noDefaultFeatures": true, - "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.cargo.target": "thumbv6m-none-eabi", - "rust-analyzer.linkedProjects": [ - ".\\examples\\Cargo.toml" - ] -} \ No newline at end of file diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index ea4fa15c9..000000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright 2019-2022 Embassy project contributors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index 87c052836..000000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019-2022 Embassy project contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/ci.sh b/ci.sh deleted file mode 100755 index 0a2876987..000000000 --- a/ci.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -euxo pipefail - -export DEFMT_LOG=trace - -# build examples -#================== - -(cd examples; cargo build --bin multisocket --release) -(cd examples; cargo build --bin tcp-client --release) -(cd examples; cargo build --bin tcp-server --release) -(cd examples; cargo build --bin udp --release) - -# build lib -#============ - -cargo build --target thumbv6m-none-eabi diff --git a/Cargo.toml b/embassy-net-w5500/Cargo.toml similarity index 100% rename from Cargo.toml rename to embassy-net-w5500/Cargo.toml diff --git a/README.md b/embassy-net-w5500/README.md similarity index 100% rename from README.md rename to embassy-net-w5500/README.md diff --git a/src/device.rs b/embassy-net-w5500/src/device.rs similarity index 100% rename from src/device.rs rename to embassy-net-w5500/src/device.rs diff --git a/src/lib.rs b/embassy-net-w5500/src/lib.rs similarity index 100% rename from src/lib.rs rename to embassy-net-w5500/src/lib.rs diff --git a/src/socket.rs b/embassy-net-w5500/src/socket.rs similarity index 100% rename from src/socket.rs rename to embassy-net-w5500/src/socket.rs diff --git a/src/spi.rs b/embassy-net-w5500/src/spi.rs similarity index 100% rename from src/spi.rs rename to embassy-net-w5500/src/spi.rs diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml deleted file mode 100644 index e6b6b4a41..000000000 --- a/examples/.cargo/config.toml +++ /dev/null @@ -1,8 +0,0 @@ -[target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-rs-cli run --chip RP2040" - -[build] -target = "thumbv6m-none-eabi" - -[env] -DEFMT_LOG = "info" diff --git a/examples/Cargo.toml b/examples/Cargo.toml deleted file mode 100644 index 46659c2bc..000000000 --- a/examples/Cargo.toml +++ /dev/null @@ -1,70 +0,0 @@ -[package] -name = "embassy-net-w5500-examples" -version = "0.1.0" -edition = "2021" - -[dependencies] -embassy-executor = { version = "0.2.0", features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] } -embassy-time = { version = "0.1.0", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } -embassy-net = { version = "0.1.0", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet", "unstable-traits", "nightly"] } -embassy-sync = { version = "0.1.0" } -embassy-futures = { version = "0.1.0" } -embassy-net-driver = { version = "0.1.0" } -embassy-net-driver-channel = { version = "0.1.0" } -atomic-polyfill = "0.1.5" -static_cell = "1.0" - -defmt = "=0.3.2" -defmt-rtt = "0.3" -panic-probe = { version = "0.3", features = ["print-defmt"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -cortex-m-rt = "0.7.0" - -embedded-io = { version = "0.4.0", features = ["async", "defmt"] } -heapless = "0.7.15" -embedded-hal = { version = "1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } -rand = { version = "0.8.5", default-features = false } - -embassy-net-w5500 = { path = "../" } - -[patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } - -[profile.dev] -debug = 2 -debug-assertions = true -opt-level = 1 -overflow-checks = true - -[profile.release] -codegen-units = 1 -debug = 1 -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/examples/README.md b/examples/README.md deleted file mode 100644 index d818c4a89..000000000 --- a/examples/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Examples for the rp2040 `WIZnet W5500-EVB-Pico` board - -Examples are written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. - -## Prerequisites -```bash -cargo install probe-rs-cli -``` - -## TCP server example -```bash -cargo run --bin tcp-server --release -``` -This example implements a TCP echo server on port 1234 and using DHCP. -Send it some data, you should see it echoed back and printed in the console. - -## Multi-socket example -```bash -cargo run --bin multisocket --release -``` -This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. - -## TCP client example -```bash -cargo run --bin tcp-client --release -``` -This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. - -## UDP server example -```bash -cargo run --bin udp --release -``` -This example implements a UDP server listening on port 1234 and echoing back the data. diff --git a/examples/build.rs b/examples/build.rs deleted file mode 100644 index 3f915f931..000000000 --- a/examples/build.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! 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=-Tlink-rp.x"); - println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); -} diff --git a/examples/memory.x b/examples/memory.x deleted file mode 100644 index eb8c1731d..000000000 --- a/examples/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY { - BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 - FLASH : ORIGIN = 0x10000100, LENGTH = 1024K - 0x100 - RAM : ORIGIN = 0x20000000, LENGTH = 256K -} \ No newline at end of file diff --git a/examples/src/bin/multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs similarity index 94% rename from examples/src/bin/multisocket.rs rename to examples/rp/src/bin/ethernet_w5500_multisocket.rs index 49bcbdbb0..eb3b8de81 100644 --- a/examples/src/bin/multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -1,3 +1,7 @@ +//! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/src/bin/tcp-client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs similarity index 93% rename from examples/src/bin/tcp-client.rs rename to examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 32dfb6a68..e166e0f35 100644 --- a/examples/src/bin/tcp-client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -1,3 +1,7 @@ +//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/src/bin/tcp-server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs similarity index 93% rename from examples/src/bin/tcp-server.rs rename to examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 04b220146..ffd664d15 100644 --- a/examples/src/bin/tcp-server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -1,3 +1,8 @@ +//! This example implements a TCP echo server on port 1234 and using DHCP. +//! Send it some data, you should see it echoed back and printed in the console. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/src/bin/udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs similarity index 94% rename from examples/src/bin/udp.rs rename to examples/rp/src/bin/ethernet_w5500_udp.rs index 4dc5e1f2f..08ffeb244 100644 --- a/examples/src/bin/udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -1,3 +1,7 @@ +//! This example implements a UDP server listening on port 1234 and echoing back the data. +//! +//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 2582e88f5..000000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,8 +0,0 @@ -# Before upgrading check that everything is available on all tier1 targets here: -# https://rust-lang.github.io/rustup-components-history -[toolchain] -channel = "nightly-2023-04-18" -components = [ "rust-src", "rustfmt" ] -targets = [ - "thumbv6m-none-eabi", -] From d70994e4a8ea695f07b777fa99d7db4e5d4a7122 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 May 2023 01:01:30 +0200 Subject: [PATCH 1281/1575] net-w5500: integrate into main repo. --- embassy-net-w5500/Cargo.toml | 12 +---- embassy-net-w5500/src/device.rs | 33 ++++-------- embassy-net-w5500/src/lib.rs | 12 ++--- embassy-net-w5500/src/socket.rs | 53 ++++++------------- embassy-net-w5500/src/spi.rs | 17 ++---- examples/rp/Cargo.toml | 4 +- .../rp/src/bin/ethernet_w5500_multisocket.rs | 25 +++------ .../rp/src/bin/ethernet_w5500_tcp_client.rs | 13 ++--- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 13 ++--- examples/rp/src/bin/ethernet_w5500_udp.rs | 22 ++------ 10 files changed, 55 insertions(+), 149 deletions(-) diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 1921e812d..3f19e3d39 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -10,17 +10,7 @@ edition = "2021" [dependencies] embedded-hal = { version = "1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } -embassy-net-driver-channel = { version = "0.1.0" } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} embassy-time = { version = "0.1.0" } embassy-futures = { version = "0.1.0" } defmt = { version = "0.3", optional = true } - -[patch.crates-io] -embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } -embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" } diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index 8158bc98e..9874df0d6 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,6 +1,7 @@ +use embedded_hal_async::spi::SpiDevice; + use crate::socket; use crate::spi::SpiInterface; -use embedded_hal_async::spi::SpiDevice; pub const MODE: u16 = 0x00; pub const MAC: u16 = 0x09; @@ -27,12 +28,10 @@ impl W5500 { pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { let mut bus = SpiInterface(spi); // Reset device - bus.write_frame(RegisterBlock::Common, MODE, &[0x80]) - .await?; + bus.write_frame(RegisterBlock::Common, MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]) - .await?; + bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt bus.write_frame( RegisterBlock::Socket0, @@ -42,8 +41,7 @@ impl W5500 { .await?; // Set MAC address - bus.write_frame(RegisterBlock::Common, MAC, &mac_addr) - .await?; + bus.write_frame(RegisterBlock::Common, MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16]) @@ -53,8 +51,7 @@ impl W5500 { // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]) - .await?; + bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]).await?; socket::command(&mut bus, socket::Command::Open).await?; Ok(Self { bus }) @@ -70,17 +67,9 @@ impl W5500 { &mut buffer[..rx_size - offset as usize] }; - let read_ptr = socket::get_rx_read_ptr(&mut self.bus) - .await? - .wrapping_add(offset); - self.bus - .read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer) - .await?; - socket::set_rx_read_ptr( - &mut self.bus, - read_ptr.wrapping_add(read_buffer.len() as u16), - ) - .await?; + let read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?.wrapping_add(offset); + self.bus.read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer).await?; + socket::set_rx_read_ptr(&mut self.bus, read_ptr.wrapping_add(read_buffer.len() as u16)).await?; Ok(read_buffer.len()) } @@ -125,9 +114,7 @@ impl W5500 { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; - self.bus - .write_frame(RegisterBlock::TxBuf, write_ptr, frame) - .await?; + self.bus.write_frame(RegisterBlock::TxBuf, write_ptr, frame).await?; socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; socket::command(&mut self.bus, socket::Command::Send).await?; Ok(frame.len()) diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index bf14b05b4..6821373e3 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -4,7 +4,6 @@ mod device; mod socket; mod spi; -use crate::device::W5500; use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; @@ -12,6 +11,8 @@ use embassy_time::{Duration, Timer}; use embedded_hal::digital::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; + +use crate::device::W5500; const MTU: usize = 1514; /// Type alias for the embassy-net driver for W5500 @@ -77,14 +78,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } /// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net). -pub async fn new< - 'a, - const N_RX: usize, - const N_TX: usize, - SPI: SpiDevice, - INT: Wait, - RST: OutputPin, ->( +pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, spi_dev: SPI, diff --git a/embassy-net-w5500/src/socket.rs b/embassy-net-w5500/src/socket.rs index 3f64d04d1..3d65583c1 100644 --- a/embassy-net-w5500/src/socket.rs +++ b/embassy-net-w5500/src/socket.rs @@ -1,6 +1,7 @@ +use embedded_hal_async::spi::SpiDevice; + use crate::device::RegisterBlock; use crate::spi::SpiInterface; -use embedded_hal_async::spi::SpiDevice; pub const MODE: u16 = 0x00; pub const COMMAND: u16 = 0x01; @@ -25,79 +26,55 @@ pub enum Interrupt { Receive = 0b00100_u8, } -pub async fn reset_interrupt( - bus: &mut SpiInterface, - code: Interrupt, -) -> Result<(), SPI::Error> { +pub async fn reset_interrupt(bus: &mut SpiInterface, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; bus.write_frame(RegisterBlock::Socket0, INTR, &data).await } -pub async fn get_tx_write_ptr( - bus: &mut SpiInterface, -) -> Result { +pub async fn get_tx_write_ptr(bus: &mut SpiInterface) -> Result { let mut data = [0u8; 2]; bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data) .await?; Ok(u16::from_be_bytes(data)) } -pub async fn set_tx_write_ptr( - bus: &mut SpiInterface, - ptr: u16, -) -> Result<(), SPI::Error> { +pub async fn set_tx_write_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data) - .await + bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data).await } -pub async fn get_rx_read_ptr( - bus: &mut SpiInterface, -) -> Result { +pub async fn get_rx_read_ptr(bus: &mut SpiInterface) -> Result { let mut data = [0u8; 2]; bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data) .await?; Ok(u16::from_be_bytes(data)) } -pub async fn set_rx_read_ptr( - bus: &mut SpiInterface, - ptr: u16, -) -> Result<(), SPI::Error> { +pub async fn set_rx_read_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data) - .await + bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data).await } -pub async fn command( - bus: &mut SpiInterface, - command: Command, -) -> Result<(), SPI::Error> { +pub async fn command(bus: &mut SpiInterface, command: Command) -> Result<(), SPI::Error> { let data = [command as u8]; - bus.write_frame(RegisterBlock::Socket0, COMMAND, &data) - .await + bus.write_frame(RegisterBlock::Socket0, COMMAND, &data).await } pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { loop { // Wait until two sequential reads are equal let mut res0 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0) - .await?; + bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0).await?; let mut res1 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1) - .await?; + bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1).await?; if res0 == res1 { break Ok(u16::from_be_bytes(res0)); } } } -pub async fn get_tx_free_size( - bus: &mut SpiInterface, -) -> Result { +pub async fn get_tx_free_size(bus: &mut SpiInterface) -> Result { let mut data = [0; 2]; - bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data) - .await?; + bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data).await?; Ok(u16::from_be_bytes(data)) } diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs index 55d311888..6cd52c44d 100644 --- a/embassy-net-w5500/src/spi.rs +++ b/embassy-net-w5500/src/spi.rs @@ -1,17 +1,13 @@ -use crate::device::RegisterBlock; use embedded_hal_async::spi::{Operation, SpiDevice}; +use crate::device::RegisterBlock; + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SpiInterface(pub SPI); impl SpiInterface { - pub async fn read_frame( - &mut self, - block: RegisterBlock, - address: u16, - data: &mut [u8], - ) -> Result<(), SPI::Error> { + pub async fn read_frame(&mut self, block: RegisterBlock, address: u16, data: &mut [u8]) -> Result<(), SPI::Error> { let address_phase = address.to_be_bytes(); let control_phase = [(block as u8) << 3]; let operations = &mut [ @@ -22,12 +18,7 @@ impl SpiInterface { self.0.transaction(operations).await } - pub async fn write_frame( - &mut self, - block: RegisterBlock, - address: u16, - data: &[u8], - ) -> Result<(), SPI::Error> { + pub async fn write_frame(&mut self, block: RegisterBlock, address: u16, data: &[u8]) -> Result<(), SPI::Error> { let address_phase = address.to_be_bytes(); let control_phase = [(block as u8) << 3 | 0b0000_0100]; let data_phase = data; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index f77377a6f..58b701915 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -12,7 +12,8 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } +embassy-net-w5500 = { version = "0.1.0", path = "../../embassy-net-w5500", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } @@ -48,6 +49,7 @@ static_cell = "1.0.0" log = "0.4" pio-proc = "0.2" pio = "0.2.1" +rand = { version = "0.8.5", default-features = false } [profile.release] debug = true diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index eb3b8de81..c8e6d46a6 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -15,6 +15,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::Duration; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -62,14 +63,8 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = singleton!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( - mac_addr, - state, - ExclusiveDevice::new(spi, cs), - w5500_int, - w5500_reset, - ) - .await; + let (device, runner) = + embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed @@ -103,18 +98,14 @@ async fn listen_task(stack: &'static Stack>, id: u8, port: u16) let mut buf = [0; 4096]; loop { let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(Duration::from_secs(10))); info!("SOCKET {}: Listening on TCP:{}...", id, port); if let Err(e) = socket.accept(port).await { warn!("accept error: {:?}", e); continue; } - info!( - "SOCKET {}: Received connection from {:?}", - id, - socket.remote_endpoint() - ); + info!("SOCKET {}: Received connection from {:?}", id, socket.remote_endpoint()); loop { let n = match socket.read(&mut buf).await { @@ -128,11 +119,7 @@ async fn listen_task(stack: &'static Stack>, id: u8, port: u16) break; } }; - info!( - "SOCKET {}: rxd {}", - id, - core::str::from_utf8(&buf[..n]).unwrap() - ); + info!("SOCKET {}: rxd {}", id, core::str::from_utf8(&buf[..n]).unwrap()); if let Err(e) = socket.write_all(&buf[..n]).await { warn!("write error: {:?}", e); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index e166e0f35..9a7c3ad19 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -7,6 +7,7 @@ #![feature(type_alias_impl_trait)] use core::str::FromStr; + use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; @@ -65,14 +66,8 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = singleton!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( - mac_addr, - state, - ExclusiveDevice::new(spi, cs), - w5500_int, - w5500_reset, - ) - .await; + let (device, runner) = + embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed @@ -98,7 +93,7 @@ async fn main(spawner: Spawner) { let mut tx_buffer = [0; 4096]; loop { let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(Duration::from_secs(10))); led.set_low(); info!("Connecting..."); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index ffd664d15..f02543246 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -16,6 +16,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::Duration; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -64,14 +65,8 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = singleton!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( - mac_addr, - state, - ExclusiveDevice::new(spi, cs), - w5500_int, - w5500_reset, - ) - .await; + let (device, runner) = + embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed @@ -98,7 +93,7 @@ async fn main(spawner: Spawner) { let mut buf = [0; 4096]; loop { let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(Duration::from_secs(10))); led.set_low(); info!("Listening on TCP:1234..."); diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 08ffeb244..2c54f711e 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -9,8 +9,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; -use embassy_net::udp::UdpSocket; -use embassy_net::{PacketMetadata, Stack, StackResources}; +use embassy_net::udp::{PacketMetadata, UdpSocket}; +use embassy_net::{Stack, StackResources}; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -62,14 +62,8 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = singleton!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( - mac_addr, - state, - ExclusiveDevice::new(spi, cs), - w5500_int, - w5500_reset, - ) - .await; + let (device, runner) = + embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed @@ -98,13 +92,7 @@ async fn main(spawner: Spawner) { let mut tx_meta = [PacketMetadata::EMPTY; 16]; let mut buf = [0; 4096]; loop { - let mut socket = UdpSocket::new( - stack, - &mut rx_meta, - &mut rx_buffer, - &mut tx_meta, - &mut tx_buffer, - ); + let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); socket.bind(1234).unwrap(); loop { From 98398d31f7d9bbda06fbb97804ba6844298710bc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 May 2023 01:12:08 +0200 Subject: [PATCH 1282/1575] README fixes. --- README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index b65dcbe15..315d247ee 100644 --- a/README.md +++ b/README.md @@ -99,13 +99,6 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer ### Running examples -- Setup git submodules (needed for STM32 examples) - -```bash -git submodule init -git submodule update -``` - - Install `probe-rs-cli` with defmt support. ```bash @@ -123,7 +116,7 @@ cd examples/nrf52840 For example: ```bash -cargo run --bin blinky +cargo run --release --bin blinky ``` ## Developing Embassy with Rust Analyzer based editors From 16bfbd4e99dbc765c93610557a7857a9013ff756 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 30 May 2023 21:14:25 -0500 Subject: [PATCH 1283/1575] stm32/can: add hw test and cleanup --- embassy-stm32/src/can/bxcan.rs | 78 ++++++++++++++++------------------ tests/stm32/Cargo.toml | 8 +++- tests/stm32/src/bin/can.rs | 78 ++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 43 deletions(-) create mode 100644 tests/stm32/src/bin/can.rs diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 8ae1dcb94..08ba783ff 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -39,6 +39,7 @@ pub struct Rx0InterruptHandler { impl interrupt::Handler for Rx0InterruptHandler { unsafe fn on_interrupt() { + // info!("rx0 irq"); Can::::receive_fifo(RxFifo::Fifo0); } } @@ -49,6 +50,7 @@ pub struct Rx1InterruptHandler { impl interrupt::Handler for Rx1InterruptHandler { unsafe fn on_interrupt() { + // info!("rx1 irq"); Can::::receive_fifo(RxFifo::Fifo1); } } @@ -59,6 +61,7 @@ pub struct SceInterruptHandler { impl interrupt::Handler for SceInterruptHandler { unsafe fn on_interrupt() { + // info!("sce irq"); let msr = T::regs().msr(); let msr_val = msr.read(); @@ -87,36 +90,10 @@ pub enum BusError { BusWarning, } -pub enum FrameOrError { - Frame(Frame), - Error(BusError), -} - impl<'d, T: Instance> Can<'d, T> { - /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. - pub fn new( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(peri, rx, tx); - - unsafe { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } - - T::enable(); - T::reset(); - - Self { - can: bxcan::Can::builder(BxcanInstance(peri)).enable(), - } - } - /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. /// You must call [Can::enable_non_blocking] to use the peripheral. - pub fn new_disabled( + pub fn new( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, @@ -136,6 +113,25 @@ impl<'d, T: Instance> Can<'d, T> { T::enable(); T::reset(); + unsafe { + use crate::pac::can::vals::{Errie, Fmpie, Tmeie}; + + T::regs().ier().write(|w| { + // TODO: fix metapac + + w.set_errie(Errie(1)); + w.set_fmpie(0, Fmpie(1)); + w.set_fmpie(1, Fmpie(1)); + w.set_tmeie(Tmeie(1)); + }); + + T::regs().mcr().write(|w| { + // Enable timestamps on rx messages + + w.set_ttcm(true); + }) + } + unsafe { T::TXInterrupt::steal().unpend(); T::TXInterrupt::steal().enable(); @@ -159,12 +155,8 @@ impl<'d, T: Instance> Can<'d, T> { self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); } - pub async fn transmit(&mut self, frame: &Frame) { - let tx_status = self.queue_transmit(frame).await; - self.wait_transission(tx_status.mailbox()).await; - } - - async fn queue_transmit(&mut self, frame: &Frame) -> bxcan::TransmitStatus { + /// Queues the message to be sent but exerts backpressure + pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { poll_fn(|cx| { if let Ok(status) = self.can.transmit(frame) { return Poll::Ready(status); @@ -175,7 +167,7 @@ impl<'d, T: Instance> Can<'d, T> { .await } - async fn wait_transission(&self, mb: bxcan::Mailbox) { + pub async fn flush(&self, mb: bxcan::Mailbox) { poll_fn(|cx| unsafe { if T::regs().tsr().read().tme(mb.index()) { return Poll::Ready(()); @@ -186,12 +178,13 @@ impl<'d, T: Instance> Can<'d, T> { .await; } - pub async fn receive_frame_or_error(&mut self) -> FrameOrError { + /// Returns a tuple of the time the message was received and the message frame + pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { poll_fn(|cx| { - if let Poll::Ready(frame) = T::state().rx_queue.recv().poll_unpin(cx) { - return Poll::Ready(FrameOrError::Frame(frame)); + if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { + return Poll::Ready(Ok((time, frame))); } else if let Some(err) = self.curr_error() { - return Poll::Ready(FrameOrError::Error(err)); + return Poll::Ready(Err(err)); } T::state().err_waker.register(cx.waker()); Poll::Pending @@ -240,6 +233,7 @@ impl<'d, T: Instance> Can<'d, T> { data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); + let time = fifo.rdtr().read().time(); let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); rfr.modify(|v| v.set_rfom(true)); @@ -247,11 +241,11 @@ impl<'d, T: Instance> Can<'d, T> { /* NOTE: consensus was reached that if rx_queue is full, packets should be dropped */ - let _ = state.rx_queue.try_send(frame); + let _ = state.rx_queue.try_send((time, frame)); } } - pub fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option { + pub const fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option { const BS1_MAX: u8 = 16; const BS2_MAX: u8 = 8; const MAX_SAMPLE_POINT_PERMILL: u16 = 900; @@ -316,7 +310,7 @@ impl<'d, T: Instance> Can<'d, T> { // - With rounding to zero let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first let mut bs2 = bs1_bs2_sum - bs1; - assert!(bs1_bs2_sum > bs1); + core::assert!(bs1_bs2_sum > bs1); let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { @@ -379,7 +373,7 @@ pub(crate) mod sealed { pub struct State { pub tx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Channel, + pub rx_queue: Channel, } impl State { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bd8d90abe..70d3eb138 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,7 +7,7 @@ autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono", "not-gpdma"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono", "can", "not-gpdma"] # Nucleo stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo @@ -19,6 +19,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] chrono = ["embassy-stm32/chrono", "dep:chrono"] ble = [] +can = [] not-gpdma = [] [dependencies] @@ -49,6 +50,11 @@ name = "ble" path = "src/bin/ble.rs" required-features = [ "ble",] +[[bin]] +name = "can" +path = "src/bin/can.rs" +required-features = [ "can",] + [[bin]] name = "gpio" path = "src/bin/gpio.rs" diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs new file mode 100644 index 000000000..f39f83e82 --- /dev/null +++ b/tests/stm32/src/bin/can.rs @@ -0,0 +1,78 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// required-features: can + +#[path = "../example_common.rs"] +mod example_common; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::can::bxcan::filter::Mask32; +use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; +use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; +use embassy_stm32::gpio::{Input, Pull}; +use embassy_stm32::peripherals::CAN1; +use example_common::*; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + CAN1_RX0 => Rx0InterruptHandler; + CAN1_RX1 => Rx1InterruptHandler; + CAN1_SCE => SceInterruptHandler; + CAN1_TX => TxInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_stm32::init(config()); + info!("Hello World!"); + + // HW is connected as follows: + // PB13 -> PD0 + // PB12 -> PD1 + + // The next two lines are a workaround for testing without transceiver. + // To synchronise to the bus the RX input needs to see a high level. + // Use `mem::forget()` to release the borrow on the pin but keep the + // pull-up resistor enabled. + let rx_pin = Input::new(&mut p.PD0, Pull::Up); + core::mem::forget(rx_pin); + + let mut can = Can::new(p.CAN1, p.PD0, p.PD1, Irqs); + + info!("Configuring can..."); + + can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + + can.set_bitrate(1_000_000); + can.modify_config() + .set_loopback(true) // Receive own frames + .set_silent(true) + // .set_bit_timing(0x001c0003) + .enable(); + + info!("Can configured"); + + let mut i: u8 = 0; + loop { + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); + + info!("Transmitting frame..."); + can.write(&tx_frame).await; + + info!("Receiving frame..."); + let (time, rx_frame) = can.read().await.unwrap(); + + info!("loopback time {}", time); + info!("loopback frame {=u8}", rx_frame.data().unwrap()[0]); + + i += 1; + if i > 10 { + break; + } + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 046a99aba01051c93d2f6a4d2f6e54ddde4f7fd4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 May 2023 13:56:12 +0200 Subject: [PATCH 1284/1575] Move doc building to new CI. --- .github/ci/doc.sh | 34 +++++++++++++++ .github/workflows/doc.yml | 87 ------------------------------------- embassy-cortex-m/Cargo.toml | 2 - 3 files changed, 34 insertions(+), 89 deletions(-) create mode 100755 .github/ci/doc.sh delete mode 100644 .github/workflows/doc.yml diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh new file mode 100755 index 000000000..eabde742b --- /dev/null +++ b/.github/ci/doc.sh @@ -0,0 +1,34 @@ +#!/bin/bash +## on push branch=main + +set -euo pipefail + +export RUSTUP_HOME=/ci/cache/rustup +export CARGO_HOME=/ci/cache/cargo +export CARGO_TARGET_DIR=/ci/cache/target +export BUILDER_THREADS=6 + +docserver-builder ./embassy-boot/boot crates/embassy-boot/git.zup +docserver-builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup +docserver-builder ./embassy-boot/rp crates/embassy-boot-rp/git.zup +docserver-builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup +docserver-builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup +docserver-builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup +docserver-builder ./embassy-executor crates/embassy-executor/git.zup +docserver-builder ./embassy-futures crates/embassy-futures/git.zup +docserver-builder ./embassy-lora crates/embassy-lora/git.zup +docserver-builder ./embassy-net crates/embassy-net/git.zup +docserver-builder ./embassy-net-driver crates/embassy-net-driver/git.zup +docserver-builder ./embassy-net-driver-channel crates/embassy-net-driver-channel/git.zup +docserver-builder ./embassy-nrf crates/embassy-nrf/git.zup +docserver-builder ./embassy-rp crates/embassy-rp/git.zup +docserver-builder ./embassy-sync crates/embassy-sync/git.zup +docserver-builder ./embassy-time crates/embassy-time/git.zup +docserver-builder ./embassy-usb crates/embassy-usb/git.zup +docserver-builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup +docserver-builder ./embassy-usb-logger crates/embassy-usb-logger/git.zup +#docserver-builder ./embassy-stm32 crates/embassy-stm32/git.zup + +export KUBECONFIG=/ci/secrets/kubeconfig.yml +POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) +kubectl cp crates $POD:/data diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml deleted file mode 100644 index a69a49718..000000000 --- a/.github/workflows/doc.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Docs - -on: - push: - branches: [main] - -env: - BUILDER_THREADS: '1' - -jobs: - doc: - runs-on: ubuntu-latest - - # Since stm32 crates take SO LONG to build, we split them - # into a separate job. This way it doesn't slow down updating - # the rest. - strategy: - matrix: - crates: - #- stm32 # runs out of disk space... - - rest - - # This will ensure at most one doc build job is running at a time - # (for stm32 and non-stm32 independently). - # If another job is already running, the new job will wait. - # If another job is already waiting, it'll be canceled. - # This means some commits will be skipped, but that's fine because - # we only care that the latest gets built. - concurrency: doc-${{ matrix.crates }} - - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Install Rust targets - run: | - rustup target add x86_64-unknown-linux-gnu - rustup target add wasm32-unknown-unknown - rustup target add thumbv6m-none-eabi - rustup target add thumbv7m-none-eabi - rustup target add thumbv7em-none-eabi - rustup target add thumbv7em-none-eabihf - rustup target add thumbv8m.base-none-eabi - rustup target add thumbv8m.main-none-eabi - rustup target add thumbv8m.main-none-eabihf - - - name: Install docserver - run: | - wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.4/builder" - chmod +x /usr/local/bin/builder - - - name: build-stm32 - if: ${{ matrix.crates=='stm32' }} - run: | - mkdir crates - builder ./embassy-stm32 crates/embassy-stm32/git.zup - - - name: build-rest - if: ${{ matrix.crates=='rest' }} - run: | - mkdir crates - builder ./embassy-boot/boot crates/embassy-boot/git.zup - builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup - builder ./embassy-boot/rp crates/embassy-boot-rp/git.zup - builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup - builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup - builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup - builder ./embassy-executor crates/embassy-executor/git.zup - builder ./embassy-futures crates/embassy-futures/git.zup - builder ./embassy-lora crates/embassy-lora/git.zup - builder ./embassy-net crates/embassy-net/git.zup - builder ./embassy-net-driver crates/embassy-net-driver/git.zup - builder ./embassy-net-driver-channel crates/embassy-net-driver-channel/git.zup - builder ./embassy-nrf crates/embassy-nrf/git.zup - builder ./embassy-rp crates/embassy-rp/git.zup - builder ./embassy-sync crates/embassy-sync/git.zup - builder ./embassy-time crates/embassy-time/git.zup - builder ./embassy-usb crates/embassy-usb/git.zup - builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup - builder ./embassy-usb-logger crates/embassy-usb-logger/git.zup - - - name: upload - run: | - mkdir -p ~/.kube - echo "${{secrets.KUBECONFIG}}" > ~/.kube/config - POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) - kubectl cp crates $POD:/data diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml index 2eb0cce2a..70adda7df 100644 --- a/embassy-cortex-m/Cargo.toml +++ b/embassy-cortex-m/Cargo.toml @@ -13,8 +13,6 @@ flavors = [ { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] }, { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] }, - { name = "thumbv8m.base-none-eabi", target = "thumbv8m.base-none-eabi", features = [] }, - { name = "thumbv8m.main-none-eabi", target = "thumbv8m.main-none-eabi", features = [] }, { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, ] From 7fcded57058cbd119e6527308be750949337b0f5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 May 2023 20:03:39 +0200 Subject: [PATCH 1285/1575] stm32/rtc: fix build failure in some L4s --- ci.sh | 1 + embassy-stm32/src/rtc/v3.rs | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ci.sh b/ci.sh index 8a3669f07..3d6e28796 100755 --- a/ci.sh +++ b/ci.sh @@ -71,6 +71,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 546fe88c7..24f6496a6 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -9,11 +9,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { // Unlock the backup domain unsafe { - #[cfg(any(rtc_v3u5, rcc_g0, rcc_g4))] - use crate::pac::rcc::vals::Rtcsel; - #[cfg(not(any(rtc_v3u5, rcc_g0, rcc_g4, rcc_wl5, rcc_wle)))] - use crate::pac::rtc::vals::Rtcsel; - #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] { crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); @@ -32,7 +27,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let config_rtcsel = rtc_config.clock_config as u8; #[cfg(not(any(rcc_wl5, rcc_wle)))] - let config_rtcsel = Rtcsel(config_rtcsel); + let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel); if !reg.rtcen() || reg.rtcsel() != config_rtcsel { crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); From 25f367432d6065bbcdf25cba80f2734237c432e4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 31 May 2023 20:04:17 +0200 Subject: [PATCH 1286/1575] ci: build stm32 docs. --- .github/ci/doc.sh | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index eabde742b..7f7dfaa03 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -7,27 +7,28 @@ export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target export BUILDER_THREADS=6 +export BUILDER_COMPRESS=true -docserver-builder ./embassy-boot/boot crates/embassy-boot/git.zup -docserver-builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup -docserver-builder ./embassy-boot/rp crates/embassy-boot-rp/git.zup -docserver-builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup -docserver-builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup -docserver-builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup -docserver-builder ./embassy-executor crates/embassy-executor/git.zup -docserver-builder ./embassy-futures crates/embassy-futures/git.zup -docserver-builder ./embassy-lora crates/embassy-lora/git.zup -docserver-builder ./embassy-net crates/embassy-net/git.zup -docserver-builder ./embassy-net-driver crates/embassy-net-driver/git.zup -docserver-builder ./embassy-net-driver-channel crates/embassy-net-driver-channel/git.zup -docserver-builder ./embassy-nrf crates/embassy-nrf/git.zup -docserver-builder ./embassy-rp crates/embassy-rp/git.zup -docserver-builder ./embassy-sync crates/embassy-sync/git.zup -docserver-builder ./embassy-time crates/embassy-time/git.zup -docserver-builder ./embassy-usb crates/embassy-usb/git.zup -docserver-builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup -docserver-builder ./embassy-usb-logger crates/embassy-usb-logger/git.zup -#docserver-builder ./embassy-stm32 crates/embassy-stm32/git.zup +docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup +docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup +docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup +docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup +docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup +docserver-builder -i ./embassy-cortex-m -o crates/embassy-cortex-m/git.zup +docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup +docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup +docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup +docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup +docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup +docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup +docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup +docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup +docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup +docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup +docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup +docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup +docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup +docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) From d7d66bd74f2ef1bc8903b15df630ddbb8fe97df4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Jun 2023 00:15:37 +0200 Subject: [PATCH 1287/1575] Document w5500, cyw43 --- .github/ci/doc.sh | 3 +++ cyw43-pio/Cargo.toml | 5 +++++ cyw43/Cargo.toml | 6 ++++++ embassy-net-w5500/Cargo.toml | 5 +++++ 4 files changed, 19 insertions(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 7f7dfaa03..736249368 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -29,6 +29,9 @@ docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup +docserver-builder -i ./cyw43 -o crates/cyw43/git.zup +docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup +docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 6e9e784a0..14c07178f 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -15,3 +15,8 @@ pio-proc = "0.2" pio = "0.2.1" fixed = "1.23.1" defmt = { version = "0.3", optional = true } + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-pio-v$VERSION/cyw43-pio/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43-pio/src/" +target = "thumbv6m-none-eabi" diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index c7f8816f5..61caa0272 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -26,3 +26,9 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } num_enum = { version = "0.5.7", default-features = false } + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/" +target = "thumbv6m-none-eabi" +features = ["defmt", "firmware-logs"] \ No newline at end of file diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 3f19e3d39..37d15c7ac 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -14,3 +14,8 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- embassy-time = { version = "0.1.0" } embassy-futures = { version = "0.1.0" } defmt = { version = "0.3", optional = true } + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" +target = "thumbv7em-none-eabi" \ No newline at end of file From 1d8321b821d114b369d5a087a1a7a6600228b032 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Jun 2023 01:32:11 +0200 Subject: [PATCH 1288/1575] Use make_static! from static-cell v1.1 --- embassy-executor/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-sync/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 4 +-- examples/nrf52840/src/bin/usb_ethernet.rs | 32 ++++++++----------- .../nrf52840/src/bin/usb_serial_multitask.rs | 23 ++++--------- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- .../rp/src/bin/ethernet_w5500_multisocket.rs | 17 +++------- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 17 +++------- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 18 +++-------- examples/rp/src/bin/ethernet_w5500_udp.rs | 18 +++-------- .../rp/src/bin/lora_p2p_send_multicore.rs | 2 +- examples/rp/src/bin/multicore.rs | 2 +- examples/rp/src/bin/uart_buffered_split.rs | 15 ++------- examples/rp/src/bin/usb_ethernet.rs | 30 ++++++++--------- examples/rp/src/bin/wifi_ap_tcp_server.rs | 16 +++------- examples/rp/src/bin/wifi_scan.rs | 12 ++----- examples/rp/src/bin/wifi_tcp_server.rs | 16 +++------- examples/std/Cargo.toml | 2 +- examples/std/src/bin/net.rs | 18 ++++------- examples/std/src/bin/net_dns.rs | 18 ++++------- examples/std/src/bin/net_udp.rs | 18 ++++------- examples/std/src/bin/tcp_accept.rs | 18 ++++------- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/src/bin/usb_ethernet.rs | 32 ++++++++----------- examples/stm32f7/Cargo.toml | 2 +- examples/stm32f7/src/bin/eth.rs | 21 +++++------- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h5/src/bin/eth.rs | 21 +++++------- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h7/src/bin/eth.rs | 21 +++++------- examples/stm32h7/src/bin/eth_client.rs | 21 +++++------- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 30 ++++++++--------- 38 files changed, 168 insertions(+), 300 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index ce032479d..1e5494ef8 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -65,7 +65,7 @@ embassy-macros = { version = "0.2.0", path = "../embassy-macros" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" -static_cell = "1.0" +static_cell = "1.1" # arch-cortex-m dependencies cortex-m = { version = "0.7.6", optional = true } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index e032dfdae..5f08c7f33 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -86,4 +86,4 @@ rp2040-boot2 = "0.3" [dev-dependencies] embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } -static_cell = "1.0" +static_cell = "1.1" diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index bc06b92cd..340724eab 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -45,4 +45,4 @@ futures-util = { version = "0.3.17", features = [ "channel" ] } # Enable critical-section implementation for std, for tests critical-section = { version = "1.1", features = ["std"] } -static_cell = "1.0" +static_cell = "1.1" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 0f75b3ab7..6627b7861 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", "static_cell/nightly", "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lora-phy", "lorawan-device", "lorawan"] [dependencies] @@ -26,7 +26,7 @@ lorawan = { version = "0.7.3", default-features = false, features = ["default-cr defmt = "0.3" defmt-rtt = "0.4" -static_cell = "1.0" +static_cell = "1.1" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 786025c43..1065f5b5d 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -16,7 +16,7 @@ use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; use embedded_io::asynch::Write; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -27,15 +27,6 @@ bind_interrupts!(struct Irqs { type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - const MTU: usize = 1514; #[embassy_executor::task] @@ -83,11 +74,11 @@ async fn main(spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 128])[..], - &mut singleton!([0; 128])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 128])[..], + &mut make_static!([0; 128])[..], ); // Our MAC addr. @@ -96,14 +87,14 @@ async fn main(spawner: Spawner) { let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); @@ -120,7 +111,12 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index ac22d9499..cd4392903 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -12,7 +12,7 @@ use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -20,15 +20,6 @@ bind_interrupts!(struct Irqs { POWER_CLOCK => usb::vbus_detect::InterruptHandler; }); -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; #[embassy_executor::task] @@ -73,17 +64,17 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; config.composite_with_iads = true; - let state = singleton!(State::new()); + let state = make_static!(State::new()); // Create embassy-usb DeviceBuilder using the driver and config. let mut builder = Builder::new( driver, config, - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 128])[..], - &mut singleton!([0; 128])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 128])[..], + &mut make_static!([0; 128])[..], ); // Create classes on the builder. diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 40422e7df..efb66bae6 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -42,7 +42,7 @@ embedded-io = { version = "0.4.0", features = [ "async" ]} defmt = "0.3" defmt-rtt = "0.4" -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 58b701915..e946b481d 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -45,7 +45,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = "0.2.0-alpha.1" embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } -static_cell = "1.0.0" +static_cell = { version = "1.1", features = ["nightly"]} log = "0.4" pio-proc = "0.2" pio = "0.2.1" diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index c8e6d46a6..63e142e7d 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -19,18 +19,9 @@ use embassy_time::Duration; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -62,7 +53,7 @@ async fn main(spawner: Spawner) { let w5500_reset = Output::new(p.PIN_20, Level::High); let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; - let state = singleton!(State::<8, 8>::new()); + let state = make_static!(State::<8, 8>::new()); let (device, runner) = embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); @@ -71,10 +62,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new( + let stack = &*make_static!(Stack::new( device, embassy_net::Config::Dhcp(Default::default()), - singleton!(StackResources::<3>::new()), + make_static!(StackResources::<3>::new()), seed )); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 9a7c3ad19..a532de00d 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -21,18 +21,9 @@ use embassy_time::{Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -65,7 +56,7 @@ async fn main(spawner: Spawner) { let w5500_reset = Output::new(p.PIN_20, Level::High); let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; - let state = singleton!(State::<8, 8>::new()); + let state = make_static!(State::<8, 8>::new()); let (device, runner) = embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); @@ -74,10 +65,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new( + let stack = &*make_static!(Stack::new( device, embassy_net::Config::Dhcp(Default::default()), - singleton!(StackResources::<2>::new()), + make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index f02543246..599f6b1e9 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -20,18 +20,8 @@ use embassy_time::Duration; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -64,7 +54,7 @@ async fn main(spawner: Spawner) { let w5500_reset = Output::new(p.PIN_20, Level::High); let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; - let state = singleton!(State::<8, 8>::new()); + let state = make_static!(State::<8, 8>::new()); let (device, runner) = embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); @@ -73,10 +63,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new( + let stack = &*make_static!(Stack::new( device, embassy_net::Config::Dhcp(Default::default()), - singleton!(StackResources::<2>::new()), + make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 2c54f711e..ac5a65bb6 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -18,18 +18,8 @@ use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embedded_hal_async::spi::ExclusiveDevice; use rand::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -61,7 +51,7 @@ async fn main(spawner: Spawner) { let w5500_reset = Output::new(p.PIN_20, Level::High); let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; - let state = singleton!(State::<8, 8>::new()); + let state = make_static!(State::<8, 8>::new()); let (device, runner) = embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; unwrap!(spawner.spawn(ethernet_task(runner))); @@ -70,10 +60,10 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new( + let stack = &*make_static!(Stack::new( device, embassy_net::Config::Dhcp(Default::default()), - singleton!(StackResources::<2>::new()), + make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs index 5585606d8..eef2f7a53 100644 --- a/examples/rp/src/bin/lora_p2p_send_multicore.rs +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -7,7 +7,6 @@ use defmt::*; use embassy_executor::Executor; -use embassy_executor::_export::StaticCell; use embassy_lora::iv::GenericSx126xInterfaceVariant; use embassy_rp::gpio::{AnyPin, Input, Level, Output, Pin, Pull}; use embassy_rp::multicore::{spawn_core1, Stack}; @@ -19,6 +18,7 @@ use embassy_time::{Delay, Duration, Timer}; use lora_phy::mod_params::*; use lora_phy::sx1261_2::SX1261_2; use lora_phy::LoRa; +use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; static mut CORE1_STACK: Stack<4096> = Stack::new(); diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 376b2b61e..57278dd6c 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -4,13 +4,13 @@ use defmt::*; use embassy_executor::Executor; -use embassy_executor::_export::StaticCell; use embassy_rp::gpio::{Level, Output}; use embassy_rp::multicore::{spawn_core1, Stack}; use embassy_rp::peripherals::PIN_25; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; static mut CORE1_STACK: Stack<4096> = Stack::new(); diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index d6f01b4de..9df99bd58 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -4,34 +4,25 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_executor::_export::StaticCell; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::{Read, Write}; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { UART0_IRQ => BufferedInterruptHandler; }); -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let (tx_pin, rx_pin, uart) = (p.PIN_0, p.PIN_1, p.UART0); - let tx_buf = &mut singleton!([0u8; 16])[..]; - let rx_buf = &mut singleton!([0u8; 16])[..]; + let tx_buf = &mut make_static!([0u8; 16])[..]; + let rx_buf = &mut make_static!([0u8; 16])[..]; let uart = BufferedUart::new(uart, Irqs, tx_pin, rx_pin, tx_buf, rx_buf, Config::default()); let (rx, mut tx) = uart.split(); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 38ff1620d..16fbf5e91 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -13,7 +13,7 @@ use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; use embedded_io::asynch::Write; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -22,15 +22,6 @@ bind_interrupts!(struct Irqs { type MyDriver = Driver<'static, peripherals::USB>; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - const MTU: usize = 1514; #[embassy_executor::task] @@ -73,10 +64,10 @@ async fn main(spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 128])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 128])[..], ); // Our MAC addr. @@ -85,14 +76,14 @@ async fn main(spawner: Spawner) { let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); @@ -106,7 +97,12 @@ async fn main(spawner: Spawner) { let seed = 1234; // guaranteed random, chosen by a fair dice roll // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 15264524e..b27d3c9f8 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -16,17 +16,9 @@ use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::Pio; use embassy_time::Duration; use embedded_io::asynch::Write; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} - #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -60,7 +52,7 @@ async fn main(spawner: Spawner) { let mut pio = Pio::new(p.PIO0); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); - let state = singleton!(cyw43::State::new()); + let state = make_static!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); @@ -80,10 +72,10 @@ async fn main(spawner: Spawner) { let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. // Init network stack - let stack = &*singleton!(Stack::new( + let stack = &*make_static!(Stack::new( net_device, config, - singleton!(StackResources::<2>::new()), + make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index aa5e5a399..79534f229 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -13,17 +13,9 @@ use embassy_net::Stack; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::Pio; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} - #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -57,7 +49,7 @@ async fn main(spawner: Spawner) { let mut pio = Pio::new(p.PIO0); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); - let state = singleton!(cyw43::State::new()); + let state = make_static!(cyw43::State::new()); let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index eafa25f68..1a00bca96 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -16,17 +16,9 @@ use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::Pio; use embassy_time::Duration; use embedded_io::asynch::Write; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} - #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -60,7 +52,7 @@ async fn main(spawner: Spawner) { let mut pio = Pio::new(p.PIO0); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); - let state = singleton!(cyw43::State::new()); + let state = make_static!(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); @@ -80,10 +72,10 @@ async fn main(spawner: Spawner) { let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. // Init network stack - let stack = &*singleton!(Stack::new( + let stack = &*make_static!(Stack::new( net_device, config, - singleton!(StackResources::<2>::new()), + make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 36770ca9c..878ad8c5a 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -22,4 +22,4 @@ libc = "0.2.101" clap = { version = "3.0.0-beta.5", features = ["derive"] } rand_core = { version = "0.6.3", features = ["std"] } heapless = { version = "0.7.5", default-features = false } -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index d93616254..b42bfc13b 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -11,21 +11,12 @@ use embedded_io::asynch::Write; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; -use static_cell::StaticCell; +use static_cell::{make_static, StaticCell}; #[path = "../tuntap.rs"] mod tuntap; use crate::tuntap::TunTapDevice; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} - #[derive(Parser)] #[clap(version = "1.0")] struct Opts { @@ -66,7 +57,12 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<3>::new()), + seed + )); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index d1e1f8212..932ac5831 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -9,21 +9,12 @@ use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; -use static_cell::StaticCell; +use static_cell::{make_static, StaticCell}; #[path = "../tuntap.rs"] mod tuntap; use crate::tuntap::TunTapDevice; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} - #[derive(Parser)] #[clap(version = "1.0")] struct Opts { @@ -65,7 +56,12 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); + let stack: &Stack<_> = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<3>::new()), + seed + )); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 4df23edf6..d89ec7643 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -7,21 +7,12 @@ use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; -use static_cell::StaticCell; +use static_cell::{make_static, StaticCell}; #[path = "../tuntap.rs"] mod tuntap; use crate::tuntap::TunTapDevice; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} - #[derive(Parser)] #[clap(version = "1.0")] struct Opts { @@ -62,7 +53,12 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<3>::new()), + seed + )); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index 97ce77f42..d24e218dc 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -12,21 +12,12 @@ use embedded_io::asynch::Write as _; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; -use static_cell::StaticCell; +use static_cell::{make_static, StaticCell}; #[path = "../tuntap.rs"] mod tuntap; use crate::tuntap::TunTapDevice; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init_with(move || $val) - }}; -} - #[derive(Parser)] #[clap(version = "1.0")] struct Opts { @@ -77,7 +68,12 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<3>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<3>::new()), + seed + )); // Launch network task spawner.spawn(net_task(stack)).unwrap(); diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 9c59c45c6..ff134bb0e 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -16,4 +16,4 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 3d314e6c5..0fe9cb122 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -23,4 +23,4 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" embedded-storage = "0.3.0" -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index f5f8b632d..3a8efdd06 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -25,7 +25,7 @@ heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" embedded-storage = "0.3.0" micromath = "2.0.0" -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} chrono = { version = "^0.4", default-features = false} [profile.release] diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index c4e395f0f..d229cc3ef 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -14,20 +14,11 @@ use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; use embedded_io::asynch::Write; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; type UsbDriver = Driver<'static, embassy_stm32::peripherals::USB_OTG_FS>; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - const MTU: usize = 1514; #[embassy_executor::task] @@ -60,7 +51,7 @@ async fn main(spawner: Spawner) { let p = embassy_stm32::init(config); // Create the driver, from the HAL. - let ep_out_buffer = &mut singleton!([0; 256])[..]; + let ep_out_buffer = &mut make_static!([0; 256])[..]; let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer); // Create embassy-usb Config @@ -81,10 +72,10 @@ async fn main(spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 128])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 128])[..], ); // Our MAC addr. @@ -93,14 +84,14 @@ async fn main(spawner: Spawner) { let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); @@ -117,7 +108,12 @@ async fn main(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); unwrap!(spawner.spawn(net_task(stack))); diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 6ddb7186e..7a650067c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -26,4 +26,4 @@ nb = "1.0.0" rand_core = "0.6.3" critical-section = "1.1" embedded-storage = "0.3.0" -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 6d286c368..d8438241c 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -15,18 +15,8 @@ use embassy_stm32::{bind_interrupts, eth, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; }); @@ -55,7 +45,7 @@ async fn main(spawner: Spawner) -> ! { let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( - singleton!(PacketQueue::<16, 16>::new()), + make_static!(PacketQueue::<16, 16>::new()), p.ETH, Irqs, p.PA1, @@ -80,7 +70,12 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index b9204fba8..d49a0dde7 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -30,7 +30,7 @@ critical-section = "1.1" micromath = "2.0.0" stm32-fmc = "0.2.4" embedded-storage = "0.3.0" -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} # cargo build/run [profile.dev] diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index fa1f225fe..5d1eadf4b 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -16,18 +16,8 @@ use embassy_stm32::{bind_interrupts, eth, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; }); @@ -74,7 +64,7 @@ async fn main(spawner: Spawner) -> ! { let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( - singleton!(PacketQueue::<4, 4>::new()), + make_static!(PacketQueue::<4, 4>::new()), p.ETH, Irqs, p.PA1, @@ -99,7 +89,12 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 8b534ca05..08b57f988 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -30,7 +30,7 @@ critical-section = "1.1" micromath = "2.0.0" stm32-fmc = "0.2.4" embedded-storage = "0.3.0" -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} # cargo build/run [profile.dev] diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index dbfc90cf4..3aa7b2271 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -15,18 +15,8 @@ use embassy_stm32::{bind_interrupts, eth, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; }); @@ -56,7 +46,7 @@ async fn main(spawner: Spawner) -> ! { let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( - singleton!(PacketQueue::<16, 16>::new()), + make_static!(PacketQueue::<16, 16>::new()), p.ETH, Irqs, p.PA1, @@ -81,7 +71,12 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 14e6b7914..575c716b6 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -16,18 +16,8 @@ use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; use rand_core::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; - -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; }); @@ -57,7 +47,7 @@ async fn main(spawner: Spawner) -> ! { let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; let device = Ethernet::new( - singleton!(PacketQueue::<16, 16>::new()), + make_static!(PacketQueue::<16, 16>::new()), p.ETH, Irqs, p.PA1, @@ -82,7 +72,12 @@ async fn main(spawner: Spawner) -> ! { //}); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); // Launch network task unwrap!(spawner.spawn(net_task(&stack))); diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index ffb6cdb43..235f1b0b3 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -31,4 +31,4 @@ 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 } embedded-hal = "0.2.6" -static_cell = "1.0" +static_cell = "1.1" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index acb48c765..2ac9c180d 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -25,4 +25,4 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } embedded-io = { version = "0.4.0", features = ["async"] } -static_cell = "1.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index b84e53d3a..6163e0709 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -15,20 +15,11 @@ use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; use embedded_io::asynch::Write; use rand_core::RngCore; -use static_cell::StaticCell; +use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; type MyDriver = Driver<'static, embassy_stm32::peripherals::USB>; -macro_rules! singleton { - ($val:expr) => {{ - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - let (x,) = STATIC_CELL.init(($val,)); - x - }}; -} - const MTU: usize = 1514; bind_interrupts!(struct Irqs { @@ -78,10 +69,10 @@ async fn main(spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 256])[..], - &mut singleton!([0; 128])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 256])[..], + &mut make_static!([0; 128])[..], ); // Our MAC addr. @@ -90,14 +81,14 @@ async fn main(spawner: Spawner) { let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; // Create classes on the builder. - let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); + let class = CdcNcmClass::new(&mut builder, make_static!(State::new()), host_mac_addr, 64); // Build the builder. let usb = builder.build(); unwrap!(spawner.spawn(usb_task(usb))); - let (runner, device) = class.into_embassy_net_device::(singleton!(NetState::new()), our_mac_addr); + let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); @@ -112,7 +103,12 @@ async fn main(spawner: Spawner) { let seed = rng.next_u64(); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); unwrap!(spawner.spawn(net_task(stack))); From 2a435e53b761182dabf9496963052f8323125f3a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Jun 2023 02:22:31 +0200 Subject: [PATCH 1289/1575] cortex-m: remove PeripheralMutex. --- embassy-cortex-m/src/lib.rs | 1 - embassy-cortex-m/src/peripheral.rs | 144 ----------------------------- 2 files changed, 145 deletions(-) delete mode 100644 embassy-cortex-m/src/peripheral.rs diff --git a/embassy-cortex-m/src/lib.rs b/embassy-cortex-m/src/lib.rs index e4b713a06..7bc16d3ba 100644 --- a/embassy-cortex-m/src/lib.rs +++ b/embassy-cortex-m/src/lib.rs @@ -7,4 +7,3 @@ pub(crate) mod fmt; pub use embassy_executor as executor; pub mod interrupt; -pub mod peripheral; diff --git a/embassy-cortex-m/src/peripheral.rs b/embassy-cortex-m/src/peripheral.rs deleted file mode 100644 index e2f295579..000000000 --- a/embassy-cortex-m/src/peripheral.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Peripheral interrupt handling specific to cortex-m devices. -use core::mem::MaybeUninit; - -use cortex_m::peripheral::scb::VectActive; -use cortex_m::peripheral::{NVIC, SCB}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; - -use crate::interrupt::{Interrupt, InterruptExt, Priority}; - -/// A type which can be used as state with `PeripheralMutex`. -/// -/// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt, -/// and `&mut T` is only `Send` where `T: Send`. -pub trait PeripheralState: Send { - /// The interrupt that is used for this peripheral. - type Interrupt: Interrupt; - - /// The interrupt handler that should be invoked for the peripheral. Implementations need to clear the appropriate interrupt flags to ensure the handle will not be called again. - fn on_interrupt(&mut self); -} - -/// A type for storing the state of a peripheral that can be stored in a static. -pub struct StateStorage(MaybeUninit); - -impl StateStorage { - /// Create a new instance for storing peripheral state. - pub const fn new() -> Self { - Self(MaybeUninit::uninit()) - } -} - -/// A type for a peripheral that keeps the state of a peripheral that can be accessed from thread mode and an interrupt handler in -/// a safe way. -pub struct PeripheralMutex<'a, S: PeripheralState> { - state: *mut S, - irq: PeripheralRef<'a, S::Interrupt>, -} - -/// Whether `irq` can be preempted by the current interrupt. -pub(crate) fn can_be_preempted(irq: &impl Interrupt) -> bool { - match SCB::vect_active() { - // Thread mode can't preempt anything. - VectActive::ThreadMode => false, - // Exceptions don't always preempt interrupts, - // but there isn't much of a good reason to be keeping a `PeripheralMutex` in an exception anyway. - VectActive::Exception(_) => true, - VectActive::Interrupt { irqn } => { - #[derive(Clone, Copy)] - struct NrWrap(u16); - unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { - fn number(self) -> u16 { - self.0 - } - } - NVIC::get_priority(NrWrap(irqn.into())) < irq.get_priority().into() - } - } -} - -impl<'a, S: PeripheralState> PeripheralMutex<'a, S> { - /// Create a new `PeripheralMutex` wrapping `irq`, with `init` initializing the initial state. - /// - /// Registers `on_interrupt` as the `irq`'s handler, and enables it. - pub fn new( - irq: impl Peripheral

+ 'a, - storage: &'a mut StateStorage, - init: impl FnOnce() -> S, - ) -> Self { - into_ref!(irq); - - if can_be_preempted(&*irq) { - panic!( - "`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps" - ); - } - - let state_ptr = storage.0.as_mut_ptr(); - - // Safety: The pointer is valid and not used by anyone else - // because we have the `&mut StateStorage`. - unsafe { state_ptr.write(init()) }; - - irq.disable(); - irq.set_handler(|p| unsafe { - // Safety: it's OK to get a &mut to the state, since - // - We checked that the thread owning the `PeripheralMutex` can't preempt us in `new`. - // Interrupts' priorities can only be changed with raw embassy `Interrupts`, - // which can't safely store a `PeripheralMutex` across invocations. - // - We can't have preempted a with() call because the irq is disabled during it. - let state = &mut *(p as *mut S); - state.on_interrupt(); - }); - irq.set_handler_context(state_ptr as *mut ()); - irq.enable(); - - Self { irq, state: state_ptr } - } - - /// Access the peripheral state ensuring interrupts are disabled so that the state can be - /// safely accessed. - pub fn with(&mut self, f: impl FnOnce(&mut S) -> R) -> R { - self.irq.disable(); - - // Safety: it's OK to get a &mut to the state, since the irq is disabled. - let state = unsafe { &mut *self.state }; - let r = f(state); - - self.irq.enable(); - - r - } - - /// Returns whether the wrapped interrupt is currently in a pending state. - pub fn is_pending(&self) -> bool { - self.irq.is_pending() - } - - /// Forces the wrapped interrupt into a pending state. - pub fn pend(&self) { - self.irq.pend() - } - - /// Forces the wrapped interrupt out of a pending state. - pub fn unpend(&self) { - self.irq.unpend() - } - - /// Gets the priority of the wrapped interrupt. - pub fn priority(&self) -> Priority { - self.irq.get_priority() - } -} - -impl<'a, S: PeripheralState> Drop for PeripheralMutex<'a, S> { - fn drop(&mut self) { - self.irq.disable(); - self.irq.remove_handler(); - - // safety: - // - we initialized the state in `new`, so we know it's initialized. - // - the irq is disabled, so it won't preempt us while dropping. - unsafe { self.state.drop_in_place() } - } -} From 404aa292890503806a32eac5ae518dbeeadd60eb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 1 Jun 2023 02:22:46 +0200 Subject: [PATCH 1290/1575] cortex-m: remove owned interrupts. --- embassy-cortex-m/src/interrupt.rs | 180 +++++------------- embassy-lora/src/iv.rs | 6 +- embassy-macros/src/lib.rs | 11 -- .../src/macros/cortex_m_interrupt_declare.rs | 21 +- .../src/macros/cortex_m_interrupt_take.rs | 57 ------ embassy-macros/src/macros/mod.rs | 1 - embassy-nrf/src/buffered_uarte.rs | 8 +- embassy-nrf/src/gpiote.rs | 56 +++--- embassy-nrf/src/i2s.rs | 5 +- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/pdm.rs | 6 +- embassy-nrf/src/qdec.rs | 7 +- embassy-nrf/src/qspi.rs | 6 +- embassy-nrf/src/rng.rs | 7 +- embassy-nrf/src/saadc.rs | 6 +- embassy-nrf/src/spim.rs | 6 +- embassy-nrf/src/spis.rs | 6 +- embassy-nrf/src/temp.rs | 7 +- embassy-nrf/src/time_driver.rs | 7 +- embassy-nrf/src/twim.rs | 6 +- embassy-nrf/src/twis.rs | 6 +- embassy-nrf/src/uarte.rs | 14 +- embassy-nrf/src/usb/mod.rs | 6 +- embassy-nrf/src/usb/vbus_detect.rs | 6 +- embassy-rp/src/adc.rs | 6 +- embassy-rp/src/dma.rs | 9 +- embassy-rp/src/gpio.rs | 9 +- embassy-rp/src/i2c.rs | 14 +- embassy-rp/src/interrupt.rs | 2 +- embassy-rp/src/multicore.rs | 5 +- embassy-rp/src/pio.rs | 16 +- embassy-rp/src/timer.rs | 10 +- embassy-rp/src/uart/buffered.rs | 14 +- embassy-rp/src/uart/mod.rs | 14 +- embassy-rp/src/usb.rs | 8 +- embassy-stm32/src/dcmi.rs | 6 +- embassy-stm32/src/dma/bdma.rs | 7 +- embassy-stm32/src/dma/dma.rs | 7 +- embassy-stm32/src/dma/gpdma.rs | 7 +- embassy-stm32/src/eth/v1/mod.rs | 6 +- embassy-stm32/src/eth/v2/mod.rs | 6 +- embassy-stm32/src/exti.rs | 4 +- embassy-stm32/src/flash/asynch.rs | 7 +- embassy-stm32/src/i2c/v2.rs | 6 +- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 8 +- embassy-stm32/src/time_driver.rs | 7 +- embassy-stm32/src/tl_mbox/mod.rs | 10 +- embassy-stm32/src/usart/buffered.rs | 18 +- embassy-stm32/src/usart/mod.rs | 10 +- embassy-stm32/src/usb/usb.rs | 6 +- embassy-stm32/src/usb_otg/usb.rs | 8 +- examples/boot/bootloader/rp/Cargo.toml | 1 + examples/std/src/bin/tcp_accept.rs | 2 +- 54 files changed, 239 insertions(+), 439 deletions(-) delete mode 100644 embassy-macros/src/macros/cortex_m_interrupt_take.rs diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index 3a82726df..0e790eaaf 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -1,10 +1,8 @@ //! Interrupt handling for cortex-m devices. -use core::{mem, ptr}; +use core::mem; +use core::sync::atomic::{compiler_fence, Ordering}; -use atomic_polyfill::{compiler_fence, AtomicPtr, Ordering}; use cortex_m::peripheral::NVIC; -use embassy_hal_common::Peripheral; -pub use embassy_macros::cortex_m_interrupt_take as take; /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. #[doc(hidden)] @@ -43,22 +41,6 @@ pub trait Handler { /// This allows drivers to check bindings at compile-time. pub unsafe trait Binding> {} -/// Implementation detail, do not use outside embassy crates. -#[doc(hidden)] -pub struct DynHandler { - pub func: AtomicPtr<()>, - pub ctx: AtomicPtr<()>, -} - -impl DynHandler { - pub const fn new() -> Self { - Self { - func: AtomicPtr::new(ptr::null_mut()), - ctx: AtomicPtr::new(ptr::null_mut()), - } - } -} - #[derive(Clone, Copy)] pub(crate) struct NrWrap(pub(crate) u16); unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { @@ -69,144 +51,68 @@ unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { /// Represents an interrupt type that can be configured by embassy to handle /// interrupts. -pub unsafe trait Interrupt: Peripheral

{ +pub unsafe trait Interrupt { /// Return the NVIC interrupt number for this interrupt. - fn number(&self) -> u16; - /// Steal an instance of this interrupt - /// - /// # Safety - /// - /// This may panic if the interrupt has already been stolen and configured. - unsafe fn steal() -> Self; + fn number() -> u16; - /// Implementation detail, do not use outside embassy crates. - #[doc(hidden)] - unsafe fn __handler(&self) -> &'static DynHandler; -} - -/// Represents additional behavior for all interrupts. -pub trait InterruptExt: Interrupt { - /// Configure the interrupt handler for this interrupt. - /// - /// # Safety - /// - /// It is the responsibility of the caller to ensure the handler - /// points to a valid handler as long as interrupts are enabled. - fn set_handler(&self, func: unsafe fn(*mut ())); - - /// Remove the interrupt handler for this interrupt. - fn remove_handler(&self); - - /// Set point to a context that is passed to the interrupt handler when - /// an interrupt is pending. - /// - /// # Safety - /// - /// It is the responsibility of the caller to ensure the context - /// points to a valid handler as long as interrupts are enabled. - fn set_handler_context(&self, ctx: *mut ()); - - /// Enable the interrupt. Once enabled, the interrupt handler may - /// be called "any time". - fn enable(&self); + /// Enable the interrupt. + #[inline] + unsafe fn enable() { + compiler_fence(Ordering::SeqCst); + NVIC::unmask(NrWrap(Self::number())) + } /// Disable the interrupt. - fn disable(&self); + #[inline] + fn disable() { + NVIC::mask(NrWrap(Self::number())); + compiler_fence(Ordering::SeqCst); + } /// Check if interrupt is being handled. + #[inline] #[cfg(not(armv6m))] - fn is_active(&self) -> bool; + fn is_active() -> bool { + NVIC::is_active(NrWrap(Self::number())) + } /// Check if interrupt is enabled. - fn is_enabled(&self) -> bool; + #[inline] + fn is_enabled() -> bool { + NVIC::is_enabled(NrWrap(Self::number())) + } /// Check if interrupt is pending. - fn is_pending(&self) -> bool; + #[inline] + fn is_pending() -> bool { + NVIC::is_pending(NrWrap(Self::number())) + } /// Set interrupt pending. - fn pend(&self); + #[inline] + fn pend() { + NVIC::pend(NrWrap(Self::number())) + } /// Unset interrupt pending. - fn unpend(&self); + #[inline] + fn unpend() { + NVIC::unpend(NrWrap(Self::number())) + } /// Get the priority of the interrupt. - fn get_priority(&self) -> Priority; + #[inline] + fn get_priority() -> Priority { + Priority::from(NVIC::get_priority(NrWrap(Self::number()))) + } /// Set the interrupt priority. - fn set_priority(&self, prio: Priority); -} - -impl InterruptExt for T { - fn set_handler(&self, func: unsafe fn(*mut ())) { - compiler_fence(Ordering::SeqCst); - let handler = unsafe { self.__handler() }; - handler.func.store(func as *mut (), Ordering::Relaxed); - compiler_fence(Ordering::SeqCst); - } - - fn remove_handler(&self) { - compiler_fence(Ordering::SeqCst); - let handler = unsafe { self.__handler() }; - handler.func.store(ptr::null_mut(), Ordering::Relaxed); - compiler_fence(Ordering::SeqCst); - } - - fn set_handler_context(&self, ctx: *mut ()) { - let handler = unsafe { self.__handler() }; - handler.ctx.store(ctx, Ordering::Relaxed); - } - #[inline] - fn enable(&self) { - compiler_fence(Ordering::SeqCst); - unsafe { - NVIC::unmask(NrWrap(self.number())); - } - } - - #[inline] - fn disable(&self) { - NVIC::mask(NrWrap(self.number())); - compiler_fence(Ordering::SeqCst); - } - - #[inline] - #[cfg(not(armv6m))] - fn is_active(&self) -> bool { - NVIC::is_active(NrWrap(self.number())) - } - - #[inline] - fn is_enabled(&self) -> bool { - NVIC::is_enabled(NrWrap(self.number())) - } - - #[inline] - fn is_pending(&self) -> bool { - NVIC::is_pending(NrWrap(self.number())) - } - - #[inline] - fn pend(&self) { - NVIC::pend(NrWrap(self.number())) - } - - #[inline] - fn unpend(&self) { - NVIC::unpend(NrWrap(self.number())) - } - - #[inline] - fn get_priority(&self) -> Priority { - Priority::from(NVIC::get_priority(NrWrap(self.number()))) - } - - #[inline] - fn set_priority(&self, prio: Priority) { - unsafe { + fn set_priority(prio: Priority) { + critical_section::with(|_| unsafe { let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); - nvic.set_priority(NrWrap(self.number()), prio.into()) - } + nvic.set_priority(NrWrap(Self::number()), prio.into()) + }) } } diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs index d515bc365..8d521040f 100644 --- a/embassy-lora/src/iv.rs +++ b/embassy-lora/src/iv.rs @@ -22,7 +22,7 @@ pub struct InterruptHandler {} #[cfg(feature = "stm32wl")] impl interrupt::Handler for InterruptHandler { unsafe fn on_interrupt() { - unsafe { SUBGHZ_RADIO::steal() }.disable(); + interrupt::SUBGHZ_RADIO::disable(); IRQ_SIGNAL.signal(()); } } @@ -49,7 +49,7 @@ where rf_switch_rx: Option, rf_switch_tx: Option, ) -> Result { - unsafe { interrupt::SUBGHZ_RADIO::steal() }.disable(); + interrupt::SUBGHZ_RADIO::disable(); Ok(Self { board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board rf_switch_rx, @@ -95,7 +95,7 @@ where } async fn await_irq(&mut self) -> Result<(), RadioError> { - unsafe { interrupt::SUBGHZ_RADIO::steal() }.enable(); + unsafe { interrupt::SUBGHZ_RADIO::enable() }; IRQ_SIGNAL.wait().await; Ok(()) } diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index dc5b519ff..d7ca1f69c 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -169,14 +169,3 @@ pub fn cortex_m_interrupt_declare(item: TokenStream) -> TokenStream { let name = syn::parse_macro_input!(item as syn::Ident); cortex_m_interrupt_declare::run(name).unwrap_or_else(|x| x).into() } - -/// # interrupt_take procedural macro -/// -/// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation. -/// We are aware that this brings bloat in the form of core::fmt, but the bloat is already included with e.g. array indexing panics. -/// To get rid of this bloat, use the compiler flags `-Zbuild-std=core -Zbuild-std-features=panic_immediate_abort`. -#[proc_macro] -pub fn cortex_m_interrupt_take(item: TokenStream) -> TokenStream { - let name = syn::parse_macro_input!(item as syn::Ident); - cortex_m_interrupt_take::run(name).unwrap_or_else(|x| x).into() -} diff --git a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs index 699883efa..b317482f5 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs @@ -3,32 +3,19 @@ use quote::{format_ident, quote}; pub fn run(name: syn::Ident) -> Result { let name = format_ident!("{}", name); - let name_interrupt = format_ident!("{}", name); - let name_handler = format!("__EMBASSY_{}_HANDLER", name); - - let doc = format!("{} interrupt singleton.", name); + let doc = format!("{} interrupt.", name); let result = quote! { #[doc = #doc] #[allow(non_camel_case_types)] - pub struct #name_interrupt(()); - unsafe impl ::embassy_cortex_m::interrupt::Interrupt for #name_interrupt { - fn number(&self) -> u16 { + pub enum #name{} + unsafe impl ::embassy_cortex_m::interrupt::Interrupt for #name { + fn number() -> u16 { use cortex_m::interrupt::InterruptNumber; let irq = InterruptEnum::#name; irq.number() as u16 } - unsafe fn steal() -> Self { - Self(()) - } - unsafe fn __handler(&self) -> &'static ::embassy_cortex_m::interrupt::DynHandler { - #[export_name = #name_handler] - static HANDLER: ::embassy_cortex_m::interrupt::DynHandler = ::embassy_cortex_m::interrupt::DynHandler::new(); - &HANDLER - } } - - ::embassy_hal_common::impl_peripheral!(#name_interrupt); }; Ok(result) } diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs deleted file mode 100644 index 4806d1c12..000000000 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ /dev/null @@ -1,57 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; - -pub fn run(name: syn::Ident) -> Result { - let name = format!("{}", name); - let name_interrupt = format_ident!("{}", name); - let name_handler = format!("__EMBASSY_{}_HANDLER", name); - - #[cfg(feature = "rtos-trace-interrupt")] - let (isr_enter, isr_exit) = ( - quote! { - ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::_export::trace::isr_enter(); - } - }, - quote! { - ::embassy_executor::rtos_trace_interrupt! { - ::embassy_executor::_export::trace::isr_exit(); - } - }, - ); - - #[cfg(not(feature = "rtos-trace-interrupt"))] - let (isr_enter, isr_exit) = (quote! {}, quote! {}); - - let result = quote! { - { - #[allow(non_snake_case)] - #[export_name = #name] - pub unsafe extern "C" fn trampoline() { - extern "C" { - #[link_name = #name_handler] - static HANDLER: interrupt::DynHandler; - } - - let func = HANDLER.func.load(interrupt::_export::atomic::Ordering::Relaxed); - let ctx = HANDLER.ctx.load(interrupt::_export::atomic::Ordering::Relaxed); - let func: fn(*mut ()) = ::core::mem::transmute(func); - #isr_enter - - func(ctx); - #isr_exit - - } - - static TAKEN: interrupt::_export::atomic::AtomicBool = interrupt::_export::atomic::AtomicBool::new(false); - - if TAKEN.compare_exchange(false, true, interrupt::_export::atomic::Ordering::AcqRel, interrupt::_export::atomic::Ordering::Acquire).is_err() { - core::panic!("IRQ Already taken"); - } - - let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) }; - irq - } - }; - Ok(result) -} diff --git a/embassy-macros/src/macros/mod.rs b/embassy-macros/src/macros/mod.rs index e547736fc..a5e7a50e6 100644 --- a/embassy-macros/src/macros/mod.rs +++ b/embassy-macros/src/macros/mod.rs @@ -1,5 +1,4 @@ pub mod cortex_m_interrupt; pub mod cortex_m_interrupt_declare; -pub mod cortex_m_interrupt_take; pub mod main; pub mod task; diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 4d053c023..b4fe2d874 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -24,7 +24,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::gpio::sealed::Pin; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{self, InterruptExt}; +use crate::interrupt::{self}; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; @@ -362,8 +362,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { ppi_ch2.disable(); ppi_group.add_channel(&ppi_ch2); - unsafe { U::Interrupt::steal() }.pend(); - unsafe { U::Interrupt::steal() }.enable(); + U::Interrupt::pend(); + unsafe { U::Interrupt::enable() }; Self { _peri: peri, @@ -375,7 +375,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } fn pend_irq() { - unsafe { ::steal() }.pend() + U::Interrupt::pend() } /// Adjust the baud rate to the provided value. diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 66c682b43..2ec5220a7 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::ppi::{Event, Task}; use crate::{interrupt, pac, peripherals}; @@ -74,42 +74,38 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { } // Enable interrupts - cfg_if::cfg_if! { - if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s"))] { - let irq = unsafe { interrupt::GPIOTE0::steal() }; - } else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns"))] { - let irq = unsafe { interrupt::GPIOTE1::steal() }; - } else { - let irq = unsafe { interrupt::GPIOTE::steal() }; - } - } + #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))] + type Irq = interrupt::GPIOTE0; + #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] + type Irq = interrupt::GPIOTE1; + #[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))] + type Irq = interrupt::GPIOTE; - irq.unpend(); - irq.set_priority(irq_prio); - irq.enable(); + Irq::unpend(); + Irq::set_priority(irq_prio); + unsafe { Irq::enable() }; let g = regs(); g.events_port.write(|w| w); g.intenset.write(|w| w.port().set()); } -cfg_if::cfg_if! { - if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s"))] { - #[interrupt] - fn GPIOTE0() { - unsafe { handle_gpiote_interrupt() }; - } - } else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns"))] { - #[interrupt] - fn GPIOTE1() { - unsafe { handle_gpiote_interrupt() }; - } - } else { - #[interrupt] - fn GPIOTE() { - unsafe { handle_gpiote_interrupt() }; - } - } +#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))] +#[interrupt] +fn GPIOTE0() { + unsafe { handle_gpiote_interrupt() }; +} + +#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] +#[interrupt] +fn GPIOTE1() { + unsafe { handle_gpiote_interrupt() }; +} + +#[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))] +#[interrupt] +fn GPIOTE() { + unsafe { handle_gpiote_interrupt() }; } unsafe fn handle_gpiote_interrupt() { diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 8a1188ce4..13db77d3b 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -9,7 +9,6 @@ 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}; @@ -564,8 +563,8 @@ impl<'d, T: Instance> I2S<'d, T> { } fn setup_interrupt(&self) { - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; let device = Device::::new(); device.disable_tx_ptr_interrupt(); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d4d7a1cad..6b57c2545 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -96,7 +96,7 @@ mod chip; pub mod interrupt { //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; - pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; + pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority}; pub use crate::chip::irqs::*; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 8815bb316..9df685a26 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -14,7 +14,7 @@ use futures::future::poll_fn; use crate::chip::EASY_DMA_SIZE; use crate::gpio::sealed::Pin; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::{self, InterruptExt}; +use crate::interrupt::{self}; use crate::Peripheral; /// Interrupt handler. @@ -94,8 +94,8 @@ impl<'d, T: Instance> Pdm<'d, T> { r.gainr.write(|w| w.gainr().default_gain()); // IRQ - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; r.enable.write(|w| w.enable().set_bit()); diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index c845492fc..5761d04e1 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -6,12 +6,11 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::InterruptExt; +use crate::interrupt::Interrupt; use crate::{interrupt, Peripheral}; /// Quadrature decoder driver. @@ -134,8 +133,8 @@ impl<'d, T: Instance> Qdec<'d, T> { SamplePeriod::_131ms => w.sampleper()._131ms(), }); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; // Enable peripheral r.enable.write(|w| w.enable().set_bit()); diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 2e16c2ff5..3f48568b3 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -12,7 +12,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; pub use crate::pac::qspi::ifconfig0::{ ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode, }; @@ -207,8 +207,8 @@ impl<'d, T: Instance> Qspi<'d, T> { w }); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; // Enable it r.enable.write(|w| w.enable().enabled()); diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index a5602248d..7e9b35481 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -8,12 +8,11 @@ use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use crate::interrupt::InterruptExt; +use crate::interrupt::Interrupt; use crate::{interrupt, Peripheral}; /// Interrupt handler. @@ -99,8 +98,8 @@ impl<'d, T: Instance> Rng<'d, T> { this.stop(); this.disable_irq(); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; this } diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 8aff7df16..39764e380 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,7 +6,7 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -189,8 +189,8 @@ impl<'d, const N: usize> Saadc<'d, N> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); - unsafe { interrupt::SAADC::steal() }.unpend(); - unsafe { interrupt::SAADC::steal() }.enable(); + interrupt::SAADC::unpend(); + unsafe { interrupt::SAADC::enable() }; Self { _p: saadc } } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 89cbdfee9..bb9cda323 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -15,7 +15,7 @@ pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -207,8 +207,8 @@ impl<'d, T: Instance> Spim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { _p: spim } } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 55b5e060e..a1d6803ed 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -13,7 +13,7 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin}; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{pac, Peripheral}; @@ -214,8 +214,8 @@ impl<'d, T: Instance> Spis<'d, T> { // Disable all events interrupts. r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { _p: spis } } diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 0653710af..8a127efc5 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -3,13 +3,12 @@ use core::future::poll_fn; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; -use crate::interrupt::InterruptExt; +use crate::interrupt::Interrupt; use crate::peripherals::TEMP; use crate::{interrupt, pac, Peripheral}; @@ -42,8 +41,8 @@ impl<'d> Temp<'d> { into_ref!(_peri); // Enable interrupt that signals temperature values - unsafe { interrupt::TEMP::steal() }.unpend(); - unsafe { interrupt::TEMP::steal() }.enable(); + interrupt::TEMP::unpend(); + unsafe { interrupt::TEMP::enable() }; Self { _peri } } diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index c82c238cc..4feff8a75 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -7,7 +7,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; use embassy_time::driver::{AlarmHandle, Driver}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::{interrupt, pac}; fn rtc() -> &'static pac::rtc0::RegisterBlock { @@ -142,9 +142,8 @@ impl RtcDriver { // Wait for clear while r.counter.read().bits() != 0 {} - let irq = unsafe { interrupt::RTC1::steal() }; - irq.set_priority(irq_prio); - irq.enable(); + interrupt::RTC1::set_priority(irq_prio); + unsafe { interrupt::RTC1::enable() }; } fn on_interrupt(&self) { diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index cab36884f..dea398a67 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -16,7 +16,7 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::{gpio, pac, Peripheral}; @@ -174,8 +174,8 @@ impl<'d, T: Instance> Twim<'d, T> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { _p: twim } } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index f68a9940a..752a8c046 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -15,7 +15,7 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; use crate::util::slice_in_ram_or; use crate::{gpio, pac, Peripheral}; @@ -204,8 +204,8 @@ impl<'d, T: Instance> Twis<'d, T> { // Generate suspend on read event r.shorts.write(|w| w.read_suspend().enabled()); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { _p: twis } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 032089635..6c6941ee8 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -27,7 +27,7 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; @@ -168,8 +168,8 @@ impl<'d, T: Instance> Uarte<'d, T> { } r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; let hardware_flow_control = match (rts.is_some(), cts.is_some()) { (false, false) => false, @@ -358,8 +358,8 @@ impl<'d, T: Instance> UarteTx<'d, T> { let hardware_flow_control = cts.is_some(); configure(r, config, hardware_flow_control); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); @@ -551,8 +551,8 @@ impl<'d, T: Instance> UarteRx<'d, T> { r.psel.txd.write(|w| w.connect().disconnected()); r.psel.cts.write(|w| w.connect().disconnected()); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; let hardware_flow_control = rts.is_some(); configure(r, config, hardware_flow_control); diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index c1f3f48cb..3c62b4452 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -18,7 +18,7 @@ use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo use pac::usbd::RegisterBlock; use self::vbus_detect::VbusDetect; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; use crate::util::slice_in_ram; use crate::{pac, Peripheral}; @@ -103,8 +103,8 @@ impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { ) -> Self { into_ref!(usb); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { _p: usb, diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs index cecd4c595..a6a959905 100644 --- a/embassy-nrf/src/usb/vbus_detect.rs +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -7,7 +7,7 @@ use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; use super::BUS_WAKER; -use crate::interrupt::{self, Interrupt, InterruptExt}; +use crate::interrupt::{self, Interrupt}; use crate::pac; /// Trait for detecting USB VBUS power. @@ -80,8 +80,8 @@ impl HardwareVbusDetect { pub fn new(_irq: impl interrupt::Binding + 'static) -> Self { let regs = unsafe { &*UsbRegPeri::ptr() }; - unsafe { UsbRegIrq::steal() }.unpend(); - unsafe { UsbRegIrq::steal() }.enable(); + UsbRegIrq::unpend(); + unsafe { UsbRegIrq::enable() }; regs.intenset .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 59c7a47ce..86a353670 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -8,7 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; use embedded_hal_02::adc::{Channel, OneShot}; use crate::gpio::Pin; -use crate::interrupt::{self, InterruptExt, ADC_IRQ_FIFO}; +use crate::interrupt::{self, ADC_IRQ_FIFO}; use crate::peripherals::ADC; use crate::{pac, peripherals, Peripheral}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -63,8 +63,8 @@ impl<'d> Adc<'d> { // Setup IRQ unsafe { - ADC_IRQ_FIFO::steal().unpend(); - ADC_IRQ_FIFO::steal().enable(); + ADC_IRQ_FIFO::unpend(); + ADC_IRQ_FIFO::enable(); }; Self { phantom: PhantomData } diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index ba07a88df..74f4e6998 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -4,7 +4,7 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::vals::DataSize; @@ -29,13 +29,12 @@ unsafe fn DMA_IRQ_0() { } pub(crate) unsafe fn init() { - let irq = interrupt::DMA_IRQ_0::steal(); - irq.disable(); - irq.set_priority(interrupt::Priority::P3); + interrupt::DMA_IRQ_0::disable(); + interrupt::DMA_IRQ_0::set_priority(interrupt::Priority::P3); pac::DMA.inte0().write(|w| w.set_inte0(0xFFFF)); - irq.enable(); + interrupt::DMA_IRQ_0::enable(); } pub unsafe fn read<'a, C: Channel, W: Word>( diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index da8efba91..f213bfeab 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -3,7 +3,7 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -137,10 +137,9 @@ pub enum InterruptTrigger { } pub(crate) unsafe fn init() { - let irq = interrupt::IO_IRQ_BANK0::steal(); - irq.disable(); - irq.set_priority(interrupt::Priority::P3); - irq.enable(); + interrupt::IO_IRQ_BANK0::disable(); + interrupt::IO_IRQ_BANK0::set_priority(interrupt::Priority::P3); + interrupt::IO_IRQ_BANK0::enable(); } #[interrupt] diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 6ce77f073..124f1c00a 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -2,7 +2,7 @@ use core::future; use core::marker::PhantomData; use core::task::Poll; -use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; @@ -82,14 +82,12 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config); - unsafe { - let i2c = T::regs(); + let r = T::regs(); - // mask everything initially - i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); - T::Interrupt::steal().unpend(); - T::Interrupt::steal().enable(); - } + // mask everything initially + unsafe { r.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)) } + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; i2c } diff --git a/embassy-rp/src/interrupt.rs b/embassy-rp/src/interrupt.rs index 1db13deef..c9298644d 100644 --- a/embassy-rp/src/interrupt.rs +++ b/embassy-rp/src/interrupt.rs @@ -1,7 +1,7 @@ //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; use embassy_cortex_m::interrupt::_export::declare; -pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; +pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority}; use crate::pac::Interrupt as InterruptEnum; declare!(TIMER_IRQ_0); diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index a13209f74..807fda57b 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -50,7 +50,7 @@ use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::peripherals::CORE1; use crate::{gpio, interrupt, pac}; @@ -156,8 +156,7 @@ where IS_CORE1_INIT.store(true, Ordering::Release); // Enable fifo interrupt on CORE1 for `pause` functionality. - let irq = unsafe { interrupt::SIO_IRQ_PROC1::steal() }; - irq.enable(); + unsafe { interrupt::SIO_IRQ_PROC1::enable() }; entry() } diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index cbe45334a..93e5bd34b 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::extra::U8; @@ -110,17 +110,15 @@ unsafe fn PIO1_IRQ_0() { } pub(crate) unsafe fn init() { - let irq = interrupt::PIO0_IRQ_0::steal(); - irq.disable(); - irq.set_priority(interrupt::Priority::P3); + interrupt::PIO0_IRQ_0::disable(); + interrupt::PIO0_IRQ_0::set_priority(interrupt::Priority::P3); pac::PIO0.irqs(0).inte().write(|m| m.0 = 0); - irq.enable(); + interrupt::PIO0_IRQ_0::enable(); - let irq = interrupt::PIO1_IRQ_0::steal(); - irq.disable(); - irq.set_priority(interrupt::Priority::P3); + interrupt::PIO1_IRQ_0::disable(); + interrupt::PIO1_IRQ_0::set_priority(interrupt::Priority::P3); pac::PIO1.irqs(0).inte().write(|m| m.0 = 0); - irq.enable(); + interrupt::PIO1_IRQ_0::enable(); } /// Future that waits for TX-FIFO to become writable diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 80efd779f..68793950f 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time::driver::{AlarmHandle, Driver}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::{interrupt, pac}; struct AlarmState { @@ -145,10 +145,10 @@ pub unsafe fn init() { w.set_alarm(2, true); w.set_alarm(3, true); }); - interrupt::TIMER_IRQ_0::steal().enable(); - interrupt::TIMER_IRQ_1::steal().enable(); - interrupt::TIMER_IRQ_2::steal().enable(); - interrupt::TIMER_IRQ_3::steal().enable(); + interrupt::TIMER_IRQ_0::enable(); + interrupt::TIMER_IRQ_1::enable(); + interrupt::TIMER_IRQ_2::enable(); + interrupt::TIMER_IRQ_3::enable(); } #[interrupt] diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 12d6b8d91..bb808c467 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -3,7 +3,7 @@ use core::slice; use core::task::Poll; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt}; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; @@ -80,8 +80,8 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( w.set_txim(true); }); - T::Interrupt::steal().unpend(); - T::Interrupt::steal().enable(); + T::Interrupt::unpend(); + T::Interrupt::enable(); }; } @@ -362,7 +362,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { // FIFO and the number of bytes drops below a threshold. When the // FIFO was empty we have to manually pend the interrupt to shovel // TX data from the buffer into the FIFO. - unsafe { T::Interrupt::steal() }.pend(); + T::Interrupt::pend(); Poll::Ready(Ok(n)) }) } @@ -398,7 +398,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { // FIFO and the number of bytes drops below a threshold. When the // FIFO was empty we have to manually pend the interrupt to shovel // TX data from the buffer into the FIFO. - unsafe { T::Interrupt::steal() }.pend(); + T::Interrupt::pend(); return Ok(n); } } @@ -460,7 +460,7 @@ impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { // TX is inactive if the the buffer is not available. // We can now unregister the interrupt handler if state.tx_buf.len() == 0 { - T::Interrupt::steal().disable(); + T::Interrupt::disable(); } } } @@ -475,7 +475,7 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { // RX is inactive if the the buffer is not available. // We can now unregister the interrupt handler if state.rx_buf.len() == 0 { - T::Interrupt::steal().disable(); + T::Interrupt::disable(); } } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 44e0ca0f6..a83d94e49 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; -use embassy_cortex_m::interrupt::{self, Binding, Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::{self, Binding, Interrupt}; use embassy_futures::select::{select, Either}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -245,12 +245,10 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { fn new_inner(has_irq: bool, rx_dma: Option>) -> Self { debug_assert_eq!(has_irq, rx_dma.is_some()); if has_irq { - unsafe { - // disable all error interrupts initially - T::regs().uartimsc().write(|w| w.0 = 0); - T::Interrupt::steal().unpend(); - T::Interrupt::steal().enable(); - } + // disable all error interrupts initially + unsafe { T::regs().uartimsc().write(|w| w.0 = 0) } + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; } Self { rx_dma, @@ -295,7 +293,7 @@ impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { fn drop(&mut self) { if let Some(_) = self.rx_dma { unsafe { - T::Interrupt::steal().disable(); + T::Interrupt::disable(); // clear dma flags. irq handlers use these to disambiguate among themselves. T::regs().uartdmacr().write_clear(|reg| { reg.set_rxdmae(true); diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index fada2790f..cc88226df 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -11,7 +11,7 @@ use embassy_usb_driver::{ Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::{pac, peripherals, Peripheral, RegExt}; pub(crate) mod sealed { @@ -106,10 +106,8 @@ pub struct Driver<'d, T: Instance> { impl<'d, T: Instance> Driver<'d, T> { pub fn new(_usb: impl Peripheral

+ 'd, _irq: impl Binding>) -> Self { - unsafe { - T::Interrupt::steal().unpend(); - T::Interrupt::steal().enable(); - } + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; let regs = T::regs(); unsafe { diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 5f3fc6a93..c13915a1b 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -8,7 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::Speed; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::{interrupt, Peripheral}; /// Interrupt handler. @@ -346,8 +346,8 @@ where }); } - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { inner: peri, dma } } diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 9dafa26d0..7a1ecda35 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -14,7 +14,7 @@ use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::pac; use crate::pac::bdma::{regs, vals}; @@ -70,9 +70,8 @@ static STATE: State = State::new(); pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { - let irq = crate::interrupt::$irq::steal(); - irq.set_priority(irq_priority); - irq.enable(); + crate::interrupt::$irq::set_priority(irq_priority); + crate::interrupt::$irq::enable(); }; } crate::_generated::init_bdma(); diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 47b749ece..3b602b991 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -13,7 +13,7 @@ use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::pac::dma::{regs, vals}; use crate::{interrupt, pac}; @@ -149,9 +149,8 @@ static STATE: State = State::new(); pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { - let irq = interrupt::$irq::steal(); - irq.set_priority(irq_priority); - irq.enable(); + interrupt::$irq::set_priority(irq_priority); + interrupt::$irq::enable(); }; } crate::_generated::init_dma(); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 5a516ccda..7f8b82b46 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -12,7 +12,7 @@ use embassy_sync::waitqueue::AtomicWaker; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::GPDMA_CHANNEL_COUNT; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::pac; use crate::pac::gpdma::vals; @@ -56,9 +56,8 @@ static STATE: State = State::new(); pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { - let irq = crate::interrupt::$irq::steal(); - irq.set_priority(irq_priority); - irq.enable(); + crate::interrupt::$irq::set_priority(irq_priority); + crate::interrupt::$irq::enable(); }; } crate::_generated::init_gpdma(); diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 8ef2c3584..a5f1a268d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -5,7 +5,7 @@ mod tx_desc; use core::sync::atomic::{fence, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; @@ -267,8 +267,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { P::phy_reset(&mut this); P::phy_init(&mut this); - interrupt::ETH::steal().unpend(); - interrupt::ETH::steal().enable(); + interrupt::ETH::unpend(); + interrupt::ETH::enable(); this } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index b56c3e8ab..9efa436ac 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -2,7 +2,7 @@ mod descriptors; use core::sync::atomic::{fence, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; @@ -238,8 +238,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { P::phy_reset(&mut this); P::phy_init(&mut this); - interrupt::ETH::steal().unpend(); - interrupt::ETH::steal().enable(); + interrupt::ETH::unpend(); + interrupt::ETH::enable(); this } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 10109e56a..c2fa31b20 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -354,13 +354,13 @@ impl_exti!(EXTI15, 15); macro_rules! enable_irq { ($e:ident) => { - crate::interrupt::$e::steal().enable(); + crate::interrupt::$e::enable(); }; } /// safety: must be called only once pub(crate) unsafe fn init() { - use crate::interrupt::{Interrupt, InterruptExt}; + use crate::interrupt::Interrupt; foreach_exti_irq!(enable_irq); diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 3c3ece99a..872614d4e 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use atomic_polyfill::{fence, Ordering}; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -23,9 +23,8 @@ impl<'d> Flash<'d, Async> { ) -> Self { into_ref!(p); - let flash_irq = unsafe { crate::interrupt::FLASH::steal() }; - flash_irq.unpend(); - flash_irq.enable(); + crate::interrupt::FLASH::unpend(); + unsafe { crate::interrupt::FLASH::enable() }; Self { inner: p, diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 642ddc18c..10f57f700 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -3,7 +3,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -133,8 +133,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }); } - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { _peri: peri, diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 6533509eb..75d8af3dd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -75,7 +75,7 @@ pub(crate) mod _generated { pub mod interrupt { //! Interrupt definitions and macros to bind them. pub use cortex_m::interrupt::{CriticalSection, Mutex}; - pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, InterruptExt, Priority}; + pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority}; pub use crate::_generated::interrupt::*; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index be03a1bac..3cc17aa68 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -14,7 +14,7 @@ use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, use crate::dma::NoDma; use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::{AnyPin, Pull, Speed}; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -447,8 +447,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { T::enable(); T::reset(); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; let regs = T::regs(); unsafe { @@ -1288,7 +1288,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { impl<'d, T: Instance, Dma: SdmmcDma + 'd> Drop for Sdmmc<'d, T, Dma> { fn drop(&mut self) { - unsafe { T::Interrupt::steal() }.disable(); + T::Interrupt::disable(); unsafe { Self::on_drop() }; critical_section::with(|_| unsafe { diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 2236fde28..bab700993 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -11,7 +11,7 @@ use embassy_time::driver::{AlarmHandle, Driver}; use embassy_time::TICK_HZ; use stm32_metapac::timer::regs; -use crate::interrupt::InterruptExt; +use crate::interrupt::Interrupt; use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; @@ -177,9 +177,8 @@ impl RtcDriver { w.set_ccie(0, true); }); - let irq: ::Interrupt = core::mem::transmute(()); - irq.unpend(); - irq.enable(); + ::Interrupt::unpend(); + ::Interrupt::enable(); r.cr1().modify(|w| w.set_cen(true)); }) diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index 616f7dc56..efbbf2d1d 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -2,7 +2,7 @@ use core::mem::MaybeUninit; use atomic_polyfill::{compiler_fence, Ordering}; use bit_field::BitField; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -379,11 +379,11 @@ impl<'d> TlMbox<'d> { MemoryManager::enable(); // enable interrupts - unsafe { crate::interrupt::IPCC_C1_RX::steal() }.unpend(); - unsafe { crate::interrupt::IPCC_C1_TX::steal() }.unpend(); + crate::interrupt::IPCC_C1_RX::unpend(); + crate::interrupt::IPCC_C1_TX::unpend(); - unsafe { crate::interrupt::IPCC_C1_RX::steal() }.enable(); - unsafe { crate::interrupt::IPCC_C1_TX::steal() }.enable(); + unsafe { crate::interrupt::IPCC_C1_RX::enable() }; + unsafe { crate::interrupt::IPCC_C1_TX::enable() }; Self { _ipcc: ipcc } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 9f1da3583..252e945da 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -216,8 +216,8 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { }); } - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; Self { rx: BufferedUartRx { phantom: PhantomData }, @@ -245,7 +245,7 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { rx_reader.pop_done(len); if do_pend { - unsafe { T::Interrupt::steal().pend() }; + T::Interrupt::pend(); } return Poll::Ready(Ok(len)); @@ -271,7 +271,7 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { rx_reader.pop_done(len); if do_pend { - unsafe { T::Interrupt::steal().pend() }; + T::Interrupt::pend(); } return Ok(len); @@ -301,7 +301,7 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> { let full = state.rx_buf.is_full(); rx_reader.pop_done(amt); if full { - unsafe { T::Interrupt::steal().pend() }; + T::Interrupt::pend(); } } } @@ -324,7 +324,7 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { tx_writer.push_done(n); if empty { - unsafe { T::Interrupt::steal() }.pend(); + T::Interrupt::pend(); } Poll::Ready(Ok(n)) @@ -358,7 +358,7 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> { tx_writer.push_done(n); if empty { - unsafe { T::Interrupt::steal() }.pend(); + T::Interrupt::pend(); } return Ok(n); @@ -385,7 +385,7 @@ impl<'d, T: BasicInstance> Drop for BufferedUartRx<'d, T> { // TX is inactive if the the buffer is not available. // We can now unregister the interrupt handler if state.tx_buf.len() == 0 { - T::Interrupt::steal().disable(); + T::Interrupt::disable(); } } } @@ -400,7 +400,7 @@ impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { // RX is inactive if the the buffer is not available. // We can now unregister the interrupt handler if state.rx_buf.len() == 0 { - T::Interrupt::steal().disable(); + T::Interrupt::disable(); } } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 05ccb8749..ef1080153 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use futures::future::{select, Either}; @@ -331,8 +331,8 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { configure(r, &config, T::frequency(), T::KIND, true, false); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; // create state once! let _s = T::state(); @@ -732,8 +732,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { configure(r, &config, T::frequency(), T::KIND, true, true); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; // create state once! let _s = T::state(); diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index a9ff284ae..134107978 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -14,7 +14,7 @@ use embassy_usb_driver::{ use super::{DmPin, DpPin, Instance}; use crate::gpio::sealed::AFType; -use crate::interrupt::{Interrupt, InterruptExt}; +use crate::interrupt::Interrupt; use crate::pac::usb::regs; use crate::pac::usb::vals::{EpType, Stat}; use crate::pac::USBRAM; @@ -260,8 +260,8 @@ impl<'d, T: Instance> Driver<'d, T> { dm: impl Peripheral

> + 'd, ) -> Self { into_ref!(dp, dm); - unsafe { T::Interrupt::steal() }.unpend(); - unsafe { T::Interrupt::steal() }.enable(); + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; let regs = T::regs(); diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 921a73c8b..c7f19f6e9 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; -use embassy_cortex_m::interrupt::InterruptExt; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ @@ -629,7 +629,7 @@ impl<'d, T: Instance> Bus<'d, T> { } fn disable(&mut self) { - unsafe { T::Interrupt::steal() }.disable(); + T::Interrupt::disable(); ::disable(); @@ -902,8 +902,8 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { ::enable(); ::reset(); - T::Interrupt::steal().unpend(); - T::Interrupt::steal().enable(); + T::Interrupt::unpend(); + T::Interrupt::enable(); let r = T::regs(); let core_id = r.cid().read().0; diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index 8d60f18be..c1dc99eec 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -30,3 +30,4 @@ debug = ["defmt-rtt", "defmt"] [profile.release] debug = true +opt-level = 's' diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index d24e218dc..01695baea 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -108,7 +108,7 @@ async fn main_task(spawner: Spawner) { info!("Closing the connection"); socket.abort(); info!("Flushing the RST out..."); - socket.flush().await; + _ = socket.flush().await; info!("Finished with the socket"); } } From fa7510968acb930324986309f197273177b3071c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 2 Jun 2023 03:44:49 +0200 Subject: [PATCH 1291/1575] rp/spi: start rx dma first. --- embassy-rp/src/spi.rs | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 742a35d49..2cd2aa753 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -360,18 +360,22 @@ impl<'d, T: Instance> Spi<'d, T, Async> { reg.set_txdmae(true); }) }; - let tx_ch = self.tx_dma.as_mut().unwrap(); - let tx_transfer = unsafe { - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) - }; + + // Start RX first. Transfer starts when TX starts, if RX + // is not started yet we might lose bytes. let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) }; + + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) + }; join(tx_transfer, rx_transfer).await; Ok(()) } @@ -395,6 +399,15 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }) }; + // Start RX first. Transfer starts when TX starts, if RX + // is not started yet we might lose bytes. + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) + }; + let mut tx_ch = self.tx_dma.as_mut().unwrap(); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. @@ -411,13 +424,6 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } } }; - - let rx_ch = self.rx_dma.as_mut().unwrap(); - let rx_transfer = unsafe { - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) - }; join(tx_transfer, rx_transfer).await; // if tx > rx we should clear any overflow of the FIFO SPI buffer From dd5ce985bda5f36fdd934fe27de55c46a9972a1a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 2 Jun 2023 04:04:38 +0200 Subject: [PATCH 1292/1575] rp/spi: enable rxdmae/txdmae only once at init. see https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_spi/spi.c#L27-L28 --- embassy-rp/src/spi.rs | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 2cd2aa753..7da214743 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -90,10 +90,16 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { w.set_sph(config.phase == Phase::CaptureOnSecondTransition); w.set_scr(postdiv); }); - p.cr1().write(|w| { - w.set_sse(true); // enable + + // Always enable DREQ signals -- harmless if DMA is not listening + p.dmacr().write(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); }); + // finally, enable. + p.cr1().write(|w| w.set_sse(true)); + if let Some(pin) = &clk { pin.io().ctrl().write(|w| w.set_funcsel(1)); } @@ -329,9 +335,6 @@ impl<'d, T: Instance> Spi<'d, T, Async> { pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_txdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) @@ -354,13 +357,6 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - unsafe { - self.inner.regs().dmacr().write(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - }) - }; - // Start RX first. Transfer starts when TX starts, if RX // is not started yet we might lose bytes. let rx_ch = self.rx_dma.as_mut().unwrap(); @@ -392,13 +388,6 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let (_, tx_len) = crate::dma::slice_ptr_parts(tx_ptr); let (_, rx_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); - unsafe { - self.inner.regs().dmacr().write(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - }) - }; - // Start RX first. Transfer starts when TX starts, if RX // is not started yet we might lose bytes. let rx_ch = self.rx_dma.as_mut().unwrap(); From 3539dd7d4cad23b1bdf7edbf7811fa2cd1755e72 Mon Sep 17 00:00:00 2001 From: gak Date: Sat, 3 Jun 2023 06:59:31 +1000 Subject: [PATCH 1293/1575] Fix #1528 HS USB on STM32F7 --- embassy-stm32/src/usb_otg/usb.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index c7f19f6e9..e602bcb70 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -828,6 +828,30 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); } + #[cfg(stm32f7)] + { + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpien(ulpien); + } else { + w.set_usb_otg_hsen(ulpien); + } + }); + + // Low power mode + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpilpen(ulpien); + } else { + w.set_usb_otg_hslpen(ulpien); + } + }); + }); + } + #[cfg(stm32h7)] { // If true, VDD33USB is generated by internal regulator from VDD50USB From 2f269f32560ab178f62d08730909745f49b8eaef Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sat, 3 Jun 2023 22:05:24 -0400 Subject: [PATCH 1294/1575] stm32/rcc: Implement basic PLL support for STM32G4 series --- embassy-stm32/src/rcc/g4.rs | 163 +++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 7e748c7b5..13dced73d 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,4 +1,4 @@ -use stm32_metapac::rcc::vals::{Hpre, Ppre, Sw}; +use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; use crate::pac::{PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -15,6 +15,7 @@ pub const LSI_FREQ: Hertz = Hertz(32_000); pub enum ClockSrc { HSE(Hertz), HSI16, + PLL(PllSrc, PllM, PllN, PllClkDiv), } /// AHB prescaler @@ -41,6 +42,128 @@ pub enum APBPrescaler { Div16, } +/// PLL clock input source +#[derive(Clone, Copy, Debug)] +pub enum PllSrc { + HSI16, + HSE(Hertz), +} + +impl Into for PllSrc { + fn into(self) -> Pllsrc { + match self { + PllSrc::HSE(..) => Pllsrc::HSE, + PllSrc::HSI16 => Pllsrc::HSI16, + } + } +} + +#[derive(Clone, Copy)] +pub enum PllClkDiv { + Div2, + Div4, + Div6, + Div8, +} + +impl PllClkDiv { + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + (val as u32 + 1) * 2 + } +} + +impl From for u8 { + fn from(val: PllClkDiv) -> u8 { + match val { + PllClkDiv::Div2 => 0b00, + PllClkDiv::Div4 => 0b01, + PllClkDiv::Div6 => 0b10, + PllClkDiv::Div8 => 0b11, + } + } +} + +seq_macro::seq!(N in 8..=127 { + #[derive(Clone, Copy)] + pub enum PllN { + #( + Mul~N, + )* + } + + impl From for u8 { + fn from(val: PllN) -> u8 { + match val { + #( + PllN::Mul~N => N, + )* + } + } + } + + impl PllN { + pub fn to_mul(self) -> u32 { + match self { + #( + PllN::Mul~N => N, + )* + } + } + } +}); + +// Pre-division +#[derive(Copy, Clone)] +pub enum PllM { + Div1, + Div2, + Div3, + Div4, + Div5, + Div6, + Div7, + Div8, + Div9, + Div10, + Div11, + Div12, + Div13, + Div14, + Div15, + Div16, +} + +impl PllM { + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + val as u32 + 1 + } +} + +impl From for u8 { + fn from(val: PllM) -> u8 { + match val { + PllM::Div1 => 0b0000, + PllM::Div2 => 0b0001, + PllM::Div3 => 0b0010, + PllM::Div4 => 0b0011, + PllM::Div5 => 0b0100, + PllM::Div6 => 0b0101, + PllM::Div7 => 0b0110, + PllM::Div8 => 0b0111, + PllM::Div9 => 0b1000, + PllM::Div10 => 0b1001, + PllM::Div11 => 0b1010, + PllM::Div12 => 0b1011, + PllM::Div13 => 0b1100, + PllM::Div14 => 0b1101, + PllM::Div15 => 0b1110, + PllM::Div16 => 0b1111, + } + } +} + impl AHBPrescaler { const fn div(self) -> u32 { match self { @@ -135,6 +258,44 @@ pub(crate) unsafe fn init(config: Config) { (freq.0, Sw::HSE) } + ClockSrc::PLL(src, prediv, mul, div) => { + let src_freq = match src { + PllSrc::HSI16 => { + // Enable HSI16 + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + HSI_FREQ.0 + } + PllSrc::HSE(freq) => { + // Enable HSE + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + + freq.0 + } + }; + + // Disable PLL + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} + + let freq = src_freq / prediv.to_div() * mul.to_mul() / div.to_div(); + assert!(freq <= 170_000_000); + + RCC.pllcfgr().write(move |w| { + w.set_plln(mul.into()); + w.set_pllm(prediv.into()); + w.set_pllr(div.into()); + w.set_pllsrc(src.into()); + }); + + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + RCC.pllcfgr().modify(|w| w.set_pllren(true)); + + (freq, Sw::PLLRCLK) + } }; RCC.cfgr().modify(|w| { From 675499449fa3e348d27aebc5aae2b7f736648609 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sat, 3 Jun 2023 22:10:43 -0400 Subject: [PATCH 1295/1575] Example using PLL --- examples/stm32g4/src/bin/pll.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/stm32g4/src/bin/pll.rs diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs new file mode 100644 index 000000000..5dcdf4e53 --- /dev/null +++ b/examples/stm32g4/src/bin/pll.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{ClockSrc, PllClkDiv, PllM, PllN, PllSrc}; +use embassy_stm32::time::mhz; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + // Configure PLL to max frequency of 170 MHz + config.rcc.mux = ClockSrc::PLL(PllSrc::HSE(mhz(16)), PllM::Div4, PllN::Mul85, PllClkDiv::Div2); + + let _p = embassy_stm32::init(config); + info!("Hello World!"); + + loop { + Timer::after(Duration::from_millis(1000)).await; + info!("1s elapsed"); + } +} From 24c4ea71b11ce6c07d92dd6876fec6a9b0f6ed10 Mon Sep 17 00:00:00 2001 From: ZhangYong Date: Sat, 3 Jun 2023 17:44:25 +0800 Subject: [PATCH 1296/1575] sync/pipe: write all user data to pipe sync/pipe: add write_all function --- embassy-sync/src/pipe.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index ee27cdec8..db6ebb08b 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -294,6 +294,16 @@ where WriteFuture { pipe: self, buf } } + /// Write all bytes to the pipe. + /// + /// This method writes all bytes from `buf` into the pipe + pub async fn write_all(&self, mut buf: &[u8]) { + while !buf.is_empty() { + let n = self.write(buf).await; + buf = &buf[n..]; + } + } + /// Attempt to immediately write some bytes to the pipe. /// /// This method will either write a nonzero amount of bytes to the pipe immediately, From 6fe853a7d353edcc8b0fba8773915c4269599179 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 10:58:44 -0400 Subject: [PATCH 1297/1575] Better comments --- embassy-stm32/src/rcc/g4.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 13dced73d..7d061192b 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -261,14 +261,14 @@ pub(crate) unsafe fn init(config: Config) { ClockSrc::PLL(src, prediv, mul, div) => { let src_freq = match src { PllSrc::HSI16 => { - // Enable HSI16 + // Enable HSI16 as clock source for PLL RCC.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {} HSI_FREQ.0 } PllSrc::HSE(freq) => { - // Enable HSE + // Enable HSE as clock source for PLL RCC.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {} @@ -276,7 +276,7 @@ pub(crate) unsafe fn init(config: Config) { } }; - // Disable PLL + // Make sure PLL is disabled while we configure it RCC.cr().modify(|w| w.set_pllon(false)); while RCC.cr().read().pllrdy() {} @@ -290,6 +290,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllsrc(src.into()); }); + // Enable PLL RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} RCC.pllcfgr().modify(|w| w.set_pllren(true)); From e83762e9790d79f0716af5b3f1fcfd730f6fab35 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 11:05:13 -0400 Subject: [PATCH 1298/1575] Use HSI16 for exemple since HSE might have a different value depending on board --- examples/stm32g4/src/bin/pll.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 5dcdf4e53..580afe03d 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PllClkDiv, PllM, PllN, PllSrc}; -use embassy_stm32::time::mhz; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,8 +13,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let mut config = Config::default(); - // Configure PLL to max frequency of 170 MHz - config.rcc.mux = ClockSrc::PLL(PllSrc::HSE(mhz(16)), PllM::Div4, PllN::Mul85, PllClkDiv::Div2); + // Configure PLL to 128Mhz frequency + config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul64, PllClkDiv::Div2); let _p = embassy_stm32::init(config); info!("Hello World!"); From ade46489f190df685b5a81e0ccc267efc05b2de6 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 11:57:42 -0400 Subject: [PATCH 1299/1575] Added Vcore boost mode and Flash wait state --- embassy-stm32/src/rcc/g4.rs | 35 +++++++++++++++++++++++++++++++++ examples/stm32g4/src/bin/pll.rs | 4 ++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 7d061192b..6b1206c6a 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -1,4 +1,6 @@ +use stm32_metapac::flash::vals::Latency; use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; +use stm32_metapac::FLASH; use crate::pac::{PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -283,6 +285,39 @@ pub(crate) unsafe fn init(config: Config) { let freq = src_freq / prediv.to_div() * mul.to_mul() / div.to_div(); assert!(freq <= 170_000_000); + if freq >= 150_000_000 { + // Enable Core Boost mode ([RM0440] p234) + PWR.cr5() + .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(false)); + // Set flash wait state in boost mode based on frequency ([RM0440] p191) + if freq <= 36_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); + } else if freq <= 68_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); + } else if freq <= 102_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); + } else if freq <= 136_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); + } else { + FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); + } + } else { + PWR.cr5() + .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(true)); + // Set flash wait state in normal mode based on frequency ([RM0440] p191) + if freq <= 30_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); + } else if freq <= 60_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); + } else if freq <= 80_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); + } else if freq <= 120_000_000 { + FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); + } else { + FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); + } + } + RCC.pllcfgr().write(move |w| { w.set_plln(mul.into()); w.set_pllm(prediv.into()); diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 580afe03d..bde30c284 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -13,8 +13,8 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let mut config = Config::default(); - // Configure PLL to 128Mhz frequency - config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul64, PllClkDiv::Div2); + // Configure PLL to max frequency of 170 MHz + config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul85, PllClkDiv::Div2); let _p = embassy_stm32::init(config); info!("Hello World!"); From 4185c10bf83b96f015ef8d861b37555fff241061 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Sun, 4 Jun 2023 12:09:03 -0400 Subject: [PATCH 1300/1575] Cleanup --- embassy-stm32/src/rcc/g4.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 6b1206c6a..3ba9e7eb0 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -286,9 +286,8 @@ pub(crate) unsafe fn init(config: Config) { assert!(freq <= 170_000_000); if freq >= 150_000_000 { - // Enable Core Boost mode ([RM0440] p234) - PWR.cr5() - .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(false)); + // Enable Core Boost mode on freq >= 150Mhz ([RM0440] p234) + PWR.cr5().modify(|w| w.set_r1mode(false)); // Set flash wait state in boost mode based on frequency ([RM0440] p191) if freq <= 36_000_000 { FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); @@ -302,8 +301,7 @@ pub(crate) unsafe fn init(config: Config) { FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); } } else { - PWR.cr5() - .modify(|w: &mut stm32_metapac::pwr::regs::Cr5| w.set_r1mode(true)); + PWR.cr5().modify(|w| w.set_r1mode(true)); // Set flash wait state in normal mode based on frequency ([RM0440] p191) if freq <= 30_000_000 { FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); From a4b8fc420a377bd231d413dbdb2543550e3726ef Mon Sep 17 00:00:00 2001 From: ExplodingWaffle Date: Mon, 5 Jun 2023 01:30:34 +0100 Subject: [PATCH 1301/1575] Replace Into for Level with From for bool --- embassy-rp/src/gpio.rs | 6 +++--- embassy-stm32/src/gpio.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index f213bfeab..91cef8609 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -31,9 +31,9 @@ impl From for Level { } } -impl Into for Level { - fn into(self) -> bool { - match self { +impl From for bool { + fn from(level: Level) -> bool { + match level { Level::Low => false, Level::High => true, } diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 4895684e0..7a066a4ca 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -341,9 +341,9 @@ impl From for Level { } } -impl Into for Level { - fn into(self) -> bool { - match self { +impl From for bool { + fn from(level: Level) -> bool { + match level { Level::Low => false, Level::High => true, } From 44624b2d7a15ccc94971fc8d2091800f67821c58 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Thu, 1 Jun 2023 17:10:18 +0200 Subject: [PATCH 1302/1575] Put proto-ipv4 behind a feature flag --- embassy-net/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 0a47c5d94..4ac572577 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -26,7 +26,8 @@ unstable-traits = [] udp = ["smoltcp/socket-udp"] tcp = ["smoltcp/socket-tcp"] dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"] -dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] +dhcpv4 = ["proto-ipv4", "medium-ethernet", "smoltcp/socket-dhcpv4"] +proto-ipv4 = ["smoltcp/proto-ipv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] @@ -38,7 +39,6 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } smoltcp = { version = "0.9.0", default-features = false, features = [ - "proto-ipv4", "socket", "async", ]} From 5ee26a5dd1af3e172610b94b2657af709b319dad Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 5 Jun 2023 22:28:14 +0200 Subject: [PATCH 1303/1575] rp/dma: fix use-after-free read. --- embassy-rp/src/dma.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 74f4e6998..1cbb4651a 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -75,16 +75,17 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } +static DUMMY: u32 = 0; + pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, to: *mut W, len: usize, dreq: u8, ) -> Transfer<'a, C> { - let dummy: u32 = 0; copy_inner( ch, - &dummy as *const u32, + &DUMMY as *const u32, to as *mut u32, len, W::size(), From adf053a935d03711b48859e3243e86d3038fa7c4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 5 Jun 2023 22:54:25 +0200 Subject: [PATCH 1304/1575] rp/flash: unify FLASH_BASE const. --- embassy-rp/src/flash.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 0410429e0..838b987dc 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -9,7 +9,7 @@ use embedded_storage::nor_flash::{ use crate::pac; use crate::peripherals::FLASH; -pub const FLASH_BASE: usize = 0x10000000; +pub const FLASH_BASE: *const u32 = 0x10000000 as _; // **NOTE**: // @@ -63,8 +63,8 @@ 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() + FLASH_BASE as u32 + offset, + FLASH_BASE as u32 + offset + bytes.len() as u32 ); check_read(self, offset, bytes.len())?; @@ -242,6 +242,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S mod ram_helpers { use core::marker::PhantomData; + use super::*; use crate::rom_data; #[repr(C)] @@ -321,7 +322,7 @@ mod ram_helpers { pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(true, false, &boot2) } else { flash_function_pointers(true, false) @@ -351,7 +352,7 @@ mod ram_helpers { pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(true, true, &boot2) } else { flash_function_pointers(true, true) @@ -386,7 +387,7 @@ mod ram_helpers { pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(false, true, &boot2) } else { flash_function_pointers(false, true) @@ -511,7 +512,7 @@ mod ram_helpers { pub unsafe fn flash_unique_id(out: &mut [u8], use_boot2: bool) { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(false, false, &boot2) } else { flash_function_pointers(false, false) @@ -539,7 +540,7 @@ mod ram_helpers { pub unsafe fn flash_jedec_id(use_boot2: bool) -> u32 { let mut boot2 = [0u32; 256 / 4]; let ptrs = if use_boot2 { - rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); + rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(false, false, &boot2) } else { flash_function_pointers(false, false) From 70e1b976d87c5bde4b07e7e0b0fcf1499e4d98ef Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 5 Jun 2023 23:40:34 +0200 Subject: [PATCH 1305/1575] rp/flash: fix missing clobbers, do not clobber frame pointer (r7). --- embassy-rp/src/flash.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 838b987dc..cd34e605e 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -587,7 +587,6 @@ mod ram_helpers { "ldr r4, [r5, #4]", "blx r4", // flash_exit_xip() - "mov r7, r10", // cmd "movs r4, #0x18", "lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13 @@ -604,8 +603,9 @@ mod ram_helpers { "str r1, [r4, #0]", // Write ctrlr1 with len-1 - "ldr r0, [r7, #8]", // dummy_len - "ldr r1, [r7, #16]", // data_len + "mov r3, r10", // cmd + "ldr r0, [r3, #8]", // dummy_len + "ldr r1, [r3, #16]", // data_len "add r0, r1", "subs r0, #1", "str r0, [r4, #0x04]", // CTRLR1 @@ -617,8 +617,8 @@ mod ram_helpers { // Write cmd/addr phase to DR "mov r2, r4", "adds r2, 0x60", // &DR - "ldr r0, [r7, #0]", // cmd_addr - "ldr r1, [r7, #4]", // cmd_addr_len + "ldr r0, [r3, #0]", // cmd_addr + "ldr r1, [r3, #4]", // cmd_addr_len "10:", "ldrb r3, [r0]", "strb r3, [r2]", // DR @@ -627,7 +627,8 @@ mod ram_helpers { "bne 10b", // Skip any dummy cycles - "ldr r1, [r7, #8]", // dummy_len + "mov r3, r10", // cmd + "ldr r1, [r3, #8]", // dummy_len "cmp r1, #0", "beq 9f", "4:", @@ -644,8 +645,9 @@ mod ram_helpers { // Read RX fifo "9:", - "ldr r0, [r7, #12]", // data - "ldr r1, [r7, #16]", // data_len + "mov r2, r10", // cmd + "ldr r0, [r2, #12]", // data + "ldr r1, [r2, #16]", // data_len "2:", "ldr r3, [r4, #0x28]", // SR @@ -679,13 +681,12 @@ mod ram_helpers { out("r2") _, out("r3") _, out("r4") _, + out("r5") _, // Registers r8-r10 are used to store values // from r0-r2 in registers not clobbered by // function calls. // The values can't be passed in using r8-r10 directly // due to https://github.com/rust-lang/rust/issues/99071 - out("r8") _, - out("r9") _, out("r10") _, clobber_abi("C"), ); From 162d48530436be9cfa6a0d4aba88d97f52a5ad4a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 5 Jun 2023 23:41:26 +0200 Subject: [PATCH 1306/1575] rp/flash: centralize `USE_BOOT2` in a single const. --- embassy-rp/src/flash.rs | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index cd34e605e..0372afb1e 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -10,6 +10,7 @@ use crate::pac; use crate::peripherals::FLASH; pub const FLASH_BASE: *const u32 = 0x10000000 as _; +pub const USE_BOOT2: bool = true; // **NOTE**: // @@ -89,7 +90,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let len = to - from; - unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true))? }; + unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len))? }; Ok(()) } @@ -114,7 +115,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let unaligned_offset = offset as usize - start; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? } } let remaining_len = bytes.len() - start_padding; @@ -132,12 +133,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { if bytes.as_ptr() as usize >= 0x2000_0000 { let aligned_data = &bytes[start_padding..end_padding]; - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true))? } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? } } else { for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) { let mut ram_buf = [0xFF_u8; PAGE_SIZE]; ram_buf.copy_from_slice(chunk); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true))? } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? } aligned_offset += PAGE_SIZE; } } @@ -152,7 +153,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset); - unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } + unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? } } Ok(()) @@ -190,7 +191,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { /// Read SPI flash unique ID pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { - unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid, true))? }; + unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? }; Ok(()) } @@ -199,7 +200,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let mut jedec = None; unsafe { self.in_ram(|| { - jedec.replace(ram_helpers::flash_jedec_id(true)); + jedec.replace(ram_helpers::flash_jedec_id()); })?; }; Ok(jedec.unwrap()) @@ -307,7 +308,7 @@ mod ram_helpers { /// /// `addr` and `len` must be multiples of 4096 /// - /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader /// is used to re-initialize the XIP engine after flashing. /// /// # Safety @@ -319,9 +320,9 @@ mod ram_helpers { /// - DMA must not access flash memory /// /// `addr` and `len` parameters must be valid and are not checked. - pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) { + pub unsafe fn flash_range_erase(addr: u32, len: u32) { let mut boot2 = [0u32; 256 / 4]; - let ptrs = if use_boot2 { + let ptrs = if USE_BOOT2 { rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(true, false, &boot2) } else { @@ -337,7 +338,7 @@ mod ram_helpers { /// /// `addr` and `data.len()` must be multiples of 4096 /// - /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader /// is used to re-initialize the XIP engine after flashing. /// /// # Safety @@ -349,9 +350,9 @@ mod ram_helpers { /// - DMA must not access flash memory /// /// `addr` and `len` parameters must be valid and are not checked. - pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) { + pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) { let mut boot2 = [0u32; 256 / 4]; - let ptrs = if use_boot2 { + let ptrs = if USE_BOOT2 { rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(true, true, &boot2) } else { @@ -372,7 +373,7 @@ mod ram_helpers { /// /// `addr` and `data.len()` must be multiples of 256 /// - /// If `use_boot2` is `true`, a copy of the 2nd stage boot loader + /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader /// is used to re-initialize the XIP engine after flashing. /// /// # Safety @@ -384,9 +385,9 @@ mod ram_helpers { /// - DMA must not access flash memory /// /// `addr` and `len` parameters must be valid and are not checked. - pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) { + pub unsafe fn flash_range_program(addr: u32, data: &[u8]) { let mut boot2 = [0u32; 256 / 4]; - let ptrs = if use_boot2 { + let ptrs = if USE_BOOT2 { rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(false, true, &boot2) } else { @@ -509,9 +510,9 @@ mod ram_helpers { /// - DMA must not access flash memory /// /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) - pub unsafe fn flash_unique_id(out: &mut [u8], use_boot2: bool) { + pub unsafe fn flash_unique_id(out: &mut [u8]) { let mut boot2 = [0u32; 256 / 4]; - let ptrs = if use_boot2 { + let ptrs = if USE_BOOT2 { rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(false, false, &boot2) } else { @@ -537,9 +538,9 @@ mod ram_helpers { /// - DMA must not access flash memory /// /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) - pub unsafe fn flash_jedec_id(use_boot2: bool) -> u32 { + pub unsafe fn flash_jedec_id() -> u32 { let mut boot2 = [0u32; 256 / 4]; - let ptrs = if use_boot2 { + let ptrs = if USE_BOOT2 { rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); flash_function_pointers_with_boot2(false, false, &boot2) } else { From 4f03dff577f08bb9af1ea3fc118857973baa686e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 6 Jun 2023 00:06:32 +0200 Subject: [PATCH 1307/1575] rp: add run-from-ram feature. --- embassy-rp/Cargo.toml | 5 +++++ embassy-rp/src/flash.rs | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 5f08c7f33..ddada655b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -41,6 +41,11 @@ boot2-ram-memcpy = [] boot2-w25q080 = [] boot2-w25x10cl = [] +# Indicate code is running from RAM. +# Set this if all code is in RAM, and the cores never access memory-mapped flash memory through XIP. +# This allows the flash driver to not force pausing execution on both cores when doing flash operations. +run-from-ram = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 0372afb1e..5d928abad 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -10,7 +10,10 @@ use crate::pac; use crate::peripherals::FLASH; pub const FLASH_BASE: *const u32 = 0x10000000 as _; -pub const USE_BOOT2: bool = true; + +// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead. +// TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance. +pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram"); // **NOTE**: // From 593fc78dd892338386a2a81400e0621372264053 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 6 Jun 2023 00:07:03 +0200 Subject: [PATCH 1308/1575] tests/rp: enable run-from-ram. Otherwise the flash test is flaky because it attempts to use boot2. --- tests/rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 1786baee3..6bcac373c 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -10,7 +10,7 @@ teleprobe-meta = "1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" From 6701606e4c7811082808271b67caa1cdc9387163 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 6 Jun 2023 02:28:00 +0200 Subject: [PATCH 1309/1575] cyw43: add perf HIL test. --- ci.sh | 2 +- tests/perf-server/Cargo.toml | 8 + tests/perf-server/deploy.sh | 11 ++ tests/perf-server/perf-server.service | 16 ++ tests/perf-server/src/main.rs | 90 +++++++++ tests/rp/Cargo.toml | 6 +- tests/rp/src/bin/cyw43-perf.rs | 267 ++++++++++++++++++++++++++ 7 files changed, 398 insertions(+), 2 deletions(-) create mode 100644 tests/perf-server/Cargo.toml create mode 100755 tests/perf-server/deploy.sh create mode 100644 tests/perf-server/perf-server.service create mode 100644 tests/perf-server/src/main.rs create mode 100644 tests/rp/src/bin/cyw43-perf.rs diff --git a/ci.sh b/ci.sh index 3d6e28796..08558a18f 100755 --- a/ci.sh +++ b/ci.sh @@ -3,7 +3,7 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings -export DEFMT_LOG=trace +export DEFMT_LOG=trace,cyw43=info,cyw43_pio=info,smoltcp=info # needed by wifi examples export WIFI_NETWORK=x diff --git a/tests/perf-server/Cargo.toml b/tests/perf-server/Cargo.toml new file mode 100644 index 000000000..532039050 --- /dev/null +++ b/tests/perf-server/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "perf-server" +version = "0.1.0" +edition = "2021" + +[dependencies] +log = "0.4.17" +pretty_env_logger = "0.4.0" diff --git a/tests/perf-server/deploy.sh b/tests/perf-server/deploy.sh new file mode 100755 index 000000000..032e99c30 --- /dev/null +++ b/tests/perf-server/deploy.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -euxo pipefail + +HOST=root@192.168.1.3 + +cargo build --release +ssh $HOST -- systemctl stop perf-server +scp target/release/perf-server $HOST:/root +scp perf-server.service $HOST:/etc/systemd/system/ +ssh $HOST -- 'systemctl daemon-reload; systemctl restart perf-server' \ No newline at end of file diff --git a/tests/perf-server/perf-server.service b/tests/perf-server/perf-server.service new file mode 100644 index 000000000..c14c5d16f --- /dev/null +++ b/tests/perf-server/perf-server.service @@ -0,0 +1,16 @@ +[Unit] +Description=perf-server +After=network.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +Restart=always +RestartSec=1 +User=root +ExecStart=/root/perf-server +Environment=RUST_BACKTRACE=1 +Environment=RUST_LOG=info + +[Install] +WantedBy=multi-user.target diff --git a/tests/perf-server/src/main.rs b/tests/perf-server/src/main.rs new file mode 100644 index 000000000..f6e7efc59 --- /dev/null +++ b/tests/perf-server/src/main.rs @@ -0,0 +1,90 @@ +use std::io::{Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::thread::spawn; +use std::time::Duration; + +use log::info; + +fn main() { + pretty_env_logger::init(); + spawn(|| rx_listen()); + spawn(|| rxtx_listen()); + tx_listen(); +} + +fn tx_listen() { + info!("tx: listening on 0.0.0.0:4321"); + let listener = TcpListener::bind("0.0.0.0:4321").unwrap(); + loop { + let (socket, addr) = listener.accept().unwrap(); + info!("tx: received connection from: {}", addr); + spawn(|| tx_conn(socket)); + } +} + +fn tx_conn(mut socket: TcpStream) { + socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap(); + socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap(); + + let buf = [0; 1024]; + loop { + if let Err(e) = socket.write_all(&buf) { + info!("tx: failed to write to socket; err = {:?}", e); + return; + } + } +} + +fn rx_listen() { + info!("rx: listening on 0.0.0.0:4322"); + let listener = TcpListener::bind("0.0.0.0:4322").unwrap(); + loop { + let (socket, addr) = listener.accept().unwrap(); + info!("rx: received connection from: {}", addr); + spawn(|| rx_conn(socket)); + } +} + +fn rx_conn(mut socket: TcpStream) { + socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap(); + socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap(); + + let mut buf = [0; 1024]; + loop { + if let Err(e) = socket.read_exact(&mut buf) { + info!("rx: failed to read from socket; err = {:?}", e); + return; + } + } +} + +fn rxtx_listen() { + info!("rxtx: listening on 0.0.0.0:4323"); + let listener = TcpListener::bind("0.0.0.0:4323").unwrap(); + loop { + let (socket, addr) = listener.accept().unwrap(); + info!("rxtx: received connection from: {}", addr); + spawn(|| rxtx_conn(socket)); + } +} + +fn rxtx_conn(mut socket: TcpStream) { + socket.set_read_timeout(Some(Duration::from_secs(30))).unwrap(); + socket.set_write_timeout(Some(Duration::from_secs(30))).unwrap(); + + let mut buf = [0; 1024]; + loop { + match socket.read(&mut buf) { + Ok(n) => { + if let Err(e) = socket.write_all(&buf[..n]) { + info!("rxtx: failed to write to socket; err = {:?}", e); + return; + } + } + Err(e) => { + info!("rxtx: failed to read from socket; err = {:?}", e); + return; + } + } + } +} diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 6bcac373c..fa97fdcc4 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -5,13 +5,16 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -teleprobe-meta = "1" +teleprobe-meta = "1.1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } +cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } +cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } defmt = "0.3.0" defmt-rtt = "0.4" @@ -25,6 +28,7 @@ panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.4.0", features = ["async"] } embedded-storage = { version = "0.3" } +static_cell = { version = "1.1", features = ["nightly"]} [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs new file mode 100644 index 000000000..568f1b826 --- /dev/null +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -0,0 +1,267 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use cyw43_pio::PioSpi; +use defmt::{assert, panic, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Config, Ipv4Address, Stack, StackResources}; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; +use embassy_rp::pio::Pio; +use embassy_rp::rom_data; +use embassy_time::{with_timeout, Duration, Timer}; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +teleprobe_meta::timeout!(120); + +#[embassy_executor::task] +async fn wifi_task( + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + let p = embassy_rp::init(Default::default()); + + // needed for reading the firmware from flash via XIP. + unsafe { + rom_data::flash_exit_xip(); + rom_data::flash_enter_cmd_xip(); + } + + // cyw43 firmware needs to be flashed manually: + // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000 + // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000 + let fw = unsafe { core::slice::from_raw_parts(0x101c0000 as *const u8, 224190) }; + let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 4752) }; + + let pwr = Output::new(p.PIN_23, Level::Low); + let cs = Output::new(p.PIN_25, Level::High); + let mut pio = Pio::new(p.PIO0); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); + + let state = make_static!(cyw43::State::new()); + let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; + unwrap!(spawner.spawn(wifi_task(runner))); + + control.init(clm).await; + control + .set_power_management(cyw43::PowerManagementMode::PowerSave) + .await; + + let config = Config::Dhcp(Default::default()); + //let config = embassy_net::Config::Static(embassy_net::Config { + // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), + //}); + + // Generate random seed + let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. + + // Init network stack + let stack = &*make_static!(Stack::new( + net_device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + loop { + match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { + Ok(_) => break, + Err(err) => { + panic!("join failed with status={}", err.status); + } + } + } + + info!("Waiting for DHCP up..."); + while stack.config().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); + assert!(up > TEST_EXPECTED_UPLOAD_KBPS); + assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +// Test-only wifi network, no internet access! +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + +const TEST_DURATION: usize = 10; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 500; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 500; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 400; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &'static Stack>) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &'static Stack>) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &'static Stack>) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} From 54bab33c7342510be538bc6d8545fe50146557cf Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 5 Jun 2023 14:57:17 +0200 Subject: [PATCH 1310/1575] Rename StaticConfig to StaticConfigV4 --- embassy-net/src/lib.rs | 12 ++++++------ examples/nrf52840/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/ethernet_w5500_multisocket.rs | 2 +- examples/rp/src/bin/ethernet_w5500_tcp_client.rs | 2 +- examples/rp/src/bin/ethernet_w5500_tcp_server.rs | 2 +- examples/rp/src/bin/ethernet_w5500_udp.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/wifi_ap_tcp_server.rs | 2 +- examples/rp/src/bin/wifi_tcp_server.rs | 2 +- examples/std/src/bin/net.rs | 2 +- examples/std/src/bin/net_dns.rs | 2 +- examples/std/src/bin/net_udp.rs | 2 +- examples/std/src/bin/tcp_accept.rs | 2 +- examples/stm32f4/src/bin/usb_ethernet.rs | 2 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32h5/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- 19 files changed, 24 insertions(+), 24 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index bccbad521..ddb325c6c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -68,7 +68,7 @@ impl StackResources { /// Static IP address configuration. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct StaticConfig { +pub struct StaticConfigV4 { /// IP address and subnet mask. pub address: Ipv4Cidr, /// Default gateway. @@ -114,7 +114,7 @@ impl Default for DhcpConfig { /// Network stack configuration. pub enum Config { /// Use a static IP address configuration. - Static(StaticConfig), + Static(StaticConfigV4), /// Use DHCP to obtain an IP address configuration. #[cfg(feature = "dhcpv4")] Dhcp(DhcpConfig), @@ -131,7 +131,7 @@ pub struct Stack { struct Inner { device: D, link_up: bool, - config: Option, + config: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, #[cfg(feature = "dns")] @@ -243,7 +243,7 @@ impl Stack { } /// Get the current IP configuration. - pub fn config(&self) -> Option { + pub fn config(&self) -> Option { self.with(|_s, i| i.config.clone()) } @@ -374,7 +374,7 @@ impl SocketStack { } impl Inner { - fn apply_config(&mut self, s: &mut SocketStack, config: StaticConfig) { + fn apply_config(&mut self, s: &mut SocketStack, config: StaticConfigV4) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -470,7 +470,7 @@ impl Inner { None => {} Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), Some(dhcpv4::Event::Configured(config)) => { - let config = StaticConfig { + let config = StaticConfigV4 { address: config.address, gateway: config.router, dns_servers: config.dns_servers, diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 1065f5b5d..b4316f5a4 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -98,7 +98,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 63e142e7d..7e9e20b28 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -120,7 +120,7 @@ async fn listen_task(stack: &'static Stack>, id: u8, port: u16) } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config() { return config.clone(); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index a532de00d..c0275be1b 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -108,7 +108,7 @@ async fn main(spawner: Spawner) { } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config() { return config.clone(); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 599f6b1e9..da73e41ff 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -116,7 +116,7 @@ async fn main(spawner: Spawner) { } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config() { return config.clone(); diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index ac5a65bb6..4c861cbd2 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -95,7 +95,7 @@ async fn main(spawner: Spawner) { } } -async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfig { +async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { if let Some(config) = stack.config() { return config.clone(); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 16fbf5e91..d33d9c72e 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -87,7 +87,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index b27d3c9f8..970cf4b32 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -62,7 +62,7 @@ async fn main(spawner: Spawner) { .await; // Use a link-local address for communication without DHCP server - let config = Config::Static(embassy_net::StaticConfig { + let config = Config::StaticV4(embassy_net::StaticConfigV4 { address: embassy_net::Ipv4Cidr::new(embassy_net::Ipv4Address::new(169, 254, 1, 1), 16), dns_servers: heapless::Vec::new(), gateway: None, diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 1a00bca96..9f95f8b03 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -62,7 +62,7 @@ async fn main(spawner: Spawner) { .await; let config = Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::Config { + //let config = embassy_net::Config::StaticV4(embassy_net::Config { // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index b42bfc13b..14cf3f25b 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -42,7 +42,7 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::Static(embassy_net::StaticConfig { + Config::StaticV4(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 932ac5831..0a479a744 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -40,7 +40,7 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::Static(embassy_net::StaticConfig { + Config::StaticV4(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()]) .unwrap(), diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index d89ec7643..0ede5d998 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -38,7 +38,7 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::Static(embassy_net::StaticConfig { + Config::StaticV4(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index 01695baea..4379d0439 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -53,7 +53,7 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::Static(embassy_net::StaticConfig { + Config::StaticV4(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index d229cc3ef..0856b4842 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -95,7 +95,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index d8438241c..ebdf0d8a0 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -63,7 +63,7 @@ async fn main(spawner: Spawner) -> ! { ); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 5d1eadf4b..811f74c88 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -82,7 +82,7 @@ async fn main(spawner: Spawner) -> ! { ); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 3aa7b2271..5360a213a 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -64,7 +64,7 @@ async fn main(spawner: Spawner) -> ! { ); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 575c716b6..42781cfdc 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -65,7 +65,7 @@ async fn main(spawner: Spawner) -> ! { ); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticConfig(embassy_net::Config { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 6163e0709..d96d2f350 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -92,7 +92,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(usb_ncm_task(runner))); let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::StaticConfig { + //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), From e871324bde25bd61241aed83416caf6e49376d5a Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 5 Jun 2023 16:00:53 +0200 Subject: [PATCH 1311/1575] net: StaticV4 config behind proto-ipv4 --- embassy-net/src/device.rs | 5 +- embassy-net/src/dns.rs | 1 + embassy-net/src/lib.rs | 46 ++++++++++++++----- embassy-net/src/tcp.rs | 3 ++ .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 +- examples/rp/src/bin/ethernet_w5500_udp.rs | 2 +- 8 files changed, 47 insertions(+), 16 deletions(-) diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 5daa00544..583cdc87f 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -59,7 +59,10 @@ where smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); smolcaps.checksum.tcp = convert(caps.checksum.tcp); smolcaps.checksum.udp = convert(caps.checksum.udp); - smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + #[cfg(feature = "proto-ipv4")] + { + smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + } #[cfg(feature = "proto-ipv6")] { smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6); diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 3fd235b2c..94f75f108 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -88,6 +88,7 @@ where let addrs = self.query(host, qtype).await?; if let Some(first) = addrs.get(0) { Ok(match first { + #[cfg(feature = "proto-ipv4")] IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ddb325c6c..2e713cd10 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -34,7 +34,9 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig}; pub use smoltcp::wire::IpListenEndpoint; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; -pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; +pub use smoltcp::wire::{IpAddress, IpCidr}; +#[cfg(feature = "proto-ipv4")] +pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; @@ -67,6 +69,7 @@ impl StackResources { } /// Static IP address configuration. +#[cfg(feature = "proto-ipv4")] #[derive(Debug, Clone, PartialEq, Eq)] pub struct StaticConfigV4 { /// IP address and subnet mask. @@ -114,7 +117,8 @@ impl Default for DhcpConfig { /// Network stack configuration. pub enum Config { /// Use a static IP address configuration. - Static(StaticConfigV4), + #[cfg(feature = "proto-ipv4")] + StaticV4(StaticConfigV4), /// Use DHCP to obtain an IP address configuration. #[cfg(feature = "dhcpv4")] Dhcp(DhcpConfig), @@ -131,7 +135,8 @@ pub struct Stack { struct Inner { device: D, link_up: bool, - config: Option, + #[cfg(feature = "proto-ipv4")] + static_v4: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, #[cfg(feature = "dns")] @@ -187,7 +192,8 @@ impl Stack { let mut inner = Inner { device, link_up: false, - config: None, + #[cfg(feature = "proto-ipv4")] + static_v4: None, #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] @@ -200,7 +206,8 @@ impl Stack { }; match config { - Config::Static(config) => { + #[cfg(feature = "proto-ipv4")] + Config::StaticV4(config) => { inner.apply_config(&mut socket, config); } #[cfg(feature = "dhcpv4")] @@ -239,12 +246,21 @@ impl Stack { /// Get whether the network stack has a valid IP configuration. /// This is true if the network stack has a static IP configuration or if DHCP has completed pub fn is_config_up(&self) -> bool { - self.with(|_s, i| i.config.is_some()) + #[cfg(feature = "proto-ipv4")] + { + return self.config_v4().is_some(); + } + + #[cfg(not(any(feature = "proto-ipv4")))] + { + false + } } /// Get the current IP configuration. - pub fn config(&self) -> Option { - self.with(|_s, i| i.config.clone()) + #[cfg(feature = "proto-ipv4")] + pub fn config_v4(&self) -> Option { + self.with(|_s, i| i.static_v4.clone()) } /// Run the network stack. @@ -264,6 +280,7 @@ impl Stack { pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { // For A and AAAA queries we try detect whether `name` is just an IP address match qtype { + #[cfg(feature = "proto-ipv4")] dns::DnsQueryType::A => { if let Ok(ip) = name.parse().map(IpAddress::Ipv4) { return Ok([ip].into_iter().collect()); @@ -374,6 +391,7 @@ impl SocketStack { } impl Inner { + #[cfg(feature = "proto-ipv4")] fn apply_config(&mut self, s: &mut SocketStack, config: StaticConfigV4) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -410,7 +428,7 @@ impl Inner { socket.update_servers(&servers[..]); } - self.config = Some(config) + self.static_v4 = Some(config) } #[cfg(feature = "dhcpv4")] @@ -430,9 +448,15 @@ impl Inner { s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear()); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { - s.iface.routes_mut().remove_default_ipv4_route(); + #[cfg(feature = "proto-ipv4")] + { + s.iface.routes_mut().remove_default_ipv4_route(); + } + } + #[cfg(feature = "proto-ipv4")] + { + self.static_v4 = None } - self.config = None } fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 7babb5293..52fee6883 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -472,7 +472,10 @@ pub mod client { Self: 'a, { let addr: crate::IpAddress = match remote.ip() { + #[cfg(feature = "proto-ipv4")] IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), + #[cfg(not(feature = "proto-ipv4"))] + IpAddr::V4(_) => panic!("ipv4 support not enabled"), #[cfg(feature = "proto-ipv6")] IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), #[cfg(not(feature = "proto-ipv6"))] diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 7e9e20b28..066ecf2bc 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -122,7 +122,7 @@ async fn listen_task(stack: &'static Stack>, id: u8, port: u16) async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { - if let Some(config) = stack.config() { + if let Some(config) = stack.config_v4() { return config.clone(); } yield_now().await; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index c0275be1b..3e0f5d136 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -110,7 +110,7 @@ async fn main(spawner: Spawner) { async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { - if let Some(config) = stack.config() { + if let Some(config) = stack.config_v4() { return config.clone(); } yield_now().await; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index da73e41ff..db178d49d 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -118,7 +118,7 @@ async fn main(spawner: Spawner) { async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { - if let Some(config) = stack.config() { + if let Some(config) = stack.config_v4() { return config.clone(); } yield_now().await; diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 4c861cbd2..21943c3c6 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -97,7 +97,7 @@ async fn main(spawner: Spawner) { async fn wait_for_config(stack: &'static Stack>) -> embassy_net::StaticConfigV4 { loop { - if let Some(config) = stack.config() { + if let Some(config) = stack.config_v4() { return config.clone(); } yield_now().await; From d7f674e410a674f06a749859bde4081a99718f58 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 5 Jun 2023 16:12:24 +0200 Subject: [PATCH 1312/1575] net: Allow setting an IPv6 in the stack --- embassy-net/src/lib.rs | 72 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 2e713cd10..9f83cb4ea 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -80,6 +80,18 @@ pub struct StaticConfigV4 { pub dns_servers: Vec, } +/// Static IPv6 address configuration +#[cfg(feature = "proto-ipv6")] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StaticConfigV6 { + /// IP address and subnet mask. + pub address: Ipv6Cidr, + /// Default gateway. + pub gateway: Option, + /// DNS servers. + pub dns_servers: Vec, +} + /// DHCP configuration. #[cfg(feature = "dhcpv4")] #[derive(Debug, Clone, PartialEq, Eq)] @@ -116,9 +128,12 @@ impl Default for DhcpConfig { /// Network stack configuration. pub enum Config { - /// Use a static IP address configuration. + /// Use a static IPv4 address configuration. #[cfg(feature = "proto-ipv4")] StaticV4(StaticConfigV4), + /// Use a static IPv6 address configuration. + #[cfg(feature = "proto-ipv6")] + StaticV6(StaticConfigV6), /// Use DHCP to obtain an IP address configuration. #[cfg(feature = "dhcpv4")] Dhcp(DhcpConfig), @@ -137,6 +152,8 @@ struct Inner { link_up: bool, #[cfg(feature = "proto-ipv4")] static_v4: Option, + #[cfg(feature = "proto-ipv6")] + static_v6: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, #[cfg(feature = "dns")] @@ -194,6 +211,8 @@ impl Stack { link_up: false, #[cfg(feature = "proto-ipv4")] static_v4: None, + #[cfg(feature = "proto-ipv6")] + static_v6: None, #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] @@ -208,7 +227,11 @@ impl Stack { match config { #[cfg(feature = "proto-ipv4")] Config::StaticV4(config) => { - inner.apply_config(&mut socket, config); + inner.apply_config_v4(&mut socket, config); + } + #[cfg(feature = "proto-ipv6")] + Config::StaticV6(config) => { + inner.apply_config_v6(&mut socket, config); } #[cfg(feature = "dhcpv4")] Config::Dhcp(config) => { @@ -392,7 +415,7 @@ impl SocketStack { impl Inner { #[cfg(feature = "proto-ipv4")] - fn apply_config(&mut self, s: &mut SocketStack, config: StaticConfigV4) { + fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -431,6 +454,47 @@ impl Inner { self.static_v4 = Some(config) } + /// Replaces the current IPv6 static configuration with a newly supplied config. + #[cfg(feature = "proto-ipv6")] + fn apply_config_v6(&mut self, s: &mut SocketStack, config: StaticConfigV6) { + #[cfg(feature = "medium-ethernet")] + let medium = self.device.capabilities().medium; + + debug!("Acquired IPv6 configuration:"); + + debug!(" IP address: {}", config.address); + s.iface.update_ip_addrs(|addrs| { + if addrs.is_empty() { + addrs.push(IpCidr::Ipv6(config.address)).unwrap(); + } else { + addrs[0] = IpCidr::Ipv6(config.address); + } + }); + + #[cfg(feature = "medium-ethernet")] + if Medium::Ethernet == medium { + if let Some(gateway) = config.gateway { + debug!(" Default gateway: {}", gateway); + s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap(); + } else { + debug!(" Default gateway: None"); + s.iface.routes_mut().remove_default_ipv6_route(); + } + } + for (i, s) in config.dns_servers.iter().enumerate() { + debug!(" DNS server {}: {}", i, s); + } + + #[cfg(feature = "dns")] + { + let socket = s.sockets.get_mut::(self.dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv6(*c)).collect(); + socket.update_servers(&servers[..]); + } + + self.static_v6 = Some(config) + } + #[cfg(feature = "dhcpv4")] fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { socket.set_ignore_naks(config.ignore_naks); @@ -499,7 +563,7 @@ impl Inner { gateway: config.router, dns_servers: config.dns_servers, }; - self.apply_config(s, config) + self.apply_config_v4(s, config) } } } else if old_link_up { From 18578fd15f29e92280e4e317ff3148ea498566dd Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 5 Jun 2023 16:35:31 +0200 Subject: [PATCH 1313/1575] net: Allow a combined use of IPv4 and IPv6 DNS servers --- embassy-net/src/lib.rs | 49 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 9f83cb4ea..23ec33262 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -444,14 +444,12 @@ impl Inner { debug!(" DNS server {}: {}", i, s); } + self.static_v4 = Some(config); + #[cfg(feature = "dns")] { - let socket = s.sockets.get_mut::(self.dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - socket.update_servers(&servers[..]); + self.update_dns_servers(s) } - - self.static_v4 = Some(config) } /// Replaces the current IPv6 static configuration with a newly supplied config. @@ -485,14 +483,47 @@ impl Inner { debug!(" DNS server {}: {}", i, s); } + self.static_v6 = Some(config); + #[cfg(feature = "dns")] { - let socket = s.sockets.get_mut::(self.dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv6(*c)).collect(); - socket.update_servers(&servers[..]); + self.update_dns_servers(s) + } + } + + #[cfg(feature = "dns")] + fn update_dns_servers(&mut self, s: &mut SocketStack) { + let socket = s.sockets.get_mut::(self.dns_socket); + + let servers_v4; + #[cfg(feature = "proto-ipv4")] + { + servers_v4 = self + .static_v4 + .iter() + .flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv4(*c))); + }; + #[cfg(not(feature = "proto-ipv4"))] + { + servers_v4 = core::iter::empty(); } - self.static_v6 = Some(config) + let servers_v6; + #[cfg(feature = "proto-ipv6")] + { + servers_v6 = self + .static_v6 + .iter() + .flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv6(*c))); + } + #[cfg(not(feature = "proto-ipv6"))] + { + servers_v6 = core::iter::empty(); + } + + // Prefer the v6 DNS servers over the v4 servers + let servers: Vec = servers_v6.chain(servers_v4).collect(); + socket.update_servers(&servers[..]); } #[cfg(feature = "dhcpv4")] From d2a6c5c6082dbf3c9783b809d89faf9b369ed1c9 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 5 Jun 2023 16:42:46 +0200 Subject: [PATCH 1314/1575] CI: Add proto-ipv6 tests without ipv4 to CI --- ci.sh | 4 ++++ ci_stable.sh | 2 ++ 2 files changed, 6 insertions(+) diff --git a/ci.sh b/ci.sh index 3d6e28796..91e7abcee 100755 --- a/ci.sh +++ b/ci.sh @@ -30,6 +30,10 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ diff --git a/ci_stable.sh b/ci_stable.sh index a67087ffb..72318fc43 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -17,6 +17,8 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ From ada7ec2289c1d396ab8ddcf90943ea593d334c52 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 5 Jun 2023 17:14:20 +0200 Subject: [PATCH 1315/1575] CI: add proto-ipv4 to embassy-net test --- ci.sh | 4 ++-- ci_stable.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci.sh b/ci.sh index 91e7abcee..852e512cd 100755 --- a/ci.sh +++ b/ci.sh @@ -25,7 +25,7 @@ cargo batch \ --- 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,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ @@ -163,4 +163,4 @@ if [[ -z "${TELEPROBE_TOKEN-}" ]]; then exit fi -teleprobe client run -r out/tests \ No newline at end of file +teleprobe client run -r out/tests diff --git a/ci_stable.sh b/ci_stable.sh index 72318fc43..daae98961 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -14,7 +14,7 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ From ae1dedc0596832f5ec2389d8ff845b10c0a480f1 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Tue, 6 Jun 2023 11:17:02 +0200 Subject: [PATCH 1316/1575] net: proto-ipv6 in is_config_up --- embassy-net/src/lib.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 23ec33262..4cc191d44 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -269,23 +269,42 @@ impl Stack { /// Get whether the network stack has a valid IP configuration. /// This is true if the network stack has a static IP configuration or if DHCP has completed pub fn is_config_up(&self) -> bool { + let v4_up; + let v6_up; + #[cfg(feature = "proto-ipv4")] { - return self.config_v4().is_some(); + v4_up = self.config_v4().is_some(); + } + #[cfg(not(feature = "proto-ipv4"))] + { + v4_up = false; } - #[cfg(not(any(feature = "proto-ipv4")))] + #[cfg(feature = "proto-ipv6")] { - false + v6_up = self.config_v6().is_some(); } + #[cfg(not(feature = "proto-ipv6"))] + { + v6_up = false; + } + + v4_up || v6_up } - /// Get the current IP configuration. + /// Get the current IPv4 configuration. #[cfg(feature = "proto-ipv4")] pub fn config_v4(&self) -> Option { self.with(|_s, i| i.static_v4.clone()) } + /// Get the current IPv6 configuration. + #[cfg(feature = "proto-ipv6")] + pub fn config_v6(&self) -> Option { + self.with(|_s, i| i.static_v6.clone()) + } + /// Run the network stack. /// /// You must call this in a background task, to process network events. From ca47af6978e85012ff70a98726fcb63ed985109d Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Tue, 6 Jun 2023 18:21:45 +0200 Subject: [PATCH 1317/1575] CI: introduce tests for dual stack v4 and v6 --- ci.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci.sh b/ci.sh index 852e512cd..f4f59177f 100755 --- a/ci.sh +++ b/ci.sh @@ -34,6 +34,10 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ From 2eb08b2dc92bc0393dc417de6ccdc039d30f6dd5 Mon Sep 17 00:00:00 2001 From: Russ Hewgill Date: Tue, 6 Jun 2023 09:49:38 -0700 Subject: [PATCH 1318/1575] updated can_recv and may_recv to match the smoltcp functions. --- embassy-net/src/tcp.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 7babb5293..515bbc5be 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -278,10 +278,18 @@ impl<'a> TcpSocket<'a> { self.io.with(|s, _| s.may_send()) } - /// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer. + /// return whether the recieve half of the full-duplex connection is open. + /// This function returns true if it’s possible to receive data from the remote endpoint. + /// It will return true while there is data in the receive buffer, and if there isn’t, + /// as long as the remote endpoint has not closed the connection. pub fn may_recv(&self) -> bool { self.io.with(|s, _| s.may_recv()) } + + /// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer. + pub fn can_recv(&self) -> bool { + self.io.with(|s, _| s.can_recv()) + } } impl<'a> Drop for TcpSocket<'a> { From 352f0b6c3823797576c36f417d6be40189bca5d5 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Wed, 7 Jun 2023 12:04:15 +0200 Subject: [PATCH 1319/1575] =?UTF-8?q?net:=20Support=20dual=20stack=C2=A0IP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- embassy-net/src/lib.rs | 75 +++++++++++++++---- examples/nrf52840/src/bin/usb_ethernet.rs | 6 +- .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 +- examples/rp/src/bin/ethernet_w5500_udp.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 4 +- examples/rp/src/bin/wifi_ap_tcp_server.rs | 2 +- examples/rp/src/bin/wifi_tcp_server.rs | 4 +- examples/std/src/bin/net.rs | 4 +- examples/std/src/bin/net_dns.rs | 4 +- examples/std/src/bin/net_udp.rs | 4 +- examples/std/src/bin/tcp_accept.rs | 4 +- examples/stm32f4/src/bin/usb_ethernet.rs | 4 +- examples/stm32f7/src/bin/eth.rs | 4 +- examples/stm32h5/src/bin/eth.rs | 4 +- examples/stm32h7/src/bin/eth.rs | 4 +- examples/stm32h7/src/bin/eth_client.rs | 4 +- examples/stm32l5/src/bin/usb_ethernet.rs | 4 +- 19 files changed, 94 insertions(+), 45 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 4cc191d44..cf7ebad3f 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -127,16 +127,61 @@ impl Default for DhcpConfig { } /// Network stack configuration. -pub enum Config { - /// Use a static IPv4 address configuration. +pub struct Config { #[cfg(feature = "proto-ipv4")] - StaticV4(StaticConfigV4), - /// Use a static IPv6 address configuration. + pub ipv4: ConfigV4, #[cfg(feature = "proto-ipv6")] - StaticV6(StaticConfigV6), + pub ipv6: ConfigV6, +} + +impl Config { + #[cfg(feature = "proto-ipv4")] + pub fn ipv4_static(config: StaticConfigV4) -> Self { + Self { + ipv4: ConfigV4::Static(config), + #[cfg(feature = "proto-ipv6")] + ipv6: ConfigV6::None, + } + } + + #[cfg(feature = "proto-ipv6")] + pub fn ipv6_static(config: StaticConfigV6) -> Self { + Self { + #[cfg(feature = "proto-ipv4")] + ipv4: ConfigV4::None, + ipv6: ConfigV6::Static(config), + } + } + + #[cfg(feature = "dhcpv4")] + pub fn dhcpv4(config: DhcpConfig) -> Self { + Self { + ipv4: ConfigV4::Dhcp(config), + #[cfg(feature = "proto-ipv6")] + ipv6: ConfigV6::None, + } + } +} + +/// Network stack IPv4 configuration. +#[cfg(feature = "proto-ipv4")] +pub enum ConfigV4 { + /// Use a static IPv4 address configuration. + Static(StaticConfigV4), /// Use DHCP to obtain an IP address configuration. #[cfg(feature = "dhcpv4")] Dhcp(DhcpConfig), + /// Do not configure IPv6. + None, +} + +/// Network stack IPv6 configuration. +#[cfg(feature = "proto-ipv6")] +pub enum ConfigV6 { + /// Use a static IPv6 address configuration. + Static(StaticConfigV6), + /// Do not configure IPv6. + None, } /// A network stack. @@ -224,22 +269,26 @@ impl Stack { dns_waker: WakerRegistration::new(), }; - match config { - #[cfg(feature = "proto-ipv4")] - Config::StaticV4(config) => { + #[cfg(feature = "proto-ipv4")] + match config.ipv4 { + ConfigV4::Static(config) => { inner.apply_config_v4(&mut socket, config); } - #[cfg(feature = "proto-ipv6")] - Config::StaticV6(config) => { - inner.apply_config_v6(&mut socket, config); - } #[cfg(feature = "dhcpv4")] - Config::Dhcp(config) => { + ConfigV4::Dhcp(config) => { let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); inner.apply_dhcp_config(&mut dhcp_socket, config); let handle = socket.sockets.add(dhcp_socket); inner.dhcp_socket = Some(handle); } + ConfigV4::None => {} + } + #[cfg(feature = "proto-ipv6")] + match config.ipv6 { + ConfigV6::Static(config) => { + inner.apply_config_v6(&mut socket, config); + } + ConfigV6::None => {} } Self { diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index b4316f5a4..f527c0d7f 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -97,12 +97,12 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), - //}); + // }); // Generate random seed let mut rng = Rng::new(p.RNG, Irqs); diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 066ecf2bc..82568254a 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -64,7 +64,7 @@ async fn main(spawner: Spawner) { // Init network stack let stack = &*make_static!(Stack::new( device, - embassy_net::Config::Dhcp(Default::default()), + embassy_net::Config::dhcpv4(Default::default()), make_static!(StackResources::<3>::new()), seed )); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 3e0f5d136..d562defad 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -67,7 +67,7 @@ async fn main(spawner: Spawner) { // Init network stack let stack = &*make_static!(Stack::new( device, - embassy_net::Config::Dhcp(Default::default()), + embassy_net::Config::dhcpv4(Default::default()), make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index db178d49d..7f521cdb4 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -65,7 +65,7 @@ async fn main(spawner: Spawner) { // Init network stack let stack = &*make_static!(Stack::new( device, - embassy_net::Config::Dhcp(Default::default()), + embassy_net::Config::dhcpv4(Default::default()), make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 21943c3c6..ada86ae55 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -62,7 +62,7 @@ async fn main(spawner: Spawner) { // Init network stack let stack = &*make_static!(Stack::new( device, - embassy_net::Config::Dhcp(Default::default()), + embassy_net::Config::dhcpv4(Default::default()), make_static!(StackResources::<2>::new()), seed )); diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index d33d9c72e..91d1ec8e7 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -86,8 +86,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 970cf4b32..e8197390c 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -62,7 +62,7 @@ async fn main(spawner: Spawner) { .await; // Use a link-local address for communication without DHCP server - let config = Config::StaticV4(embassy_net::StaticConfigV4 { + let config = Config::ipv4_static(embassy_net::StaticConfigV4 { address: embassy_net::Ipv4Cidr::new(embassy_net::Ipv4Address::new(169, 254, 1, 1), 16), dns_servers: heapless::Vec::new(), gateway: None, diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 9f95f8b03..026e056fa 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -61,8 +61,8 @@ async fn main(spawner: Spawner) { .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; - let config = Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::Config { + let config = Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 14cf3f25b..3aadb029d 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -42,13 +42,13 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::StaticV4(embassy_net::StaticConfigV4 { + Config::ipv4_static(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }) } else { - Config::Dhcp(Default::default()) + Config::dhcpv4(Default::default()) }; // Generate random seed diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 0a479a744..65b5a2cd9 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -40,14 +40,14 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::StaticV4(embassy_net::StaticConfigV4 { + Config::ipv4_static(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()]) .unwrap(), gateway: Some(Ipv4Address::new(192, 168, 69, 100)), }) } else { - Config::Dhcp(Default::default()) + Config::dhcpv4(Default::default()) }; // Generate random seed diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 0ede5d998..3fc46156c 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -38,13 +38,13 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::StaticV4(embassy_net::StaticConfigV4 { + Config::ipv4_static(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }) } else { - Config::Dhcp(Default::default()) + Config::dhcpv4(Default::default()) }; // Generate random seed diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index 4379d0439..df09986ac 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -53,13 +53,13 @@ async fn main_task(spawner: Spawner) { // Choose between dhcp or static ip let config = if opts.static_ip { - Config::StaticV4(embassy_net::StaticConfigV4 { + Config::ipv4_static(embassy_net::StaticConfigV4 { address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }) } else { - Config::Dhcp(Default::default()) + Config::dhcpv4(Default::default()) }; // Generate random seed diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 0856b4842..953d99a45 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -94,8 +94,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index ebdf0d8a0..fde6a7576 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -62,8 +62,8 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 811f74c88..78c8282a6 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -81,8 +81,8 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 5360a213a..12d37f7a4 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -63,8 +63,8 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 42781cfdc..6078fc3fe 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -64,8 +64,8 @@ async fn main(spawner: Spawner) -> ! { 0, ); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index d96d2f350..32eba4277 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -91,8 +91,8 @@ async fn main(spawner: Spawner) { let (runner, device) = class.into_embassy_net_device::(make_static!(NetState::new()), our_mac_addr); unwrap!(spawner.spawn(usb_ncm_task(runner))); - let config = embassy_net::Config::Dhcp(Default::default()); - //let config = embassy_net::Config::StaticV4(embassy_net::StaticConfigV4 { + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), // dns_servers: Vec::new(), // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), From 2455fd4dbe4e874a9a200d3026f66d437449b04d Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Wed, 7 Jun 2023 12:08:53 +0200 Subject: [PATCH 1320/1575] net: Add documentation to new Config system --- embassy-net/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index cf7ebad3f..7e8f765f9 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -128,13 +128,16 @@ impl Default for DhcpConfig { /// Network stack configuration. pub struct Config { + /// IPv4 configuration #[cfg(feature = "proto-ipv4")] pub ipv4: ConfigV4, + /// IPv6 configuration #[cfg(feature = "proto-ipv6")] pub ipv6: ConfigV6, } impl Config { + /// IPv4 configuration with static addressing. #[cfg(feature = "proto-ipv4")] pub fn ipv4_static(config: StaticConfigV4) -> Self { Self { @@ -144,6 +147,7 @@ impl Config { } } + /// IPv6 configuration with static addressing. #[cfg(feature = "proto-ipv6")] pub fn ipv6_static(config: StaticConfigV6) -> Self { Self { @@ -153,6 +157,12 @@ impl Config { } } + /// IPv6 configuration with dynamic addressing. + /// + /// # Example + /// ```rust + /// let _cfg = Config::dhcpv4(Default::default()); + /// ``` #[cfg(feature = "dhcpv4")] pub fn dhcpv4(config: DhcpConfig) -> Self { Self { From ce1d72c609ae1e04410e68458ec3d6c36c7dae27 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 8 Jun 2023 16:26:47 +0100 Subject: [PATCH 1321/1575] wip --- embassy-stm32/src/tl_mbox/ble.rs | 39 ++- embassy-stm32/src/tl_mbox/cmd.rs | 42 ++- embassy-stm32/src/tl_mbox/consts.rs | 4 +- embassy-stm32/src/tl_mbox/evt.rs | 106 +++++-- embassy-stm32/src/tl_mbox/hci.rs | 60 ++++ embassy-stm32/src/tl_mbox/mm.rs | 58 ++-- embassy-stm32/src/tl_mbox/mod.rs | 365 ++++++++++++---------- embassy-stm32/src/tl_mbox/shci.rs | 35 +-- embassy-stm32/src/tl_mbox/sys.rs | 67 ++-- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 55 +--- 10 files changed, 485 insertions(+), 346 deletions(-) create mode 100644 embassy-stm32/src/tl_mbox/hci.rs diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs index 062377999..45bf81ef2 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32/src/tl_mbox/ble.rs @@ -1,35 +1,36 @@ use embassy_futures::block_on; -use super::cmd::CmdSerial; +use super::cmd::{CmdPacket, CmdSerial}; use super::consts::TlPacketType; use super::evt::EvtBox; +use super::ipcc::Ipcc; use super::unsafe_linked_list::LinkedListNode; use super::{ - channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_CHANNEL, + channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, HEAPLESS_EVT_QUEUE, TL_BLE_TABLE, TL_REF_TABLE, }; -use crate::tl_mbox::cmd::CmdPacket; -use crate::tl_mbox::ipcc::Ipcc; pub struct Ble; impl Ble { - pub fn enable() { + pub(super) fn new() -> Self { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable { pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), - pcs_buffer: CS_BUFFER.as_mut_ptr().cast(), + pcs_buffer: CS_BUFFER.as_ptr().cast(), pevt_queue: EVT_QUEUE.as_ptr().cast(), phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(), }); } Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); + + Ble } - pub fn evt_handler() { + pub(super) fn evt_handler() { unsafe { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; @@ -40,25 +41,41 @@ impl Ble { let event = node_ptr.cast(); let event = EvtBox::new(event); - block_on(TL_CHANNEL.send(event)); + block_on(HEAPLESS_EVT_QUEUE.send(event)); } } Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); } + pub(super) fn acl_data_handler(&self) { + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, false); + + // TODO: ACL data ack to the user + } + pub fn send_cmd(buf: &[u8]) { unsafe { let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; - let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial; + let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmdserial; let pcmd_serial_buf: *mut u8 = pcmd_serial.cast(); core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); - let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; - cmd_packet.cmd_serial.ty = TlPacketType::BleCmd as u8; + let mut cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; + cmd_packet.cmdserial.ty = TlPacketType::BleCmd as u8; } Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); } + + #[allow(dead_code)] // Not used currently but reserved + pub(super) fn send_acl_data() { + let cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer }; + + cmd_packet.acl_data_serial.ty = TlPacketType::AclData as u8; + + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL); + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, true); + } } diff --git a/embassy-stm32/src/tl_mbox/cmd.rs b/embassy-stm32/src/tl_mbox/cmd.rs index 3507c3231..781aa669d 100644 --- a/embassy-stm32/src/tl_mbox/cmd.rs +++ b/embassy-stm32/src/tl_mbox/cmd.rs @@ -1,7 +1,8 @@ -use super::PacketHeader; +use crate::tl_mbox::evt::{EvtPacket, EvtSerial}; +use crate::tl_mbox::{PacketHeader, TL_EVT_HEADER_SIZE}; -#[repr(C, packed)] #[derive(Copy, Clone)] +#[repr(C, packed)] pub struct Cmd { pub cmd_code: u16, pub payload_len: u8, @@ -18,22 +19,49 @@ impl Default for Cmd { } } -#[repr(C, packed)] #[derive(Copy, Clone, Default)] +#[repr(C, packed)] pub struct CmdSerial { pub ty: u8, pub cmd: Cmd, } -#[repr(C, packed)] #[derive(Copy, Clone, Default)] +#[repr(C, packed)] pub struct CmdPacket { pub header: PacketHeader, - pub cmd_serial: CmdSerial, + pub cmdserial: CmdSerial, +} + +impl CmdPacket { + /// Writes an underlying CmdPacket into the provided buffer. + /// Returns a number of bytes that were written. + /// Returns an error if event kind is unknown or if provided buffer size is not enough. + #[allow(clippy::result_unit_err)] + pub fn write(&self, buf: &mut [u8]) -> Result { + unsafe { + let cmd_ptr: *const CmdPacket = self; + let self_as_evt_ptr: *const EvtPacket = cmd_ptr.cast(); + let evt_serial: *const EvtSerial = &(*self_as_evt_ptr).evt_serial; + + let acl_data: *const AclDataPacket = cmd_ptr.cast(); + let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; + let acl_serial_buf: *const u8 = acl_serial.cast(); + + let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; + if len > buf.len() { + return Err(()); + } + + core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); + + Ok(len) + } + } } -#[repr(C, packed)] #[derive(Copy, Clone)] +#[repr(C, packed)] pub struct AclDataSerial { pub ty: u8, pub handle: u16, @@ -41,8 +69,8 @@ pub struct AclDataSerial { pub acl_data: [u8; 1], } -#[repr(C, packed)] #[derive(Copy, Clone)] +#[repr(C, packed)] pub struct AclDataPacket { pub header: PacketHeader, pub acl_data_serial: AclDataSerial, diff --git a/embassy-stm32/src/tl_mbox/consts.rs b/embassy-stm32/src/tl_mbox/consts.rs index e16a26cd0..caf26c06b 100644 --- a/embassy-stm32/src/tl_mbox/consts.rs +++ b/embassy-stm32/src/tl_mbox/consts.rs @@ -1,4 +1,6 @@ -#[derive(PartialEq)] +use core::convert::TryFrom; + +#[derive(Debug)] #[repr(C)] pub enum TlPacketType { BleCmd = 0x01, diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs index 47a8b72fd..77ce7b4ca 100644 --- a/embassy-stm32/src/tl_mbox/evt.rs +++ b/embassy-stm32/src/tl_mbox/evt.rs @@ -2,11 +2,13 @@ use core::mem::MaybeUninit; use super::cmd::{AclDataPacket, AclDataSerial}; use super::consts::TlPacketType; +use super::mm::MemoryManager; use super::{PacketHeader, TL_EVT_HEADER_SIZE}; -use crate::tl_mbox::mm::MemoryManager; -/// the payload of [`Evt`] for a command status event -#[derive(Copy, Clone)] +/** + * The payload of `Evt` for a command status event + */ +#[derive(Debug, Copy, Clone)] #[repr(C, packed)] pub struct CsEvt { pub status: u8, @@ -14,16 +16,39 @@ pub struct CsEvt { pub cmd_code: u16, } -/// the payload of [`Evt`] for a command complete event -#[derive(Clone, Copy, Default)] +/** + * The payload of `Evt` for a command complete event + */ +#[derive(Debug, Copy, Clone, Default)] #[repr(C, packed)] pub struct CcEvt { pub num_cmd: u8, - pub cmd_code: u8, + pub cmd_code: u16, pub payload: [u8; 1], } -#[derive(Clone, Copy, Default)] +impl CcEvt { + pub fn write(&self, buf: &mut [u8]) { + unsafe { + let len = core::mem::size_of::(); + assert!(buf.len() >= len); + + let self_ptr: *const CcEvt = self; + let self_buf_ptr: *const u8 = self_ptr.cast(); + + core::ptr::copy(self_buf_ptr, buf.as_mut_ptr(), len); + } + } +} + +#[derive(Debug, Copy, Clone, Default)] +#[repr(C, packed)] +pub struct AsynchEvt { + sub_evt_code: u16, + payload: [u8; 1], +} + +#[derive(Debug, Copy, Clone, Default)] #[repr(C, packed)] pub struct Evt { pub evt_code: u8, @@ -31,7 +56,7 @@ pub struct Evt { pub payload: [u8; 1], } -#[derive(Clone, Copy, Default)] +#[derive(Debug, Copy, Clone, Default)] #[repr(C, packed)] pub struct EvtSerial { pub kind: u8, @@ -46,14 +71,26 @@ pub struct EvtSerial { /// Be careful that the asynchronous events reported by the CPU2 on the system channel do /// include the header and shall use `EvtPacket` format. Only the command response format on the /// system channel is different. -#[derive(Clone, Copy, Default)] +#[derive(Copy, Clone, Default)] #[repr(C, packed)] pub struct EvtPacket { pub header: PacketHeader, pub evt_serial: EvtSerial, } -/// Smart pointer to the [`EvtPacket`] that will dispose of it automatically on drop +impl EvtPacket { + pub fn kind(&self) -> u8 { + self.evt_serial.kind + } + + pub fn evt(&self) -> &Evt { + &self.evt_serial.evt + } +} + +/// smart pointer to the [`EvtPacket`] that will dispose of [`EvtPacket`] buffer automatically +/// on [`Drop`] +#[derive(Debug)] pub struct EvtBox { ptr: *mut EvtPacket, } @@ -64,7 +101,7 @@ impl EvtBox { Self { ptr } } - /// Copies the event data from inner pointer and returns and event structure + /// copies event data from inner pointer and returns an event structure pub fn evt(&self) -> EvtPacket { let mut evt = MaybeUninit::uninit(); unsafe { @@ -73,28 +110,11 @@ impl EvtBox { } } - /// Returns the size of a buffer required to hold this event - pub fn size(&self) -> Result { - unsafe { - let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; - - if evt_kind == TlPacketType::AclData { - let acl_data: *const AclDataPacket = self.ptr.cast(); - let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; - - Ok((*acl_serial).length as usize + 5) - } else { - let evt_data: *const EvtPacket = self.ptr.cast(); - let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; - - Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE) - } - } - } - - /// writes an underlying [`EvtPacket`] into the provided buffer. Returns the number of bytes that were - /// written. Returns an error if event kind is unkown or if provided buffer size is not enough - pub fn copy_into_slice(&self, buf: &mut [u8]) -> Result { + /// writes an underlying [`EvtPacket`] into the provided buffer. + /// Returns the number of bytes that were written. + /// Returns an error if event kind is unknown or if provided buffer size is not enough. + #[allow(clippy::result_unit_err)] + pub fn write(&self, buf: &mut [u8]) -> Result { unsafe { let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; @@ -127,6 +147,26 @@ impl EvtBox { } } } + + /// returns the size of a buffer required to hold this event + #[allow(clippy::result_unit_err)] + pub fn size(&self) -> Result { + unsafe { + let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + + let evt_data: *const EvtPacket = self.ptr.cast(); + let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; + + let acl_data: *const AclDataPacket = self.ptr.cast(); + let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; + + if let TlPacketType::AclData = evt_kind { + Ok((*acl_serial).length as usize + 5) + } else { + Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE) + } + } + } } impl Drop for EvtBox { diff --git a/embassy-stm32/src/tl_mbox/hci.rs b/embassy-stm32/src/tl_mbox/hci.rs new file mode 100644 index 000000000..5bb4ba666 --- /dev/null +++ b/embassy-stm32/src/tl_mbox/hci.rs @@ -0,0 +1,60 @@ +use super::ble::Ble; +use super::consts::TlPacketType; +use super::evt::CcEvt; +use super::shci::{shci_ble_init, ShciBleInitCmdParam}; +use super::{TlMbox, STATE}; + +pub struct RadioCoprocessor<'d> { + mbox: TlMbox<'d>, + config: ShciBleInitCmdParam, + rx_buffer: [u8; 500], +} + +impl<'d> RadioCoprocessor<'d> { + pub fn new(mbox: TlMbox<'d>, config: ShciBleInitCmdParam) -> Self { + Self { + mbox, + config, + rx_buffer: [0u8; 500], + } + } + + pub fn write(&mut self, params: &[u8]) -> Result<(), ()> { + let cmd_code = params[0]; + let cmd = TlPacketType::try_from(cmd_code)?; + + match cmd { + TlPacketType::BleCmd => Ble::send_cmd(params), + _ => todo!(), + } + + Ok(()) + } + + pub async fn read(&mut self) -> &[u8] { + self.rx_buffer = [0u8; 500]; + + loop { + STATE.wait().await; + + if let Some(evt) = self.mbox.dequeue_event() { + let event = evt.evt(); + evt.write(&mut self.rx_buffer).unwrap(); + + if event.kind() == 18 { + shci_ble_init(self.config); + self.rx_buffer[0] = 0x04; // replace event code with one that is supported by HCI + } + + if let Some(cc) = self.mbox.pop_last_cc_evt() { + + + continue; + } + + let payload_len = self.rx_buffer[2]; + return &self.rx_buffer[..3 + payload_len as usize]; + } + } + } +} diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs index e28a6aa0c..a40438499 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32/src/tl_mbox/mm.rs @@ -1,56 +1,57 @@ +//! Memory manager routines + +use core::mem::MaybeUninit; + use super::evt::EvtPacket; +use super::ipcc::Ipcc; use super::unsafe_linked_list::LinkedListNode; use super::{ - channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, + channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, TL_REF_TABLE, }; -use crate::tl_mbox::ipcc::Ipcc; -pub struct MemoryManager; +pub(super) struct MemoryManager; impl MemoryManager { - pub fn enable() { + pub fn new() -> Self { unsafe { - LinkedListNode::init_head(FREE_BUFF_QUEUE.as_mut_ptr()); + LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); - TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { + TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable { spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), - ble_pool: EVT_POOL.as_ptr().cast(), - ble_pool_size: POOL_SIZE as u32, - pevt_free_buffer_queue: FREE_BUFF_QUEUE.as_mut_ptr(), + blepool: EVT_POOL.as_ptr().cast(), + blepoolsize: POOL_SIZE as u32, + pevt_free_buffer_queue: FREE_BUF_QUEUE.as_mut_ptr(), traces_evt_pool: core::ptr::null(), - traces_pool_size: 0, + tracespoolsize: 0, }); } - } - pub fn evt_handler() { - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); - Self::send_free_buf(); - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + MemoryManager } pub fn evt_drop(evt: *mut EvtPacket) { unsafe { let list_node = evt.cast(); - LinkedListNode::remove_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); - } + LinkedListNode::insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); - let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); - // postpone event buffer freeing to IPCC interrupt handler - if channel_is_busy { - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); - } else { - Self::send_free_buf(); - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + // postpone event buffer freeing to IPCC interrupt handler + if channel_is_busy { + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); + } else { + Self::send_free_buf(); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + } } } - fn send_free_buf() { + /// gives free event buffers back to CPU2 from local buffer queue + pub fn send_free_buf() { unsafe { let mut node_ptr = core::ptr::null_mut(); let node_ptr_ptr: *mut _ = &mut node_ptr; @@ -64,4 +65,11 @@ impl MemoryManager { } } } + + /// free buffer channel interrupt handler + pub fn free_buf_handler() { + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); + Self::send_free_buf(); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + } } diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index efbbf2d1d..21a954417 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -1,79 +1,50 @@ use core::mem::MaybeUninit; -use atomic_polyfill::{compiler_fence, Ordering}; use bit_field::BitField; use embassy_cortex_m::interrupt::Interrupt; +use embassy_futures::block_on; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; +use embassy_sync::signal::Signal; -use self::ble::Ble; use self::cmd::{AclDataPacket, CmdPacket}; -use self::evt::{CsEvt, EvtBox}; -use self::mm::MemoryManager; -use self::shci::{shci_ble_init, ShciBleInitCmdParam}; -use self::sys::Sys; +use self::evt::{CcEvt, EvtBox}; +use self::ipcc::{Config, Ipcc}; use self::unsafe_linked_list::LinkedListNode; use crate::interrupt; use crate::peripherals::IPCC; -pub use crate::tl_mbox::ipcc::Config; -use crate::tl_mbox::ipcc::Ipcc; -mod ble; -mod channels; -mod cmd; -mod consts; -mod evt; -mod ipcc; -mod mm; -mod shci; -mod sys; -mod unsafe_linked_list; - -pub type PacketHeader = LinkedListNode; - -const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); -const TL_EVT_HEADER_SIZE: usize = 3; -const TL_CS_EVT_SIZE: usize = core::mem::size_of::(); - -const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5; -const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; -const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE; - -const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); - -const fn divc(x: usize, y: usize) -> usize { - (x + y - 1) / y -} - -#[repr(C, packed)] -#[derive(Copy, Clone)] -pub struct SafeBootInfoTable { - version: u32, -} - -#[repr(C, packed)] -#[derive(Copy, Clone)] -pub struct FusInfoTable { - version: u32, - memory_size: u32, - fus_info: u32, -} +pub mod ble; +pub mod channels; +pub mod cmd; +pub mod consts; +pub mod evt; +pub mod hci; +pub mod ipcc; +pub mod mm; +pub mod shci; +pub mod sys; +pub mod unsafe_linked_list; /// Interrupt handler. pub struct ReceiveInterruptHandler {} impl interrupt::Handler for ReceiveInterruptHandler { unsafe fn on_interrupt() { - // info!("ipcc rx interrupt"); + debug!("ipcc rx interrupt"); if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { + debug!("sys evt"); sys::Sys::evt_handler(); } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { + debug!("ble evt"); ble::Ble::evt_handler(); } else { todo!() } + + STATE.signal(()); } } @@ -81,37 +52,62 @@ pub struct TransmitInterruptHandler {} impl interrupt::Handler for TransmitInterruptHandler { unsafe fn on_interrupt() { - // info!("ipcc tx interrupt"); + debug!("ipcc tx interrupt"); if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { + debug!("sys cmd rsp"); // TODO: handle this case - let _ = sys::Sys::cmd_evt_handler(); + let cc = sys::Sys::cmd_evt_handler(); + let a = unsafe { core::slice::from_raw_parts(&cc as *const _ as *const u8, core::mem::size_of::()) }; + debug!("{:#04x}", a); + + LAST_CC_EVT.signal(cc); } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) { - mm::MemoryManager::evt_handler(); + debug!("mm"); + mm::MemoryManager::free_buf_handler(); } else { todo!() } + + STATE.signal(()); } } -/// # Version -/// - 0 -> 3 = Build - 0: Untracked - 15:Released - x: Tracked version -/// - 4 -> 7 = branch - 0: Mass Market - x: ... -/// - 8 -> 15 = Subversion -/// - 16 -> 23 = Version minor -/// - 24 -> 31 = Version major -/// # Memory Size -/// - 0 -> 7 = Flash ( Number of 4k sector) -/// - 8 -> 15 = Reserved ( Shall be set to 0 - may be used as flash extension ) -/// - 16 -> 23 = SRAM2b ( Number of 1k sector) -/// - 24 -> 31 = SRAM2a ( Number of 1k sector) +#[derive(Debug, Copy, Clone)] +#[repr(C, packed)] +pub struct SafeBootInfoTable { + version: u32, +} + +#[derive(Debug, Copy, Clone)] +#[repr(C, packed)] +pub struct RssInfoTable { + version: u32, + memory_size: u32, + rss_info: u32, +} + +/** + * Version + * [0:3] = Build - 0: Untracked - 15:Released - x: Tracked version + * [4:7] = branch - 0: Mass Market - x: ... + * [8:15] = Subversion + * [16:23] = Version minor + * [24:31] = Version major + * + * Memory Size + * [0:7] = Flash ( Number of 4k sector) + * [8:15] = Reserved ( Shall be set to 0 - may be used as flash extension ) + * [16:23] = SRAM2b ( Number of 1k sector) + * [24:31] = SRAM2a ( Number of 1k sector) + */ +#[derive(Debug, Copy, Clone)] #[repr(C, packed)] -#[derive(Copy, Clone)] pub struct WirelessFwInfoTable { version: u32, memory_size: u32, - info_stack: u32, - reserved: u32, + thread_info: u32, + ble_info: u32, } impl WirelessFwInfoTable { @@ -122,42 +118,43 @@ impl WirelessFwInfoTable { pub fn version_minor(&self) -> u8 { let version = self.version; - (version.get_bits(16..23) & 0xff) as u8 + (version.clone().get_bits(16..23) & 0xff) as u8 } pub fn subversion(&self) -> u8 { let version = self.version; - (version.get_bits(8..15) & 0xff) as u8 + (version.clone().get_bits(8..15) & 0xff) as u8 } - /// size of FLASH, expressed in number of 4K sectors + /// Size of FLASH, expressed in number of 4K sectors. pub fn flash_size(&self) -> u8 { let memory_size = self.memory_size; - (memory_size.get_bits(0..7) & 0xff) as u8 + (memory_size.clone().get_bits(0..7) & 0xff) as u8 } - /// size for SRAM2a, expressed in number of 1K sectors + /// Size of SRAM2a, expressed in number of 1K sectors. pub fn sram2a_size(&self) -> u8 { let memory_size = self.memory_size; - (memory_size.get_bits(24..31) & 0xff) as u8 + (memory_size.clone().get_bits(24..31) & 0xff) as u8 } - /// size of SRAM2b, expressed in number of 1K sectors + /// Size of SRAM2b, expressed in number of 1K sectors. pub fn sram2b_size(&self) -> u8 { let memory_size = self.memory_size; - (memory_size.get_bits(16..23) & 0xff) as u8 + (memory_size.clone().get_bits(16..23) & 0xff) as u8 } } -#[repr(C, packed)] -#[derive(Copy, Clone)] +#[derive(Debug, Clone)] +#[repr(C, align(4))] pub struct DeviceInfoTable { pub safe_boot_info_table: SafeBootInfoTable, - pub fus_info_table: FusInfoTable, + pub rss_info_table: RssInfoTable, pub wireless_fw_info_table: WirelessFwInfoTable, } -#[repr(C, packed)] +#[derive(Debug)] +#[repr(C, align(4))] struct BleTable { pcmd_buffer: *mut CmdPacket, pcs_buffer: *const u8, @@ -165,81 +162,86 @@ struct BleTable { phci_acl_data_buffer: *mut AclDataPacket, } -#[repr(C, packed)] +#[derive(Debug)] +#[repr(C, align(4))] struct ThreadTable { - no_stack_buffer: *const u8, - cli_cmd_rsp_buffer: *const u8, - ot_cmd_rsp_buffer: *const u8, + nostack_buffer: *const u8, + clicmdrsp_buffer: *const u8, + otcmdrsp_buffer: *const u8, } -#[repr(C, packed)] +// TODO: use later +#[derive(Debug)] +#[repr(C, align(4))] +pub struct LldTestsTable { + clicmdrsp_buffer: *const u8, + m0cmd_buffer: *const u8, +} + +// TODO: use later +#[derive(Debug)] +#[repr(C, align(4))] +pub struct BleLldTable { + cmdrsp_buffer: *const u8, + m0cmd_buffer: *const u8, +} + +// TODO: use later +#[derive(Debug)] +#[repr(C, align(4))] +pub struct ZigbeeTable { + notif_m0_to_m4_buffer: *const u8, + appli_cmd_m4_to_m0_bufer: *const u8, + request_m0_to_m4_buffer: *const u8, +} + +#[derive(Debug)] +#[repr(C, align(4))] struct SysTable { pcmd_buffer: *mut CmdPacket, sys_queue: *const LinkedListNode, } -#[allow(dead_code)] // Not used currently but reserved -#[repr(C, packed)] -struct LldTestTable { - cli_cmd_rsp_buffer: *const u8, - m0_cmd_buffer: *const u8, -} - -#[allow(dead_code)] // Not used currently but reserved -#[repr(C, packed)] -struct BleLldTable { - cmd_rsp_buffer: *const u8, - m0_cmd_buffer: *const u8, -} - -#[allow(dead_code)] // Not used currently but reserved -#[repr(C, packed)] -struct ZigbeeTable { - notif_m0_to_m4_buffer: *const u8, - appli_cmd_m4_to_m0_buffer: *const u8, - request_m0_to_m4_buffer: *const u8, -} - -#[repr(C, packed)] +#[derive(Debug)] +#[repr(C, align(4))] struct MemManagerTable { spare_ble_buffer: *const u8, spare_sys_buffer: *const u8, - ble_pool: *const u8, - ble_pool_size: u32, + blepool: *const u8, + blepoolsize: u32, pevt_free_buffer_queue: *mut LinkedListNode, traces_evt_pool: *const u8, - traces_pool_size: u32, + tracespoolsize: u32, } -#[repr(C, packed)] +#[derive(Debug)] +#[repr(C, align(4))] struct TracesTable { traces_queue: *const u8, } -#[repr(C, packed)] +#[derive(Debug)] +#[repr(C, align(4))] struct Mac802_15_4Table { - pcmd_rsp_buffer: *const u8, - pnotack_buffer: *const u8, + p_cmdrsp_buffer: *const u8, + p_notack_buffer: *const u8, evt_queue: *const u8, } -/// reference table. Contains pointers to all other tables -#[repr(C, packed)] -#[derive(Copy, Clone)] +/// Reference table. Contains pointers to all other tables. +#[derive(Debug, Copy, Clone)] +#[repr(C)] pub struct RefTable { - pub device_info_table: *const DeviceInfoTable, + device_info_table: *const DeviceInfoTable, ble_table: *const BleTable, thread_table: *const ThreadTable, sys_table: *const SysTable, mem_manager_table: *const MemManagerTable, traces_table: *const TracesTable, mac_802_15_4_table: *const Mac802_15_4Table, - zigbee_table: *const ZigbeeTable, - lld_tests_table: *const LldTestTable, - ble_lld_table: *const BleLldTable, } #[link_section = "TL_REF_TABLE"] @@ -254,12 +256,6 @@ static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM1"] static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "MB_MEM1"] -static mut TL_LLD_TESTS_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_BLE_LLD_TABLE: MaybeUninit = MaybeUninit::uninit(); - #[link_section = "MB_MEM1"] static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); @@ -272,15 +268,21 @@ static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM1"] static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "MB_MEM1"] -static mut TL_ZIGBEE_TABLE: MaybeUninit = MaybeUninit::uninit(); +#[link_section = "MB_MEM2"] +static mut FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +// Not in shared RAM +static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[allow(dead_code)] // Not used currently but reserved -#[link_section = "MB_MEM1"] -static mut FREE_BUFF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +#[link_section = "MB_MEM2"] +static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -// not in shared RAM -static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +type PacketHeader = LinkedListNode; + +const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); +const TL_EVT_HEADER_SIZE: usize = 3; +const TL_CS_EVT_SIZE: usize = core::mem::size_of::(); #[link_section = "MB_MEM2"] static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = @@ -293,7 +295,30 @@ static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] -static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); +pub static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); + +/** + * Queue length of BLE Event + * This parameter defines the number of asynchronous events that can be stored in the HCI layer before + * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer + * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large + * enough to store all asynchronous events received in between. + * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events + * between the HCI command and its event. + * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is to small, + * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting + * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate + * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). + */ +const CFG_TLBLE_EVT_QUEUE_LENGTH: usize = 5; +const CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; +const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE; + +const fn divc(x: usize, y: usize) -> usize { + ((x) + (y) - 1) / (y) +} + +const POOL_SIZE: usize = CFG_TLBLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); #[link_section = "MB_MEM2"] static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); @@ -310,18 +335,27 @@ static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HE static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] -// "magic" numbers from ST ---v---v +// fuck these "magic" numbers from ST ---v---v static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); -// TODO: get a better size, this is a placeholder -pub(crate) static TL_CHANNEL: Channel = Channel::new(); +static HEAPLESS_EVT_QUEUE: Channel = Channel::new(); + +static STATE: Signal = Signal::new(); + +/// current event that is produced during IPCC IRQ handler execution +/// on SYS channel +/// last received Command Complete event +static LAST_CC_EVT: Signal = Signal::new(); pub struct TlMbox<'d> { + sys: sys::Sys, + ble: ble::Ble, + _mm: mm::MemoryManager, + _ipcc: PeripheralRef<'d, IPCC>, } impl<'d> TlMbox<'d> { - /// initializes low-level transport between CPU1 and BLE stack on CPU2 pub fn new( ipcc: impl Peripheral

+ 'd, _irqs: impl interrupt::Binding @@ -331,25 +365,16 @@ impl<'d> TlMbox<'d> { into_ref!(ipcc); unsafe { - compiler_fence(Ordering::AcqRel); - TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { - device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), + device_info_table: TL_DEVICE_INFO_TABLE.as_mut_ptr(), ble_table: TL_BLE_TABLE.as_ptr(), thread_table: TL_THREAD_TABLE.as_ptr(), sys_table: TL_SYS_TABLE.as_ptr(), mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), traces_table: TL_TRACES_TABLE.as_ptr(), mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), - zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), - lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), - ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), }); - // info!("TL_REF_TABLE addr: {:x}", TL_REF_TABLE.as_ptr() as usize); - - compiler_fence(Ordering::AcqRel); - TL_SYS_TABLE = MaybeUninit::zeroed(); TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed(); TL_BLE_TABLE = MaybeUninit::zeroed(); @@ -357,9 +382,6 @@ impl<'d> TlMbox<'d> { TL_MEM_MANAGER_TABLE = MaybeUninit::zeroed(); TL_TRACES_TABLE = MaybeUninit::zeroed(); TL_MAC_802_15_4_TABLE = MaybeUninit::zeroed(); - TL_ZIGBEE_TABLE = MaybeUninit::zeroed(); - TL_LLD_TESTS_TABLE = MaybeUninit::zeroed(); - TL_BLE_LLD_TABLE = MaybeUninit::zeroed(); EVT_POOL = MaybeUninit::zeroed(); SYS_SPARE_EVT_BUF = MaybeUninit::zeroed(); @@ -368,15 +390,13 @@ impl<'d> TlMbox<'d> { CS_BUFFER = MaybeUninit::zeroed(); BLE_CMD_BUFFER = MaybeUninit::zeroed(); HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed(); - - compiler_fence(Ordering::AcqRel); } Ipcc::enable(config); - Sys::enable(); - Ble::enable(); - MemoryManager::enable(); + let sys = sys::Sys::new(); + let ble = ble::Ble::new(); + let mm = mm::MemoryManager::new(); // enable interrupts crate::interrupt::IPCC_C1_RX::unpend(); @@ -385,13 +405,19 @@ impl<'d> TlMbox<'d> { unsafe { crate::interrupt::IPCC_C1_RX::enable() }; unsafe { crate::interrupt::IPCC_C1_TX::enable() }; - Self { _ipcc: ipcc } + Self { + sys, + ble, + _mm: mm, + _ipcc: ipcc, + } } + /// Returns CPU2 wireless firmware information (if present). pub fn wireless_fw_info(&self) -> Option { let info = unsafe { &(*(*TL_REF_TABLE.as_ptr()).device_info_table).wireless_fw_info_table }; - // zero version indicates that CPU2 wasn't active and didn't fill the information table + // Zero version indicates that CPU2 wasn't active and didn't fill the information table if info.version != 0 { Some(*info) } else { @@ -399,19 +425,22 @@ impl<'d> TlMbox<'d> { } } - pub fn shci_ble_init(&self, param: ShciBleInitCmdParam) { - shci_ble_init(param); + /// picks single [`EvtBox`] from internal event queue. + /// + /// Internal event queu is populated in IPCC_RX_IRQ handler + pub fn dequeue_event(&mut self) -> Option { + HEAPLESS_EVT_QUEUE.try_recv().ok() } - pub fn send_ble_cmd(&self, buf: &[u8]) { - ble::Ble::send_cmd(buf); - } + /// retrieves last Command Complete event and removes it from mailbox + pub fn pop_last_cc_evt(&mut self) -> Option { + if LAST_CC_EVT.signaled() { + let cc = Some(block_on(LAST_CC_EVT.wait())); + LAST_CC_EVT.reset(); - // pub fn send_sys_cmd(&self, buf: &[u8]) { - // sys::Sys::send_cmd(buf); - // } - - pub async fn read(&self) -> EvtBox { - TL_CHANNEL.recv().await + cc + } else { + None + } } } diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32/src/tl_mbox/shci.rs index 6b5b2dd19..b19baa702 100644 --- a/embassy-stm32/src/tl_mbox/shci.rs +++ b/embassy-stm32/src/tl_mbox/shci.rs @@ -1,16 +1,10 @@ -//! HCI commands for system channel - use super::cmd::CmdPacket; use super::consts::TlPacketType; -use super::{channels, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE}; -use crate::tl_mbox::ipcc::Ipcc; +use super::{sys, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE}; const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; -pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; -#[allow(dead_code)] -const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[repr(C, packed)] pub struct ShciBleInitCmdParam { /// NOT USED CURRENTLY @@ -63,39 +57,44 @@ impl Default for ShciBleInitCmdParam { } } -#[derive(Clone, Copy, Default)] +#[derive(Debug, Clone, Copy, Default)] #[repr(C, packed)] pub struct ShciHeader { metadata: [u32; 3], } -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] #[repr(C, packed)] pub struct ShciBleInitCmdPacket { header: ShciHeader, param: ShciBleInitCmdParam, } +pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; +#[allow(dead_code)] // Not used currently but reserved +const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; + pub fn shci_ble_init(param: ShciBleInitCmdParam) { + debug!("shci init"); + let mut packet = ShciBleInitCmdPacket { header: ShciHeader::default(), param, }; - let packet_ptr: *mut ShciBleInitCmdPacket = &mut packet; + let packet_ptr: *mut _ = &mut packet; unsafe { let cmd_ptr: *mut CmdPacket = packet_ptr.cast(); - (*cmd_ptr).cmd_serial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT; - (*cmd_ptr).cmd_serial.cmd.payload_len = core::mem::size_of::() as u8; + (*cmd_ptr).cmdserial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT; + (*cmd_ptr).cmdserial.cmd.payload_len = core::mem::size_of::() as u8; - let cmd_buf = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; - core::ptr::write(cmd_buf, *cmd_ptr); + let mut p_cmd_buffer = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; + core::ptr::write(p_cmd_buffer, *cmd_ptr); - cmd_buf.cmd_serial.ty = TlPacketType::SysCmd as u8; + p_cmd_buffer.cmdserial.ty = TlPacketType::SysCmd as u8; - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + sys::send_cmd(); } } diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs index 9685fb920..c87aa440c 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32/src/tl_mbox/sys.rs @@ -1,45 +1,27 @@ use embassy_futures::block_on; use super::cmd::{CmdPacket, CmdSerial}; -use super::consts::TlPacketType; use super::evt::{CcEvt, EvtBox, EvtSerial}; +use super::ipcc::Ipcc; use super::unsafe_linked_list::LinkedListNode; -use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_CHANNEL, TL_REF_TABLE, TL_SYS_TABLE}; -use crate::tl_mbox::ipcc::Ipcc; +use super::{channels, SysTable, HEAPLESS_EVT_QUEUE, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; pub struct Sys; impl Sys { - pub fn enable() { + pub fn new() -> Self { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), - }); + }) } Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); - } - pub fn evt_handler() { - unsafe { - let mut node_ptr = core::ptr::null_mut(); - let node_ptr_ptr: *mut _ = &mut node_ptr; - - while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); - - let event = node_ptr.cast(); - let event = EvtBox::new(event); - - // TODO: not really happy about this - block_on(TL_CHANNEL.send(event)); - } - } - - Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); + Sys } pub fn cmd_evt_handler() -> CcEvt { @@ -55,29 +37,40 @@ impl Sys { // 4. CcEvt type is the actual SHCI response // 5. profit unsafe { - let cmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer; - let cmd_serial: *const CmdSerial = &(*cmd).cmd_serial; + let pcmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer; + + let a = unsafe { + core::slice::from_raw_parts(&pcmd as *const _ as *const u8, core::mem::size_of::()) + }; + debug!("shci response {:#04x}", a); + + let cmd_serial: *const CmdSerial = &(*pcmd).cmdserial; let evt_serial: *const EvtSerial = cmd_serial.cast(); - let cc = (*evt_serial).evt.payload.as_ptr().cast(); + let cc: *const CcEvt = (*evt_serial).evt.payload.as_ptr().cast(); *cc } } - #[allow(dead_code)] - pub fn send_cmd(buf: &[u8]) { + pub fn evt_handler() { unsafe { - // TODO: check this - let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; - let cmd_serial: *mut CmdSerial = &mut cmd_buffer.cmd_serial; - let cmd_serial_buf = cmd_serial.cast(); + let mut node_ptr = core::ptr::null_mut(); + let node_ptr_ptr: *mut _ = &mut node_ptr; - core::ptr::copy(buf.as_ptr(), cmd_serial_buf, buf.len()); + while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { + LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); - let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; - cmd_packet.cmd_serial.ty = TlPacketType::SysCmd as u8; + let event = node_ptr.cast(); + let event = EvtBox::new(event); - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + block_on(HEAPLESS_EVT_QUEUE.send(event)); + } } + + Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); } } + +pub fn send_cmd() { + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); +} diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 1724d946f..0525d3f37 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -4,7 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::tl_mbox::{Config, TlMbox}; +use embassy_stm32::tl_mbox::hci::RadioCoprocessor; +use embassy_stm32::tl_mbox::ipcc::Config; +use embassy_stm32::tl_mbox::TlMbox; use embassy_stm32::{bind_interrupts, tl_mbox}; use {defmt_rtt as _, panic_probe as _}; @@ -45,53 +47,14 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mbox = TlMbox::new(p.IPCC, Irqs, config); - info!("waiting for coprocessor to boot"); - let event_box = mbox.read().await; + let mut rc = RadioCoprocessor::new(mbox, Default::default()); + rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]).unwrap(); - let mut payload = [0u8; 6]; - event_box.copy_into_slice(&mut payload).unwrap(); + let response = rc.read().await; + info!("coprocessor ready {}", response); - let event_packet = event_box.evt(); - let kind = event_packet.evt_serial.kind; - - // means recieved SYS event, which indicates in this case that the coprocessor is ready - if kind == 0x12 { - let code = event_packet.evt_serial.evt.evt_code; - let payload_len = event_packet.evt_serial.evt.payload_len; - - info!( - "==> kind: {:#04x}, code: {:#04x}, payload_length: {}, payload: {:#04x}", - kind, - code, - payload_len, - payload[3..] - ); - } - - // initialize ble stack, does not return a response - mbox.shci_ble_init(Default::default()); - - info!("resetting BLE"); - mbox.send_ble_cmd(&[0x01, 0x03, 0x0c, 0x00, 0x00]); - - let event_box = mbox.read().await; - - let mut payload = [0u8; 7]; - event_box.copy_into_slice(&mut payload).unwrap(); - - let event_packet = event_box.evt(); - let kind = event_packet.evt_serial.kind; - - let code = event_packet.evt_serial.evt.evt_code; - let payload_len = event_packet.evt_serial.evt.payload_len; - - info!( - "==> kind: {:#04x}, code: {:#04x}, payload_length: {}, payload: {:#04x}", - kind, - code, - payload_len, - payload[3..] - ); + let response = rc.read().await; + info!("coprocessor ready {}", response); info!("Test OK"); cortex_m::asm::bkpt(); From 921780e6bfb9bcb2cd087b8aa8b094d792c99fa2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Jun 2023 16:08:40 +0200 Subject: [PATCH 1322/1575] Make interrupt module more standard. - Move typelevel interrupts to a special-purpose mod: `embassy_xx::interrupt::typelevel`. - Reexport the PAC interrupt enum in `embassy_xx::interrupt`. This has a few advantages: - The `embassy_xx::interrupt` module is now more "standard". - It works with `cortex-m` functions for manipulating interrupts, for example. - It works with RTIC. - the interrupt enum allows holding value that can be "any interrupt at runtime", this can't be done with typelevel irqs. - When "const-generics on enums" is stable, we can remove the typelevel interrupts without disruptive changes to `embassy_xx::interrupt`. --- embassy-cortex-m/src/interrupt.rs | 211 +++++++++++++----- embassy-lora/src/iv.rs | 12 +- embassy-macros/src/lib.rs | 13 -- .../src/macros/cortex_m_interrupt.rs | 66 ------ .../src/macros/cortex_m_interrupt_declare.rs | 21 -- embassy-macros/src/macros/mod.rs | 2 - embassy-nrf/src/buffered_uarte.rs | 11 +- embassy-nrf/src/chips/nrf52805.rs | 56 +++-- embassy-nrf/src/chips/nrf52810.rs | 62 +++-- embassy-nrf/src/chips/nrf52811.rs | 62 +++-- embassy-nrf/src/chips/nrf52820.rs | 60 +++-- embassy-nrf/src/chips/nrf52832.rs | 82 ++++--- embassy-nrf/src/chips/nrf52833.rs | 90 ++++---- embassy-nrf/src/chips/nrf52840.rs | 94 ++++---- embassy-nrf/src/chips/nrf5340_app.rs | 90 ++++---- embassy-nrf/src/chips/nrf5340_net.rs | 48 ++-- embassy-nrf/src/chips/nrf9160.rs | 70 +++--- embassy-nrf/src/gpiote.rs | 14 +- embassy-nrf/src/i2s.rs | 14 +- embassy-nrf/src/lib.rs | 29 +-- embassy-nrf/src/pdm.rs | 13 +- embassy-nrf/src/pwm.rs | 7 +- embassy-nrf/src/qdec.rs | 12 +- embassy-nrf/src/qspi.rs | 12 +- embassy-nrf/src/rng.rs | 10 +- embassy-nrf/src/saadc.rs | 10 +- embassy-nrf/src/spim.rs | 16 +- embassy-nrf/src/spis.rs | 16 +- embassy-nrf/src/temp.rs | 10 +- embassy-nrf/src/time_driver.rs | 6 +- embassy-nrf/src/timer.rs | 5 +- embassy-nrf/src/twim.rs | 12 +- embassy-nrf/src/twis.rs | 12 +- embassy-nrf/src/uarte.rs | 22 +- embassy-nrf/src/usb/mod.rs | 12 +- embassy-nrf/src/usb/vbus_detect.rs | 12 +- embassy-rp/src/adc.rs | 16 +- embassy-rp/src/dma.rs | 8 +- embassy-rp/src/gpio.rs | 8 +- embassy-rp/src/i2c.rs | 13 +- embassy-rp/src/interrupt.rs | 65 ------ embassy-rp/src/lib.rs | 64 +++++- embassy-rp/src/multicore.rs | 4 +- embassy-rp/src/pio.rs | 14 +- embassy-rp/src/timer.rs | 10 +- embassy-rp/src/uart/buffered.rs | 6 +- embassy-rp/src/uart/mod.rs | 10 +- embassy-rp/src/usb.rs | 11 +- embassy-stm32/build.rs | 8 +- embassy-stm32/src/dcmi.rs | 24 +- embassy-stm32/src/dma/bdma.rs | 6 +- embassy-stm32/src/dma/dma.rs | 6 +- embassy-stm32/src/dma/gpdma.rs | 4 +- embassy-stm32/src/eth/v1/mod.rs | 10 +- embassy-stm32/src/eth/v2/mod.rs | 10 +- embassy-stm32/src/exti.rs | 4 +- embassy-stm32/src/flash/asynch.rs | 10 +- embassy-stm32/src/i2c/mod.rs | 6 +- embassy-stm32/src/i2c/v1.rs | 4 +- embassy-stm32/src/i2c/v2.rs | 6 +- embassy-stm32/src/lib.rs | 51 ++--- embassy-stm32/src/sdmmc/mod.rs | 16 +- embassy-stm32/src/time_driver.rs | 2 +- embassy-stm32/src/timer/mod.rs | 6 +- embassy-stm32/src/tl_mbox/mod.rs | 18 +- embassy-stm32/src/usart/buffered.rs | 10 +- embassy-stm32/src/usart/mod.rs | 18 +- embassy-stm32/src/usb/mod.rs | 6 +- embassy-stm32/src/usb/usb.rs | 6 +- embassy-stm32/src/usb_otg/mod.rs | 10 +- embassy-stm32/src/usb_otg/usb.rs | 8 +- examples/stm32f7/build.rs | 3 +- 72 files changed, 845 insertions(+), 930 deletions(-) delete mode 100644 embassy-macros/src/macros/cortex_m_interrupt.rs delete mode 100644 embassy-macros/src/macros/cortex_m_interrupt_declare.rs delete mode 100644 embassy-rp/src/interrupt.rs diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index 0e790eaaf..8e45c36c7 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -2,120 +2,207 @@ use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; +use cortex_m::interrupt::InterruptNumber; use cortex_m::peripheral::NVIC; -/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. -#[doc(hidden)] -pub mod _export { - pub use atomic_polyfill as atomic; - pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare}; -} +/// Generate a standard `mod interrupt` for a HAL. +#[macro_export] +macro_rules! interrupt_mod { + ($($irqs:ident),* $(,)?) => { + pub use cortex_m_rt::interrupt; -/// Interrupt handler trait. -/// -/// Drivers that need to handle interrupts implement this trait. -/// The user must ensure `on_interrupt()` is called every time the interrupt fires. -/// Drivers must use use [`Binding`] to assert at compile time that the user has done so. -pub trait Handler { - /// Interrupt handler function. - /// - /// Must be called every time the `I` interrupt fires, synchronously from - /// the interrupt handler context. - /// - /// # Safety - /// - /// This function must ONLY be called from the interrupt handler for `I`. - unsafe fn on_interrupt(); -} + /// Interrupt definitions. + pub mod interrupt { + pub use embassy_cortex_m::interrupt::{InterruptExt, Priority}; + pub use crate::pac::interrupt::*; + pub use crate::pac::Interrupt; -/// Compile-time assertion that an interrupt has been bound to a handler. -/// -/// For the vast majority of cases, you should use the `bind_interrupts!` -/// macro instead of writing `unsafe impl`s of this trait. -/// -/// # Safety -/// -/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` -/// to be called every time the `I` interrupt fires. -/// -/// This allows drivers to check bindings at compile-time. -pub unsafe trait Binding> {} + /// Type-level interrupt infrastructure. + /// + /// This module contains one *type* per interrupt. This is used for checking at compile time that + /// the interrupts are correctly bound to HAL drivers. + /// + /// As an end user, you shouldn't need to use this module directly. Use the [`crate::bind_interrupts!`] macro + /// to bind interrupts, and the [`crate::interrupt`] module to manually register interrupt handlers and manipulate + /// interrupts directly (pending/unpending, enabling/disabling, setting the priority, etc...) + pub mod typelevel { + use super::InterruptExt; -#[derive(Clone, Copy)] -pub(crate) struct NrWrap(pub(crate) u16); -unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { - fn number(self) -> u16 { - self.0 - } + mod sealed { + pub trait Interrupt {} + } + + /// Type-level interrupt. + /// + /// This trait is implemented for all typelevel interrupt types in this module. + pub trait Interrupt: sealed::Interrupt { + + /// Interrupt enum variant. + /// + /// This allows going from typelevel interrupts (one type per interrupt) to + /// non-typelevel interrupts (a single `Interrupt` enum type, with one variant per interrupt). + const IRQ: super::Interrupt; + + /// Enable the interrupt. + #[inline] + unsafe fn enable() { + Self::IRQ.enable() + } + + /// Disable the interrupt. + #[inline] + fn disable() { + Self::IRQ.disable() + } + + /// Check if interrupt is enabled. + #[inline] + fn is_enabled() -> bool { + Self::IRQ.is_enabled() + } + + /// Check if interrupt is pending. + #[inline] + fn is_pending() -> bool { + Self::IRQ.is_pending() + } + + /// Set interrupt pending. + #[inline] + fn pend() { + Self::IRQ.pend() + } + + /// Unset interrupt pending. + #[inline] + fn unpend() { + Self::IRQ.unpend() + } + + /// Get the priority of the interrupt. + #[inline] + fn get_priority() -> crate::interrupt::Priority { + Self::IRQ.get_priority() + } + + /// Set the interrupt priority. + #[inline] + fn set_priority(prio: crate::interrupt::Priority) { + Self::IRQ.set_priority(prio) + } + } + + $( + #[allow(non_camel_case_types)] + #[doc=stringify!($irqs)] + #[doc=" typelevel interrupt."] + pub enum $irqs {} + impl sealed::Interrupt for $irqs{} + impl Interrupt for $irqs { + const IRQ: super::Interrupt = super::Interrupt::$irqs; + } + )* + + /// Interrupt handler trait. + /// + /// Drivers that need to handle interrupts implement this trait. + /// The user must ensure `on_interrupt()` is called every time the interrupt fires. + /// Drivers must use use [`Binding`] to assert at compile time that the user has done so. + pub trait Handler { + /// Interrupt handler function. + /// + /// Must be called every time the `I` interrupt fires, synchronously from + /// the interrupt handler context. + /// + /// # Safety + /// + /// This function must ONLY be called from the interrupt handler for `I`. + unsafe fn on_interrupt(); + } + + /// Compile-time assertion that an interrupt has been bound to a handler. + /// + /// For the vast majority of cases, you should use the `bind_interrupts!` + /// macro instead of writing `unsafe impl`s of this trait. + /// + /// # Safety + /// + /// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` + /// to be called every time the `I` interrupt fires. + /// + /// This allows drivers to check bindings at compile-time. + pub unsafe trait Binding> {} + } + } + }; } /// Represents an interrupt type that can be configured by embassy to handle /// interrupts. -pub unsafe trait Interrupt { - /// Return the NVIC interrupt number for this interrupt. - fn number() -> u16; - +pub unsafe trait InterruptExt: InterruptNumber + Copy { /// Enable the interrupt. #[inline] - unsafe fn enable() { + unsafe fn enable(self) { compiler_fence(Ordering::SeqCst); - NVIC::unmask(NrWrap(Self::number())) + NVIC::unmask(self) } /// Disable the interrupt. #[inline] - fn disable() { - NVIC::mask(NrWrap(Self::number())); + fn disable(self) { + NVIC::mask(self); compiler_fence(Ordering::SeqCst); } /// Check if interrupt is being handled. #[inline] #[cfg(not(armv6m))] - fn is_active() -> bool { - NVIC::is_active(NrWrap(Self::number())) + fn is_active(self) -> bool { + NVIC::is_active(self) } /// Check if interrupt is enabled. #[inline] - fn is_enabled() -> bool { - NVIC::is_enabled(NrWrap(Self::number())) + fn is_enabled(self) -> bool { + NVIC::is_enabled(self) } /// Check if interrupt is pending. #[inline] - fn is_pending() -> bool { - NVIC::is_pending(NrWrap(Self::number())) + fn is_pending(self) -> bool { + NVIC::is_pending(self) } /// Set interrupt pending. #[inline] - fn pend() { - NVIC::pend(NrWrap(Self::number())) + fn pend(self) { + NVIC::pend(self) } /// Unset interrupt pending. #[inline] - fn unpend() { - NVIC::unpend(NrWrap(Self::number())) + fn unpend(self) { + NVIC::unpend(self) } /// Get the priority of the interrupt. #[inline] - fn get_priority() -> Priority { - Priority::from(NVIC::get_priority(NrWrap(Self::number()))) + fn get_priority(self) -> Priority { + Priority::from(NVIC::get_priority(self)) } /// Set the interrupt priority. #[inline] - fn set_priority(prio: Priority) { + fn set_priority(self, prio: Priority) { critical_section::with(|_| unsafe { let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); - nvic.set_priority(NrWrap(Self::number()), prio.into()) + nvic.set_priority(self, prio.into()) }) } } +unsafe impl InterruptExt for T {} + impl From for Priority { fn from(priority: u8) -> Self { unsafe { mem::transmute(priority & PRIO_MASK) } diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs index 8d521040f..2e0b68d1a 100644 --- a/embassy-lora/src/iv.rs +++ b/embassy-lora/src/iv.rs @@ -1,7 +1,7 @@ #[cfg(feature = "stm32wl")] use embassy_stm32::interrupt; #[cfg(feature = "stm32wl")] -use embassy_stm32::interrupt::*; +use embassy_stm32::interrupt::InterruptExt; #[cfg(feature = "stm32wl")] use embassy_stm32::pac; #[cfg(feature = "stm32wl")] @@ -20,9 +20,9 @@ use lora_phy::mod_traits::InterfaceVariant; pub struct InterruptHandler {} #[cfg(feature = "stm32wl")] -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - interrupt::SUBGHZ_RADIO::disable(); + interrupt::SUBGHZ_RADIO.disable(); IRQ_SIGNAL.signal(()); } } @@ -45,11 +45,11 @@ where { /// Create an InterfaceVariant instance for an stm32wl/sx1262 combination pub fn new( - _irq: impl interrupt::Binding, + _irq: impl interrupt::typelevel::Binding, rf_switch_rx: Option, rf_switch_tx: Option, ) -> Result { - interrupt::SUBGHZ_RADIO::disable(); + interrupt::SUBGHZ_RADIO.disable(); Ok(Self { board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board rf_switch_rx, @@ -95,7 +95,7 @@ where } async fn await_irq(&mut self) -> Result<(), RadioError> { - unsafe { interrupt::SUBGHZ_RADIO::enable() }; + unsafe { interrupt::SUBGHZ_RADIO.enable() }; IRQ_SIGNAL.wait().await; Ok(()) } diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index d7ca1f69c..ba4f13b77 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -156,16 +156,3 @@ pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { 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] -pub fn cortex_m_interrupt(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); - cortex_m_interrupt::run(args, f).unwrap_or_else(|x| x).into() -} - -#[proc_macro] -pub fn cortex_m_interrupt_declare(item: TokenStream) -> TokenStream { - let name = syn::parse_macro_input!(item as syn::Ident); - cortex_m_interrupt_declare::run(name).unwrap_or_else(|x| x).into() -} diff --git a/embassy-macros/src/macros/cortex_m_interrupt.rs b/embassy-macros/src/macros/cortex_m_interrupt.rs deleted file mode 100644 index 13af8ca07..000000000 --- a/embassy-macros/src/macros/cortex_m_interrupt.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::iter; - -use darling::FromMeta; -use proc_macro2::TokenStream; -use quote::quote; -use syn::{ReturnType, Type, Visibility}; - -use crate::util::ctxt::Ctxt; - -#[derive(Debug, FromMeta)] -struct Args {} - -pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result { - let _args = Args::from_list(&args).map_err(|e| e.write_errors())?; - - let ident = f.sig.ident.clone(); - let ident_s = ident.to_string(); - - // XXX should we blacklist other attributes? - - let valid_signature = f.sig.constness.is_none() - && f.vis == Visibility::Inherited - && f.sig.abi.is_none() - && f.sig.inputs.is_empty() - && f.sig.generics.params.is_empty() - && f.sig.generics.where_clause.is_none() - && f.sig.variadic.is_none() - && match f.sig.output { - ReturnType::Default => true, - ReturnType::Type(_, ref ty) => match **ty { - Type::Tuple(ref tuple) => tuple.elems.is_empty(), - Type::Never(..) => true, - _ => false, - }, - }; - - let ctxt = Ctxt::new(); - - if !valid_signature { - ctxt.error_spanned_by( - &f.sig, - "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", - ); - } - - ctxt.check()?; - - f.block.stmts = iter::once( - syn::parse2(quote! {{ - // Check that this interrupt actually exists - let __irq_exists_check: interrupt::#ident; - }}) - .unwrap(), - ) - .chain(f.block.stmts) - .collect(); - - let result = quote!( - #[doc(hidden)] - #[export_name = #ident_s] - #[allow(non_snake_case)] - #f - ); - - Ok(result) -} diff --git a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs b/embassy-macros/src/macros/cortex_m_interrupt_declare.rs deleted file mode 100644 index b317482f5..000000000 --- a/embassy-macros/src/macros/cortex_m_interrupt_declare.rs +++ /dev/null @@ -1,21 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{format_ident, quote}; - -pub fn run(name: syn::Ident) -> Result { - let name = format_ident!("{}", name); - let doc = format!("{} interrupt.", name); - - let result = quote! { - #[doc = #doc] - #[allow(non_camel_case_types)] - pub enum #name{} - unsafe impl ::embassy_cortex_m::interrupt::Interrupt for #name { - fn number() -> u16 { - use cortex_m::interrupt::InterruptNumber; - let irq = InterruptEnum::#name; - irq.number() as u16 - } - } - }; - Ok(result) -} diff --git a/embassy-macros/src/macros/mod.rs b/embassy-macros/src/macros/mod.rs index a5e7a50e6..572094ca6 100644 --- a/embassy-macros/src/macros/mod.rs +++ b/embassy-macros/src/macros/mod.rs @@ -1,4 +1,2 @@ -pub mod cortex_m_interrupt; -pub mod cortex_m_interrupt_declare; pub mod main; pub mod task; diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index b4fe2d874..9bc1c1e7a 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,7 +15,6 @@ use core::slice; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -24,13 +23,13 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::gpio::sealed::Pin; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{self}; +use crate::interrupt::typelevel::Interrupt; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; use crate::timer::{Instance as TimerInstance, Timer}; use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance}; -use crate::{pac, Peripheral}; +use crate::{interrupt, pac, Peripheral}; mod sealed { use super::*; @@ -77,7 +76,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { //trace!("irq: start"); let r = U::regs(); @@ -202,7 +201,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { ppi_ch1: impl Peripheral

+ 'd, ppi_ch2: impl Peripheral

+ 'd, ppi_group: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, config: Config, @@ -237,7 +236,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { ppi_ch1: impl Peripheral

+ 'd, ppi_ch2: impl Peripheral

+ 'd, ppi_group: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index e406c081b..8fbd760dc 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -208,33 +208,29 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(POWER_CLOCK); - declare!(RADIO); - declare!(UARTE0_UART0); - declare!(TWIM0_TWIS0_TWI0); - declare!(SPIM0_SPIS0_SPI0); - declare!(GPIOTE); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(TEMP); - declare!(RNG); - declare!(ECB); - declare!(CCM_AAR); - declare!(WDT); - declare!(RTC1); - declare!(QDEC); - declare!(SWI0_EGU0); - declare!(SWI1_EGU1); - declare!(SWI2); - declare!(SWI3); - declare!(SWI4); - declare!(SWI5); -} +embassy_cortex_m::interrupt_mod!( + POWER_CLOCK, + RADIO, + UARTE0_UART0, + TWIM0_TWIS0_TWI0, + SPIM0_SPIS0_SPI0, + GPIOTE, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + SWI0_EGU0, + SWI1_EGU1, + SWI2, + SWI3, + SWI4, + SWI5, +); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 153795e54..bbf8f7ccf 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -234,36 +234,32 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(POWER_CLOCK); - declare!(RADIO); - declare!(UARTE0_UART0); - declare!(TWIM0_TWIS0_TWI0); - declare!(SPIM0_SPIS0_SPI0); - declare!(GPIOTE); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(TEMP); - declare!(RNG); - declare!(ECB); - declare!(CCM_AAR); - declare!(WDT); - declare!(RTC1); - declare!(QDEC); - declare!(COMP); - declare!(SWI0_EGU0); - declare!(SWI1_EGU1); - declare!(SWI2); - declare!(SWI3); - declare!(SWI4); - declare!(SWI5); - declare!(PWM0); - declare!(PDM); -} +embassy_cortex_m::interrupt_mod!( + POWER_CLOCK, + RADIO, + UARTE0_UART0, + TWIM0_TWIS0_TWI0, + SPIM0_SPIS0_SPI0, + GPIOTE, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + COMP, + SWI0_EGU0, + SWI1_EGU1, + SWI2, + SWI3, + SWI4, + SWI5, + PWM0, + PDM, +); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index a7a7cf58c..31a8dd6af 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -236,36 +236,32 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(POWER_CLOCK); - declare!(RADIO); - declare!(UARTE0_UART0); - declare!(TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); - declare!(SPIM1_SPIS1_SPI1); - declare!(GPIOTE); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(TEMP); - declare!(RNG); - declare!(ECB); - declare!(CCM_AAR); - declare!(WDT); - declare!(RTC1); - declare!(QDEC); - declare!(COMP); - declare!(SWI0_EGU0); - declare!(SWI1_EGU1); - declare!(SWI2); - declare!(SWI3); - declare!(SWI4); - declare!(SWI5); - declare!(PWM0); - declare!(PDM); -} +embassy_cortex_m::interrupt_mod!( + POWER_CLOCK, + RADIO, + UARTE0_UART0, + TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0, + SPIM1_SPIS1_SPI1, + GPIOTE, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + COMP, + SWI0_EGU0, + SWI1_EGU1, + SWI2, + SWI3, + SWI4, + SWI5, + PWM0, + PDM, +); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 14a1b8cc9..6a6f4fcf2 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -224,35 +224,31 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(POWER_CLOCK); - declare!(RADIO); - declare!(UARTE0_UART0); - declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); - declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - declare!(GPIOTE); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(TEMP); - declare!(RNG); - declare!(ECB); - declare!(CCM_AAR); - declare!(WDT); - declare!(RTC1); - declare!(QDEC); - declare!(COMP); - declare!(SWI0_EGU0); - declare!(SWI1_EGU1); - declare!(SWI2_EGU2); - declare!(SWI3_EGU3); - declare!(SWI4_EGU4); - declare!(SWI5_EGU5); - declare!(TIMER3); - declare!(USBD); -} +embassy_cortex_m::interrupt_mod!( + POWER_CLOCK, + RADIO, + UARTE0_UART0, + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + GPIOTE, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + COMP, + SWI0_EGU0, + SWI1_EGU1, + SWI2_EGU2, + SWI3_EGU3, + SWI4_EGU4, + SWI5_EGU5, + TIMER3, + USBD, +); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 83ecd0deb..e43b3d5b2 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -263,46 +263,42 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(POWER_CLOCK); - declare!(RADIO); - declare!(UARTE0_UART0); - declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); - declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - declare!(NFCT); - declare!(GPIOTE); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(TEMP); - declare!(RNG); - declare!(ECB); - declare!(CCM_AAR); - declare!(WDT); - declare!(RTC1); - declare!(QDEC); - declare!(COMP_LPCOMP); - declare!(SWI0_EGU0); - declare!(SWI1_EGU1); - declare!(SWI2_EGU2); - declare!(SWI3_EGU3); - declare!(SWI4_EGU4); - declare!(SWI5_EGU5); - declare!(TIMER3); - declare!(TIMER4); - declare!(PWM0); - declare!(PDM); - declare!(MWU); - declare!(PWM1); - declare!(PWM2); - declare!(SPIM2_SPIS2_SPI2); - declare!(RTC2); - declare!(FPU); - declare!(I2S); -} +embassy_cortex_m::interrupt_mod!( + POWER_CLOCK, + RADIO, + UARTE0_UART0, + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + NFCT, + GPIOTE, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + COMP_LPCOMP, + SWI0_EGU0, + SWI1_EGU1, + SWI2_EGU2, + SWI3_EGU3, + SWI4_EGU4, + SWI5_EGU5, + TIMER3, + TIMER4, + PWM0, + PDM, + MWU, + PWM1, + PWM2, + SPIM2_SPIS2_SPI2, + RTC2, + FPU, + I2S, +); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 5e5db04de..d95e3497c 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -306,50 +306,46 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(POWER_CLOCK); - declare!(RADIO); - declare!(UARTE0_UART0); - declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); - declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - declare!(NFCT); - declare!(GPIOTE); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(TEMP); - declare!(RNG); - declare!(ECB); - declare!(CCM_AAR); - declare!(WDT); - declare!(RTC1); - declare!(QDEC); - declare!(COMP_LPCOMP); - declare!(SWI0_EGU0); - declare!(SWI1_EGU1); - declare!(SWI2_EGU2); - declare!(SWI3_EGU3); - declare!(SWI4_EGU4); - declare!(SWI5_EGU5); - declare!(TIMER3); - declare!(TIMER4); - declare!(PWM0); - declare!(PDM); - declare!(MWU); - declare!(PWM1); - declare!(PWM2); - declare!(SPIM2_SPIS2_SPI2); - declare!(RTC2); - declare!(FPU); - declare!(USBD); - declare!(UARTE1); - declare!(PWM3); - declare!(SPIM3); - declare!(I2S); -} +embassy_cortex_m::interrupt_mod!( + POWER_CLOCK, + RADIO, + UARTE0_UART0, + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + NFCT, + GPIOTE, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + COMP_LPCOMP, + SWI0_EGU0, + SWI1_EGU1, + SWI2_EGU2, + SWI3_EGU3, + SWI4_EGU4, + SWI5_EGU5, + TIMER3, + TIMER4, + PWM0, + PDM, + MWU, + PWM1, + PWM2, + SPIM2_SPIS2_SPI2, + RTC2, + FPU, + USBD, + UARTE1, + PWM3, + SPIM3, + I2S, +); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index f6d33f85c..0094b1220 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -311,52 +311,48 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(POWER_CLOCK); - declare!(RADIO); - declare!(UARTE0_UART0); - declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); - declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); - declare!(NFCT); - declare!(GPIOTE); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(TEMP); - declare!(RNG); - declare!(ECB); - declare!(CCM_AAR); - declare!(WDT); - declare!(RTC1); - declare!(QDEC); - declare!(COMP_LPCOMP); - declare!(SWI0_EGU0); - declare!(SWI1_EGU1); - declare!(SWI2_EGU2); - declare!(SWI3_EGU3); - declare!(SWI4_EGU4); - declare!(SWI5_EGU5); - declare!(TIMER3); - declare!(TIMER4); - declare!(PWM0); - declare!(PDM); - declare!(MWU); - declare!(PWM1); - declare!(PWM2); - declare!(SPIM2_SPIS2_SPI2); - declare!(RTC2); - declare!(FPU); - declare!(USBD); - declare!(UARTE1); - declare!(QSPI); - declare!(CRYPTOCELL); - declare!(PWM3); - declare!(SPIM3); - declare!(I2S); -} +embassy_cortex_m::interrupt_mod!( + POWER_CLOCK, + RADIO, + UARTE0_UART0, + SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, + SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + NFCT, + GPIOTE, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + COMP_LPCOMP, + SWI0_EGU0, + SWI1_EGU1, + SWI2_EGU2, + SWI3_EGU3, + SWI4_EGU4, + SWI5_EGU5, + TIMER3, + TIMER4, + PWM0, + PDM, + MWU, + PWM1, + PWM2, + SPIM2_SPIS2_SPI2, + RTC2, + FPU, + USBD, + UARTE1, + QSPI, + CRYPTOCELL, + PWM3, + SPIM3, + I2S, +); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 34f96800f..c10520051 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -504,50 +504,46 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(FPU); - declare!(CACHE); - declare!(SPU); - declare!(CLOCK_POWER); - declare!(SERIAL0); - declare!(SERIAL1); - declare!(SPIM4); - declare!(SERIAL2); - declare!(SERIAL3); - declare!(GPIOTE0); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(RTC1); - declare!(WDT0); - declare!(WDT1); - declare!(COMP_LPCOMP); - declare!(EGU0); - declare!(EGU1); - declare!(EGU2); - declare!(EGU3); - declare!(EGU4); - declare!(EGU5); - declare!(PWM0); - declare!(PWM1); - declare!(PWM2); - declare!(PWM3); - declare!(PDM0); - declare!(I2S0); - declare!(IPC); - declare!(QSPI); - declare!(NFCT); - declare!(GPIOTE1); - declare!(QDEC0); - declare!(QDEC1); - declare!(USBD); - declare!(USBREGULATOR); - declare!(KMU); - declare!(CRYPTOCELL); -} +embassy_cortex_m::interrupt_mod!( + FPU, + CACHE, + SPU, + CLOCK_POWER, + SERIAL0, + SERIAL1, + SPIM4, + SERIAL2, + SERIAL3, + GPIOTE0, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + RTC1, + WDT0, + WDT1, + COMP_LPCOMP, + EGU0, + EGU1, + EGU2, + EGU3, + EGU4, + EGU5, + PWM0, + PWM1, + PWM2, + PWM3, + PDM0, + I2S0, + IPC, + QSPI, + NFCT, + GPIOTE1, + QDEC0, + QDEC1, + USBD, + USBREGULATOR, + KMU, + CRYPTOCELL, +); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 1e59528cb..a6fb1d4cc 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -340,29 +340,25 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(CLOCK_POWER); - declare!(RADIO); - declare!(RNG); - declare!(GPIOTE); - declare!(WDT); - declare!(TIMER0); - declare!(ECB); - declare!(AAR_CCM); - declare!(TEMP); - declare!(RTC0); - declare!(IPC); - declare!(SERIAL0); - declare!(EGU0); - declare!(RTC1); - declare!(TIMER1); - declare!(TIMER2); - declare!(SWI0); - declare!(SWI1); - declare!(SWI2); - declare!(SWI3); -} +embassy_cortex_m::interrupt_mod!( + CLOCK_POWER, + RADIO, + RNG, + GPIOTE, + WDT, + TIMER0, + ECB, + AAR_CCM, + TEMP, + RTC0, + IPC, + SERIAL0, + EGU0, + RTC1, + TIMER1, + TIMER2, + SWI0, + SWI1, + SWI2, + SWI3, +); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index d2b45114f..b6ae78bbe 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -366,40 +366,36 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -pub mod irqs { - use embassy_cortex_m::interrupt::_export::declare; - - use crate::pac::Interrupt as InterruptEnum; - - declare!(SPU); - declare!(CLOCK_POWER); - declare!(UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); - declare!(UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); - declare!(UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); - declare!(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); - declare!(GPIOTE0); - declare!(SAADC); - declare!(TIMER0); - declare!(TIMER1); - declare!(TIMER2); - declare!(RTC0); - declare!(RTC1); - declare!(WDT); - declare!(EGU0); - declare!(EGU1); - declare!(EGU2); - declare!(EGU3); - declare!(EGU4); - declare!(EGU5); - declare!(PWM0); - declare!(PWM1); - declare!(PWM2); - declare!(PDM); - declare!(PWM3); - declare!(I2S); - declare!(IPC); - declare!(FPU); - declare!(GPIOTE1); - declare!(KMU); - declare!(CRYPTOCELL); -} +embassy_cortex_m::interrupt_mod!( + SPU, + CLOCK_POWER, + UARTE0_SPIM0_SPIS0_TWIM0_TWIS0, + UARTE1_SPIM1_SPIS1_TWIM1_TWIS1, + UARTE2_SPIM2_SPIS2_TWIM2_TWIS2, + UARTE3_SPIM3_SPIS3_TWIM3_TWIS3, + GPIOTE0, + SAADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + RTC1, + WDT, + EGU0, + EGU1, + EGU2, + EGU3, + EGU4, + EGU5, + PWM0, + PWM1, + PWM2, + PDM, + PWM3, + I2S, + IPC, + FPU, + GPIOTE1, + KMU, + CRYPTOCELL, +); diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 2ec5220a7..1d7725bd3 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; -use crate::interrupt::Interrupt; +use crate::interrupt::InterruptExt; use crate::ppi::{Event, Task}; use crate::{interrupt, pac, peripherals}; @@ -75,15 +75,15 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { // Enable interrupts #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))] - type Irq = interrupt::GPIOTE0; + let irq = interrupt::GPIOTE0; #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] - type Irq = interrupt::GPIOTE1; + let irq = interrupt::GPIOTE1; #[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))] - type Irq = interrupt::GPIOTE; + let irq = interrupt::GPIOTE; - Irq::unpend(); - Irq::set_priority(irq_prio); - unsafe { Irq::enable() }; + irq.unpend(); + irq.set_priority(irq_prio); + unsafe { irq.enable() }; let g = regs(); g.events_port.write(|w| w); diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 13db77d3b..fea38c4c0 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -13,10 +13,10 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; use crate::pac::i2s::RegisterBlock; use crate::util::{slice_in_ram_or, slice_ptr_parts}; -use crate::{Peripheral, EASY_DMA_SIZE}; +use crate::{interrupt, Peripheral, EASY_DMA_SIZE}; /// Type alias for `MultiBuffering` with 2 buffers. pub type DoubleBuffering = MultiBuffering; @@ -367,7 +367,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let device = Device::::new(); let s = T::state(); @@ -408,7 +408,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S in master mode pub fn new_master( i2s: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, mck: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, @@ -431,7 +431,7 @@ impl<'d, T: Instance> I2S<'d, T> { /// Create a new I2S in slave mode pub fn new_slave( i2s: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, sck: impl Peripheral

+ 'd, lrck: impl Peripheral

+ 'd, config: Config, @@ -1173,7 +1173,7 @@ pub(crate) mod sealed { /// I2S peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_i2s { @@ -1188,7 +1188,7 @@ macro_rules! impl_i2s { } } impl crate::i2s::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 6b57c2545..a73d22a63 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -93,21 +93,16 @@ pub mod wdt; #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] mod chip; -pub mod interrupt { - //! Interrupt definitions and macros to bind them. - pub use cortex_m::interrupt::{CriticalSection, Mutex}; - pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority}; +pub use crate::chip::interrupt; - pub use crate::chip::irqs::*; - - /// Macro to bind interrupts to handlers. - /// - /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) - /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to - /// prove at compile-time that the right interrupts have been bound. - // developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. - #[macro_export] - macro_rules! bind_interrupts { +/// Macro to bind interrupts to handlers. +/// +/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) +/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to +/// prove at compile-time that the right interrupts have been bound. +// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. +#[macro_export] +macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { $vis struct $name; @@ -116,17 +111,16 @@ pub mod interrupt { #[no_mangle] unsafe extern "C" fn $irq() { $( - <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); )* } $( - unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* )* }; } -} // Reexports @@ -136,7 +130,6 @@ pub use chip::pac; pub(crate) use chip::pac; pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; pub use embassy_cortex_m::executor; -pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; pub mod config { diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 9df685a26..0e30f7002 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -6,7 +6,6 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use futures::future::poll_fn; @@ -14,15 +13,15 @@ use futures::future::poll_fn; use crate::chip::EASY_DMA_SIZE; use crate::gpio::sealed::Pin; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::{self}; -use crate::Peripheral; +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, Peripheral}; /// Interrupt handler. pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { T::regs().intenclr.write(|w| w.end().clear()); T::state().waker.wake(); @@ -53,7 +52,7 @@ impl<'d, T: Instance> Pdm<'d, T> { /// Create PDM driver pub fn new( pdm: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, clk: impl Peripheral

+ 'd, din: impl Peripheral

+ 'd, config: Config, @@ -274,7 +273,7 @@ pub(crate) mod sealed { /// PDM peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_pdm { @@ -289,7 +288,7 @@ macro_rules! impl_pdm { } } impl crate::pdm::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 708f23104..363a255d5 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -8,10 +8,9 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::Interrupt; use crate::ppi::{Event, Task}; use crate::util::slice_in_ram_or; -use crate::{pac, Peripheral}; +use crate::{interrupt, pac, Peripheral}; /// SimplePwm is the traditional pwm interface you're probably used to, allowing /// to simply set a duty cycle across up to four channels. @@ -843,7 +842,7 @@ pub(crate) mod sealed { /// PWM peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_pwm { @@ -854,7 +853,7 @@ macro_rules! impl_pwm { } } impl crate::pwm::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 5761d04e1..8bac87d37 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -10,7 +10,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, Peripheral}; /// Quadrature decoder driver. @@ -50,7 +50,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { T::regs().intenclr.write(|w| w.reportrdy().clear()); T::state().waker.wake(); @@ -61,7 +61,7 @@ impl<'d, T: Instance> Qdec<'d, T> { /// Create a new QDEC. pub fn new( qdec: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, config: Config, @@ -73,7 +73,7 @@ impl<'d, T: Instance> Qdec<'d, T> { /// Create a new QDEC, with a pin for LED output. pub fn new_with_led( qdec: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, a: impl Peripheral

+ 'd, b: impl Peripheral

+ 'd, led: impl Peripheral

+ 'd, @@ -271,7 +271,7 @@ pub(crate) mod sealed { /// qdec peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_qdec { @@ -286,7 +286,7 @@ macro_rules! impl_qdec { } } impl crate::qdec::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 3f48568b3..baefc7967 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -12,12 +12,12 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; pub use crate::pac::qspi::ifconfig0::{ ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode, }; pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; -use crate::Peripheral; +use crate::{interrupt, Peripheral}; /// Deep power-down config. pub struct DeepPowerDownConfig { @@ -120,7 +120,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); @@ -143,7 +143,7 @@ impl<'d, T: Instance> Qspi<'d, T> { /// Create a new QSPI driver. pub fn new( qspi: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, sck: impl Peripheral

+ 'd, csn: impl Peripheral

+ 'd, io0: impl Peripheral

+ 'd, @@ -644,7 +644,7 @@ pub(crate) mod sealed { /// QSPI peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_qspi { @@ -659,7 +659,7 @@ macro_rules! impl_qspi { } } impl crate::qspi::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 7e9b35481..923b8b467 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -12,7 +12,7 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, Peripheral}; /// Interrupt handler. @@ -20,7 +20,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let s = T::state(); let r = T::regs(); @@ -89,7 +89,7 @@ impl<'d, T: Instance> Rng<'d, T> { /// The synchronous API is safe. pub fn new( rng: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { into_ref!(rng); @@ -255,7 +255,7 @@ pub(crate) mod sealed { /// RNG peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_rng { @@ -270,7 +270,7 @@ macro_rules! impl_rng { } } impl crate::rng::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 39764e380..cf3fb9993 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,7 +6,6 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -18,6 +17,7 @@ use saadc::oversample::OVERSAMPLE_A; use saadc::resolution::VAL_A; use self::sealed::Input as _; +use crate::interrupt::InterruptExt; use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::{interrupt, pac, peripherals, Peripheral}; @@ -33,7 +33,7 @@ pub struct InterruptHandler { _private: (), } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = unsafe { &*SAADC::ptr() }; @@ -144,7 +144,7 @@ impl<'d, const N: usize> Saadc<'d, N> { /// Create a new SAADC driver. pub fn new( saadc: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding + 'd, + _irq: impl interrupt::typelevel::Binding + 'd, config: Config, channel_configs: [ChannelConfig; N], ) -> Self { @@ -189,8 +189,8 @@ impl<'d, const N: usize> Saadc<'d, N> { // Disable all events interrupts r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); - interrupt::SAADC::unpend(); - unsafe { interrupt::SAADC::enable() }; + interrupt::SAADC.unpend(); + unsafe { interrupt::SAADC.enable() }; Self { _p: saadc } } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index bb9cda323..66bbd1a8f 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -15,9 +15,9 @@ pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; -use crate::{pac, Peripheral}; +use crate::{interrupt, pac, Peripheral}; /// SPIM error #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -63,7 +63,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); @@ -84,7 +84,7 @@ impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver. pub fn new( spim: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, @@ -103,7 +103,7 @@ impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver, capable of TX only (MOSI only). pub fn new_txonly( spim: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, sck: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, config: Config, @@ -115,7 +115,7 @@ impl<'d, T: Instance> Spim<'d, T> { /// Create a new SPIM driver, capable of RX only (MISO only). pub fn new_rxonly( spim: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, config: Config, @@ -408,7 +408,7 @@ pub(crate) mod sealed { /// SPIM peripheral instance pub trait Instance: Peripheral

+ sealed::Instance + 'static { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_spim { @@ -423,7 +423,7 @@ macro_rules! impl_spim { } } impl crate::spim::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index a1d6803ed..aa438415a 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -13,9 +13,9 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin}; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; -use crate::{pac, Peripheral}; +use crate::{interrupt, pac, Peripheral}; /// SPIS error #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -68,7 +68,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); @@ -94,7 +94,7 @@ impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver. pub fn new( spis: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, @@ -115,7 +115,7 @@ impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver, capable of TX only (MISO only). pub fn new_txonly( spis: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, miso: impl Peripheral

+ 'd, @@ -128,7 +128,7 @@ impl<'d, T: Instance> Spis<'d, T> { /// Create a new SPIS driver, capable of RX only (MOSI only). pub fn new_rxonly( spis: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, cs: impl Peripheral

+ 'd, sck: impl Peripheral

+ 'd, mosi: impl Peripheral

+ 'd, @@ -480,7 +480,7 @@ pub(crate) mod sealed { /// SPIS peripheral instance pub trait Instance: Peripheral

+ sealed::Instance + 'static { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_spis { @@ -495,7 +495,7 @@ macro_rules! impl_spis { } } impl crate::spis::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 8a127efc5..491e92c04 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -8,7 +8,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; -use crate::interrupt::Interrupt; +use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; use crate::{interrupt, pac, Peripheral}; @@ -17,7 +17,7 @@ pub struct InterruptHandler { _private: (), } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = unsafe { &*pac::TEMP::PTR }; r.intenclr.write(|w| w.datardy().clear()); @@ -36,13 +36,13 @@ impl<'d> Temp<'d> { /// Create a new temperature sensor driver. pub fn new( _peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding + 'd, + _irq: impl interrupt::typelevel::Binding + 'd, ) -> Self { into_ref!(_peri); // Enable interrupt that signals temperature values - interrupt::TEMP::unpend(); - unsafe { interrupt::TEMP::enable() }; + interrupt::TEMP.unpend(); + unsafe { interrupt::TEMP.enable() }; Self { _peri } } diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 4feff8a75..f993d7b2e 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -7,7 +7,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; use embassy_time::driver::{AlarmHandle, Driver}; -use crate::interrupt::Interrupt; +use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; fn rtc() -> &'static pac::rtc0::RegisterBlock { @@ -142,8 +142,8 @@ impl RtcDriver { // Wait for clear while r.counter.read().bits() != 0 {} - interrupt::RTC1::set_priority(irq_prio); - unsafe { interrupt::RTC1::enable() }; + interrupt::RTC1.set_priority(irq_prio); + unsafe { interrupt::RTC1.enable() }; } fn on_interrupt(&self) { diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 2a0e16a50..dc3757856 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -8,7 +8,6 @@ use embassy_hal_common::{into_ref, PeripheralRef}; -use crate::interrupt::Interrupt; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; @@ -29,7 +28,7 @@ pub(crate) mod sealed { /// Basic Timer instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: crate::interrupt::typelevel::Interrupt; } /// Extended timer instance. @@ -44,7 +43,7 @@ macro_rules! impl_timer { } } impl crate::timer::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; ($type:ident, $pac_type:ident, $irq:ident) => { diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index dea398a67..2ad0d19b1 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -16,9 +16,9 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; use crate::util::{slice_in_ram, slice_in_ram_or}; -use crate::{gpio, pac, Peripheral}; +use crate::{gpio, interrupt, pac, Peripheral}; /// TWI frequency #[derive(Clone, Copy)] @@ -98,7 +98,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); @@ -123,7 +123,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// Create a new TWI driver. pub fn new( twim: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, @@ -750,7 +750,7 @@ pub(crate) mod sealed { /// TWIM peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_twim { @@ -765,7 +765,7 @@ macro_rules! impl_twim { } } impl crate::twim::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 752a8c046..a115d5616 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -15,9 +15,9 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; use crate::util::slice_in_ram_or; -use crate::{gpio, pac, Peripheral}; +use crate::{gpio, interrupt, pac, Peripheral}; /// TWIS config. #[non_exhaustive] @@ -114,7 +114,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); @@ -143,7 +143,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Create a new TWIS driver. pub fn new( twis: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, sda: impl Peripheral

+ 'd, scl: impl Peripheral

+ 'd, config: Config, @@ -778,7 +778,7 @@ pub(crate) mod sealed { /// TWIS peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_twis { @@ -793,7 +793,7 @@ macro_rules! impl_twis { } } impl crate::twis::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 6c6941ee8..85a951ae0 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -27,11 +27,11 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::sealed::Pin as _; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; -use crate::{pac, Peripheral}; +use crate::{interrupt, pac, Peripheral}; /// UARTE config. #[derive(Clone)] @@ -68,7 +68,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); @@ -108,7 +108,7 @@ impl<'d, T: Instance> Uarte<'d, T> { /// Create a new UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, config: Config, @@ -120,7 +120,7 @@ impl<'d, T: Instance> Uarte<'d, T> { /// Create a new UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rxd: impl Peripheral

+ 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, @@ -313,7 +313,7 @@ impl<'d, T: Instance> UarteTx<'d, T> { /// Create a new tx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, txd: impl Peripheral

+ 'd, config: Config, ) -> Self { @@ -324,7 +324,7 @@ impl<'d, T: Instance> UarteTx<'d, T> { /// Create a new tx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, txd: impl Peripheral

+ 'd, cts: impl Peripheral

+ 'd, config: Config, @@ -509,7 +509,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { /// Create a new rx-only UARTE without hardware flow control pub fn new( uarte: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rxd: impl Peripheral

+ 'd, config: Config, ) -> Self { @@ -520,7 +520,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { /// Create a new rx-only UARTE with hardware flow control (RTS/CTS) pub fn new_with_rtscts( uarte: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rxd: impl Peripheral

+ 'd, rts: impl Peripheral

+ 'd, config: Config, @@ -889,7 +889,7 @@ pub(crate) mod sealed { /// UARTE peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_uarte { @@ -908,7 +908,7 @@ macro_rules! impl_uarte { } } impl crate::uarte::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 3c62b4452..76cf40ac7 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -18,9 +18,9 @@ use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo use pac::usbd::RegisterBlock; use self::vbus_detect::VbusDetect; -use crate::interrupt::{self, Interrupt}; +use crate::interrupt::typelevel::Interrupt; use crate::util::slice_in_ram; -use crate::{pac, Peripheral}; +use crate::{interrupt, pac, Peripheral}; const NEW_AW: AtomicWaker = AtomicWaker::new(); static BUS_WAKER: AtomicWaker = NEW_AW; @@ -34,7 +34,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let regs = T::regs(); @@ -98,7 +98,7 @@ impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { /// Create a new USB driver. pub fn new( usb: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, vbus_detect: V, ) -> Self { into_ref!(usb); @@ -804,7 +804,7 @@ pub(crate) mod sealed { /// USB peripheral instance. pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { /// Interrupt for this peripheral. - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_usb { @@ -815,7 +815,7 @@ macro_rules! impl_usb { } } impl crate::usb::Instance for peripherals::$type { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs index a6a959905..a05e5aa52 100644 --- a/embassy-nrf/src/usb/vbus_detect.rs +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -7,8 +7,8 @@ use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; use super::BUS_WAKER; -use crate::interrupt::{self, Interrupt}; -use crate::pac; +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, pac}; /// Trait for detecting USB VBUS power. /// @@ -29,9 +29,9 @@ pub trait VbusDetect { } #[cfg(not(feature = "_nrf5340"))] -type UsbRegIrq = interrupt::POWER_CLOCK; +type UsbRegIrq = interrupt::typelevel::POWER_CLOCK; #[cfg(feature = "_nrf5340")] -type UsbRegIrq = interrupt::USBREGULATOR; +type UsbRegIrq = interrupt::typelevel::USBREGULATOR; #[cfg(not(feature = "_nrf5340"))] type UsbRegPeri = pac::POWER; @@ -43,7 +43,7 @@ pub struct InterruptHandler { _private: (), } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let regs = unsafe { &*UsbRegPeri::ptr() }; @@ -77,7 +77,7 @@ static POWER_WAKER: AtomicWaker = AtomicWaker::new(); impl HardwareVbusDetect { /// Create a new `VbusDetectNative`. - pub fn new(_irq: impl interrupt::Binding + 'static) -> Self { + pub fn new(_irq: impl interrupt::typelevel::Binding + 'static) -> Self { let regs = unsafe { &*UsbRegPeri::ptr() }; UsbRegIrq::unpend(); diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 86a353670..f29c4dfe1 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -3,14 +3,14 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::{Binding, Interrupt}; use embassy_sync::waitqueue::AtomicWaker; use embedded_hal_02::adc::{Channel, OneShot}; use crate::gpio::Pin; -use crate::interrupt::{self, ADC_IRQ_FIFO}; +use crate::interrupt::typelevel::Binding; +use crate::interrupt::InterruptExt; use crate::peripherals::ADC; -use crate::{pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, Peripheral}; static WAKER: AtomicWaker = AtomicWaker::new(); #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -47,7 +47,7 @@ impl<'d> Adc<'d> { pub fn new( _inner: impl Peripheral

+ 'd, - _irq: impl Binding, + _irq: impl Binding, _config: Config, ) -> Self { unsafe { @@ -62,10 +62,8 @@ impl<'d> Adc<'d> { } // Setup IRQ - unsafe { - ADC_IRQ_FIFO::unpend(); - ADC_IRQ_FIFO::enable(); - }; + interrupt::ADC_IRQ_FIFO.unpend(); + unsafe { interrupt::ADC_IRQ_FIFO.enable() }; Self { phantom: PhantomData } } @@ -164,7 +162,7 @@ pub struct InterruptHandler { _empty: (), } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = Adc::regs(); r.inte().write(|w| w.set_fifo(false)); diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 1cbb4651a..042ca99a9 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -4,11 +4,11 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::vals::DataSize; +use crate::interrupt::InterruptExt; use crate::pac::dma::vals; use crate::{interrupt, pac, peripherals}; @@ -29,12 +29,12 @@ unsafe fn DMA_IRQ_0() { } pub(crate) unsafe fn init() { - interrupt::DMA_IRQ_0::disable(); - interrupt::DMA_IRQ_0::set_priority(interrupt::Priority::P3); + interrupt::DMA_IRQ_0.disable(); + interrupt::DMA_IRQ_0.set_priority(interrupt::Priority::P3); pac::DMA.inte0().write(|w| w.set_inte0(0xFFFF)); - interrupt::DMA_IRQ_0::enable(); + interrupt::DMA_IRQ_0.enable(); } pub unsafe fn read<'a, C: Channel, W: Word>( diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 91cef8609..ebd932f50 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -3,10 +3,10 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use crate::interrupt::InterruptExt; use crate::pac::common::{Reg, RW}; use crate::pac::SIO; use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; @@ -137,9 +137,9 @@ pub enum InterruptTrigger { } pub(crate) unsafe fn init() { - interrupt::IO_IRQ_BANK0::disable(); - interrupt::IO_IRQ_BANK0::set_priority(interrupt::Priority::P3); - interrupt::IO_IRQ_BANK0::enable(); + interrupt::IO_IRQ_BANK0.disable(); + interrupt::IO_IRQ_BANK0.set_priority(interrupt::Priority::P3); + interrupt::IO_IRQ_BANK0.enable(); } #[interrupt] diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 124f1c00a..ce9a082a2 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -2,14 +2,14 @@ use core::future; use core::marker::PhantomData; use core::task::Poll; -use embassy_cortex_m::interrupt::{self, Binding, Interrupt}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; -use crate::{pac, peripherals, Peripheral}; +use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::{interrupt, pac, peripherals, Peripheral}; /// I2C error abort reason #[derive(Debug)] @@ -312,7 +312,7 @@ pub struct InterruptHandler { _uart: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { // Mask interrupts and wake any task waiting for this interrupt unsafe fn on_interrupt() { let i2c = T::regs(); @@ -760,14 +760,15 @@ fn i2c_reserved_addr(addr: u16) -> bool { } mod sealed { - use embassy_cortex_m::interrupt::Interrupt; use embassy_sync::waitqueue::AtomicWaker; + use crate::interrupt; + pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; fn regs() -> crate::pac::i2c::I2c; fn reset() -> crate::pac::resets::regs::Peripherals; @@ -803,7 +804,7 @@ macro_rules! impl_instance { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; #[inline] fn regs() -> pac::i2c::I2c { diff --git a/embassy-rp/src/interrupt.rs b/embassy-rp/src/interrupt.rs deleted file mode 100644 index c9298644d..000000000 --- a/embassy-rp/src/interrupt.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Interrupt definitions and macros to bind them. -pub use cortex_m::interrupt::{CriticalSection, Mutex}; -use embassy_cortex_m::interrupt::_export::declare; -pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority}; - -use crate::pac::Interrupt as InterruptEnum; -declare!(TIMER_IRQ_0); -declare!(TIMER_IRQ_1); -declare!(TIMER_IRQ_2); -declare!(TIMER_IRQ_3); -declare!(PWM_IRQ_WRAP); -declare!(USBCTRL_IRQ); -declare!(XIP_IRQ); -declare!(PIO0_IRQ_0); -declare!(PIO0_IRQ_1); -declare!(PIO1_IRQ_0); -declare!(PIO1_IRQ_1); -declare!(DMA_IRQ_0); -declare!(DMA_IRQ_1); -declare!(IO_IRQ_BANK0); -declare!(IO_IRQ_QSPI); -declare!(SIO_IRQ_PROC0); -declare!(SIO_IRQ_PROC1); -declare!(CLOCKS_IRQ); -declare!(SPI0_IRQ); -declare!(SPI1_IRQ); -declare!(UART0_IRQ); -declare!(UART1_IRQ); -declare!(ADC_IRQ_FIFO); -declare!(I2C0_IRQ); -declare!(I2C1_IRQ); -declare!(RTC_IRQ); -declare!(SWI_IRQ_0); -declare!(SWI_IRQ_1); -declare!(SWI_IRQ_2); -declare!(SWI_IRQ_3); -declare!(SWI_IRQ_4); -declare!(SWI_IRQ_5); - -/// Macro to bind interrupts to handlers. -/// -/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) -/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to -/// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. -#[macro_export] -macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { - $vis struct $name; - - $( - #[allow(non_snake_case)] - #[no_mangle] - unsafe extern "C" fn $irq() { - $( - <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); - )* - } - - $( - unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} - )* - )* - }; -} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 4e4542d70..70a410ef9 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -16,7 +16,6 @@ pub mod flash; mod float; pub mod gpio; pub mod i2c; -pub mod interrupt; pub mod multicore; pub mod pwm; mod reset; @@ -38,13 +37,74 @@ pub mod relocate; // Reexports pub use embassy_cortex_m::executor; -pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use rp_pac as pac; +embassy_cortex_m::interrupt_mod!( + TIMER_IRQ_0, + TIMER_IRQ_1, + TIMER_IRQ_2, + TIMER_IRQ_3, + PWM_IRQ_WRAP, + USBCTRL_IRQ, + XIP_IRQ, + PIO0_IRQ_0, + PIO0_IRQ_1, + PIO1_IRQ_0, + PIO1_IRQ_1, + DMA_IRQ_0, + DMA_IRQ_1, + IO_IRQ_BANK0, + IO_IRQ_QSPI, + SIO_IRQ_PROC0, + SIO_IRQ_PROC1, + CLOCKS_IRQ, + SPI0_IRQ, + SPI1_IRQ, + UART0_IRQ, + UART1_IRQ, + ADC_IRQ_FIFO, + I2C0_IRQ, + I2C1_IRQ, + RTC_IRQ, + SWI_IRQ_0, + SWI_IRQ_1, + SWI_IRQ_2, + SWI_IRQ_3, + SWI_IRQ_4, + SWI_IRQ_5, +); + +/// Macro to bind interrupts to handlers. +/// +/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) +/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to +/// prove at compile-time that the right interrupts have been bound. +// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. +#[macro_export] +macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + )* + } + + $( + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} + )* + )* + }; +} + embassy_hal_common::peripherals! { PIN_0, PIN_1, diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 807fda57b..e1246ce8f 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -50,7 +50,7 @@ use core::mem::ManuallyDrop; use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; -use crate::interrupt::Interrupt; +use crate::interrupt::InterruptExt; use crate::peripherals::CORE1; use crate::{gpio, interrupt, pac}; @@ -156,7 +156,7 @@ where IS_CORE1_INIT.store(true, Ordering::Release); // Enable fifo interrupt on CORE1 for `pause` functionality. - unsafe { interrupt::SIO_IRQ_PROC1::enable() }; + unsafe { interrupt::SIO_IRQ_PROC1.enable() }; entry() } diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 93e5bd34b..f0a5c17a9 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,7 +5,6 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::extra::U8; @@ -17,6 +16,7 @@ use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; +use crate::interrupt::InterruptExt; use crate::pac::dma::vals::TreqSel; use crate::relocate::RelocatedProgram; use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; @@ -110,15 +110,15 @@ unsafe fn PIO1_IRQ_0() { } pub(crate) unsafe fn init() { - interrupt::PIO0_IRQ_0::disable(); - interrupt::PIO0_IRQ_0::set_priority(interrupt::Priority::P3); + interrupt::PIO0_IRQ_0.disable(); + interrupt::PIO0_IRQ_0.set_priority(interrupt::Priority::P3); pac::PIO0.irqs(0).inte().write(|m| m.0 = 0); - interrupt::PIO0_IRQ_0::enable(); + interrupt::PIO0_IRQ_0.enable(); - interrupt::PIO1_IRQ_0::disable(); - interrupt::PIO1_IRQ_0::set_priority(interrupt::Priority::P3); + interrupt::PIO1_IRQ_0.disable(); + interrupt::PIO1_IRQ_0.set_priority(interrupt::Priority::P3); pac::PIO1.irqs(0).inte().write(|m| m.0 = 0); - interrupt::PIO1_IRQ_0::enable(); + interrupt::PIO1_IRQ_0.enable(); } /// Future that waits for TX-FIFO to become writable diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 68793950f..37f86c930 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time::driver::{AlarmHandle, Driver}; -use crate::interrupt::Interrupt; +use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; struct AlarmState { @@ -145,10 +145,10 @@ pub unsafe fn init() { w.set_alarm(2, true); w.set_alarm(3, true); }); - interrupt::TIMER_IRQ_0::enable(); - interrupt::TIMER_IRQ_1::enable(); - interrupt::TIMER_IRQ_2::enable(); - interrupt::TIMER_IRQ_3::enable(); + interrupt::TIMER_IRQ_0.enable(); + interrupt::TIMER_IRQ_1.enable(); + interrupt::TIMER_IRQ_2.enable(); + interrupt::TIMER_IRQ_3.enable(); } #[interrupt] diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index bb808c467..6660d5dc9 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -3,14 +3,14 @@ use core::slice; use core::task::Poll; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_cortex_m::interrupt::{self, Binding, Interrupt}; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; use super::*; use crate::clocks::clk_peri_freq; -use crate::RegExt; +use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::{interrupt, RegExt}; pub struct State { tx_waker: AtomicWaker, @@ -485,7 +485,7 @@ pub struct BufferedInterruptHandler { _uart: PhantomData, } -impl interrupt::Handler for BufferedInterruptHandler { +impl interrupt::typelevel::Handler for BufferedInterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); if r.uartdmacr().read().rxdmae() { diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index a83d94e49..5e3ae8a25 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; -use embassy_cortex_m::interrupt::{self, Binding, Interrupt}; use embassy_futures::select::{select, Either}; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -14,8 +13,9 @@ use crate::clocks::clk_peri_freq; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; +use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::pac::io::vals::{Inover, Outover}; -use crate::{pac, peripherals, Peripheral, RegExt}; +use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; #[cfg(feature = "nightly")] mod buffered; @@ -332,7 +332,7 @@ pub struct InterruptHandler { _uart: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let uart = T::regs(); if !uart.uartdmacr().read().rxdmae() { @@ -930,7 +930,7 @@ mod sealed { const TX_DREQ: u8; const RX_DREQ: u8; - type Interrupt: crate::interrupt::Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; fn regs() -> pac::uart::Uart; @@ -968,7 +968,7 @@ macro_rules! impl_instance { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; fn regs() -> pac::uart::Uart { pac::$inst diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index cc88226df..9fb0dfb70 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -4,15 +4,14 @@ use core::slice; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::{self, Binding}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{ Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; -use crate::interrupt::Interrupt; -use crate::{pac, peripherals, Peripheral, RegExt}; +use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; pub(crate) mod sealed { pub trait Instance { @@ -22,7 +21,7 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + 'static { - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } impl crate::usb::sealed::Instance for peripherals::USB { @@ -35,7 +34,7 @@ impl crate::usb::sealed::Instance for peripherals::USB { } impl crate::usb::Instance for peripherals::USB { - type Interrupt = crate::interrupt::USBCTRL_IRQ; + type Interrupt = crate::interrupt::typelevel::USBCTRL_IRQ; } const EP_COUNT: usize = 16; @@ -249,7 +248,7 @@ pub struct InterruptHandler { _uart: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let regs = T::regs(); //let x = regs.istr().read().0; diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 730c78f5e..267fec387 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -160,13 +160,11 @@ fn main() { } g.extend(quote! { - pub mod interrupt { - use crate::pac::Interrupt as InterruptEnum; - use embassy_cortex_m::interrupt::_export::declare; + embassy_cortex_m::interrupt_mod!( #( - declare!(#irqs); + #irqs, )* - } + ); }); // ======== diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index c13915a1b..41305d273 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -8,7 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::Speed; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, Peripheral}; /// Interrupt handler. @@ -16,7 +16,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let ris = crate::pac::DCMI.ris().read(); if ris.err_ris() { @@ -119,7 +119,7 @@ where pub fn new_8bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -143,7 +143,7 @@ where pub fn new_10bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -169,7 +169,7 @@ where pub fn new_12bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -197,7 +197,7 @@ where pub fn new_14bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -227,7 +227,7 @@ where pub fn new_es_8bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -249,7 +249,7 @@ where pub fn new_es_10bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -273,7 +273,7 @@ where pub fn new_es_12bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -299,7 +299,7 @@ where pub fn new_es_14bit( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, d0: impl Peripheral

> + 'd, d1: impl Peripheral

> + 'd, d2: impl Peripheral

> + 'd, @@ -570,7 +570,7 @@ mod sealed { } pub trait Instance: sealed::Instance + 'static { - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } pin_trait!(D0Pin, Instance); @@ -602,7 +602,7 @@ macro_rules! impl_peripheral { } impl Instance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; } diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 7a1ecda35..83ab4b18f 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -14,7 +14,7 @@ use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::pac; use crate::pac::bdma::{regs, vals}; @@ -70,8 +70,8 @@ static STATE: State = State::new(); pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { - crate::interrupt::$irq::set_priority(irq_priority); - crate::interrupt::$irq::enable(); + crate::interrupt::typelevel::$irq::set_priority(irq_priority); + crate::interrupt::typelevel::$irq::enable(); }; } crate::_generated::init_bdma(); diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 3b602b991..17313b310 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -13,7 +13,7 @@ use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::pac::dma::{regs, vals}; use crate::{interrupt, pac}; @@ -149,8 +149,8 @@ static STATE: State = State::new(); pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { - interrupt::$irq::set_priority(irq_priority); - interrupt::$irq::enable(); + interrupt::typelevel::$irq::set_priority(irq_priority); + interrupt::typelevel::$irq::enable(); }; } crate::_generated::init_dma(); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 7f8b82b46..07fef360f 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -56,8 +56,8 @@ static STATE: State = State::new(); pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { - crate::interrupt::$irq::set_priority(irq_priority); - crate::interrupt::$irq::enable(); + crate::interrupt::typelevel::$irq::set_priority(irq_priority); + crate::interrupt::typelevel::$irq::enable(); }; } crate::_generated::init_gpdma(); diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index a5f1a268d..540cdd027 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -5,7 +5,6 @@ mod tx_desc; use core::sync::atomic::{fence, Ordering}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; @@ -14,6 +13,7 @@ pub(crate) use self::tx_desc::{TDes, TDesRing}; use super::*; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; use crate::gpio::AnyPin; +use crate::interrupt::InterruptExt; #[cfg(eth_v1a)] use crate::pac::AFIO; #[cfg(any(eth_v1b, eth_v1c))] @@ -24,7 +24,7 @@ use crate::{interrupt, Peripheral}; /// Interrupt handler. pub struct InterruptHandler {} -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { WAKER.wake(); @@ -100,7 +100,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { pub fn new( queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding + 'd, + _irq: impl interrupt::typelevel::Binding + 'd, ref_clk: impl Peripheral

> + 'd, mdio: impl Peripheral

> + 'd, mdc: impl Peripheral

> + 'd, @@ -267,8 +267,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { P::phy_reset(&mut this); P::phy_init(&mut this); - interrupt::ETH::unpend(); - interrupt::ETH::enable(); + interrupt::ETH.unpend(); + interrupt::ETH.enable(); this } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 9efa436ac..3e45eafd5 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -2,20 +2,20 @@ mod descriptors; use core::sync::atomic::{fence, Ordering}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; use super::*; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Speed}; +use crate::interrupt::InterruptExt; use crate::pac::ETH; use crate::{interrupt, Peripheral}; /// Interrupt handler. pub struct InterruptHandler {} -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { WAKER.wake(); @@ -64,7 +64,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { pub fn new( queue: &'d mut PacketQueue, peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding + 'd, + _irq: impl interrupt::typelevel::Binding + 'd, ref_clk: impl Peripheral

> + 'd, mdio: impl Peripheral

> + 'd, mdc: impl Peripheral

> + 'd, @@ -238,8 +238,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { P::phy_reset(&mut this); P::phy_init(&mut this); - interrupt::ETH::unpend(); - interrupt::ETH::enable(); + interrupt::ETH.unpend(); + interrupt::ETH.enable(); this } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index c2fa31b20..a2ed07093 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -354,13 +354,13 @@ impl_exti!(EXTI15, 15); macro_rules! enable_irq { ($e:ident) => { - crate::interrupt::$e::enable(); + crate::interrupt::typelevel::$e::enable(); }; } /// safety: must be called only once pub(crate) unsafe fn init() { - use crate::interrupt::Interrupt; + use crate::interrupt::typelevel::Interrupt; foreach_exti_irq!(enable_irq); diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 872614d4e..70a5ded62 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; use atomic_polyfill::{fence, Ordering}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -11,6 +10,7 @@ use super::{ blocking_read, ensure_sector_aligned, family, get_sector, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, WRITE_SIZE, }; +use crate::interrupt::InterruptExt; use crate::peripherals::FLASH; use crate::{interrupt, Peripheral}; @@ -19,12 +19,12 @@ pub(super) static REGION_ACCESS: Mutex = Mutex::new impl<'d> Flash<'d, Async> { pub fn new( p: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding + 'd, + _irq: impl interrupt::typelevel::Binding + 'd, ) -> Self { into_ref!(p); - crate::interrupt::FLASH::unpend(); - unsafe { crate::interrupt::FLASH::enable() }; + crate::interrupt::FLASH.unpend(); + unsafe { crate::interrupt::FLASH.enable() }; Self { inner: p, @@ -49,7 +49,7 @@ impl<'d> Flash<'d, Async> { /// Interrupt handler pub struct InterruptHandler; -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { family::on_interrupt(); } diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f898fcc8b..b35678ed9 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -1,6 +1,6 @@ #![macro_use] -use crate::interrupt::Interrupt; +use crate::interrupt; #[cfg_attr(i2c_v1, path = "v1.rs")] #[cfg_attr(i2c_v2, path = "v2.rs")] @@ -35,7 +35,7 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + 'static { - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } pin_trait!(SclPin, Instance); @@ -57,7 +57,7 @@ foreach_interrupt!( } impl Instance for peripherals::$inst { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; ); diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index b9be2e587..e04038886 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -16,7 +16,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() {} } @@ -57,7 +57,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { _peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, freq: Hertz, diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 10f57f700..1aaf2b46b 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -3,7 +3,6 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -13,6 +12,7 @@ use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; +use crate::interrupt::typelevel::Interrupt; use crate::pac::i2c; use crate::time::Hertz; use crate::{interrupt, Peripheral}; @@ -22,7 +22,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let regs = T::regs(); let isr = regs.isr().read(); @@ -78,7 +78,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { peri: impl Peripheral

+ 'd, scl: impl Peripheral

> + 'd, sda: impl Peripheral

> + 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, freq: Hertz, diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 75d8af3dd..b42864567 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -72,46 +72,39 @@ pub(crate) mod _generated { include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } -pub mod interrupt { - //! Interrupt definitions and macros to bind them. - pub use cortex_m::interrupt::{CriticalSection, Mutex}; - pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority}; +pub use crate::_generated::interrupt; - pub use crate::_generated::interrupt::*; +/// Macro to bind interrupts to handlers. +/// +/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) +/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to +/// prove at compile-time that the right interrupts have been bound. +// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. +#[macro_export] +macro_rules! bind_interrupts { + ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + $vis struct $name; - /// Macro to bind interrupts to handlers. - /// - /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) - /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to - /// prove at compile-time that the right interrupts have been bound. - // developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. - #[macro_export] - macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { - $vis struct $name; + $( + #[allow(non_snake_case)] + #[no_mangle] + unsafe extern "C" fn $irq() { + $( + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + )* + } $( - #[allow(non_snake_case)] - #[no_mangle] - unsafe extern "C" fn $irq() { - $( - <$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); - )* - } - - $( - unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} - )* + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* - }; - } + )* + }; } // Reexports pub use _generated::{peripherals, Peripherals}; pub use embassy_cortex_m::executor; 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")] pub use stm32_metapac as pac; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 3cc17aa68..28eb49ab6 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -14,7 +14,7 @@ use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, use crate::dma::NoDma; use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::{AnyPin, Pull, Speed}; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -42,7 +42,7 @@ impl InterruptHandler { } } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { Self::data_interrupts(false); T::state().wake(); @@ -276,7 +276,7 @@ pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma = NoDma> { impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { pub fn new_1bit( sdmmc: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, dma: impl Peripheral

+ 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, @@ -310,7 +310,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { pub fn new_4bit( sdmmc: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, dma: impl Peripheral

+ 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, @@ -356,7 +356,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { pub fn new_1bit( sdmmc: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -389,7 +389,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { pub fn new_4bit( sdmmc: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, clk: impl Peripheral

> + 'd, cmd: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -1401,7 +1401,7 @@ pub(crate) mod sealed { use super::*; pub trait Instance { - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; fn regs() -> RegBlock; fn state() -> &'static AtomicWaker; @@ -1490,7 +1490,7 @@ cfg_if::cfg_if! { foreach_peripheral!( (sdmmc, $inst:ident) => { impl sealed::Instance for peripherals::$inst { - type Interrupt = crate::interrupt::$inst; + type Interrupt = crate::interrupt::typelevel::$inst; fn regs() -> RegBlock { crate::pac::$inst diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index bab700993..8f282bafb 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -11,7 +11,7 @@ use embassy_time::driver::{AlarmHandle, Driver}; use embassy_time::TICK_HZ; use stm32_metapac::timer::regs; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::{Basic16bitInstance as BasicInstance, GeneralPurpose16bitInstance as Instance}; diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 772c67686..14db97024 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,6 +1,6 @@ use stm32_metapac::timer::vals; -use crate::interrupt::Interrupt; +use crate::interrupt; use crate::rcc::sealed::RccPeripheral as __RccPeri; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -13,7 +13,7 @@ pub mod low_level { pub(crate) mod sealed { use super::*; pub trait Basic16bitInstance: RccPeripheral { - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; fn regs() -> crate::pac::timer::TimBasic; @@ -57,7 +57,7 @@ pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} macro_rules! impl_basic_16bit_timer { ($inst:ident, $irq:ident) => { impl sealed::Basic16bitInstance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; fn regs() -> crate::pac::timer::TimBasic { crate::pac::timer::TimBasic(crate::pac::$inst.0) diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs index efbbf2d1d..d39c78b2c 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32/src/tl_mbox/mod.rs @@ -2,7 +2,6 @@ use core::mem::MaybeUninit; use atomic_polyfill::{compiler_fence, Ordering}; use bit_field::BitField; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -15,6 +14,7 @@ use self::shci::{shci_ble_init, ShciBleInitCmdParam}; use self::sys::Sys; use self::unsafe_linked_list::LinkedListNode; use crate::interrupt; +use crate::interrupt::InterruptExt; use crate::peripherals::IPCC; pub use crate::tl_mbox::ipcc::Config; use crate::tl_mbox::ipcc::Ipcc; @@ -63,7 +63,7 @@ pub struct FusInfoTable { /// Interrupt handler. pub struct ReceiveInterruptHandler {} -impl interrupt::Handler for ReceiveInterruptHandler { +impl interrupt::typelevel::Handler for ReceiveInterruptHandler { unsafe fn on_interrupt() { // info!("ipcc rx interrupt"); @@ -79,7 +79,7 @@ impl interrupt::Handler for ReceiveInterruptHandler { pub struct TransmitInterruptHandler {} -impl interrupt::Handler for TransmitInterruptHandler { +impl interrupt::typelevel::Handler for TransmitInterruptHandler { unsafe fn on_interrupt() { // info!("ipcc tx interrupt"); @@ -324,8 +324,8 @@ impl<'d> TlMbox<'d> { /// initializes low-level transport between CPU1 and BLE stack on CPU2 pub fn new( ipcc: impl Peripheral

+ 'd, - _irqs: impl interrupt::Binding - + interrupt::Binding, + _irqs: impl interrupt::typelevel::Binding + + interrupt::typelevel::Binding, config: Config, ) -> Self { into_ref!(ipcc); @@ -379,11 +379,11 @@ impl<'d> TlMbox<'d> { MemoryManager::enable(); // enable interrupts - crate::interrupt::IPCC_C1_RX::unpend(); - crate::interrupt::IPCC_C1_TX::unpend(); + crate::interrupt::IPCC_C1_RX.unpend(); + crate::interrupt::IPCC_C1_TX.unpend(); - unsafe { crate::interrupt::IPCC_C1_RX::enable() }; - unsafe { crate::interrupt::IPCC_C1_TX::enable() }; + unsafe { crate::interrupt::IPCC_C1_RX.enable() }; + unsafe { crate::interrupt::IPCC_C1_TX.enable() }; Self { _ipcc: ipcc } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 252e945da..613da5674 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -2,18 +2,18 @@ use core::future::poll_fn; use core::slice; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use super::*; +use crate::interrupt::typelevel::Interrupt; /// Interrupt handler. pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let state = T::buffered_state(); @@ -115,7 +115,7 @@ pub struct BufferedUartRx<'d, T: BasicInstance> { impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new( peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, tx_buffer: &'d mut [u8], @@ -130,7 +130,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { pub fn new_with_rtscts( peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, @@ -159,7 +159,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { #[cfg(not(any(usart_v1, usart_v2)))] pub fn new_with_de( peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, de: impl Peripheral

> + 'd, diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ef1080153..da3644a81 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -5,13 +5,13 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; +use crate::interrupt::typelevel::Interrupt; #[cfg(not(any(usart_v1, usart_v2)))] #[allow(unused_imports)] use crate::pac::usart::regs::Isr as Sr; @@ -31,7 +31,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { let r = T::regs(); let s = T::state(); @@ -281,7 +281,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, config: Config, @@ -294,7 +294,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { pub fn new_with_rts( peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, rx_dma: impl Peripheral

+ 'd, @@ -650,7 +650,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, @@ -665,7 +665,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, @@ -693,7 +693,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, de: impl Peripheral

> + 'd, tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, @@ -1179,7 +1179,7 @@ pub(crate) mod sealed { pub trait BasicInstance: crate::rcc::RccPeripheral { const KIND: Kind; - type Interrupt: crate::interrupt::Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; fn regs() -> Regs; fn state() -> &'static State; @@ -1211,7 +1211,7 @@ macro_rules! impl_usart { ($inst:ident, $irq:ident, $kind:expr) => { impl sealed::BasicInstance for crate::peripherals::$inst { const KIND: Kind = $kind; - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; fn regs() -> Regs { Regs(crate::pac::$inst.0) diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index fbd1fa823..bee287fe6 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -1,4 +1,4 @@ -use crate::interrupt::Interrupt; +use crate::interrupt; use crate::rcc::RccPeripheral; #[cfg(feature = "nightly")] @@ -13,7 +13,7 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + RccPeripheral + 'static { - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } // Internal PHY pins @@ -29,7 +29,7 @@ foreach_interrupt!( } impl Instance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; ); diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 134107978..7486bd376 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -14,7 +14,7 @@ use embassy_usb_driver::{ use super::{DmPin, DpPin, Instance}; use crate::gpio::sealed::AFType; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::pac::usb::regs; use crate::pac::usb::vals::{EpType, Stat}; use crate::pac::USBRAM; @@ -26,7 +26,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { unsafe { let regs = T::regs(); @@ -255,7 +255,7 @@ pub struct Driver<'d, T: Instance> { impl<'d, T: Instance> Driver<'d, T> { pub fn new( _usb: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, dp: impl Peripheral

> + 'd, dm: impl Peripheral

> + 'd, ) -> Self { diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs index 193e0df0d..317264cbb 100644 --- a/embassy-stm32/src/usb_otg/mod.rs +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -1,7 +1,5 @@ -use embassy_cortex_m::interrupt::Interrupt; - -use crate::peripherals; use crate::rcc::RccPeripheral; +use crate::{interrupt, peripherals}; #[cfg(feature = "nightly")] mod usb; @@ -25,7 +23,7 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + RccPeripheral { - type Interrupt: Interrupt; + type Interrupt: interrupt::typelevel::Interrupt; } // Internal PHY pins @@ -109,7 +107,7 @@ foreach_interrupt!( } impl Instance for peripherals::USB_OTG_FS { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; @@ -161,7 +159,7 @@ foreach_interrupt!( } impl Instance for peripherals::USB_OTG_HS { - type Interrupt = crate::interrupt::$irq; + type Interrupt = crate::interrupt::typelevel::$irq; } }; ); diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index e602bcb70..16cbf1a95 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ @@ -15,6 +14,7 @@ use futures::future::poll_fn; use super::*; use crate::gpio::sealed::AFType; use crate::interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::pac::otg::{regs, vals}; use crate::rcc::sealed::RccPeripheral; use crate::time::Hertz; @@ -24,7 +24,7 @@ pub struct InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for InterruptHandler { +impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { trace!("irq"); let r = T::regs(); @@ -291,7 +291,7 @@ impl<'d, T: Instance> Driver<'d, T> { /// Endpoint allocation will fail if it is too small. pub fn new_fs( _peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, dp: impl Peripheral

> + 'd, dm: impl Peripheral

> + 'd, ep_out_buffer: &'d mut [u8], @@ -322,7 +322,7 @@ impl<'d, T: Instance> Driver<'d, T> { /// Endpoint allocation will fail if it is too small. pub fn new_hs_ulpi( _peri: impl Peripheral

+ 'd, - _irq: impl interrupt::Binding> + 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, ulpi_clk: impl Peripheral

> + 'd, ulpi_dir: impl Peripheral

> + 'd, ulpi_nxt: impl Peripheral

> + 'd, diff --git a/examples/stm32f7/build.rs b/examples/stm32f7/build.rs index c4e15f19c..2b5d412a9 100644 --- a/examples/stm32f7/build.rs +++ b/examples/stm32f7/build.rs @@ -1,9 +1,8 @@ //! adapted from https://github.com/stm32-rs/stm32f7xx-hal/blob/master/build.rs -use std::env; use std::fs::File; use std::io::prelude::*; -use std::io::{self}; use std::path::PathBuf; +use std::{env, io}; #[derive(Debug)] enum Error { From bce24e800516402a3a3991ebde0432e5167be174 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Jun 2023 18:07:44 +0200 Subject: [PATCH 1323/1575] asdg --- embassy-stm32/src/dma/gpdma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 07fef360f..59ec205bf 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -12,7 +12,7 @@ use embassy_sync::waitqueue::AtomicWaker; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::GPDMA_CHANNEL_COUNT; -use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::pac; use crate::pac::gpdma::vals; From 5c2f02c73505cf630c2fbe9b098707a33293d702 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Jun 2023 16:39:05 +0200 Subject: [PATCH 1324/1575] Reexport NVIC_PRIO_BITS at HAL root. This allows using RTIC with `#[rtic::app(device = embassy_nrf, ...)]` --- embassy-nrf/src/chips/nrf5340_app.rs | 2 ++ embassy-nrf/src/chips/nrf5340_net.rs | 2 ++ embassy-nrf/src/chips/nrf9160.rs | 2 ++ embassy-nrf/src/lib.rs | 5 +++-- embassy-rp/src/lib.rs | 2 ++ embassy-stm32/src/lib.rs | 2 ++ 6 files changed, 13 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index c10520051..cb879f736 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -5,6 +5,8 @@ pub mod pac { // The nRF5340 has a secure and non-secure (NS) mode. // To avoid cfg spam, we remove _ns or _s suffixes here. + pub use nrf5340_app_pac::NVIC_PRIO_BITS; + #[doc(no_inline)] pub use nrf5340_app_pac::{ interrupt, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index a6fb1d4cc..6e2c0bb67 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -5,6 +5,8 @@ pub mod pac { // The nRF5340 has a secure and non-secure (NS) mode. // To avoid cfg spam, we remove _ns or _s suffixes here. + pub use nrf5340_net_pac::NVIC_PRIO_BITS; + #[doc(no_inline)] pub use nrf5340_net_pac::{ interrupt, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index b6ae78bbe..9944ac4af 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -5,6 +5,8 @@ pub mod pac { // The nRF9160 has a secure and non-secure (NS) mode. // To avoid cfg spam, we remove _ns or _s suffixes here. + pub use nrf9160_pac::NVIC_PRIO_BITS; + #[doc(no_inline)] pub use nrf9160_pac::{ interrupt, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index a73d22a63..ddabf93a9 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -93,8 +93,6 @@ pub mod wdt; #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] mod chip; -pub use crate::chip::interrupt; - /// Macro to bind interrupts to handlers. /// /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) @@ -132,6 +130,9 @@ pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; pub use embassy_cortex_m::executor; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use crate::chip::interrupt; +pub use crate::pac::NVIC_PRIO_BITS; + pub mod config { //! Configuration options used when initializing the HAL. diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 70a410ef9..5e3e59692 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -43,6 +43,8 @@ pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use rp_pac as pac; +pub use crate::pac::NVIC_PRIO_BITS; + embassy_cortex_m::interrupt_mod!( TIMER_IRQ_0, TIMER_IRQ_1, diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index b42864567..565d6817c 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -111,6 +111,8 @@ pub use stm32_metapac as pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use stm32_metapac as pac; +pub use crate::pac::NVIC_PRIO_BITS; + #[non_exhaustive] pub struct Config { pub rcc: rcc::Config, From f498c689e7c3a2314c7a80bf14fc2cb491528aa4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Jun 2023 16:40:02 +0200 Subject: [PATCH 1325/1575] Add RTIC example. --- ci.sh | 1 + examples/nrf52840-rtic/.cargo/config.toml | 9 +++++ examples/nrf52840-rtic/Cargo.toml | 21 +++++++++++ examples/nrf52840-rtic/build.rs | 35 ++++++++++++++++++ examples/nrf52840-rtic/memory.x | 7 ++++ examples/nrf52840-rtic/src/bin/blinky.rs | 43 +++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 examples/nrf52840-rtic/.cargo/config.toml create mode 100644 examples/nrf52840-rtic/Cargo.toml create mode 100644 examples/nrf52840-rtic/build.rs create mode 100644 examples/nrf52840-rtic/memory.x create mode 100644 examples/nrf52840-rtic/src/bin/blinky.rs diff --git a/ci.sh b/ci.sh index 08558a18f..da2414d68 100755 --- a/ci.sh +++ b/ci.sh @@ -104,6 +104,7 @@ cargo batch \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \ + --- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \ --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \ --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \ --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \ diff --git a/examples/nrf52840-rtic/.cargo/config.toml b/examples/nrf52840-rtic/.cargo/config.toml new file mode 100644 index 000000000..3872e7189 --- /dev/null +++ b/examples/nrf52840-rtic/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip nRF52840_xxAA" + +[build] +target = "thumbv7em-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml new file mode 100644 index 000000000..0f9048b0f --- /dev/null +++ b/examples/nrf52840-rtic/Cargo.toml @@ -0,0 +1,21 @@ +[package] +edition = "2021" +name = "embassy-nrf52840-rtic-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +rtic = { version = "2", features = ["thumbv7-backend"] } + +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nightly", "unstable-traits", "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/nrf52840-rtic/build.rs b/examples/nrf52840-rtic/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf52840-rtic/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/nrf52840-rtic/memory.x b/examples/nrf52840-rtic/memory.x new file mode 100644 index 000000000..9b04edec0 --- /dev/null +++ b/examples/nrf52840-rtic/memory.x @@ -0,0 +1,7 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs new file mode 100644 index 000000000..a682c1932 --- /dev/null +++ b/examples/nrf52840-rtic/src/bin/blinky.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use {defmt_rtt as _, panic_probe as _}; + +#[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [SWI0_EGU0, SWI1_EGU1])] +mod app { + use defmt::info; + use embassy_nrf::gpio::{Level, Output, OutputDrive}; + use embassy_nrf::peripherals; + use embassy_time::{Duration, Timer}; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) { + info!("Hello World!"); + + let p = embassy_nrf::init(Default::default()); + blink::spawn(p.P0_13).map_err(|_| ()).unwrap(); + + (Shared {}, Local {}) + } + + #[task(priority = 1)] + async fn blink(_cx: blink::Context, pin: peripherals::P0_13) { + let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); + + loop { + info!("off!"); + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + info!("on!"); + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } + } +} From 8c93805ab5a13c784e072c8e6e59b354ee902d99 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Jun 2023 18:00:19 +0200 Subject: [PATCH 1326/1575] Add `rt` feature to HALs, cfg out interrupt handling when not set. --- embassy-cortex-m/src/interrupt.rs | 3 ++- embassy-nrf/Cargo.toml | 3 ++- embassy-nrf/src/gpiote.rs | 3 +++ embassy-nrf/src/time_driver.rs | 1 + embassy-rp/Cargo.toml | 3 ++- embassy-rp/src/dma.rs | 1 + embassy-rp/src/gpio.rs | 1 + embassy-rp/src/lib.rs | 1 + embassy-rp/src/multicore.rs | 2 ++ embassy-rp/src/pio.rs | 2 ++ embassy-rp/src/timer.rs | 4 ++++ embassy-stm32/Cargo.toml | 4 +++- embassy-stm32/build.rs | 1 + embassy-stm32/src/exti.rs | 1 + embassy-stm32/src/lib.rs | 1 + embassy-stm32/src/rng.rs | 2 ++ embassy-stm32/src/time_driver.rs | 6 ++++++ 17 files changed, 35 insertions(+), 4 deletions(-) diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-cortex-m/src/interrupt.rs index 8e45c36c7..e9fa43e87 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-cortex-m/src/interrupt.rs @@ -9,12 +9,13 @@ use cortex_m::peripheral::NVIC; #[macro_export] macro_rules! interrupt_mod { ($($irqs:ident),* $(,)?) => { + #[cfg(feature = "rt")] pub use cortex_m_rt::interrupt; /// Interrupt definitions. pub mod interrupt { pub use embassy_cortex_m::interrupt::{InterruptExt, Priority}; - pub use crate::pac::interrupt::*; + pub use crate::pac::Interrupt::*; pub use crate::pac::Interrupt; /// Type-level interrupt infrastructure. diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 83900d4d0..5fab11f37 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -16,7 +16,8 @@ flavors = [ ] [features] -default = [ +default = ["rt"] +rt = [ "nrf52805-pac?/rt", "nrf52810-pac?/rt", "nrf52811-pac?/rt", diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 1d7725bd3..21d0d9564 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -91,18 +91,21 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { } #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))] +#[cfg(feature = "rt")] #[interrupt] fn GPIOTE0() { unsafe { handle_gpiote_interrupt() }; } #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] +#[cfg(feature = "rt")] #[interrupt] fn GPIOTE1() { unsafe { handle_gpiote_interrupt() }; } #[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))] +#[cfg(feature = "rt")] #[interrupt] fn GPIOTE() { unsafe { handle_gpiote_interrupt() }; diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index f993d7b2e..f1ab4f8fd 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -295,6 +295,7 @@ impl Driver for RtcDriver { } } +#[cfg(feature = "rt")] #[interrupt] fn RTC1() { DRIVER.on_interrupt() diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ddada655b..ee06e99ce 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -13,7 +13,8 @@ flavors = [ ] [features] -default = [ "rp-pac/rt" ] +default = [ "rt" ] +rt = [ "rp-pac/rt" ] defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 042ca99a9..25819f03e 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -12,6 +12,7 @@ use crate::interrupt::InterruptExt; use crate::pac::dma::vals; use crate::{interrupt, pac, peripherals}; +#[cfg(feature = "rt")] #[interrupt] unsafe fn DMA_IRQ_0() { let ints0 = pac::DMA.ints0().read().ints0(); diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ebd932f50..66faa2489 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -142,6 +142,7 @@ pub(crate) unsafe fn init() { interrupt::IO_IRQ_BANK0.enable(); } +#[cfg(feature = "rt")] #[interrupt] unsafe fn IO_IRQ_BANK0() { let cpu = SIO.cpuid().read() as usize; diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 5e3e59692..4e4b76141 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -43,6 +43,7 @@ pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use rp_pac as pac; +#[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; embassy_cortex_m::interrupt_mod!( diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index e1246ce8f..2a7e4822a 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -106,6 +106,7 @@ impl Stack { } } +#[cfg(feature = "rt")] #[interrupt] #[link_section = ".data.ram_func"] unsafe fn SIO_IRQ_PROC1() { @@ -297,6 +298,7 @@ fn fifo_read() -> u32 { // Pop a value from inter-core FIFO, `wfe` until available #[inline(always)] +#[allow(unused)] fn fifo_read_wfe() -> u32 { unsafe { let sio = pac::SIO; diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index f0a5c17a9..0fa3bd771 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -85,6 +85,7 @@ const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; +#[cfg(feature = "rt")] #[interrupt] unsafe fn PIO0_IRQ_0() { use crate::pac; @@ -97,6 +98,7 @@ unsafe fn PIO0_IRQ_0() { pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints); } +#[cfg(feature = "rt")] #[interrupt] unsafe fn PIO1_IRQ_0() { use crate::pac; diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 37f86c930..ca8c96c0f 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -151,21 +151,25 @@ pub unsafe fn init() { interrupt::TIMER_IRQ_3.enable(); } +#[cfg(feature = "rt")] #[interrupt] unsafe fn TIMER_IRQ_0() { DRIVER.check_alarm(0) } +#[cfg(feature = "rt")] #[interrupt] unsafe fn TIMER_IRQ_1() { DRIVER.check_alarm(1) } +#[cfg(feature = "rt")] #[interrupt] unsafe fn TIMER_IRQ_2() { DRIVER.check_alarm(2) } +#[cfg(feature = "rt")] #[interrupt] unsafe fn TIMER_IRQ_3() { DRIVER.check_alarm(3) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4e29bb32f..f1f894c6c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -79,7 +79,9 @@ quote = "1.0.15" stm32-metapac = { version = "9", default-features = false, features = ["metadata"]} [features] -default = ["stm32-metapac/rt"] +default = ["rt"] +rt = ["stm32-metapac/rt"] + defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] exti = [] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 267fec387..ba5f91ccf 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -295,6 +295,7 @@ fn main() { let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch)); g.extend(quote! { + #[cfg(feature = "rt")] #[crate::interrupt] unsafe fn #irq () { #( diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index a2ed07093..0631ae473 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -291,6 +291,7 @@ macro_rules! foreach_exti_irq { macro_rules! impl_irq { ($e:ident) => { + #[cfg(feature = "rt")] #[interrupt] unsafe fn $e() { on_irq() diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 565d6817c..f8857fdd2 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -111,6 +111,7 @@ pub use stm32_metapac as pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use stm32_metapac as pac; +#[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; #[non_exhaustive] diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 1e16b8478..c657bf70e 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -149,6 +149,7 @@ foreach_peripheral!( }; ); +#[cfg(feature = "rt")] macro_rules! irq { ($irq:ident) => { mod rng_irq { @@ -166,6 +167,7 @@ macro_rules! irq { }; } +#[cfg(feature = "rt")] foreach_interrupt!( (RNG) => { irq!(RNG); diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 8f282bafb..e82501a45 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -40,6 +40,7 @@ type T = peripherals::TIM15; foreach_interrupt! { (TIM2, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim2)] + #[cfg(feature = "rt")] #[interrupt] fn $irq() { DRIVER.on_interrupt() @@ -47,6 +48,7 @@ foreach_interrupt! { }; (TIM3, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim3)] + #[cfg(feature = "rt")] #[interrupt] fn $irq() { DRIVER.on_interrupt() @@ -54,6 +56,7 @@ foreach_interrupt! { }; (TIM4, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim4)] + #[cfg(feature = "rt")] #[interrupt] fn $irq() { DRIVER.on_interrupt() @@ -61,6 +64,7 @@ foreach_interrupt! { }; (TIM5, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim5)] + #[cfg(feature = "rt")] #[interrupt] fn $irq() { DRIVER.on_interrupt() @@ -68,6 +72,7 @@ foreach_interrupt! { }; (TIM12, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim12)] + #[cfg(feature = "rt")] #[interrupt] fn $irq() { DRIVER.on_interrupt() @@ -75,6 +80,7 @@ foreach_interrupt! { }; (TIM15, timer, $block:ident, UP, $irq:ident) => { #[cfg(time_driver_tim15)] + #[cfg(feature = "rt")] #[interrupt] fn $irq() { DRIVER.on_interrupt() From 4716166041e78a4227331de2cab601b5e3252273 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Jun 2023 19:37:11 +0200 Subject: [PATCH 1327/1575] tests/rp: update cyw43-perf for embassy-net changes. --- tests/rp/src/bin/cyw43-perf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 568f1b826..dd67914aa 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -63,7 +63,7 @@ async fn main(spawner: Spawner) { .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; - let config = Config::Dhcp(Default::default()); + let config = Config::dhcpv4(Default::default()); //let config = embassy_net::Config::Static(embassy_net::Config { // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), // dns_servers: Vec::new(), @@ -93,7 +93,7 @@ async fn main(spawner: Spawner) { } info!("Waiting for DHCP up..."); - while stack.config().is_none() { + while stack.config_v4().is_none() { Timer::after(Duration::from_millis(100)).await; } info!("IP addressing up!"); From 3dde01597a7861817ee52fef6ea4b5732a77debc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 8 Jun 2023 21:12:34 +0200 Subject: [PATCH 1328/1575] tests/rp: make cyw43-perf less strict. --- tests/rp/src/bin/cyw43-perf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index dd67914aa..7a94ea191 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -115,9 +115,9 @@ const WIFI_NETWORK: &str = "EmbassyTest"; const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 500; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 500; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 400; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 300; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 300; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 300; const RX_BUFFER_SIZE: usize = 4096; const TX_BUFFER_SIZE: usize = 4096; const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); From 8ddeaddc674871db2125a7462c5b18eef938f497 Mon Sep 17 00:00:00 2001 From: Carl St-Laurent Date: Thu, 8 Jun 2023 20:46:48 -0400 Subject: [PATCH 1329/1575] Rename to follow ref manual and CubeIDE --- embassy-stm32/src/rcc/g4.rs | 20 ++++++++++---------- examples/stm32g4/src/bin/pll.rs | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 3ba9e7eb0..2b52416b2 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -17,7 +17,7 @@ pub const LSI_FREQ: Hertz = Hertz(32_000); pub enum ClockSrc { HSE(Hertz), HSI16, - PLL(PllSrc, PllM, PllN, PllClkDiv), + PLLCLK(PllSrc, PllM, PllN, PllR), } /// AHB prescaler @@ -61,27 +61,27 @@ impl Into for PllSrc { } #[derive(Clone, Copy)] -pub enum PllClkDiv { +pub enum PllR { Div2, Div4, Div6, Div8, } -impl PllClkDiv { +impl PllR { pub fn to_div(self) -> u32 { let val: u8 = self.into(); (val as u32 + 1) * 2 } } -impl From for u8 { - fn from(val: PllClkDiv) -> u8 { +impl From for u8 { + fn from(val: PllR) -> u8 { match val { - PllClkDiv::Div2 => 0b00, - PllClkDiv::Div4 => 0b01, - PllClkDiv::Div6 => 0b10, - PllClkDiv::Div8 => 0b11, + PllR::Div2 => 0b00, + PllR::Div4 => 0b01, + PllR::Div6 => 0b10, + PllR::Div8 => 0b11, } } } @@ -260,7 +260,7 @@ pub(crate) unsafe fn init(config: Config) { (freq.0, Sw::HSE) } - ClockSrc::PLL(src, prediv, mul, div) => { + ClockSrc::PLLCLK(src, prediv, mul, div) => { let src_freq = match src { PllSrc::HSI16 => { // Enable HSI16 as clock source for PLL diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index bde30c284..8cee41e9b 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ClockSrc, PllClkDiv, PllM, PllN, PllSrc}; +use embassy_stm32::rcc::{ClockSrc, PllM, PllN, PllR, PllSrc}; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); // Configure PLL to max frequency of 170 MHz - config.rcc.mux = ClockSrc::PLL(PllSrc::HSI16, PllM::Div4, PllN::Mul85, PllClkDiv::Div2); + config.rcc.mux = ClockSrc::PLLCLK(PllSrc::HSI16, PllM::Div4, PllN::Mul85, PllR::Div2); let _p = embassy_stm32::init(config); info!("Hello World!"); From 3465452a93719cdb46a2af4b6d893da3aacc0a15 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Jun 2023 03:33:39 +0200 Subject: [PATCH 1330/1575] fmt: remove unused defmt::timestamp! --- cyw43/src/fmt.rs | 3 --- embassy-cortex-m/src/fmt.rs | 3 --- embassy-futures/src/fmt.rs | 3 --- embassy-sync/src/fmt.rs | 3 --- 4 files changed, 12 deletions(-) diff --git a/cyw43/src/fmt.rs b/cyw43/src/fmt.rs index 5730447b3..9534c101c 100644 --- a/cyw43/src/fmt.rs +++ b/cyw43/src/fmt.rs @@ -197,9 +197,6 @@ macro_rules! unwrap { } } -#[cfg(feature = "defmt-timestamp-uptime")] -defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } - #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct NoneError; diff --git a/embassy-cortex-m/src/fmt.rs b/embassy-cortex-m/src/fmt.rs index f8bb0a035..066970813 100644 --- a/embassy-cortex-m/src/fmt.rs +++ b/embassy-cortex-m/src/fmt.rs @@ -195,9 +195,6 @@ macro_rules! unwrap { } } -#[cfg(feature = "defmt-timestamp-uptime")] -defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } - #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct NoneError; diff --git a/embassy-futures/src/fmt.rs b/embassy-futures/src/fmt.rs index f8bb0a035..066970813 100644 --- a/embassy-futures/src/fmt.rs +++ b/embassy-futures/src/fmt.rs @@ -195,9 +195,6 @@ macro_rules! unwrap { } } -#[cfg(feature = "defmt-timestamp-uptime")] -defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } - #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct NoneError; diff --git a/embassy-sync/src/fmt.rs b/embassy-sync/src/fmt.rs index f8bb0a035..066970813 100644 --- a/embassy-sync/src/fmt.rs +++ b/embassy-sync/src/fmt.rs @@ -195,9 +195,6 @@ macro_rules! unwrap { } } -#[cfg(feature = "defmt-timestamp-uptime")] -defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } - #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct NoneError; From d46b2b49c7941afed6578556dd2d6d98d072cbbb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Jun 2023 03:34:12 +0200 Subject: [PATCH 1331/1575] cyw43: remove pointless wait_complete. --- cyw43/src/control.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 6919d569e..c67614dd6 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -381,10 +381,7 @@ impl<'a> Control<'a> { } let ioctl = CancelOnDrop(self.ioctl_state); - - ioctl.0.do_ioctl(kind, cmd, iface, buf).await; - let resp_len = ioctl.0.wait_complete().await; - + let resp_len = ioctl.0.do_ioctl(kind, cmd, iface, buf).await; ioctl.defuse(); resp_len From dc8e34420f434505829cafe0cb844af9c1c0b500 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Jun 2023 16:02:12 +0200 Subject: [PATCH 1332/1575] Remove executor dep+reexports from HALs. Closes #1547 --- embassy-nrf/Cargo.toml | 3 +-- embassy-nrf/src/lib.rs | 1 - embassy-rp/Cargo.toml | 5 ++--- embassy-rp/src/lib.rs | 1 - embassy-stm32/Cargo.toml | 5 ++--- embassy-stm32/src/lib.rs | 1 - examples/nrf52840/src/bin/multiprio.rs | 2 +- examples/rp/src/bin/multiprio.rs | 2 +- 8 files changed, 7 insertions(+), 13 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 5fab11f37..2bb14b9ed 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -32,7 +32,7 @@ rt = [ time = ["dep:embassy-time"] -defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] @@ -91,7 +91,6 @@ _dppi = [] _gpio-p1 = [] [dependencies] -embassy-executor = { version = "0.2.0", path = "../embassy-executor", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index ddabf93a9..e2e514bb5 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -127,7 +127,6 @@ pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; -pub use embassy_cortex_m::executor; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; pub use crate::chip::interrupt; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ee06e99ce..ff2f9b2fd 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -48,7 +48,7 @@ boot2-w25x10cl = [] run-from-ram = [] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -56,7 +56,6 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} @@ -91,5 +90,5 @@ pio = {version= "0.2.1" } rp2040-boot2 = "0.3" [dev-dependencies] -embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } +embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] } static_cell = "1.1" diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 4e4b76141..ad8c6f285 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -36,7 +36,6 @@ pub mod pio_instr_util; pub mod relocate; // Reexports -pub use embassy_cortex_m::executor; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use rp_pac as pac; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index f1f894c6c..067f96756 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -32,7 +32,6 @@ flavors = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} @@ -82,7 +81,7 @@ stm32-metapac = { version = "9", default-features = false, features = ["metadata default = ["rt"] rt = ["stm32-metapac/rt"] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] exti = [] @@ -101,7 +100,7 @@ time-driver-tim12 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] # Reexport stm32-metapac at `embassy_stm32::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index f8857fdd2..e583993e5 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -103,7 +103,6 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; -pub use embassy_cortex_m::executor; use embassy_cortex_m::interrupt::Priority; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 851e189ea..37eb6565f 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::{info, unwrap}; -use embassy_nrf::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_nrf::interrupt; use embassy_nrf::pac::Interrupt; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs index 2f79ba49e..fea1d5b03 100644 --- a/examples/rp/src/bin/multiprio.rs +++ b/examples/rp/src/bin/multiprio.rs @@ -62,7 +62,7 @@ use core::mem; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::{info, unwrap}; -use embassy_rp::executor::{Executor, InterruptExecutor}; +use embassy_executor::{Executor, InterruptExecutor}; use embassy_rp::interrupt; use embassy_rp::pac::Interrupt; use embassy_time::{Duration, Instant, Timer, TICK_HZ}; From 98c821ac39c65903057c2d8ed320d1616e9f23ae Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Jun 2023 16:14:13 +0200 Subject: [PATCH 1333/1575] Remove embassy-cortex-m crate, move stuff to embassy-hal-common. --- .github/ci/doc.sh | 1 - embassy-cortex-m/Cargo.toml | 45 ---- embassy-cortex-m/src/fmt.rs | 225 ------------------ embassy-cortex-m/src/lib.rs | 9 - embassy-hal-common/Cargo.toml | 16 ++ .../build.rs | 0 .../src/interrupt.rs | 2 +- embassy-hal-common/src/lib.rs | 3 + embassy-nrf/Cargo.toml | 3 +- embassy-nrf/src/chips/nrf52805.rs | 2 +- embassy-nrf/src/chips/nrf52810.rs | 2 +- embassy-nrf/src/chips/nrf52811.rs | 2 +- embassy-nrf/src/chips/nrf52820.rs | 2 +- embassy-nrf/src/chips/nrf52832.rs | 2 +- embassy-nrf/src/chips/nrf52833.rs | 2 +- embassy-nrf/src/chips/nrf52840.rs | 2 +- embassy-nrf/src/chips/nrf5340_app.rs | 2 +- embassy-nrf/src/chips/nrf5340_net.rs | 2 +- embassy-nrf/src/chips/nrf9160.rs | 2 +- embassy-nrf/src/lib.rs | 2 +- embassy-rp/Cargo.toml | 3 +- embassy-rp/src/lib.rs | 4 +- embassy-stm32/Cargo.toml | 3 +- embassy-stm32/build.rs | 2 +- embassy-stm32/src/dma/bdma.rs | 2 +- embassy-stm32/src/dma/dma.rs | 2 +- embassy-stm32/src/dma/gpdma.rs | 2 +- embassy-stm32/src/dma/mod.rs | 2 +- embassy-stm32/src/lib.rs | 4 +- examples/rp/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 50 files changed, 64 insertions(+), 328 deletions(-) delete mode 100644 embassy-cortex-m/Cargo.toml delete mode 100644 embassy-cortex-m/src/fmt.rs delete mode 100644 embassy-cortex-m/src/lib.rs rename {embassy-cortex-m => embassy-hal-common}/build.rs (100%) rename {embassy-cortex-m => embassy-hal-common}/src/interrupt.rs (99%) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 736249368..72c6465f7 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -14,7 +14,6 @@ docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup -docserver-builder -i ./embassy-cortex-m -o crates/embassy-cortex-m/git.zup docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup diff --git a/embassy-cortex-m/Cargo.toml b/embassy-cortex-m/Cargo.toml deleted file mode 100644 index 70adda7df..000000000 --- a/embassy-cortex-m/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "embassy-cortex-m" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-cortex-m/src/" -features = ["prio-bits-3"] -flavors = [ - { name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] }, - { name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] }, - { name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] }, - { name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] }, - { name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] }, -] - -[features] -default = [] - -# Define the number of NVIC priority bits. -prio-bits-0 = [] -prio-bits-1 = [] -prio-bits-2 = [] -prio-bits-3 = [] -prio-bits-4 = [] -prio-bits-5 = [] -prio-bits-6 = [] -prio-bits-7 = [] -prio-bits-8 = [] - -[dependencies] -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } - -embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../embassy-executor"} -embassy-macros = { version = "0.2.0", path = "../embassy-macros"} -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common"} -atomic-polyfill = "1.0.1" -critical-section = "1.1" -cfg-if = "1.0.0" -cortex-m = "0.7.6" - diff --git a/embassy-cortex-m/src/fmt.rs b/embassy-cortex-m/src/fmt.rs deleted file mode 100644 index 066970813..000000000 --- a/embassy-cortex-m/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![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-cortex-m/src/lib.rs b/embassy-cortex-m/src/lib.rs deleted file mode 100644 index 7bc16d3ba..000000000 --- a/embassy-cortex-m/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Embassy executor and interrupt handling specific to cortex-m devices. -#![no_std] -#![warn(missing_docs)] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - -pub use embassy_executor as executor; -pub mod interrupt; diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-common/Cargo.toml index e8617c02f..18c758d7b 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-common/Cargo.toml @@ -6,8 +6,24 @@ license = "MIT OR Apache-2.0" [features] +# Define the number of NVIC priority bits. +prio-bits-0 = [] +prio-bits-1 = [] +prio-bits-2 = [] +prio-bits-3 = [] +prio-bits-4 = [] +prio-bits-5 = [] +prio-bits-6 = [] +prio-bits-7 = [] +prio-bits-8 = [] + +cortex-m = ["dep:cortex-m", "dep:critical-section"] + [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } num-traits = { version = "0.2.14", default-features = false } + +cortex-m = { version = "0.7.6", optional = true } +critical-section = { version = "1", optional = true } \ No newline at end of file diff --git a/embassy-cortex-m/build.rs b/embassy-hal-common/build.rs similarity index 100% rename from embassy-cortex-m/build.rs rename to embassy-hal-common/build.rs diff --git a/embassy-cortex-m/src/interrupt.rs b/embassy-hal-common/src/interrupt.rs similarity index 99% rename from embassy-cortex-m/src/interrupt.rs rename to embassy-hal-common/src/interrupt.rs index e9fa43e87..b970aa2cd 100644 --- a/embassy-cortex-m/src/interrupt.rs +++ b/embassy-hal-common/src/interrupt.rs @@ -14,7 +14,7 @@ macro_rules! interrupt_mod { /// Interrupt definitions. pub mod interrupt { - pub use embassy_cortex_m::interrupt::{InterruptExt, Priority}; + pub use $crate::interrupt::{InterruptExt, Priority}; pub use crate::pac::Interrupt::*; pub use crate::pac::Interrupt; diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs index b2a35cd35..235964aa4 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-common/src/lib.rs @@ -11,3 +11,6 @@ mod peripheral; pub mod ratio; pub mod ring_buffer; pub use peripheral::{Peripheral, PeripheralRef}; + +#[cfg(feature = "cortex-m")] +pub mod interrupt; diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 2bb14b9ed..3e858f854 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -93,8 +93,7 @@ _gpio-p1 = [] [dependencies] embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 8fbd760dc..8776000c8 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -208,7 +208,7 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index bbf8f7ccf..5519e8953 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -234,7 +234,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 31a8dd6af..d5367c59a 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -236,7 +236,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 6a6f4fcf2..785170447 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -224,7 +224,7 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index e43b3d5b2..b77564a5c 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -263,7 +263,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index d95e3497c..bff7f4ebb 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -306,7 +306,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 0094b1220..9b0050823 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -311,7 +311,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index cb879f736..410ae921c 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -506,7 +506,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( FPU, CACHE, SPU, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 6e2c0bb67..6ac783085 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -342,7 +342,7 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( CLOCK_POWER, RADIO, RNG, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 9944ac4af..67ea032ff 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -368,7 +368,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( SPU, CLOCK_POWER, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index e2e514bb5..691545662 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -98,7 +98,7 @@ mod chip; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ff2f9b2fd..b68f95385 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -58,8 +58,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } atomic-polyfill = "1.0.1" diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index ad8c6f285..d6f73219f 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -45,7 +45,7 @@ pub(crate) use rp_pac as pac; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; -embassy_cortex_m::interrupt_mod!( +embassy_hal_common::interrupt_mod!( TIMER_IRQ_0, TIMER_IRQ_1, TIMER_IRQ_2, @@ -85,7 +85,7 @@ embassy_cortex_m::interrupt_mod!( /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 067f96756..f876c7146 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -34,8 +34,7 @@ flavors = [ embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ba5f91ccf..9e597f187 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -160,7 +160,7 @@ fn main() { } g.extend(quote! { - embassy_cortex_m::interrupt_mod!( + embassy_hal_common::interrupt_mod!( #( #irqs, )* diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 83ab4b18f..c0a503e25 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -6,7 +6,6 @@ use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; use atomic_polyfill::AtomicUsize; -use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -15,6 +14,7 @@ use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; use crate::interrupt::typelevel::Interrupt; +use crate::interrupt::Priority; use crate::pac; use crate::pac::bdma::{regs, vals}; diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 17313b310..874cb013a 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -5,7 +5,6 @@ use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; use atomic_polyfill::AtomicUsize; -use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -14,6 +13,7 @@ use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; use crate::interrupt::typelevel::Interrupt; +use crate::interrupt::Priority; use crate::pac::dma::{regs, vals}; use crate::{interrupt, pac}; diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 59ec205bf..3f0d5e8fa 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -5,7 +5,6 @@ use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll}; -use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; @@ -13,6 +12,7 @@ use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::GPDMA_CHANNEL_COUNT; use crate::interrupt::typelevel::Interrupt; +use crate::interrupt::Priority; use crate::pac; use crate::pac::gpdma::vals; diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 3ac0d1b3d..0858587bd 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -26,11 +26,11 @@ pub mod word; use core::mem; -use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::impl_peripheral; #[cfg(dmamux)] pub use self::dmamux::*; +use crate::interrupt::Priority; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index e583993e5..f4ec0a80d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -79,7 +79,7 @@ pub use crate::_generated::interrupt; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -103,13 +103,13 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; -use embassy_cortex_m::interrupt::Priority; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use stm32_metapac as pac; +use crate::interrupt::Priority; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index e946b481d..48f3a26bb 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index ad11fbd1c..43f432520 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index ff134bb0e..8d2248ed0 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = "0.3" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 345e948a6..d34fd439a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index e4f97a589..5e3e0d0f7 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 0fe9cb122..29ab2009c 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "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", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 3a8efdd06..7ecb64fce 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 7a650067c..657251c50 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 4d7fc4548..c5245757b 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 00e2dae4c..f94df2dd3 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index d49a0dde7..ebe511347 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 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", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 08b57f988..62ef5e9e4 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 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", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 235f1b0b3..2ead714e4 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", +nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly", "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "embedded-io/async"] [dependencies] diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 8b6508c87..93d48abeb 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "stm32l151cb-a", "time-driver-any", "memory-x"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 29d091f94..3bb473ef5 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 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"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 2ac9c180d..6035c291f 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index be205f880..e2318c3d6 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 8cfac772a..14897b171 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6191d01e9..260f9afa1 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index fa97fdcc4..180d0ebbe 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1.1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index f1b0ba121..3f48bf3f1 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -25,7 +25,7 @@ not-gpdma = [] teleprobe-meta = "1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } From 6653f262d7c2ec17e6aba91b89d3835504320a5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Jun 2023 16:44:58 +0200 Subject: [PATCH 1334/1575] examples: use nicer InterrupExt to set irq priority in multprio. --- examples/nrf52840/src/bin/multiprio.rs | 14 +++++--------- examples/rp/src/bin/multiprio.rs | 16 +++++----------- examples/stm32f0/src/bin/multiprio.rs | 14 +++++--------- examples/stm32f3/src/bin/multiprio.rs | 14 +++++--------- examples/stm32f4/src/bin/multiprio.rs | 14 +++++--------- 5 files changed, 25 insertions(+), 47 deletions(-) diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 37eb6565f..aab819117 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs @@ -57,14 +57,11 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::mem; - -use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::{info, unwrap}; use embassy_executor::{Executor, InterruptExecutor}; use embassy_nrf::interrupt; -use embassy_nrf::pac::Interrupt; +use embassy_nrf::interrupt::{InterruptExt, Priority}; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -130,16 +127,15 @@ fn main() -> ! { info!("Hello World!"); let _p = embassy_nrf::init(Default::default()); - let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: SWI1_EGU1, priority level 6 - unsafe { nvic.set_priority(Interrupt::SWI1_EGU1, 6 << 5) }; - let spawner = EXECUTOR_HIGH.start(Interrupt::SWI1_EGU1); + interrupt::SWI1_EGU1.set_priority(Priority::P6); + let spawner = EXECUTOR_HIGH.start(interrupt::SWI1_EGU1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: SWI0_EGU0, priority level 7 - unsafe { nvic.set_priority(Interrupt::SWI0_EGU0, 7 << 5) }; - let spawner = EXECUTOR_MED.start(Interrupt::SWI0_EGU0); + interrupt::SWI0_EGU0.set_priority(Priority::P7); + let spawner = EXECUTOR_MED.start(interrupt::SWI0_EGU0); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs index fea1d5b03..9ace4cd68 100644 --- a/examples/rp/src/bin/multiprio.rs +++ b/examples/rp/src/bin/multiprio.rs @@ -57,14 +57,11 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::mem; - -use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::{info, unwrap}; use embassy_executor::{Executor, InterruptExecutor}; use embassy_rp::interrupt; -use embassy_rp::pac::Interrupt; +use embassy_rp::interrupt::{InterruptExt, Priority}; use embassy_time::{Duration, Instant, Timer, TICK_HZ}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -130,18 +127,15 @@ fn main() -> ! { info!("Hello World!"); let _p = embassy_rp::init(Default::default()); - let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: SWI_IRQ_1, priority level 2 - unsafe { nvic.set_priority(Interrupt::SWI_IRQ_1, 2 << 6) }; - info!("bla: {}", NVIC::get_priority(Interrupt::SWI_IRQ_1)); - let spawner = EXECUTOR_HIGH.start(Interrupt::SWI_IRQ_1); + interrupt::SWI_IRQ_1.set_priority(Priority::P2); + let spawner = EXECUTOR_HIGH.start(interrupt::SWI_IRQ_1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: SWI_IRQ_0, priority level 3 - unsafe { nvic.set_priority(Interrupt::SWI_IRQ_0, 3 << 6) }; - info!("bla: {}", NVIC::get_priority(Interrupt::SWI_IRQ_0)); - let spawner = EXECUTOR_MED.start(Interrupt::SWI_IRQ_0); + interrupt::SWI_IRQ_0.set_priority(Priority::P3); + let spawner = EXECUTOR_MED.start(interrupt::SWI_IRQ_0); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs index 430a805fc..988ffeef1 100644 --- a/examples/stm32f0/src/bin/multiprio.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -57,14 +57,11 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::mem; - -use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::pac::Interrupt; +use embassy_stm32::interrupt::{InterruptExt, Priority}; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -129,16 +126,15 @@ unsafe fn USART2() { fn main() -> ! { // Initialize and create handle for devicer peripherals let _p = embassy_stm32::init(Default::default()); - let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: USART1, priority level 6 - unsafe { nvic.set_priority(Interrupt::USART1, 6 << 4) }; - let spawner = EXECUTOR_HIGH.start(Interrupt::USART1); + interrupt::USART1.set_priority(Priority::P6); + let spawner = EXECUTOR_HIGH.start(interrupt::USART1); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: USART2, priority level 7 - unsafe { nvic.set_priority(Interrupt::USART2, 7 << 4) }; - let spawner = EXECUTOR_MED.start(Interrupt::USART2); + interrupt::USART2.set_priority(Priority::P7); + let spawner = EXECUTOR_MED.start(interrupt::USART2); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 5d010f799..80bf59deb 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -57,14 +57,11 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::mem; - -use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::pac::Interrupt; +use embassy_stm32::interrupt::{InterruptExt, Priority}; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -130,16 +127,15 @@ fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); - let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: UART4, priority level 6 - unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; - let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); + interrupt::UART4.set_priority(Priority::P6); + let spawner = EXECUTOR_HIGH.start(interrupt::UART4); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: UART5, priority level 7 - unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; - let spawner = EXECUTOR_MED.start(Interrupt::UART5); + interrupt::UART5.set_priority(Priority::P7); + let spawner = EXECUTOR_MED.start(interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 5d010f799..80bf59deb 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -57,14 +57,11 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::mem; - -use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; use defmt::*; use embassy_executor::{Executor, InterruptExecutor}; use embassy_stm32::interrupt; -use embassy_stm32::pac::Interrupt; +use embassy_stm32::interrupt::{InterruptExt, Priority}; use embassy_time::{Duration, Instant, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -130,16 +127,15 @@ fn main() -> ! { info!("Hello World!"); let _p = embassy_stm32::init(Default::default()); - let mut nvic: NVIC = unsafe { mem::transmute(()) }; // High-priority executor: UART4, priority level 6 - unsafe { nvic.set_priority(Interrupt::UART4, 6 << 4) }; - let spawner = EXECUTOR_HIGH.start(Interrupt::UART4); + interrupt::UART4.set_priority(Priority::P6); + let spawner = EXECUTOR_HIGH.start(interrupt::UART4); unwrap!(spawner.spawn(run_high())); // Medium-priority executor: UART5, priority level 7 - unsafe { nvic.set_priority(Interrupt::UART5, 7 << 4) }; - let spawner = EXECUTOR_MED.start(Interrupt::UART5); + interrupt::UART5.set_priority(Priority::P7); + let spawner = EXECUTOR_MED.start(interrupt::UART5); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV From ca8957da435eb91242fa33eb986e80a33bbc4da0 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 12 Jun 2023 12:27:51 +0100 Subject: [PATCH 1335/1575] stm32/ipcc: move tl_mbox into `embassy-stm32-wpan` --- embassy-stm32-wpan/Cargo.toml | 23 ++ .../tl_mbox => embassy-stm32-wpan/src}/ble.rs | 37 +-- .../src}/channels.rs | 16 +- .../tl_mbox => embassy-stm32-wpan/src}/cmd.rs | 4 +- .../src}/consts.rs | 0 .../tl_mbox => embassy-stm32-wpan/src}/evt.rs | 14 +- embassy-stm32-wpan/src/fmt.rs | 225 +++++++++++++++ .../mod.rs => embassy-stm32-wpan/src/lib.rs | 266 +++--------------- .../tl_mbox => embassy-stm32-wpan/src}/mm.rs | 18 +- embassy-stm32-wpan/src/rc.rs | 50 ++++ .../src}/shci.rs | 4 +- .../tl_mbox => embassy-stm32-wpan/src}/sys.rs | 36 +-- embassy-stm32-wpan/src/tables.rs | 175 ++++++++++++ .../src}/unsafe_linked_list.rs | 9 +- embassy-stm32/src/{tl_mbox => }/ipcc.rs | 60 ++++ embassy-stm32/src/lib.rs | 4 +- embassy-stm32/src/tl_mbox/hci.rs | 60 ---- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 20 +- 18 files changed, 652 insertions(+), 369 deletions(-) create mode 100644 embassy-stm32-wpan/Cargo.toml rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/ble.rs (72%) rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/channels.rs (90%) rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/cmd.rs (94%) rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/consts.rs (100%) rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/evt.rs (94%) create mode 100644 embassy-stm32-wpan/src/fmt.rs rename embassy-stm32/src/tl_mbox/mod.rs => embassy-stm32-wpan/src/lib.rs (59%) rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/mm.rs (87%) create mode 100644 embassy-stm32-wpan/src/rc.rs rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/shci.rs (97%) rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/sys.rs (68%) create mode 100644 embassy-stm32-wpan/src/tables.rs rename {embassy-stm32/src/tl_mbox => embassy-stm32-wpan/src}/unsafe_linked_list.rs (92%) rename embassy-stm32/src/{tl_mbox => }/ipcc.rs (75%) delete mode 100644 embassy-stm32/src/tl_mbox/hci.rs diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml new file mode 100644 index 000000000..a6673d472 --- /dev/null +++ b/embassy-stm32-wpan/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "embassy-stm32-wpan" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", features = ["stm32wb55rg"] } +embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} +embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } + +defmt = { version = "0.3", optional = true } +cortex-m = "0.7.6" +heapless = "0.7.16" + +bit_field = "0.10.2" + +[features] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32-wpan/src/ble.rs similarity index 72% rename from embassy-stm32/src/tl_mbox/ble.rs rename to embassy-stm32-wpan/src/ble.rs index 45bf81ef2..4546bde07 100644 --- a/embassy-stm32/src/tl_mbox/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -1,23 +1,24 @@ -use embassy_futures::block_on; +use core::mem::MaybeUninit; -use super::cmd::{CmdPacket, CmdSerial}; -use super::consts::TlPacketType; -use super::evt::EvtBox; -use super::ipcc::Ipcc; -use super::unsafe_linked_list::LinkedListNode; -use super::{ - channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, HEAPLESS_EVT_QUEUE, TL_BLE_TABLE, - TL_REF_TABLE, +use embassy_stm32::ipcc::Ipcc; + +use crate::cmd::{CmdPacket, CmdSerial}; +use crate::consts::TlPacketType; +use crate::evt::EvtBox; +use crate::tables::BleTable; +use crate::unsafe_linked_list::LinkedListNode; +use crate::{ + channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_CHANNEL, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_REF_TABLE, }; pub struct Ble; impl Ble { - pub(super) fn new() -> Self { + pub(super) fn enable() { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); - TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable { + TL_BLE_TABLE = MaybeUninit::new(BleTable { pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), pcs_buffer: CS_BUFFER.as_ptr().cast(), pevt_queue: EVT_QUEUE.as_ptr().cast(), @@ -26,8 +27,6 @@ impl Ble { } Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); - - Ble } pub(super) fn evt_handler() { @@ -41,20 +40,22 @@ impl Ble { let event = node_ptr.cast(); let event = EvtBox::new(event); - block_on(HEAPLESS_EVT_QUEUE.send(event)); + EVT_CHANNEL.try_send(event).unwrap(); } } Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); } - pub(super) fn acl_data_handler(&self) { + pub(super) fn acl_data_handler() { Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, false); // TODO: ACL data ack to the user } - pub fn send_cmd(buf: &[u8]) { + pub fn ble_send_cmd(buf: &[u8]) { + debug!("writing ble cmd"); + unsafe { let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmdserial; @@ -70,8 +71,8 @@ impl Ble { } #[allow(dead_code)] // Not used currently but reserved - pub(super) fn send_acl_data() { - let cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer }; + pub(super) fn ble_send_acl_data() { + let mut cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer }; cmd_packet.acl_data_serial.ty = TlPacketType::AclData as u8; diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32-wpan/src/channels.rs similarity index 90% rename from embassy-stm32/src/tl_mbox/channels.rs rename to embassy-stm32-wpan/src/channels.rs index 25a065ba4..9a2be1cfa 100644 --- a/embassy-stm32/src/tl_mbox/channels.rs +++ b/embassy-stm32-wpan/src/channels.rs @@ -50,36 +50,30 @@ //! pub mod cpu1 { - use crate::tl_mbox::ipcc::IpccChannel; + use embassy_stm32::ipcc::IpccChannel; - // Not used currently but reserved pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; - // Not used currently but reserved pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; - #[allow(dead_code)] // Not used currently but reserved pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; - // Not used currently but reserved - pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; - #[allow(dead_code)] // Not used currently but reserved pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6; } pub mod cpu2 { - use crate::tl_mbox::ipcc::IpccChannel; + use embassy_stm32::ipcc::IpccChannel; pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; - #[allow(dead_code)] // Not used currently but reserved pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; @@ -88,10 +82,8 @@ pub mod cpu2 { #[allow(dead_code)] // Not used currently but reserved pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; #[allow(dead_code)] // Not used currently but reserved - pub const IPCC_BLE_LLD_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; - #[allow(dead_code)] // Not used currently but reserved + pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4; - #[allow(dead_code)] // Not used currently but reserved pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5; #[allow(dead_code)] // Not used currently but reserved pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; diff --git a/embassy-stm32/src/tl_mbox/cmd.rs b/embassy-stm32-wpan/src/cmd.rs similarity index 94% rename from embassy-stm32/src/tl_mbox/cmd.rs rename to embassy-stm32-wpan/src/cmd.rs index 781aa669d..1f7dae7f7 100644 --- a/embassy-stm32/src/tl_mbox/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -1,5 +1,5 @@ -use crate::tl_mbox::evt::{EvtPacket, EvtSerial}; -use crate::tl_mbox::{PacketHeader, TL_EVT_HEADER_SIZE}; +use crate::evt::{EvtPacket, EvtSerial}; +use crate::{PacketHeader, TL_EVT_HEADER_SIZE}; #[derive(Copy, Clone)] #[repr(C, packed)] diff --git a/embassy-stm32/src/tl_mbox/consts.rs b/embassy-stm32-wpan/src/consts.rs similarity index 100% rename from embassy-stm32/src/tl_mbox/consts.rs rename to embassy-stm32-wpan/src/consts.rs diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32-wpan/src/evt.rs similarity index 94% rename from embassy-stm32/src/tl_mbox/evt.rs rename to embassy-stm32-wpan/src/evt.rs index 77ce7b4ca..b53fe506e 100644 --- a/embassy-stm32/src/tl_mbox/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -2,13 +2,13 @@ use core::mem::MaybeUninit; use super::cmd::{AclDataPacket, AclDataSerial}; use super::consts::TlPacketType; -use super::mm::MemoryManager; use super::{PacketHeader, TL_EVT_HEADER_SIZE}; +use crate::mm; /** * The payload of `Evt` for a command status event */ -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] #[repr(C, packed)] pub struct CsEvt { pub status: u8, @@ -19,7 +19,7 @@ pub struct CsEvt { /** * The payload of `Evt` for a command complete event */ -#[derive(Debug, Copy, Clone, Default)] +#[derive(Copy, Clone, Default)] #[repr(C, packed)] pub struct CcEvt { pub num_cmd: u8, @@ -41,14 +41,14 @@ impl CcEvt { } } -#[derive(Debug, Copy, Clone, Default)] +#[derive(Copy, Clone, Default)] #[repr(C, packed)] pub struct AsynchEvt { sub_evt_code: u16, payload: [u8; 1], } -#[derive(Debug, Copy, Clone, Default)] +#[derive(Copy, Clone, Default)] #[repr(C, packed)] pub struct Evt { pub evt_code: u8, @@ -56,7 +56,7 @@ pub struct Evt { pub payload: [u8; 1], } -#[derive(Debug, Copy, Clone, Default)] +#[derive(Copy, Clone, Default)] #[repr(C, packed)] pub struct EvtSerial { pub kind: u8, @@ -171,6 +171,6 @@ impl EvtBox { impl Drop for EvtBox { fn drop(&mut self) { - MemoryManager::evt_drop(self.ptr); + mm::MemoryManager::evt_drop(self.ptr); } } diff --git a/embassy-stm32-wpan/src/fmt.rs b/embassy-stm32-wpan/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-stm32-wpan/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-stm32/src/tl_mbox/mod.rs b/embassy-stm32-wpan/src/lib.rs similarity index 59% rename from embassy-stm32/src/tl_mbox/mod.rs rename to embassy-stm32-wpan/src/lib.rs index 21a954417..b3206428e 100644 --- a/embassy-stm32/src/tl_mbox/mod.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -1,30 +1,37 @@ +#![no_std] + +// This must go FIRST so that all the other modules see its macros. +pub mod fmt; + use core::mem::MaybeUninit; -use bit_field::BitField; +use cmd::CmdPacket; use embassy_cortex_m::interrupt::Interrupt; use embassy_futures::block_on; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_stm32::interrupt; +use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::peripherals::IPCC; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::signal::Signal; - -use self::cmd::{AclDataPacket, CmdPacket}; -use self::evt::{CcEvt, EvtBox}; -use self::ipcc::{Config, Ipcc}; -use self::unsafe_linked_list::LinkedListNode; -use crate::interrupt; -use crate::peripherals::IPCC; +use evt::{CcEvt, EvtBox}; +use tables::{ + BleTable, DeviceInfoTable, Mac802_15_4Table, MemManagerTable, RefTable, SysTable, ThreadTable, TracesTable, + WirelessFwInfoTable, +}; +use unsafe_linked_list::LinkedListNode; pub mod ble; pub mod channels; pub mod cmd; pub mod consts; pub mod evt; -pub mod hci; -pub mod ipcc; pub mod mm; +pub mod rc; pub mod shci; pub mod sys; +pub mod tables; pub mod unsafe_linked_list; /// Interrupt handler. @@ -32,16 +39,12 @@ pub struct ReceiveInterruptHandler {} impl interrupt::Handler for ReceiveInterruptHandler { unsafe fn on_interrupt() { - debug!("ipcc rx interrupt"); - if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { - debug!("sys evt"); + debug!("RX SYS evt"); sys::Sys::evt_handler(); } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { - debug!("ble evt"); + debug!("RX BLE evt"); ble::Ble::evt_handler(); - } else { - todo!() } STATE.signal(()); @@ -52,198 +55,23 @@ pub struct TransmitInterruptHandler {} impl interrupt::Handler for TransmitInterruptHandler { unsafe fn on_interrupt() { - debug!("ipcc tx interrupt"); - if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { - debug!("sys cmd rsp"); - // TODO: handle this case + debug!("TX SYS cmd rsp"); let cc = sys::Sys::cmd_evt_handler(); - let a = unsafe { core::slice::from_raw_parts(&cc as *const _ as *const u8, core::mem::size_of::()) }; - debug!("{:#04x}", a); LAST_CC_EVT.signal(cc); } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) { - debug!("mm"); + debug!("TX MM release"); mm::MemoryManager::free_buf_handler(); - } else { - todo!() + } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL) { + debug!("TX HCI acl"); + ble::Ble::acl_data_handler(); } STATE.signal(()); } } -#[derive(Debug, Copy, Clone)] -#[repr(C, packed)] -pub struct SafeBootInfoTable { - version: u32, -} - -#[derive(Debug, Copy, Clone)] -#[repr(C, packed)] -pub struct RssInfoTable { - version: u32, - memory_size: u32, - rss_info: u32, -} - -/** - * Version - * [0:3] = Build - 0: Untracked - 15:Released - x: Tracked version - * [4:7] = branch - 0: Mass Market - x: ... - * [8:15] = Subversion - * [16:23] = Version minor - * [24:31] = Version major - * - * Memory Size - * [0:7] = Flash ( Number of 4k sector) - * [8:15] = Reserved ( Shall be set to 0 - may be used as flash extension ) - * [16:23] = SRAM2b ( Number of 1k sector) - * [24:31] = SRAM2a ( Number of 1k sector) - */ -#[derive(Debug, Copy, Clone)] -#[repr(C, packed)] -pub struct WirelessFwInfoTable { - version: u32, - memory_size: u32, - thread_info: u32, - ble_info: u32, -} - -impl WirelessFwInfoTable { - pub fn version_major(&self) -> u8 { - let version = self.version; - (version.get_bits(24..31) & 0xff) as u8 - } - - pub fn version_minor(&self) -> u8 { - let version = self.version; - (version.clone().get_bits(16..23) & 0xff) as u8 - } - - pub fn subversion(&self) -> u8 { - let version = self.version; - (version.clone().get_bits(8..15) & 0xff) as u8 - } - - /// Size of FLASH, expressed in number of 4K sectors. - pub fn flash_size(&self) -> u8 { - let memory_size = self.memory_size; - (memory_size.clone().get_bits(0..7) & 0xff) as u8 - } - - /// Size of SRAM2a, expressed in number of 1K sectors. - pub fn sram2a_size(&self) -> u8 { - let memory_size = self.memory_size; - (memory_size.clone().get_bits(24..31) & 0xff) as u8 - } - - /// Size of SRAM2b, expressed in number of 1K sectors. - pub fn sram2b_size(&self) -> u8 { - let memory_size = self.memory_size; - (memory_size.clone().get_bits(16..23) & 0xff) as u8 - } -} - -#[derive(Debug, Clone)] -#[repr(C, align(4))] -pub struct DeviceInfoTable { - pub safe_boot_info_table: SafeBootInfoTable, - pub rss_info_table: RssInfoTable, - pub wireless_fw_info_table: WirelessFwInfoTable, -} - -#[derive(Debug)] -#[repr(C, align(4))] -struct BleTable { - pcmd_buffer: *mut CmdPacket, - pcs_buffer: *const u8, - pevt_queue: *const u8, - phci_acl_data_buffer: *mut AclDataPacket, -} - -#[derive(Debug)] -#[repr(C, align(4))] -struct ThreadTable { - nostack_buffer: *const u8, - clicmdrsp_buffer: *const u8, - otcmdrsp_buffer: *const u8, -} - -// TODO: use later -#[derive(Debug)] -#[repr(C, align(4))] -pub struct LldTestsTable { - clicmdrsp_buffer: *const u8, - m0cmd_buffer: *const u8, -} - -// TODO: use later -#[derive(Debug)] -#[repr(C, align(4))] -pub struct BleLldTable { - cmdrsp_buffer: *const u8, - m0cmd_buffer: *const u8, -} - -// TODO: use later -#[derive(Debug)] -#[repr(C, align(4))] -pub struct ZigbeeTable { - notif_m0_to_m4_buffer: *const u8, - appli_cmd_m4_to_m0_bufer: *const u8, - request_m0_to_m4_buffer: *const u8, -} - -#[derive(Debug)] -#[repr(C, align(4))] -struct SysTable { - pcmd_buffer: *mut CmdPacket, - sys_queue: *const LinkedListNode, -} - -#[derive(Debug)] -#[repr(C, align(4))] -struct MemManagerTable { - spare_ble_buffer: *const u8, - spare_sys_buffer: *const u8, - - blepool: *const u8, - blepoolsize: u32, - - pevt_free_buffer_queue: *mut LinkedListNode, - - traces_evt_pool: *const u8, - tracespoolsize: u32, -} - -#[derive(Debug)] -#[repr(C, align(4))] -struct TracesTable { - traces_queue: *const u8, -} - -#[derive(Debug)] -#[repr(C, align(4))] -struct Mac802_15_4Table { - p_cmdrsp_buffer: *const u8, - p_notack_buffer: *const u8, - evt_queue: *const u8, -} - -/// Reference table. Contains pointers to all other tables. -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub struct RefTable { - device_info_table: *const DeviceInfoTable, - ble_table: *const BleTable, - thread_table: *const ThreadTable, - sys_table: *const SysTable, - mem_manager_table: *const MemManagerTable, - traces_table: *const TracesTable, - mac_802_15_4_table: *const Mac802_15_4Table, -} - #[link_section = "TL_REF_TABLE"] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); @@ -338,25 +166,21 @@ static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); // fuck these "magic" numbers from ST ---v---v static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); -static HEAPLESS_EVT_QUEUE: Channel = Channel::new(); - -static STATE: Signal = Signal::new(); - /// current event that is produced during IPCC IRQ handler execution /// on SYS channel +static EVT_CHANNEL: Channel = Channel::new(); + /// last received Command Complete event static LAST_CC_EVT: Signal = Signal::new(); -pub struct TlMbox<'d> { - sys: sys::Sys, - ble: ble::Ble, - _mm: mm::MemoryManager, +static STATE: Signal = Signal::new(); +pub struct TlMbox<'d> { _ipcc: PeripheralRef<'d, IPCC>, } impl<'d> TlMbox<'d> { - pub fn new( + pub fn init( ipcc: impl Peripheral

+ 'd, _irqs: impl interrupt::Binding + interrupt::Binding, @@ -365,7 +189,7 @@ impl<'d> TlMbox<'d> { into_ref!(ipcc); unsafe { - TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { + TL_REF_TABLE = MaybeUninit::new(RefTable { device_info_table: TL_DEVICE_INFO_TABLE.as_mut_ptr(), ble_table: TL_BLE_TABLE.as_ptr(), thread_table: TL_THREAD_TABLE.as_ptr(), @@ -394,23 +218,20 @@ impl<'d> TlMbox<'d> { Ipcc::enable(config); - let sys = sys::Sys::new(); - let ble = ble::Ble::new(); - let mm = mm::MemoryManager::new(); + sys::Sys::enable(); + ble::Ble::enable(); + mm::MemoryManager::enable(); // enable interrupts - crate::interrupt::IPCC_C1_RX::unpend(); - crate::interrupt::IPCC_C1_TX::unpend(); + interrupt::IPCC_C1_RX::unpend(); + interrupt::IPCC_C1_TX::unpend(); - unsafe { crate::interrupt::IPCC_C1_RX::enable() }; - unsafe { crate::interrupt::IPCC_C1_TX::enable() }; + unsafe { interrupt::IPCC_C1_RX::enable() }; + unsafe { interrupt::IPCC_C1_TX::enable() }; - Self { - sys, - ble, - _mm: mm, - _ipcc: ipcc, - } + STATE.reset(); + + Self { _ipcc: ipcc } } /// Returns CPU2 wireless firmware information (if present). @@ -429,16 +250,15 @@ impl<'d> TlMbox<'d> { /// /// Internal event queu is populated in IPCC_RX_IRQ handler pub fn dequeue_event(&mut self) -> Option { - HEAPLESS_EVT_QUEUE.try_recv().ok() + EVT_CHANNEL.try_recv().ok() } /// retrieves last Command Complete event and removes it from mailbox pub fn pop_last_cc_evt(&mut self) -> Option { if LAST_CC_EVT.signaled() { - let cc = Some(block_on(LAST_CC_EVT.wait())); + let cc = block_on(LAST_CC_EVT.wait()); LAST_CC_EVT.reset(); - - cc + Some(cc) } else { None } diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32-wpan/src/mm.rs similarity index 87% rename from embassy-stm32/src/tl_mbox/mm.rs rename to embassy-stm32-wpan/src/mm.rs index a40438499..ed13b0dbf 100644 --- a/embassy-stm32/src/tl_mbox/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -2,18 +2,20 @@ use core::mem::MaybeUninit; -use super::evt::EvtPacket; -use super::ipcc::Ipcc; -use super::unsafe_linked_list::LinkedListNode; -use super::{ - channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, - SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, TL_REF_TABLE, +use embassy_stm32::ipcc::Ipcc; + +use crate::evt::EvtPacket; +use crate::tables::MemManagerTable; +use crate::unsafe_linked_list::LinkedListNode; +use crate::{ + channels, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, + TL_MEM_MANAGER_TABLE, TL_REF_TABLE, }; pub(super) struct MemoryManager; impl MemoryManager { - pub fn new() -> Self { + pub fn enable() { unsafe { LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); @@ -28,8 +30,6 @@ impl MemoryManager { tracespoolsize: 0, }); } - - MemoryManager } pub fn evt_drop(evt: *mut EvtPacket) { diff --git a/embassy-stm32-wpan/src/rc.rs b/embassy-stm32-wpan/src/rc.rs new file mode 100644 index 000000000..aae2265ed --- /dev/null +++ b/embassy-stm32-wpan/src/rc.rs @@ -0,0 +1,50 @@ +use crate::ble::Ble; +use crate::consts::TlPacketType; +use crate::{shci, TlMbox, STATE}; + +pub struct RadioCoprocessor<'d> { + mbox: TlMbox<'d>, + rx_buf: [u8; 500], +} + +impl<'d> RadioCoprocessor<'d> { + pub fn new(mbox: TlMbox<'d>) -> Self { + Self { + mbox, + rx_buf: [0u8; 500], + } + } + + pub fn write(&self, buf: &[u8]) { + let cmd_code = buf[0]; + let cmd = TlPacketType::try_from(cmd_code).unwrap(); + + match &cmd { + TlPacketType::BleCmd => Ble::ble_send_cmd(buf), + _ => todo!(), + } + } + + pub async fn read(&mut self) -> &[u8] { + loop { + STATE.wait().await; + + while let Some(evt) = self.mbox.dequeue_event() { + let event = evt.evt(); + + evt.write(&mut self.rx_buf).unwrap(); + + if event.kind() == 18 { + shci::shci_ble_init(Default::default()); + self.rx_buf[0] = 0x04; + } + } + + if self.mbox.pop_last_cc_evt().is_some() { + continue; + } + + return &self.rx_buf; + } + } +} diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32-wpan/src/shci.rs similarity index 97% rename from embassy-stm32/src/tl_mbox/shci.rs rename to embassy-stm32-wpan/src/shci.rs index b19baa702..4f4d08886 100644 --- a/embassy-stm32/src/tl_mbox/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -75,7 +75,7 @@ pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; pub fn shci_ble_init(param: ShciBleInitCmdParam) { - debug!("shci init"); + debug!("sending SHCI"); let mut packet = ShciBleInitCmdPacket { header: ShciHeader::default(), @@ -95,6 +95,6 @@ pub fn shci_ble_init(param: ShciBleInitCmdParam) { p_cmd_buffer.cmdserial.ty = TlPacketType::SysCmd as u8; - sys::send_cmd(); + sys::Sys::send_cmd(); } } diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32-wpan/src/sys.rs similarity index 68% rename from embassy-stm32/src/tl_mbox/sys.rs rename to embassy-stm32-wpan/src/sys.rs index c87aa440c..a19d12d27 100644 --- a/embassy-stm32/src/tl_mbox/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,27 +1,27 @@ -use embassy_futures::block_on; +use core::mem::MaybeUninit; -use super::cmd::{CmdPacket, CmdSerial}; -use super::evt::{CcEvt, EvtBox, EvtSerial}; -use super::ipcc::Ipcc; -use super::unsafe_linked_list::LinkedListNode; -use super::{channels, SysTable, HEAPLESS_EVT_QUEUE, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; +use embassy_stm32::ipcc::Ipcc; + +use crate::cmd::{CmdPacket, CmdSerial}; +use crate::evt::{CcEvt, EvtBox, EvtSerial}; +use crate::tables::SysTable; +use crate::unsafe_linked_list::LinkedListNode; +use crate::{channels, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; pub struct Sys; impl Sys { - pub fn new() -> Self { + pub fn enable() { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); - TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { + TL_SYS_TABLE = MaybeUninit::new(SysTable { pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), }) } Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); - - Sys } pub fn cmd_evt_handler() -> CcEvt { @@ -38,12 +38,6 @@ impl Sys { // 5. profit unsafe { let pcmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer; - - let a = unsafe { - core::slice::from_raw_parts(&pcmd as *const _ as *const u8, core::mem::size_of::()) - }; - debug!("shci response {:#04x}", a); - let cmd_serial: *const CmdSerial = &(*pcmd).cmdserial; let evt_serial: *const EvtSerial = cmd_serial.cast(); let cc: *const CcEvt = (*evt_serial).evt.payload.as_ptr().cast(); @@ -62,15 +56,15 @@ impl Sys { let event = node_ptr.cast(); let event = EvtBox::new(event); - block_on(HEAPLESS_EVT_QUEUE.send(event)); + EVT_CHANNEL.try_send(event).unwrap(); } } Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); } -} -pub fn send_cmd() { - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + pub fn send_cmd() { + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); + Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + } } diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs new file mode 100644 index 000000000..151216958 --- /dev/null +++ b/embassy-stm32-wpan/src/tables.rs @@ -0,0 +1,175 @@ +use bit_field::BitField; + +use crate::cmd::{AclDataPacket, CmdPacket}; +use crate::unsafe_linked_list::LinkedListNode; + +#[derive(Debug, Copy, Clone)] +#[repr(C, packed)] +pub struct SafeBootInfoTable { + version: u32, +} + +#[derive(Debug, Copy, Clone)] +#[repr(C, packed)] +pub struct RssInfoTable { + pub version: u32, + pub memory_size: u32, + pub rss_info: u32, +} + +/** + * Version + * [0:3] = Build - 0: Untracked - 15:Released - x: Tracked version + * [4:7] = branch - 0: Mass Market - x: ... + * [8:15] = Subversion + * [16:23] = Version minor + * [24:31] = Version major + * + * Memory Size + * [0:7] = Flash ( Number of 4k sector) + * [8:15] = Reserved ( Shall be set to 0 - may be used as flash extension ) + * [16:23] = SRAM2b ( Number of 1k sector) + * [24:31] = SRAM2a ( Number of 1k sector) + */ +#[derive(Debug, Copy, Clone)] +#[repr(C, packed)] +pub struct WirelessFwInfoTable { + pub version: u32, + pub memory_size: u32, + pub thread_info: u32, + pub ble_info: u32, +} + +impl WirelessFwInfoTable { + pub fn version_major(&self) -> u8 { + let version = self.version; + (version.get_bits(24..31) & 0xff) as u8 + } + + pub fn version_minor(&self) -> u8 { + let version = self.version; + (version.clone().get_bits(16..23) & 0xff) as u8 + } + + pub fn subversion(&self) -> u8 { + let version = self.version; + (version.clone().get_bits(8..15) & 0xff) as u8 + } + + /// Size of FLASH, expressed in number of 4K sectors. + pub fn flash_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.clone().get_bits(0..7) & 0xff) as u8 + } + + /// Size of SRAM2a, expressed in number of 1K sectors. + pub fn sram2a_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.clone().get_bits(24..31) & 0xff) as u8 + } + + /// Size of SRAM2b, expressed in number of 1K sectors. + pub fn sram2b_size(&self) -> u8 { + let memory_size = self.memory_size; + (memory_size.clone().get_bits(16..23) & 0xff) as u8 + } +} + +#[derive(Debug, Clone)] +#[repr(C, align(4))] +pub struct DeviceInfoTable { + pub safe_boot_info_table: SafeBootInfoTable, + pub rss_info_table: RssInfoTable, + pub wireless_fw_info_table: WirelessFwInfoTable, +} + +#[derive(Debug)] +#[repr(C, align(4))] +pub struct BleTable { + pub pcmd_buffer: *mut CmdPacket, + pub pcs_buffer: *const u8, + pub pevt_queue: *const u8, + pub phci_acl_data_buffer: *mut AclDataPacket, +} + +#[derive(Debug)] +#[repr(C, align(4))] +pub struct ThreadTable { + pub nostack_buffer: *const u8, + pub clicmdrsp_buffer: *const u8, + pub otcmdrsp_buffer: *const u8, +} + +// TODO: use later +#[derive(Debug)] +#[repr(C, align(4))] +pub struct LldTestsTable { + pub clicmdrsp_buffer: *const u8, + pub m0cmd_buffer: *const u8, +} + +// TODO: use later +#[derive(Debug)] +#[repr(C, align(4))] +pub struct BleLldTable { + pub cmdrsp_buffer: *const u8, + pub m0cmd_buffer: *const u8, +} + +// TODO: use later +#[derive(Debug)] +#[repr(C, align(4))] +pub struct ZigbeeTable { + pub notif_m0_to_m4_buffer: *const u8, + pub appli_cmd_m4_to_m0_bufer: *const u8, + pub request_m0_to_m4_buffer: *const u8, +} + +#[derive(Debug)] +#[repr(C, align(4))] +pub struct SysTable { + pub pcmd_buffer: *mut CmdPacket, + pub sys_queue: *const LinkedListNode, +} + +#[derive(Debug)] +#[repr(C, align(4))] +pub struct MemManagerTable { + pub spare_ble_buffer: *const u8, + pub spare_sys_buffer: *const u8, + + pub blepool: *const u8, + pub blepoolsize: u32, + + pub pevt_free_buffer_queue: *mut LinkedListNode, + + pub traces_evt_pool: *const u8, + pub tracespoolsize: u32, +} + +#[derive(Debug)] +#[repr(C, align(4))] +pub struct TracesTable { + pub traces_queue: *const u8, +} + +#[derive(Debug)] +#[repr(C, align(4))] +pub struct Mac802_15_4Table { + pub p_cmdrsp_buffer: *const u8, + pub p_notack_buffer: *const u8, + pub evt_queue: *const u8, +} + +/// Reference table. Contains pointers to all other tables. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub struct RefTable { + pub device_info_table: *const DeviceInfoTable, + pub ble_table: *const BleTable, + pub thread_table: *const ThreadTable, + pub sys_table: *const SysTable, + pub mem_manager_table: *const MemManagerTable, + pub traces_table: *const TracesTable, + pub mac_802_15_4_table: *const Mac802_15_4Table, +} diff --git a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs similarity index 92% rename from embassy-stm32/src/tl_mbox/unsafe_linked_list.rs rename to embassy-stm32-wpan/src/unsafe_linked_list.rs index 482e2bf5a..52c106fa2 100644 --- a/embassy-stm32/src/tl_mbox/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -57,6 +57,7 @@ impl LinkedListNode { }); } + /// Remove `node` from the linked list pub unsafe fn remove_node(mut node: *mut LinkedListNode) { interrupt::free(|_| { (*(*node).prev).next = (*node).next; @@ -64,6 +65,7 @@ impl LinkedListNode { }); } + /// Remove `list_head` into `node` pub unsafe fn remove_head(mut list_head: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { interrupt::free(|_| { *node = (*list_head).next; @@ -71,10 +73,11 @@ impl LinkedListNode { }); } - pub unsafe fn remove_tail(mut list_head: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + /// Remove `list_tail` into `node` + pub unsafe fn remove_tail(mut list_tail: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { interrupt::free(|_| { - *node = (*list_head).prev; - Self::remove_node((*list_head).prev); + *node = (*list_tail).prev; + Self::remove_node((*list_tail).prev); }); } diff --git a/embassy-stm32/src/tl_mbox/ipcc.rs b/embassy-stm32/src/ipcc.rs similarity index 75% rename from embassy-stm32/src/tl_mbox/ipcc.rs rename to embassy-stm32/src/ipcc.rs index d1ac731ed..8bb0774b8 100644 --- a/embassy-stm32/src/tl_mbox/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -167,8 +167,68 @@ impl sealed::Instance for crate::peripherals::IPCC { } unsafe fn _configure_pwr() { + let pwr = crate::pac::PWR; let rcc = crate::pac::RCC; + rcc.cfgr().modify(|w| w.set_stopwuck(true)); + + pwr.cr1().modify(|w| w.set_dbp(true)); + pwr.cr1().modify(|w| w.set_dbp(true)); + + // configure LSE + rcc.bdcr().modify(|w| w.set_lseon(true)); + + // select system clock source = PLL + // set PLL coefficients + // m: 2, + // n: 12, + // r: 3, + // q: 4, + // p: 3, + let src_bits = 0b11; + let pllp = (3 - 1) & 0b11111; + let pllq = (4 - 1) & 0b111; + let pllr = (3 - 1) & 0b111; + let plln = 12 & 0b1111111; + let pllm = (2 - 1) & 0b111; + rcc.pllcfgr().modify(|w| { + w.set_pllsrc(src_bits); + w.set_pllm(pllm); + w.set_plln(plln); + w.set_pllr(pllr); + w.set_pllp(pllp); + w.set_pllpen(true); + w.set_pllq(pllq); + w.set_pllqen(true); + }); + // enable PLL + rcc.cr().modify(|w| w.set_pllon(true)); + rcc.cr().write(|w| w.set_hsion(false)); + // while !rcc.cr().read().pllrdy() {} + + // configure SYSCLK mux to use PLL clocl + rcc.cfgr().modify(|w| w.set_sw(0b11)); + + // configure CPU1 & CPU2 dividers + rcc.cfgr().modify(|w| w.set_hpre(0)); // not divided + rcc.extcfgr().modify(|w| { + w.set_c2hpre(0b1000); // div2 + w.set_shdhpre(0); // not divided + }); + + // apply APB1 / APB2 values + rcc.cfgr().modify(|w| { + w.set_ppre1(0b000); // not divided + w.set_ppre2(0b000); // not divided + }); + + // TODO: required // set RF wake-up clock = LSE rcc.csr().modify(|w| w.set_rfwkpsel(0b01)); + + // set LPTIM1 & LPTIM2 clock source + rcc.ccipr().modify(|w| { + w.set_lptim1sel(0b00); // PCLK + w.set_lptim2sel(0b00); // PCLK + }); } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 75d8af3dd..8c13774a0 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -41,6 +41,8 @@ pub mod crc; pub mod flash; #[cfg(all(spi_v1, rcc_f4))] pub mod i2s; +#[cfg(stm32wb)] +pub mod ipcc; pub mod pwm; #[cfg(quadspi)] pub mod qspi; @@ -52,8 +54,6 @@ pub mod rtc; pub mod sdmmc; #[cfg(spi)] pub mod spi; -#[cfg(stm32wb)] -pub mod tl_mbox; #[cfg(usart)] pub mod usart; #[cfg(usb)] diff --git a/embassy-stm32/src/tl_mbox/hci.rs b/embassy-stm32/src/tl_mbox/hci.rs deleted file mode 100644 index 5bb4ba666..000000000 --- a/embassy-stm32/src/tl_mbox/hci.rs +++ /dev/null @@ -1,60 +0,0 @@ -use super::ble::Ble; -use super::consts::TlPacketType; -use super::evt::CcEvt; -use super::shci::{shci_ble_init, ShciBleInitCmdParam}; -use super::{TlMbox, STATE}; - -pub struct RadioCoprocessor<'d> { - mbox: TlMbox<'d>, - config: ShciBleInitCmdParam, - rx_buffer: [u8; 500], -} - -impl<'d> RadioCoprocessor<'d> { - pub fn new(mbox: TlMbox<'d>, config: ShciBleInitCmdParam) -> Self { - Self { - mbox, - config, - rx_buffer: [0u8; 500], - } - } - - pub fn write(&mut self, params: &[u8]) -> Result<(), ()> { - let cmd_code = params[0]; - let cmd = TlPacketType::try_from(cmd_code)?; - - match cmd { - TlPacketType::BleCmd => Ble::send_cmd(params), - _ => todo!(), - } - - Ok(()) - } - - pub async fn read(&mut self) -> &[u8] { - self.rx_buffer = [0u8; 500]; - - loop { - STATE.wait().await; - - if let Some(evt) = self.mbox.dequeue_event() { - let event = evt.evt(); - evt.write(&mut self.rx_buffer).unwrap(); - - if event.kind() == 18 { - shci_ble_init(self.config); - self.rx_buffer[0] = 0x04; // replace event code with one that is supported by HCI - } - - if let Some(cc) = self.mbox.pop_last_cc_evt() { - - - continue; - } - - let payload_len = self.rx_buffer[2]; - return &self.rx_buffer[..3 + payload_len as usize]; - } - } - } -} diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 0525d3f37..3132ab3e4 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -4,15 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::tl_mbox::hci::RadioCoprocessor; -use embassy_stm32::tl_mbox::ipcc::Config; -use embassy_stm32::tl_mbox::TlMbox; -use embassy_stm32::{bind_interrupts, tl_mbox}; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::Config; +use embassy_stm32_wpan::rc::RadioCoprocessor; +use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ - IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; - IPCC_C1_TX => tl_mbox::TransmitInterruptHandler; + IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; + IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; }); #[embassy_executor::main] @@ -45,16 +45,16 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::new(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config); - let mut rc = RadioCoprocessor::new(mbox, Default::default()); - rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]).unwrap(); + let mut rc = RadioCoprocessor::new(mbox); let response = rc.read().await; info!("coprocessor ready {}", response); + rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); let response = rc.read().await; - info!("coprocessor ready {}", response); + info!("ble reset rsp {}", response); info!("Test OK"); cortex_m::asm::bkpt(); From 2dd5ce83ec0421564e85b667f5dabd592f313e5c Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 12 Jun 2023 12:31:15 +0100 Subject: [PATCH 1336/1575] stm32/ipcc: fix `tl_mbox` example --- examples/stm32wb/src/bin/tl_mbox.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 8f4e70af0..ae36a7e79 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -4,14 +4,15 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::tl_mbox::{Config, TlMbox}; -use embassy_stm32::{bind_interrupts, tl_mbox}; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::Config; +use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ - IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; - IPCC_C1_TX => tl_mbox::TransmitInterruptHandler; + IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; + IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; }); #[embassy_executor::main] @@ -44,7 +45,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::new(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config); loop { let wireless_fw_info = mbox.wireless_fw_info(); From a1b27783a645673500833d13bbabb21d4e4202df Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 12 Jun 2023 14:44:30 +0100 Subject: [PATCH 1337/1575] fix build --- embassy-stm32-wpan/Cargo.toml | 24 ++++++++++++++++++++---- embassy-stm32-wpan/build.rs | 34 ++++++++++++++++++++++++++++++++++ embassy-stm32-wpan/src/lib.rs | 18 +++++++++--------- examples/stm32wb/Cargo.toml | 1 + tests/stm32/src/bin/tl_mbox.rs | 11 ++++++----- 5 files changed, 70 insertions(+), 18 deletions(-) create mode 100644 embassy-stm32-wpan/build.rs diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index a6673d472..058d0e29f 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -5,13 +5,12 @@ edition = "2021" license = "MIT OR Apache-2.0" [dependencies] -embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", features = ["stm32wb55rg"] } +embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } -embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } +embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } cortex-m = "0.7.6" @@ -21,3 +20,20 @@ bit_field = "0.10.2" [features] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] + +stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] +stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] +stm32wb30ce = [ "embassy-stm32/stm32wb30ce" ] +stm32wb35cc = [ "embassy-stm32/stm32wb35cc" ] +stm32wb35ce = [ "embassy-stm32/stm32wb35ce" ] +stm32wb50cg = [ "embassy-stm32/stm32wb50cg" ] +stm32wb55cc = [ "embassy-stm32/stm32wb55cc" ] +stm32wb55ce = [ "embassy-stm32/stm32wb55ce" ] +stm32wb55cg = [ "embassy-stm32/stm32wb55cg" ] +stm32wb55rc = [ "embassy-stm32/stm32wb55rc" ] +stm32wb55re = [ "embassy-stm32/stm32wb55re" ] +stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ] +stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ] +stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ] +stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ] +stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] \ No newline at end of file diff --git a/embassy-stm32-wpan/build.rs b/embassy-stm32-wpan/build.rs new file mode 100644 index 000000000..4edf73d59 --- /dev/null +++ b/embassy-stm32-wpan/build.rs @@ -0,0 +1,34 @@ +use std::env; + +fn main() { + match env::vars() + .map(|(a, _)| a) + .filter(|x| x.starts_with("CARGO_FEATURE_STM32")) + .get_one() + { + Ok(_) => {} + Err(GetOneError::None) => panic!("No stm32xx Cargo feature enabled"), + Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), + } +} + +enum GetOneError { + None, + Multiple, +} + +trait IteratorExt: Iterator { + fn get_one(self) -> Result; +} + +impl IteratorExt for T { + fn get_one(mut self) -> Result { + match self.next() { + None => Err(GetOneError::None), + Some(res) => match self.next() { + Some(_) => Err(GetOneError::Multiple), + None => Ok(res), + }, + } + } +} diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index b3206428e..c37b67dc4 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -6,10 +6,10 @@ pub mod fmt; use core::mem::MaybeUninit; use cmd::CmdPacket; -use embassy_cortex_m::interrupt::Interrupt; use embassy_futures::block_on; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; +use embassy_stm32::interrupt::typelevel::Interrupt; use embassy_stm32::ipcc::{Config, Ipcc}; use embassy_stm32::peripherals::IPCC; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -37,7 +37,7 @@ pub mod unsafe_linked_list; /// Interrupt handler. pub struct ReceiveInterruptHandler {} -impl interrupt::Handler for ReceiveInterruptHandler { +impl interrupt::typelevel::Handler for ReceiveInterruptHandler { unsafe fn on_interrupt() { if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { debug!("RX SYS evt"); @@ -53,7 +53,7 @@ impl interrupt::Handler for ReceiveInterruptHandler { pub struct TransmitInterruptHandler {} -impl interrupt::Handler for TransmitInterruptHandler { +impl interrupt::typelevel::Handler for TransmitInterruptHandler { unsafe fn on_interrupt() { if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { debug!("TX SYS cmd rsp"); @@ -182,8 +182,8 @@ pub struct TlMbox<'d> { impl<'d> TlMbox<'d> { pub fn init( ipcc: impl Peripheral

+ 'd, - _irqs: impl interrupt::Binding - + interrupt::Binding, + _irqs: impl interrupt::typelevel::Binding + + interrupt::typelevel::Binding, config: Config, ) -> Self { into_ref!(ipcc); @@ -223,11 +223,11 @@ impl<'d> TlMbox<'d> { mm::MemoryManager::enable(); // enable interrupts - interrupt::IPCC_C1_RX::unpend(); - interrupt::IPCC_C1_TX::unpend(); + interrupt::typelevel::IPCC_C1_RX::unpend(); + interrupt::typelevel::IPCC_C1_TX::unpend(); - unsafe { interrupt::IPCC_C1_RX::enable() }; - unsafe { interrupt::IPCC_C1_TX::enable() }; + unsafe { interrupt::typelevel::IPCC_C1_RX::enable() }; + unsafe { interrupt::typelevel::IPCC_C1_TX::enable() }; STATE.reset(); diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 14897b171..5b82dd83b 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -9,6 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } +embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index fab9f0e1b..0f643d578 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -8,13 +8,14 @@ mod common; use common::*; use embassy_executor::Spawner; -use embassy_stm32::tl_mbox::{Config, TlMbox}; -use embassy_stm32::{bind_interrupts, tl_mbox}; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::Config; +use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; bind_interrupts!(struct Irqs{ - IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; - IPCC_C1_TX => tl_mbox::TransmitInterruptHandler; + IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; + IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; }); #[embassy_executor::main] @@ -23,7 +24,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::new(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config); loop { let wireless_fw_info = mbox.wireless_fw_info(); From bb5ceb2d9c511e7923b538ac14a30bd78368b189 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 12 Jun 2023 14:52:14 +0100 Subject: [PATCH 1338/1575] fix CI error --- examples/stm32wb/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 5b82dd83b..83a443754 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "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", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } -embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt"] } +embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } defmt = "0.3" defmt-rtt = "0.4" From 553c93432559e75d211b84363840f1d272489283 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 12 Jun 2023 14:54:17 +0100 Subject: [PATCH 1339/1575] fix CI for tests --- tests/stm32/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 3f48bf3f1..3a50493f4 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -29,6 +29,7 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } defmt = "0.3.0" defmt-rtt = "0.4" From 802416d267028dd0624f6f4183fd1d44278946e1 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 12 Jun 2023 15:04:52 +0100 Subject: [PATCH 1340/1575] fix CI for tests --- tests/stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 3a50493f4..db97e929b 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,7 +12,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "dep:embassy-stm32-wpan"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board @@ -29,7 +29,7 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } +embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg"] } defmt = "0.3.0" defmt-rtt = "0.4" From bf32dc5d3acee1f73fe445976bde474ffa6d04e0 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 12 Jun 2023 15:07:46 +0100 Subject: [PATCH 1341/1575] clippy --- embassy-stm32-wpan/src/ble.rs | 4 ++-- embassy-stm32-wpan/src/shci.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 4546bde07..19955a8a3 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -63,7 +63,7 @@ impl Ble { core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); - let mut cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; + let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; cmd_packet.cmdserial.ty = TlPacketType::BleCmd as u8; } @@ -72,7 +72,7 @@ impl Ble { #[allow(dead_code)] // Not used currently but reserved pub(super) fn ble_send_acl_data() { - let mut cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer }; + let cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer }; cmd_packet.acl_data_serial.ty = TlPacketType::AclData as u8; diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index 4f4d08886..8537995ff 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -90,7 +90,7 @@ pub fn shci_ble_init(param: ShciBleInitCmdParam) { (*cmd_ptr).cmdserial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT; (*cmd_ptr).cmdserial.cmd.payload_len = core::mem::size_of::() as u8; - let mut p_cmd_buffer = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; + let p_cmd_buffer = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; core::ptr::write(p_cmd_buffer, *cmd_ptr); p_cmd_buffer.cmdserial.ty = TlPacketType::SysCmd as u8; From 8d5f995a010eadcc70c6e933ca0facab133bbef4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Jun 2023 16:37:36 +0200 Subject: [PATCH 1342/1575] usb-logger: fix breakage in log 0.4.19 https://github.com/rust-lang/log/issues/567 --- embassy-usb-logger/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 1d8dd13ce..6e50c40ba 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -138,7 +138,7 @@ 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 _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); } let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; }; From 6863786243e0f7cb28661d49165eeffacbfd8152 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Sat, 10 Jun 2023 18:14:45 +0200 Subject: [PATCH 1343/1575] Document external button. Add wifi_blinky.rs for easy beginners start. --- examples/rp/src/bin/button.rs | 5 ++- examples/rp/src/bin/wifi_blinky.rs | 59 ++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 examples/rp/src/bin/wifi_blinky.rs diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index c5422c616..edfb09cdb 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs @@ -9,9 +9,12 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); - let button = Input::new(p.PIN_28, Pull::Up); let mut led = Output::new(p.PIN_25, Level::Low); + // Use PIN_28, Pin34 on J0 for RP Pico, as a input. + // You need to ad your own button. + let button = Input::new(p.PIN_28, Pull::Up); + loop { if button.is_high() { led.set_high(); diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs new file mode 100644 index 000000000..f567e7c5d --- /dev/null +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use cyw43_pio::PioSpi; +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; +use embassy_rp::pio::Pio; +use embassy_time::{Duration, Timer}; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn wifi_task( + runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, +) -> ! { + runner.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); + let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); + + // To make flashing faster for development, you may want to flash the firmwares independently + // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: + // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + + let pwr = Output::new(p.PIN_23, Level::Low); + let cs = Output::new(p.PIN_25, Level::High); + let mut pio = Pio::new(p.PIO0); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); + + let state = make_static!(cyw43::State::new()); + let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; + unwrap!(spawner.spawn(wifi_task(runner))); + + control.init(clm).await; + control + .set_power_management(cyw43::PowerManagementMode::PowerSave) + .await; + + let delay = Duration::from_secs(1); + loop { + info!("led on!"); + control.gpio_set(0, true).await; + Timer::after(delay).await; + + info!("led off!"); + control.gpio_set(0, false).await; + Timer::after(delay).await; + } +} From 23724b6bf6ce6492d40a3ff3af77c8b4d15a36d8 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Mon, 12 Jun 2023 18:12:35 +0200 Subject: [PATCH 1344/1575] Code cleanup. --- examples/rp/src/bin/wifi_blinky.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index f567e7c5d..be965807b 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -40,7 +40,7 @@ async fn main(spawner: Spawner) { let state = make_static!(cyw43::State::new()); let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); - + control.init(clm).await; control .set_power_management(cyw43::PowerManagementMode::PowerSave) From 35db5cf41653bdb995f5cad2d04788118a746921 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Mon, 12 Jun 2023 18:56:20 +0200 Subject: [PATCH 1345/1575] Spelling. --- examples/rp/src/bin/button.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index edfb09cdb..0d246c093 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs @@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PIN_25, Level::Low); // Use PIN_28, Pin34 on J0 for RP Pico, as a input. - // You need to ad your own button. + // You need to add your own button. let button = Input::new(p.PIN_28, Pull::Up); loop { From 9cfcc5b89afb8fee9371af3bfbe8480deb2ef634 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 13 Jun 2023 03:29:51 +0200 Subject: [PATCH 1346/1575] Fix docs build. --- .github/ci/doc.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 72c6465f7..947823b1f 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -9,6 +9,12 @@ export CARGO_TARGET_DIR=/ci/cache/target export BUILDER_THREADS=6 export BUILDER_COMPRESS=true +# force rustup to download the toolchain before starting building. +# Otherwise, the docs builder is running multiple instances of cargo rustdoc concurrently. +# They all see the toolchain is not installed and try to install it in parallel +# which makes rustup very sad +rustc --version > /dev/null + docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup From 3c98587a884e7c13d4b3203669866e23ad05003a Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 12 Jun 2023 21:23:42 -0500 Subject: [PATCH 1347/1575] tests/ble: disable test for now does not work in --release --- tests/stm32/Cargo.toml | 4 ++-- tests/stm32/src/bin/tl_mbox.rs | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index db97e929b..dcc7d9b8a 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,13 +12,13 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "dep:embassy-stm32-wpan"] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] chrono = ["embassy-stm32/chrono", "dep:chrono"] -ble = [] +ble = ["dep:embassy-stm32-wpan"] not-gpdma = [] [dependencies] diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 0f643d578..b57519a54 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -50,6 +50,15 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_millis(50)).await; } + // let mut rc = RadioCoprocessor::new(mbox); + // + // let response = rc.read().await; + // info!("coprocessor ready {}", response); + // + // rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); + // let response = rc.read().await; + // info!("ble reset rsp {}", response); + info!("Test OK"); cortex_m::asm::bkpt(); } From b55e618175e3af81f2b0d04bca45a96adc24a661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 12 Jun 2023 09:36:07 +0200 Subject: [PATCH 1348/1575] embassy-nrf: Idle detection for RX only uarte Introduce `with_idle()` to upgrade an `UarteRx` instance to `UarteRxWithIdle`. Use the method in the split constructor aswell. --- embassy-nrf/src/uarte.rs | 95 +++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 85a951ae0..48d57fea4 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -205,50 +205,7 @@ impl<'d, T: Instance> Uarte<'d, T> { ppi_ch1: impl Peripheral

+ 'd, ppi_ch2: impl Peripheral

+ 'd, ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) { - let timer = Timer::new(timer); - - into_ref!(ppi_ch1, ppi_ch2); - - let r = T::regs(); - - // BAUDRATE register values are `baudrate * 2^32 / 16000000` - // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values - // - // We want to stop RX if line is idle for 2 bytes worth of time - // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) - // This gives us the amount of 16M ticks for 20 bits. - let baudrate = r.baudrate.read().baudrate().variant().unwrap(); - let timeout = 0x8000_0000 / (baudrate as u32 / 40); - - timer.set_frequency(Frequency::F16MHz); - timer.cc(0).write(timeout); - timer.cc(0).short_compare_clear(); - timer.cc(0).short_compare_stop(); - - let mut ppi_ch1 = Ppi::new_one_to_two( - ppi_ch1.map_into(), - Event::from_reg(&r.events_rxdrdy), - timer.task_clear(), - timer.task_start(), - ); - ppi_ch1.enable(); - - let mut ppi_ch2 = Ppi::new_one_to_one( - ppi_ch2.map_into(), - timer.cc(0).event_compare(), - Task::from_reg(&r.tasks_stoprx), - ); - ppi_ch2.enable(); - - ( - self.tx, - UarteRxWithIdle { - rx: self.rx, - timer, - ppi_ch1: ppi_ch1, - _ppi_ch2: ppi_ch2, - }, - ) + (self.tx, self.rx.with_idle(timer, ppi_ch1, ppi_ch2)) } /// Return the endtx event for use with PPI @@ -563,6 +520,56 @@ impl<'d, T: Instance> UarteRx<'d, T> { Self { _p: uarte } } + /// Upgrade to an instance that supports idle line detection. + pub fn with_idle( + self, + timer: impl Peripheral

+ 'd, + ppi_ch1: impl Peripheral

+ 'd, + ppi_ch2: impl Peripheral

+ 'd, + ) -> UarteRxWithIdle<'d, T, U> { + let timer = Timer::new(timer); + + into_ref!(ppi_ch1, ppi_ch2); + + let r = T::regs(); + + // BAUDRATE register values are `baudrate * 2^32 / 16000000` + // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values + // + // We want to stop RX if line is idle for 2 bytes worth of time + // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) + // This gives us the amount of 16M ticks for 20 bits. + let baudrate = r.baudrate.read().baudrate().variant().unwrap(); + let timeout = 0x8000_0000 / (baudrate as u32 / 40); + + timer.set_frequency(Frequency::F16MHz); + timer.cc(0).write(timeout); + timer.cc(0).short_compare_clear(); + timer.cc(0).short_compare_stop(); + + let mut ppi_ch1 = Ppi::new_one_to_two( + ppi_ch1.map_into(), + Event::from_reg(&r.events_rxdrdy), + timer.task_clear(), + timer.task_start(), + ); + ppi_ch1.enable(); + + let mut ppi_ch2 = Ppi::new_one_to_one( + ppi_ch2.map_into(), + timer.cc(0).event_compare(), + Task::from_reg(&r.tasks_stoprx), + ); + ppi_ch2.enable(); + + UarteRxWithIdle { + rx: self, + timer, + ppi_ch1: ppi_ch1, + _ppi_ch2: ppi_ch2, + } + } + /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { if buffer.len() == 0 { From 055f6afdccec98a8151b83cad3a87d8858c08e8c Mon Sep 17 00:00:00 2001 From: goueslati Date: Tue, 13 Jun 2023 09:51:21 +0100 Subject: [PATCH 1349/1575] stm32-wpan: add doc metadata --- .github/ci/doc.sh | 1 + embassy-stm32-wpan/Cargo.toml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 947823b1f..1402e742f 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -37,6 +37,7 @@ docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup docserver-builder -i ./cyw43 -o crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 058d0e29f..1c1b57b36 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -4,6 +4,12 @@ version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src" +target = "thumbv7em-none-eabihf" +features = ["stm32wb55rg"] + [dependencies] embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } From 6c13f381c45aefece76f39d5b14da6b60e02b7ee Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 17:12:34 -0500 Subject: [PATCH 1350/1575] stm32/wpan: get --release working --- embassy-stm32-wpan/src/ble.rs | 4 +- embassy-stm32-wpan/src/lib.rs | 74 ++++++++++--- embassy-stm32-wpan/src/mm.rs | 2 +- embassy-stm32-wpan/src/sys.rs | 6 +- embassy-stm32-wpan/src/unsafe_linked_list.rs | 105 ++++++++++++++----- tests/stm32/Cargo.toml | 2 +- tests/stm32/src/bin/tl_mbox.rs | 17 +-- 7 files changed, 156 insertions(+), 54 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 19955a8a3..57195ef9d 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -18,7 +18,7 @@ impl Ble { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); - TL_BLE_TABLE = MaybeUninit::new(BleTable { + TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable { pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), pcs_buffer: CS_BUFFER.as_ptr().cast(), pevt_queue: EVT_QUEUE.as_ptr().cast(), @@ -41,6 +41,8 @@ impl Ble { let event = EvtBox::new(event); EVT_CHANNEL.try_send(event).unwrap(); + + break; } } diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index c37b67dc4..000282f2b 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -4,6 +4,7 @@ pub mod fmt; use core::mem::MaybeUninit; +use core::sync::atomic::{compiler_fence, Ordering}; use cmd::CmdPacket; use embassy_futures::block_on; @@ -189,33 +190,76 @@ impl<'d> TlMbox<'d> { into_ref!(ipcc); unsafe { - TL_REF_TABLE = MaybeUninit::new(RefTable { - device_info_table: TL_DEVICE_INFO_TABLE.as_mut_ptr(), + TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { + device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), ble_table: TL_BLE_TABLE.as_ptr(), thread_table: TL_THREAD_TABLE.as_ptr(), sys_table: TL_SYS_TABLE.as_ptr(), mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), traces_table: TL_TRACES_TABLE.as_ptr(), mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), + // zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), + // lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), + // ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), }); - TL_SYS_TABLE = MaybeUninit::zeroed(); - TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed(); - TL_BLE_TABLE = MaybeUninit::zeroed(); - TL_THREAD_TABLE = MaybeUninit::zeroed(); - TL_MEM_MANAGER_TABLE = MaybeUninit::zeroed(); - TL_TRACES_TABLE = MaybeUninit::zeroed(); - TL_MAC_802_15_4_TABLE = MaybeUninit::zeroed(); + TL_SYS_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_DEVICE_INFO_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_BLE_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_THREAD_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_MEM_MANAGER_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); - EVT_POOL = MaybeUninit::zeroed(); - SYS_SPARE_EVT_BUF = MaybeUninit::zeroed(); - BLE_SPARE_EVT_BUF = MaybeUninit::zeroed(); + TL_TRACES_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_MAC_802_15_4_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); +// TL_ZIGBEE_TABLE +// .as_mut_ptr() +// .write_volatile(MaybeUninit::zeroed().assume_init()); +// TL_LLD_TESTS_TABLE +// .as_mut_ptr() +// .write_volatile(MaybeUninit::zeroed().assume_init()); +// TL_BLE_LLD_TABLE +// .as_mut_ptr() +// .write_volatile(MaybeUninit::zeroed().assume_init()); - CS_BUFFER = MaybeUninit::zeroed(); - BLE_CMD_BUFFER = MaybeUninit::zeroed(); - HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed(); + EVT_POOL + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + SYS_SPARE_EVT_BUF + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + BLE_SPARE_EVT_BUF + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + + { + BLE_CMD_BUFFER + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + HCI_ACL_DATA_BUFFER + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + CS_BUFFER + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + } } + compiler_fence(Ordering::SeqCst); + Ipcc::enable(config); sys::Sys::enable(); diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index ed13b0dbf..e46c63558 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -20,7 +20,7 @@ impl MemoryManager { LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); - TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable { + TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), blepool: EVT_POOL.as_ptr().cast(), diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index a19d12d27..0a1db7c1f 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -15,7 +15,7 @@ impl Sys { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); - TL_SYS_TABLE = MaybeUninit::new(SysTable { + TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), }) @@ -51,12 +51,14 @@ impl Sys { let node_ptr_ptr: *mut _ = &mut node_ptr; while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + LinkedListNode::get_next_node(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); let event = node_ptr.cast(); let event = EvtBox::new(event); EVT_CHANNEL.try_send(event).unwrap(); + + break; } } diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs index 52c106fa2..95f4bef44 100644 --- a/embassy-stm32-wpan/src/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -11,6 +11,8 @@ unused_mut )] +use core::ptr; + use cortex_m::interrupt; #[derive(Copy, Clone)] @@ -30,54 +32,102 @@ impl Default for LinkedListNode { } impl LinkedListNode { - pub unsafe fn init_head(mut list_head: *mut LinkedListNode) { - (*list_head).next = list_head; - (*list_head).prev = list_head; + pub unsafe fn init_head(mut p_list_head: *mut LinkedListNode) { + ptr::write_volatile( + p_list_head, + LinkedListNode { + next: p_list_head, + prev: p_list_head, + }, + ); } - pub unsafe fn is_empty(mut list_head: *mut LinkedListNode) -> bool { - interrupt::free(|_| ((*list_head).next) == list_head) + pub unsafe fn is_empty(mut p_list_head: *mut LinkedListNode) -> bool { + interrupt::free(|_| ptr::read_volatile(p_list_head).next == p_list_head) } - pub unsafe fn insert_head(mut list_head: *mut LinkedListNode, mut node: *mut LinkedListNode) { + /// Insert `node` after `list_head` and before the next node + pub unsafe fn insert_head(mut p_list_head: *mut LinkedListNode, mut p_node: *mut LinkedListNode) { interrupt::free(|_| { - (*node).next = (*list_head).next; - (*node).prev = list_head; - (*list_head).next = node; - (*(*node).next).prev = node; + let mut list_head = ptr::read_volatile(p_list_head); + let mut node_next = ptr::read_volatile(list_head.next); + let node = LinkedListNode { + next: list_head.next, + prev: p_list_head, + }; + + list_head.next = p_node; + node_next.prev = p_node; + + // All nodes must be written because they will all be seen by another core + ptr::write_volatile(p_node, node); + ptr::write_volatile(node.next, node_next); + ptr::write_volatile(p_list_head, list_head); }); } - pub unsafe fn insert_tail(mut list_head: *mut LinkedListNode, mut node: *mut LinkedListNode) { + /// Insert `node` before `list_tail` and after the second-to-last node + pub unsafe fn insert_tail(mut p_list_tail: *mut LinkedListNode, mut p_node: *mut LinkedListNode) { interrupt::free(|_| { - (*node).next = list_head; - (*node).prev = (*list_head).prev; - (*list_head).prev = node; - (*(*node).prev).next = node; + let mut list_tail = ptr::read_volatile(p_list_tail); + let mut node_prev = ptr::read_volatile(list_tail.prev); + let node = LinkedListNode { + next: p_list_tail, + prev: list_tail.prev, + }; + + list_tail.prev = p_node; + node_prev.next = p_node; + + // All nodes must be written because they will all be seen by another core + ptr::write_volatile(p_node, node); + ptr::write_volatile(node.prev, node_prev); + ptr::write_volatile(p_list_tail, list_tail); }); } /// Remove `node` from the linked list - pub unsafe fn remove_node(mut node: *mut LinkedListNode) { + // pub unsafe fn remove_node(mut node: *mut LinkedListNode) { + // interrupt::free(|_| { + // (*(*node).prev).next = (*node).next; + // (*(*node).next).prev = (*node).prev; + // }); + // } + + /// Remove `node` from the linked list + pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) { interrupt::free(|_| { - (*(*node).prev).next = (*node).next; - (*(*node).next).prev = (*node).prev; + let node = ptr::read_volatile(p_node); + let mut node_prev = ptr::read_volatile(node.prev); + let mut node_next = ptr::read_volatile(node.next); + + node_prev.next = node.next; + node_next.prev = node.prev; + + ptr::write_volatile(node.prev, node_prev); + ptr::write_volatile(node.next, node_next); }); } /// Remove `list_head` into `node` - pub unsafe fn remove_head(mut list_head: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { interrupt::free(|_| { - *node = (*list_head).next; - Self::remove_node((*list_head).next); + let list_head = ptr::read_volatile(p_list_head); + + // Allowed because a removed node is not seen by another core + *p_node = list_head.next; + Self::remove_node(list_head.next); }); } /// Remove `list_tail` into `node` - pub unsafe fn remove_tail(mut list_tail: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { interrupt::free(|_| { - *node = (*list_tail).prev; - Self::remove_node((*list_tail).prev); + let list_tail = ptr::read_volatile(p_list_tail); + + // Allowed because a removed node is not seen by another core + *p_node = list_tail.prev; + Self::remove_node(list_tail.prev); }); } @@ -114,9 +164,12 @@ impl LinkedListNode { }) } - pub unsafe fn get_next_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + pub unsafe fn get_next_node(mut p_ref_node: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { interrupt::free(|_| { - *node = (*ref_node).next; + let ref_node = ptr::read_volatile(p_ref_node); + + // Allowed because a removed node is not seen by another core + *p_node = ref_node.next; }); } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index dcc7d9b8a..487ef4626 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,7 +12,7 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma" ] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index b57519a54..26d9cc68e 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -11,6 +11,7 @@ use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::Config; use embassy_stm32_wpan::TlMbox; +use embassy_stm32_wpan::rc::RadioCoprocessor; use embassy_time::{Duration, Timer}; bind_interrupts!(struct Irqs{ @@ -50,14 +51,14 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_millis(50)).await; } - // let mut rc = RadioCoprocessor::new(mbox); - // - // let response = rc.read().await; - // info!("coprocessor ready {}", response); - // - // rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); - // let response = rc.read().await; - // info!("ble reset rsp {}", response); + let mut rc = RadioCoprocessor::new(mbox); + + let response = rc.read().await; + info!("coprocessor ready {}", response); + + rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); + let response = rc.read().await; + info!("ble reset rsp {}", response); info!("Test OK"); cortex_m::asm::bkpt(); From f15a0203e4c6f9b38099d3a2ebd40c554016a11d Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 17:13:25 -0500 Subject: [PATCH 1351/1575] rustfmt --- embassy-stm32-wpan/src/ble.rs | 2 -- embassy-stm32-wpan/src/mm.rs | 2 -- embassy-stm32-wpan/src/sys.rs | 2 -- 3 files changed, 6 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 57195ef9d..ed13cc495 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -1,5 +1,3 @@ -use core::mem::MaybeUninit; - use embassy_stm32::ipcc::Ipcc; use crate::cmd::{CmdPacket, CmdSerial}; diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index e46c63558..68db49b82 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -1,7 +1,5 @@ //! Memory manager routines -use core::mem::MaybeUninit; - use embassy_stm32::ipcc::Ipcc; use crate::evt::EvtPacket; diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index 0a1db7c1f..a7cbbae39 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,5 +1,3 @@ -use core::mem::MaybeUninit; - use embassy_stm32::ipcc::Ipcc; use crate::cmd::{CmdPacket, CmdSerial}; From 29513074ee0462e43b040b05d08a08430efee302 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 17:16:12 -0500 Subject: [PATCH 1352/1575] rustfmt --- tests/stm32/src/bin/tl_mbox.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 26d9cc68e..4669cbc62 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -10,8 +10,8 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::Config; -use embassy_stm32_wpan::TlMbox; use embassy_stm32_wpan::rc::RadioCoprocessor; +use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; bind_interrupts!(struct Irqs{ From c484f0715b28a7692b1c6c3b9d8caec50b84efc9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 17:17:10 -0500 Subject: [PATCH 1353/1575] rustfmt --- embassy-stm32-wpan/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 000282f2b..2852d6270 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -225,15 +225,15 @@ impl<'d> TlMbox<'d> { TL_MAC_802_15_4_TABLE .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); -// TL_ZIGBEE_TABLE -// .as_mut_ptr() -// .write_volatile(MaybeUninit::zeroed().assume_init()); -// TL_LLD_TESTS_TABLE -// .as_mut_ptr() -// .write_volatile(MaybeUninit::zeroed().assume_init()); -// TL_BLE_LLD_TABLE -// .as_mut_ptr() -// .write_volatile(MaybeUninit::zeroed().assume_init()); + // TL_ZIGBEE_TABLE + // .as_mut_ptr() + // .write_volatile(MaybeUninit::zeroed().assume_init()); + // TL_LLD_TESTS_TABLE + // .as_mut_ptr() + // .write_volatile(MaybeUninit::zeroed().assume_init()); + // TL_BLE_LLD_TABLE + // .as_mut_ptr() + // .write_volatile(MaybeUninit::zeroed().assume_init()); EVT_POOL .as_mut_ptr() From c1fc98c3136299780535bff22a842dd78df4a254 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 18:04:05 -0500 Subject: [PATCH 1354/1575] stm32/wpan: fix linked list bug --- embassy-stm32-wpan/src/sys.rs | 4 +- embassy-stm32-wpan/src/unsafe_linked_list.rs | 41 +++++++++++++------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index a7cbbae39..f41fac58a 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -49,14 +49,12 @@ impl Sys { let node_ptr_ptr: *mut _ = &mut node_ptr; while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - LinkedListNode::get_next_node(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); let event = node_ptr.cast(); let event = EvtBox::new(event); EVT_CHANNEL.try_send(event).unwrap(); - - break; } } diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs index 95f4bef44..c4a6c3a72 100644 --- a/embassy-stm32-wpan/src/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -86,25 +86,17 @@ impl LinkedListNode { }); } - /// Remove `node` from the linked list - // pub unsafe fn remove_node(mut node: *mut LinkedListNode) { - // interrupt::free(|_| { - // (*(*node).prev).next = (*node).next; - // (*(*node).next).prev = (*node).prev; - // }); - // } - /// Remove `node` from the linked list pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) { interrupt::free(|_| { + // Writes must occur sequentially, because if prev node, and next node are the same, both must be updated let node = ptr::read_volatile(p_node); let mut node_prev = ptr::read_volatile(node.prev); - let mut node_next = ptr::read_volatile(node.next); - node_prev.next = node.next; - node_next.prev = node.prev; - ptr::write_volatile(node.prev, node_prev); + + let mut node_next = ptr::read_volatile(node.next); + node_next.prev = node.prev; ptr::write_volatile(node.next, node_next); }); } @@ -116,7 +108,7 @@ impl LinkedListNode { // Allowed because a removed node is not seen by another core *p_node = list_head.next; - Self::remove_node(list_head.next); + Self::remove_node(*p_node); }); } @@ -127,7 +119,7 @@ impl LinkedListNode { // Allowed because a removed node is not seen by another core *p_node = list_tail.prev; - Self::remove_node(list_tail.prev); + Self::remove_node(*p_node); }); } @@ -179,3 +171,24 @@ impl LinkedListNode { }); } } + +#[allow(dead_code)] +unsafe fn debug_linked_list(mut p_node: *mut LinkedListNode) { + info!("iterating list from node: {:x}", p_node); + let mut p_current_node = p_node; + let mut i = 0; + loop { + let current_node = ptr::read_volatile(p_current_node); + info!( + "node (prev, current, next): {:x}, {:x}, {:x}", + current_node.prev, p_current_node, current_node.next + ); + + i += 1; + if i > 10 || current_node.next == p_node { + break; + } + + p_current_node = current_node.next; + } +} From bc0734eee5a9bef5d759e923f5545e8ff54deda7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 18:05:27 -0500 Subject: [PATCH 1355/1575] stm32/wpan: remove break --- embassy-stm32-wpan/src/ble.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index ed13cc495..ba369bb3a 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -39,8 +39,6 @@ impl Ble { let event = EvtBox::new(event); EVT_CHANNEL.try_send(event).unwrap(); - - break; } } From ae9983324d9ddfa747d7bfc11aecc401c0abfeb9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 21:10:42 -0500 Subject: [PATCH 1356/1575] stm32/wpan: cleanup linked list and fix edge cases --- embassy-stm32-wpan/src/ble.rs | 5 +- embassy-stm32-wpan/src/mm.rs | 6 +- embassy-stm32-wpan/src/sys.rs | 5 +- embassy-stm32-wpan/src/unsafe_linked_list.rs | 134 +++++++++++++------ 4 files changed, 96 insertions(+), 54 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index ba369bb3a..57348a925 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -29,11 +29,8 @@ impl Ble { pub(super) fn evt_handler() { unsafe { - let mut node_ptr = core::ptr::null_mut(); - let node_ptr_ptr: *mut _ = &mut node_ptr; - while !LinkedListNode::is_empty(EVT_QUEUE.as_mut_ptr()) { - LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + let node_ptr = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()); let event = node_ptr.cast(); let event = EvtBox::new(event); diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index 68db49b82..06063b89a 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -51,11 +51,9 @@ impl MemoryManager { /// gives free event buffers back to CPU2 from local buffer queue pub fn send_free_buf() { unsafe { - let mut node_ptr = core::ptr::null_mut(); - let node_ptr_ptr: *mut _ = &mut node_ptr; - while !LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { - LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), node_ptr_ptr); + let node_ptr = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); + LinkedListNode::insert_tail( (*(*TL_REF_TABLE.as_ptr()).mem_manager_table).pevt_free_buffer_queue, node_ptr, diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index f41fac58a..0cff5c746 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -45,11 +45,8 @@ impl Sys { pub fn evt_handler() { unsafe { - let mut node_ptr = core::ptr::null_mut(); - let node_ptr_ptr: *mut _ = &mut node_ptr; - while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr(), node_ptr_ptr); + let node_ptr = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); let event = node_ptr.cast(); let event = EvtBox::new(event); diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs index c4a6c3a72..ddec1afbc 100644 --- a/embassy-stm32-wpan/src/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -50,19 +50,33 @@ impl LinkedListNode { pub unsafe fn insert_head(mut p_list_head: *mut LinkedListNode, mut p_node: *mut LinkedListNode) { interrupt::free(|_| { let mut list_head = ptr::read_volatile(p_list_head); - let mut node_next = ptr::read_volatile(list_head.next); - let node = LinkedListNode { - next: list_head.next, - prev: p_list_head, - }; + if p_list_head != list_head.next { + let mut node_next = ptr::read_volatile(list_head.next); + let node = LinkedListNode { + next: list_head.next, + prev: p_list_head, + }; - list_head.next = p_node; - node_next.prev = p_node; + list_head.next = p_node; + node_next.prev = p_node; - // All nodes must be written because they will all be seen by another core - ptr::write_volatile(p_node, node); - ptr::write_volatile(node.next, node_next); - ptr::write_volatile(p_list_head, list_head); + // All nodes must be written because they will all be seen by another core + ptr::write_volatile(p_node, node); + ptr::write_volatile(node.next, node_next); + ptr::write_volatile(p_list_head, list_head); + } else { + let node = LinkedListNode { + next: list_head.next, + prev: p_list_head, + }; + + list_head.next = p_node; + list_head.prev = p_node; + + // All nodes must be written because they will all be seen by another core + ptr::write_volatile(p_node, node); + ptr::write_volatile(p_list_head, list_head); + } }); } @@ -70,57 +84,84 @@ impl LinkedListNode { pub unsafe fn insert_tail(mut p_list_tail: *mut LinkedListNode, mut p_node: *mut LinkedListNode) { interrupt::free(|_| { let mut list_tail = ptr::read_volatile(p_list_tail); - let mut node_prev = ptr::read_volatile(list_tail.prev); - let node = LinkedListNode { - next: p_list_tail, - prev: list_tail.prev, - }; + if p_list_tail != list_tail.prev { + let mut node_prev = ptr::read_volatile(list_tail.prev); + let node = LinkedListNode { + next: p_list_tail, + prev: list_tail.prev, + }; - list_tail.prev = p_node; - node_prev.next = p_node; + list_tail.prev = p_node; + node_prev.next = p_node; - // All nodes must be written because they will all be seen by another core - ptr::write_volatile(p_node, node); - ptr::write_volatile(node.prev, node_prev); - ptr::write_volatile(p_list_tail, list_tail); + // All nodes must be written because they will all be seen by another core + ptr::write_volatile(p_node, node); + ptr::write_volatile(node.prev, node_prev); + ptr::write_volatile(p_list_tail, list_tail); + } else { + let node = LinkedListNode { + next: p_list_tail, + prev: list_tail.prev, + }; + + list_tail.prev = p_node; + list_tail.next = p_node; + + // All nodes must be written because they will all be seen by another core + ptr::write_volatile(p_node, node); + ptr::write_volatile(p_list_tail, list_tail); + } }); } /// Remove `node` from the linked list pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) { interrupt::free(|_| { - // Writes must occur sequentially, because if prev node, and next node are the same, both must be updated let node = ptr::read_volatile(p_node); - let mut node_prev = ptr::read_volatile(node.prev); - node_prev.next = node.next; - ptr::write_volatile(node.prev, node_prev); + if node.next != node.prev { + let mut node_next = ptr::read_volatile(node.next); + let mut node_prev = ptr::read_volatile(node.prev); - let mut node_next = ptr::read_volatile(node.next); - node_next.prev = node.prev; - ptr::write_volatile(node.next, node_next); + node_prev.next = node.next; + node_next.prev = node.prev; + + ptr::write_volatile(node.next, node_next); + ptr::write_volatile(node.prev, node_prev); + } else { + let mut node_next = ptr::read_volatile(node.next); + + node_next.next = node.next; + node_next.prev = node.prev; + + ptr::write_volatile(node.next, node_next); + } }); } - /// Remove `list_head` into `node` - pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { + /// Remove `list_head` and return a pointer to the `node`. + pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> *mut LinkedListNode { interrupt::free(|_| { let list_head = ptr::read_volatile(p_list_head); // Allowed because a removed node is not seen by another core - *p_node = list_head.next; - Self::remove_node(*p_node); - }); + let p_node = list_head.next; + Self::remove_node(p_node); + + p_node + }) } - /// Remove `list_tail` into `node` - pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { + /// Remove `list_tail` and return a pointer to the `node`. + pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> *mut LinkedListNode { interrupt::free(|_| { let list_tail = ptr::read_volatile(p_list_tail); // Allowed because a removed node is not seen by another core - *p_node = list_tail.prev; - Self::remove_node(*p_node); - }); + let p_node = list_tail.prev; + Self::remove_node(p_node); + + p_node + }) } pub unsafe fn insert_node_after(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { @@ -130,6 +171,8 @@ impl LinkedListNode { (*ref_node).next = node; (*(*node).next).prev = node; }); + + todo!("this function has not been converted to volatile semantics"); } pub unsafe fn insert_node_before(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) { @@ -139,6 +182,8 @@ impl LinkedListNode { (*ref_node).prev = node; (*(*node).prev).next = node; }); + + todo!("this function has not been converted to volatile semantics"); } pub unsafe fn get_size(mut list_head: *mut LinkedListNode) -> usize { @@ -153,7 +198,9 @@ impl LinkedListNode { } size - }) + }); + + todo!("this function has not been converted to volatile semantics"); } pub unsafe fn get_next_node(mut p_ref_node: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { @@ -165,9 +212,12 @@ impl LinkedListNode { }); } - pub unsafe fn get_prev_node(mut ref_node: *mut LinkedListNode, mut node: *mut *mut LinkedListNode) { + pub unsafe fn get_prev_node(mut p_ref_node: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { interrupt::free(|_| { - *node = (*ref_node).prev; + let ref_node = ptr::read_volatile(p_ref_node); + + // Allowed because a removed node is not seen by another core + *p_node = ref_node.prev; }); } } From 4601f4e1ebc94aa50edd224af087168bc996d7af Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 13 Jun 2023 21:15:01 -0500 Subject: [PATCH 1357/1575] stm32/wpan: minor linked list cleanpu --- embassy-stm32-wpan/src/unsafe_linked_list.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs index ddec1afbc..a312178b3 100644 --- a/embassy-stm32-wpan/src/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -203,22 +203,22 @@ impl LinkedListNode { todo!("this function has not been converted to volatile semantics"); } - pub unsafe fn get_next_node(mut p_ref_node: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { + pub unsafe fn get_next_node(mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode { interrupt::free(|_| { let ref_node = ptr::read_volatile(p_ref_node); // Allowed because a removed node is not seen by another core - *p_node = ref_node.next; - }); + ref_node.next + }) } - pub unsafe fn get_prev_node(mut p_ref_node: *mut LinkedListNode, mut p_node: *mut *mut LinkedListNode) { + pub unsafe fn get_prev_node(mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode { interrupt::free(|_| { let ref_node = ptr::read_volatile(p_ref_node); // Allowed because a removed node is not seen by another core - *p_node = ref_node.prev; - }); + ref_node.prev + }) } } From c94ba8489289789e295a248720c96040b2dc724c Mon Sep 17 00:00:00 2001 From: Kevin Lannen Date: Wed, 14 Jun 2023 10:44:51 -0600 Subject: [PATCH 1358/1575] stm32g4: PLL: Add support for configuring PLL_P and PLL_Q --- embassy-stm32/src/rcc/g4.rs | 207 ++++++++++++++++++++++++++------ examples/stm32g4/src/bin/pll.rs | 15 ++- 2 files changed, 184 insertions(+), 38 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 2b52416b2..9401af4c3 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -17,7 +17,7 @@ pub const LSI_FREQ: Hertz = Hertz(32_000); pub enum ClockSrc { HSE(Hertz), HSI16, - PLLCLK(PllSrc, PllM, PllN, PllR), + PLL, } /// AHB prescaler @@ -60,6 +60,68 @@ impl Into for PllSrc { } } +seq_macro::seq!(P in 2..=31 { + /// Output divider for the PLL P output. + #[derive(Clone, Copy)] + pub enum PllP { + // Note: If PLL P is set to 0 the PLLP bit controls the output division. There does not seem to + // a good reason to do this so the API does not support it. + // Div1 is invalid + #( + Div~P, + )* + } + + impl From for u8 { + /// Returns the register value for the P output divider. + fn from(val: PllP) -> u8 { + match val { + #( + PllP::Div~P => P, + )* + } + } + } +}); + +impl PllP { + /// Returns the numeric value of the P output divider. + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + val as u32 + } +} + +/// Output divider for the PLL Q output. +#[derive(Clone, Copy)] +pub enum PllQ { + Div2, + Div4, + Div6, + Div8, +} + +impl PllQ { + /// Returns the numeric value of the Q output divider. + pub fn to_div(self) -> u32 { + let val: u8 = self.into(); + (val as u32 + 1) * 2 + } +} + +impl From for u8 { + /// Returns the register value for the Q output divider. + fn from(val: PllQ) -> u8 { + match val { + PllQ::Div2 => 0b00, + PllQ::Div4 => 0b01, + PllQ::Div6 => 0b10, + PllQ::Div8 => 0b11, + } + } +} + +/// Output divider for the PLL R output. #[derive(Clone, Copy)] pub enum PllR { Div2, @@ -69,6 +131,7 @@ pub enum PllR { } impl PllR { + /// Returns the numeric value of the R output divider. pub fn to_div(self) -> u32 { let val: u8 = self.into(); (val as u32 + 1) * 2 @@ -76,6 +139,7 @@ impl PllR { } impl From for u8 { + /// Returns the register value for the R output divider. fn from(val: PllR) -> u8 { match val { PllR::Div2 => 0b00, @@ -87,6 +151,7 @@ impl From for u8 { } seq_macro::seq!(N in 8..=127 { + /// Multiplication factor for the PLL VCO input clock. #[derive(Clone, Copy)] pub enum PllN { #( @@ -95,6 +160,7 @@ seq_macro::seq!(N in 8..=127 { } impl From for u8 { + /// Returns the register value for the N multiplication factor. fn from(val: PllN) -> u8 { match val { #( @@ -105,6 +171,7 @@ seq_macro::seq!(N in 8..=127 { } impl PllN { + /// Returns the numeric value of the N multiplication factor. pub fn to_mul(self) -> u32 { match self { #( @@ -115,7 +182,7 @@ seq_macro::seq!(N in 8..=127 { } }); -// Pre-division +/// PLL Pre-division. This must be set such that the PLL input is between 2.66 MHz and 16 MHz. #[derive(Copy, Clone)] pub enum PllM { Div1, @@ -137,6 +204,7 @@ pub enum PllM { } impl PllM { + /// Returns the numeric value of the M pre-division. pub fn to_div(self) -> u32 { let val: u8 = self.into(); val as u32 + 1 @@ -144,6 +212,7 @@ impl PllM { } impl From for u8 { + /// Returns the register value for the M pre-division. fn from(val: PllM) -> u8 { match val { PllM::Div1 => 0b0000, @@ -166,6 +235,31 @@ impl From for u8 { } } +/// PLL Configuration +/// +/// Use this struct to configure the PLL source, input frequency, multiplication factor, and output +/// dividers. Be sure to keep check the datasheet for your specific part for the appropriate +/// frequency ranges for each of these settings. +pub struct Pll { + /// PLL Source clock selection. + pub source: PllSrc, + + /// PLL pre-divider + pub prediv_m: PllM, + + /// PLL multiplication factor for VCO + pub mul_n: PllN, + + /// PLL division factor for P clock (ADC Clock) + pub div_p: Option, + + /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI) + pub div_q: Option, + + /// PLL division factor for R clock (SYSCLK) + pub div_r: Option, +} + impl AHBPrescaler { const fn div(self) -> u32 { match self { @@ -229,6 +323,9 @@ pub struct Config { pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, pub low_power_run: bool, + /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration + /// MUST turn on the PLLR output. + pub pll: Option, } impl Default for Config { @@ -240,11 +337,80 @@ impl Default for Config { apb1_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided, low_power_run: false, + pll: None, } } } +pub struct PllFreq { + pub pll_p: Option, + pub pll_q: Option, + pub pll_r: Option, +} + pub(crate) unsafe fn init(config: Config) { + let pll_freq = config.pll.map(|pll_config| { + let src_freq = match pll_config.source { + PllSrc::HSI16 => { + RCC.cr().write(|w| w.set_hsion(true)); + while !RCC.cr().read().hsirdy() {} + + HSI_FREQ.0 + } + PllSrc::HSE(freq) => { + RCC.cr().write(|w| w.set_hseon(true)); + while !RCC.cr().read().hserdy() {} + freq.0 + } + }; + + // Disable PLL before configuration + RCC.cr().modify(|w| w.set_pllon(false)); + while RCC.cr().read().pllrdy() {} + + let internal_freq = src_freq / pll_config.prediv_m.to_div() * pll_config.mul_n.to_mul(); + + RCC.pllcfgr().write(|w| { + w.set_plln(pll_config.mul_n.into()); + w.set_pllm(pll_config.prediv_m.into()); + w.set_pllsrc(pll_config.source.into()); + }); + + let pll_p_freq = pll_config.div_p.map(|div_p| { + RCC.pllcfgr().modify(|w| { + w.set_pllpdiv(div_p.into()); + w.set_pllpen(true); + }); + Hertz(internal_freq / div_p.to_div()) + }); + + let pll_q_freq = pll_config.div_q.map(|div_q| { + RCC.pllcfgr().modify(|w| { + w.set_pllq(div_q.into()); + w.set_pllqen(true); + }); + Hertz(internal_freq / div_q.to_div()) + }); + + let pll_r_freq = pll_config.div_r.map(|div_r| { + RCC.pllcfgr().modify(|w| { + w.set_pllr(div_r.into()); + w.set_pllren(true); + }); + Hertz(internal_freq / div_r.to_div()) + }); + + // Enable the PLL + RCC.cr().modify(|w| w.set_pllon(true)); + while !RCC.cr().read().pllrdy() {} + + PllFreq { + pll_p: pll_p_freq, + pll_q: pll_q_freq, + pll_r: pll_r_freq, + } + }); + let (sys_clk, sw) = match config.mux { ClockSrc::HSI16 => { // Enable HSI16 @@ -260,29 +426,12 @@ pub(crate) unsafe fn init(config: Config) { (freq.0, Sw::HSE) } - ClockSrc::PLLCLK(src, prediv, mul, div) => { - let src_freq = match src { - PllSrc::HSI16 => { - // Enable HSI16 as clock source for PLL - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} + ClockSrc::PLL => { + assert!(pll_freq.is_some()); + assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); - HSI_FREQ.0 - } - PllSrc::HSE(freq) => { - // Enable HSE as clock source for PLL - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} + let freq = pll_freq.unwrap().pll_r.unwrap().0; - freq.0 - } - }; - - // Make sure PLL is disabled while we configure it - RCC.cr().modify(|w| w.set_pllon(false)); - while RCC.cr().read().pllrdy() {} - - let freq = src_freq / prediv.to_div() * mul.to_mul() / div.to_div(); assert!(freq <= 170_000_000); if freq >= 150_000_000 { @@ -316,18 +465,6 @@ pub(crate) unsafe fn init(config: Config) { } } - RCC.pllcfgr().write(move |w| { - w.set_plln(mul.into()); - w.set_pllm(prediv.into()); - w.set_pllr(div.into()); - w.set_pllsrc(src.into()); - }); - - // Enable PLL - RCC.cr().modify(|w| w.set_pllon(true)); - while !RCC.cr().read().pllrdy() {} - RCC.pllcfgr().modify(|w| w.set_pllren(true)); - (freq, Sw::PLLRCLK) } }; diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 8cee41e9b..ef7d4800c 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ClockSrc, PllM, PllN, PllR, PllSrc}; +use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSrc}; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -13,8 +13,17 @@ use {defmt_rtt as _, panic_probe as _}; async fn main(_spawner: Spawner) { let mut config = Config::default(); - // Configure PLL to max frequency of 170 MHz - config.rcc.mux = ClockSrc::PLLCLK(PllSrc::HSI16, PllM::Div4, PllN::Mul85, PllR::Div2); + config.rcc.pll = Some(Pll { + source: PllSrc::HSI16, + prediv_m: PllM::Div4, + mul_n: PllN::Mul85, + div_p: None, + div_q: None, + // Main system clock at 170 MHz + div_r: Some(PllR::Div2), + }); + + config.rcc.mux = ClockSrc::PLL; let _p = embassy_stm32::init(config); info!("Hello World!"); From 61aa6b5236b68b037db1c5f349e8183a2980ffc5 Mon Sep 17 00:00:00 2001 From: Kevin Lannen Date: Wed, 14 Jun 2023 11:07:19 -0600 Subject: [PATCH 1359/1575] STM32G4: Add USB Serial example --- examples/stm32g4/Cargo.toml | 1 + examples/stm32g4/src/bin/usb_serial.rs | 110 +++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 examples/stm32g4/src/bin/usb_serial.rs diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index f94df2dd3..fbfbc6408 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -10,6 +10,7 @@ embassy-executor = { version = "0.2.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", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs new file mode 100644 index 000000000..ecbe3a6e6 --- /dev/null +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -0,0 +1,110 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllQ, PllR, PllSrc}; +use embassy_stm32::time::Hertz; +use embassy_stm32::usb::{self, Driver, Instance}; +use embassy_stm32::{bind_interrupts, pac, peripherals, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USB_LP => usb::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + config.rcc.pll = Some(Pll { + source: PllSrc::HSE(Hertz(8000000)), + prediv_m: PllM::Div2, + mul_n: PllN::Mul72, + div_p: None, + // USB and CAN at 48 MHz + div_q: Some(PllQ::Div6), + // Main system clock at 144 MHz + div_r: Some(PllR::Div2), + }); + + config.rcc.mux = ClockSrc::PLL; + + let p = embassy_stm32::init(config); + info!("Hello World!"); + + unsafe { + pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10)); + } + + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); + + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-Serial Example"); + config.serial_number = Some("123456"); + + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + let mut usb = builder.build(); + + let usb_fut = usb.run(); + + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From 837950cd7440460f0e1f89e1ab9607f1a9d656ab Mon Sep 17 00:00:00 2001 From: Peter Gibson Date: Wed, 14 Jun 2023 11:56:44 +1000 Subject: [PATCH 1360/1575] ensure DR is read to clear idle/overflow interrupt when they occur independently of the rxne --- embassy-stm32/src/usart/buffered.rs | 37 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 613da5674..f55123e1d 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -21,27 +21,32 @@ impl interrupt::typelevel::Handler for Interrupt // RX unsafe { let sr = sr(r).read(); + // Reading DR clears the rxne, error and idle interrupt flags on v1. + let dr = if sr.ore() || sr.idle() || sr.rxne() { + Some(rdr(r).read_volatile()) + } + else { + None + }; clear_interrupt_flags(r, sr); + if sr.pe() { + warn!("Parity error"); + } + if sr.fe() { + warn!("Framing error"); + } + if sr.ne() { + warn!("Noise error"); + } + if sr.ore() { + warn!("Overrun error"); + } if sr.rxne() { - if sr.pe() { - warn!("Parity error"); - } - if sr.fe() { - warn!("Framing error"); - } - if sr.ne() { - warn!("Noise error"); - } - if sr.ore() { - warn!("Overrun error"); - } - let mut rx_writer = state.rx_buf.writer(); let buf = rx_writer.push_slice(); if !buf.is_empty() { - // This read also clears the error and idle interrupt flags on v1. - buf[0] = rdr(r).read_volatile(); + buf[0] = dr.unwrap(); rx_writer.push_done(1); } else { // FIXME: Should we disable any further RX interrupts when the buffer becomes full. @@ -54,7 +59,7 @@ impl interrupt::typelevel::Handler for Interrupt if sr.idle() { state.rx_waker.wake(); - }; + } } // TX From d23717904b283fa46a02525acfb42eb9f42e61e3 Mon Sep 17 00:00:00 2001 From: Peter Gibson Date: Thu, 15 Jun 2023 18:33:01 +1000 Subject: [PATCH 1361/1575] fix formatting --- embassy-stm32/src/usart/buffered.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index f55123e1d..e04b3ea0f 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -48,7 +48,8 @@ impl interrupt::typelevel::Handler for Interrupt if !buf.is_empty() { buf[0] = dr.unwrap(); rx_writer.push_done(1); - } else { + } + else { // FIXME: Should we disable any further RX interrupts when the buffer becomes full. } From d236f3dbf9b53c5e646020946d8da1458eb591a1 Mon Sep 17 00:00:00 2001 From: Peter Gibson Date: Thu, 15 Jun 2023 18:35:58 +1000 Subject: [PATCH 1362/1575] actually fix formatting --- embassy-stm32/src/usart/buffered.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index e04b3ea0f..530760bd1 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -24,8 +24,7 @@ impl interrupt::typelevel::Handler for Interrupt // Reading DR clears the rxne, error and idle interrupt flags on v1. let dr = if sr.ore() || sr.idle() || sr.rxne() { Some(rdr(r).read_volatile()) - } - else { + } else { None }; clear_interrupt_flags(r, sr); @@ -48,8 +47,7 @@ impl interrupt::typelevel::Handler for Interrupt if !buf.is_empty() { buf[0] = dr.unwrap(); rx_writer.push_done(1); - } - else { + } else { // FIXME: Should we disable any further RX interrupts when the buffer becomes full. } From 837ebe405feabfbee92e9f5e4fc36a5ac56a281c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 16 Jun 2023 01:32:18 +0200 Subject: [PATCH 1363/1575] rp: update rp-pac. --- embassy-rp/Cargo.toml | 2 +- embassy-rp/src/adc.rs | 120 +++---- embassy-rp/src/clocks.rs | 82 ++--- embassy-rp/src/critical_section_impl.rs | 19 +- embassy-rp/src/dma.rs | 48 ++- embassy-rp/src/flash.rs | 2 +- embassy-rp/src/float/div.rs | 70 ++-- embassy-rp/src/gpio.rs | 170 +++++---- embassy-rp/src/i2c.rs | 312 ++++++++-------- embassy-rp/src/lib.rs | 30 +- embassy-rp/src/multicore.rs | 56 ++- embassy-rp/src/pio.rs | 450 ++++++++++-------------- embassy-rp/src/pwm.rs | 88 ++--- embassy-rp/src/reset.rs | 4 +- embassy-rp/src/rtc/mod.rs | 81 ++--- embassy-rp/src/spi.rs | 207 +++++------ embassy-rp/src/timer.rs | 30 +- embassy-rp/src/uart/buffered.rs | 275 +++++++-------- embassy-rp/src/uart/mod.rs | 317 ++++++++--------- embassy-rp/src/usb.rs | 299 ++++++++-------- embassy-rp/src/watchdog.rs | 60 ++-- tests/rp/src/bin/float.rs | 10 +- 22 files changed, 1239 insertions(+), 1493 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b68f95385..49aa6a4d5 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -76,7 +76,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "4" } +rp-pac = { version = "5" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index f29c4dfe1..b96d5a4a8 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -50,16 +50,14 @@ impl<'d> Adc<'d> { _irq: impl Binding, _config: Config, ) -> Self { - 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() {} - } + 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 interrupt::ADC_IRQ_FIFO.unpend(); @@ -70,80 +68,70 @@ impl<'d> Adc<'d> { 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; - } + 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> + Pin>(&mut self, pin: &mut PIN) -> u16 { let r = Self::regs(); - unsafe { - // disable pull-down and pull-up resistors - // pull-down resistors are enabled by default - pin.pad_ctrl().modify(|w| { - w.set_ie(true); - let (pu, pd) = (false, false); - w.set_pue(pu); - w.set_pde(pd); - }); - r.cs().modify(|w| { - w.set_ainsel(PIN::channel()); - w.set_start_once(true) - }); - Self::wait_for_ready().await; - r.result().read().result().into() - } + // disable pull-down and pull-up resistors + // pull-down resistors are enabled by default + pin.pad_ctrl().modify(|w| { + w.set_ie(true); + let (pu, pd) = (false, false); + w.set_pue(pu); + w.set_pde(pd); + }); + 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) - }); + r.cs().modify(|w| w.set_ts_en(true)); + if !r.cs().read().ready() { Self::wait_for_ready().await; - r.result().read().result().into() } + 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() - } + 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() - } + 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() } } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 67439fda3..4c6223107 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -542,7 +542,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { reset::unreset_wait(peris); } -unsafe fn configure_rosc(config: RoscConfig) -> u32 { +fn configure_rosc(config: RoscConfig) -> u32 { let p = pac::ROSC; p.freqa().write(|w| { @@ -620,7 +620,7 @@ pub fn clk_rtc_freq() -> u16 { CLOCKS.rtc.load(Ordering::Relaxed) } -unsafe fn start_xosc(crystal_hz: u32) { +fn start_xosc(crystal_hz: u32) { pac::XOSC .ctrl() .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ)); @@ -635,7 +635,7 @@ unsafe fn start_xosc(crystal_hz: u32) { } #[inline(always)] -unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { +fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { let ref_freq = input_freq / config.refdiv as u32; assert!(config.fbdiv >= 16 && config.fbdiv <= 320); assert!(config.post_div1 >= 1 && config.post_div1 <= 7); @@ -700,9 +700,7 @@ impl<'d, T: Pin> Gpin<'d, T> { pub fn new(gpin: impl Peripheral

+ 'd) -> Gpin<'d, P> { into_ref!(gpin); - unsafe { - gpin.io().ctrl().write(|w| w.set_funcsel(0x08)); - } + gpin.io().ctrl().write(|w| w.set_funcsel(0x08)); Gpin { gpin: gpin.map_into(), @@ -717,12 +715,10 @@ impl<'d, T: Pin> Gpin<'d, T> { impl<'d, T: Pin> Drop for Gpin<'d, T> { fn drop(&mut self) { - unsafe { - self.gpin - .io() - .ctrl() - .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); - } + self.gpin + .io() + .ctrl() + .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); } } @@ -768,53 +764,43 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn new(gpout: impl Peripheral

+ 'd) -> Self { into_ref!(gpout); - unsafe { - gpout.io().ctrl().write(|w| w.set_funcsel(0x08)); - } + gpout.io().ctrl().write(|w| w.set_funcsel(0x08)); Self { gpout } } pub fn set_div(&self, int: u32, frac: u8) { - unsafe { - let c = pac::CLOCKS; - c.clk_gpout_div(self.gpout.number()).write(|w| { - w.set_int(int); - w.set_frac(frac); - }); - } + let c = pac::CLOCKS; + c.clk_gpout_div(self.gpout.number()).write(|w| { + w.set_int(int); + w.set_frac(frac); + }); } pub fn set_src(&self, src: GpoutSrc) { - unsafe { - let c = pac::CLOCKS; - c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { - w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _)); - }); - } + let c = pac::CLOCKS; + c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { + w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _)); + }); } pub fn enable(&self) { - unsafe { - let c = pac::CLOCKS; - c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { - w.set_enable(true); - }); - } + let c = pac::CLOCKS; + c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { + w.set_enable(true); + }); } pub fn disable(&self) { - unsafe { - let c = pac::CLOCKS; - c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { - w.set_enable(false); - }); - } + let c = pac::CLOCKS; + c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { + w.set_enable(false); + }); } pub fn get_freq(&self) -> u32 { let c = pac::CLOCKS; - let src = unsafe { c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc() }; + let src = c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc(); let base = match src { ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), @@ -831,7 +817,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { _ => unreachable!(), }; - let div = unsafe { c.clk_gpout_div(self.gpout.number()).read() }; + let div = c.clk_gpout_div(self.gpout.number()).read(); let int = if div.int() == 0 { 65536 } else { div.int() } as u64; let frac = div.frac() as u64; @@ -842,12 +828,10 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { fn drop(&mut self) { self.disable(); - unsafe { - self.gpout - .io() - .ctrl() - .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); - } + self.gpout + .io() + .ctrl() + .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); } } @@ -864,7 +848,7 @@ impl RoscRng { let mut acc = 0; for _ in 0..u8::BITS { acc <<= 1; - acc |= unsafe { random_reg.read().randombit() as u8 }; + acc |= random_reg.read().randombit() as u8; } acc } diff --git a/embassy-rp/src/critical_section_impl.rs b/embassy-rp/src/critical_section_impl.rs index ce284c856..d233e6fab 100644 --- a/embassy-rp/src/critical_section_impl.rs +++ b/embassy-rp/src/critical_section_impl.rs @@ -103,14 +103,11 @@ where /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is /// already in use somewhere else. pub fn try_claim() -> Option { - // Safety: We're only reading from this register - unsafe { - let lock = pac::SIO.spinlock(N).read(); - if lock > 0 { - Some(Self(core::marker::PhantomData)) - } else { - None - } + let lock = pac::SIO.spinlock(N).read(); + if lock > 0 { + Some(Self(core::marker::PhantomData)) + } else { + None } } @@ -120,10 +117,8 @@ where /// /// Only call this function if you hold the spin-lock. pub unsafe fn release() { - unsafe { - // Write (any value): release the lock - pac::SIO.spinlock(N).write_value(1); - } + // Write (any value): release the lock + pac::SIO.spinlock(N).write_value(1); } } diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 25819f03e..1a458778c 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -14,7 +14,7 @@ use crate::{interrupt, pac, peripherals}; #[cfg(feature = "rt")] #[interrupt] -unsafe fn DMA_IRQ_0() { +fn DMA_IRQ_0() { let ints0 = pac::DMA.ints0().read().ints0(); for channel in 0..CHANNEL_COUNT { let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read(); @@ -128,28 +128,26 @@ fn copy_inner<'a, C: Channel>( ) -> Transfer<'a, C> { into_ref!(ch); - unsafe { - let p = ch.regs(); + let p = ch.regs(); - p.read_addr().write_value(from as u32); - p.write_addr().write_value(to as u32); - p.trans_count().write_value(len as u32); + p.read_addr().write_value(from as u32); + p.write_addr().write_value(to as u32); + p.trans_count().write_value(len as u32); - compiler_fence(Ordering::SeqCst); + compiler_fence(Ordering::SeqCst); - p.ctrl_trig().write(|w| { - // TODO: Add all DREQ options to pac vals::TreqSel, and use - // `set_treq:sel` - w.0 = ((dreq as u32) & 0x3f) << 15usize; - w.set_data_size(data_size); - w.set_incr_read(incr_read); - w.set_incr_write(incr_write); - w.set_chain_to(ch.number()); - w.set_en(true); - }); + p.ctrl_trig().write(|w| { + // TODO: Add all DREQ options to pac vals::TreqSel, and use + // `set_treq:sel` + w.0 = ((dreq as u32) & 0x3f) << 15usize; + w.set_data_size(data_size); + w.set_incr_read(incr_read); + w.set_incr_write(incr_write); + w.set_chain_to(ch.number()); + w.set_en(true); + }); - compiler_fence(Ordering::SeqCst); - } + compiler_fence(Ordering::SeqCst); Transfer::new(ch) } @@ -169,12 +167,10 @@ impl<'a, C: Channel> Transfer<'a, C> { impl<'a, C: Channel> Drop for Transfer<'a, C> { fn drop(&mut self) { let p = self.channel.regs(); - unsafe { - pac::DMA - .chan_abort() - .modify(|m| m.set_chan_abort(1 << self.channel.number())); - while p.ctrl_trig().read().busy() {} - } + pac::DMA + .chan_abort() + .modify(|m| m.set_chan_abort(1 << self.channel.number())); + while p.ctrl_trig().read().busy() {} } } @@ -186,7 +182,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { // calls to wake will deregister the waker. CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); - if unsafe { self.channel.regs().ctrl_trig().read().busy() } { + if self.channel.regs().ctrl_trig().read().busy() { Poll::Pending } else { Poll::Ready(()) diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 5d928abad..96d2d4541 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -167,7 +167,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { /// - DMA must not access flash memory unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> { // Make sure we're running on CORE0 - let core_id: u32 = unsafe { pac::SIO.cpuid().read() }; + let core_id: u32 = pac::SIO.cpuid().read(); if core_id != 0 { return Err(Error::InvalidCore); } diff --git a/embassy-rp/src/float/div.rs b/embassy-rp/src/float/div.rs index 094dec446..aff0dcb07 100644 --- a/embassy-rp/src/float/div.rs +++ b/embassy-rp/src/float/div.rs @@ -17,45 +17,43 @@ where { let sio = rp_pac::SIO; - unsafe { - // Since we can't save the signed-ness of the calculation, we have to make - // sure that there's at least an 8 cycle delay before we read the result. - // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads. - // Since we can't be sure the Rust implementation will optimize to the same, - // just use an explicit wait. - while !sio.div().csr().read().ready() {} + // Since we can't save the signed-ness of the calculation, we have to make + // sure that there's at least an 8 cycle delay before we read the result. + // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads. + // Since we can't be sure the Rust implementation will optimize to the same, + // just use an explicit wait. + while !sio.div().csr().read().ready() {} - // Read the quotient last, since that's what clears the dirty flag - let dividend = sio.div().udividend().read(); - let divisor = sio.div().udivisor().read(); - let remainder = sio.div().remainder().read(); - let quotient = sio.div().quotient().read(); + // Read the quotient last, since that's what clears the dirty flag + let dividend = sio.div().udividend().read(); + let divisor = sio.div().udivisor().read(); + let remainder = sio.div().remainder().read(); + let quotient = sio.div().quotient().read(); - // If we get interrupted here (before a write sets the DIRTY flag) its fine, since - // we have the full state, so the interruptor doesn't have to restore it. Once the - // write happens and the DIRTY flag is set, the interruptor becomes responsible for - // restoring our state. - let result = f(); + // If we get interrupted here (before a write sets the DIRTY flag) its fine, since + // we have the full state, so the interruptor doesn't have to restore it. Once the + // write happens and the DIRTY flag is set, the interruptor becomes responsible for + // restoring our state. + let result = f(); - // If we are interrupted here, then the interruptor will start an incorrect calculation - // using a wrong divisor, but we'll restore the divisor and result ourselves correctly. - // This sets DIRTY, so any interruptor will save the state. - sio.div().udividend().write_value(dividend); - // If we are interrupted here, the the interruptor may start the calculation using - // incorrectly signed inputs, but we'll restore the result ourselves. - // This sets DIRTY, so any interruptor will save the state. - sio.div().udivisor().write_value(divisor); - // If we are interrupted here, the interruptor will have restored everything but the - // quotient may be wrongly signed. If the calculation started by the above writes is - // still ongoing it is stopped, so it won't replace the result we're restoring. - // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state. - sio.div().remainder().write_value(remainder); - // State fully restored after the quotient write. This sets both DIRTY and READY, so - // whatever we may have interrupted can read the result. - sio.div().quotient().write_value(quotient); + // If we are interrupted here, then the interruptor will start an incorrect calculation + // using a wrong divisor, but we'll restore the divisor and result ourselves correctly. + // This sets DIRTY, so any interruptor will save the state. + sio.div().udividend().write_value(dividend); + // If we are interrupted here, the the interruptor may start the calculation using + // incorrectly signed inputs, but we'll restore the result ourselves. + // This sets DIRTY, so any interruptor will save the state. + sio.div().udivisor().write_value(divisor); + // If we are interrupted here, the interruptor will have restored everything but the + // quotient may be wrongly signed. If the calculation started by the above writes is + // still ongoing it is stopped, so it won't replace the result we're restoring. + // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state. + sio.div().remainder().write_value(remainder); + // State fully restored after the quotient write. This sets both DIRTY and READY, so + // whatever we may have interrupted can read the result. + sio.div().quotient().write_value(quotient); - result - } + result } fn save_divider(f: F) -> R @@ -63,7 +61,7 @@ where F: FnOnce() -> R, { let sio = rp_pac::SIO; - if unsafe { !sio.div().csr().read().dirty() } { + if !sio.div().csr().read().dirty() { // Not dirty, so nothing is waiting for the calculation. So we can just // issue it directly without a save/restore. f() diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 66faa2489..ce0d02557 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -144,7 +144,7 @@ pub(crate) unsafe fn init() { #[cfg(feature = "rt")] #[interrupt] -unsafe fn IO_IRQ_BANK0() { +fn IO_IRQ_BANK0() { let cpu = SIO.cpuid().read() as usize; // There are two sets of interrupt registers, one for cpu0 and one for cpu1 // and here we are selecting the set that belongs to the currently executing @@ -185,46 +185,44 @@ struct InputFuture<'a, T: Pin> { impl<'d, T: Pin> InputFuture<'d, T> { pub fn new(pin: impl Peripheral

+ 'd, level: InterruptTrigger) -> Self { into_ref!(pin); - unsafe { - let pin_group = (pin.pin() % 8) as usize; - // first, clear the INTR register bits. without this INTR will still - // contain reports of previous edges, causing the IRQ to fire early - // on stale state. clearing these means that we can only detect edges - // that occur *after* the clear happened, but since both this and the - // alternative are fundamentally racy it's probably fine. - // (the alternative being checking the current level and waiting for - // its inverse, but that requires reading the current level and thus - // missing anything that happened before the level was read.) - pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { - w.set_edge_high(pin_group, true); - w.set_edge_low(pin_group, true); - }); + let pin_group = (pin.pin() % 8) as usize; + // first, clear the INTR register bits. without this INTR will still + // contain reports of previous edges, causing the IRQ to fire early + // on stale state. clearing these means that we can only detect edges + // that occur *after* the clear happened, but since both this and the + // alternative are fundamentally racy it's probably fine. + // (the alternative being checking the current level and waiting for + // its inverse, but that requires reading the current level and thus + // missing anything that happened before the level was read.) + pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); + }); - // Each INTR register is divided into 8 groups, one group for each - // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, - // and EGDE_HIGH. - pin.int_proc() - .inte((pin.pin() / 8) as usize) - .write_set(|w| match level { - InterruptTrigger::LevelHigh => { - trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); - w.set_level_high(pin_group, true); - } - InterruptTrigger::LevelLow => { - w.set_level_low(pin_group, true); - } - InterruptTrigger::EdgeHigh => { - w.set_edge_high(pin_group, true); - } - InterruptTrigger::EdgeLow => { - w.set_edge_low(pin_group, true); - } - InterruptTrigger::AnyEdge => { - w.set_edge_high(pin_group, true); - w.set_edge_low(pin_group, true); - } - }); - } + // Each INTR register is divided into 8 groups, one group for each + // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, + // and EGDE_HIGH. + pin.int_proc() + .inte((pin.pin() / 8) as usize) + .write_set(|w| match level { + InterruptTrigger::LevelHigh => { + trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); + w.set_level_high(pin_group, true); + } + InterruptTrigger::LevelLow => { + w.set_level_low(pin_group, true); + } + InterruptTrigger::EdgeHigh => { + w.set_edge_high(pin_group, true); + } + InterruptTrigger::EdgeLow => { + w.set_edge_low(pin_group, true); + } + InterruptTrigger::AnyEdge => { + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); + } + }); Self { pin, level } } @@ -242,7 +240,7 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // then we want to access the interrupt enable register for our // pin (there are 4 of these PROC0_INTE0, PROC0_INTE1, PROC0_INTE2, and // PROC0_INTE3 per cpu). - let inte: pac::io::regs::Int = unsafe { self.pin.int_proc().inte((self.pin.pin() / 8) as usize).read() }; + let inte: pac::io::regs::Int = self.pin.int_proc().inte((self.pin.pin() / 8) as usize).read(); // The register is divided into groups of four, one group for // each pin. Each group consists of four trigger levels LEVEL_LOW, // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin. @@ -449,15 +447,13 @@ impl<'d, T: Pin> Flex<'d, T> { pub fn new(pin: impl Peripheral

+ 'd) -> Self { into_ref!(pin); - unsafe { - pin.pad_ctrl().write(|w| { - w.set_ie(true); - }); + pin.pad_ctrl().write(|w| { + w.set_ie(true); + }); - pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0); - }); - } + pin.io().ctrl().write(|w| { + w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0); + }); Self { pin } } @@ -470,43 +466,37 @@ impl<'d, T: Pin> Flex<'d, T> { /// Set the pin's pull. #[inline] pub fn set_pull(&mut self, pull: Pull) { - unsafe { - self.pin.pad_ctrl().modify(|w| { - w.set_ie(true); - let (pu, pd) = match pull { - Pull::Up => (true, false), - Pull::Down => (false, true), - Pull::None => (false, false), - }; - w.set_pue(pu); - w.set_pde(pd); - }); - } + self.pin.pad_ctrl().modify(|w| { + w.set_ie(true); + let (pu, pd) = match pull { + Pull::Up => (true, false), + Pull::Down => (false, true), + Pull::None => (false, false), + }; + w.set_pue(pu); + w.set_pde(pd); + }); } /// Set the pin's drive strength. #[inline] pub fn set_drive_strength(&mut self, strength: Drive) { - unsafe { - self.pin.pad_ctrl().modify(|w| { - w.set_drive(match strength { - Drive::_2mA => pac::pads::vals::Drive::_2MA, - Drive::_4mA => pac::pads::vals::Drive::_4MA, - Drive::_8mA => pac::pads::vals::Drive::_8MA, - Drive::_12mA => pac::pads::vals::Drive::_12MA, - }); + self.pin.pad_ctrl().modify(|w| { + w.set_drive(match strength { + Drive::_2mA => pac::pads::vals::Drive::_2MA, + Drive::_4mA => pac::pads::vals::Drive::_4MA, + Drive::_8mA => pac::pads::vals::Drive::_8MA, + Drive::_12mA => pac::pads::vals::Drive::_12MA, }); - } + }); } // Set the pin's slew rate. #[inline] pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { - unsafe { - self.pin.pad_ctrl().modify(|w| { - w.set_slewfast(slew_rate == SlewRate::Fast); - }); - } + self.pin.pad_ctrl().modify(|w| { + w.set_slewfast(slew_rate == SlewRate::Fast); + }); } /// Put the pin into input mode. @@ -514,7 +504,7 @@ impl<'d, T: Pin> Flex<'d, T> { /// The pull setting is left unchanged. #[inline] pub fn set_as_input(&mut self) { - unsafe { self.pin.sio_oe().value_clr().write_value(self.bit()) } + self.pin.sio_oe().value_clr().write_value(self.bit()) } /// Put the pin into output mode. @@ -523,17 +513,17 @@ impl<'d, T: Pin> Flex<'d, T> { /// at a specific level, call `set_high`/`set_low` on the pin first. #[inline] pub fn set_as_output(&mut self) { - unsafe { self.pin.sio_oe().value_set().write_value(self.bit()) } + self.pin.sio_oe().value_set().write_value(self.bit()) } #[inline] fn is_set_as_output(&self) -> bool { - unsafe { (self.pin.sio_oe().value().read() & self.bit()) != 0 } + (self.pin.sio_oe().value().read() & self.bit()) != 0 } #[inline] pub fn toggle_set_as_output(&mut self) { - unsafe { self.pin.sio_oe().value_xor().write_value(self.bit()) } + self.pin.sio_oe().value_xor().write_value(self.bit()) } #[inline] @@ -543,7 +533,7 @@ impl<'d, T: Pin> Flex<'d, T> { #[inline] pub fn is_low(&self) -> bool { - unsafe { self.pin.sio_in().read() & self.bit() == 0 } + self.pin.sio_in().read() & self.bit() == 0 } /// Returns current pin level @@ -555,13 +545,13 @@ impl<'d, T: Pin> Flex<'d, T> { /// Set the output as high. #[inline] pub fn set_high(&mut self) { - unsafe { self.pin.sio_out().value_set().write_value(self.bit()) } + self.pin.sio_out().value_set().write_value(self.bit()) } /// Set the output as low. #[inline] pub fn set_low(&mut self) { - unsafe { self.pin.sio_out().value_clr().write_value(self.bit()) } + self.pin.sio_out().value_clr().write_value(self.bit()) } /// Set the output level. @@ -576,7 +566,7 @@ impl<'d, T: Pin> Flex<'d, T> { /// Is the output level high? #[inline] pub fn is_set_high(&self) -> bool { - unsafe { (self.pin.sio_out().value().read() & self.bit()) == 0 } + (self.pin.sio_out().value().read() & self.bit()) == 0 } /// Is the output level low? @@ -594,7 +584,7 @@ impl<'d, T: Pin> Flex<'d, T> { /// Toggle pin output #[inline] pub fn toggle(&mut self) { - unsafe { self.pin.sio_out().value_xor().write_value(self.bit()) } + self.pin.sio_out().value_xor().write_value(self.bit()) } #[inline] @@ -626,12 +616,10 @@ impl<'d, T: Pin> Flex<'d, T> { impl<'d, T: Pin> Drop for Flex<'d, T> { #[inline] fn drop(&mut self) { - unsafe { - self.pin.pad_ctrl().write(|_| {}); - self.pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0); - }); - } + self.pin.pad_ctrl().write(|_| {}); + self.pin.io().ctrl().write(|w| { + w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0); + }); } } @@ -688,7 +676,7 @@ pub(crate) mod sealed { Bank::Bank0 => crate::pac::IO_BANK0, Bank::Qspi => crate::pac::IO_QSPI, }; - let proc = unsafe { SIO.cpuid().read() }; + let proc = SIO.cpuid().read(); io_block.int_proc(proc as _) } } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index ce9a082a2..791c64554 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -85,7 +85,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let r = T::regs(); // mask everything initially - unsafe { r.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)) } + r.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -135,13 +135,11 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let last = remaining_queue == 0; batch += 1; - unsafe { - p.ic_data_cmd().write(|w| { - w.set_restart(restart && remaining_queue == buffer.len() - 1); - w.set_stop(last && send_stop); - w.set_cmd(true); - }); - } + p.ic_data_cmd().write(|w| { + w.set_restart(restart && remaining_queue == buffer.len() - 1); + w.set_stop(last && send_stop); + w.set_cmd(true); + }); } // We've either run out of txfifo or just plain finished setting up @@ -161,7 +159,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { Poll::Pending } }, - |_me| unsafe { + |_me| { // Set the read threshold to the number of bytes we're // expecting so we don't get spurious interrupts. p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1)); @@ -185,7 +183,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let rxbytes = (rxfifo as usize).min(remaining); let received = buffer.len() - remaining; for b in &mut buffer[received..received + rxbytes] { - *b = unsafe { p.ic_data_cmd().read().dat() }; + *b = p.ic_data_cmd().read().dat(); } remaining -= rxbytes; } @@ -211,13 +209,11 @@ impl<'d, T: Instance> I2c<'d, T, Async> { if let Some(byte) = bytes.next() { let last = bytes.peek().is_none(); - unsafe { - p.ic_data_cmd().write(|w| { - w.set_stop(last && send_stop); - w.set_cmd(false); - w.set_dat(byte); - }); - } + p.ic_data_cmd().write(|w| { + w.set_stop(last && send_stop); + w.set_cmd(false); + w.set_dat(byte); + }); } else { break 'xmit Ok(()); } @@ -235,7 +231,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { Poll::Pending } }, - |_me| unsafe { + |_me| { // Set tx "free" threshold a little high so that we get // woken before the fifo completely drains to minimize // transfer stalls. @@ -267,7 +263,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { let had_abort2 = self .wait_on( - |me| unsafe { + |me| { // We could see an abort while processing fifo backlog, // so handle it here. let abort = me.read_and_clear_abort_reason(); @@ -279,7 +275,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { Poll::Pending } }, - |_me| unsafe { + |_me| { p.ic_intr_mask().modify(|w| { w.set_m_stop_det(true); w.set_m_tx_abrt(true); @@ -287,9 +283,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> { }, ) .await; - unsafe { - p.ic_clr_stop_det().read(); - } + p.ic_clr_stop_det().read(); had_abort.and(had_abort2) } else { @@ -336,95 +330,93 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { let p = T::regs(); - unsafe { - let reset = T::reset(); - crate::reset::reset(reset); - crate::reset::unreset_wait(reset); + let reset = T::reset(); + crate::reset::reset(reset); + crate::reset::unreset_wait(reset); - p.ic_enable().write(|w| w.set_enable(false)); + p.ic_enable().write(|w| w.set_enable(false)); - // Select controller mode & speed - p.ic_con().modify(|w| { - // Always use "fast" mode (<= 400 kHz, works fine for standard - // mode too) - w.set_speed(i2c::vals::Speed::FAST); - w.set_master_mode(true); - w.set_ic_slave_disable(true); - w.set_ic_restart_en(true); - w.set_tx_empty_ctrl(true); - }); + // Select controller mode & speed + p.ic_con().modify(|w| { + // Always use "fast" mode (<= 400 kHz, works fine for standard + // mode too) + w.set_speed(i2c::vals::Speed::FAST); + w.set_master_mode(true); + w.set_ic_slave_disable(true); + w.set_ic_restart_en(true); + w.set_tx_empty_ctrl(true); + }); - // Set FIFO watermarks to 1 to make things simpler. This is encoded - // by a register value of 0. - p.ic_tx_tl().write(|w| w.set_tx_tl(0)); - p.ic_rx_tl().write(|w| w.set_rx_tl(0)); + // Set FIFO watermarks to 1 to make things simpler. This is encoded + // by a register value of 0. + p.ic_tx_tl().write(|w| w.set_tx_tl(0)); + p.ic_rx_tl().write(|w| w.set_rx_tl(0)); - // Configure SCL & SDA pins - scl.io().ctrl().write(|w| w.set_funcsel(3)); - sda.io().ctrl().write(|w| w.set_funcsel(3)); + // Configure SCL & SDA pins + scl.io().ctrl().write(|w| w.set_funcsel(3)); + sda.io().ctrl().write(|w| w.set_funcsel(3)); - scl.pad_ctrl().write(|w| { - w.set_schmitt(true); - w.set_ie(true); - w.set_od(false); - w.set_pue(true); - w.set_pde(false); - }); - sda.pad_ctrl().write(|w| { - w.set_schmitt(true); - w.set_ie(true); - w.set_od(false); - w.set_pue(true); - w.set_pde(false); - }); + scl.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); + }); + sda.pad_ctrl().write(|w| { + w.set_schmitt(true); + w.set_ie(true); + w.set_od(false); + w.set_pue(true); + w.set_pde(false); + }); - // Configure baudrate + // Configure baudrate - // There are some subtleties to I2C timing which we are completely - // ignoring here See: - // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 - let clk_base = crate::clocks::clk_peri_freq(); + // There are some subtleties to I2C timing which we are completely + // ignoring here See: + // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 + let clk_base = crate::clocks::clk_peri_freq(); - let period = (clk_base + config.frequency / 2) / config.frequency; - let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low - let hcnt = period - lcnt; // and 2/5 (40%) of the period high + let period = (clk_base + config.frequency / 2) / config.frequency; + let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low + let hcnt = period - lcnt; // and 2/5 (40%) of the period high - // Check for out-of-range divisors: - assert!(hcnt <= 0xffff); - assert!(lcnt <= 0xffff); - assert!(hcnt >= 8); - assert!(lcnt >= 8); + // Check for out-of-range divisors: + assert!(hcnt <= 0xffff); + assert!(lcnt <= 0xffff); + assert!(hcnt >= 8); + assert!(lcnt >= 8); - // Per I2C-bus specification a device in standard or fast mode must - // internally provide a hold time of at least 300ns for the SDA - // signal to bridge the undefined region of the falling edge of SCL. - // A smaller hold time of 120ns is used for fast mode plus. - let sda_tx_hold_count = if config.frequency < 1_000_000 { - // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / - // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't - // fit in uint. Add 1 to avoid division truncation. - ((clk_base * 3) / 10_000_000) + 1 - } else { - // fast mode plus requires a clk_base > 32MHz - assert!(clk_base >= 32_000_000); + // Per I2C-bus specification a device in standard or fast mode must + // internally provide a hold time of at least 300ns for the SDA + // signal to bridge the undefined region of the falling edge of SCL. + // A smaller hold time of 120ns is used for fast mode plus. + let sda_tx_hold_count = if config.frequency < 1_000_000 { + // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / + // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. + ((clk_base * 3) / 10_000_000) + 1 + } else { + // fast mode plus requires a clk_base > 32MHz + assert!(clk_base >= 32_000_000); - // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / - // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't - // fit in uint. Add 1 to avoid division truncation. - ((clk_base * 3) / 25_000_000) + 1 - }; - assert!(sda_tx_hold_count <= lcnt - 2); + // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / + // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't + // fit in uint. Add 1 to avoid division truncation. + ((clk_base * 3) / 25_000_000) + 1 + }; + assert!(sda_tx_hold_count <= lcnt - 2); - p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); - p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); - p.ic_fs_spklen() - .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); - p.ic_sda_hold() - .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); + p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); + p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); + p.ic_fs_spklen() + .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); + p.ic_sda_hold() + .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); - // Enable I2C block - p.ic_enable().write(|w| w.set_enable(true)); - } + // Enable I2C block + p.ic_enable().write(|w| w.set_enable(true)); Self { phantom: PhantomData } } @@ -439,11 +431,9 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { } let p = T::regs(); - unsafe { - p.ic_enable().write(|w| w.set_enable(false)); - p.ic_tar().write(|w| w.set_ic_tar(addr)); - p.ic_enable().write(|w| w.set_enable(true)); - } + p.ic_enable().write(|w| w.set_enable(false)); + p.ic_tar().write(|w| w.set_ic_tar(addr)); + p.ic_enable().write(|w| w.set_enable(true)); Ok(()) } @@ -455,40 +445,38 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { #[inline] fn tx_fifo_capacity() -> u8 { let p = T::regs(); - unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() } + FIFO_SIZE - p.ic_txflr().read().txflr() } #[inline] fn rx_fifo_len() -> u8 { let p = T::regs(); - unsafe { p.ic_rxflr().read().rxflr() } + p.ic_rxflr().read().rxflr() } fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { let p = T::regs(); - unsafe { - let abort_reason = p.ic_tx_abrt_source().read(); - if abort_reason.0 != 0 { - // Note clearing the abort flag also clears the reason, and this - // instance of flag is clear-on-read! Note also the - // IC_CLR_TX_ABRT register always reads as 0. - p.ic_clr_tx_abrt().read(); + let abort_reason = p.ic_tx_abrt_source().read(); + if abort_reason.0 != 0 { + // Note clearing the abort flag also clears the reason, and this + // instance of flag is clear-on-read! Note also the + // IC_CLR_TX_ABRT register always reads as 0. + p.ic_clr_tx_abrt().read(); - let reason = if abort_reason.abrt_7b_addr_noack() - | abort_reason.abrt_10addr1_noack() - | abort_reason.abrt_10addr2_noack() - { - AbortReason::NoAcknowledge - } else if abort_reason.arb_lost() { - AbortReason::ArbitrationLoss - } else { - AbortReason::Other(abort_reason.0) - }; - - Err(Error::Abort(reason)) + let reason = if abort_reason.abrt_7b_addr_noack() + | abort_reason.abrt_10addr1_noack() + | abort_reason.abrt_10addr2_noack() + { + AbortReason::NoAcknowledge + } else if abort_reason.arb_lost() { + AbortReason::ArbitrationLoss } else { - Ok(()) - } + AbortReason::Other(abort_reason.0) + }; + + Err(Error::Abort(reason)) + } else { + Ok(()) } } @@ -503,24 +491,21 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { let first = i == 0; let last = i == lastindex; - // NOTE(unsafe) We have &mut self - unsafe { - // wait until there is space in the FIFO to write the next byte - while Self::tx_fifo_full() {} + // wait until there is space in the FIFO to write the next byte + while Self::tx_fifo_full() {} - p.ic_data_cmd().write(|w| { - w.set_restart(restart && first); - w.set_stop(send_stop && last); + p.ic_data_cmd().write(|w| { + w.set_restart(restart && first); + w.set_stop(send_stop && last); - w.set_cmd(true); - }); + w.set_cmd(true); + }); - while Self::rx_fifo_len() == 0 { - self.read_and_clear_abort_reason()?; - } - - *byte = p.ic_data_cmd().read().dat(); + while Self::rx_fifo_len() == 0 { + self.read_and_clear_abort_reason()?; } + + *byte = p.ic_data_cmd().read().dat(); } Ok(()) @@ -536,36 +521,33 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { for (i, byte) in write.iter().enumerate() { let last = i == write.len() - 1; - // NOTE(unsafe) We have &mut self - unsafe { - p.ic_data_cmd().write(|w| { - w.set_stop(send_stop && last); - w.set_dat(*byte); - }); + p.ic_data_cmd().write(|w| { + w.set_stop(send_stop && last); + w.set_dat(*byte); + }); - // Wait until the transmission of the address/data from the - // internal shift register has completed. For this to function - // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The - // TX_EMPTY_CTRL flag was set in i2c_init. - while !p.ic_raw_intr_stat().read().tx_empty() {} + // Wait until the transmission of the address/data from the + // internal shift register has completed. For this to function + // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The + // TX_EMPTY_CTRL flag was set in i2c_init. + while !p.ic_raw_intr_stat().read().tx_empty() {} - let abort_reason = self.read_and_clear_abort_reason(); + let abort_reason = self.read_and_clear_abort_reason(); - if abort_reason.is_err() || (send_stop && last) { - // If the transaction was aborted or if it completed - // successfully wait until the STOP condition has occurred. + if abort_reason.is_err() || (send_stop && last) { + // If the transaction was aborted or if it completed + // successfully wait until the STOP condition has occurred. - while !p.ic_raw_intr_stat().read().stop_det() {} + while !p.ic_raw_intr_stat().read().stop_det() {} - p.ic_clr_stop_det().read().clr_stop_det(); - } - - // Note the hardware issues a STOP automatically on an abort - // condition. Note also the hardware clears RX FIFO as well as - // TX on abort, ecause we set hwparam - // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. - abort_reason?; + p.ic_clr_stop_det().read().clr_stop_det(); } + + // Note the hardware issues a STOP automatically on an abort + // condition. Note also the hardware clears RX FIFO as well as + // TX on abort, ecause we set hwparam + // IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0. + abort_reason?; } Ok(()) } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d6f73219f..4fd3cb46a 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -261,33 +261,39 @@ pub fn init(config: config::Config) -> Peripherals { /// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes. trait RegExt { - unsafe fn write_xor(&self, f: impl FnOnce(&mut T) -> R) -> R; - unsafe fn write_set(&self, f: impl FnOnce(&mut T) -> R) -> R; - unsafe fn write_clear(&self, f: impl FnOnce(&mut T) -> R) -> R; + fn write_xor(&self, f: impl FnOnce(&mut T) -> R) -> R; + fn write_set(&self, f: impl FnOnce(&mut T) -> R) -> R; + fn write_clear(&self, f: impl FnOnce(&mut T) -> R) -> R; } impl RegExt for pac::common::Reg { - unsafe fn write_xor(&self, f: impl FnOnce(&mut T) -> R) -> R { + fn write_xor(&self, f: impl FnOnce(&mut T) -> R) -> R { let mut val = Default::default(); let res = f(&mut val); - let ptr = (self.ptr() as *mut u8).add(0x1000) as *mut T; - ptr.write_volatile(val); + unsafe { + let ptr = (self.as_ptr() as *mut u8).add(0x1000) as *mut T; + ptr.write_volatile(val); + } res } - unsafe fn write_set(&self, f: impl FnOnce(&mut T) -> R) -> R { + fn write_set(&self, f: impl FnOnce(&mut T) -> R) -> R { let mut val = Default::default(); let res = f(&mut val); - let ptr = (self.ptr() as *mut u8).add(0x2000) as *mut T; - ptr.write_volatile(val); + unsafe { + let ptr = (self.as_ptr() as *mut u8).add(0x2000) as *mut T; + ptr.write_volatile(val); + } res } - unsafe fn write_clear(&self, f: impl FnOnce(&mut T) -> R) -> R { + fn write_clear(&self, f: impl FnOnce(&mut T) -> R) -> R { let mut val = Default::default(); let res = f(&mut val); - let ptr = (self.ptr() as *mut u8).add(0x3000) as *mut T; - ptr.write_volatile(val); + unsafe { + let ptr = (self.as_ptr() as *mut u8).add(0x3000) as *mut T; + ptr.write_volatile(val); + } res } } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 2a7e4822a..468e8470a 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -163,14 +163,12 @@ where } // Reset the core - unsafe { - let psm = pac::PSM; - psm.frce_off().modify(|w| w.set_proc1(true)); - while !psm.frce_off().read().proc1() { - cortex_m::asm::nop(); - } - psm.frce_off().modify(|w| w.set_proc1(false)); + let psm = pac::PSM; + psm.frce_off().modify(|w| w.set_proc1(true)); + while !psm.frce_off().read().proc1() { + cortex_m::asm::nop(); } + psm.frce_off().modify(|w| w.set_proc1(false)); // The ARM AAPCS ABI requires 8-byte stack alignment. // #[align] on `struct Stack` ensures the bottom is aligned, but the top could still be @@ -270,14 +268,12 @@ pub fn resume_core1() { // Push a value to the inter-core FIFO, block until space is available #[inline(always)] fn fifo_write(value: u32) { - unsafe { - let sio = pac::SIO; - // Wait for the FIFO to have enough space - while !sio.fifo().st().read().rdy() { - cortex_m::asm::nop(); - } - sio.fifo().wr().write_value(value); + let sio = pac::SIO; + // Wait for the FIFO to have enough space + while !sio.fifo().st().read().rdy() { + cortex_m::asm::nop(); } + sio.fifo().wr().write_value(value); // Fire off an event to the other core. // This is required as the other core may be `wfe` (waiting for event) cortex_m::asm::sev(); @@ -286,38 +282,32 @@ fn fifo_write(value: u32) { // Pop a value from inter-core FIFO, block until available #[inline(always)] fn fifo_read() -> u32 { - unsafe { - let sio = pac::SIO; - // Wait until FIFO has data - while !sio.fifo().st().read().vld() { - cortex_m::asm::nop(); - } - sio.fifo().rd().read() + let sio = pac::SIO; + // Wait until FIFO has data + while !sio.fifo().st().read().vld() { + cortex_m::asm::nop(); } + sio.fifo().rd().read() } // Pop a value from inter-core FIFO, `wfe` until available #[inline(always)] #[allow(unused)] fn fifo_read_wfe() -> u32 { - unsafe { - let sio = pac::SIO; - // Wait until FIFO has data - while !sio.fifo().st().read().vld() { - cortex_m::asm::wfe(); - } - sio.fifo().rd().read() + let sio = pac::SIO; + // Wait until FIFO has data + while !sio.fifo().st().read().vld() { + cortex_m::asm::wfe(); } + sio.fifo().rd().read() } // Drain inter-core FIFO #[inline(always)] fn fifo_drain() { - unsafe { - let sio = pac::SIO; - while sio.fifo().st().read().vld() { - let _ = sio.fifo().rd().read(); - } + let sio = pac::SIO; + while sio.fifo().st().read().vld() { + let _ = sio.fifo().rd().read(); } } diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 0fa3bd771..1b36e0a54 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -87,7 +87,7 @@ const SMIRQ_MASK: u32 = 1 << 8; #[cfg(feature = "rt")] #[interrupt] -unsafe fn PIO0_IRQ_0() { +fn PIO0_IRQ_0() { use crate::pac; let ints = pac::PIO0.irqs(0).ints().read().0; for bit in 0..12 { @@ -100,7 +100,7 @@ unsafe fn PIO0_IRQ_0() { #[cfg(feature = "rt")] #[interrupt] -unsafe fn PIO1_IRQ_0() { +fn PIO1_IRQ_0() { use crate::pac; let ints = pac::PIO1.irqs(0).ints().read().0; for bit in 0..12 { @@ -145,11 +145,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI Poll::Ready(()) } else { WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker()); - unsafe { - PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = TXNFULL_MASK << SM; - }); - } + PIO::PIO.irqs(0).inte().write_set(|m| { + m.0 = TXNFULL_MASK << SM; + }); // debug!("Pending"); Poll::Pending } @@ -158,11 +156,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { - unsafe { - PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = TXNFULL_MASK << SM; - }); - } + PIO::PIO.irqs(0).inte().write_clear(|m| { + m.0 = TXNFULL_MASK << SM; + }); } } @@ -186,11 +182,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO Poll::Ready(v) } else { WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker()); - unsafe { - PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = RXNEMPTY_MASK << SM; - }); - } + PIO::PIO.irqs(0).inte().write_set(|m| { + m.0 = RXNEMPTY_MASK << SM; + }); //debug!("Pending"); Poll::Pending } @@ -199,11 +193,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { - unsafe { - PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = RXNEMPTY_MASK << SM; - }); - } + PIO::PIO.irqs(0).inte().write_clear(|m| { + m.0 = RXNEMPTY_MASK << SM; + }); } } @@ -220,30 +212,24 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { //debug!("Poll {},{}", PIO::PIO_NO, SM); // Check if IRQ flag is already set - if unsafe { PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 } { - unsafe { - PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no); - } + if PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 { + PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no); return Poll::Ready(()); } WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker()); - unsafe { - PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = SMIRQ_MASK << self.irq_no; - }); - } + PIO::PIO.irqs(0).inte().write_set(|m| { + m.0 = SMIRQ_MASK << self.irq_no; + }); Poll::Pending } } impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> { fn drop(&mut self) { - unsafe { - PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = SMIRQ_MASK << self.irq_no; - }); - } + PIO::PIO.irqs(0).inte().write_clear(|m| { + m.0 = SMIRQ_MASK << self.irq_no; + }); } } @@ -256,57 +242,47 @@ impl<'l, PIO: Instance> Pin<'l, PIO> { /// Set the pin's drive strength. #[inline] pub fn set_drive_strength(&mut self, strength: Drive) { - unsafe { - self.pin.pad_ctrl().modify(|w| { - w.set_drive(match strength { - Drive::_2mA => pac::pads::vals::Drive::_2MA, - Drive::_4mA => pac::pads::vals::Drive::_4MA, - Drive::_8mA => pac::pads::vals::Drive::_8MA, - Drive::_12mA => pac::pads::vals::Drive::_12MA, - }); + self.pin.pad_ctrl().modify(|w| { + w.set_drive(match strength { + Drive::_2mA => pac::pads::vals::Drive::_2MA, + Drive::_4mA => pac::pads::vals::Drive::_4MA, + Drive::_8mA => pac::pads::vals::Drive::_8MA, + Drive::_12mA => pac::pads::vals::Drive::_12MA, }); - } + }); } // Set the pin's slew rate. #[inline] pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { - unsafe { - self.pin.pad_ctrl().modify(|w| { - w.set_slewfast(slew_rate == SlewRate::Fast); - }); - } + self.pin.pad_ctrl().modify(|w| { + w.set_slewfast(slew_rate == SlewRate::Fast); + }); } /// Set the pin's pull. #[inline] pub fn set_pull(&mut self, pull: Pull) { - unsafe { - self.pin.pad_ctrl().modify(|w| { - w.set_pue(pull == Pull::Up); - w.set_pde(pull == Pull::Down); - }); - } + self.pin.pad_ctrl().modify(|w| { + w.set_pue(pull == Pull::Up); + w.set_pde(pull == Pull::Down); + }); } /// Set the pin's schmitt trigger. #[inline] pub fn set_schmitt(&mut self, enable: bool) { - unsafe { - self.pin.pad_ctrl().modify(|w| { - w.set_schmitt(enable); - }); - } + self.pin.pad_ctrl().modify(|w| { + w.set_schmitt(enable); + }); } pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { let mask = 1 << self.pin(); - unsafe { - if bypass { - PIO::PIO.input_sync_bypass().write_set(|w| *w = mask); - } else { - PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask); - } + if bypass { + PIO::PIO.input_sync_bypass().write_set(|w| *w = mask); + } else { + PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask); } } @@ -321,41 +297,37 @@ pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> { impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { pub fn empty(&self) -> bool { - unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } + PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } pub fn full(&self) -> bool { - unsafe { PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 } + PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 } pub fn level(&self) -> u8 { - unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f } + (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f } pub fn stalled(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().rxstall() & (1 << SM) != 0; - if ret { - fdebug.write(|w| w.set_rxstall(1 << SM)); - } - ret + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().rxstall() & (1 << SM) != 0; + if ret { + fdebug.write(|w| w.set_rxstall(1 << SM)); } + ret } pub fn underflowed(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().rxunder() & (1 << SM) != 0; - if ret { - fdebug.write(|w| w.set_rxunder(1 << SM)); - } - ret + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().rxunder() & (1 << SM) != 0; + if ret { + fdebug.write(|w| w.set_rxunder(1 << SM)); } + ret } pub fn pull(&mut self) -> u32 { - unsafe { PIO::PIO.rxf(SM).read() } + PIO::PIO.rxf(SM).read() } pub fn try_pull(&mut self) -> Option { @@ -374,24 +346,22 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { ch: PeripheralRef<'a, C>, data: &'a mut [W], ) -> Transfer<'a, C> { - unsafe { - let pio_no = PIO::PIO_NO; - let p = ch.regs(); - p.write_addr().write_value(data.as_ptr() as u32); - p.read_addr().write_value(PIO::PIO.rxf(SM).ptr() as u32); - p.trans_count().write_value(data.len() as u32); - compiler_fence(Ordering::SeqCst); - p.ctrl_trig().write(|w| { - // Set RX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4)); - w.set_data_size(W::size()); - w.set_chain_to(ch.number()); - w.set_incr_read(false); - w.set_incr_write(true); - w.set_en(true); - }); - compiler_fence(Ordering::SeqCst); - } + let pio_no = PIO::PIO_NO; + let p = ch.regs(); + p.write_addr().write_value(data.as_ptr() as u32); + p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32); + p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); + p.ctrl_trig().write(|w| { + // Set RX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4)); + w.set_data_size(W::size()); + w.set_chain_to(ch.number()); + w.set_incr_read(false); + w.set_incr_write(true); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); Transfer::new(ch) } } @@ -402,42 +372,36 @@ pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> { impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { pub fn empty(&self) -> bool { - unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } + PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } pub fn full(&self) -> bool { - unsafe { PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 } + PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 } pub fn level(&self) -> u8 { - unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f } + (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f } pub fn stalled(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().txstall() & (1 << SM) != 0; - if ret { - fdebug.write(|w| w.set_txstall(1 << SM)); - } - ret + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().txstall() & (1 << SM) != 0; + if ret { + fdebug.write(|w| w.set_txstall(1 << SM)); } + ret } pub fn overflowed(&self) -> bool { - unsafe { - let fdebug = PIO::PIO.fdebug(); - let ret = fdebug.read().txover() & (1 << SM) != 0; - if ret { - fdebug.write(|w| w.set_txover(1 << SM)); - } - ret + let fdebug = PIO::PIO.fdebug(); + let ret = fdebug.read().txover() & (1 << SM) != 0; + if ret { + fdebug.write(|w| w.set_txover(1 << SM)); } + ret } pub fn push(&mut self, v: u32) { - unsafe { - PIO::PIO.txf(SM).write_value(v); - } + PIO::PIO.txf(SM).write_value(v); } pub fn try_push(&mut self, v: u32) -> bool { @@ -453,24 +417,22 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { } pub fn dma_push<'a, C: Channel, W: Word>(&'a mut self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { - unsafe { - let pio_no = PIO::PIO_NO; - let p = ch.regs(); - p.read_addr().write_value(data.as_ptr() as u32); - p.write_addr().write_value(PIO::PIO.txf(SM).ptr() as u32); - p.trans_count().write_value(data.len() as u32); - compiler_fence(Ordering::SeqCst); - p.ctrl_trig().write(|w| { - // Set TX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8)); - w.set_data_size(W::size()); - w.set_chain_to(ch.number()); - w.set_incr_read(true); - w.set_incr_write(false); - w.set_en(true); - }); - compiler_fence(Ordering::SeqCst); - } + let pio_no = PIO::PIO_NO; + let p = ch.regs(); + p.read_addr().write_value(data.as_ptr() as u32); + p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32); + p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); + p.ctrl_trig().write(|w| { + // Set TX DREQ for this statemachine + w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8)); + w.set_data_size(W::size()); + w.set_chain_to(ch.number()); + w.set_incr_read(true); + w.set_incr_write(false); + w.set_en(true); + }); + compiler_fence(Ordering::SeqCst); Transfer::new(ch) } } @@ -482,9 +444,7 @@ pub struct StateMachine<'d, PIO: Instance, const SM: usize> { impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { fn drop(&mut self) { - unsafe { - PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); - } + PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); on_pio_drop::(); } } @@ -647,45 +607,43 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32"); assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32"); let sm = Self::this_sm(); - unsafe { - sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8); - sm.execctrl().write(|w| { - w.set_side_en(config.exec.side_en); - w.set_side_pindir(config.exec.side_pindir); - w.set_jmp_pin(config.exec.jmp_pin); - w.set_out_en_sel(config.out_en_sel); - w.set_inline_out_en(config.inline_out_en); - w.set_out_sticky(config.out_sticky); - w.set_wrap_top(config.exec.wrap_top); - w.set_wrap_bottom(config.exec.wrap_bottom); - w.set_status_sel(match config.status_sel { - StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, - StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL, - }); - w.set_status_n(config.status_n); + sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8); + sm.execctrl().write(|w| { + w.set_side_en(config.exec.side_en); + w.set_side_pindir(config.exec.side_pindir); + w.set_jmp_pin(config.exec.jmp_pin); + w.set_out_en_sel(config.out_en_sel); + w.set_inline_out_en(config.inline_out_en); + w.set_out_sticky(config.out_sticky); + w.set_wrap_top(config.exec.wrap_top); + w.set_wrap_bottom(config.exec.wrap_bottom); + w.set_status_sel(match config.status_sel { + StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, + StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL, }); - sm.shiftctrl().write(|w| { - w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); - w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly); - w.set_pull_thresh(config.shift_out.threshold); - w.set_push_thresh(config.shift_in.threshold); - w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right); - w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right); - w.set_autopull(config.shift_out.auto_fill); - w.set_autopush(config.shift_in.auto_fill); - }); - sm.pinctrl().write(|w| { - w.set_sideset_count(config.pins.sideset_count); - w.set_set_count(config.pins.set_count); - w.set_out_count(config.pins.out_count); - w.set_in_base(config.pins.in_base); - w.set_sideset_base(config.pins.sideset_base); - w.set_set_base(config.pins.set_base); - w.set_out_base(config.pins.out_base); - }); - if let Some(origin) = config.origin { - pio_instr_util::exec_jmp(self, origin); - } + w.set_status_n(config.status_n); + }); + sm.shiftctrl().write(|w| { + w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); + w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly); + w.set_pull_thresh(config.shift_out.threshold); + w.set_push_thresh(config.shift_in.threshold); + w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right); + w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right); + w.set_autopull(config.shift_out.auto_fill); + w.set_autopush(config.shift_in.auto_fill); + }); + sm.pinctrl().write(|w| { + w.set_sideset_count(config.pins.sideset_count); + w.set_set_count(config.pins.set_count); + w.set_out_count(config.pins.out_count); + w.set_in_base(config.pins.in_base); + w.set_sideset_base(config.pins.sideset_base); + w.set_set_base(config.pins.set_base); + w.set_out_base(config.pins.out_base); + }); + if let Some(origin) = config.origin { + unsafe { pio_instr_util::exec_jmp(self, origin) } } } @@ -696,45 +654,35 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { pub fn restart(&mut self) { let mask = 1u8 << SM; - unsafe { - PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); - } + PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); } pub fn set_enable(&mut self, enable: bool) { let mask = 1u8 << SM; - unsafe { - if enable { - PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); - } else { - PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); - } + if enable { + PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); + } else { + PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); } } pub fn is_enabled(&self) -> bool { - unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } + PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } pub fn clkdiv_restart(&mut self) { let mask = 1u8 << SM; - unsafe { - PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); - } + PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); } fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { let enabled = self.is_enabled(); self.set_enable(false); - let pincfg = unsafe { Self::this_sm().pinctrl().read() }; - let execcfg = unsafe { Self::this_sm().execctrl().read() }; - unsafe { - Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true)); - } + let pincfg = Self::this_sm().pinctrl().read(); + let execcfg = Self::this_sm().execctrl().read(); + Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true)); f(self); - unsafe { - Self::this_sm().pinctrl().write_value(pincfg); - Self::this_sm().execctrl().write_value(execcfg); - } + Self::this_sm().pinctrl().write_value(pincfg); + Self::this_sm().execctrl().write_value(execcfg); self.set_enable(enabled); } @@ -743,14 +691,12 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { self.with_paused(|sm| { for pin in pins { - unsafe { - Self::this_sm().pinctrl().write(|w| { - w.set_set_base(pin.pin()); - w.set_set_count(1); - }); - // SET PINDIRS, (dir) - sm.exec_instr(0b111_00000_100_00000 | dir as u16); - } + Self::this_sm().pinctrl().write(|w| { + w.set_set_base(pin.pin()); + w.set_set_count(1); + }); + // SET PINDIRS, (dir) + unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) }; } }); } @@ -760,29 +706,25 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) { self.with_paused(|sm| { for pin in pins { - unsafe { - Self::this_sm().pinctrl().write(|w| { - w.set_set_base(pin.pin()); - w.set_set_count(1); - }); - // SET PINS, (dir) - sm.exec_instr(0b111_00000_000_00000 | level as u16); - } + Self::this_sm().pinctrl().write(|w| { + w.set_set_base(pin.pin()); + w.set_set_count(1); + }); + // SET PINS, (dir) + unsafe { sm.exec_instr(0b111_00000_000_00000 | level as u16) }; } }); } pub fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs - unsafe { - let shiftctrl = Self::this_sm().shiftctrl(); - shiftctrl.modify(|w| { - w.set_fjoin_rx(!w.fjoin_rx()); - }); - shiftctrl.modify(|w| { - w.set_fjoin_rx(!w.fjoin_rx()); - }); - } + let shiftctrl = Self::this_sm().shiftctrl(); + shiftctrl.modify(|w| { + w.set_fjoin_rx(!w.fjoin_rx()); + }); + shiftctrl.modify(|w| { + w.set_fjoin_rx(!w.fjoin_rx()); + }); } pub unsafe fn exec_instr(&mut self, instr: u16) { @@ -856,11 +798,9 @@ impl<'d, PIO: Instance> Common<'d, PIO> { if (self.instructions_used | used_mask) & mask != 0 { return Err(addr); } - unsafe { - PIO::PIO.instr_mem(addr).write(|w| { - w.set_instr_mem(instr); - }); - } + PIO::PIO.instr_mem(addr).write(|w| { + w.set_instr_mem(instr); + }); used_mask |= mask; } self.instructions_used |= used_mask; @@ -877,17 +817,15 @@ impl<'d, PIO: Instance> Common<'d, PIO> { } pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { - unsafe { - // this can interfere with per-pin bypass functions. splitting the - // modification is going to be fine since nothing that relies on - // it can reasonably run before we finish. - PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); - PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); - } + // this can interfere with per-pin bypass functions. splitting the + // modification is going to be fine since nothing that relies on + // it can reasonably run before we finish. + PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); + PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); } pub fn get_input_sync_bypass(&self) -> u32 { - unsafe { PIO::PIO.input_sync_bypass().read() } + PIO::PIO.input_sync_bypass().read() } /// Register a pin for PIO usage. Pins will be released from the PIO block @@ -896,9 +834,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { /// of [`Pio`] do not keep pin registrations alive.** pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); - unsafe { - pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); - } + pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); Pin { @@ -916,13 +852,11 @@ impl<'d, PIO: Instance> Common<'d, PIO> { _pio: PhantomData, }; f(&mut batch); - unsafe { - PIO::PIO.ctrl().modify(|w| { - w.set_clkdiv_restart(batch.clkdiv_restart); - w.set_sm_restart(batch.sm_restart); - w.set_sm_enable((w.sm_enable() & !batch.sm_enable_mask) | batch.sm_enable); - }); - } + PIO::PIO.ctrl().modify(|w| { + w.set_clkdiv_restart(batch.clkdiv_restart); + w.set_sm_restart(batch.sm_restart); + w.set_sm_enable((w.sm_enable() & !batch.sm_enable_mask) | batch.sm_enable); + }); } } @@ -974,11 +908,11 @@ impl<'d, PIO: Instance> IrqFlags<'d, PIO> { } pub fn check_any(&self, irqs: u8) -> bool { - unsafe { PIO::PIO.irq().read().irq() & irqs != 0 } + PIO::PIO.irq().read().irq() & irqs != 0 } pub fn check_all(&self, irqs: u8) -> bool { - unsafe { PIO::PIO.irq().read().irq() & irqs == irqs } + PIO::PIO.irq().read().irq() & irqs == irqs } pub fn clear(&self, irq_no: usize) { @@ -987,7 +921,7 @@ impl<'d, PIO: Instance> IrqFlags<'d, PIO> { } pub fn clear_all(&self, irqs: u8) { - unsafe { PIO::PIO.irq().write(|w| w.set_irq(irqs)) } + PIO::PIO.irq().write(|w| w.set_irq(irqs)) } pub fn set(&self, irq_no: usize) { @@ -996,7 +930,7 @@ impl<'d, PIO: Instance> IrqFlags<'d, PIO> { } pub fn set_all(&self, irqs: u8) { - unsafe { PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs)) } + PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs)) } } @@ -1068,9 +1002,7 @@ fn on_pio_drop() { // we only have 30 pins. don't test the other two since gpio() asserts. for i in 0..30 { if used_pins & (1 << i) != 0 { - unsafe { - pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); - } + pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); } } } diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 0f9dcf479..20bb88446 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -71,20 +71,18 @@ impl<'d, T: Channel> Pwm<'d, T> { into_ref!(inner); let p = inner.regs(); - unsafe { - p.csr().modify(|w| { - w.set_divmode(divmode); - w.set_en(false); - }); - p.ctr().write(|w| w.0 = 0); - Self::configure(p, &config); + p.csr().modify(|w| { + w.set_divmode(divmode); + w.set_en(false); + }); + p.ctr().write(|w| w.0 = 0); + Self::configure(p, &config); - if let Some(pin) = &a { - pin.io().ctrl().write(|w| w.set_funcsel(4)); - } - if let Some(pin) = &b { - pin.io().ctrl().write(|w| w.set_funcsel(4)); - } + if let Some(pin) = &a { + pin.io().ctrl().write(|w| w.set_funcsel(4)); + } + if let Some(pin) = &b { + pin.io().ctrl().write(|w| w.set_funcsel(4)); } Self { inner, @@ -161,31 +159,29 @@ impl<'d, T: Channel> Pwm<'d, T> { panic!("Requested divider is too large"); } - unsafe { - p.div().write_value(ChDiv(config.divider.to_bits() as u32)); - p.cc().write(|w| { - w.set_a(config.compare_a); - w.set_b(config.compare_b); - }); - p.top().write(|w| w.set_top(config.top)); - p.csr().modify(|w| { - w.set_a_inv(config.invert_a); - w.set_b_inv(config.invert_b); - w.set_ph_correct(config.phase_correct); - w.set_en(config.enable); - }); - } + p.div().write_value(ChDiv(config.divider.to_bits() as u32)); + p.cc().write(|w| { + w.set_a(config.compare_a); + w.set_b(config.compare_b); + }); + p.top().write(|w| w.set_top(config.top)); + p.csr().modify(|w| { + w.set_a_inv(config.invert_a); + w.set_b_inv(config.invert_b); + w.set_ph_correct(config.phase_correct); + w.set_en(config.enable); + }); } #[inline] - pub unsafe fn phase_advance(&mut self) { + pub fn phase_advance(&mut self) { let p = self.inner.regs(); p.csr().write_set(|w| w.set_ph_adv(true)); while p.csr().read().ph_adv() {} } #[inline] - pub unsafe fn phase_retard(&mut self) { + pub fn phase_retard(&mut self) { let p = self.inner.regs(); p.csr().write_set(|w| w.set_ph_ret(true)); while p.csr().read().ph_ret() {} @@ -193,12 +189,12 @@ impl<'d, T: Channel> Pwm<'d, T> { #[inline] pub fn counter(&self) -> u16 { - unsafe { self.inner.regs().ctr().read().ctr() } + self.inner.regs().ctr().read().ctr() } #[inline] pub fn set_counter(&self, ctr: u16) { - unsafe { self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) } + self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) } #[inline] @@ -209,14 +205,12 @@ impl<'d, T: Channel> Pwm<'d, T> { #[inline] pub fn wrapped(&mut self) -> bool { - unsafe { pac::PWM.intr().read().0 & self.bit() != 0 } + pac::PWM.intr().read().0 & self.bit() != 0 } #[inline] pub fn clear_wrapped(&mut self) { - unsafe { - pac::PWM.intr().write_value(Intr(self.bit() as _)); - } + pac::PWM.intr().write_value(Intr(self.bit() as _)); } #[inline] @@ -237,26 +231,22 @@ impl PwmBatch { pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) { let mut en = PwmBatch(0); batch(&mut en); - unsafe { - if enabled { - pac::PWM.en().write_set(|w| w.0 = en.0); - } else { - pac::PWM.en().write_clear(|w| w.0 = en.0); - } + if enabled { + pac::PWM.en().write_set(|w| w.0 = en.0); + } else { + pac::PWM.en().write_clear(|w| w.0 = en.0); } } } impl<'d, T: Channel> Drop for Pwm<'d, T> { fn drop(&mut self) { - unsafe { - self.inner.regs().csr().write_clear(|w| w.set_en(false)); - if let Some(pin) = &self.pin_a { - pin.io().ctrl().write(|w| w.set_funcsel(31)); - } - if let Some(pin) = &self.pin_b { - pin.io().ctrl().write(|w| w.set_funcsel(31)); - } + self.inner.regs().csr().write_clear(|w| w.set_en(false)); + if let Some(pin) = &self.pin_a { + pin.io().ctrl().write(|w| w.set_funcsel(31)); + } + if let Some(pin) = &self.pin_b { + pin.io().ctrl().write(|w| w.set_funcsel(31)); } } } diff --git a/embassy-rp/src/reset.rs b/embassy-rp/src/reset.rs index edd47c223..70512fa14 100644 --- a/embassy-rp/src/reset.rs +++ b/embassy-rp/src/reset.rs @@ -4,11 +4,11 @@ use crate::pac; pub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ffffff); -pub unsafe fn reset(peris: Peripherals) { +pub(crate) fn reset(peris: Peripherals) { pac::RESETS.reset().write_value(peris); } -pub unsafe fn unreset_wait(peris: Peripherals) { +pub(crate) fn unreset_wait(peris: Peripherals) { // TODO use the "atomic clear" register version pac::RESETS.reset().modify(|v| *v = Peripherals(v.0 & !peris.0)); while ((!pac::RESETS.reset_done().read().0) & peris.0) != 0 {} diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index e1d886d4a..b18f12fc4 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -26,7 +26,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { into_ref!(inner); // Set the RTC divider - unsafe { inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)) }; + inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); let mut result = Self { inner }; result.set_leap_year_check(true); // should be on by default, make sure this is the case. @@ -38,17 +38,14 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { /// /// Leap year checking is enabled by default. pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) { - unsafe { - self.inner - .regs() - .ctrl() - .modify(|w| w.set_force_notleapyear(!leap_year_check_enabled)) - }; + self.inner.regs().ctrl().modify(|w| { + w.set_force_notleapyear(!leap_year_check_enabled); + }); } /// Checks to see if this RealTimeClock is running pub fn is_running(&self) -> bool { - unsafe { self.inner.regs().ctrl().read().rtc_active() } + self.inner.regs().ctrl().read().rtc_active() } /// Set the datetime to a new value. @@ -60,25 +57,23 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; // disable RTC while we configure it - unsafe { - self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); - while self.inner.regs().ctrl().read().rtc_active() { - core::hint::spin_loop(); - } + self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); + while self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } - self.inner.regs().setup_0().write(|w| { - self::datetime::write_setup_0(&t, w); - }); - self.inner.regs().setup_1().write(|w| { - self::datetime::write_setup_1(&t, w); - }); + self.inner.regs().setup_0().write(|w| { + self::datetime::write_setup_0(&t, w); + }); + self.inner.regs().setup_1().write(|w| { + self::datetime::write_setup_1(&t, w); + }); - // Load the new datetime and re-enable RTC - self.inner.regs().ctrl().write(|w| w.set_load(true)); - self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); - while !self.inner.regs().ctrl().read().rtc_active() { - core::hint::spin_loop(); - } + // Load the new datetime and re-enable RTC + self.inner.regs().ctrl().write(|w| w.set_load(true)); + self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); + while !self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); } Ok(()) } @@ -93,8 +88,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { return Err(RtcError::NotRunning); } - let rtc_0 = unsafe { self.inner.regs().rtc_0().read() }; - let rtc_1 = unsafe { self.inner.regs().rtc_1().read() }; + let rtc_0 = self.inner.regs().rtc_0().read(); + let rtc_1 = self.inner.regs().rtc_1().read(); self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime) } @@ -103,12 +98,10 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { /// /// [`schedule_alarm`]: #method.schedule_alarm pub fn disable_alarm(&mut self) { - unsafe { - self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false)); + self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false)); - while self.inner.regs().irq_setup_0().read().match_active() { - core::hint::spin_loop(); - } + while self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); } } @@ -132,21 +125,19 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { pub fn schedule_alarm(&mut self, filter: DateTimeFilter) { self.disable_alarm(); - unsafe { - self.inner.regs().irq_setup_0().write(|w| { - filter.write_setup_0(w); - }); - self.inner.regs().irq_setup_1().write(|w| { - filter.write_setup_1(w); - }); + self.inner.regs().irq_setup_0().write(|w| { + filter.write_setup_0(w); + }); + self.inner.regs().irq_setup_1().write(|w| { + filter.write_setup_1(w); + }); - self.inner.regs().inte().modify(|w| w.set_rtc(true)); + self.inner.regs().inte().modify(|w| w.set_rtc(true)); - // Set the enable bit and check if it is set - self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); - while !self.inner.regs().irq_setup_0().read().match_active() { - core::hint::spin_loop(); - } + // Set the enable bit and check if it is set + self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); + while !self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); } } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 7da214743..e817d074e 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -79,39 +79,37 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { ) -> Self { into_ref!(inner); - unsafe { - let p = inner.regs(); - let (presc, postdiv) = calc_prescs(config.frequency); + let p = inner.regs(); + let (presc, postdiv) = calc_prescs(config.frequency); - p.cpsr().write(|w| w.set_cpsdvsr(presc)); - p.cr0().write(|w| { - w.set_dss(0b0111); // 8bit - w.set_spo(config.polarity == Polarity::IdleHigh); - w.set_sph(config.phase == Phase::CaptureOnSecondTransition); - w.set_scr(postdiv); - }); + p.cpsr().write(|w| w.set_cpsdvsr(presc)); + p.cr0().write(|w| { + w.set_dss(0b0111); // 8bit + w.set_spo(config.polarity == Polarity::IdleHigh); + w.set_sph(config.phase == Phase::CaptureOnSecondTransition); + w.set_scr(postdiv); + }); - // Always enable DREQ signals -- harmless if DMA is not listening - p.dmacr().write(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - }); + // Always enable DREQ signals -- harmless if DMA is not listening + p.dmacr().write(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + }); - // finally, enable. - p.cr1().write(|w| w.set_sse(true)); + // finally, enable. + p.cr1().write(|w| w.set_sse(true)); - if let Some(pin) = &clk { - pin.io().ctrl().write(|w| w.set_funcsel(1)); - } - if let Some(pin) = &mosi { - pin.io().ctrl().write(|w| w.set_funcsel(1)); - } - if let Some(pin) = &miso { - pin.io().ctrl().write(|w| w.set_funcsel(1)); - } - if let Some(pin) = &cs { - pin.io().ctrl().write(|w| w.set_funcsel(1)); - } + if let Some(pin) = &clk { + pin.io().ctrl().write(|w| w.set_funcsel(1)); + } + if let Some(pin) = &mosi { + pin.io().ctrl().write(|w| w.set_funcsel(1)); + } + if let Some(pin) = &miso { + pin.io().ctrl().write(|w| w.set_funcsel(1)); + } + if let Some(pin) = &cs { + pin.io().ctrl().write(|w| w.set_funcsel(1)); } Self { inner, @@ -122,60 +120,52 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { } pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { - unsafe { - let p = self.inner.regs(); - for &b in data { - while !p.sr().read().tnf() {} - p.dr().write(|w| w.set_data(b as _)); - while !p.sr().read().rne() {} - let _ = p.dr().read(); - } + let p = self.inner.regs(); + for &b in data { + while !p.sr().read().tnf() {} + p.dr().write(|w| w.set_data(b as _)); + while !p.sr().read().rne() {} + let _ = p.dr().read(); } self.flush()?; Ok(()) } pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { - unsafe { - let p = self.inner.regs(); - for b in data { - while !p.sr().read().tnf() {} - p.dr().write(|w| w.set_data(*b as _)); - while !p.sr().read().rne() {} - *b = p.dr().read().data() as u8; - } + let p = self.inner.regs(); + for b in data { + while !p.sr().read().tnf() {} + p.dr().write(|w| w.set_data(*b as _)); + while !p.sr().read().rne() {} + *b = p.dr().read().data() as u8; } self.flush()?; Ok(()) } pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { - unsafe { - let p = self.inner.regs(); - for b in data { - while !p.sr().read().tnf() {} - p.dr().write(|w| w.set_data(0)); - while !p.sr().read().rne() {} - *b = p.dr().read().data() as u8; - } + let p = self.inner.regs(); + for b in data { + while !p.sr().read().tnf() {} + p.dr().write(|w| w.set_data(0)); + while !p.sr().read().rne() {} + *b = p.dr().read().data() as u8; } self.flush()?; Ok(()) } pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { - unsafe { - let p = self.inner.regs(); - let len = read.len().max(write.len()); - for i in 0..len { - let wb = write.get(i).copied().unwrap_or(0); - while !p.sr().read().tnf() {} - p.dr().write(|w| w.set_data(wb as _)); - while !p.sr().read().rne() {} - let rb = p.dr().read().data() as u8; - if let Some(r) = read.get_mut(i) { - *r = rb; - } + let p = self.inner.regs(); + let len = read.len().max(write.len()); + for i in 0..len { + let wb = write.get(i).copied().unwrap_or(0); + while !p.sr().read().tnf() {} + p.dr().write(|w| w.set_data(wb as _)); + while !p.sr().read().rne() {} + let rb = p.dr().read().data() as u8; + if let Some(r) = read.get_mut(i) { + *r = rb; } } self.flush()?; @@ -183,29 +173,25 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { } pub fn flush(&mut self) -> Result<(), Error> { - unsafe { - let p = self.inner.regs(); - while p.sr().read().bsy() {} - } + let p = self.inner.regs(); + while p.sr().read().bsy() {} Ok(()) } pub fn set_frequency(&mut self, freq: u32) { let (presc, postdiv) = calc_prescs(freq); let p = self.inner.regs(); - unsafe { - // disable - p.cr1().write(|w| w.set_sse(false)); + // disable + p.cr1().write(|w| w.set_sse(false)); - // change stuff - p.cpsr().write(|w| w.set_cpsdvsr(presc)); - p.cr0().modify(|w| { - w.set_scr(postdiv); - }); + // change stuff + p.cpsr().write(|w| w.set_cpsdvsr(presc)); + p.cr0().modify(|w| { + w.set_scr(postdiv); + }); - // enable - p.cr1().write(|w| w.set_sse(true)); - } + // enable + p.cr1().write(|w| w.set_sse(true)); } } @@ -337,21 +323,19 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(tx_ch, buffer, self.inner.regs().dr().as_ptr() as *mut _, T::TX_DREQ) }; tx_transfer.await; let p = self.inner.regs(); - unsafe { - while p.sr().read().bsy() {} + while p.sr().read().bsy() {} - // clear RX FIFO contents to prevent stale reads - while p.sr().read().rne() { - let _: u16 = p.dr().read().data(); - } - // clear RX overrun interrupt - p.icr().write(|w| w.set_roric(true)); + // clear RX FIFO contents to prevent stale reads + while p.sr().read().rne() { + let _: u16 = p.dr().read().data(); } + // clear RX overrun interrupt + p.icr().write(|w| w.set_roric(true)); Ok(()) } @@ -363,14 +347,19 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let rx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::read(rx_ch, self.inner.regs().dr().as_ptr() as *const _, buffer, T::RX_DREQ) }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) + crate::dma::write_repeated( + tx_ch, + self.inner.regs().dr().as_ptr() as *mut u8, + buffer.len(), + T::TX_DREQ, + ) }; join(tx_transfer, rx_transfer).await; Ok(()) @@ -394,7 +383,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let rx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) + crate::dma::read(rx_ch, self.inner.regs().dr().as_ptr() as *const _, rx_ptr, T::RX_DREQ) }; let mut tx_ch = self.tx_dma.as_mut().unwrap(); @@ -403,13 +392,13 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let tx_transfer = async { let p = self.inner.regs(); unsafe { - crate::dma::write(&mut tx_ch, tx_ptr, p.dr().ptr() as *mut _, T::TX_DREQ).await; + crate::dma::write(&mut tx_ch, tx_ptr, p.dr().as_ptr() as *mut _, T::TX_DREQ).await; if rx_len > tx_len { let write_bytes_len = rx_len - tx_len; // write dummy data // this will disable incrementation of the buffers - crate::dma::write_repeated(tx_ch, p.dr().ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await + crate::dma::write_repeated(tx_ch, p.dr().as_ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await } } }; @@ -418,16 +407,14 @@ impl<'d, T: Instance> Spi<'d, T, Async> { // if tx > rx we should clear any overflow of the FIFO SPI buffer if tx_len > rx_len { let p = self.inner.regs(); - unsafe { - while p.sr().read().bsy() {} + while p.sr().read().bsy() {} - // clear RX FIFO contents to prevent stale reads - while p.sr().read().rne() { - let _: u16 = p.dr().read().data(); - } - // clear RX overrun interrupt - p.icr().write(|w| w.set_roric(true)); + // clear RX FIFO contents to prevent stale reads + while p.sr().read().rne() { + let _: u16 = p.dr().read().data(); } + // clear RX overrun interrupt + p.icr().write(|w| w.set_roric(true)); } Ok(()) @@ -625,14 +612,12 @@ impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> { fn set_config(&mut self, config: &Self::Config) { let p = self.inner.regs(); let (presc, postdiv) = calc_prescs(config.frequency); - unsafe { - p.cpsr().write(|w| w.set_cpsdvsr(presc)); - p.cr0().write(|w| { - w.set_dss(0b0111); // 8bit - w.set_spo(config.polarity == Polarity::IdleHigh); - w.set_sph(config.phase == Phase::CaptureOnSecondTransition); - w.set_scr(postdiv); - }); - } + p.cpsr().write(|w| w.set_cpsdvsr(presc)); + p.cr0().write(|w| { + w.set_dss(0b0111); // 8bit + w.set_spo(config.polarity == Polarity::IdleHigh); + w.set_sph(config.phase == Phase::CaptureOnSecondTransition); + w.set_scr(postdiv); + }); } } diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index ca8c96c0f..faa8df037 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs @@ -34,13 +34,11 @@ embassy_time::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ impl Driver for TimerDriver { fn now(&self) -> u64 { loop { - unsafe { - let hi = pac::TIMER.timerawh().read(); - let lo = pac::TIMER.timerawl().read(); - let hi2 = pac::TIMER.timerawh().read(); - if hi == hi2 { - return (hi as u64) << 32 | (lo as u64); - } + let hi = pac::TIMER.timerawh().read(); + let lo = pac::TIMER.timerawl().read(); + let hi2 = pac::TIMER.timerawh().read(); + if hi == hi2 { + return (hi as u64) << 32 | (lo as u64); } } } @@ -78,13 +76,13 @@ impl Driver for TimerDriver { // Note that we're not checking the high bits at all. This means the irq may fire early // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire // it is checked if the alarm time has passed. - unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; + pac::TIMER.alarm(n).write_value(timestamp as u32); let now = self.now(); if timestamp <= now { // If alarm timestamp has passed the alarm will not fire. // Disarm the alarm and return `false` to indicate that. - unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } + pac::TIMER.armed().write(|w| w.set_armed(1 << n)); alarm.timestamp.set(u64::MAX); @@ -106,17 +104,17 @@ impl TimerDriver { } else { // Not elapsed, arm it again. // This can happen if it was set more than 2^32 us in the future. - unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; + pac::TIMER.alarm(n).write_value(timestamp as u32); } }); // clear the irq - unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) } + pac::TIMER.intr().write(|w| w.set_alarm(n, true)); } fn trigger_alarm(&self, n: usize, cs: CriticalSection) { // disarm - unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } + pac::TIMER.armed().write(|w| w.set_armed(1 << n)); let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(u64::MAX); @@ -153,24 +151,24 @@ pub unsafe fn init() { #[cfg(feature = "rt")] #[interrupt] -unsafe fn TIMER_IRQ_0() { +fn TIMER_IRQ_0() { DRIVER.check_alarm(0) } #[cfg(feature = "rt")] #[interrupt] -unsafe fn TIMER_IRQ_1() { +fn TIMER_IRQ_1() { DRIVER.check_alarm(1) } #[cfg(feature = "rt")] #[interrupt] -unsafe fn TIMER_IRQ_2() { +fn TIMER_IRQ_2() { DRIVER.check_alarm(2) } #[cfg(feature = "rt")] #[interrupt] -unsafe fn TIMER_IRQ_3() { +fn TIMER_IRQ_3() { DRIVER.check_alarm(3) } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6660d5dc9..30eeb5476 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -73,16 +73,14 @@ pub(crate) fn init_buffers<'d, T: Instance + 'd>( // we clear it after it happens. The downside is that the we manually have // to pend the ISR when we want data transmission to start. let regs = T::regs(); - unsafe { - regs.uartimsc().write(|w| { - w.set_rxim(true); - w.set_rtim(true); - w.set_txim(true); - }); + regs.uartimsc().write(|w| { + w.set_rxim(true); + w.set_rtim(true); + w.set_txim(true); + }); - T::Interrupt::unpend(); - T::Interrupt::enable(); - }; + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; } impl<'d, T: Instance> BufferedUart<'d, T> { @@ -247,12 +245,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { // (Re-)Enable the interrupt to receive more data in case it was // disabled because the buffer was full or errors were detected. let regs = T::regs(); - unsafe { - regs.uartimsc().write_set(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); - } + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); Poll::Ready(result) } @@ -299,12 +295,10 @@ impl<'d, T: Instance> BufferedUartRx<'d, T> { // (Re-)Enable the interrupt to receive more data in case it was // disabled because the buffer was full or errors were detected. let regs = T::regs(); - unsafe { - regs.uartimsc().write_set(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); - } + regs.uartimsc().write_set(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); } } @@ -414,7 +408,7 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { } pub fn busy(&self) -> bool { - unsafe { T::regs().uartfr().read().busy() } + T::regs().uartfr().read().busy() } /// Assert a break condition after waiting for the transmit buffers to empty, @@ -426,42 +420,35 @@ impl<'d, T: Instance> BufferedUartTx<'d, T> { /// for the transmit fifo to empty, which may take a while on slow links. pub async fn send_break(&mut self, bits: u32) { let regs = T::regs(); - let bits = bits.max(unsafe { + let bits = bits.max({ let lcr = regs.uartlcr_h().read(); let width = lcr.wlen() as u32 + 5; let parity = lcr.pen() as u32; let stops = 1 + lcr.stp2() as u32; 2 * (1 + width + parity + stops) }); - let divx64 = unsafe { - ((regs.uartibrd().read().baud_divint() as u32) << 6) + regs.uartfbrd().read().baud_divfrac() as u32 - } as u64; + let divx64 = (((regs.uartibrd().read().baud_divint() as u32) << 6) + + regs.uartfbrd().read().baud_divfrac() as u32) as u64; let div_clk = clk_peri_freq() as u64 * 64; let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; Self::flush().await.unwrap(); while self.busy() {} - unsafe { - regs.uartlcr_h().write_set(|w| w.set_brk(true)); - } + regs.uartlcr_h().write_set(|w| w.set_brk(true)); Timer::after(Duration::from_micros(wait_usecs)).await; - unsafe { - regs.uartlcr_h().write_clear(|w| w.set_brk(true)); - } + regs.uartlcr_h().write_clear(|w| w.set_brk(true)); } } impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { fn drop(&mut self) { let state = T::buffered_state(); - unsafe { - state.rx_buf.deinit(); + unsafe { state.rx_buf.deinit() } - // TX is inactive if the the buffer is not available. - // We can now unregister the interrupt handler - if state.tx_buf.len() == 0 { - T::Interrupt::disable(); - } + // TX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.tx_buf.len() == 0 { + T::Interrupt::disable(); } } } @@ -469,14 +456,12 @@ impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { fn drop(&mut self) { let state = T::buffered_state(); - unsafe { - state.tx_buf.deinit(); + unsafe { state.tx_buf.deinit() } - // RX is inactive if the the buffer is not available. - // We can now unregister the interrupt handler - if state.rx_buf.len() == 0 { - T::Interrupt::disable(); - } + // RX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.rx_buf.len() == 0 { + T::Interrupt::disable(); } } } @@ -494,94 +479,92 @@ impl interrupt::typelevel::Handler for BufferedInterr let s = T::buffered_state(); - unsafe { - // Clear TX and error interrupt flags - // RX interrupt flags are cleared by reading from the FIFO. - let ris = r.uartris().read(); - r.uarticr().write(|w| { - w.set_txic(ris.txris()); - w.set_feic(ris.feris()); - w.set_peic(ris.peris()); - w.set_beic(ris.beris()); - w.set_oeic(ris.oeris()); - }); + // Clear TX and error interrupt flags + // RX interrupt flags are cleared by reading from the FIFO. + let ris = r.uartris().read(); + r.uarticr().write(|w| { + w.set_txic(ris.txris()); + w.set_feic(ris.feris()); + w.set_peic(ris.peris()); + w.set_beic(ris.beris()); + w.set_oeic(ris.oeris()); + }); - trace!("on_interrupt ris={:#X}", ris.0); + trace!("on_interrupt ris={:#X}", ris.0); - // Errors - if ris.feris() { - warn!("Framing error"); - } - if ris.peris() { - warn!("Parity error"); - } - if ris.beris() { - warn!("Break error"); - } - if ris.oeris() { - warn!("Overrun error"); - } - - // RX - let mut rx_writer = s.rx_buf.writer(); - let rx_buf = rx_writer.push_slice(); - let mut n_read = 0; - let mut error = false; - for rx_byte in rx_buf { - if r.uartfr().read().rxfe() { - break; - } - let dr = r.uartdr().read(); - if (dr.0 >> 8) != 0 { - s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); - error = true; - // only fill the buffer with valid characters. the current character is fine - // if the error is an overrun, but if we add it to the buffer we'll report - // the overrun one character too late. drop it instead and pretend we were - // a bit slower at draining the rx fifo than we actually were. - // this is consistent with blocking uart error reporting. - break; - } - *rx_byte = dr.data(); - n_read += 1; - } - if n_read > 0 { - rx_writer.push_done(n_read); - s.rx_waker.wake(); - } else if error { - s.rx_waker.wake(); - } - // Disable any further RX interrupts when the buffer becomes full or - // errors have occurred. This lets us buffer additional errors in the - // fifo without needing more error storage locations, and most applications - // will want to do a full reset of their uart state anyway once an error - // has happened. - if s.rx_buf.is_full() || error { - r.uartimsc().write_clear(|w| { - w.set_rxim(true); - w.set_rtim(true); - }); - } - - // TX - let mut tx_reader = s.tx_buf.reader(); - let tx_buf = tx_reader.pop_slice(); - let mut n_written = 0; - for tx_byte in tx_buf.iter_mut() { - if r.uartfr().read().txff() { - break; - } - r.uartdr().write(|w| w.set_data(*tx_byte)); - n_written += 1; - } - if n_written > 0 { - tx_reader.pop_done(n_written); - s.tx_waker.wake(); - } - // The TX interrupt only triggers once when the FIFO threshold is - // crossed. No need to disable it when the buffer becomes empty - // as it does re-trigger anymore once we have cleared it. + // Errors + if ris.feris() { + warn!("Framing error"); } + if ris.peris() { + warn!("Parity error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + // RX + let mut rx_writer = unsafe { s.rx_buf.writer() }; + let rx_buf = rx_writer.push_slice(); + let mut n_read = 0; + let mut error = false; + for rx_byte in rx_buf { + if r.uartfr().read().rxfe() { + break; + } + let dr = r.uartdr().read(); + if (dr.0 >> 8) != 0 { + s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); + error = true; + // only fill the buffer with valid characters. the current character is fine + // if the error is an overrun, but if we add it to the buffer we'll report + // the overrun one character too late. drop it instead and pretend we were + // a bit slower at draining the rx fifo than we actually were. + // this is consistent with blocking uart error reporting. + break; + } + *rx_byte = dr.data(); + n_read += 1; + } + if n_read > 0 { + rx_writer.push_done(n_read); + s.rx_waker.wake(); + } else if error { + s.rx_waker.wake(); + } + // Disable any further RX interrupts when the buffer becomes full or + // errors have occurred. This lets us buffer additional errors in the + // fifo without needing more error storage locations, and most applications + // will want to do a full reset of their uart state anyway once an error + // has happened. + if s.rx_buf.is_full() || error { + r.uartimsc().write_clear(|w| { + w.set_rxim(true); + w.set_rtim(true); + }); + } + + // TX + let mut tx_reader = unsafe { s.tx_buf.reader() }; + let tx_buf = tx_reader.pop_slice(); + let mut n_written = 0; + for tx_byte in tx_buf.iter_mut() { + if r.uartfr().read().txff() { + break; + } + r.uartdr().write(|w| w.set_data(*tx_byte)); + n_written += 1; + } + if n_written > 0 { + tx_reader.pop_done(n_written); + s.tx_waker.wake(); + } + // The TX interrupt only triggers once when the FIFO threshold is + // crossed. No need to disable it when the buffer becomes empty + // as it does re-trigger anymore once we have cleared it. } } @@ -695,24 +678,22 @@ mod eh02 { fn read(&mut self) -> Result> { let r = T::regs(); - unsafe { - if r.uartfr().read().rxfe() { - return Err(nb::Error::WouldBlock); - } + if r.uartfr().read().rxfe() { + return Err(nb::Error::WouldBlock); + } - let dr = r.uartdr().read(); + let dr = r.uartdr().read(); - if dr.oe() { - Err(nb::Error::Other(Error::Overrun)) - } else if dr.be() { - Err(nb::Error::Other(Error::Break)) - } else if dr.pe() { - Err(nb::Error::Other(Error::Parity)) - } else if dr.fe() { - Err(nb::Error::Other(Error::Framing)) - } else { - Ok(dr.data()) - } + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else { + Ok(dr.data()) } } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 5e3ae8a25..7b94bce5e 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -146,23 +146,21 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { let r = T::regs(); - unsafe { - for &b in buffer { - while r.uartfr().read().txff() {} - r.uartdr().write(|w| w.set_data(b)); - } + for &b in buffer { + while r.uartfr().read().txff() {} + r.uartdr().write(|w| w.set_data(b)); } Ok(()) } pub fn blocking_flush(&mut self) -> Result<(), Error> { let r = T::regs(); - unsafe { while !r.uartfr().read().txfe() {} } + while !r.uartfr().read().txfe() {} Ok(()) } pub fn busy(&self) -> bool { - unsafe { T::regs().uartfr().read().busy() } + T::regs().uartfr().read().busy() } /// Assert a break condition after waiting for the transmit buffers to empty, @@ -174,28 +172,23 @@ impl<'d, T: Instance, M: Mode> UartTx<'d, T, M> { /// for the transmit fifo to empty, which may take a while on slow links. pub async fn send_break(&mut self, bits: u32) { let regs = T::regs(); - let bits = bits.max(unsafe { + let bits = bits.max({ let lcr = regs.uartlcr_h().read(); let width = lcr.wlen() as u32 + 5; let parity = lcr.pen() as u32; let stops = 1 + lcr.stp2() as u32; 2 * (1 + width + parity + stops) }); - let divx64 = unsafe { - ((regs.uartibrd().read().baud_divint() as u32) << 6) + regs.uartfbrd().read().baud_divfrac() as u32 - } as u64; + let divx64 = (((regs.uartibrd().read().baud_divint() as u32) << 6) + + regs.uartfbrd().read().baud_divfrac() as u32) as u64; let div_clk = clk_peri_freq() as u64 * 64; let wait_usecs = (1_000_000 * bits as u64 * divx64 * 16 + div_clk - 1) / div_clk; self.blocking_flush().unwrap(); while self.busy() {} - unsafe { - regs.uartlcr_h().write_set(|w| w.set_brk(true)); - } + regs.uartlcr_h().write_set(|w| w.set_brk(true)); Timer::after(Duration::from_micros(wait_usecs)).await; - unsafe { - regs.uartlcr_h().write_clear(|w| w.set_brk(true)); - } + regs.uartlcr_h().write_clear(|w| w.set_brk(true)); } } @@ -221,7 +214,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, T::regs().uartdr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(ch, buffer, T::regs().uartdr().as_ptr() as *mut _, T::TX_DREQ) }; transfer.await; Ok(()) @@ -246,7 +239,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { debug_assert_eq!(has_irq, rx_dma.is_some()); if has_irq { // disable all error interrupts initially - unsafe { T::regs().uartimsc().write(|w| w.0 = 0) } + T::regs().uartimsc().write(|w| w.0 = 0); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; } @@ -267,11 +260,11 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result { let r = T::regs(); for (i, b) in buffer.iter_mut().enumerate() { - if unsafe { r.uartfr().read().rxfe() } { + if r.uartfr().read().rxfe() { return Ok(i); } - let dr = unsafe { r.uartdr().read() }; + let dr = r.uartdr().read(); if dr.oe() { return Err(Error::Overrun); @@ -292,15 +285,13 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { fn drop(&mut self) { if let Some(_) = self.rx_dma { - unsafe { - T::Interrupt::disable(); - // clear dma flags. irq handlers use these to disambiguate among themselves. - T::regs().uartdmacr().write_clear(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - reg.set_dmaonerr(true); - }); - } + T::Interrupt::disable(); + // clear dma flags. irq handlers use these to disambiguate among themselves. + T::regs().uartdmacr().write_clear(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + reg.set_dmaonerr(true); + }); } } } @@ -355,14 +346,12 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // clear error flags before we drain the fifo. errors that have accumulated // in the flags will also be present in the fifo. T::dma_state().rx_errs.store(0, Ordering::Relaxed); - unsafe { - T::regs().uarticr().write(|w| { - w.set_oeic(true); - w.set_beic(true); - w.set_peic(true); - w.set_feic(true); - }); - } + T::regs().uarticr().write(|w| { + w.set_oeic(true); + w.set_beic(true); + w.set_peic(true); + w.set_feic(true); + }); // then drain the fifo. we need to read at most 32 bytes. errors that apply // to fifo bytes will be reported directly. @@ -379,20 +368,20 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { // interrupt flags will have been raised, and those will be picked up immediately // by the interrupt handler. let ch = self.rx_dma.as_mut().unwrap(); + T::regs().uartimsc().write_set(|w| { + w.set_oeim(true); + w.set_beim(true); + w.set_peim(true); + w.set_feim(true); + }); + T::regs().uartdmacr().write_set(|reg| { + reg.set_rxdmae(true); + reg.set_dmaonerr(true); + }); let transfer = unsafe { - T::regs().uartimsc().write_set(|w| { - w.set_oeim(true); - w.set_beim(true); - w.set_peim(true); - w.set_feim(true); - }); - T::regs().uartdmacr().write_set(|reg| { - reg.set_rxdmae(true); - reg.set_dmaonerr(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, T::regs().uartdr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::read(ch, T::regs().uartdr().as_ptr() as *const _, buffer, T::RX_DREQ) }; // wait for either the transfer to complete or an error to happen. @@ -575,81 +564,79 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { config: Config, ) { let r = T::regs(); - unsafe { - if let Some(pin) = &tx { - pin.io().ctrl().write(|w| { - w.set_funcsel(2); - w.set_outover(if config.invert_tx { - Outover::INVERT - } else { - Outover::NORMAL - }); + if let Some(pin) = &tx { + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_outover(if config.invert_tx { + Outover::INVERT + } else { + Outover::NORMAL }); - pin.pad_ctrl().write(|w| w.set_ie(true)); - } - if let Some(pin) = &rx { - pin.io().ctrl().write(|w| { - w.set_funcsel(2); - w.set_inover(if config.invert_rx { - Inover::INVERT - } else { - Inover::NORMAL - }); - }); - pin.pad_ctrl().write(|w| w.set_ie(true)); - } - if let Some(pin) = &cts { - pin.io().ctrl().write(|w| { - w.set_funcsel(2); - w.set_inover(if config.invert_cts { - Inover::INVERT - } else { - Inover::NORMAL - }); - }); - pin.pad_ctrl().write(|w| w.set_ie(true)); - } - if let Some(pin) = &rts { - pin.io().ctrl().write(|w| { - w.set_funcsel(2); - w.set_outover(if config.invert_rts { - Outover::INVERT - } else { - Outover::NORMAL - }); - }); - pin.pad_ctrl().write(|w| w.set_ie(true)); - } - - Self::set_baudrate_inner(config.baudrate); - - let (pen, eps) = match config.parity { - Parity::ParityNone => (false, false), - Parity::ParityOdd => (true, false), - Parity::ParityEven => (true, true), - }; - - r.uartlcr_h().write(|w| { - w.set_wlen(config.data_bits.bits()); - w.set_stp2(config.stop_bits == StopBits::STOP2); - w.set_pen(pen); - w.set_eps(eps); - w.set_fen(true); - }); - - r.uartifls().write(|w| { - w.set_rxiflsel(0b000); - w.set_txiflsel(0b000); - }); - - r.uartcr().write(|w| { - w.set_uarten(true); - w.set_rxe(true); - w.set_txe(true); - w.set_ctsen(cts.is_some()); - w.set_rtsen(rts.is_some()); }); + pin.pad_ctrl().write(|w| w.set_ie(true)); } + if let Some(pin) = &rx { + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_rx { + Inover::INVERT + } else { + Inover::NORMAL + }); + }); + pin.pad_ctrl().write(|w| w.set_ie(true)); + } + if let Some(pin) = &cts { + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_cts { + Inover::INVERT + } else { + Inover::NORMAL + }); + }); + pin.pad_ctrl().write(|w| w.set_ie(true)); + } + if let Some(pin) = &rts { + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_outover(if config.invert_rts { + Outover::INVERT + } else { + Outover::NORMAL + }); + }); + pin.pad_ctrl().write(|w| w.set_ie(true)); + } + + Self::set_baudrate_inner(config.baudrate); + + let (pen, eps) = match config.parity { + Parity::ParityNone => (false, false), + Parity::ParityOdd => (true, false), + Parity::ParityEven => (true, true), + }; + + r.uartlcr_h().write(|w| { + w.set_wlen(config.data_bits.bits()); + w.set_stp2(config.stop_bits == StopBits::STOP2); + w.set_pen(pen); + w.set_eps(eps); + w.set_fen(true); + }); + + r.uartifls().write(|w| { + w.set_rxiflsel(0b000); + w.set_txiflsel(0b000); + }); + + r.uartcr().write(|w| { + w.set_uarten(true); + w.set_rxe(true); + w.set_txe(true); + w.set_ctsen(cts.is_some()); + w.set_rtsen(rts.is_some()); + }); } /// sets baudrate on runtime @@ -674,15 +661,13 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { baud_fbrd = 0; } - unsafe { - // Load PL011's baud divisor registers - r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); - r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); + // Load PL011's baud divisor registers + r.uartibrd().write_value(pac::uart::regs::Uartibrd(baud_ibrd)); + r.uartfbrd().write_value(pac::uart::regs::Uartfbrd(baud_fbrd)); - // PL011 needs a (dummy) line control register write to latch in the - // divisors. We don't want to actually change LCR contents here. - r.uartlcr_h().modify(|_| {}); - } + // PL011 needs a (dummy) line control register write to latch in the + // divisors. We don't want to actually change LCR contents here. + r.uartlcr_h().modify(|_| {}); } } @@ -731,24 +716,22 @@ mod eh02 { type Error = Error; fn read(&mut self) -> Result> { let r = T::regs(); - unsafe { - if r.uartfr().read().rxfe() { - return Err(nb::Error::WouldBlock); - } + if r.uartfr().read().rxfe() { + return Err(nb::Error::WouldBlock); + } - let dr = r.uartdr().read(); + let dr = r.uartdr().read(); - if dr.oe() { - Err(nb::Error::Other(Error::Overrun)) - } else if dr.be() { - Err(nb::Error::Other(Error::Break)) - } else if dr.pe() { - Err(nb::Error::Other(Error::Parity)) - } else if dr.fe() { - Err(nb::Error::Other(Error::Framing)) - } else { - Ok(dr.data()) - } + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else { + Ok(dr.data()) } } } @@ -758,22 +741,18 @@ mod eh02 { fn write(&mut self, word: u8) -> Result<(), nb::Error> { let r = T::regs(); - unsafe { - if r.uartfr().read().txff() { - return Err(nb::Error::WouldBlock); - } - - r.uartdr().write(|w| w.set_data(word)); + if r.uartfr().read().txff() { + return Err(nb::Error::WouldBlock); } + + r.uartdr().write(|w| w.set_data(word)); Ok(()) } fn flush(&mut self) -> Result<(), nb::Error> { let r = T::regs(); - unsafe { - if !r.uartfr().read().txfe() { - return Err(nb::Error::WouldBlock); - } + if !r.uartfr().read().txfe() { + return Err(nb::Error::WouldBlock); } Ok(()) } @@ -854,22 +833,20 @@ mod eh1 { impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> { fn read(&mut self) -> nb::Result { let r = T::regs(); - unsafe { - let dr = r.uartdr().read(); + let dr = r.uartdr().read(); - if dr.oe() { - Err(nb::Error::Other(Error::Overrun)) - } else if dr.be() { - Err(nb::Error::Other(Error::Break)) - } else if dr.pe() { - Err(nb::Error::Other(Error::Parity)) - } else if dr.fe() { - Err(nb::Error::Other(Error::Framing)) - } else if dr.fe() { - Ok(dr.data()) - } else { - Err(nb::Error::WouldBlock) - } + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else if dr.fe() { + Ok(dr.data()) + } else { + Err(nb::Error::WouldBlock) } } } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 9fb0dfb70..1900ab416 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -39,7 +39,7 @@ impl crate::usb::Instance for peripherals::USB { const EP_COUNT: usize = 16; const EP_MEMORY_SIZE: usize = 4096; -const EP_MEMORY: *mut u8 = pac::USBCTRL_DPRAM.0; +const EP_MEMORY: *mut u8 = pac::USBCTRL_DPRAM.as_ptr() as *mut u8; const NEW_AW: AtomicWaker = AtomicWaker::new(); static BUS_WAKER: AtomicWaker = NEW_AW; @@ -111,7 +111,7 @@ impl<'d, T: Instance> Driver<'d, T> { let regs = T::regs(); unsafe { // zero fill regs - let p = regs.0 as *mut u32; + let p = regs.as_ptr() as *mut u32; for i in 0..0x9c / 4 { p.add(i).write_volatile(0) } @@ -121,20 +121,20 @@ impl<'d, T: Instance> Driver<'d, T> { for i in 0..0x100 / 4 { p.add(i).write_volatile(0) } - - regs.usb_muxing().write(|w| { - w.set_to_phy(true); - w.set_softcon(true); - }); - regs.usb_pwr().write(|w| { - w.set_vbus_detect(true); - w.set_vbus_detect_override_en(true); - }); - regs.main_ctrl().write(|w| { - w.set_controller_en(true); - }); } + regs.usb_muxing().write(|w| { + w.set_to_phy(true); + w.set_softcon(true); + }); + regs.usb_pwr().write(|w| { + w.set_vbus_detect(true); + w.set_vbus_detect_override_en(true); + }); + regs.main_ctrl().write(|w| { + w.set_controller_en(true); + }); + // Initialize the bus so that it signals that power is available BUS_WAKER.wake(); @@ -213,22 +213,18 @@ impl<'d, T: Instance> Driver<'d, T> { }; match D::dir() { - Direction::Out => unsafe { - T::dpram().ep_out_control(index - 1).write(|w| { - w.set_enable(false); - w.set_buffer_address(addr); - w.set_interrupt_per_buff(true); - w.set_endpoint_type(ep_type_reg); - }) - }, - Direction::In => unsafe { - T::dpram().ep_in_control(index - 1).write(|w| { - w.set_enable(false); - w.set_buffer_address(addr); - w.set_interrupt_per_buff(true); - w.set_endpoint_type(ep_type_reg); - }) - }, + Direction::Out => T::dpram().ep_out_control(index - 1).write(|w| { + w.set_enable(false); + w.set_buffer_address(addr); + w.set_interrupt_per_buff(true); + w.set_endpoint_type(ep_type_reg); + }), + Direction::In => T::dpram().ep_in_control(index - 1).write(|w| { + w.set_enable(false); + w.set_buffer_address(addr); + w.set_interrupt_per_buff(true); + w.set_endpoint_type(ep_type_reg); + }), } Ok(Endpoint { @@ -315,22 +311,21 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { let regs = T::regs(); - unsafe { - regs.inte().write(|w| { - w.set_bus_reset(true); - w.set_buff_status(true); - w.set_dev_resume_from_host(true); - w.set_dev_suspend(true); - w.set_setup_req(true); - }); - regs.int_ep_ctrl().write(|w| { - w.set_int_ep_active(0xFFFE); // all EPs - }); - regs.sie_ctrl().write(|w| { - w.set_ep0_int_1buf(true); - w.set_pullup_en(true); - }) - } + regs.inte().write(|w| { + w.set_bus_reset(true); + w.set_buff_status(true); + w.set_dev_resume_from_host(true); + w.set_dev_suspend(true); + w.set_setup_req(true); + }); + regs.int_ep_ctrl().write(|w| { + w.set_int_ep_active(0xFFFE); // all EPs + }); + regs.sie_ctrl().write(|w| { + w.set_ep0_int_1buf(true); + w.set_pullup_en(true); + }); + trace!("enabled"); ( @@ -355,7 +350,7 @@ pub struct Bus<'d, T: Instance> { impl<'d, T: Instance> driver::Bus for Bus<'d, T> { async fn poll(&mut self) -> Event { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { BUS_WAKER.register(cx.waker()); if !self.inited { @@ -425,14 +420,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let n = ep_addr.index(); match ep_addr.direction() { - Direction::In => unsafe { + Direction::In => { T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| { w.set_pid(0, true); // first packet is DATA0, but PID is flipped before }); EP_IN_WAKERS[n].wake(); - }, - Direction::Out => unsafe { + } + Direction::Out => { T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| { @@ -446,7 +441,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { w.set_available(0, true); }); EP_OUT_WAKERS[n].wake(); - }, + } } } @@ -504,7 +499,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { 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() }; + let val = T::dpram().ep_in_control(self.info.addr.index() - 1).read(); if val.enable() { Poll::Ready(()) } else { @@ -526,7 +521,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { 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() }; + let val = T::dpram().ep_out_control(self.info.addr.index() - 1).read(); if val.enable() { Poll::Ready(()) } else { @@ -542,7 +537,7 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { 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 { + let val = poll_fn(|cx| { EP_OUT_WAKERS[index].register(cx.waker()); let val = T::dpram().ep_out_buffer_control(index).read(); if val.available(0) { @@ -561,19 +556,17 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { 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); - }); - } + 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) } @@ -588,7 +581,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { trace!("WRITE WAITING"); let index = self.info.addr.index(); - let val = poll_fn(|cx| unsafe { + let val = poll_fn(|cx| { EP_IN_WAKERS[index].register(cx.waker()); let val = T::dpram().ep_in_buffer_control(index).read(); if val.available(0) { @@ -601,21 +594,19 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { 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); - }); - } + 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"); @@ -637,9 +628,9 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { loop { trace!("SETUP read waiting"); let regs = T::regs(); - unsafe { regs.inte().write_set(|w| w.set_setup_req(true)) }; + regs.inte().write_set(|w| w.set_setup_req(true)); - poll_fn(|cx| unsafe { + poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); let regs = T::regs(); if regs.sie_status().read().setup_rec() { @@ -654,13 +645,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { EndpointBuffer::::new(0, 8).read(&mut buf); let regs = T::regs(); - unsafe { - regs.sie_status().write(|w| w.set_setup_rec(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)); - } + // 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; @@ -668,23 +657,21 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { } 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); - }); - } + 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 { + let val = poll_fn(|cx| { EP_OUT_WAKERS[0].register(cx.waker()); let val = T::dpram().ep_out_buffer_control(0).read(); if val.available(0) { @@ -714,24 +701,22 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { } 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); - }); - } + 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 { + poll_fn(|cx| { EP_IN_WAKERS[0].register(cx.waker()); let bufcontrol = T::dpram().ep_in_buffer_control(0); if bufcontrol.read().available(0) { @@ -745,19 +730,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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); - }); - } + 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(()) @@ -767,26 +750,24 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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); - }); - } + 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) } { + if bufcontrol.read().available(0) { Poll::Pending } else { Poll::Ready(()) @@ -799,14 +780,12 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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)); - } + 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)); } async fn accept_set_address(&mut self, addr: u8) { @@ -814,6 +793,6 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { let regs = T::regs(); trace!("setting addr: {}", addr); - unsafe { regs.addr_endp().write(|w| w.set_address(addr)) } + regs.addr_endp().write(|w| w.set_address(addr)) } } diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index 78a295ae7..d37795cc9 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -35,45 +35,37 @@ impl Watchdog { /// * `cycles` - Total number of tick cycles before the next tick is generated. /// It is expected to be the frequency in MHz of clk_ref. pub fn enable_tick_generation(&mut self, cycles: u8) { - unsafe { - let watchdog = pac::WATCHDOG; - watchdog.tick().write(|w| { - w.set_enable(true); - w.set_cycles(cycles.into()) - }); - } + let watchdog = pac::WATCHDOG; + watchdog.tick().write(|w| { + w.set_enable(true); + w.set_cycles(cycles.into()) + }); } /// Defines whether or not the watchdog timer should be paused when processor(s) are in debug mode /// or when JTAG is accessing bus fabric pub fn pause_on_debug(&mut self, pause: bool) { - unsafe { - let watchdog = pac::WATCHDOG; - watchdog.ctrl().write(|w| { - w.set_pause_dbg0(pause); - w.set_pause_dbg1(pause); - w.set_pause_jtag(pause); - }) - } + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| { + w.set_pause_dbg0(pause); + w.set_pause_dbg1(pause); + w.set_pause_jtag(pause); + }) } fn load_counter(&self, counter: u32) { - unsafe { - let watchdog = pac::WATCHDOG; - watchdog.load().write_value(pac::watchdog::regs::Load(counter)); - } + let watchdog = pac::WATCHDOG; + watchdog.load().write_value(pac::watchdog::regs::Load(counter)); } fn enable(&self, bit: bool) { - unsafe { - let watchdog = pac::WATCHDOG; - watchdog.ctrl().write(|w| w.set_enable(bit)) - } + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| w.set_enable(bit)) } // Configure which hardware will be reset by the watchdog // (everything except ROSC, XOSC) - unsafe fn configure_wdog_reset_triggers(&self) { + fn configure_wdog_reset_triggers(&self) { let psm = pac::PSM; psm.wdsel().write_value(pac::psm::regs::Wdsel( 0x0001ffff & !(0x01 << 0usize) & !(0x01 << 1usize), @@ -100,23 +92,19 @@ impl Watchdog { self.load_value = delay_us * 2; self.enable(false); - unsafe { - self.configure_wdog_reset_triggers(); - } + self.configure_wdog_reset_triggers(); self.load_counter(self.load_value); self.enable(true); } /// Trigger a system reset pub fn trigger_reset(&mut self) { - unsafe { - self.configure_wdog_reset_triggers(); - self.pause_on_debug(false); - self.enable(true); - let watchdog = pac::WATCHDOG; - watchdog.ctrl().write(|w| { - w.set_trigger(true); - }) - } + self.configure_wdog_reset_triggers(); + self.pause_on_debug(false); + self.enable(true); + let watchdog = pac::WATCHDOG; + watchdog.ctrl().write(|w| { + w.set_trigger(true); + }) } } diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs index 6a982507a..0e0de85fa 100644 --- a/tests/rp/src/bin/float.rs +++ b/tests/rp/src/bin/float.rs @@ -18,11 +18,9 @@ async fn main(_spawner: Spawner) { const PI_F: f32 = 3.1415926535f32; const PI_D: f64 = 3.14159265358979323846f64; - unsafe { - pac::BUSCTRL - .perfsel(0) - .write(|r| r.set_perfsel(pac::busctrl::vals::Perfsel::ROM)); - } + pac::BUSCTRL + .perfsel(0) + .write(|r| r.set_perfsel(pac::busctrl::vals::Perfsel::ROM)); for i in 0..=360 { let rad_f = (i as f32) * PI_F / 180.0; @@ -46,7 +44,7 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_millis(10)).await; } - let rom_accesses = unsafe { pac::BUSCTRL.perfctr(0).read().perfctr() }; + let rom_accesses = pac::BUSCTRL.perfctr(0).read().perfctr(); // every float operation used here uses at least 10 cycles defmt::assert!(rom_accesses >= 360 * 12 * 10); From af451b5462b4cbfaab6ac3ffe56ec9e981f460f7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 15 Jun 2023 21:02:10 -0500 Subject: [PATCH 1364/1575] stm32/wpan: move schi command into sys --- embassy-stm32-wpan/src/ble.rs | 4 ++-- embassy-stm32-wpan/src/cmd.rs | 8 +++++++ embassy-stm32-wpan/src/rc.rs | 7 +----- embassy-stm32-wpan/src/shci.rs | 39 ++++++++++------------------------ embassy-stm32-wpan/src/sys.rs | 33 ++++++++++++++++++++++++++-- tests/stm32/src/bin/tl_mbox.rs | 3 +++ 6 files changed, 56 insertions(+), 38 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 57348a925..46a2f41c4 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -48,11 +48,11 @@ impl Ble { // TODO: ACL data ack to the user } - pub fn ble_send_cmd(buf: &[u8]) { + pub fn send_cmd(buf: &[u8]) { debug!("writing ble cmd"); unsafe { - let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; + let pcmd_buffer: *mut CmdPacket = BLE_CMD_BUFFER.as_mut_ptr(); let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmdserial; let pcmd_serial_buf: *mut u8 = pcmd_serial.cast(); diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index 1f7dae7f7..a023201ba 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -26,6 +26,14 @@ pub struct CmdSerial { pub cmd: Cmd, } +#[derive(Copy, Clone, Default)] +#[repr(C, packed)] +pub struct CmdSerialStub { + pub ty: u8, + pub cmd_code: u16, + pub payload_len: u8, +} + #[derive(Copy, Clone, Default)] #[repr(C, packed)] pub struct CmdPacket { diff --git a/embassy-stm32-wpan/src/rc.rs b/embassy-stm32-wpan/src/rc.rs index aae2265ed..a217aa224 100644 --- a/embassy-stm32-wpan/src/rc.rs +++ b/embassy-stm32-wpan/src/rc.rs @@ -20,7 +20,7 @@ impl<'d> RadioCoprocessor<'d> { let cmd = TlPacketType::try_from(cmd_code).unwrap(); match &cmd { - TlPacketType::BleCmd => Ble::ble_send_cmd(buf), + TlPacketType::BleCmd => Ble::send_cmd(buf), _ => todo!(), } } @@ -33,11 +33,6 @@ impl<'d> RadioCoprocessor<'d> { let event = evt.evt(); evt.write(&mut self.rx_buf).unwrap(); - - if event.kind() == 18 { - shci::shci_ble_init(Default::default()); - self.rx_buf[0] = 0x04; - } } if self.mbox.pop_last_cc_evt().is_some() { diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index 8537995ff..6e58a7156 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -1,8 +1,10 @@ +use core::{mem, slice}; + use super::cmd::CmdPacket; use super::consts::TlPacketType; use super::{sys, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE}; -const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; +pub const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; #[derive(Debug, Clone, Copy)] #[repr(C, packed)] @@ -32,6 +34,12 @@ pub struct ShciBleInitCmdParam { pub hw_version: u8, } +impl ShciBleInitCmdParam { + pub fn payload<'a>(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::()) } + } +} + impl Default for ShciBleInitCmdParam { fn default() -> Self { Self { @@ -66,35 +74,10 @@ pub struct ShciHeader { #[derive(Debug, Clone, Copy)] #[repr(C, packed)] pub struct ShciBleInitCmdPacket { - header: ShciHeader, - param: ShciBleInitCmdParam, + pub header: ShciHeader, + pub param: ShciBleInitCmdParam, } pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; #[allow(dead_code)] // Not used currently but reserved const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; - -pub fn shci_ble_init(param: ShciBleInitCmdParam) { - debug!("sending SHCI"); - - let mut packet = ShciBleInitCmdPacket { - header: ShciHeader::default(), - param, - }; - - let packet_ptr: *mut _ = &mut packet; - - unsafe { - let cmd_ptr: *mut CmdPacket = packet_ptr.cast(); - - (*cmd_ptr).cmdserial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT; - (*cmd_ptr).cmdserial.cmd.payload_len = core::mem::size_of::() as u8; - - let p_cmd_buffer = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer; - core::ptr::write(p_cmd_buffer, *cmd_ptr); - - p_cmd_buffer.cmdserial.ty = TlPacketType::SysCmd as u8; - - sys::Sys::send_cmd(); - } -} diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index 0cff5c746..76f65cbd8 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,7 +1,12 @@ +use core::ptr; +use core::sync::atomic::{compiler_fence, Ordering}; + use embassy_stm32::ipcc::Ipcc; -use crate::cmd::{CmdPacket, CmdSerial}; +use crate::cmd::{CmdPacket, CmdSerial, CmdSerialStub}; +use crate::consts::TlPacketType; use crate::evt::{CcEvt, EvtBox, EvtSerial}; +use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT}; use crate::tables::SysTable; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; @@ -58,7 +63,31 @@ impl Sys { Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); } - pub fn send_cmd() { + pub fn shci_ble_init(param: ShciBleInitCmdParam) { + debug!("sending SHCI"); + + Self::send_cmd(SCHI_OPCODE_BLE_INIT, param.payload()); + } + + pub fn send_cmd(opcode: u16, payload: &[u8]) { + unsafe { + let p_cmd_serial = &mut (*SYS_CMD_BUF.as_mut_ptr()).cmdserial as *mut _ as *mut CmdSerialStub; + let p_payload = &mut (*SYS_CMD_BUF.as_mut_ptr()).cmdserial.cmd.payload as *mut _; + + ptr::write_volatile( + p_cmd_serial, + CmdSerialStub { + ty: TlPacketType::SysCmd as u8, + cmd_code: opcode, + payload_len: payload.len() as u8, + }, + ); + + ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); + } + + compiler_fence(Ordering::SeqCst); + Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); } diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 4669cbc62..5e7d11ef5 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -11,6 +11,7 @@ use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::Config; use embassy_stm32_wpan::rc::RadioCoprocessor; +use embassy_stm32_wpan::sys::Sys; use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; @@ -56,6 +57,8 @@ async fn main(_spawner: Spawner) { let response = rc.read().await; info!("coprocessor ready {}", response); + Sys::shci_ble_init(Default::default()); + rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); let response = rc.read().await; info!("ble reset rsp {}", response); From 54fc933932e9f8e510331401820b57ba32d8ade3 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Fri, 16 Jun 2023 12:59:23 +0200 Subject: [PATCH 1365/1575] embassy-executor: introduce `InterruptExecutor::spawner()` --- embassy-executor/src/arch/cortex_m.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index d6a55c4c7..94c8134d6 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -205,5 +205,20 @@ mod interrupt { executor.spawner().make_send() } + + /// Get a SendSpawner for this executor + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on this + /// executor. + /// + /// This MUST only be called on an executor that has already been spawned. + /// The function will panic otherwise. + pub fn spawner(&'static self) -> crate::SendSpawner { + if !self.started.load(Ordering::Acquire) { + panic!("InterruptExecutor::spawner() called on uninitialized executor."); + } + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.spawner().make_send() + } } } From f6c1108bdf444ba9aef1113ec39c7fc814dab85e Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Fri, 16 Jun 2023 14:56:28 +0200 Subject: [PATCH 1366/1575] fix extended can id --- embassy-stm32/src/can/bxcan.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 08ba783ff..1ab4dc1fc 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -226,7 +226,10 @@ impl<'d, T: Instance> Can<'d, T> { let id = if rir.ide() == RirIde::STANDARD { Id::from(StandardId::new_unchecked(rir.stid())) } else { - Id::from(ExtendedId::new_unchecked(rir.exid())) + let stid = (rir.stid() & 0x7FF) as u32; + let exid = rir.exid() & 0x3FFFF; + let id = (stid << 18) | (exid as u32); + Id::from(ExtendedId::new_unchecked(id)) }; let data_len = fifo.rdtr().read().dlc() as usize; let mut data: [u8; 8] = [0; 8]; From e1161dfc80e583719bf486d112fa84c948b71fc1 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 16 Jun 2023 20:15:03 -0500 Subject: [PATCH 1367/1575] stm32/wpan: improve linked list ergonomics --- embassy-stm32-wpan/src/ble.rs | 27 ++++++++---------- embassy-stm32-wpan/src/cmd.rs | 19 +++++++++++++ embassy-stm32-wpan/src/mm.rs | 11 ++----- embassy-stm32-wpan/src/rc.rs | 8 ++---- embassy-stm32-wpan/src/shci.rs | 4 +-- embassy-stm32-wpan/src/sys.rs | 30 +++++++------------- embassy-stm32-wpan/src/unsafe_linked_list.rs | 28 +++++++++++------- tests/stm32/src/bin/tl_mbox.rs | 5 +++- 8 files changed, 70 insertions(+), 62 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 46a2f41c4..d8af861ae 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -1,6 +1,8 @@ +use core::mem::MaybeUninit; + use embassy_stm32::ipcc::Ipcc; -use crate::cmd::{CmdPacket, CmdSerial}; +use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::EvtBox; use crate::tables::BleTable; @@ -14,6 +16,11 @@ pub struct Ble; impl Ble { pub(super) fn enable() { unsafe { + // Ensure reproducible behavior + BLE_CMD_BUFFER + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable { @@ -29,11 +36,8 @@ impl Ble { pub(super) fn evt_handler() { unsafe { - while !LinkedListNode::is_empty(EVT_QUEUE.as_mut_ptr()) { - let node_ptr = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()); - - let event = node_ptr.cast(); - let event = EvtBox::new(event); + while let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { + let event = EvtBox::new(node_ptr.cast()); EVT_CHANNEL.try_send(event).unwrap(); } @@ -48,18 +52,11 @@ impl Ble { // TODO: ACL data ack to the user } - pub fn send_cmd(buf: &[u8]) { + pub fn send_cmd(opcode: u16, payload: &[u8]) { debug!("writing ble cmd"); unsafe { - let pcmd_buffer: *mut CmdPacket = BLE_CMD_BUFFER.as_mut_ptr(); - let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmdserial; - let pcmd_serial_buf: *mut u8 = pcmd_serial.cast(); - - core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); - - let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; - cmd_packet.cmdserial.ty = TlPacketType::BleCmd as u8; + CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); } Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index a023201ba..581e5019b 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -1,3 +1,6 @@ +use core::ptr; + +use crate::consts::TlPacketType; use crate::evt::{EvtPacket, EvtSerial}; use crate::{PacketHeader, TL_EVT_HEADER_SIZE}; @@ -42,6 +45,22 @@ pub struct CmdPacket { } impl CmdPacket { + pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) { + let p_cmd_serial = &mut (*cmd_buf).cmdserial as *mut _ as *mut CmdSerialStub; + let p_payload = &mut (*cmd_buf).cmdserial.cmd.payload as *mut _; + + ptr::write_volatile( + p_cmd_serial, + CmdSerialStub { + ty: packet_type as u8, + cmd_code: cmd_code, + payload_len: payload.len() as u8, + }, + ); + + ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); + } + /// Writes an underlying CmdPacket into the provided buffer. /// Returns a number of bytes that were written. /// Returns an error if event kind is unknown or if provided buffer size is not enough. diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index 06063b89a..1ea6edeff 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -7,7 +7,7 @@ use crate::tables::MemManagerTable; use crate::unsafe_linked_list::LinkedListNode; use crate::{ channels, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, - TL_MEM_MANAGER_TABLE, TL_REF_TABLE, + TL_MEM_MANAGER_TABLE, }; pub(super) struct MemoryManager; @@ -51,13 +51,8 @@ impl MemoryManager { /// gives free event buffers back to CPU2 from local buffer queue pub fn send_free_buf() { unsafe { - while !LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { - let node_ptr = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); - - LinkedListNode::insert_tail( - (*(*TL_REF_TABLE.as_ptr()).mem_manager_table).pevt_free_buffer_queue, - node_ptr, - ); + while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { + LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); } } } diff --git a/embassy-stm32-wpan/src/rc.rs b/embassy-stm32-wpan/src/rc.rs index a217aa224..e67350f79 100644 --- a/embassy-stm32-wpan/src/rc.rs +++ b/embassy-stm32-wpan/src/rc.rs @@ -1,6 +1,6 @@ use crate::ble::Ble; use crate::consts::TlPacketType; -use crate::{shci, TlMbox, STATE}; +use crate::{TlMbox, STATE}; pub struct RadioCoprocessor<'d> { mbox: TlMbox<'d>, @@ -15,12 +15,12 @@ impl<'d> RadioCoprocessor<'d> { } } - pub fn write(&self, buf: &[u8]) { + pub fn write(&self, opcode: u16, buf: &[u8]) { let cmd_code = buf[0]; let cmd = TlPacketType::try_from(cmd_code).unwrap(); match &cmd { - TlPacketType::BleCmd => Ble::send_cmd(buf), + TlPacketType::BleCmd => Ble::send_cmd(opcode, buf), _ => todo!(), } } @@ -30,8 +30,6 @@ impl<'d> RadioCoprocessor<'d> { STATE.wait().await; while let Some(evt) = self.mbox.dequeue_event() { - let event = evt.evt(); - evt.write(&mut self.rx_buf).unwrap(); } diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index 6e58a7156..cdf027d5e 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -1,8 +1,6 @@ use core::{mem, slice}; -use super::cmd::CmdPacket; -use super::consts::TlPacketType; -use super::{sys, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE}; +use super::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; pub const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index 76f65cbd8..fb247f276 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,9 +1,9 @@ -use core::ptr; +use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; use embassy_stm32::ipcc::Ipcc; -use crate::cmd::{CmdPacket, CmdSerial, CmdSerialStub}; +use crate::cmd::{CmdPacket, CmdSerial}; use crate::consts::TlPacketType; use crate::evt::{CcEvt, EvtBox, EvtSerial}; use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT}; @@ -16,6 +16,11 @@ pub struct Sys; impl Sys { pub fn enable() { unsafe { + // Ensure reproducible behavior + SYS_CMD_BUF + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { @@ -50,11 +55,8 @@ impl Sys { pub fn evt_handler() { unsafe { - while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - let node_ptr = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); - - let event = node_ptr.cast(); - let event = EvtBox::new(event); + while let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { + let event = EvtBox::new(node_ptr.cast()); EVT_CHANNEL.try_send(event).unwrap(); } @@ -71,19 +73,7 @@ impl Sys { pub fn send_cmd(opcode: u16, payload: &[u8]) { unsafe { - let p_cmd_serial = &mut (*SYS_CMD_BUF.as_mut_ptr()).cmdserial as *mut _ as *mut CmdSerialStub; - let p_payload = &mut (*SYS_CMD_BUF.as_mut_ptr()).cmdserial.cmd.payload as *mut _; - - ptr::write_volatile( - p_cmd_serial, - CmdSerialStub { - ty: TlPacketType::SysCmd as u8, - cmd_code: opcode, - payload_len: payload.len() as u8, - }, - ); - - ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); + CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload); } compiler_fence(Ordering::SeqCst); diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs index a312178b3..5b210aac4 100644 --- a/embassy-stm32-wpan/src/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -139,28 +139,36 @@ impl LinkedListNode { } /// Remove `list_head` and return a pointer to the `node`. - pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> *mut LinkedListNode { + pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> Option<*mut LinkedListNode> { interrupt::free(|_| { let list_head = ptr::read_volatile(p_list_head); - // Allowed because a removed node is not seen by another core - let p_node = list_head.next; - Self::remove_node(p_node); + if list_head.next == p_list_head { + None + } else { + // Allowed because a removed node is not seen by another core + let p_node = list_head.next; + Self::remove_node(p_node); - p_node + Some(p_node) + } }) } /// Remove `list_tail` and return a pointer to the `node`. - pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> *mut LinkedListNode { + pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> Option<*mut LinkedListNode> { interrupt::free(|_| { let list_tail = ptr::read_volatile(p_list_tail); - // Allowed because a removed node is not seen by another core - let p_node = list_tail.prev; - Self::remove_node(p_node); + if list_tail.prev == p_list_tail { + None + } else { + // Allowed because a removed node is not seen by another core + let p_node = list_tail.prev; + Self::remove_node(p_node); - p_node + Some(p_node) + } }) } diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 5e7d11ef5..5a2309263 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -10,6 +10,7 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::Config; +use embassy_stm32_wpan::ble::Ble; use embassy_stm32_wpan::rc::RadioCoprocessor; use embassy_stm32_wpan::sys::Sys; use embassy_stm32_wpan::TlMbox; @@ -59,7 +60,9 @@ async fn main(_spawner: Spawner) { Sys::shci_ble_init(Default::default()); - rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); + // rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); + Ble::send_cmd(0x0c, &[]); + let response = rc.read().await; info!("ble reset rsp {}", response); From f5d084552d9f44d24f020269cc605de0fb4d1041 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 17 Jun 2023 11:48:21 +0200 Subject: [PATCH 1368/1575] implement mwe of a DMA write() method for DAC --- embassy-stm32/build.rs | 2 + embassy-stm32/src/{dac.rs => dac/mod.rs} | 76 +++++++++++++++++++++--- 2 files changed, 71 insertions(+), 7 deletions(-) rename embassy-stm32/src/{dac.rs => dac/mod.rs} (76%) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9e597f187..e2bd01d7b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -699,6 +699,8 @@ fn main() { // SDMMCv1 uses the same channel for both directions, so just implement for RX (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), + (("dac", "CH1"), quote!(crate::dac::Dma)), + (("dac", "CH2"), quote!(crate::dac::Dma)), ] .into(); diff --git a/embassy-stm32/src/dac.rs b/embassy-stm32/src/dac/mod.rs similarity index 76% rename from embassy-stm32/src/dac.rs rename to embassy-stm32/src/dac/mod.rs index 60e856c78..348d8bccd 100644 --- a/embassy-stm32/src/dac.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -2,6 +2,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; +use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -97,39 +98,58 @@ pub enum Value { Bit12(u16, Alignment), } -pub struct Dac<'d, T: Instance> { +pub struct Dac<'d, T: Instance, Tx> { channels: u8, + txdma: PeripheralRef<'d, Tx>, _peri: PeripheralRef<'d, T>, } -impl<'d, T: Instance> Dac<'d, T> { - pub fn new_1ch(peri: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd) -> Self { +impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { + pub fn new_1ch( + peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + _ch1: impl Peripheral

> + 'd, + ) -> Self { into_ref!(peri); - Self::new_inner(peri, 1) + Self::new_inner(peri, 1, txdma) } pub fn new_2ch( peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd, _ch2: impl Peripheral

> + 'd, ) -> Self { into_ref!(peri); - Self::new_inner(peri, 2) + Self::new_inner(peri, 2, txdma) } - fn new_inner(peri: PeripheralRef<'d, T>, channels: u8) -> Self { + fn new_inner(peri: PeripheralRef<'d, T>, channels: u8, txdma: impl Peripheral

+ 'd) -> Self { + into_ref!(txdma); T::enable(); T::reset(); unsafe { + T::regs().mcr().modify(|reg| { + for ch in 0..channels { + reg.set_mode(ch as usize, 0); + reg.set_mode(ch as usize, 0); + } + }); + T::regs().cr().modify(|reg| { for ch in 0..channels { reg.set_en(ch as usize, true); + reg.set_ten(ch as usize, true); } }); } - Self { channels, _peri: peri } + Self { + channels, + txdma, + _peri: peri, + } } /// Check the channel is configured @@ -215,6 +235,47 @@ impl<'d, T: Instance> Dac<'d, T> { } Ok(()) } + + /// TODO: Allow an array of Value instead of only u16, right-aligned + pub async fn write(&mut self, data: &[u16]) -> Result<(), Error> + where + Tx: Dma, + { + // TODO: Make this a parameter or get it from the struct or so... + const CHANNEL: usize = 0; + + //debug!("Starting DAC"); + unsafe { + T::regs().cr().modify(|w| { + w.set_en(CHANNEL, true); + w.set_dmaen(CHANNEL, true); + }); + } + + let tx_request = self.txdma.request(); + + // Use the 12 bit right-aligned register for now. TODO: distinguish values + let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16; + + let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; + + //debug!("Awaiting tx_f"); + + tx_f.await; + + // finish dma + unsafe { + // TODO: Do we need to check any status registers here? + + T::regs().cr().modify(|w| { + // Disable the dac peripheral + //w.set_en(CHANNEL, false); + // Disable the DMA. TODO: Is this necessary? + //w.set_dmaen(CHANNEL, false); + }); + } + Ok(()) + } } pub(crate) mod sealed { @@ -224,6 +285,7 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + RccPeripheral + 'static {} +dma_trait!(Dma, Instance); pub trait DacPin: crate::gpio::Pin + 'static {} From 78a2ca8a0e5af3fe2c76a6cd025b74ea4322f6cf Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 17 Jun 2023 11:51:57 +0200 Subject: [PATCH 1369/1575] remove unnecessary use, disable DAC and DMA after transfer --- embassy-stm32/src/dac/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 348d8bccd..7b81ec1f2 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -2,7 +2,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; -use crate::dma::{slice_ptr_parts, word, Transfer}; +use crate::dma::Transfer; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -269,9 +269,9 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { T::regs().cr().modify(|w| { // Disable the dac peripheral - //w.set_en(CHANNEL, false); + w.set_en(CHANNEL, false); // Disable the DMA. TODO: Is this necessary? - //w.set_dmaen(CHANNEL, false); + w.set_dmaen(CHANNEL, false); }); } Ok(()) From 9e8de5f596ffa9036c2343ccc1e69f471a4770eb Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 08:11:55 -0500 Subject: [PATCH 1370/1575] fut: add poll_once --- embassy-futures/src/block_on.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/embassy-futures/src/block_on.rs b/embassy-futures/src/block_on.rs index da90351ec..77695216c 100644 --- a/embassy-futures/src/block_on.rs +++ b/embassy-futures/src/block_on.rs @@ -31,3 +31,15 @@ pub fn block_on(mut fut: F) -> F::Output { } } } + +/// Poll a future once. +pub fn poll_once(mut fut: F) -> Poll { + // safety: we don't move the future after this line. + let mut fut = unsafe { Pin::new_unchecked(&mut fut) }; + + let raw_waker = RawWaker::new(ptr::null(), &VTABLE); + let waker = unsafe { Waker::from_raw(raw_waker) }; + let mut cx = Context::from_waker(&waker); + + fut.as_mut().poll(&mut cx) +} From 391f0b5d09dba018dd984a706dc8071f87e06dec Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 08:37:00 -0500 Subject: [PATCH 1371/1575] revert reset changes --- embassy-stm32-wpan/src/ble.rs | 5 ----- embassy-stm32-wpan/src/sys.rs | 5 ----- 2 files changed, 10 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index d8af861ae..3a6cc6f07 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -16,11 +16,6 @@ pub struct Ble; impl Ble { pub(super) fn enable() { unsafe { - // Ensure reproducible behavior - BLE_CMD_BUFFER - .as_mut_ptr() - .write_volatile(MaybeUninit::zeroed().assume_init()); - LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable { diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index fb247f276..36b4a144d 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -16,11 +16,6 @@ pub struct Sys; impl Sys { pub fn enable() { unsafe { - // Ensure reproducible behavior - SYS_CMD_BUF - .as_mut_ptr() - .write_volatile(MaybeUninit::zeroed().assume_init()); - LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { From 6ef060ca1719216751811336ebc4d41247daaeff Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 10:44:36 -0500 Subject: [PATCH 1372/1575] disable mm --- embassy-stm32-wpan/src/mm.rs | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index 1ea6edeff..4ccae06f4 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -31,36 +31,36 @@ impl MemoryManager { } pub fn evt_drop(evt: *mut EvtPacket) { - unsafe { - let list_node = evt.cast(); - - LinkedListNode::insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); - - let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); - - // postpone event buffer freeing to IPCC interrupt handler - if channel_is_busy { - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); - } else { - Self::send_free_buf(); - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); - } - } + // unsafe { + // let list_node = evt.cast(); + // + // LinkedListNode::insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); + // + // let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + // + // // postpone event buffer freeing to IPCC interrupt handler + // if channel_is_busy { + // Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); + // } else { + // Self::send_free_buf(); + // Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + // } + // } } /// gives free event buffers back to CPU2 from local buffer queue pub fn send_free_buf() { - unsafe { - while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { - LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); - } - } + // unsafe { + // while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { + // LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); + // } + // } } /// free buffer channel interrupt handler pub fn free_buf_handler() { - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); - Self::send_free_buf(); - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + // Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); + // Self::send_free_buf(); + // Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); } } From 4c9b7befaa498e491f34140e1497dcb98da62713 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 10:50:06 -0500 Subject: [PATCH 1373/1575] stm32/ipcc: add clear debug --- embassy-stm32/src/ipcc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 8bb0774b8..28f51baa5 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -111,6 +111,7 @@ impl Ipcc { pub fn c1_clear_flag_channel(channel: IpccChannel) { let regs = IPCC::regs(); + trace!("ipcc: ch {}: clear rx", channel as u8); unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } } From b0a2f0c4fec5358063e6323bf9f9ee001341c473 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 11:02:31 -0500 Subject: [PATCH 1374/1575] stm32/wpan: debug remove node --- embassy-stm32-wpan/src/unsafe_linked_list.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs index 5b210aac4..a2d2840f0 100644 --- a/embassy-stm32-wpan/src/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -117,7 +117,10 @@ impl LinkedListNode { /// Remove `node` from the linked list pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) { interrupt::free(|_| { + trace!("remove node: {:x}", p_node); let node = ptr::read_volatile(p_node); + trace!("remove node: prev/next {:x}/{:x}", node.prev, node.next); + if node.next != node.prev { let mut node_next = ptr::read_volatile(node.next); let mut node_prev = ptr::read_volatile(node.prev); From 6b5d55eb29aa55795cfdf98593feba2f53b85b9c Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 12:00:33 -0500 Subject: [PATCH 1375/1575] stm32/wpan: convert to new ipcc --- embassy-stm32-wpan/src/ble.rs | 54 ++-- embassy-stm32-wpan/src/evt.rs | 4 +- embassy-stm32-wpan/src/lib.rs | 60 +--- embassy-stm32-wpan/src/mm.rs | 66 +++-- embassy-stm32-wpan/src/rc.rs | 43 --- embassy-stm32-wpan/src/sys.rs | 91 +++--- embassy-stm32-wpan/src/unsafe_linked_list.rs | 8 +- embassy-stm32/src/ipcc.rs | 296 +++++++++++++------ tests/stm32/src/bin/tl_mbox.rs | 43 ++- 9 files changed, 330 insertions(+), 335 deletions(-) delete mode 100644 embassy-stm32-wpan/src/rc.rs diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 3a6cc6f07..b7d152631 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -1,10 +1,11 @@ use core::mem::MaybeUninit; +use core::ptr; use embassy_stm32::ipcc::Ipcc; -use crate::cmd::CmdPacket; +use crate::cmd::{Cmd, CmdPacket, CmdSerial}; use crate::consts::TlPacketType; -use crate::evt::EvtBox; +use crate::evt::{EvtBox, EvtPacket}; use crate::tables::BleTable; use crate::unsafe_linked_list::LinkedListNode; use crate::{ @@ -25,45 +26,26 @@ impl Ble { phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(), }); } - - Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); } - - pub(super) fn evt_handler() { - unsafe { - while let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { - let event = EvtBox::new(node_ptr.cast()); - - EVT_CHANNEL.try_send(event).unwrap(); + /// `HW_IPCC_BLE_EvtNot` + pub async fn read() -> EvtBox { + Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { + if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { + Some(EvtBox::new(node_ptr.cast())) + } else { + None } - } - - Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); + }) + .await } - pub(super) fn acl_data_handler() { - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, false); - - // TODO: ACL data ack to the user - } - - pub fn send_cmd(opcode: u16, payload: &[u8]) { - debug!("writing ble cmd"); - - unsafe { + /// `TL_BLE_SendCmd` + pub async fn write(opcode: u16, payload: &[u8]) { + Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe { CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); - } - - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); + }) + .await; } - #[allow(dead_code)] // Not used currently but reserved - pub(super) fn ble_send_acl_data() { - let cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer }; - - cmd_packet.acl_data_serial.ty = TlPacketType::AclData as u8; - - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL); - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, true); - } + // TODO: acl commands } diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index b53fe506e..82f73a6f8 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -171,6 +171,8 @@ impl EvtBox { impl Drop for EvtBox { fn drop(&mut self) { - mm::MemoryManager::evt_drop(self.ptr); + trace!("evt box drop packet"); + + unsafe { mm::MemoryManager::drop_event_packet(self.ptr) }; } } diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 2852d6270..78516263c 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -7,11 +7,10 @@ use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; use cmd::CmdPacket; -use embassy_futures::block_on; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; use embassy_stm32::interrupt::typelevel::Interrupt; -use embassy_stm32::ipcc::{Config, Ipcc}; +use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -29,50 +28,11 @@ pub mod cmd; pub mod consts; pub mod evt; pub mod mm; -pub mod rc; pub mod shci; pub mod sys; pub mod tables; pub mod unsafe_linked_list; -/// Interrupt handler. -pub struct ReceiveInterruptHandler {} - -impl interrupt::typelevel::Handler for ReceiveInterruptHandler { - unsafe fn on_interrupt() { - if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) { - debug!("RX SYS evt"); - sys::Sys::evt_handler(); - } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) { - debug!("RX BLE evt"); - ble::Ble::evt_handler(); - } - - STATE.signal(()); - } -} - -pub struct TransmitInterruptHandler {} - -impl interrupt::typelevel::Handler for TransmitInterruptHandler { - unsafe fn on_interrupt() { - if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) { - debug!("TX SYS cmd rsp"); - let cc = sys::Sys::cmd_evt_handler(); - - LAST_CC_EVT.signal(cc); - } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) { - debug!("TX MM release"); - mm::MemoryManager::free_buf_handler(); - } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL) { - debug!("TX HCI acl"); - ble::Ble::acl_data_handler(); - } - - STATE.signal(()); - } -} - #[link_section = "TL_REF_TABLE"] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); @@ -289,22 +249,4 @@ impl<'d> TlMbox<'d> { None } } - - /// picks single [`EvtBox`] from internal event queue. - /// - /// Internal event queu is populated in IPCC_RX_IRQ handler - pub fn dequeue_event(&mut self) -> Option { - EVT_CHANNEL.try_recv().ok() - } - - /// retrieves last Command Complete event and removes it from mailbox - pub fn pop_last_cc_evt(&mut self) -> Option { - if LAST_CC_EVT.signaled() { - let cc = block_on(LAST_CC_EVT.wait()); - LAST_CC_EVT.reset(); - Some(cc) - } else { - None - } - } } diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index 4ccae06f4..d0551d690 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -1,6 +1,11 @@ //! Memory manager routines +use core::future::poll_fn; +use core::task::Poll; + +use cortex_m::interrupt; use embassy_stm32::ipcc::Ipcc; +use embassy_sync::waitqueue::AtomicWaker; use crate::evt::EvtPacket; use crate::tables::MemManagerTable; @@ -10,7 +15,9 @@ use crate::{ TL_MEM_MANAGER_TABLE, }; -pub(super) struct MemoryManager; +static MM_WAKER: AtomicWaker = AtomicWaker::new(); + +pub struct MemoryManager; impl MemoryManager { pub fn enable() { @@ -30,37 +37,36 @@ impl MemoryManager { } } - pub fn evt_drop(evt: *mut EvtPacket) { - // unsafe { - // let list_node = evt.cast(); - // - // LinkedListNode::insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); - // - // let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); - // - // // postpone event buffer freeing to IPCC interrupt handler - // if channel_is_busy { - // Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); - // } else { - // Self::send_free_buf(); - // Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); - // } - // } + /// SAFETY: passing a pointer to something other than an event packet is UB + pub unsafe fn drop_event_packet(evt: *mut EvtPacket) { + interrupt::free(|_| unsafe { + LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _); + }); + + MM_WAKER.wake(); } - /// gives free event buffers back to CPU2 from local buffer queue - pub fn send_free_buf() { - // unsafe { - // while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { - // LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); - // } - // } - } + pub async fn run_queue() { + loop { + poll_fn(|cx| unsafe { + MM_WAKER.register(cx.waker()); + if LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; - /// free buffer channel interrupt handler - pub fn free_buf_handler() { - // Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); - // Self::send_free_buf(); - // Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); + Ipcc::send(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, || { + interrupt::free(|_| unsafe { + // CS required while moving nodes + while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { + LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); + } + }) + }) + .await; + } } } diff --git a/embassy-stm32-wpan/src/rc.rs b/embassy-stm32-wpan/src/rc.rs deleted file mode 100644 index e67350f79..000000000 --- a/embassy-stm32-wpan/src/rc.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::ble::Ble; -use crate::consts::TlPacketType; -use crate::{TlMbox, STATE}; - -pub struct RadioCoprocessor<'d> { - mbox: TlMbox<'d>, - rx_buf: [u8; 500], -} - -impl<'d> RadioCoprocessor<'d> { - pub fn new(mbox: TlMbox<'d>) -> Self { - Self { - mbox, - rx_buf: [0u8; 500], - } - } - - pub fn write(&self, opcode: u16, buf: &[u8]) { - let cmd_code = buf[0]; - let cmd = TlPacketType::try_from(cmd_code).unwrap(); - - match &cmd { - TlPacketType::BleCmd => Ble::send_cmd(opcode, buf), - _ => todo!(), - } - } - - pub async fn read(&mut self) -> &[u8] { - loop { - STATE.wait().await; - - while let Some(evt) = self.mbox.dequeue_event() { - evt.write(&mut self.rx_buf).unwrap(); - } - - if self.mbox.pop_last_cc_evt().is_some() { - continue; - } - - return &self.rx_buf; - } - } -} diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index 36b4a144d..f10327b9d 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,19 +1,18 @@ use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::{mem, ptr}; -use embassy_stm32::ipcc::Ipcc; - -use crate::cmd::{CmdPacket, CmdSerial}; +use crate::cmd::{CmdPacket, CmdSerialStub}; use crate::consts::TlPacketType; -use crate::evt::{CcEvt, EvtBox, EvtSerial}; -use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT}; +use crate::evt::{CcEvt, EvtBox, EvtPacket, EvtSerial}; +use crate::shci::{ShciBleInitCmdPacket, ShciBleInitCmdParam, ShciHeader, SCHI_OPCODE_BLE_INIT}; use crate::tables::SysTable; use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; +use crate::{channels, mm, Ipcc, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; pub struct Sys; impl Sys { + /// TL_Sys_Init pub fn enable() { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); @@ -21,59 +20,47 @@ impl Sys { TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), - }) - } - - Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); - } - - pub fn cmd_evt_handler() -> CcEvt { - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); - - // ST's command response data structure is really convoluted. - // - // for command response events on SYS channel, the header is missing - // and one should: - // 1. interpret the content of CMD_BUFFER as CmdPacket - // 2. Access CmdPacket's cmdserial field and interpret its content as EvtSerial - // 3. Access EvtSerial's evt field (as Evt) and interpret its payload as CcEvt - // 4. CcEvt type is the actual SHCI response - // 5. profit - unsafe { - let pcmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer; - let cmd_serial: *const CmdSerial = &(*pcmd).cmdserial; - let evt_serial: *const EvtSerial = cmd_serial.cast(); - let cc: *const CcEvt = (*evt_serial).evt.payload.as_ptr().cast(); - *cc + }); } } - pub fn evt_handler() { - unsafe { - while let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - let event = EvtBox::new(node_ptr.cast()); + // pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> SchiCommandStatus { + // let command_event = self + // .write_and_get_response(TlPacketType::SysCmd, ShciOpcode::BleInit as u16, param.payload()) + // .await; + // + // let payload = command_event.payload[0]; + // // info!("payload: {:x}", payload); + // + // payload.try_into().unwrap() + // } - EVT_CHANNEL.try_send(event).unwrap(); - } - } - - Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); - } - - pub fn shci_ble_init(param: ShciBleInitCmdParam) { - debug!("sending SHCI"); - - Self::send_cmd(SCHI_OPCODE_BLE_INIT, param.payload()); - } - - pub fn send_cmd(opcode: u16, payload: &[u8]) { + pub fn write(opcode: u16, payload: &[u8]) { unsafe { CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload); } + } - compiler_fence(Ordering::SeqCst); + pub async fn shci_c2_ble_init(param: ShciBleInitCmdParam) { + debug!("sending SHCI"); - Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); - Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); + Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || { + Self::write(SCHI_OPCODE_BLE_INIT, param.payload()); + }) + .await; + + Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; + } + + /// `HW_IPCC_SYS_EvtNot` + pub async fn read() -> EvtBox { + Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { + if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { + Some(EvtBox::new(node_ptr.cast())) + } else { + None + } + }) + .await } } diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs index a2d2840f0..d8bc29763 100644 --- a/embassy-stm32-wpan/src/unsafe_linked_list.rs +++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs @@ -117,9 +117,11 @@ impl LinkedListNode { /// Remove `node` from the linked list pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) { interrupt::free(|_| { - trace!("remove node: {:x}", p_node); - let node = ptr::read_volatile(p_node); - trace!("remove node: prev/next {:x}/{:x}", node.prev, node.next); + // trace!("remove node: {:x}", p_node); + // apparently linked list nodes are not always aligned. + // if more hardfaults occur, more of these may need to be converted to unaligned. + let node = ptr::read_unaligned(p_node); + // trace!("remove node: prev/next {:x}/{:x}", node.prev, node.next); if node.next != node.prev { let mut node_next = ptr::read_volatile(node.next); diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 28f51baa5..609c4d2c3 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,7 +1,77 @@ +use core::future::poll_fn; +use core::task::Poll; + +use atomic_polyfill::{compiler_fence, Ordering}; + use self::sealed::Instance; +use crate::interrupt; +use crate::interrupt::typelevel::Interrupt; use crate::peripherals::IPCC; use crate::rcc::sealed::RccPeripheral; +/// Interrupt handler. +pub struct ReceiveInterruptHandler {} + +impl interrupt::typelevel::Handler for ReceiveInterruptHandler { + unsafe fn on_interrupt() { + let regs = IPCC::regs(); + + let channels = [ + IpccChannel::Channel1, + IpccChannel::Channel2, + IpccChannel::Channel3, + IpccChannel::Channel4, + IpccChannel::Channel5, + IpccChannel::Channel6, + ]; + + // Status register gives channel occupied status. For rx, use cpu1. + let sr = unsafe { regs.cpu(1).sr().read() }; + regs.cpu(0).mr().modify(|w| { + for channel in channels { + if sr.chf(channel as usize) { + // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt + w.set_chom(channel as usize, true); + + // There shouldn't be a race because the channel is masked only if the interrupt has fired + IPCC::state().rx_waker_for(channel).wake(); + } + } + }) + } +} + +pub struct TransmitInterruptHandler {} + +impl interrupt::typelevel::Handler for TransmitInterruptHandler { + unsafe fn on_interrupt() { + let regs = IPCC::regs(); + + let channels = [ + IpccChannel::Channel1, + IpccChannel::Channel2, + IpccChannel::Channel3, + IpccChannel::Channel4, + IpccChannel::Channel5, + IpccChannel::Channel6, + ]; + + // Status register gives channel occupied status. For tx, use cpu0. + let sr = unsafe { regs.cpu(0).sr().read() }; + regs.cpu(0).mr().modify(|w| { + for channel in channels { + if !sr.chf(channel as usize) { + // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt + w.set_chfm(channel as usize, true); + + // There shouldn't be a race because the channel is masked only if the interrupt has fired + IPCC::state().tx_waker_for(channel).wake(); + } + } + }); + } +} + #[non_exhaustive] #[derive(Clone, Copy, Default)] pub struct Config { @@ -20,13 +90,6 @@ pub enum IpccChannel { Channel6 = 5, } -pub mod sealed { - pub trait Instance: crate::rcc::RccPeripheral { - fn regs() -> crate::pac::ipcc::Ipcc; - fn set_cpu2(enabled: bool); - } -} - pub struct Ipcc; impl Ipcc { @@ -45,115 +108,99 @@ impl Ipcc { w.set_txfie(true); }) } + + // enable interrupts + crate::interrupt::typelevel::IPCC_C1_RX::unpend(); + crate::interrupt::typelevel::IPCC_C1_TX::unpend(); + + unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; + unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; } - pub fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) { + /// Send data to an IPCC channel. The closure is called to write the data when appropriate. + pub async fn send(channel: IpccChannel, f: impl FnOnce()) { let regs = IPCC::regs(); - // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } - } + Self::flush(channel).await; + compiler_fence(Ordering::SeqCst); - pub fn c1_get_rx_channel(channel: IpccChannel) -> bool { - let regs = IPCC::regs(); + f(); - // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(0).mr().read().chom(channel as usize) } - } - - #[allow(dead_code)] - pub fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) { - let regs = IPCC::regs(); - - // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } - } - - #[allow(dead_code)] - pub fn c2_get_rx_channel(channel: IpccChannel) -> bool { - let regs = IPCC::regs(); - - // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(1).mr().read().chom(channel as usize) } - } - - pub fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) { - let regs = IPCC::regs(); - - // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } - } - - pub fn c1_get_tx_channel(channel: IpccChannel) -> bool { - let regs = IPCC::regs(); - - // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) } - } - - #[allow(dead_code)] - pub fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) { - let regs = IPCC::regs(); - - // If bit is set to 1 then interrupt is disabled - unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } - } - - #[allow(dead_code)] - pub fn c2_get_tx_channel(channel: IpccChannel) -> bool { - let regs = IPCC::regs(); - - // If bit is set to 1 then interrupt is disabled - unsafe { !regs.cpu(1).mr().read().chfm(channel as usize) } - } - - /// clears IPCC receive channel status for CPU1 - pub fn c1_clear_flag_channel(channel: IpccChannel) { - let regs = IPCC::regs(); - - trace!("ipcc: ch {}: clear rx", channel as u8); - unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } - } - - #[allow(dead_code)] - /// clears IPCC receive channel status for CPU2 - pub fn c2_clear_flag_channel(channel: IpccChannel) { - let regs = IPCC::regs(); - - unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) } - } - - pub fn c1_set_flag_channel(channel: IpccChannel) { - let regs = IPCC::regs(); + compiler_fence(Ordering::SeqCst); + trace!("ipcc: ch {}: send data", channel as u8); unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } } - #[allow(dead_code)] - pub fn c2_set_flag_channel(channel: IpccChannel) { + /// Wait for the tx channel to become clear + pub async fn flush(channel: IpccChannel) { let regs = IPCC::regs(); - unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) } + // This is a race, but is nice for debugging + if unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) { + trace!("ipcc: ch {}: wait for tx free", channel as u8); + } + + poll_fn(|cx| { + IPCC::state().tx_waker_for(channel).register(cx.waker()); + // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt + unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)) } + + compiler_fence(Ordering::SeqCst); + + if !unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) { + // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt + unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) } + + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; } - pub fn c1_is_active_flag(channel: IpccChannel) -> bool { + /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. + pub async fn receive(channel: IpccChannel, mut f: impl FnMut() -> Option) -> R { let regs = IPCC::regs(); - unsafe { regs.cpu(0).sr().read().chf(channel as usize) } - } + loop { + // This is a race, but is nice for debugging + if !unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) { + trace!("ipcc: ch {}: wait for rx occupied", channel as u8); + } - pub fn c2_is_active_flag(channel: IpccChannel) -> bool { - let regs = IPCC::regs(); + poll_fn(|cx| { + IPCC::state().rx_waker_for(channel).register(cx.waker()); + // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt + unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)) } - unsafe { regs.cpu(1).sr().read().chf(channel as usize) } - } + compiler_fence(Ordering::SeqCst); - pub fn is_tx_pending(channel: IpccChannel) -> bool { - !Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel) - } + if unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) { + // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt + unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) } - pub fn is_rx_pending(channel: IpccChannel) -> bool { - Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel) + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + trace!("ipcc: ch {}: read data", channel as u8); + compiler_fence(Ordering::SeqCst); + + match f() { + Some(ret) => return ret, + None => {} + } + + trace!("ipcc: ch {}: clear rx", channel as u8); + compiler_fence(Ordering::SeqCst); + // If the channel is clear and the read function returns none, fetch more data + unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } + } } } @@ -165,9 +212,66 @@ impl sealed::Instance for crate::peripherals::IPCC { fn set_cpu2(enabled: bool) { unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) } } + + fn state() -> &'static self::sealed::State { + static STATE: self::sealed::State = self::sealed::State::new(); + &STATE + } +} + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + use super::*; + + pub struct State { + rx_wakers: [AtomicWaker; 6], + tx_wakers: [AtomicWaker; 6], + } + + impl State { + pub const fn new() -> Self { + const WAKER: AtomicWaker = AtomicWaker::new(); + + Self { + rx_wakers: [WAKER; 6], + tx_wakers: [WAKER; 6], + } + } + + pub fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { + match channel { + IpccChannel::Channel1 => &self.rx_wakers[0], + IpccChannel::Channel2 => &self.rx_wakers[1], + IpccChannel::Channel3 => &self.rx_wakers[2], + IpccChannel::Channel4 => &self.rx_wakers[3], + IpccChannel::Channel5 => &self.rx_wakers[4], + IpccChannel::Channel6 => &self.rx_wakers[5], + } + } + + pub fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { + match channel { + IpccChannel::Channel1 => &self.tx_wakers[0], + IpccChannel::Channel2 => &self.tx_wakers[1], + IpccChannel::Channel3 => &self.tx_wakers[2], + IpccChannel::Channel4 => &self.tx_wakers[3], + IpccChannel::Channel5 => &self.tx_wakers[4], + IpccChannel::Channel6 => &self.tx_wakers[5], + } + } + } + + pub trait Instance: crate::rcc::RccPeripheral { + fn regs() -> crate::pac::ipcc::Ipcc; + fn set_cpu2(enabled: bool); + fn state() -> &'static State; + } } unsafe fn _configure_pwr() { + // TODO: move this to RCC + let pwr = crate::pac::PWR; let rcc = crate::pac::RCC; diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 5a2309263..bb38204b6 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -8,27 +8,41 @@ mod common; use common::*; use embassy_executor::Spawner; +use embassy_futures::poll_once; use embassy_stm32::bind_interrupts; -use embassy_stm32::ipcc::Config; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32_wpan::ble::Ble; -use embassy_stm32_wpan::rc::RadioCoprocessor; use embassy_stm32_wpan::sys::Sys; -use embassy_stm32_wpan::TlMbox; +use embassy_stm32_wpan::{mm, TlMbox}; use embassy_time::{Duration, Timer}; bind_interrupts!(struct Irqs{ - IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; - IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; }); +#[embassy_executor::task] +async fn run_mm_queue() { + mm::MemoryManager::run_queue().await; +} + #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); + spawner.spawn(run_mm_queue()).unwrap(); + let config = Config::default(); let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mut rx_buf = [0u8; 500]; + let ready_event = Sys::read().await; + let _ = poll_once(Sys::read()); // clear rx not + ready_event.write(&mut rx_buf).unwrap(); + + info!("coprocessor ready {}", rx_buf); + loop { let wireless_fw_info = mbox.wireless_fw_info(); match wireless_fw_info { @@ -53,19 +67,18 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_millis(50)).await; } - let mut rc = RadioCoprocessor::new(mbox); + Sys::shci_c2_ble_init(Default::default()).await; - let response = rc.read().await; - info!("coprocessor ready {}", response); + info!("starting ble..."); + Ble::write(0x0c, &[]).await; - Sys::shci_ble_init(Default::default()); + info!("waiting for ble..."); + let ble_event = Ble::read().await; + ble_event.write(&mut rx_buf).unwrap(); - // rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); - Ble::send_cmd(0x0c, &[]); - - let response = rc.read().await; - info!("ble reset rsp {}", response); + info!("ble event: {}", rx_buf); + // Timer::after(Duration::from_secs(3)).await; info!("Test OK"); cortex_m::asm::bkpt(); } From faa58b907418ce63fef4d3283765067c9e5e3d0b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 12:06:00 -0500 Subject: [PATCH 1376/1575] rustfmt --- embassy-stm32-wpan/src/ble.rs | 11 +++-------- embassy-stm32-wpan/src/lib.rs | 4 ++++ embassy-stm32-wpan/src/sys.rs | 11 ++++------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index b7d152631..86a6331fb 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -1,16 +1,11 @@ -use core::mem::MaybeUninit; -use core::ptr; - use embassy_stm32::ipcc::Ipcc; -use crate::cmd::{Cmd, CmdPacket, CmdSerial}; +use crate::cmd::CmdPacket; use crate::consts::TlPacketType; -use crate::evt::{EvtBox, EvtPacket}; +use crate::evt::EvtBox; use crate::tables::BleTable; use crate::unsafe_linked_list::LinkedListNode; -use crate::{ - channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_CHANNEL, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_REF_TABLE, -}; +use crate::{channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; pub struct Ble; diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 78516263c..2f694acf8 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -127,10 +127,14 @@ static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); // fuck these "magic" numbers from ST ---v---v static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); +// TODO: remove these items + +#[allow(dead_code)] /// current event that is produced during IPCC IRQ handler execution /// on SYS channel static EVT_CHANNEL: Channel = Channel::new(); +#[allow(dead_code)] /// last received Command Complete event static LAST_CC_EVT: Signal = Signal::new(); diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index f10327b9d..78c357b3f 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,13 +1,10 @@ -use core::mem::MaybeUninit; -use core::{mem, ptr}; - -use crate::cmd::{CmdPacket, CmdSerialStub}; +use crate::cmd::CmdPacket; use crate::consts::TlPacketType; -use crate::evt::{CcEvt, EvtBox, EvtPacket, EvtSerial}; -use crate::shci::{ShciBleInitCmdPacket, ShciBleInitCmdParam, ShciHeader, SCHI_OPCODE_BLE_INIT}; +use crate::evt::EvtBox; +use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT}; use crate::tables::SysTable; use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, mm, Ipcc, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; +use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; pub struct Sys; From 4d2d7d7684dd23a9a9bd9e5467068e3c389db3cb Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 12:13:51 -0500 Subject: [PATCH 1377/1575] stm32/wpan: fix examples --- examples/stm32wb/src/bin/tl_mbox.rs | 5 +++-- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 26 +++++++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index ae36a7e79..a28a8e21b 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -6,13 +6,14 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::Config; +use embassy_stm32::ipcc::{ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ - IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; - IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; }); #[embassy_executor::main] diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 3132ab3e4..91a0f9c0a 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -5,14 +5,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; -use embassy_stm32::ipcc::Config; -use embassy_stm32_wpan::rc::RadioCoprocessor; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::ble::Ble; +use embassy_stm32_wpan::sys::Sys; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ - IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; - IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; }); #[embassy_executor::main] @@ -45,16 +46,19 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let _ = TlMbox::init(p.IPCC, Irqs, config); - let mut rc = RadioCoprocessor::new(mbox); + let mut rx_buf = [0u8; 500]; + Sys::shci_c2_ble_init(Default::default()).await; - let response = rc.read().await; - info!("coprocessor ready {}", response); + info!("starting ble..."); + Ble::write(0x0c, &[]).await; - rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); - let response = rc.read().await; - info!("ble reset rsp {}", response); + info!("waiting for ble..."); + let ble_event = Ble::read().await; + ble_event.write(&mut rx_buf).unwrap(); + + info!("ble event: {}", rx_buf); info!("Test OK"); cortex_m::asm::bkpt(); From 041a4a4208dae563ab22f9986391509f4b90f740 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 12:15:00 -0500 Subject: [PATCH 1378/1575] rustfmt --- examples/stm32wb/src/bin/tl_mbox.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index a28a8e21b..b010fdff1 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -5,8 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; -use embassy_stm32::ipcc::Config; -use embassy_stm32::ipcc::{ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; From c7b0df569b02bee80de808a5b0ad69df3d32d84c Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 14:38:36 -0500 Subject: [PATCH 1379/1575] stm32/wpan: modify evtbox to use slice view --- embassy-stm32-wpan/src/cmd.rs | 54 ++++----- embassy-stm32-wpan/src/evt.rs | 135 ++++++++++++---------- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 4 +- tests/stm32/src/bin/tl_mbox.rs | 14 ++- 4 files changed, 112 insertions(+), 95 deletions(-) diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index 581e5019b..edca82390 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -1,8 +1,7 @@ use core::ptr; use crate::consts::TlPacketType; -use crate::evt::{EvtPacket, EvtSerial}; -use crate::{PacketHeader, TL_EVT_HEADER_SIZE}; +use crate::PacketHeader; #[derive(Copy, Clone)] #[repr(C, packed)] @@ -60,31 +59,6 @@ impl CmdPacket { ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); } - - /// Writes an underlying CmdPacket into the provided buffer. - /// Returns a number of bytes that were written. - /// Returns an error if event kind is unknown or if provided buffer size is not enough. - #[allow(clippy::result_unit_err)] - pub fn write(&self, buf: &mut [u8]) -> Result { - unsafe { - let cmd_ptr: *const CmdPacket = self; - let self_as_evt_ptr: *const EvtPacket = cmd_ptr.cast(); - let evt_serial: *const EvtSerial = &(*self_as_evt_ptr).evt_serial; - - let acl_data: *const AclDataPacket = cmd_ptr.cast(); - let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; - let acl_serial_buf: *const u8 = acl_serial.cast(); - - let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; - if len > buf.len() { - return Err(()); - } - - core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); - - Ok(len) - } - } } #[derive(Copy, Clone)] @@ -96,9 +70,35 @@ pub struct AclDataSerial { pub acl_data: [u8; 1], } +#[derive(Copy, Clone)] +#[repr(C, packed)] +pub struct AclDataSerialStub { + pub ty: u8, + pub handle: u16, + pub length: u16, +} + #[derive(Copy, Clone)] #[repr(C, packed)] pub struct AclDataPacket { pub header: PacketHeader, pub acl_data_serial: AclDataSerial, } + +impl AclDataPacket { + pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) { + let p_cmd_serial = &mut (*cmd_buf).acl_data_serial as *mut _ as *mut AclDataSerialStub; + let p_payload = &mut (*cmd_buf).acl_data_serial.acl_data as *mut _; + + ptr::write_volatile( + p_cmd_serial, + AclDataSerialStub { + ty: packet_type as u8, + handle: handle, + length: payload.len() as u16, + }, + ); + + ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); + } +} diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index 82f73a6f8..3a9d03576 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -1,8 +1,6 @@ -use core::mem::MaybeUninit; +use core::{ptr, slice}; -use super::cmd::{AclDataPacket, AclDataSerial}; -use super::consts::TlPacketType; -use super::{PacketHeader, TL_EVT_HEADER_SIZE}; +use super::PacketHeader; use crate::mm; /** @@ -63,6 +61,12 @@ pub struct EvtSerial { pub evt: Evt, } +#[derive(Copy, Clone, Default)] +pub struct EvtStub { + pub kind: u8, + pub evt_code: u8, +} + /// This format shall be used for all events (asynchronous and command response) reported /// by the CPU2 except for the command response of a system command where the header is not there /// and the format to be used shall be `EvtSerial`. @@ -101,72 +105,85 @@ impl EvtBox { Self { ptr } } - /// copies event data from inner pointer and returns an event structure - pub fn evt(&self) -> EvtPacket { - let mut evt = MaybeUninit::uninit(); + /// Returns information about the event + pub fn stub(&self) -> EvtStub { unsafe { - self.ptr.copy_to(evt.as_mut_ptr(), 1); - evt.assume_init() + let p_evt_stub = &(*self.ptr).evt_serial as *const _ as *const EvtStub; + + ptr::read_volatile(p_evt_stub) } } - /// writes an underlying [`EvtPacket`] into the provided buffer. - /// Returns the number of bytes that were written. - /// Returns an error if event kind is unknown or if provided buffer size is not enough. - #[allow(clippy::result_unit_err)] - pub fn write(&self, buf: &mut [u8]) -> Result { + pub fn payload<'a>(&self) -> &'a [u8] { unsafe { - let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + let p_payload_len = &(*self.ptr).evt_serial.evt.payload_len as *const u8; + let p_payload = &(*self.ptr).evt_serial.evt.payload as *const u8; - let evt_data: *const EvtPacket = self.ptr.cast(); - let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; - let evt_serial_buf: *const u8 = evt_serial.cast(); + let payload_len = ptr::read_volatile(p_payload_len); - let acl_data: *const AclDataPacket = self.ptr.cast(); - let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; - let acl_serial_buf: *const u8 = acl_serial.cast(); - - if let TlPacketType::AclData = evt_kind { - let len = (*acl_serial).length as usize + 5; - if len > buf.len() { - return Err(()); - } - - core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len); - - Ok(len) - } else { - let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; - if len > buf.len() { - return Err(()); - } - - core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); - - Ok(len) - } + slice::from_raw_parts(p_payload, payload_len as usize) } } - /// returns the size of a buffer required to hold this event - #[allow(clippy::result_unit_err)] - pub fn size(&self) -> Result { - unsafe { - let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + // TODO: bring back acl - let evt_data: *const EvtPacket = self.ptr.cast(); - let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; - - let acl_data: *const AclDataPacket = self.ptr.cast(); - let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; - - if let TlPacketType::AclData = evt_kind { - Ok((*acl_serial).length as usize + 5) - } else { - Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE) - } - } - } + // /// writes an underlying [`EvtPacket`] into the provided buffer. + // /// Returns the number of bytes that were written. + // /// Returns an error if event kind is unknown or if provided buffer size is not enough. + // #[allow(clippy::result_unit_err)] + // pub fn write(&self, buf: &mut [u8]) -> Result { + // unsafe { + // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + // + // let evt_data: *const EvtPacket = self.ptr.cast(); + // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; + // let evt_serial_buf: *const u8 = evt_serial.cast(); + // + // let acl_data: *const AclDataPacket = self.ptr.cast(); + // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; + // let acl_serial_buf: *const u8 = acl_serial.cast(); + // + // if let TlPacketType::AclData = evt_kind { + // let len = (*acl_serial).length as usize + 5; + // if len > buf.len() { + // return Err(()); + // } + // + // core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len); + // + // Ok(len) + // } else { + // let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; + // if len > buf.len() { + // return Err(()); + // } + // + // core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); + // + // Ok(len) + // } + // } + // } + // + // /// returns the size of a buffer required to hold this event + // #[allow(clippy::result_unit_err)] + // pub fn size(&self) -> Result { + // unsafe { + // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; + // + // let evt_data: *const EvtPacket = self.ptr.cast(); + // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; + // + // let acl_data: *const AclDataPacket = self.ptr.cast(); + // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; + // + // if let TlPacketType::AclData = evt_kind { + // Ok((*acl_serial).length as usize + 5) + // } else { + // Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE) + // } + // } + // } } impl Drop for EvtBox { diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 91a0f9c0a..84a4f78e4 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -48,7 +48,6 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let _ = TlMbox::init(p.IPCC, Irqs, config); - let mut rx_buf = [0u8; 500]; Sys::shci_c2_ble_init(Default::default()).await; info!("starting ble..."); @@ -56,9 +55,8 @@ async fn main(_spawner: Spawner) { info!("waiting for ble..."); let ble_event = Ble::read().await; - ble_event.write(&mut rx_buf).unwrap(); - info!("ble event: {}", rx_buf); + info!("ble event: {}", ble_event.payload()); info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index bb38204b6..259889e35 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -6,6 +6,8 @@ #[path = "../common.rs"] mod common; +use core::mem; + use common::*; use embassy_executor::Spawner; use embassy_futures::poll_once; @@ -36,12 +38,13 @@ async fn main(spawner: Spawner) { let config = Config::default(); let mbox = TlMbox::init(p.IPCC, Irqs, config); - let mut rx_buf = [0u8; 500]; let ready_event = Sys::read().await; let _ = poll_once(Sys::read()); // clear rx not - ready_event.write(&mut rx_buf).unwrap(); - info!("coprocessor ready {}", rx_buf); + info!("coprocessor ready {}", ready_event.payload()); + + // test memory manager + mem::drop(ready_event); loop { let wireless_fw_info = mbox.wireless_fw_info(); @@ -74,11 +77,10 @@ async fn main(spawner: Spawner) { info!("waiting for ble..."); let ble_event = Ble::read().await; - ble_event.write(&mut rx_buf).unwrap(); - info!("ble event: {}", rx_buf); + info!("ble event: {}", ble_event.payload()); - // Timer::after(Duration::from_secs(3)).await; + Timer::after(Duration::from_millis(150)).await; info!("Test OK"); cortex_m::asm::bkpt(); } From 6d7d617f40399c5ffaa588f9e5dfefd34164c531 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 15:18:15 -0500 Subject: [PATCH 1380/1575] stm32/wpan: add ble acl_write --- embassy-stm32-wpan/src/ble.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 86a6331fb..a39198d06 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -42,5 +42,16 @@ impl Ble { .await; } - // TODO: acl commands + /// `TL_BLE_SendAclData` + pub async fn acl_write(handle: u16, payload: &[u8]) { + Ipcc::send(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, || unsafe { + CmdPacket::write_into( + HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, + TlPacketType::AclData, + handle, + payload, + ); + }) + .await; + } } From 443550b353c733aee7d122468a82df432014d8fe Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 17 Jun 2023 15:37:34 -0500 Subject: [PATCH 1381/1575] stm32/wpan: use new ownership model --- embassy-stm32-wpan/src/ble.rs | 16 ++++--- embassy-stm32-wpan/src/lib.rs | 31 +++++++------- embassy-stm32-wpan/src/mm.rs | 13 ++++-- embassy-stm32-wpan/src/sys.rs | 43 +++++++++++-------- examples/stm32wb/src/bin/tl_mbox.rs | 2 +- examples/stm32wb/src/bin/tl_mbox_tx_rx.rs | 13 +++--- tests/stm32/src/bin/tl_mbox.rs | 51 +++++++++-------------- 7 files changed, 88 insertions(+), 81 deletions(-) diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index a39198d06..f0bd6f48c 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use embassy_stm32::ipcc::Ipcc; use crate::cmd::CmdPacket; @@ -7,10 +9,12 @@ use crate::tables::BleTable; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; -pub struct Ble; +pub struct Ble { + phantom: PhantomData, +} impl Ble { - pub(super) fn enable() { + pub(crate) fn new() -> Self { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); @@ -21,9 +25,11 @@ impl Ble { phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(), }); } + + Self { phantom: PhantomData } } /// `HW_IPCC_BLE_EvtNot` - pub async fn read() -> EvtBox { + pub async fn read(&self) -> EvtBox { Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { Some(EvtBox::new(node_ptr.cast())) @@ -35,7 +41,7 @@ impl Ble { } /// `TL_BLE_SendCmd` - pub async fn write(opcode: u16, payload: &[u8]) { + pub async fn write(&self, opcode: u16, payload: &[u8]) { Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe { CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); }) @@ -43,7 +49,7 @@ impl Ble { } /// `TL_BLE_SendAclData` - pub async fn acl_write(handle: u16, payload: &[u8]) { + pub async fn acl_write(&self, handle: u16, payload: &[u8]) { Ipcc::send(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, || unsafe { CmdPacket::write_into( HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 2f694acf8..833db0df3 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -6,6 +6,7 @@ pub mod fmt; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; +use ble::Ble; use cmd::CmdPacket; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; @@ -16,9 +17,10 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::signal::Signal; use evt::{CcEvt, EvtBox}; +use mm::MemoryManager; +use sys::Sys; use tables::{ BleTable, DeviceInfoTable, Mac802_15_4Table, MemManagerTable, RefTable, SysTable, ThreadTable, TracesTable, - WirelessFwInfoTable, }; use unsafe_linked_list::LinkedListNode; @@ -142,6 +144,10 @@ static STATE: Signal = Signal::new(); pub struct TlMbox<'d> { _ipcc: PeripheralRef<'d, IPCC>, + + pub sys_subsystem: Sys, + pub mm_subsystem: MemoryManager, + pub ble_subsystem: Ble, } impl<'d> TlMbox<'d> { @@ -226,9 +232,9 @@ impl<'d> TlMbox<'d> { Ipcc::enable(config); - sys::Sys::enable(); - ble::Ble::enable(); - mm::MemoryManager::enable(); + let sys = sys::Sys::new(); + let ble = ble::Ble::new(); + let mm = mm::MemoryManager::new(); // enable interrupts interrupt::typelevel::IPCC_C1_RX::unpend(); @@ -239,18 +245,11 @@ impl<'d> TlMbox<'d> { STATE.reset(); - Self { _ipcc: ipcc } - } - - /// Returns CPU2 wireless firmware information (if present). - pub fn wireless_fw_info(&self) -> Option { - let info = unsafe { &(*(*TL_REF_TABLE.as_ptr()).device_info_table).wireless_fw_info_table }; - - // Zero version indicates that CPU2 wasn't active and didn't fill the information table - if info.version != 0 { - Some(*info) - } else { - None + Self { + _ipcc: ipcc, + sys_subsystem: sys, + ble_subsystem: ble, + mm_subsystem: mm, } } } diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index d0551d690..21f42409a 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -1,6 +1,7 @@ //! Memory manager routines use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; use cortex_m::interrupt; @@ -17,10 +18,12 @@ use crate::{ static MM_WAKER: AtomicWaker = AtomicWaker::new(); -pub struct MemoryManager; +pub struct MemoryManager { + phantom: PhantomData, +} impl MemoryManager { - pub fn enable() { + pub(crate) fn new() -> Self { unsafe { LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); @@ -35,10 +38,12 @@ impl MemoryManager { tracespoolsize: 0, }); } + + Self { phantom: PhantomData } } /// SAFETY: passing a pointer to something other than an event packet is UB - pub unsafe fn drop_event_packet(evt: *mut EvtPacket) { + pub(crate) unsafe fn drop_event_packet(evt: *mut EvtPacket) { interrupt::free(|_| unsafe { LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _); }); @@ -46,7 +51,7 @@ impl MemoryManager { MM_WAKER.wake(); } - pub async fn run_queue() { + pub async fn run_queue(&self) { loop { poll_fn(|cx| unsafe { MM_WAKER.register(cx.waker()); diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index 78c357b3f..a185cd4f1 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,16 +1,20 @@ +use core::marker::PhantomData; + use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::EvtBox; use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT}; -use crate::tables::SysTable; +use crate::tables::{SysTable, WirelessFwInfoTable}; use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; +use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; -pub struct Sys; +pub struct Sys { + phantom: PhantomData, +} impl Sys { /// TL_Sys_Init - pub fn enable() { + pub(crate) fn new() -> Self { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); @@ -19,30 +23,33 @@ impl Sys { sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), }); } + + Self { phantom: PhantomData } } - // pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> SchiCommandStatus { - // let command_event = self - // .write_and_get_response(TlPacketType::SysCmd, ShciOpcode::BleInit as u16, param.payload()) - // .await; - // - // let payload = command_event.payload[0]; - // // info!("payload: {:x}", payload); - // - // payload.try_into().unwrap() - // } + /// Returns CPU2 wireless firmware information (if present). + pub fn wireless_fw_info(&self) -> Option { + let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table }; - pub fn write(opcode: u16, payload: &[u8]) { + // Zero version indicates that CPU2 wasn't active and didn't fill the information table + if info.version != 0 { + Some(info) + } else { + None + } + } + + pub fn write(&self, opcode: u16, payload: &[u8]) { unsafe { CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload); } } - pub async fn shci_c2_ble_init(param: ShciBleInitCmdParam) { + pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) { debug!("sending SHCI"); Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || { - Self::write(SCHI_OPCODE_BLE_INIT, param.payload()); + self.write(SCHI_OPCODE_BLE_INIT, param.payload()); }) .await; @@ -50,7 +57,7 @@ impl Sys { } /// `HW_IPCC_SYS_EvtNot` - pub async fn read() -> EvtBox { + pub async fn read(&self) -> EvtBox { Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { Some(EvtBox::new(node_ptr.cast())) diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index b010fdff1..9fc4b8aac 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) { let mbox = TlMbox::init(p.IPCC, Irqs, config); loop { - let wireless_fw_info = mbox.wireless_fw_info(); + let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info(); match wireless_fw_info { None => info!("not yet initialized"), Some(fw_info) => { diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs index 84a4f78e4..439bd01ac 100644 --- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs +++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs @@ -6,8 +6,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::ble::Ble; -use embassy_stm32_wpan::sys::Sys; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -46,15 +44,18 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let _ = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config); - Sys::shci_c2_ble_init(Default::default()).await; + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("starting ble..."); - Ble::write(0x0c, &[]).await; + mbox.ble_subsystem.write(0x0c, &[]).await; info!("waiting for ble..."); - let ble_event = Ble::read().await; + let ble_event = mbox.ble_subsystem.read().await; info!("ble event: {}", ble_event.payload()); diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 259889e35..f6641ae31 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -13,8 +13,6 @@ use embassy_executor::Spawner; use embassy_futures::poll_once; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::ble::Ble; -use embassy_stm32_wpan::sys::Sys; use embassy_stm32_wpan::{mm, TlMbox}; use embassy_time::{Duration, Timer}; @@ -24,8 +22,8 @@ bind_interrupts!(struct Irqs{ }); #[embassy_executor::task] -async fn run_mm_queue() { - mm::MemoryManager::run_queue().await; +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; } #[embassy_executor::main] @@ -33,50 +31,41 @@ async fn main(spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); - spawner.spawn(run_mm_queue()).unwrap(); - let config = Config::default(); let mbox = TlMbox::init(p.IPCC, Irqs, config); - let ready_event = Sys::read().await; - let _ = poll_once(Sys::read()); // clear rx not + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let ready_event = mbox.sys_subsystem.read().await; + let _ = poll_once(mbox.sys_subsystem.read()); // clear rx not info!("coprocessor ready {}", ready_event.payload()); // test memory manager mem::drop(ready_event); - loop { - let wireless_fw_info = mbox.wireless_fw_info(); - match wireless_fw_info { - None => {} - Some(fw_info) => { - let version_major = fw_info.version_major(); - let version_minor = fw_info.version_minor(); - let subversion = fw_info.subversion(); + let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap(); + let version_major = fw_info.version_major(); + let version_minor = fw_info.version_minor(); + let subversion = fw_info.subversion(); - let sram2a_size = fw_info.sram2a_size(); - let sram2b_size = fw_info.sram2b_size(); + let sram2a_size = fw_info.sram2a_size(); + let sram2b_size = fw_info.sram2b_size(); - info!( - "version {}.{}.{} - SRAM2a {} - SRAM2b {}", - version_major, version_minor, subversion, sram2a_size, sram2b_size - ); + info!( + "version {}.{}.{} - SRAM2a {} - SRAM2b {}", + version_major, version_minor, subversion, sram2a_size, sram2b_size + ); - break; - } - } + Timer::after(Duration::from_millis(50)).await; - Timer::after(Duration::from_millis(50)).await; - } - - Sys::shci_c2_ble_init(Default::default()).await; + mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("starting ble..."); - Ble::write(0x0c, &[]).await; + mbox.ble_subsystem.write(0x0c, &[]).await; info!("waiting for ble..."); - let ble_event = Ble::read().await; + let ble_event = mbox.ble_subsystem.read().await; info!("ble event: {}", ble_event.payload()); From b4f96e192cd8c86f437e1d155388a860dcd3e1fd Mon Sep 17 00:00:00 2001 From: Peter Gibson Date: Sun, 18 Jun 2023 08:45:58 +1000 Subject: [PATCH 1382/1575] Don't read data register to clear flags on usart v3 ^& v4 --- embassy-stm32/src/usart/buffered.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 530760bd1..086196a2c 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -21,8 +21,10 @@ impl interrupt::typelevel::Handler for Interrupt // RX unsafe { let sr = sr(r).read(); - // Reading DR clears the rxne, error and idle interrupt flags on v1. - let dr = if sr.ore() || sr.idle() || sr.rxne() { + // On v1 & v2, reading DR clears the rxne, error and idle interrupt + // flags. Keep this close to the SR read to reduce the chance of a + // flag being set in-between. + let dr = if sr.rxne() || cfg!(any(usart_v1, usart_v2)) && (sr.ore() || sr.idle()) { Some(rdr(r).read_volatile()) } else { None From 7177e7ea1aae2eb6973816a158e714b4fcd5b9a6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 18 Jun 2023 08:37:26 -0500 Subject: [PATCH 1383/1575] stm32/wpan: cleanup and expand shci --- embassy-stm32-wpan/src/evt.rs | 65 +----- embassy-stm32-wpan/src/shci.rs | 348 ++++++++++++++++++++++++++++++--- embassy-stm32-wpan/src/sys.rs | 35 ++-- tests/stm32/src/bin/tl_mbox.rs | 8 +- 4 files changed, 349 insertions(+), 107 deletions(-) diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index 3a9d03576..a7b99e540 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -62,6 +62,7 @@ pub struct EvtSerial { } #[derive(Copy, Clone, Default)] +#[repr(C, packed)] pub struct EvtStub { pub kind: u8, pub evt_code: u8, @@ -114,7 +115,7 @@ impl EvtBox { } } - pub fn payload<'a>(&self) -> &'a [u8] { + pub fn payload<'a>(&'a self) -> &'a [u8] { unsafe { let p_payload_len = &(*self.ptr).evt_serial.evt.payload_len as *const u8; let p_payload = &(*self.ptr).evt_serial.evt.payload as *const u8; @@ -124,72 +125,10 @@ impl EvtBox { slice::from_raw_parts(p_payload, payload_len as usize) } } - - // TODO: bring back acl - - // /// writes an underlying [`EvtPacket`] into the provided buffer. - // /// Returns the number of bytes that were written. - // /// Returns an error if event kind is unknown or if provided buffer size is not enough. - // #[allow(clippy::result_unit_err)] - // pub fn write(&self, buf: &mut [u8]) -> Result { - // unsafe { - // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; - // - // let evt_data: *const EvtPacket = self.ptr.cast(); - // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; - // let evt_serial_buf: *const u8 = evt_serial.cast(); - // - // let acl_data: *const AclDataPacket = self.ptr.cast(); - // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; - // let acl_serial_buf: *const u8 = acl_serial.cast(); - // - // if let TlPacketType::AclData = evt_kind { - // let len = (*acl_serial).length as usize + 5; - // if len > buf.len() { - // return Err(()); - // } - // - // core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len); - // - // Ok(len) - // } else { - // let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; - // if len > buf.len() { - // return Err(()); - // } - // - // core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); - // - // Ok(len) - // } - // } - // } - // - // /// returns the size of a buffer required to hold this event - // #[allow(clippy::result_unit_err)] - // pub fn size(&self) -> Result { - // unsafe { - // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; - // - // let evt_data: *const EvtPacket = self.ptr.cast(); - // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial; - // - // let acl_data: *const AclDataPacket = self.ptr.cast(); - // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial; - // - // if let TlPacketType::AclData = evt_kind { - // Ok((*acl_serial).length as usize + 5) - // } else { - // Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE) - // } - // } - // } } impl Drop for EvtBox { fn drop(&mut self) { - trace!("evt box drop packet"); - unsafe { mm::MemoryManager::drop_event_packet(self.ptr) }; } } diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index cdf027d5e..f4e9ba84c 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -2,38 +2,345 @@ use core::{mem, slice}; use super::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; -pub const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; +const SHCI_OGF: u16 = 0x3F; -#[derive(Debug, Clone, Copy)] +const fn opcode(ogf: u16, ocf: u16) -> isize { + ((ogf << 10) + ocf) as isize +} + +#[allow(dead_code)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SchiCommandStatus { + ShciSuccess = 0x00, + ShciUnknownCmd = 0x01, + ShciMemoryCapacityExceededErrCode = 0x07, + ShciErrUnsupportedFeature = 0x11, + ShciErrInvalidHciCmdParams = 0x12, + ShciErrInvalidParams = 0x42, /* only used for release < v1.13.0 */ + ShciErrInvalidParamsV2 = 0x92, /* available for release >= v1.13.0 */ + ShciFusCmdNotSupported = 0xFF, +} + +impl TryFrom for SchiCommandStatus { + type Error = (); + + fn try_from(v: u8) -> Result { + match v { + x if x == SchiCommandStatus::ShciSuccess as u8 => Ok(SchiCommandStatus::ShciSuccess), + x if x == SchiCommandStatus::ShciUnknownCmd as u8 => Ok(SchiCommandStatus::ShciUnknownCmd), + x if x == SchiCommandStatus::ShciMemoryCapacityExceededErrCode as u8 => { + Ok(SchiCommandStatus::ShciMemoryCapacityExceededErrCode) + } + x if x == SchiCommandStatus::ShciErrUnsupportedFeature as u8 => { + Ok(SchiCommandStatus::ShciErrUnsupportedFeature) + } + x if x == SchiCommandStatus::ShciErrInvalidHciCmdParams as u8 => { + Ok(SchiCommandStatus::ShciErrInvalidHciCmdParams) + } + x if x == SchiCommandStatus::ShciErrInvalidParams as u8 => Ok(SchiCommandStatus::ShciErrInvalidParams), /* only used for release < v1.13.0 */ + x if x == SchiCommandStatus::ShciErrInvalidParamsV2 as u8 => Ok(SchiCommandStatus::ShciErrInvalidParamsV2), /* available for release >= v1.13.0 */ + x if x == SchiCommandStatus::ShciFusCmdNotSupported as u8 => Ok(SchiCommandStatus::ShciFusCmdNotSupported), + _ => Err(()), + } + } +} + +#[allow(dead_code)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ShciOpcode { + // 0x50 reserved + // 0x51 reserved + FusGetState = opcode(SHCI_OGF, 0x52), + // 0x53 reserved + FusFirmwareUpgrade = opcode(SHCI_OGF, 0x54), + FusFirmwareDelete = opcode(SHCI_OGF, 0x55), + FusUpdateAuthKey = opcode(SHCI_OGF, 0x56), + FusLockAuthKey = opcode(SHCI_OGF, 0x57), + FusStoreUserKey = opcode(SHCI_OGF, 0x58), + FusLoadUserKey = opcode(SHCI_OGF, 0x59), + FusStartWirelessStack = opcode(SHCI_OGF, 0x5a), + // 0x5b reserved + // 0x5c reserved + FusLockUserKey = opcode(SHCI_OGF, 0x5d), + FusUnloadUserKey = opcode(SHCI_OGF, 0x5e), + FusActivateAntirollback = opcode(SHCI_OGF, 0x5f), + // 0x60 reserved + // 0x61 reserved + // 0x62 reserved + // 0x63 reserved + // 0x64 reserved + // 0x65 reserved + BleInit = opcode(SHCI_OGF, 0x66), + ThreadInit = opcode(SHCI_OGF, 0x67), + DebugInit = opcode(SHCI_OGF, 0x68), + FlashEraseActivity = opcode(SHCI_OGF, 0x69), + ConcurrentSetMode = opcode(SHCI_OGF, 0x6a), + FlashStoreData = opcode(SHCI_OGF, 0x6b), + FlashEraseData = opcode(SHCI_OGF, 0x6c), + RadioAllowLowPower = opcode(SHCI_OGF, 0x6d), + Mac802_15_4Init = opcode(SHCI_OGF, 0x6e), + ReInit = opcode(SHCI_OGF, 0x6f), + ZigbeeInit = opcode(SHCI_OGF, 0x70), + LldTestsInit = opcode(SHCI_OGF, 0x71), + ExtraConfig = opcode(SHCI_OGF, 0x72), + SetFlashActivityControl = opcode(SHCI_OGF, 0x73), + BleLldInit = opcode(SHCI_OGF, 0x74), + Config = opcode(SHCI_OGF, 0x75), + ConcurrentGetNextBleEvtTime = opcode(SHCI_OGF, 0x76), + ConcurrentEnableNext802_15_4EvtNotification = opcode(SHCI_OGF, 0x77), + Mac802_15_4DeInit = opcode(SHCI_OGF, 0x78), +} + +pub const SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE: u8 = 1 << 0; +pub const SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 1; +pub const SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 2; +pub const SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE: u8 = 1 << 3; +pub const SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE: u8 = 1 << 4; +pub const SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE: u8 = 1 << 5; +pub const SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE: u8 = 1 << 6; + +#[derive(Clone, Copy)] +#[repr(C, packed)] +pub struct ShciConfigParam { + pub payload_cmd_size: u8, + pub config: u8, + pub event_mask: u8, + pub spare: u8, + pub ble_nvm_ram_address: u32, + pub thread_nvm_ram_address: u32, + pub revision_id: u16, + pub device_id: u16, +} + +impl ShciConfigParam { + pub fn payload<'a>(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::()) } + } +} + +impl Default for ShciConfigParam { + fn default() -> Self { + Self { + payload_cmd_size: (mem::size_of::() - 1) as u8, + config: 0, + event_mask: SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE + + SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE + + SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE + + SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE + + SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE + + SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE + + SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE, + spare: 0, + ble_nvm_ram_address: 0, + thread_nvm_ram_address: 0, + revision_id: 0, + device_id: 0, + } + } +} + +#[derive(Clone, Copy)] #[repr(C, packed)] pub struct ShciBleInitCmdParam { - /// NOT USED CURRENTLY + /// NOT USED - shall be set to 0 pub p_ble_buffer_address: u32, - - /// Size of the Buffer allocated in pBleBufferAddress + /// NOT USED - shall be set to 0 pub ble_buffer_size: u32, - + /// Maximum number of attribute records related to all the required characteristics (excluding the services) + /// that can be stored in the GATT database, for the specific BLE user application. + /// For each characteristic, the number of attribute records goes from two to five depending on the characteristic properties: + /// - minimum of two (one for declaration and one for the value) + /// - add one more record for each additional property: notify or indicate, broadcast, extended property. + /// The total calculated value must be increased by 9, due to the records related to the standard attribute profile and + /// GAP service characteristics, and automatically added when initializing GATT and GAP layers + /// - Min value: + 9 + /// - Max value: depending on the GATT database defined by user application pub num_attr_record: u16, + /// Defines the maximum number of services that can be stored in the GATT database. Note that the GAP and GATT services + /// are automatically added at initialization so this parameter must be the number of user services increased by two. + /// - Min value: + 2 + /// - Max value: depending GATT database defined by user application pub num_attr_serv: u16, + /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure ) + /// + /// Size of the storage area for the attribute values. + /// Each characteristic contributes to the attrValueArrSize value as follows: + /// - Characteristic value length plus: + /// + 5 bytes if characteristic UUID is 16 bits + /// + 19 bytes if characteristic UUID is 128 bits + /// + 2 bytes if characteristic has a server configuration descriptor + /// + 2 bytes * NumOfLinks if the characteristic has a client configuration descriptor + /// + 2 bytes if the characteristic has extended properties + /// Each descriptor contributes to the attrValueArrSize value as follows: + /// - Descriptor length pub attr_value_arr_size: u16, + /// Maximum number of BLE links supported + /// - Min value: 1 + /// - Max value: 8 pub num_of_links: u8, + /// Disable/enable the extended packet length BLE 5.0 feature + /// - Disable: 0 + /// - Enable: 1 pub extended_packet_length_enable: u8, - pub pr_write_list_size: u8, - pub mb_lock_count: u8, - + /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure ) + /// + /// Maximum number of supported "prepare write request" + /// - Min value: given by the macro DEFAULT_PREP_WRITE_LIST_SIZE + /// - Max value: a value higher than the minimum required can be specified, but it is not recommended + pub prepare_write_list_size: u8, + /// NOTE: This parameter is overwritten by the CPU2 with an hardcoded optimal value when the parameter "Options" is set to "LL_only" + /// ( see Options description in that structure ) + /// + /// Number of allocated memory blocks for the BLE stack + /// - Min value: given by the macro MBLOCKS_CALC + /// - Max value: a higher value can improve data throughput performance, but uses more memory + pub block_count: u8, + /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure ) + /// + /// Maximum ATT MTU size supported + /// - Min value: 23 + /// - Max value: 512 pub att_mtu: u16, + /// The sleep clock accuracy (ppm value) that used in BLE connected slave mode to calculate the window widening + /// (in combination with the sleep clock accuracy sent by master in CONNECT_REQ PDU), + /// refer to BLE 5.0 specifications - Vol 6 - Part B - chap 4.5.7 and 4.2.2 + /// - Min value: 0 + /// - Max value: 500 (worst possible admitted by specification) pub slave_sca: u16, + /// The sleep clock accuracy handled in master mode. It is used to determine the connection and advertising events timing. + /// It is transmitted to the slave in CONNEC_REQ PDU used by the slave to calculate the window widening, + /// see SlaveSca and Bluetooth Core Specification v5.0 Vol 6 - Part B - chap 4.5.7 and 4.2.2 + /// Possible values: + /// - 251 ppm to 500 ppm: 0 + /// - 151 ppm to 250 ppm: 1 + /// - 101 ppm to 150 ppm: 2 + /// - 76 ppm to 100 ppm: 3 + /// - 51 ppm to 75 ppm: 4 + /// - 31 ppm to 50 ppm: 5 + /// - 21 ppm to 30 ppm: 6 + /// - 0 ppm to 20 ppm: 7 pub master_sca: u8, + /// Some information for Low speed clock mapped in bits field + /// - bit 0: + /// - 1: Calibration for the RF system wakeup clock source + /// - 0: No calibration for the RF system wakeup clock source + /// - bit 1: + /// - 1: STM32W5M Module device + /// - 0: Other devices as STM32WBxx SOC, STM32WB1M module + /// - bit 2: + /// - 1: HSE/1024 Clock config + /// - 0: LSE Clock config pub ls_source: u8, + /// This parameter determines the maximum duration of a slave connection event. When this duration is reached the slave closes + /// the current connections event (whatever is the CE_length parameter specified by the master in HCI_CREATE_CONNECTION HCI command), + /// expressed in units of 625/256 µs (~2.44 µs) + /// - Min value: 0 (if 0 is specified, the master and slave perform only a single TX-RX exchange per connection event) + /// - Max value: 1638400 (4000 ms). A higher value can be specified (max 0xFFFFFFFF) but results in a maximum connection time + /// of 4000 ms as specified. In this case the parameter is not applied, and the predicted CE length calculated on slave is not shortened pub max_conn_event_length: u32, + /// Startup time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 µs (~2.44 µs). + /// - Min value: 0 + /// - Max value: 820 (~2 ms). A higher value can be specified, but the value that implemented in stack is forced to ~2 ms pub hs_startup_time: u16, + /// Viterbi implementation in BLE LL reception. + /// - 0: Enable + /// - 1: Disable pub viterbi_enable: u8, - pub ll_only: u8, + /// - bit 0: + /// - 1: LL only + /// - 0: LL + host + /// - bit 1: + /// - 1: no service change desc. + /// - 0: with service change desc. + /// - bit 2: + /// - 1: device name Read-Only + /// - 0: device name R/W + /// - bit 3: + /// - 1: extended advertizing supported + /// - 0: extended advertizing not supported + /// - bit 4: + /// - 1: CS Algo #2 supported + /// - 0: CS Algo #2 not supported + /// - bit 5: + /// - 1: Reduced GATT database in NVM + /// - 0: Full GATT database in NVM + /// - bit 6: + /// - 1: GATT caching is used + /// - 0: GATT caching is not used + /// - bit 7: + /// - 1: LE Power Class 1 + /// - 0: LE Power Classe 2-3 + /// - other bits: complete with Options_extension flag + pub options: u8, + /// Reserved for future use - shall be set to 0 pub hw_version: u8, + // /** + // * Maximum number of connection-oriented channels in initiator mode. + // * Range: 0 .. 64 + // */ + // pub max_coc_initiator_nbr: u8, + // + // /** + // * Minimum transmit power in dBm supported by the Controller. + // * Range: -127 .. 20 + // */ + // pub min_tx_power: i8, + // + // /** + // * Maximum transmit power in dBm supported by the Controller. + // * Range: -127 .. 20 + // */ + // pub max_tx_power: i8, + // + // /** + // * RX model configuration + // * - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model + // * - other bits: reserved ( shall be set to 0) + // */ + // pub rx_model_config: u8, + // + // /* Maximum number of advertising sets. + // * Range: 1 .. 8 with limitation: + // * This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based + // * on Max Extended advertising configuration supported. + // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set + // */ + // pub max_adv_set_nbr: u8, + // + // /* Maximum advertising data length (in bytes) + // * Range: 31 .. 1650 with limitation: + // * This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based + // * on Max Extended advertising configuration supported. + // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set + // */ + // pub max_adv_data_len: u16, + // + // /* RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. + // * Range: -1280 .. 1280 + // */ + // pub tx_path_compens: i16, + // + // /* RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. + // * Range: -1280 .. 1280 + // */ + // pub rx_path_compens: i16, + // + // /* BLE core specification version (8-bit unsigned integer). + // * values as: 11(5.2), 12(5.3) + // */ + // pub ble_core_version: u8, + // + // /** + // * Options flags extension + // * - bit 0: 1: appearance Writable 0: appearance Read-Only + // * - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported + // * - other bits: reserved ( shall be set to 0) + // */ + // pub options_extension: u8, } impl ShciBleInitCmdParam { - pub fn payload<'a>(&self) -> &'a [u8] { + pub fn payload<'a>(&'a self) -> &'a [u8] { unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::()) } } } @@ -48,8 +355,8 @@ impl Default for ShciBleInitCmdParam { attr_value_arr_size: 1344, num_of_links: 2, extended_packet_length_enable: 1, - pr_write_list_size: 0x3A, - mb_lock_count: 0x79, + prepare_write_list_size: 0x3A, + block_count: 0x79, att_mtu: 156, slave_sca: 500, master_sca: 0, @@ -57,25 +364,12 @@ impl Default for ShciBleInitCmdParam { max_conn_event_length: 0xFFFFFFFF, hs_startup_time: 0x148, viterbi_enable: 1, - ll_only: 0, + options: 0, hw_version: 0, } } } -#[derive(Debug, Clone, Copy, Default)] -#[repr(C, packed)] -pub struct ShciHeader { - metadata: [u32; 3], -} - -#[derive(Debug, Clone, Copy)] -#[repr(C, packed)] -pub struct ShciBleInitCmdPacket { - pub header: ShciHeader, - pub param: ShciBleInitCmdParam, -} - pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; #[allow(dead_code)] // Not used currently but reserved const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index a185cd4f1..5fe09a20f 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -1,9 +1,10 @@ use core::marker::PhantomData; +use core::ptr; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; -use crate::evt::EvtBox; -use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT}; +use crate::evt::{CcEvt, EvtBox, EvtPacket}; +use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; use crate::tables::{SysTable, WirelessFwInfoTable}; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; @@ -39,21 +40,29 @@ impl Sys { } } - pub fn write(&self, opcode: u16, payload: &[u8]) { + pub async fn write(&self, opcode: ShciOpcode, payload: &[u8]) { + Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || unsafe { + CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload); + }) + .await; + } + + /// `HW_IPCC_SYS_CmdEvtNot` + pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> SchiCommandStatus { + self.write(opcode, payload).await; + Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; + unsafe { - CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload); + let p_event_packet = SYS_CMD_BUF.as_ptr() as *const EvtPacket; + let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt; + let p_payload = &((*p_command_event).payload) as *const u8; + + ptr::read_volatile(p_payload).try_into().unwrap() } } - pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) { - debug!("sending SHCI"); - - Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || { - self.write(SCHI_OPCODE_BLE_INIT, param.payload()); - }) - .await; - - Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; + pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> SchiCommandStatus { + self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await } /// `HW_IPCC_SYS_EvtNot` diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index f6641ae31..f47a89b6f 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -39,7 +39,7 @@ async fn main(spawner: Spawner) { let ready_event = mbox.sys_subsystem.read().await; let _ = poll_once(mbox.sys_subsystem.read()); // clear rx not - info!("coprocessor ready {}", ready_event.payload()); + info!("sys event {:x} : {:x}", ready_event.stub().kind, ready_event.payload()); // test memory manager mem::drop(ready_event); @@ -59,7 +59,8 @@ async fn main(spawner: Spawner) { Timer::after(Duration::from_millis(50)).await; - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let result = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + info!("subsystem initialization: {}", result); info!("starting ble..."); mbox.ble_subsystem.write(0x0c, &[]).await; @@ -67,9 +68,8 @@ async fn main(spawner: Spawner) { info!("waiting for ble..."); let ble_event = mbox.ble_subsystem.read().await; - info!("ble event: {}", ble_event.payload()); + info!("ble event {:x} : {:x}", ble_event.stub().kind, ble_event.payload()); - Timer::after(Duration::from_millis(150)).await; info!("Test OK"); cortex_m::asm::bkpt(); } From 39334f72804ad83c90cde53d328be779af9f1b92 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 18 Jun 2023 09:43:07 -0500 Subject: [PATCH 1384/1575] stm32/wpan: add ble, mac features and cleanup --- embassy-stm32-wpan/Cargo.toml | 3 +++ embassy-stm32-wpan/src/lib.rs | 37 +++-------------------------------- tests/stm32/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 35 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 1c1b57b36..f9023ed79 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -27,6 +27,9 @@ bit_field = "0.10.2" [features] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] +ble = [] +mac = [] + stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] stm32wb30ce = [ "embassy-stm32/stm32wb30ce" ] diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 833db0df3..035b130c8 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -10,13 +10,8 @@ use ble::Ble; use cmd::CmdPacket; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; -use embassy_stm32::interrupt::typelevel::Interrupt; use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::channel::Channel; -use embassy_sync::signal::Signal; -use evt::{CcEvt, EvtBox}; use mm::MemoryManager; use sys::Sys; use tables::{ @@ -129,19 +124,6 @@ static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); // fuck these "magic" numbers from ST ---v---v static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); -// TODO: remove these items - -#[allow(dead_code)] -/// current event that is produced during IPCC IRQ handler execution -/// on SYS channel -static EVT_CHANNEL: Channel = Channel::new(); - -#[allow(dead_code)] -/// last received Command Complete event -static LAST_CC_EVT: Signal = Signal::new(); - -static STATE: Signal = Signal::new(); - pub struct TlMbox<'d> { _ipcc: PeripheralRef<'d, IPCC>, @@ -232,24 +214,11 @@ impl<'d> TlMbox<'d> { Ipcc::enable(config); - let sys = sys::Sys::new(); - let ble = ble::Ble::new(); - let mm = mm::MemoryManager::new(); - - // enable interrupts - interrupt::typelevel::IPCC_C1_RX::unpend(); - interrupt::typelevel::IPCC_C1_TX::unpend(); - - unsafe { interrupt::typelevel::IPCC_C1_RX::enable() }; - unsafe { interrupt::typelevel::IPCC_C1_TX::enable() }; - - STATE.reset(); - Self { _ipcc: ipcc, - sys_subsystem: sys, - ble_subsystem: ble, - mm_subsystem: mm, + sys_subsystem: sys::Sys::new(), + ble_subsystem: ble::Ble::new(), + mm_subsystem: mm::MemoryManager::new(), } } } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 487ef4626..539ee265f 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -29,7 +29,7 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg"] } +embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] } defmt = "0.3.0" defmt-rtt = "0.4" From 748d1ea89da160df927ea3dd597704ba236e0fb6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 18 Jun 2023 10:10:05 -0500 Subject: [PATCH 1385/1575] stm32/ipcc: minor cleanup --- embassy-stm32/src/ipcc.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 609c4d2c3..f481a5955 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -122,7 +122,6 @@ impl Ipcc { let regs = IPCC::regs(); Self::flush(channel).await; - compiler_fence(Ordering::SeqCst); f(); @@ -138,7 +137,7 @@ impl Ipcc { // This is a race, but is nice for debugging if unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) { - trace!("ipcc: ch {}: wait for tx free", channel as u8); + trace!("ipcc: ch {}: wait for tx free", channel as u8); } poll_fn(|cx| { @@ -167,7 +166,7 @@ impl Ipcc { loop { // This is a race, but is nice for debugging if !unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) { - trace!("ipcc: ch {}: wait for rx occupied", channel as u8); + trace!("ipcc: ch {}: wait for rx occupied", channel as u8); } poll_fn(|cx| { @@ -188,8 +187,7 @@ impl Ipcc { }) .await; - trace!("ipcc: ch {}: read data", channel as u8); - compiler_fence(Ordering::SeqCst); + trace!("ipcc: ch {}: read data", channel as u8); match f() { Some(ret) => return ret, @@ -239,7 +237,7 @@ pub(crate) mod sealed { } } - pub fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { + pub const fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { match channel { IpccChannel::Channel1 => &self.rx_wakers[0], IpccChannel::Channel2 => &self.rx_wakers[1], @@ -250,7 +248,7 @@ pub(crate) mod sealed { } } - pub fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { + pub const fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { match channel { IpccChannel::Channel1 => &self.tx_wakers[0], IpccChannel::Channel2 => &self.tx_wakers[1], From 9f63158aad81486b4bd0ffe6f8d7103458ba360f Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 18 Jun 2023 10:11:36 -0500 Subject: [PATCH 1386/1575] stm32/wpan: reorg modules --- embassy-stm32-wpan/src/consts.rs | 34 +++++++++++ embassy-stm32-wpan/src/lib.rs | 97 +------------------------------- embassy-stm32-wpan/src/mm.rs | 13 +++-- embassy-stm32-wpan/src/shci.rs | 2 +- embassy-stm32-wpan/src/tables.rs | 77 +++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 103 deletions(-) diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs index caf26c06b..9a107306c 100644 --- a/embassy-stm32-wpan/src/consts.rs +++ b/embassy-stm32-wpan/src/consts.rs @@ -1,5 +1,8 @@ use core::convert::TryFrom; +use crate::evt::CsEvt; +use crate::PacketHeader; + #[derive(Debug)] #[repr(C)] pub enum TlPacketType { @@ -53,3 +56,34 @@ impl TryFrom for TlPacketType { } } } + +pub const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); +pub const TL_EVT_HEADER_SIZE: usize = 3; +pub const TL_CS_EVT_SIZE: usize = core::mem::size_of::(); + +/** + * Queue length of BLE Event + * This parameter defines the number of asynchronous events that can be stored in the HCI layer before + * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer + * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large + * enough to store all asynchronous events received in between. + * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events + * between the HCI command and its event. + * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is to small, + * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting + * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate + * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). + */ +pub const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5; +pub const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; +pub const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE; + +pub const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); + +pub const fn divc(x: usize, y: usize) -> usize { + (x + y - 1) / y +} + +pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; +#[allow(dead_code)] +pub const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 035b130c8..1ddb3f2e0 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -7,16 +7,13 @@ use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; use ble::Ble; -use cmd::CmdPacket; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; use mm::MemoryManager; use sys::Sys; -use tables::{ - BleTable, DeviceInfoTable, Mac802_15_4Table, MemManagerTable, RefTable, SysTable, ThreadTable, TracesTable, -}; +use tables::*; use unsafe_linked_list::LinkedListNode; pub mod ble; @@ -30,100 +27,8 @@ pub mod sys; pub mod tables; pub mod unsafe_linked_list; -#[link_section = "TL_REF_TABLE"] -pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_DEVICE_INFO_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_MEM_MANAGER_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM1"] -static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -static mut FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); - -// Not in shared RAM -static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); - -#[allow(dead_code)] // Not used currently but reserved -#[link_section = "MB_MEM2"] -static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); - type PacketHeader = LinkedListNode; -const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::(); -const TL_EVT_HEADER_SIZE: usize = 3; -const TL_CS_EVT_SIZE: usize = core::mem::size_of::(); - -#[link_section = "MB_MEM2"] -static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = - MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -pub static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); - -/** - * Queue length of BLE Event - * This parameter defines the number of asynchronous events that can be stored in the HCI layer before - * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer - * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large - * enough to store all asynchronous events received in between. - * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events - * between the HCI command and its event. - * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is to small, - * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting - * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate - * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). - */ -const CFG_TLBLE_EVT_QUEUE_LENGTH: usize = 5; -const CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; -const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE; - -const fn divc(x: usize, y: usize) -> usize { - ((x) + (y) - 1) / (y) -} - -const POOL_SIZE: usize = CFG_TLBLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); - -#[link_section = "MB_MEM2"] -static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = - MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = - MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); - -#[link_section = "MB_MEM2"] -// fuck these "magic" numbers from ST ---v---v -static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); - pub struct TlMbox<'d> { _ipcc: PeripheralRef<'d, IPCC>, diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index 21f42409a..dc499c1e7 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -1,22 +1,23 @@ //! Memory manager routines - use core::future::poll_fn; use core::marker::PhantomData; +use core::mem::MaybeUninit; use core::task::Poll; use cortex_m::interrupt; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; +use crate::channels; +use crate::consts::POOL_SIZE; use crate::evt::EvtPacket; -use crate::tables::MemManagerTable; -use crate::unsafe_linked_list::LinkedListNode; -use crate::{ - channels, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, - TL_MEM_MANAGER_TABLE, +use crate::tables::{ + MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, }; +use crate::unsafe_linked_list::LinkedListNode; static MM_WAKER: AtomicWaker = AtomicWaker::new(); +static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); pub struct MemoryManager { phantom: PhantomData, diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index f4e9ba84c..b60158b1b 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -1,6 +1,6 @@ use core::{mem, slice}; -use super::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; +use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; const SHCI_OGF: u16 = 0x3F; diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 151216958..81873b147 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -1,6 +1,9 @@ +use core::mem::MaybeUninit; + use bit_field::BitField; use crate::cmd::{AclDataPacket, CmdPacket}; +use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; use crate::unsafe_linked_list::LinkedListNode; #[derive(Debug, Copy, Clone)] @@ -173,3 +176,77 @@ pub struct RefTable { pub traces_table: *const TracesTable, pub mac_802_15_4_table: *const Mac802_15_4Table, } + +// --------------------- ref table --------------------- +#[link_section = "TL_REF_TABLE"] +pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut TL_DEVICE_INFO_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); + +// #[link_section = "MB_MEM1"] +// pub static mut TL_LLD_TESTS_TABLE: MaybeUninit = MaybeUninit::uninit(); + +// #[link_section = "MB_MEM1"] +// pub static mut TL_BLE_LLD_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut TL_MEM_MANAGER_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); + +// #[link_section = "MB_MEM1"] +// pub static mut TL_ZIGBEE_TABLE: MaybeUninit = MaybeUninit::uninit(); + +// --------------------- tables --------------------- +#[link_section = "MB_MEM1"] +pub static mut FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[allow(dead_code)] +#[link_section = "MB_MEM1"] +pub static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +pub static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = + MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +pub static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +pub static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); + +// --------------------- app tables --------------------- +#[link_section = "MB_MEM2"] +pub static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +pub static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +pub static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + +#[link_section = "MB_MEM1"] +pub static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +pub static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + +#[link_section = "MB_MEM2"] +// fuck these "magic" numbers from ST ---v---v +pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); From f8ee33abb9943d6fe57c126eeecb36db9935c4ba Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 18 Jun 2023 18:51:36 +0200 Subject: [PATCH 1387/1575] add half transfer interrupt and circular dma --- embassy-stm32/src/dac/mod.rs | 17 ++++++++++++++--- embassy-stm32/src/dma/bdma.rs | 26 ++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 7b81ec1f2..525d45d72 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -2,7 +2,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; -use crate::dma::Transfer; +use crate::dma::{Transfer, TransferOptions}; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -237,7 +237,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { } /// TODO: Allow an array of Value instead of only u16, right-aligned - pub async fn write(&mut self, data: &[u16]) -> Result<(), Error> + pub async fn write(&mut self, data: &[u16], circular: bool) -> Result<(), Error> where Tx: Dma, { @@ -257,7 +257,18 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { // Use the 12 bit right-aligned register for now. TODO: distinguish values let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16; - let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; + let tx_f = unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + data, + tx_dst, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }; //debug!("Awaiting tx_f"); diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index c0a503e25..ca18047e7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -1,6 +1,7 @@ #![macro_use] use core::future::Future; +use core::option; use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; @@ -21,11 +22,17 @@ use crate::pac::bdma::{regs, vals}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] -pub struct TransferOptions {} +pub struct TransferOptions { + pub circular: bool, + pub halt_transfer_ir: bool, +} impl Default for TransferOptions { fn default() -> Self { - Self {} + Self { + circular: false, + halt_transfer_ir: false, + } } } @@ -253,7 +260,7 @@ impl<'a, C: Channel> Transfer<'a, C> { mem_len: usize, incr_mem: bool, data_size: WordSize, - _options: TransferOptions, + options: TransferOptions, ) -> Self { let ch = channel.regs().ch(channel.num()); @@ -284,6 +291,14 @@ impl<'a, C: Channel> Transfer<'a, C> { w.set_dir(dir.into()); w.set_teie(true); w.set_tcie(true); + w.set_htie(options.halt_transfer_ir); + if options.circular { + w.set_circ(vals::Circ::ENABLED); + debug!("Setting circular mode"); + } else { + w.set_circ(vals::Circ::DISABLED); + } + w.set_pl(vals::Pl::VERYHIGH); w.set_en(true); }); @@ -314,8 +329,9 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); let en = unsafe { ch.cr().read() }.en(); + let circular = unsafe { ch.cr().read() }.circ() == vals::Circ::ENABLED; let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; - en && !tcif + en && (circular || !tcif) } /// Gets the total remaining transfers for the channel @@ -482,6 +498,8 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let ch = self.channel.regs().ch(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. + // If the channel is enabled and transfer is not completed, we need to perform + // two separate write access to the CR register to disable the channel. unsafe { ch.cr().write(|w| { w.set_teie(true); From b95c0210b83d0742fa4663b2726e4e7b82a4e068 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 18 Jun 2023 18:51:14 -0500 Subject: [PATCH 1388/1575] stm32/wpan: add draft mac mbox --- embassy-stm32-wpan/src/ble.rs | 4 +- embassy-stm32-wpan/src/evt.rs | 15 ++- embassy-stm32-wpan/src/lib.rs | 12 +- embassy-stm32-wpan/src/mac.rs | 109 ++++++++++++++++++ embassy-stm32-wpan/src/mm.rs | 3 +- embassy-stm32-wpan/src/tables.rs | 9 ++ examples/stm32wb/Cargo.toml | 14 +++ .../bin/{tl_mbox_tx_rx.rs => tl_mbox_ble.rs} | 0 examples/stm32wb/src/bin/tl_mbox_mac.rs | 64 ++++++++++ 9 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 embassy-stm32-wpan/src/mac.rs rename examples/stm32wb/src/bin/{tl_mbox_tx_rx.rs => tl_mbox_ble.rs} (100%) create mode 100644 examples/stm32wb/src/bin/tl_mbox_mac.rs diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index f0bd6f48c..60e0cbdf8 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -2,12 +2,12 @@ use core::marker::PhantomData; use embassy_stm32::ipcc::Ipcc; +use crate::channels; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::EvtBox; -use crate::tables::BleTable; +use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; pub struct Ble { phantom: PhantomData, diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index a7b99e540..ee108ec88 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -1,7 +1,6 @@ use core::{ptr, slice}; use super::PacketHeader; -use crate::mm; /** * The payload of `Evt` for a command status event @@ -129,6 +128,18 @@ impl EvtBox { impl Drop for EvtBox { fn drop(&mut self) { - unsafe { mm::MemoryManager::drop_event_packet(self.ptr) }; + #[cfg(feature = "ble")] + unsafe { + use crate::mm; + + mm::MemoryManager::drop_event_packet(self.ptr) + }; + + #[cfg(feature = "mac")] + unsafe { + use crate::mac; + + mac::Mac::drop_event_packet(self.ptr) + } } } diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 1ddb3f2e0..5aec9933c 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -6,7 +6,6 @@ pub mod fmt; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; -use ble::Ble; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; @@ -16,11 +15,14 @@ use sys::Sys; use tables::*; use unsafe_linked_list::LinkedListNode; +#[cfg(feature = "ble")] pub mod ble; pub mod channels; pub mod cmd; pub mod consts; pub mod evt; +#[cfg(feature = "mac")] +pub mod mac; pub mod mm; pub mod shci; pub mod sys; @@ -34,7 +36,10 @@ pub struct TlMbox<'d> { pub sys_subsystem: Sys, pub mm_subsystem: MemoryManager, - pub ble_subsystem: Ble, + #[cfg(feature = "ble")] + pub ble_subsystem: ble::Ble, + #[cfg(feature = "mac")] + pub mac_subsystem: mac::Mac, } impl<'d> TlMbox<'d> { @@ -122,7 +127,10 @@ impl<'d> TlMbox<'d> { Self { _ipcc: ipcc, sys_subsystem: sys::Sys::new(), + #[cfg(feature = "ble")] ble_subsystem: ble::Ble::new(), + #[cfg(feature = "mac")] + mac_subsystem: mac::Mac::new(), mm_subsystem: mm::MemoryManager::new(), } } diff --git a/embassy-stm32-wpan/src/mac.rs b/embassy-stm32-wpan/src/mac.rs new file mode 100644 index 000000000..9fbf37473 --- /dev/null +++ b/embassy-stm32-wpan/src/mac.rs @@ -0,0 +1,109 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::ptr; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; + +use embassy_futures::poll_once; +use embassy_stm32::ipcc::Ipcc; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::cmd::CmdPacket; +use crate::consts::TlPacketType; +use crate::evt::{EvtBox, EvtPacket}; +use crate::tables::{ + Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE, +}; +use crate::unsafe_linked_list::LinkedListNode; +use crate::{channels, EVT_QUEUE}; + +static MAC_WAKER: AtomicWaker = AtomicWaker::new(); +static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); + +pub struct Mac { + phantom: PhantomData, +} + +impl Mac { + pub(crate) fn new() -> Self { + unsafe { + LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); + + TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { + p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), + p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), + evt_queue: ptr::null_mut(), + }); + } + + Self { phantom: PhantomData } + } + + /// SAFETY: passing a pointer to something other than a managed event packet is UB + pub(crate) unsafe fn drop_event_packet(_: *mut EvtPacket) { + // Write the ack + CmdPacket::write_into(MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), TlPacketType::OtAck, 0, &[]); + + // Clear the rx flag + let _ = poll_once(Ipcc::receive::( + channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, + || None, + )); + + // Allow a new read call + MAC_EVT_OUT.store(false, Ordering::SeqCst); + MAC_WAKER.wake(); + } + + /// `HW_IPCC_MAC_802_15_4_EvtNot` + /// + /// This function will stall if the previous `EvtBox` has not been dropped + pub async fn read(&self) -> EvtBox { + // Wait for the last event box to be dropped + poll_fn(|cx| { + MAC_WAKER.register(cx.waker()); + if MAC_EVT_OUT.load(Ordering::SeqCst) { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + + // Return a new event box + Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe { + // The closure is not async, therefore the closure must execute to completion (cannot be dropped) + // Therefore, the event box is guaranteed to be cleaned up if it's not leaked + MAC_EVT_OUT.store(true, Ordering::SeqCst); + + Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) + }) + .await + } + + /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` + pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { + self.write(opcode, payload).await; + Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; + + unsafe { + let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; + let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8; + + ptr::read_volatile(p_mac_rsp_evt) + } + } + + /// `TL_MAC_802_15_4_SendCmd` + pub async fn write(&self, opcode: u16, payload: &[u8]) { + Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe { + CmdPacket::write_into( + MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), + TlPacketType::OtCmd, + opcode, + payload, + ); + }) + .await; + } +} diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs index dc499c1e7..047fddcd4 100644 --- a/embassy-stm32-wpan/src/mm.rs +++ b/embassy-stm32-wpan/src/mm.rs @@ -43,7 +43,8 @@ impl MemoryManager { Self { phantom: PhantomData } } - /// SAFETY: passing a pointer to something other than an event packet is UB + #[allow(dead_code)] + /// SAFETY: passing a pointer to something other than a managed event packet is UB pub(crate) unsafe fn drop_event_packet(evt: *mut EvtPacket) { interrupt::free(|_| unsafe { LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _); diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 81873b147..2064910f0 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -230,6 +230,15 @@ pub static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); pub static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); // --------------------- app tables --------------------- +#[cfg(feature = "mac")] +#[link_section = "MB_MEM2"] +pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); + +#[cfg(feature = "mac")] +#[link_section = "MB_MEM2"] +pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = + MaybeUninit::uninit(); + #[link_section = "MB_MEM2"] pub static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 83a443754..e41424aad 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -20,3 +20,17 @@ embedded-hal = "0.2.6" 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 } + + +[features] +default = ["ble"] +mac = ["embassy-stm32-wpan/mac"] +ble = ["embassy-stm32-wpan/ble"] + +[[bin]] +name = "tl_mbox_ble" +required-features = ["ble"] + +[[bin]] +name = "tl_mbox_mac" +required-features = ["mac"] \ No newline at end of file diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs similarity index 100% rename from examples/stm32wb/src/bin/tl_mbox_tx_rx.rs rename to examples/stm32wb/src/bin/tl_mbox_ble.rs diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs new file mode 100644 index 000000000..f13f260b5 --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -0,0 +1,64 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + +// mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; +// +// info!("starting ble..."); +// mbox.ble_subsystem.write(0x0c, &[]).await; +// +// info!("waiting for ble..."); +// let ble_event = mbox.ble_subsystem.read().await; +// +// info!("ble event: {}", ble_event.payload()); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 72fd648d92738546b03166922defd4bee2d5fa9d Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 18 Jun 2023 18:56:53 -0500 Subject: [PATCH 1389/1575] stm32/wpan: add shci mac init --- embassy-stm32-wpan/src/sys.rs | 6 ++++++ examples/stm32wb/src/bin/tl_mbox_mac.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index 5fe09a20f..65a64e090 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -61,6 +61,12 @@ impl Sys { } } + #[cfg(feature = "mac")] + pub async fn shci_c2_mac_802_15_4_init(&self) -> SchiCommandStatus { + self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await + } + + #[cfg(feature = "ble")] pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> SchiCommandStatus { self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await } diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index f13f260b5..a42939bbd 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -49,15 +49,15 @@ async fn main(_spawner: Spawner) { let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); -// mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; -// -// info!("starting ble..."); -// mbox.ble_subsystem.write(0x0c, &[]).await; -// -// info!("waiting for ble..."); -// let ble_event = mbox.ble_subsystem.read().await; -// -// info!("ble event: {}", ble_event.payload()); + mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + // + // info!("starting ble..."); + // mbox.ble_subsystem.write(0x0c, &[]).await; + // + // info!("waiting for ble..."); + // let ble_event = mbox.ble_subsystem.read().await; + // + // info!("ble event: {}", ble_event.payload()); info!("Test OK"); cortex_m::asm::bkpt(); From 0122b813d3dfe90bad7d307f4f0c7adcbae0a7a5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 18 Jun 2023 19:03:50 -0500 Subject: [PATCH 1390/1575] stm32/wpan: fix lifetime param --- embassy-stm32-wpan/src/shci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index b60158b1b..30d689716 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -113,7 +113,7 @@ pub struct ShciConfigParam { } impl ShciConfigParam { - pub fn payload<'a>(&self) -> &'a [u8] { + pub fn payload<'a>(&'a self) -> &'a [u8] { unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::()) } } } From 558918651ee99024876fb1f85a559d46edba9548 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 19 Jun 2023 03:07:26 +0200 Subject: [PATCH 1391/1575] stm32: update stm32-metapac. --- .../layer-by-layer/blinky-irq/src/main.rs | 24 +- embassy-lora/src/iv.rs | 16 +- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/build.rs | 6 +- embassy-stm32/src/adc/f1.rs | 86 +-- embassy-stm32/src/adc/v1.rs | 50 +- embassy-stm32/src/adc/v2.rs | 83 +-- embassy-stm32/src/adc/v3.rs | 178 +++-- embassy-stm32/src/adc/v4.rs | 170 ++--- embassy-stm32/src/can/bxcan.rs | 16 +- embassy-stm32/src/crc/v1.rs | 10 +- embassy-stm32/src/crc/v2v3.rs | 98 ++- embassy-stm32/src/dac.rs | 72 +- embassy-stm32/src/dcmi.rs | 102 +-- embassy-stm32/src/dma/bdma.rs | 56 +- embassy-stm32/src/dma/dma.rs | 84 +-- embassy-stm32/src/dma/dmamux.rs | 2 +- embassy-stm32/src/dma/gpdma.rs | 24 +- embassy-stm32/src/eth/v1/mod.rs | 391 +++++------ embassy-stm32/src/eth/v1/rx_desc.rs | 13 +- embassy-stm32/src/eth/v1/tx_desc.rs | 11 +- embassy-stm32/src/eth/v2/descriptors.rs | 29 +- embassy-stm32/src/eth/v2/mod.rs | 381 +++++----- embassy-stm32/src/exti.rs | 6 +- embassy-stm32/src/flash/f4.rs | 30 +- embassy-stm32/src/fmc.rs | 8 +- embassy-stm32/src/gpio.rs | 36 +- embassy-stm32/src/i2c/v1.rs | 164 ++--- embassy-stm32/src/i2c/v2.rs | 382 +++++----- embassy-stm32/src/i2s.rs | 34 +- embassy-stm32/src/ipcc.rs | 40 +- embassy-stm32/src/lib.rs | 56 +- embassy-stm32/src/pwm/complementary_pwm.rs | 44 +- embassy-stm32/src/pwm/mod.rs | 58 +- embassy-stm32/src/pwm/simple_pwm.rs | 34 +- embassy-stm32/src/qspi/mod.rs | 232 +++---- embassy-stm32/src/rcc/f4.rs | 16 +- embassy-stm32/src/rcc/f7.rs | 4 +- embassy-stm32/src/rcc/g0.rs | 2 +- embassy-stm32/src/rcc/h5.rs | 15 +- embassy-stm32/src/rcc/h7.rs | 22 +- embassy-stm32/src/rng.rs | 44 +- embassy-stm32/src/rtc/datetime.rs | 40 +- embassy-stm32/src/rtc/mod.rs | 34 +- embassy-stm32/src/rtc/v2.rs | 240 ++++--- embassy-stm32/src/rtc/v3.rs | 205 +++--- embassy-stm32/src/sdmmc/mod.rs | 657 +++++++++--------- embassy-stm32/src/spi/mod.rs | 280 ++++---- embassy-stm32/src/time_driver.rs | 20 +- embassy-stm32/src/timer/mod.rs | 64 +- embassy-stm32/src/usart/buffered.rs | 148 ++-- embassy-stm32/src/usart/mod.rs | 421 +++++------ embassy-stm32/src/usart/ringbuffered.rs | 81 +-- embassy-stm32/src/usb/usb.rs | 402 +++++------ embassy-stm32/src/usb_otg/mod.rs | 2 +- embassy-stm32/src/usb_otg/usb.rs | 606 ++++++++-------- embassy-stm32/src/wdg/mod.rs | 12 +- examples/stm32f0/src/bin/wdg.rs | 4 +- examples/stm32f4/src/bin/wdt.rs | 8 +- examples/stm32g4/src/bin/usb_serial.rs | 4 +- examples/stm32h5/src/bin/usb_serial.rs | 8 +- .../stm32h7/src/bin/low_level_timer_api.rs | 54 +- examples/stm32h7/src/bin/wdg.rs | 4 +- examples/stm32l4/src/bin/adc.rs | 10 +- examples/stm32l4/src/bin/dac.rs | 8 +- examples/stm32wl/src/bin/lora_lorawan.rs | 2 +- examples/stm32wl/src/bin/random.rs | 8 +- tests/stm32/src/bin/rtc.rs | 6 +- 68 files changed, 2893 insertions(+), 3568 deletions(-) diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs index 743d0c342..aecba0755 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs @@ -20,13 +20,13 @@ fn main() -> ! { let led = Output::new(p.PB14, Level::Low, Speed::Low); let mut button = Input::new(p.PC13, Pull::Up); - cortex_m::interrupt::free(|cs| unsafe { + cortex_m::interrupt::free(|cs| { enable_interrupt(&mut button); LED.borrow(cs).borrow_mut().replace(led); BUTTON.borrow(cs).borrow_mut().replace(button); - NVIC::unmask(pac::Interrupt::EXTI15_10); + unsafe { NVIC::unmask(pac::Interrupt::EXTI15_10) }; }); loop { @@ -64,25 +64,21 @@ const PORT: u8 = 2; const PIN: usize = 13; fn check_interrupt(_pin: &mut Input<'static, P>) -> bool { let exti = pac::EXTI; - unsafe { - let pin = PIN; - let lines = exti.pr(0).read(); - lines.line(pin) - } + let pin = PIN; + let lines = exti.pr(0).read(); + lines.line(pin) } fn clear_interrupt(_pin: &mut Input<'static, P>) { let exti = pac::EXTI; - unsafe { - let pin = PIN; - let mut lines = exti.pr(0).read(); - lines.set_line(pin, true); - exti.pr(0).write_value(lines); - } + let pin = PIN; + let mut lines = exti.pr(0).read(); + lines.set_line(pin, true); + exti.pr(0).write_value(lines); } fn enable_interrupt(_pin: &mut Input<'static, P>) { - cortex_m::interrupt::free(|_| unsafe { + cortex_m::interrupt::free(|_| { let rcc = pac::RCC; rcc.apb2enr().modify(|w| w.set_syscfgen(true)); diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs index 2e0b68d1a..136973fe3 100644 --- a/embassy-lora/src/iv.rs +++ b/embassy-lora/src/iv.rs @@ -68,29 +68,23 @@ where } async fn set_nss_low(&mut self) -> Result<(), RadioError> { let pwr = pac::PWR; - unsafe { - pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW)); - } + pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW)); Ok(()) } async fn set_nss_high(&mut self) -> Result<(), RadioError> { let pwr = pac::PWR; - unsafe { - pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH)); - } + pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH)); Ok(()) } async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> { let rcc = pac::RCC; - unsafe { - rcc.csr().modify(|w| w.set_rfrst(true)); - rcc.csr().modify(|w| w.set_rfrst(false)); - } + rcc.csr().modify(|w| w.set_rfrst(true)); + rcc.csr().modify(|w| w.set_rfrst(false)); Ok(()) } async fn wait_on_busy(&mut self) -> Result<(), RadioError> { let pwr = pac::PWR; - while unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } {} + while pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY {} Ok(()) } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index f876c7146..3d9ee8261 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "9" +stm32-metapac = "10" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "9", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "10", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9e597f187..f71074bcf 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -322,7 +322,7 @@ fn main() { let rst_reg = format_ident!("{}", rst.register.to_ascii_lowercase()); let set_rst_field = format_ident!("set_{}", rst.field.to_ascii_lowercase()); quote! { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(true)); crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(false)); }); @@ -353,13 +353,13 @@ fn main() { }) } fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); #after_enable }) } fn disable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); }) } diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index d30ec001d..2322204d5 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -32,26 +32,22 @@ impl<'d, T: Instance> Adc<'d, T> { into_ref!(adc); T::enable(); T::reset(); - unsafe { - T::regs().cr2().modify(|reg| reg.set_adon(true)); - } + T::regs().cr2().modify(|reg| reg.set_adon(true)); // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) // for at least two ADC clock cycles delay.delay_us((1_000_000 * 2) / Self::freq().0 + 1); - unsafe { - // Reset calibration - T::regs().cr2().modify(|reg| reg.set_rstcal(true)); - while T::regs().cr2().read().rstcal() { - // spin - } + // Reset calibration + T::regs().cr2().modify(|reg| reg.set_rstcal(true)); + while T::regs().cr2().read().rstcal() { + // spin + } - // Calibrate - T::regs().cr2().modify(|reg| reg.set_cal(true)); - while T::regs().cr2().read().cal() { - // spin - } + // Calibrate + T::regs().cr2().modify(|reg| reg.set_cal(true)); + while T::regs().cr2().read().cal() { + // spin } // One cycle after calibration @@ -81,20 +77,16 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn enable_vref(&self, _delay: &mut impl DelayUs) -> Vref { - unsafe { - T::regs().cr2().modify(|reg| { - reg.set_tsvrefe(true); - }) - } + T::regs().cr2().modify(|reg| { + reg.set_tsvrefe(true); + }); Vref {} } pub fn enable_temperature(&self) -> Temperature { - unsafe { - T::regs().cr2().modify(|reg| { - reg.set_tsvrefe(true); - }) - } + T::regs().cr2().modify(|reg| { + reg.set_tsvrefe(true); + }); Temperature {} } @@ -104,41 +96,37 @@ impl<'d, T: Instance> Adc<'d, T> { /// Perform a single conversion. fn convert(&mut self) -> u16 { - unsafe { - T::regs().cr2().modify(|reg| { - reg.set_adon(true); - reg.set_swstart(true); - }); - while T::regs().cr2().read().swstart() {} - while !T::regs().sr().read().eoc() {} + T::regs().cr2().modify(|reg| { + reg.set_adon(true); + reg.set_swstart(true); + }); + while T::regs().cr2().read().swstart() {} + while !T::regs().sr().read().eoc() {} - T::regs().dr().read().0 as u16 - } + T::regs().dr().read().0 as u16 } pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { - unsafe { - Self::set_channel_sample_time(pin.channel(), self.sample_time); - T::regs().cr1().modify(|reg| { - reg.set_scan(false); - reg.set_discen(false); - }); - T::regs().sqr1().modify(|reg| reg.set_l(0)); + Self::set_channel_sample_time(pin.channel(), self.sample_time); + T::regs().cr1().modify(|reg| { + reg.set_scan(false); + reg.set_discen(false); + }); + T::regs().sqr1().modify(|reg| reg.set_l(0)); - T::regs().cr2().modify(|reg| { - reg.set_cont(false); - reg.set_exttrig(true); - reg.set_swstart(false); - reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART); - }); - } + T::regs().cr2().modify(|reg| { + reg.set_cont(false); + reg.set_exttrig(true); + reg.set_swstart(false); + reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART); + }); // Configure the channel to sample - unsafe { T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())) } + T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())); self.convert() } - unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { let sample_time = sample_time.into(); if ch <= 9 { T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 82a8c3efb..d9af0c55e 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -57,18 +57,14 @@ impl<'d, T: Instance> Adc<'d, T> { // // 6.3.20 Vbat monitoring characteristics // ts_vbat ≥ 4μs - unsafe { - T::regs().ccr().modify(|reg| reg.set_vbaten(true)); - } + T::regs().ccr().modify(|reg| reg.set_vbaten(true)); Vbat } pub fn enable_vref(&self, delay: &mut impl DelayUs) -> Vref { // Table 28. Embedded internal reference voltage // tstart = 10μs - unsafe { - T::regs().ccr().modify(|reg| reg.set_vrefen(true)); - } + T::regs().ccr().modify(|reg| reg.set_vrefen(true)); delay.delay_us(10); Vref } @@ -79,27 +75,23 @@ impl<'d, T: Instance> Adc<'d, T> { // 6.3.19 Temperature sensor characteristics // tstart ≤ 10μs // ts_temp ≥ 4μs - unsafe { - T::regs().ccr().modify(|reg| reg.set_tsen(true)); - } + T::regs().ccr().modify(|reg| reg.set_tsen(true)); delay.delay_us(10); Temperature } fn calibrate(&self) { - unsafe { - // A.7.1 ADC calibration code example - if T::regs().cr().read().aden() { - T::regs().cr().modify(|reg| reg.set_addis(true)); - } - while T::regs().cr().read().aden() { - // spin - } - T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); - T::regs().cr().modify(|reg| reg.set_adcal(true)); - while T::regs().cr().read().adcal() { - // spin - } + // A.7.1 ADC calibration code example + if T::regs().cr().read().aden() { + T::regs().cr().modify(|reg| reg.set_addis(true)); + } + while T::regs().cr().read().aden() { + // spin + } + T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); + T::regs().cr().modify(|reg| reg.set_adcal(true)); + while T::regs().cr().read().adcal() { + // spin } } @@ -108,9 +100,7 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - unsafe { - T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); - } + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); } pub fn read

(&mut self, pin: &mut P) -> u16 @@ -118,18 +108,16 @@ impl<'d, T: Instance> Adc<'d, T> { P: AdcPin + crate::gpio::sealed::Pin, { let channel = pin.channel(); - unsafe { - pin.set_as_analog(); - self.read_channel(channel) - } + pin.set_as_analog(); + self.read_channel(channel) } pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { let channel = channel.channel(); - unsafe { self.read_channel(channel) } + self.read_channel(channel) } - unsafe fn read_channel(&mut self, channel: u8) -> u16 { + fn read_channel(&mut self, channel: u8) -> u16 { // A.7.2 ADC enable sequence code example if T::regs().isr().read().adrdy() { T::regs().isr().modify(|reg| reg.set_adrdy(true)); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 11a51f993..091c1d447 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -100,13 +100,10 @@ where T::reset(); let presc = Prescaler::from_pclk2(T::frequency()); - unsafe { - T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); - - T::regs().cr2().modify(|reg| { - reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); - }); - } + T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); + T::regs().cr2().modify(|reg| { + reg.set_adon(crate::pac::adc::vals::Adon::ENABLED); + }); delay.delay_us(ADC_POWERUP_TIME_US); @@ -121,19 +118,15 @@ where } pub fn set_resolution(&mut self, resolution: Resolution) { - unsafe { - T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); - } + T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); } /// Enables internal voltage reference and returns [VrefInt], which can be used in /// [Adc::read_internal()] to perform conversion. pub fn enable_vrefint(&self) -> VrefInt { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); VrefInt {} } @@ -144,11 +137,9 @@ where /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, /// temperature sensor will return vbat value. pub fn enable_temperature(&self) -> Temperature { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED); + }); Temperature {} } @@ -156,37 +147,33 @@ where /// Enables vbat input and returns [Vbat], which can be used in /// [Adc::read_internal()] to perform conversion. pub fn enable_vbat(&self) -> Vbat { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED); + }); Vbat {} } /// Perform a single conversion. fn convert(&mut self) -> u16 { - unsafe { - // clear end of conversion flag - T::regs().sr().modify(|reg| { - reg.set_eoc(crate::pac::adc::vals::Eoc::NOTCOMPLETE); - }); + // clear end of conversion flag + T::regs().sr().modify(|reg| { + reg.set_eoc(crate::pac::adc::vals::Eoc::NOTCOMPLETE); + }); - // Start conversion - T::regs().cr2().modify(|reg| { - reg.set_swstart(true); - }); + // Start conversion + T::regs().cr2().modify(|reg| { + reg.set_swstart(true); + }); - while T::regs().sr().read().strt() == crate::pac::adc::vals::Strt::NOTSTARTED { - // spin //wait for actual start - } - while T::regs().sr().read().eoc() == crate::pac::adc::vals::Eoc::NOTCOMPLETE { - // spin //wait for finish - } - - T::regs().dr().read().0 as u16 + while T::regs().sr().read().strt() == crate::pac::adc::vals::Strt::NOTSTARTED { + // spin //wait for actual start } + while T::regs().sr().read().eoc() == crate::pac::adc::vals::Eoc::NOTCOMPLETE { + // spin //wait for finish + } + + T::regs().dr().read().0 as u16 } pub fn read

(&mut self, pin: &mut P) -> u16 @@ -194,18 +181,16 @@ where P: AdcPin, P: crate::gpio::sealed::Pin, { - unsafe { - pin.set_as_analog(); + pin.set_as_analog(); - self.read_channel(pin.channel()) - } + self.read_channel(pin.channel()) } pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { - unsafe { self.read_channel(channel.channel()) } + self.read_channel(channel.channel()) } - unsafe fn read_channel(&mut self, channel: u8) -> u16 { + fn read_channel(&mut self, channel: u8) -> u16 { // Configure ADC // Select channel @@ -219,7 +204,7 @@ where val } - unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { let sample_time = sample_time.into(); if ch <= 9 { T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time)); diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 90aa7d3b9..94cdc86cd 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -12,7 +12,7 @@ pub const VREF_CALIB_MV: u32 = 3000; /// Sadly we cannot use `RccPeripheral::enable` since devices are quite inconsistent ADC clock /// configuration. fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { #[cfg(stm32h7)] crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true)); #[cfg(stm32g0)] @@ -62,29 +62,25 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(adc); enable(); - unsafe { - T::regs().cr().modify(|reg| { - #[cfg(not(adc_g0))] - reg.set_deeppwd(false); - reg.set_advregen(true); - }); + T::regs().cr().modify(|reg| { + #[cfg(not(adc_g0))] + reg.set_deeppwd(false); + reg.set_advregen(true); + }); - #[cfg(adc_g0)] - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(false); - }); - } + #[cfg(adc_g0)] + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(false); + }); delay.delay_us(20); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_adcal(true); - }); + T::regs().cr().modify(|reg| { + reg.set_adcal(true); + }); - while T::regs().cr().read().adcal() { - // spin - } + while T::regs().cr().read().adcal() { + // spin } delay.delay_us(1); @@ -96,11 +92,9 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn enable_vrefint(&self, delay: &mut impl DelayUs) -> VrefInt { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us // to stabilize the internal voltage reference, we wait a little more. @@ -112,21 +106,17 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn enable_temperature(&self) -> Temperature { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_ch17sel(true); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_ch17sel(true); + }); Temperature {} } pub fn enable_vbat(&self) -> Vbat { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_ch18sel(true); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_ch18sel(true); + }); Vbat {} } @@ -136,12 +126,10 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - unsafe { - #[cfg(not(stm32g0))] - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - #[cfg(stm32g0)] - T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); - } + #[cfg(not(stm32g0))] + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + #[cfg(stm32g0)] + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); } /* @@ -155,77 +143,73 @@ impl<'d, T: Instance> Adc<'d, T> { /// Perform a single conversion. fn convert(&mut self) -> u16 { - unsafe { - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); - while !T::regs().isr().read().eos() { - // spin - } - - T::regs().dr().read().0 as u16 + while !T::regs().isr().read().eos() { + // spin } + + T::regs().dr().read().0 as u16 } pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { - unsafe { - // Make sure bits are off - while T::regs().cr().read().addis() { - // spin - } - - // Enable ADC - T::regs().isr().modify(|reg| { - reg.set_adrdy(true); - }); - T::regs().cr().modify(|reg| { - reg.set_aden(true); - }); - - while !T::regs().isr().read().adrdy() { - // spin - } - - // Configure channel - Self::set_channel_sample_time(pin.channel(), self.sample_time); - - // Select channel - #[cfg(not(stm32g0))] - T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); - #[cfg(stm32g0)] - 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 - // corrupted, so we discard it and measure again. - // - // STM32L471xx: Section 2.7.3 - // STM32G4: Section 2.7.3 - #[cfg(any(rcc_l4, rcc_g4))] - let _ = self.convert(); - - let val = self.convert(); - - T::regs().cr().modify(|reg| reg.set_addis(true)); - - val + // Make sure bits are off + while T::regs().cr().read().addis() { + // spin } + + // Enable ADC + T::regs().isr().modify(|reg| { + reg.set_adrdy(true); + }); + T::regs().cr().modify(|reg| { + reg.set_aden(true); + }); + + while !T::regs().isr().read().adrdy() { + // spin + } + + // Configure channel + Self::set_channel_sample_time(pin.channel(), self.sample_time); + + // Select channel + #[cfg(not(stm32g0))] + T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); + #[cfg(stm32g0)] + 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 + // corrupted, so we discard it and measure again. + // + // STM32L471xx: Section 2.7.3 + // STM32G4: Section 2.7.3 + #[cfg(any(rcc_l4, rcc_g4))] + let _ = self.convert(); + + let val = self.convert(); + + T::regs().cr().modify(|reg| reg.set_addis(true)); + + val } #[cfg(stm32g0)] - unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { + fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } #[cfg(not(stm32g0))] - unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { let sample_time = sample_time.into(); if ch <= 9 { T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time)); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 4707b7c95..c51c6840f 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -46,8 +46,8 @@ foreach_peripheral!( (adc, ADC1) => { impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { - match crate::rcc::get_freqs().adc { + critical_section::with(|_| { + match unsafe { crate::rcc::get_freqs() }.adc { Some(ck) => ck, None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") } @@ -55,7 +55,7 @@ foreach_peripheral!( } fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) }); ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); @@ -63,7 +63,7 @@ foreach_peripheral!( fn disable() { if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); }) } @@ -72,7 +72,7 @@ foreach_peripheral!( fn reset() { if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); }); @@ -85,8 +85,8 @@ foreach_peripheral!( (adc, ADC2) => { impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { - match crate::rcc::get_freqs().adc { + critical_section::with(|_| { + match unsafe { crate::rcc::get_freqs() }.adc { Some(ck) => ck, None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") } @@ -94,7 +94,7 @@ foreach_peripheral!( } fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true)) }); ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst); @@ -102,7 +102,7 @@ foreach_peripheral!( fn disable() { if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false)); }) } @@ -111,7 +111,7 @@ foreach_peripheral!( fn reset() { if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true)); crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false)); }); @@ -124,8 +124,8 @@ foreach_peripheral!( (adc, ADC3) => { impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { - match crate::rcc::get_freqs().adc { + critical_section::with(|_| { + match unsafe { crate::rcc::get_freqs() }.adc { Some(ck) => ck, None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.") } @@ -133,22 +133,22 @@ foreach_peripheral!( } fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true)) }); } fn disable() { - critical_section::with(|_| unsafe { - crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false)); - }) + critical_section::with(|_| { + crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false)); + }) } fn reset() { - critical_section::with(|_| unsafe { - crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true)); - crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false)); - }); + critical_section::with(|_| { + crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true)); + crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false)); + }); } } @@ -232,9 +232,7 @@ impl<'d, T: Instance> Adc<'d, T> { let prescaler = Prescaler::from_ker_ck(T::frequency()); - unsafe { - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); - } + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); let frequency = Hertz(T::frequency().0 / prescaler.divisor()); info!("ADC frequency set to {} Hz", frequency.0); @@ -251,9 +249,7 @@ impl<'d, T: Instance> Adc<'d, T> { } else { Boost::LT50 }; - unsafe { - T::regs().cr().modify(|w| w.set_boost(boost)); - } + T::regs().cr().modify(|w| w.set_boost(boost)); let mut s = Self { adc, @@ -272,84 +268,68 @@ impl<'d, T: Instance> Adc<'d, T> { } fn power_up(&mut self, delay: &mut impl DelayUs) { - unsafe { - T::regs().cr().modify(|reg| { - reg.set_deeppwd(false); - reg.set_advregen(true); - }); - } + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); delay.delay_us(10); } fn configure_differential_inputs(&mut self) { - unsafe { - T::regs().difsel().modify(|w| { - for n in 0..20 { - w.set_difsel(n, Difsel::SINGLEENDED); - } - }) - }; + T::regs().difsel().modify(|w| { + for n in 0..20 { + w.set_difsel(n, Difsel::SINGLEENDED); + } + }); } fn calibrate(&mut self) { - unsafe { - T::regs().cr().modify(|w| { - w.set_adcaldif(Adcaldif::SINGLEENDED); - w.set_adcallin(true); - }); + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::SINGLEENDED); + w.set_adcallin(true); + }); - T::regs().cr().modify(|w| w.set_adcal(true)); + T::regs().cr().modify(|w| w.set_adcal(true)); - while T::regs().cr().read().adcal() {} - } + while T::regs().cr().read().adcal() {} } fn enable(&mut self) { - unsafe { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); - } + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); } fn configure(&mut self) { // single conversion mode, software trigger - unsafe { - T::regs().cfgr().modify(|w| { - w.set_cont(false); - w.set_exten(Exten::DISABLED); - }) - } + T::regs().cfgr().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + }); } pub fn enable_vrefint(&self) -> VrefInt { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); VrefInt {} } pub fn enable_temperature(&self) -> Temperature { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_vsenseen(true); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); + }); Temperature {} } pub fn enable_vbat(&self) -> Vbat { - unsafe { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); - }); - } + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); Vbat {} } @@ -359,30 +339,26 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - unsafe { - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - } + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); } /// Perform a single conversion. fn convert(&mut self) -> u16 { - unsafe { - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); - while !T::regs().isr().read().eos() { - // spin - } - - T::regs().dr().read().0 as u16 + while !T::regs().isr().read().eos() { + // spin } + + T::regs().dr().read().0 as u16 } pub fn read

(&mut self, pin: &mut P) -> u16 @@ -390,18 +366,16 @@ impl<'d, T: Instance> Adc<'d, T> { P: AdcPin, P: crate::gpio::sealed::Pin, { - unsafe { - pin.set_as_analog(); + pin.set_as_analog(); - self.read_channel(pin.channel()) - } + self.read_channel(pin.channel()) } pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { - unsafe { self.read_channel(channel.channel()) } + self.read_channel(channel.channel()) } - unsafe fn read_channel(&mut self, channel: u8) -> u16 { + fn read_channel(&mut self, channel: u8) -> u16 { // Configure channel Self::set_channel_sample_time(channel, self.sample_time); @@ -417,7 +391,7 @@ impl<'d, T: Instance> Adc<'d, T> { self.convert() } - unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { let sample_time = sample_time.into(); if ch <= 9 { T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index bd92b35a0..85f6e99ac 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -20,10 +20,8 @@ impl<'d, T: Instance> Can<'d, T> { ) -> Self { into_ref!(peri, rx, tx); - unsafe { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); T::enable(); T::reset(); @@ -42,10 +40,8 @@ impl<'d, T: Instance> Can<'d, T> { ) -> Self { into_ref!(peri, rx, tx); - unsafe { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); T::enable(); T::reset(); @@ -60,7 +56,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> { fn drop(&mut self) { // Cannot call `free()` because it moves the instance. // Manually reset the peripheral. - unsafe { T::regs().mcr().write(|w| w.set_reset(true)) } + T::regs().mcr().write(|w| w.set_reset(true)); T::disable(); } } @@ -98,7 +94,7 @@ unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> { foreach_peripheral!( (can, $inst:ident) => { impl sealed::Instance for peripherals::$inst { - const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.0 as *mut _; + const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; fn regs() -> &'static crate::pac::can::Can { &crate::pac::$inst diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 393089eed..3946a2d47 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -27,26 +27,24 @@ impl<'d> Crc<'d> { /// Resets the CRC unit to default value (0xFFFF_FFFF) pub fn reset(&mut self) { - unsafe { PAC_CRC.cr().write(|w| w.set_reset(true)) }; + PAC_CRC.cr().write(|w| w.set_reset(true)); } /// Feeds a word to the peripheral and returns the current CRC value pub fn feed_word(&mut self, word: u32) -> u32 { // write a single byte to the device, and return the result - unsafe { - PAC_CRC.dr().write_value(word); - } + PAC_CRC.dr().write_value(word); self.read() } /// Feed a slice of words to the peripheral and return the result. pub fn feed_words(&mut self, words: &[u32]) -> u32 { for word in words { - unsafe { PAC_CRC.dr().write_value(*word) } + PAC_CRC.dr().write_value(*word); } self.read() } pub fn read(&self) -> u32 { - unsafe { PAC_CRC.dr().read() } + PAC_CRC.dr().read() } } diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index 8acb3a770..f337055a7 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -85,95 +85,79 @@ impl<'d> Crc<'d> { } pub fn reset(&mut self) { - unsafe { - PAC_CRC.cr().modify(|w| w.set_reset(true)); - } + PAC_CRC.cr().modify(|w| w.set_reset(true)); } /// Reconfigures the CRC peripheral. Doesn't reset. fn reconfigure(&mut self) { - unsafe { - // Init CRC value - PAC_CRC.init().write_value(self._config.crc_init_value); - #[cfg(crc_v3)] - PAC_CRC.pol().write_value(self._config.crc_poly); + // Init CRC value + PAC_CRC.init().write_value(self._config.crc_init_value); + #[cfg(crc_v3)] + PAC_CRC.pol().write_value(self._config.crc_poly); - // configure CR components - // (reverse I/O, polysize, poly) - PAC_CRC.cr().write(|w| { - // configure reverse output - w.set_rev_out(match self._config.reverse_out { - true => vals::RevOut::REVERSED, - false => vals::RevOut::NORMAL, - }); - // configure reverse input - w.set_rev_in(match self._config.reverse_in { - InputReverseConfig::None => vals::RevIn::NORMAL, - InputReverseConfig::Byte => vals::RevIn::BYTE, - InputReverseConfig::Halfword => vals::RevIn::HALFWORD, - InputReverseConfig::Word => vals::RevIn::WORD, - }); - // configure the polynomial. - #[cfg(crc_v3)] - w.set_polysize(match self._config.poly_size { - PolySize::Width7 => vals::Polysize::POLYSIZE7, - PolySize::Width8 => vals::Polysize::POLYSIZE8, - PolySize::Width16 => vals::Polysize::POLYSIZE16, - PolySize::Width32 => vals::Polysize::POLYSIZE32, - }); - }) - } + // configure CR components + // (reverse I/O, polysize, poly) + PAC_CRC.cr().write(|w| { + // configure reverse output + w.set_rev_out(match self._config.reverse_out { + true => vals::RevOut::REVERSED, + false => vals::RevOut::NORMAL, + }); + // configure reverse input + w.set_rev_in(match self._config.reverse_in { + InputReverseConfig::None => vals::RevIn::NORMAL, + InputReverseConfig::Byte => vals::RevIn::BYTE, + InputReverseConfig::Halfword => vals::RevIn::HALFWORD, + InputReverseConfig::Word => vals::RevIn::WORD, + }); + // configure the polynomial. + #[cfg(crc_v3)] + w.set_polysize(match self._config.poly_size { + PolySize::Width7 => vals::Polysize::POLYSIZE7, + PolySize::Width8 => vals::Polysize::POLYSIZE8, + PolySize::Width16 => vals::Polysize::POLYSIZE16, + PolySize::Width32 => vals::Polysize::POLYSIZE32, + }); + }); self.reset(); } /// Feeds a byte into the CRC peripheral. Returns the computed checksum. pub fn feed_byte(&mut self, byte: u8) -> u32 { - unsafe { - PAC_CRC.dr8().write_value(byte); - PAC_CRC.dr().read() - } + PAC_CRC.dr8().write_value(byte); + PAC_CRC.dr().read() } /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum. pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 { for byte in bytes { - unsafe { - PAC_CRC.dr8().write_value(*byte); - } + PAC_CRC.dr8().write_value(*byte); } - unsafe { PAC_CRC.dr().read() } + PAC_CRC.dr().read() } /// Feeds a halfword into the CRC peripheral. Returns the computed checksum. pub fn feed_halfword(&mut self, halfword: u16) -> u32 { - unsafe { - PAC_CRC.dr16().write_value(halfword); - PAC_CRC.dr().read() - } + PAC_CRC.dr16().write_value(halfword); + PAC_CRC.dr().read() } /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum. pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { for halfword in halfwords { - unsafe { - PAC_CRC.dr16().write_value(*halfword); - } + PAC_CRC.dr16().write_value(*halfword); } - unsafe { PAC_CRC.dr().read() } + PAC_CRC.dr().read() } /// Feeds a words into the CRC peripheral. Returns the computed checksum. pub fn feed_word(&mut self, word: u32) -> u32 { - unsafe { - PAC_CRC.dr().write_value(word as u32); - PAC_CRC.dr().read() - } + PAC_CRC.dr().write_value(word as u32); + PAC_CRC.dr().read() } /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum. pub fn feed_words(&mut self, words: &[u32]) -> u32 { for word in words { - unsafe { - PAC_CRC.dr().write_value(*word as u32); - } + PAC_CRC.dr().write_value(*word as u32); } - unsafe { PAC_CRC.dr().read() } + PAC_CRC.dr().read() } } diff --git a/embassy-stm32/src/dac.rs b/embassy-stm32/src/dac.rs index 60e856c78..631118877 100644 --- a/embassy-stm32/src/dac.rs +++ b/embassy-stm32/src/dac.rs @@ -121,13 +121,11 @@ impl<'d, T: Instance> Dac<'d, T> { T::enable(); T::reset(); - unsafe { - T::regs().cr().modify(|reg| { - for ch in 0..channels { - reg.set_en(ch as usize, true); - } - }); - } + T::regs().cr().modify(|reg| { + for ch in 0..channels { + reg.set_en(ch as usize, true); + } + }); Self { channels, _peri: peri } } @@ -143,11 +141,9 @@ impl<'d, T: Instance> Dac<'d, T> { fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { self.check_channel_exists(ch)?; - unsafe { - T::regs().cr().modify(|reg| { - reg.set_en(ch.index(), on); - }) - } + T::regs().cr().modify(|reg| { + reg.set_en(ch.index(), on); + }); Ok(()) } @@ -162,56 +158,42 @@ impl<'d, T: Instance> Dac<'d, T> { pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch1)?; unwrap!(self.disable_channel(Channel::Ch1)); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_tsel1(trigger.tsel()); - }) - } + T::regs().cr().modify(|reg| { + reg.set_tsel1(trigger.tsel()); + }); Ok(()) } pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch2)?; unwrap!(self.disable_channel(Channel::Ch2)); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_tsel2(trigger.tsel()); - }) - } + T::regs().cr().modify(|reg| { + reg.set_tsel2(trigger.tsel()); + }); Ok(()) } pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { self.check_channel_exists(ch)?; - unsafe { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(ch.index(), true); - }); - } + T::regs().swtrigr().write(|reg| { + reg.set_swtrig(ch.index(), true); + }); Ok(()) } pub fn trigger_all(&mut self) { - unsafe { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(Channel::Ch1.index(), true); - reg.set_swtrig(Channel::Ch2.index(), true); - }) - } + T::regs().swtrigr().write(|reg| { + reg.set_swtrig(Channel::Ch1.index(), true); + reg.set_swtrig(Channel::Ch2.index(), true); + }); } pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { self.check_channel_exists(ch)?; match value { - Value::Bit8(v) => unsafe { - T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)); - }, - Value::Bit12(v, Alignment::Left) => unsafe { - T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)); - }, - Value::Bit12(v, Alignment::Right) => unsafe { - T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)); - }, + Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), } Ok(()) } @@ -239,20 +221,20 @@ foreach_peripheral!( } fn reset() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); }) } fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); }) } fn disable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)); }) } diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 41305d273..78b026cb6 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -96,8 +96,7 @@ impl Default for Config { macro_rules! config_pins { ($($pin:ident),*) => { into_ref!($($pin),*); - // NOTE(unsafe) Exclusive access to the registers - critical_section::with(|_| unsafe { + critical_section::with(|_| { $( $pin.set_as_af($pin.af_num(), AFType::Input); $pin.set_speed(Speed::VeryHigh); @@ -334,17 +333,15 @@ where T::reset(); T::enable(); - unsafe { - peri.regs().cr().modify(|r| { - r.set_cm(true); // disable continuous mode (snapshot mode) - r.set_ess(use_embedded_synchronization); - r.set_pckpol(config.pixclk_polarity == PixelClockPolarity::RisingEdge); - r.set_vspol(config.vsync_level == VSyncDataInvalidLevel::High); - r.set_hspol(config.hsync_level == HSyncDataInvalidLevel::High); - r.set_fcrc(0x00); // capture every frame - r.set_edm(edm); // extended data mode - }); - } + peri.regs().cr().modify(|r| { + r.set_cm(true); // disable continuous mode (snapshot mode) + r.set_ess(use_embedded_synchronization); + r.set_pckpol(config.pixclk_polarity == PixelClockPolarity::RisingEdge); + r.set_vspol(config.vsync_level == VSyncDataInvalidLevel::High); + r.set_hspol(config.hsync_level == HSyncDataInvalidLevel::High); + r.set_fcrc(0x00); // capture every frame + r.set_edm(edm); // extended data mode + }); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -352,7 +349,7 @@ where Self { inner: peri, dma } } - unsafe fn toggle(enable: bool) { + fn toggle(enable: bool) { crate::pac::DCMI.cr().modify(|r| { r.set_enable(enable); r.set_capture(enable); @@ -360,23 +357,19 @@ where } fn enable_irqs() { - unsafe { - crate::pac::DCMI.ier().modify(|r| { - r.set_err_ie(true); - r.set_ovr_ie(true); - r.set_frame_ie(true); - }); - } + crate::pac::DCMI.ier().modify(|r| { + r.set_err_ie(true); + r.set_ovr_ie(true); + r.set_frame_ie(true); + }); } fn clear_interrupt_flags() { - unsafe { - crate::pac::DCMI.icr().write(|r| { - r.set_ovr_isc(true); - r.set_err_isc(true); - r.set_frame_isc(true); - }) - } + crate::pac::DCMI.icr().write(|r| { + r.set_ovr_isc(true); + r.set_err_isc(true); + r.set_frame_isc(true); + }) } /// This method starts the capture and finishes when both the dma transfer and DCMI finish the frame transfer. @@ -392,41 +385,30 @@ where return self.capture_giant(buffer).await; } } + async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> { let r = self.inner.regs(); - let src = r.dr().ptr() as *mut u32; + let src = r.dr().as_ptr() as *mut u32; let request = self.dma.request(); let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) }; Self::clear_interrupt_flags(); Self::enable_irqs(); - unsafe { Self::toggle(true) }; + Self::toggle(true); let result = poll_fn(|cx| { STATE.waker.register(cx.waker()); - let ris = unsafe { crate::pac::DCMI.ris().read() }; + let ris = crate::pac::DCMI.ris().read(); if ris.err_ris() { - unsafe { - crate::pac::DCMI.icr().write(|r| { - r.set_err_isc(true); - }) - }; + crate::pac::DCMI.icr().write(|r| r.set_err_isc(true)); Poll::Ready(Err(Error::PeripheralError)) } else if ris.ovr_ris() { - unsafe { - crate::pac::DCMI.icr().write(|r| { - r.set_ovr_isc(true); - }) - }; + crate::pac::DCMI.icr().write(|r| r.set_ovr_isc(true)); Poll::Ready(Err(Error::Overrun)) } else if ris.frame_ris() { - unsafe { - crate::pac::DCMI.icr().write(|r| { - r.set_frame_isc(true); - }) - }; + crate::pac::DCMI.icr().write(|r| r.set_frame_isc(true)); Poll::Ready(Ok(())) } else { Poll::Pending @@ -435,7 +417,7 @@ where let (_, result) = embassy_futures::join::join(dma_read, result).await; - unsafe { Self::toggle(false) }; + Self::toggle(false); result } @@ -468,7 +450,7 @@ where let request = channel.request(); let r = self.inner.regs(); - let src = r.dr().ptr() as *mut u32; + let src = r.dr().as_ptr() as *mut u32; let mut transfer = unsafe { crate::dma::DoubleBuffered::new_read( @@ -526,38 +508,26 @@ where let result = poll_fn(|cx| { STATE.waker.register(cx.waker()); - let ris = unsafe { crate::pac::DCMI.ris().read() }; + let ris = crate::pac::DCMI.ris().read(); if ris.err_ris() { - unsafe { - crate::pac::DCMI.icr().write(|r| { - r.set_err_isc(true); - }) - }; + crate::pac::DCMI.icr().write(|r| r.set_err_isc(true)); Poll::Ready(Err(Error::PeripheralError)) } else if ris.ovr_ris() { - unsafe { - crate::pac::DCMI.icr().write(|r| { - r.set_ovr_isc(true); - }) - }; + crate::pac::DCMI.icr().write(|r| r.set_ovr_isc(true)); Poll::Ready(Err(Error::Overrun)) } else if ris.frame_ris() { - unsafe { - crate::pac::DCMI.icr().write(|r| { - r.set_frame_isc(true); - }) - }; + crate::pac::DCMI.icr().write(|r| r.set_frame_isc(true)); Poll::Ready(Ok(())) } else { Poll::Pending } }); - unsafe { Self::toggle(true) }; + Self::toggle(true); let (_, result) = embassy_futures::join::join(dma_result, result).await; - unsafe { Self::toggle(false) }; + Self::toggle(false); result } diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index c0a503e25..5fcb30f65 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -107,7 +107,7 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index let cr = dma.ch(channel_num).cr(); if isr.teif(channel_num) { - panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); + panic!("DMA: error on BDMA@{:08x} channel {}", dma.as_ptr() as u32, channel_num); } if isr.htif(channel_num) && cr.read().htie() { @@ -291,29 +291,25 @@ impl<'a, C: Channel> Transfer<'a, C> { } fn clear_irqs(&mut self) { - unsafe { - self.channel.regs().ifcr().write(|w| { - w.set_tcif(self.channel.num(), true); - w.set_teif(self.channel.num(), true); - }) - } + self.channel.regs().ifcr().write(|w| { + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }); } pub fn request_stop(&mut self) { let ch = self.channel.regs().ch(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - unsafe { - ch.cr().write(|w| { - w.set_teie(true); - w.set_tcie(true); - }) - } + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }); } pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); - let en = unsafe { ch.cr().read() }.en(); + let en = ch.cr().read().en(); let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; en && !tcif } @@ -322,7 +318,7 @@ impl<'a, C: Channel> Transfer<'a, C> { /// Note: this will be zero for transfers that completed without cancellation. pub fn get_remaining_transfers(&self) -> u16 { let ch = self.channel.regs().ch(self.channel.num()); - unsafe { ch.ndtr().read() }.ndt() + ch.ndtr().read().ndt() } pub fn blocking_wait(mut self) { @@ -366,7 +362,7 @@ struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn get_remaining_transfers(&self) -> usize { let ch = self.0.regs().ch(self.0.num()); - unsafe { ch.ndtr().read() }.ndt() as usize + ch.ndtr().read().ndt() as usize } fn get_complete_count(&self) -> usize { @@ -442,7 +438,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { pub fn start(&mut self) { let ch = self.channel.regs().ch(self.channel.num()); - unsafe { ch.cr().write_value(self.cr) } + ch.cr().write_value(self.cr) } pub fn clear(&mut self) { @@ -469,31 +465,27 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { fn clear_irqs(&mut self) { let dma = self.channel.regs(); - unsafe { - dma.ifcr().write(|w| { - w.set_htif(self.channel.num(), true); - w.set_tcif(self.channel.num(), true); - w.set_teif(self.channel.num(), true); - }) - } + dma.ifcr().write(|w| { + w.set_htif(self.channel.num(), true); + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }); } pub fn request_stop(&mut self) { let ch = self.channel.regs().ch(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - unsafe { - ch.cr().write(|w| { - w.set_teie(true); - w.set_htie(true); - w.set_tcie(true); - }) - } + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }); } pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); - unsafe { ch.cr().read() }.en() + ch.cr().read().en() } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 874cb013a..8abe541d3 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -183,7 +183,7 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: let isr = dma.isr(channel_num / 4).read(); if isr.teif(channel_num % 4) { - panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); + panic!("DMA: error on DMA@{:08x} channel {}", dma.as_ptr() as u32, channel_num); } if isr.htif(channel_num % 4) && cr.read().htie() { @@ -387,36 +387,32 @@ impl<'a, C: Channel> Transfer<'a, C> { let isrn = self.channel.num() / 4; let isrbit = self.channel.num() % 4; - unsafe { - self.channel.regs().ifcr(isrn).write(|w| { - w.set_tcif(isrbit, true); - w.set_teif(isrbit, true); - }) - } + self.channel.regs().ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }); } pub fn request_stop(&mut self) { let ch = self.channel.regs().st(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - unsafe { - ch.cr().write(|w| { - w.set_teie(true); - w.set_tcie(true); - }) - } + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }); } pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.cr().read() }.en() + ch.cr().read().en() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. pub fn get_remaining_transfers(&self) -> u16 { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.ndtr().read() }.ndt() + ch.ndtr().read().ndt() } pub fn blocking_wait(mut self) { @@ -537,13 +533,11 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { let isrn = channel_number / 4; let isrbit = channel_number % 4; - unsafe { - dma.ifcr(isrn).write(|w| { - w.set_htif(isrbit, true); - w.set_tcif(isrbit, true); - w.set_teif(isrbit, true); - }) - } + dma.ifcr(isrn).write(|w| { + w.set_htif(isrbit, true); + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }); } pub unsafe fn set_buffer0(&mut self, buffer: *mut W) { @@ -558,7 +552,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { pub fn is_buffer0_accessible(&mut self) -> bool { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1 + ch.cr().read().ct() == vals::Ct::MEMORY1 } pub fn set_waker(&mut self, waker: &Waker) { @@ -569,24 +563,22 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { let ch = self.channel.regs().st(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - unsafe { - ch.cr().write(|w| { - w.set_teie(true); - w.set_tcie(true); - }) - } + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }); } pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.cr().read() }.en() + ch.cr().read().en() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. pub fn get_remaining_transfers(&self) -> u16 { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.ndtr().read() }.ndt() + ch.ndtr().read().ndt() } } @@ -607,7 +599,7 @@ struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn get_remaining_transfers(&self) -> usize { let ch = self.0.regs().st(self.0.num()); - unsafe { ch.ndtr().read() }.ndt() as usize + ch.ndtr().read().ndt() as usize } fn get_complete_count(&self) -> usize { @@ -698,7 +690,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { pub fn start(&mut self) { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.cr().write_value(self.cr) } + ch.cr().write_value(self.cr); } pub fn clear(&mut self) { @@ -729,31 +721,27 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let isrn = channel_number / 4; let isrbit = channel_number % 4; - unsafe { - dma.ifcr(isrn).write(|w| { - w.set_htif(isrbit, true); - w.set_tcif(isrbit, true); - w.set_teif(isrbit, true); - }) - } + dma.ifcr(isrn).write(|w| { + w.set_htif(isrbit, true); + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }); } pub fn request_stop(&mut self) { let ch = self.channel.regs().st(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - unsafe { - ch.cr().write(|w| { - w.set_teie(true); - w.set_htie(true); - w.set_tcie(true); - }) - } + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }); } pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().st(self.channel.num()); - unsafe { ch.cr().read() }.en() + ch.cr().read().en() } } diff --git a/embassy-stm32/src/dma/dmamux.rs b/embassy-stm32/src/dma/dmamux.rs index a8c4c5827..36fc03403 100644 --- a/embassy-stm32/src/dma/dmamux.rs +++ b/embassy-stm32/src/dma/dmamux.rs @@ -2,7 +2,7 @@ use crate::{pac, peripherals}; -pub(crate) unsafe fn configure_dmamux(channel: &mut M, request: u8) { +pub(crate) fn configure_dmamux(channel: &mut M, request: u8) { let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num()); ch_mux_regs.write(|reg| { reg.set_nbreq(0); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 3f0d5e8fa..c600df92d 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -92,13 +92,15 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, in if sr.dtef() { panic!( "DMA: data transfer error on DMA@{:08x} channel {}", - dma.0 as u32, channel_num + dma.as_ptr() as u32, + channel_num ); } if sr.usef() { panic!( "DMA: user settings error on DMA@{:08x} channel {}", - dma.0 as u32, channel_num + dma.as_ptr() as u32, + channel_num ); } @@ -298,26 +300,24 @@ impl<'a, C: Channel> Transfer<'a, C> { let ch = self.channel.regs().ch(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - unsafe { - ch.cr().write(|w| { - w.set_tcie(true); - w.set_useie(true); - w.set_dteie(true); - w.set_suspie(true); - }) - } + ch.cr().write(|w| { + w.set_tcie(true); + w.set_useie(true); + w.set_dteie(true); + w.set_suspie(true); + }) } pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); - !unsafe { ch.sr().read() }.tcf() + !ch.sr().read().tcf() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. pub fn get_remaining_transfers(&self) -> u16 { let ch = self.channel.regs().ch(self.channel.num()); - unsafe { ch.br1().read() }.bndt() + ch.br1().read().bndt() } pub fn blocking_wait(mut self) { diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 540cdd027..b53c2d0fa 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -29,18 +29,16 @@ impl interrupt::typelevel::Handler for InterruptHandl WAKER.wake(); // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); + let dma = ETH.ethernet_dma(); - dma.dmasr().modify(|w| { - w.set_ts(true); - w.set_rs(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmasr().read(); - dma.dmasr().read(); - } + dma.dmasr().modify(|w| { + w.set_ts(true); + w.set_rs(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmasr().read(); + dma.dmasr().read(); } } @@ -59,7 +57,6 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { #[cfg(eth_v1a)] macro_rules! config_in_pins { ($($pin:ident),*) => { - // NOTE(unsafe) Exclusive access to the registers critical_section::with(|_| { $( // TODO properly create a set_as_input function @@ -72,7 +69,6 @@ macro_rules! config_in_pins { #[cfg(eth_v1a)] macro_rules! config_af_pins { ($($pin:ident),*) => { - // NOTE(unsafe) Exclusive access to the registers critical_section::with(|_| { $( // We are lucky here, this configures to max speed (50MHz) @@ -85,7 +81,6 @@ macro_rules! config_af_pins { #[cfg(any(eth_v1b, eth_v1c))] macro_rules! config_pins { ($($pin:ident),*) => { - // NOTE(unsafe) Exclusive access to the registers critical_section::with(|_| { $( $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); @@ -116,222 +111,208 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { ) -> Self { into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - unsafe { - // Enable the necessary Clocks - // NOTE(unsafe) We have exclusive access to the registers - #[cfg(eth_v1a)] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_afioen(true)); + // Enable the necessary Clocks + #[cfg(eth_v1a)] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_afioen(true)); - // Select RMII (Reduced Media Independent Interface) - // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); + // Select RMII (Reduced Media Independent Interface) + // Must be done prior to enabling peripheral clock + AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true)); - RCC.ahbenr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); + RCC.ahbenr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); + }); + + #[cfg(any(eth_v1b, eth_v1c))] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_syscfgen(true)); + RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); }); - #[cfg(any(eth_v1b, eth_v1c))] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_syscfgen(true)); - RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); + // RMII (Reduced Media Independent Interface) + SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); + }); - // RMII (Reduced Media Independent Interface) - SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); - }); - - #[cfg(eth_v1a)] - { - config_in_pins!(ref_clk, rx_d0, rx_d1); - config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); - } - - #[cfg(any(eth_v1b, eth_v1c))] - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - - // NOTE(unsafe) We have exclusive access to the registers - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - - // Reset and wait - dma.dmabmr().modify(|w| w.set_sr(true)); - while dma.dmabmr().read().sr() {} - - mac.maccr().modify(|w| { - w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times - w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping - w.set_fes(Fes::FES100); // fast ethernet speed - w.set_dm(Dm::FULLDUPLEX); // full duplex - // TODO: Carrier sense ? ECRSFD - }); - - // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, - // so the LR write must happen after the HR write. - mac.maca0hr() - .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); - mac.maca0lr().write(|w| { - w.set_maca0l( - u32::from(mac_addr[0]) - | (u32::from(mac_addr[1]) << 8) - | (u32::from(mac_addr[2]) << 16) - | (u32::from(mac_addr[3]) << 24), - ) - }); - - // pause time - mac.macfcr().modify(|w| w.set_pt(0x100)); - - // Transfer and Forward, Receive and Forward - dma.dmaomr().modify(|w| { - w.set_tsf(Tsf::STOREFORWARD); - w.set_rsf(Rsf::STOREFORWARD); - }); - - dma.dmabmr().modify(|w| { - w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? - }); - - // TODO MTU size setting not found for v1 ethernet, check if correct - - // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called - let hclk = crate::rcc::get_freqs().ahb1; - let hclk_mhz = hclk.0 / 1_000_000; - - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), - 25..=34 => Cr::CR_20_35, // Divide by 16 - 35..=59 => Cr::CR_35_60, // Divide by 26 - 60..=99 => Cr::CR_60_100, // Divide by 42 - 100..=149 => Cr::CR_100_150, // Divide by 62 - 150..=216 => Cr::CR_150_168, // Divide by 102 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") - } - }; - - let pins = [ - ref_clk.map_into(), - mdio.map_into(), - mdc.map_into(), - crs.map_into(), - rx_d0.map_into(), - rx_d1.map_into(), - tx_d0.map_into(), - tx_d1.map_into(), - tx_en.map_into(), - ]; - - let mut this = Self { - _peri: peri, - pins, - _phy: phy, - clock_range, - phy_addr, - mac_addr, - tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), - rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), - }; - - fence(Ordering::SeqCst); - - let mac = ETH.ethernet_mac(); - let dma = ETH.ethernet_dma(); - - mac.maccr().modify(|w| { - w.set_re(true); - w.set_te(true); - }); - dma.dmaomr().modify(|w| { - w.set_ftf(Ftf::FLUSH); // flush transmit fifo (queue) - w.set_st(St::STARTED); // start transmitting channel - w.set_sr(DmaomrSr::STARTED); // start receiving channel - }); - - this.rx.demand_poll(); - - // Enable interrupts - dma.dmaier().modify(|w| { - w.set_nise(true); - w.set_rie(true); - w.set_tie(true); - }); - - P::phy_reset(&mut this); - P::phy_init(&mut this); - - interrupt::ETH.unpend(); - interrupt::ETH.enable(); - - this + #[cfg(eth_v1a)] + { + config_in_pins!(ref_clk, rx_d0, rx_d1); + config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); } + + #[cfg(any(eth_v1b, eth_v1c))] + config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); + + // Reset and wait + dma.dmabmr().modify(|w| w.set_sr(true)); + while dma.dmabmr().read().sr() {} + + mac.maccr().modify(|w| { + w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times + w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping + w.set_fes(Fes::FES100); // fast ethernet speed + w.set_dm(Dm::FULLDUPLEX); // full duplex + // TODO: Carrier sense ? ECRSFD + }); + + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, + // so the LR write must happen after the HR write. + mac.maca0hr() + .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); + mac.maca0lr().write(|w| { + w.set_maca0l( + u32::from(mac_addr[0]) + | (u32::from(mac_addr[1]) << 8) + | (u32::from(mac_addr[2]) << 16) + | (u32::from(mac_addr[3]) << 24), + ) + }); + + // pause time + mac.macfcr().modify(|w| w.set_pt(0x100)); + + // Transfer and Forward, Receive and Forward + dma.dmaomr().modify(|w| { + w.set_tsf(Tsf::STOREFORWARD); + w.set_rsf(Rsf::STOREFORWARD); + }); + + dma.dmabmr().modify(|w| { + w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ? + }); + + // TODO MTU size setting not found for v1 ethernet, check if correct + + // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called + let hclk = unsafe { crate::rcc::get_freqs() }.ahb1; + let hclk_mhz = hclk.0 / 1_000_000; + + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), + 25..=34 => Cr::CR_20_35, // Divide by 16 + 35..=59 => Cr::CR_35_60, // Divide by 26 + 60..=99 => Cr::CR_60_100, // Divide by 42 + 100..=149 => Cr::CR_100_150, // Divide by 62 + 150..=216 => Cr::CR_150_168, // Divide by 102 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; + + let pins = [ + ref_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + crs.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_en.map_into(), + ]; + + let mut this = Self { + _peri: peri, + pins, + _phy: phy, + clock_range, + phy_addr, + mac_addr, + tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), + rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), + }; + + fence(Ordering::SeqCst); + + let mac = ETH.ethernet_mac(); + let dma = ETH.ethernet_dma(); + + mac.maccr().modify(|w| { + w.set_re(true); + w.set_te(true); + }); + dma.dmaomr().modify(|w| { + w.set_ftf(Ftf::FLUSH); // flush transmit fifo (queue) + w.set_st(St::STARTED); // start transmitting channel + w.set_sr(DmaomrSr::STARTED); // start receiving channel + }); + + this.rx.demand_poll(); + + // Enable interrupts + dma.dmaier().modify(|w| { + w.set_nise(true); + w.set_rie(true); + w.set_tie(true); + }); + + P::phy_reset(&mut this); + P::phy_init(&mut this); + + interrupt::ETH.unpend(); + unsafe { interrupt::ETH.enable() }; + + this } } unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { fn smi_read(&mut self, reg: u8) -> u16 { - // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` - unsafe { - let mac = ETH.ethernet_mac(); + let mac = ETH.ethernet_mac(); - mac.macmiiar().modify(|w| { - w.set_pa(self.phy_addr); - w.set_mr(reg); - w.set_mw(Mw::READ); // read operation - w.set_cr(self.clock_range); - w.set_mb(MbProgress::BUSY); // indicate that operation is in progress - }); - while mac.macmiiar().read().mb() == MbProgress::BUSY {} - mac.macmiidr().read().md() - } + mac.macmiiar().modify(|w| { + w.set_pa(self.phy_addr); + w.set_mr(reg); + w.set_mw(Mw::READ); // read operation + w.set_cr(self.clock_range); + w.set_mb(MbProgress::BUSY); // indicate that operation is in progress + }); + while mac.macmiiar().read().mb() == MbProgress::BUSY {} + mac.macmiidr().read().md() } fn smi_write(&mut self, reg: u8, val: u16) { - // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` - unsafe { - let mac = ETH.ethernet_mac(); + let mac = ETH.ethernet_mac(); - mac.macmiidr().write(|w| w.set_md(val)); - mac.macmiiar().modify(|w| { - w.set_pa(self.phy_addr); - w.set_mr(reg); - w.set_mw(Mw::WRITE); // write - w.set_cr(self.clock_range); - w.set_mb(MbProgress::BUSY); - }); - while mac.macmiiar().read().mb() == MbProgress::BUSY {} - } + mac.macmiidr().write(|w| w.set_md(val)); + mac.macmiiar().modify(|w| { + w.set_pa(self.phy_addr); + w.set_mr(reg); + w.set_mw(Mw::WRITE); // write + w.set_cr(self.clock_range); + w.set_mb(MbProgress::BUSY); + }); + while mac.macmiiar().read().mb() == MbProgress::BUSY {} } } impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { - // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers - unsafe { - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); - // Disable the TX DMA and wait for any previous transmissions to be completed - dma.dmaomr().modify(|w| w.set_st(St::STOPPED)); + // Disable the TX DMA and wait for any previous transmissions to be completed + dma.dmaomr().modify(|w| w.set_st(St::STOPPED)); - // Disable MAC transmitter and receiver - mac.maccr().modify(|w| { - w.set_re(false); - w.set_te(false); - }); + // Disable MAC transmitter and receiver + mac.maccr().modify(|w| { + w.set_re(false); + w.set_te(false); + }); - dma.dmaomr().modify(|w| w.set_sr(DmaomrSr::STOPPED)); - } + dma.dmaomr().modify(|w| w.set_sr(DmaomrSr::STOPPED)); - // NOTE(unsafe) Exclusive access to the regs - critical_section::with(|_| unsafe { + critical_section::with(|_| { for pin in self.pins.iter_mut() { pin.set_as_disconnected(); } diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index 8b8566d92..01a073bb9 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs @@ -146,12 +146,9 @@ impl<'a> RDesRing<'a> { } // Register rx descriptor start - // NOTE (unsafe) Used for atomic writes - unsafe { - ETH.ethernet_dma() - .dmardlar() - .write(|w| w.0 = descriptors.as_ptr() as u32); - }; + ETH.ethernet_dma() + .dmardlar() + .write(|w| w.0 = descriptors.as_ptr() as u32); // We already have fences in `set_owned`, which is called in `setup` Self { @@ -162,12 +159,12 @@ impl<'a> RDesRing<'a> { } pub(crate) fn demand_poll(&self) { - unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) }; + ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)); } /// Get current `RunningState` fn running_state(&self) -> RunningState { - match unsafe { ETH.ethernet_dma().dmasr().read().rps() } { + match ETH.ethernet_dma().dmasr().read().rps() { // Reset or Stop Receive Command issued Rps::STOPPED => RunningState::Stopped, // Fetching receive transfer descriptor diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index 0e63c5443..1317d20f4 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs @@ -120,12 +120,9 @@ impl<'a> TDesRing<'a> { } // Register txdescriptor start - // NOTE (unsafe) Used for atomic writes - unsafe { - ETH.ethernet_dma() - .dmatdlar() - .write(|w| w.0 = descriptors.as_ptr() as u32); - } + ETH.ethernet_dma() + .dmatdlar() + .write(|w| w.0 = descriptors.as_ptr() as u32); Self { descriptors, @@ -169,6 +166,6 @@ impl<'a> TDesRing<'a> { self.index = 0 } // Request the DMA engine to poll the latest tx descriptor - unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) } + ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) } } diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index 2426596fb..e9799adf1 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs @@ -73,14 +73,10 @@ impl<'a> TDesRing<'a> { // Initialize the pointers in the DMA engine. (There will be a memory barrier later // before the DMA engine is enabled.) - // NOTE (unsafe) Used for atomic writes - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); - dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1)); - dma.dmactx_dtpr().write(|w| w.0 = 0); - } + let dma = ETH.ethernet_dma(); + dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); + dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1)); + dma.dmactx_dtpr().write(|w| w.0 = 0); Self { descriptors, @@ -129,8 +125,7 @@ impl<'a> TDesRing<'a> { } // signal DMA it can try again. - // NOTE(unsafe) Atomic write - unsafe { ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) } + ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) } } @@ -199,13 +194,10 @@ impl<'a> RDesRing<'a> { desc.set_ready(buffers[i].0.as_mut_ptr()); } - unsafe { - let dma = ETH.ethernet_dma(); - - dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); - dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1)); - dma.dmacrx_dtpr().write(|w| w.0 = 0); - } + let dma = ETH.ethernet_dma(); + dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32); + dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1)); + dma.dmacrx_dtpr().write(|w| w.0 = 0); Self { descriptors, @@ -254,8 +246,7 @@ impl<'a> RDesRing<'a> { fence(Ordering::Release); // signal DMA it can try again. - // NOTE(unsafe) Atomic write - unsafe { ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0) } + ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0); // Increment index. self.index += 1; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 3e45eafd5..600e1d3bc 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -20,18 +20,16 @@ impl interrupt::typelevel::Handler for InterruptHandl WAKER.wake(); // TODO: Check and clear more flags - unsafe { - let dma = ETH.ethernet_dma(); + let dma = ETH.ethernet_dma(); - dma.dmacsr().modify(|w| { - w.set_ti(true); - w.set_ri(true); - w.set_nis(true); - }); - // Delay two peripheral's clock - dma.dmacsr().read(); - dma.dmacsr().read(); - } + dma.dmacsr().modify(|w| { + w.set_ti(true); + w.set_ri(true); + w.set_nis(true); + }); + // Delay two peripheral's clock + dma.dmacsr().read(); + dma.dmacsr().read(); } } @@ -50,7 +48,6 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { macro_rules! config_pins { ($($pin:ident),*) => { - // NOTE(unsafe) Exclusive access to the registers critical_section::with(|_| { $( $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); @@ -80,239 +77,225 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { ) -> Self { into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - unsafe { - // Enable the necessary Clocks - // NOTE(unsafe) We have exclusive access to the registers - #[cfg(not(rcc_h5))] - critical_section::with(|_| { - crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true)); - crate::pac::RCC.ahb1enr().modify(|w| { - w.set_eth1macen(true); - w.set_eth1txen(true); - w.set_eth1rxen(true); - }); - - // RMII - crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); + // Enable the necessary Clocks + #[cfg(not(rcc_h5))] + critical_section::with(|_| { + crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true)); + crate::pac::RCC.ahb1enr().modify(|w| { + w.set_eth1macen(true); + w.set_eth1txen(true); + w.set_eth1rxen(true); }); - #[cfg(rcc_h5)] - critical_section::with(|_| { - crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true)); + // RMII + crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100)); + }); - crate::pac::RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); + #[cfg(rcc_h5)] + critical_section::with(|_| { + crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true)); - // RMII - crate::pac::SBS - .pmcr() - .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4)); + crate::pac::RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); }); - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + // RMII + crate::pac::SBS + .pmcr() + .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4)); + }); - // NOTE(unsafe) We have exclusive access to the registers - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); + config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - // Reset and wait - dma.dmamr().modify(|w| w.set_swr(true)); - while dma.dmamr().read().swr() {} + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); + let mtl = ETH.ethernet_mtl(); - mac.maccr().modify(|w| { - w.set_ipg(0b000); // 96 bit times - w.set_acs(true); - w.set_fes(true); - w.set_dm(true); - // TODO: Carrier sense ? ECRSFD - }); + // Reset and wait + dma.dmamr().modify(|w| w.set_swr(true)); + while dma.dmamr().read().swr() {} - // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, - // so the LR write must happen after the HR write. - mac.maca0hr() - .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); - mac.maca0lr().write(|w| { - w.set_addrlo( - u32::from(mac_addr[0]) - | (u32::from(mac_addr[1]) << 8) - | (u32::from(mac_addr[2]) << 16) - | (u32::from(mac_addr[3]) << 24), - ) - }); + mac.maccr().modify(|w| { + w.set_ipg(0b000); // 96 bit times + w.set_acs(true); + w.set_fes(true); + w.set_dm(true); + // TODO: Carrier sense ? ECRSFD + }); - mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, + // so the LR write must happen after the HR write. + mac.maca0hr() + .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8))); + mac.maca0lr().write(|w| { + w.set_addrlo( + u32::from(mac_addr[0]) + | (u32::from(mac_addr[1]) << 8) + | (u32::from(mac_addr[2]) << 16) + | (u32::from(mac_addr[3]) << 24), + ) + }); - // 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) - }); + mac.macqtx_fcr().modify(|w| w.set_pt(0x100)); - // 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); - }); + // 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) + }); - mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); - mtl.mtltx_qomr().modify(|w| w.set_tsf(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); + }); - dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? - dma.dmacrx_cr().modify(|w| { - w.set_rxpbl(1); // 32 ? - w.set_rbsz(MTU as u16); - }); + mtl.mtlrx_qomr().modify(|w| w.set_rsf(true)); + mtl.mtltx_qomr().modify(|w| w.set_tsf(true)); - // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called - let hclk = crate::rcc::get_freqs().ahb1; - let hclk_mhz = hclk.0 / 1_000_000; + dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ? + dma.dmacrx_cr().modify(|w| { + w.set_rxpbl(1); // 32 ? + w.set_rbsz(MTU as u16); + }); - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=34 => 2, // Divide by 16 - 35..=59 => 3, // Divide by 26 - 60..=99 => 0, // Divide by 42 - 100..=149 => 1, // Divide by 62 - 150..=249 => 4, // Divide by 102 - 250..=310 => 5, // Divide by 124 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") - } - }; + // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called + let hclk = unsafe { crate::rcc::get_freqs() }.ahb1; + let hclk_mhz = hclk.0 / 1_000_000; - let pins = [ - ref_clk.map_into(), - mdio.map_into(), - mdc.map_into(), - crs.map_into(), - rx_d0.map_into(), - rx_d1.map_into(), - tx_d0.map_into(), - tx_d1.map_into(), - tx_en.map_into(), - ]; + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=34 => 2, // Divide by 16 + 35..=59 => 3, // Divide by 26 + 60..=99 => 0, // Divide by 42 + 100..=149 => 1, // Divide by 62 + 150..=249 => 4, // Divide by 102 + 250..=310 => 5, // Divide by 124 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; - let mut this = Self { - _peri: peri, - tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), - rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), - pins, - _phy: phy, - clock_range, - phy_addr, - mac_addr, - }; + let pins = [ + ref_clk.map_into(), + mdio.map_into(), + mdc.map_into(), + crs.map_into(), + rx_d0.map_into(), + rx_d1.map_into(), + tx_d0.map_into(), + tx_d1.map_into(), + tx_en.map_into(), + ]; - fence(Ordering::SeqCst); + let mut this = Self { + _peri: peri, + tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), + rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), + pins, + _phy: phy, + clock_range, + phy_addr, + mac_addr, + }; - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); - let dma = ETH.ethernet_dma(); + fence(Ordering::SeqCst); - mac.maccr().modify(|w| { - w.set_re(true); - w.set_te(true); - }); - mtl.mtltx_qomr().modify(|w| w.set_ftq(true)); + let mac = ETH.ethernet_mac(); + let mtl = ETH.ethernet_mtl(); + let dma = ETH.ethernet_dma(); - dma.dmactx_cr().modify(|w| w.set_st(true)); - dma.dmacrx_cr().modify(|w| w.set_sr(true)); + mac.maccr().modify(|w| { + w.set_re(true); + w.set_te(true); + }); + mtl.mtltx_qomr().modify(|w| w.set_ftq(true)); - // Enable interrupts - dma.dmacier().modify(|w| { - w.set_nie(true); - w.set_rie(true); - w.set_tie(true); - }); + dma.dmactx_cr().modify(|w| w.set_st(true)); + dma.dmacrx_cr().modify(|w| w.set_sr(true)); - P::phy_reset(&mut this); - P::phy_init(&mut this); + // Enable interrupts + dma.dmacier().modify(|w| { + w.set_nie(true); + w.set_rie(true); + w.set_tie(true); + }); - interrupt::ETH.unpend(); - interrupt::ETH.enable(); + P::phy_reset(&mut this); + P::phy_init(&mut this); - this - } + interrupt::ETH.unpend(); + unsafe { interrupt::ETH.enable() }; + + this } } unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { fn smi_read(&mut self, reg: u8) -> u16 { - // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` - unsafe { - let mac = ETH.ethernet_mac(); + let mac = ETH.ethernet_mac(); - mac.macmdioar().modify(|w| { - w.set_pa(self.phy_addr); - w.set_rda(reg); - w.set_goc(0b11); // read - w.set_cr(self.clock_range); - w.set_mb(true); - }); - while mac.macmdioar().read().mb() {} - mac.macmdiodr().read().md() - } + mac.macmdioar().modify(|w| { + w.set_pa(self.phy_addr); + w.set_rda(reg); + w.set_goc(0b11); // read + w.set_cr(self.clock_range); + w.set_mb(true); + }); + while mac.macmdioar().read().mb() {} + mac.macmdiodr().read().md() } fn smi_write(&mut self, reg: u8, val: u16) { - // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self` - unsafe { - let mac = ETH.ethernet_mac(); + let mac = ETH.ethernet_mac(); - mac.macmdiodr().write(|w| w.set_md(val)); - mac.macmdioar().modify(|w| { - w.set_pa(self.phy_addr); - w.set_rda(reg); - w.set_goc(0b01); // write - w.set_cr(self.clock_range); - w.set_mb(true); - }); - while mac.macmdioar().read().mb() {} - } + mac.macmdiodr().write(|w| w.set_md(val)); + mac.macmdioar().modify(|w| { + w.set_pa(self.phy_addr); + w.set_rda(reg); + w.set_goc(0b01); // write + w.set_cr(self.clock_range); + w.set_mb(true); + }); + while mac.macmdioar().read().mb() {} } } impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { - // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers - unsafe { - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); + let dma = ETH.ethernet_dma(); + let mac = ETH.ethernet_mac(); + let mtl = ETH.ethernet_mtl(); - // Disable the TX DMA and wait for any previous transmissions to be completed - dma.dmactx_cr().modify(|w| w.set_st(false)); - while { - let txqueue = mtl.mtltx_qdr().read(); - txqueue.trcsts() == 0b01 || txqueue.txqsts() - } {} + // Disable the TX DMA and wait for any previous transmissions to be completed + dma.dmactx_cr().modify(|w| w.set_st(false)); + while { + let txqueue = mtl.mtltx_qdr().read(); + txqueue.trcsts() == 0b01 || txqueue.txqsts() + } {} - // Disable MAC transmitter and receiver - mac.maccr().modify(|w| { - w.set_re(false); - w.set_te(false); - }); + // Disable MAC transmitter and receiver + mac.maccr().modify(|w| { + w.set_re(false); + w.set_te(false); + }); - // Wait for previous receiver transfers to be completed and then disable the RX DMA - while { - let rxqueue = mtl.mtlrx_qdr().read(); - rxqueue.rxqsts() != 0b00 || rxqueue.prxq() != 0 - } {} - dma.dmacrx_cr().modify(|w| w.set_sr(false)); - } + // Wait for previous receiver transfers to be completed and then disable the RX DMA + while { + let rxqueue = mtl.mtlrx_qdr().read(); + rxqueue.rxqsts() != 0b00 || rxqueue.prxq() != 0 + } {} + dma.dmacrx_cr().modify(|w| w.set_sr(false)); - // NOTE(unsafe) Exclusive access to the regs - critical_section::with(|_| unsafe { + critical_section::with(|_| { for pin in self.pins.iter_mut() { pin.set_as_disconnected(); } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 0631ae473..3ff92c9e6 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -206,7 +206,7 @@ struct ExtiInputFuture<'a> { impl<'a> ExtiInputFuture<'a> { fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self { - critical_section::with(|_| unsafe { + critical_section::with(|_| { let pin = pin as usize; exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); EXTI.rtsr(0).modify(|w| w.set_line(pin, rising)); @@ -233,7 +233,7 @@ impl<'a> ExtiInputFuture<'a> { impl<'a> Drop for ExtiInputFuture<'a> { fn drop(&mut self) { - critical_section::with(|_| unsafe { + critical_section::with(|_| { let pin = self.pin as _; cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); }); @@ -246,7 +246,7 @@ impl<'a> Future for ExtiInputFuture<'a> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { EXTI_WAKERS[self.pin as usize].register(cx.waker()); - let imr = unsafe { cpu_regs().imr(0).read() }; + let imr = cpu_regs().imr(0).read(); if !imr.line(self.pin as _) { Poll::Ready(()) } else { diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 5e1fc696f..242d99278 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -192,7 +192,7 @@ impl FlashSector { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] pub(crate) fn is_default_layout() -> bool { - unsafe { !pac::FLASH.optcr().read().db1m() } + !pac::FLASH.optcr().read().db1m() } #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] @@ -336,7 +336,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E ret } -pub(crate) unsafe fn clear_all_err() { +pub(crate) fn clear_all_err() { pac::FLASH.sr().write(|w| { w.set_pgserr(true); w.set_pgperr(true); @@ -345,7 +345,7 @@ pub(crate) unsafe fn clear_all_err() { }); } -pub(crate) async unsafe fn wait_ready() -> Result<(), Error> { +pub(crate) async fn wait_ready() -> Result<(), Error> { use core::task::Poll; use futures::future::poll_fn; @@ -391,10 +391,10 @@ fn save_data_cache_state() { let dual_bank = get_flash_regions().last().unwrap().bank == FlashBank::Bank2; if dual_bank { // Disable data cache during write/erase if there are two banks, see errata 2.2.12 - let dcen = unsafe { pac::FLASH.acr().read().dcen() }; + let dcen = pac::FLASH.acr().read().dcen(); DATA_CACHE_WAS_ENABLED.store(dcen, Ordering::Relaxed); if dcen { - unsafe { pac::FLASH.acr().modify(|w| w.set_dcen(false)) }; + pac::FLASH.acr().modify(|w| w.set_dcen(false)); } } } @@ -405,12 +405,10 @@ fn restore_data_cache_state() { // Restore data cache if it was enabled let dcen = DATA_CACHE_WAS_ENABLED.load(Ordering::Relaxed); if dcen { - unsafe { - // Reset data cache before we enable it again - pac::FLASH.acr().modify(|w| w.set_dcrst(true)); - pac::FLASH.acr().modify(|w| w.set_dcrst(false)); - pac::FLASH.acr().modify(|w| w.set_dcen(true)) - }; + // Reset data cache before we enable it again + pac::FLASH.acr().modify(|w| w.set_dcrst(true)); + pac::FLASH.acr().modify(|w| w.set_dcrst(false)); + pac::FLASH.acr().modify(|w| w.set_dcen(true)) } } } @@ -445,7 +443,7 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { feature = "stm32f439vi", feature = "stm32f439zi", ))] - if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } { + if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11"); } @@ -479,11 +477,9 @@ fn pa12_is_output_pull_low() -> bool { use pac::gpio::vals; use pac::GPIOA; const PIN: usize = 12; - unsafe { - GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT - && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULLDOWN - && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW - } + GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT + && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULLDOWN + && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW } #[cfg(test)] diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index b9129cb51..a4f3b9686 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -16,7 +16,7 @@ unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T> where T: Instance, { - const REGISTERS: *const () = T::REGS.0 as *const _; + const REGISTERS: *const () = T::REGS.as_ptr() as *const _; fn enable(&mut self) { ::enable(); @@ -28,9 +28,7 @@ where // fsmc v1, v2 and v3 does not have the fmcen bit // This is a "not" because it is expected that all future versions have this bit #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))] - unsafe { - T::REGS.bcr1().modify(|r| r.set_fmcen(true)) - }; + T::REGS.bcr1().modify(|r| r.set_fmcen(true)); } fn source_clock_hz(&self) -> u32 { @@ -67,7 +65,7 @@ macro_rules! fmc_sdram_constructor { chip: CHIP ) -> stm32_fmc::Sdram, CHIP> { - critical_section::with(|_| unsafe { + critical_section::with(|_| { config_pins!( $($addr_pin_name),*, $($ba_pin_name),*, diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 7a066a4ca..af3a8eaca 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -46,7 +46,7 @@ impl<'d, T: Pin> Flex<'d, T> { /// Put the pin into input mode. #[inline] pub fn set_as_input(&mut self, pull: Pull) { - critical_section::with(|_| unsafe { + critical_section::with(|_| { let r = self.pin.block(); let n = self.pin.pin() as usize; #[cfg(gpio_v1)] @@ -84,7 +84,7 @@ impl<'d, T: Pin> Flex<'d, T> { /// at a specific level, call `set_high`/`set_low` on the pin first. #[inline] pub fn set_as_output(&mut self, speed: Speed) { - critical_section::with(|_| unsafe { + critical_section::with(|_| { let r = self.pin.block(); let n = self.pin.pin() as usize; #[cfg(gpio_v1)] @@ -116,7 +116,7 @@ impl<'d, T: Pin> Flex<'d, T> { /// at a specific level, call `set_high`/`set_low` on the pin first. #[inline] pub fn set_as_input_output(&mut self, speed: Speed, pull: Pull) { - critical_section::with(|_| unsafe { + critical_section::with(|_| { let r = self.pin.block(); let n = self.pin.pin() as usize; #[cfg(gpio_v1)] @@ -147,7 +147,7 @@ impl<'d, T: Pin> Flex<'d, T> { #[inline] pub fn is_low(&self) -> bool { - let state = unsafe { self.pin.block().idr().read().idr(self.pin.pin() as _) }; + let state = self.pin.block().idr().read().idr(self.pin.pin() as _); state == vals::Idr::LOW } @@ -164,7 +164,7 @@ impl<'d, T: Pin> Flex<'d, T> { /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { - let state = unsafe { self.pin.block().odr().read().odr(self.pin.pin() as _) }; + let state = self.pin.block().odr().read().odr(self.pin.pin() as _); state == vals::Odr::LOW } @@ -207,7 +207,7 @@ impl<'d, T: Pin> Flex<'d, T> { impl<'d, T: Pin> Drop for Flex<'d, T> { #[inline] fn drop(&mut self) { - critical_section::with(|_| unsafe { + critical_section::with(|_| { let r = self.pin.block(); let n = self.pin.pin() as usize; #[cfg(gpio_v1)] @@ -534,29 +534,25 @@ pub(crate) mod sealed { /// Set the output as high. #[inline] fn set_high(&self) { - unsafe { - let n = self._pin() as _; - self.block().bsrr().write(|w| w.set_bs(n, true)); - } + let n = self._pin() as _; + self.block().bsrr().write(|w| w.set_bs(n, true)); } /// Set the output as low. #[inline] fn set_low(&self) { - unsafe { - let n = self._pin() as _; - self.block().bsrr().write(|w| w.set_br(n, true)); - } + let n = self._pin() as _; + self.block().bsrr().write(|w| w.set_br(n, true)); } #[inline] - unsafe fn set_as_af(&self, af_num: u8, af_type: AFType) { + fn set_as_af(&self, af_num: u8, af_type: AFType) { self.set_as_af_pull(af_num, af_type, Pull::None); } #[cfg(gpio_v1)] #[inline] - unsafe fn set_as_af_pull(&self, _af_num: u8, af_type: AFType, pull: Pull) { + fn set_as_af_pull(&self, _af_num: u8, af_type: AFType, pull: Pull) { // F1 uses the AFIO register for remapping. // For now, this is not implemented, so af_num is ignored // _af_num should be zero here, since it is not set by stm32-data @@ -599,7 +595,7 @@ pub(crate) mod sealed { #[cfg(gpio_v2)] #[inline] - unsafe fn set_as_af_pull(&self, af_num: u8, af_type: AFType, pull: Pull) { + fn set_as_af_pull(&self, af_num: u8, af_type: AFType, pull: Pull) { let pin = self._pin() as usize; let block = self.block(); block.afr(pin / 8).modify(|w| w.set_afr(pin % 8, af_num)); @@ -614,7 +610,7 @@ pub(crate) mod sealed { } #[inline] - unsafe fn set_as_analog(&self) { + fn set_as_analog(&self) { let pin = self._pin() as usize; let block = self.block(); #[cfg(gpio_v1)] @@ -635,12 +631,12 @@ pub(crate) mod sealed { /// This is currently the same as set_as_analog but is semantically different really. /// Drivers should set_as_disconnected pins when dropped. #[inline] - unsafe fn set_as_disconnected(&self) { + fn set_as_disconnected(&self) { self.set_as_analog(); } #[inline] - unsafe fn set_speed(&self, speed: Speed) { + fn set_speed(&self, speed: Speed) { let pin = self._pin() as usize; #[cfg(gpio_v1)] diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index e04038886..aa485cd86 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -68,53 +68,45 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::enable(); T::reset(); - unsafe { - scl.set_as_af_pull( - scl.af_num(), - AFType::OutputOpenDrain, - match config.scl_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - sda.set_as_af_pull( - sda.af_num(), - AFType::OutputOpenDrain, - match config.sda_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - } + scl.set_as_af_pull( + scl.af_num(), + AFType::OutputOpenDrain, + match config.scl_pullup { + true => Pull::Up, + false => Pull::None, + }, + ); + sda.set_as_af_pull( + sda.af_num(), + AFType::OutputOpenDrain, + match config.sda_pullup { + true => Pull::Up, + false => Pull::None, + }, + ); - unsafe { - T::regs().cr1().modify(|reg| { - reg.set_pe(false); - //reg.set_anfoff(false); - }); - } + T::regs().cr1().modify(|reg| { + reg.set_pe(false); + //reg.set_anfoff(false); + }); let timings = Timings::new(T::frequency(), freq.into()); - unsafe { - T::regs().cr2().modify(|reg| { - reg.set_freq(timings.freq); - }); - T::regs().ccr().modify(|reg| { - reg.set_f_s(timings.mode.f_s()); - reg.set_duty(timings.duty.duty()); - reg.set_ccr(timings.ccr); - }); - T::regs().trise().modify(|reg| { - reg.set_trise(timings.trise); - }); - } + T::regs().cr2().modify(|reg| { + reg.set_freq(timings.freq); + }); + T::regs().ccr().modify(|reg| { + reg.set_f_s(timings.mode.f_s()); + reg.set_duty(timings.duty.duty()); + reg.set_ccr(timings.ccr); + }); + T::regs().trise().modify(|reg| { + reg.set_trise(timings.trise); + }); - unsafe { - T::regs().cr1().modify(|reg| { - reg.set_pe(true); - }); - } + T::regs().cr1().modify(|reg| { + reg.set_pe(true); + }); Self { phantom: PhantomData, @@ -123,7 +115,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } - unsafe fn check_and_clear_error_flags(&self) -> Result { + fn check_and_clear_error_flags(&self) -> Result { // Note that flags should only be cleared once they have been registered. If flags are // cleared otherwise, there may be an inherent race condition and flags may be missed. let sr1 = T::regs().sr1().read(); @@ -162,7 +154,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(sr1) } - unsafe fn write_bytes( + fn write_bytes( &mut self, addr: u8, bytes: &[u8], @@ -211,7 +203,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { + fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { // Wait until we're ready for sending while { // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set. @@ -234,7 +226,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result { + fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result { while { // Check for any potential error conditions. self.check_and_clear_error_flags()?; @@ -256,56 +248,52 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { ) -> Result<(), Error> { if let Some((last, buffer)) = buffer.split_last_mut() { // Send a START condition and set ACK bit - unsafe { - T::regs().cr1().modify(|reg| { - reg.set_start(true); - reg.set_ack(true); - }); - } + T::regs().cr1().modify(|reg| { + reg.set_start(true); + reg.set_ack(true); + }); // Wait until START condition was generated - while unsafe { !self.check_and_clear_error_flags()?.start() } { + while !self.check_and_clear_error_flags()?.start() { check_timeout()?; } // Also wait until signalled we're master and everything is waiting for us while { - let sr2 = unsafe { T::regs().sr2().read() }; + let sr2 = T::regs().sr2().read(); !sr2.msl() && !sr2.busy() } { check_timeout()?; } // Set up current address, we're trying to talk to - unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) } + T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)); // Wait until address was sent // Wait for the address to be acknowledged - while unsafe { !self.check_and_clear_error_flags()?.addr() } { + while !self.check_and_clear_error_flags()?.addr() { check_timeout()?; } // Clear condition by reading SR2 - let _ = unsafe { T::regs().sr2().read() }; + let _ = T::regs().sr2().read(); // Receive bytes into buffer for c in buffer { - *c = unsafe { self.recv_byte(&check_timeout)? }; + *c = self.recv_byte(&check_timeout)?; } // Prepare to send NACK then STOP after next byte - unsafe { - T::regs().cr1().modify(|reg| { - reg.set_ack(false); - reg.set_stop(true); - }) - } + T::regs().cr1().modify(|reg| { + reg.set_ack(false); + reg.set_stop(true); + }); // Receive last byte - *last = unsafe { self.recv_byte(&check_timeout)? }; + *last = self.recv_byte(&check_timeout)?; // Wait for the STOP to be sent. - while unsafe { T::regs().cr1().read().stop() } { + while T::regs().cr1().read().stop() { check_timeout()?; } @@ -326,15 +314,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { write: &[u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - unsafe { - self.write_bytes(addr, write, &check_timeout)?; - // Send a STOP condition - T::regs().cr1().modify(|reg| reg.set_stop(true)); - // Wait for STOP condition to transmit. - while T::regs().cr1().read().stop() { - check_timeout()?; - } - }; + self.write_bytes(addr, write, &check_timeout)?; + // Send a STOP condition + T::regs().cr1().modify(|reg| reg.set_stop(true)); + // Wait for STOP condition to transmit. + while T::regs().cr1().read().stop() { + check_timeout()?; + } // Fallthrough is success Ok(()) @@ -351,7 +337,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { read: &mut [u8], check_timeout: impl Fn() -> Result<(), Error>, ) -> Result<(), Error> { - unsafe { self.write_bytes(addr, write, &check_timeout)? }; + self.write_bytes(addr, write, &check_timeout)?; self.blocking_read_timeout(addr, read, &check_timeout)?; Ok(()) @@ -478,8 +464,6 @@ impl Timings { assert!(freq >= 2 && freq <= 50); // Configure bus frequency into I2C peripheral - //self.i2c.cr2.write(|w| unsafe { w.freq().bits(freq as u8) }); - let trise = if speed <= 100_000 { freq + 1 } else { @@ -539,18 +523,16 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> { type Config = Hertz; fn set_config(&mut self, config: &Self::Config) { let timings = Timings::new(T::frequency(), *config); - unsafe { - T::regs().cr2().modify(|reg| { - reg.set_freq(timings.freq); - }); - T::regs().ccr().modify(|reg| { - reg.set_f_s(timings.mode.f_s()); - reg.set_duty(timings.duty.duty()); - reg.set_ccr(timings.ccr); - }); - T::regs().trise().modify(|reg| { - reg.set_trise(timings.trise); - }); - } + T::regs().cr2().modify(|reg| { + reg.set_freq(timings.freq); + }); + T::regs().ccr().modify(|reg| { + reg.set_f_s(timings.mode.f_s()); + reg.set_duty(timings.duty.duty()); + reg.set_ccr(timings.ccr); + }); + T::regs().trise().modify(|reg| { + reg.set_trise(timings.trise); + }); } } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 1aaf2b46b..1f036d55c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -89,49 +89,41 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::enable(); T::reset(); - unsafe { - scl.set_as_af_pull( - scl.af_num(), - AFType::OutputOpenDrain, - match config.scl_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - sda.set_as_af_pull( - sda.af_num(), - AFType::OutputOpenDrain, - match config.sda_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - } + scl.set_as_af_pull( + scl.af_num(), + AFType::OutputOpenDrain, + match config.scl_pullup { + true => Pull::Up, + false => Pull::None, + }, + ); + sda.set_as_af_pull( + sda.af_num(), + AFType::OutputOpenDrain, + match config.sda_pullup { + true => Pull::Up, + false => Pull::None, + }, + ); - unsafe { - T::regs().cr1().modify(|reg| { - reg.set_pe(false); - reg.set_anfoff(false); - }); - } + T::regs().cr1().modify(|reg| { + reg.set_pe(false); + reg.set_anfoff(false); + }); let timings = Timings::new(T::frequency(), freq.into()); - unsafe { - T::regs().timingr().write(|reg| { - reg.set_presc(timings.prescale); - reg.set_scll(timings.scll); - reg.set_sclh(timings.sclh); - reg.set_sdadel(timings.sdadel); - reg.set_scldel(timings.scldel); - }); - } + T::regs().timingr().write(|reg| { + reg.set_presc(timings.prescale); + reg.set_scll(timings.scll); + reg.set_sclh(timings.sclh); + reg.set_sdadel(timings.sdadel); + reg.set_scldel(timings.scldel); + }); - unsafe { - T::regs().cr1().modify(|reg| { - reg.set_pe(true); - }); - } + T::regs().cr1().modify(|reg| { + reg.set_pe(true); + }); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -144,12 +136,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } fn master_stop(&mut self) { - unsafe { - T::regs().cr2().write(|w| w.set_stop(true)); - } + T::regs().cr2().write(|w| w.set_stop(true)); } - unsafe fn master_read( + fn master_read( address: u8, length: usize, stop: Stop, @@ -191,7 +181,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_write( + fn master_write( address: u8, length: usize, stop: Stop, @@ -229,7 +219,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(()) } - unsafe fn master_continue( + fn master_continue( length: usize, reload: bool, check_timeout: impl Fn() -> Result<(), Error>, @@ -259,13 +249,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { //$i2c.txdr.write(|w| w.txdata().bits(0)); //} - unsafe { - if T::regs().isr().read().txis() { - T::regs().txdr().write(|w| w.set_txdata(0)); - } - if !T::regs().isr().read().txe() { - T::regs().isr().modify(|w| w.set_txe(true)) - } + if T::regs().isr().read().txis() { + T::regs().txdr().write(|w| w.set_txdata(0)); + } + if !T::regs().isr().read().txe() { + T::regs().isr().modify(|w| w.set_txe(true)) } // If TXDR is not flagged as empty, write 1 to flush it @@ -276,21 +264,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { - unsafe { - let isr = T::regs().isr().read(); - if isr.txe() { - return Ok(()); - } else if isr.berr() { - T::regs().icr().write(|reg| reg.set_berrcf(true)); - return Err(Error::Bus); - } else if isr.arlo() { - T::regs().icr().write(|reg| reg.set_arlocf(true)); - return Err(Error::Arbitration); - } else if isr.nackf() { - T::regs().icr().write(|reg| reg.set_nackcf(true)); - self.flush_txdr(); - return Err(Error::Nack); - } + let isr = T::regs().isr().read(); + if isr.txe() { + return Ok(()); + } else if isr.berr() { + T::regs().icr().write(|reg| reg.set_berrcf(true)); + return Err(Error::Bus); + } else if isr.arlo() { + T::regs().icr().write(|reg| reg.set_arlocf(true)); + return Err(Error::Arbitration); + } else if isr.nackf() { + T::regs().icr().write(|reg| reg.set_nackcf(true)); + self.flush_txdr(); + return Err(Error::Nack); } check_timeout()?; @@ -299,21 +285,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { - unsafe { - let isr = T::regs().isr().read(); - if isr.rxne() { - return Ok(()); - } else if isr.berr() { - T::regs().icr().write(|reg| reg.set_berrcf(true)); - return Err(Error::Bus); - } else if isr.arlo() { - T::regs().icr().write(|reg| reg.set_arlocf(true)); - return Err(Error::Arbitration); - } else if isr.nackf() { - T::regs().icr().write(|reg| reg.set_nackcf(true)); - self.flush_txdr(); - return Err(Error::Nack); - } + let isr = T::regs().isr().read(); + if isr.rxne() { + return Ok(()); + } else if isr.berr() { + T::regs().icr().write(|reg| reg.set_berrcf(true)); + return Err(Error::Bus); + } else if isr.arlo() { + T::regs().icr().write(|reg| reg.set_arlocf(true)); + return Err(Error::Arbitration); + } else if isr.nackf() { + T::regs().icr().write(|reg| reg.set_nackcf(true)); + self.flush_txdr(); + return Err(Error::Nack); } check_timeout()?; @@ -322,21 +306,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> { loop { - unsafe { - let isr = T::regs().isr().read(); - if isr.tc() { - return Ok(()); - } else if isr.berr() { - T::regs().icr().write(|reg| reg.set_berrcf(true)); - return Err(Error::Bus); - } else if isr.arlo() { - T::regs().icr().write(|reg| reg.set_arlocf(true)); - return Err(Error::Arbitration); - } else if isr.nackf() { - T::regs().icr().write(|reg| reg.set_nackcf(true)); - self.flush_txdr(); - return Err(Error::Nack); - } + let isr = T::regs().isr().read(); + if isr.tc() { + return Ok(()); + } else if isr.berr() { + T::regs().icr().write(|reg| reg.set_berrcf(true)); + return Err(Error::Bus); + } else if isr.arlo() { + T::regs().icr().write(|reg| reg.set_arlocf(true)); + return Err(Error::Arbitration); + } else if isr.nackf() { + T::regs().icr().write(|reg| reg.set_nackcf(true)); + self.flush_txdr(); + return Err(Error::Nack); } check_timeout()?; @@ -358,32 +340,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }; let last_chunk_idx = total_chunks.saturating_sub(1); - unsafe { - Self::master_read( - address, - read.len().min(255), - Stop::Automatic, - last_chunk_idx != 0, - restart, - &check_timeout, - )?; - } + Self::master_read( + address, + read.len().min(255), + Stop::Automatic, + last_chunk_idx != 0, + restart, + &check_timeout, + )?; for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { - // NOTE(unsafe) We have &mut self - unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; - } + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } for byte in chunk { // Wait until we have received something self.wait_rxne(&check_timeout)?; - unsafe { - *byte = T::regs().rxdr().read().rxdata(); - } + *byte = T::regs().rxdr().read().rxdata(); } } Ok(()) @@ -407,23 +382,17 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // I2C start // // ST SAD+W - // NOTE(unsafe) We have &mut self - unsafe { - Self::master_write( - address, - write.len().min(255), - Stop::Software, - last_chunk_idx != 0, - &check_timeout, - )?; - } + Self::master_write( + address, + write.len().min(255), + Stop::Software, + last_chunk_idx != 0, + &check_timeout, + )?; for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { - // NOTE(unsafe) We have &mut self - unsafe { - Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; - } + Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?; } for byte in chunk { @@ -432,9 +401,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // through) self.wait_txe(&check_timeout)?; - unsafe { - T::regs().txdr().write(|w| w.set_txdata(*byte)); - } + T::regs().txdr().write(|w| w.set_txdata(*byte)); } } // Wait until the write finishes @@ -467,7 +434,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_tcie(true); } }); - let dst = regs.txdr().ptr() as *mut u8; + let dst = regs.txdr().as_ptr() as *mut u8; let ch = &mut self.tx_dma; let request = ch.request(); @@ -479,37 +446,30 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let on_drop = OnDrop::new(|| { let regs = T::regs(); - unsafe { - regs.cr1().modify(|w| { - if last_slice { - w.set_txdmaen(false); - } - w.set_tcie(false); - }) - } + regs.cr1().modify(|w| { + if last_slice { + w.set_txdmaen(false); + } + w.set_tcie(false); + }) }); poll_fn(|cx| { state.waker.register(cx.waker()); - let isr = unsafe { T::regs().isr().read() }; + let isr = T::regs().isr().read(); if remaining_len == total_len { - // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers if first_slice { - unsafe { - Self::master_write( - address, - total_len.min(255), - Stop::Software, - (total_len > 255) || !last_slice, - &check_timeout, - )?; - } + Self::master_write( + address, + total_len.min(255), + Stop::Software, + (total_len > 255) || !last_slice, + &check_timeout, + )?; } else { - unsafe { - Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?; - T::regs().cr1().modify(|w| w.set_tcie(true)); - } + Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?; + T::regs().cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { // poll_fn was woken without an interrupt present @@ -519,13 +479,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } else { let last_piece = (remaining_len <= 255) && last_slice; - // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers - unsafe { - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { - return Poll::Ready(Err(e)); - } - T::regs().cr1().modify(|w| w.set_tcie(true)); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)); } + T::regs().cr1().modify(|w| w.set_tcie(true)); } remaining_len = remaining_len.saturating_sub(255); @@ -564,7 +521,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_rxdmaen(true); w.set_tcie(true); }); - let src = regs.rxdr().ptr() as *mut u8; + let src = regs.rxdr().as_ptr() as *mut u8; let ch = &mut self.rx_dma; let request = ch.request(); @@ -576,30 +533,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let on_drop = OnDrop::new(|| { let regs = T::regs(); - unsafe { - regs.cr1().modify(|w| { - w.set_rxdmaen(false); - w.set_tcie(false); - }) - } + regs.cr1().modify(|w| { + w.set_rxdmaen(false); + w.set_tcie(false); + }) }); poll_fn(|cx| { state.waker.register(cx.waker()); - let isr = unsafe { T::regs().isr().read() }; + let isr = T::regs().isr().read(); if remaining_len == total_len { - // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers - unsafe { - Self::master_read( - address, - total_len.min(255), - Stop::Software, - total_len > 255, - restart, - &check_timeout, - )?; - } + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_len > 255, + restart, + &check_timeout, + )?; } else if !(isr.tcr() || isr.tc()) { // poll_fn was woken without an interrupt present return Poll::Pending; @@ -608,13 +560,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } else { let last_piece = remaining_len <= 255; - // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers - unsafe { - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { - return Poll::Ready(Err(e)); - } - T::regs().cr1().modify(|w| w.set_tcie(true)); + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) { + return Poll::Ready(Err(e)); } + T::regs().cr1().modify(|w| w.set_tcie(true)); } remaining_len = remaining_len.saturating_sub(255); @@ -758,16 +707,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let first_length = write[0].len(); let last_slice_index = write.len() - 1; - // NOTE(unsafe) We have &mut self - unsafe { - Self::master_write( - address, - first_length.min(255), - Stop::Software, - (first_length > 255) || (last_slice_index != 0), - &check_timeout, - )?; - } + Self::master_write( + address, + first_length.min(255), + Stop::Software, + (first_length > 255) || (last_slice_index != 0), + &check_timeout, + )?; for (idx, slice) in write.iter().enumerate() { let slice_len = slice.len(); @@ -780,26 +726,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let last_chunk_idx = total_chunks.saturating_sub(1); if idx != 0 { - // NOTE(unsafe) We have &mut self - unsafe { - Self::master_continue( - slice_len.min(255), - (idx != last_slice_index) || (slice_len > 255), - &check_timeout, - )?; - } + Self::master_continue( + slice_len.min(255), + (idx != last_slice_index) || (slice_len > 255), + &check_timeout, + )?; } for (number, chunk) in slice.chunks(255).enumerate() { if number != 0 { - // NOTE(unsafe) We have &mut self - unsafe { - Self::master_continue( - chunk.len(), - (number != last_chunk_idx) || (idx != last_slice_index), - &check_timeout, - )?; - } + Self::master_continue( + chunk.len(), + (number != last_chunk_idx) || (idx != last_slice_index), + &check_timeout, + )?; } for byte in chunk { @@ -810,9 +750,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Put byte on the wire //self.i2c.txdr.write(|w| w.txdata().bits(*byte)); - unsafe { - T::regs().txdr().write(|w| w.set_txdata(*byte)); - } + T::regs().txdr().write(|w| w.set_txdata(*byte)); } } } @@ -1061,14 +999,12 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> { type Config = Hertz; fn set_config(&mut self, config: &Self::Config) { let timings = Timings::new(T::frequency(), *config); - unsafe { - T::regs().timingr().write(|reg| { - reg.set_presc(timings.prescale); - reg.set_scll(timings.scll); - reg.set_sclh(timings.sclh); - reg.set_sdadel(timings.sdadel); - reg.set_scldel(timings.scldel); - }); - } + T::regs().timingr().write(|reg| { + reg.set_presc(timings.prescale); + reg.set_scll(timings.scll); + reg.set_sclh(timings.sclh); + reg.set_sdadel(timings.sdadel); + reg.set_scldel(timings.scldel); + }); } } diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 2bb199f68..62dda69b4 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -153,19 +153,17 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { ) -> Self { into_ref!(sd, ws, ck, mck); - unsafe { - sd.set_as_af(sd.af_num(), AFType::OutputPushPull); - sd.set_speed(crate::gpio::Speed::VeryHigh); + sd.set_as_af(sd.af_num(), AFType::OutputPushPull); + sd.set_speed(crate::gpio::Speed::VeryHigh); - ws.set_as_af(ws.af_num(), AFType::OutputPushPull); - ws.set_speed(crate::gpio::Speed::VeryHigh); + ws.set_as_af(ws.af_num(), AFType::OutputPushPull); + ws.set_speed(crate::gpio::Speed::VeryHigh); - ck.set_as_af(ck.af_num(), AFType::OutputPushPull); - ck.set_speed(crate::gpio::Speed::VeryHigh); + ck.set_as_af(ck.af_num(), AFType::OutputPushPull); + ck.set_speed(crate::gpio::Speed::VeryHigh); - mck.set_as_af(mck.af_num(), AFType::OutputPushPull); - mck.set_speed(crate::gpio::Speed::VeryHigh); - } + mck.set_as_af(mck.af_num(), AFType::OutputPushPull); + mck.set_speed(crate::gpio::Speed::VeryHigh); let spi = Spi::new_internal(peri, txdma, rxdma, freq, SpiConfig::default()); @@ -178,7 +176,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format); #[cfg(any(spi_v1, spi_f1))] - unsafe { + { use stm32_metapac::spi::vals::{I2scfg, Odd}; // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR register to define the serial clock baud @@ -232,10 +230,6 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { w.set_i2se(true) }); } - #[cfg(spi_v2)] - unsafe {} - #[cfg(any(spi_v3, spi_v4))] - unsafe {} Self { _peri: spi, @@ -264,12 +258,10 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> { fn drop(&mut self) { - unsafe { - self.sd.as_ref().map(|x| x.set_as_disconnected()); - self.ws.as_ref().map(|x| x.set_as_disconnected()); - self.ck.as_ref().map(|x| x.set_as_disconnected()); - self.mck.as_ref().map(|x| x.set_as_disconnected()); - } + self.sd.as_ref().map(|x| x.set_as_disconnected()); + self.ws.as_ref().map(|x| x.set_as_disconnected()); + self.ck.as_ref().map(|x| x.set_as_disconnected()); + self.mck.as_ref().map(|x| x.set_as_disconnected()); } } diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 609c4d2c3..3062226c7 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -26,7 +26,7 @@ impl interrupt::typelevel::Handler for Receive ]; // Status register gives channel occupied status. For rx, use cpu1. - let sr = unsafe { regs.cpu(1).sr().read() }; + let sr = regs.cpu(1).sr().read(); regs.cpu(0).mr().modify(|w| { for channel in channels { if sr.chf(channel as usize) { @@ -57,7 +57,7 @@ impl interrupt::typelevel::Handler for Transmi ]; // Status register gives channel occupied status. For tx, use cpu0. - let sr = unsafe { regs.cpu(0).sr().read() }; + let sr = regs.cpu(0).sr().read(); regs.cpu(0).mr().modify(|w| { for channel in channels { if !sr.chf(channel as usize) { @@ -98,16 +98,14 @@ impl Ipcc { IPCC::reset(); IPCC::set_cpu2(true); - unsafe { _configure_pwr() }; + _configure_pwr(); let regs = IPCC::regs(); - unsafe { - regs.cpu(0).cr().modify(|w| { - w.set_rxoie(true); - w.set_txfie(true); - }) - } + regs.cpu(0).cr().modify(|w| { + w.set_rxoie(true); + w.set_txfie(true); + }); // enable interrupts crate::interrupt::typelevel::IPCC_C1_RX::unpend(); @@ -129,7 +127,7 @@ impl Ipcc { compiler_fence(Ordering::SeqCst); trace!("ipcc: ch {}: send data", channel as u8); - unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } + regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)); } /// Wait for the tx channel to become clear @@ -137,20 +135,20 @@ impl Ipcc { let regs = IPCC::regs(); // This is a race, but is nice for debugging - if unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) { + if regs.cpu(0).sr().read().chf(channel as usize) { trace!("ipcc: ch {}: wait for tx free", channel as u8); } poll_fn(|cx| { IPCC::state().tx_waker_for(channel).register(cx.waker()); // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt - unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)) } + regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)); compiler_fence(Ordering::SeqCst); - if !unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) { + if !regs.cpu(0).sr().read().chf(channel as usize) { // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt - unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) } + regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); Poll::Ready(()) } else { @@ -166,20 +164,20 @@ impl Ipcc { loop { // This is a race, but is nice for debugging - if !unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) { + if !regs.cpu(1).sr().read().chf(channel as usize) { trace!("ipcc: ch {}: wait for rx occupied", channel as u8); } poll_fn(|cx| { IPCC::state().rx_waker_for(channel).register(cx.waker()); // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt - unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)) } + regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)); compiler_fence(Ordering::SeqCst); - if unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) { + if regs.cpu(1).sr().read().chf(channel as usize) { // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt - unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) } + regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); Poll::Ready(()) } else { @@ -199,7 +197,7 @@ impl Ipcc { trace!("ipcc: ch {}: clear rx", channel as u8); compiler_fence(Ordering::SeqCst); // If the channel is clear and the read function returns none, fetch more data - unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } + regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)); } } } @@ -210,7 +208,7 @@ impl sealed::Instance for crate::peripherals::IPCC { } fn set_cpu2(enabled: bool) { - unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) } + crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)); } fn state() -> &'static self::sealed::State { @@ -269,7 +267,7 @@ pub(crate) mod sealed { } } -unsafe fn _configure_pwr() { +fn _configure_pwr() { // TODO: move this to RCC let pwr = crate::pac::PWR; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 6fde61c06..45a7b5476 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -146,35 +146,35 @@ impl Default for Config { pub fn init(config: Config) -> Peripherals { let p = Peripherals::take(); - unsafe { - #[cfg(dbgmcu)] - if config.enable_debug_during_sleep { - crate::pac::DBGMCU.cr().modify(|cr| { - #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))] - { - cr.set_dbg_stop(true); - cr.set_dbg_standby(true); - } - #[cfg(any( - dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, - dbgmcu_l4, dbgmcu_wb, dbgmcu_wl - ))] - { - cr.set_dbg_sleep(true); - cr.set_dbg_stop(true); - cr.set_dbg_standby(true); - } - #[cfg(dbgmcu_h7)] - { - cr.set_d1dbgcken(true); - cr.set_d3dbgcken(true); - cr.set_dbgsleep_d1(true); - cr.set_dbgstby_d1(true); - cr.set_dbgstop_d1(true); - } - }); - } + #[cfg(dbgmcu)] + if config.enable_debug_during_sleep { + crate::pac::DBGMCU.cr().modify(|cr| { + #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))] + { + cr.set_dbg_stop(true); + cr.set_dbg_standby(true); + } + #[cfg(any( + dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1, + dbgmcu_l4, dbgmcu_wb, dbgmcu_wl + ))] + { + cr.set_dbg_sleep(true); + cr.set_dbg_stop(true); + cr.set_dbg_standby(true); + } + #[cfg(dbgmcu_h7)] + { + cr.set_d1dbgcken(true); + cr.set_d3dbgcken(true); + cr.set_dbgsleep_d1(true); + cr.set_dbgstby_d1(true); + cr.set_dbgstop_d1(true); + } + }); + } + unsafe { gpio::init(); dma::init( #[cfg(bdma)] diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index cfb79947c..0e153202e 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -21,7 +21,7 @@ macro_rules! complementary_channel_impl { impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); - critical_section::with(|_| unsafe { + critical_section::with(|_| { pin.set_low(); pin.set_as_af(pin.af_num(), AFType::OutputPushPull); #[cfg(gpio_v2)] @@ -72,33 +72,27 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { this.inner.set_frequency(freq); this.inner.start(); - unsafe { - this.inner.enable_outputs(true); + this.inner.enable_outputs(true); - this.inner - .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); - } + this.inner + .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); this } pub fn enable(&mut self, channel: Channel) { - unsafe { - self.inner.enable_channel(channel, true); - self.inner.enable_complementary_channel(channel, true); - } + self.inner.enable_channel(channel, true); + self.inner.enable_complementary_channel(channel, true); } pub fn disable(&mut self, channel: Channel) { - unsafe { - self.inner.enable_complementary_channel(channel, false); - self.inner.enable_channel(channel, false); - } + self.inner.enable_complementary_channel(channel, false); + self.inner.enable_channel(channel, false); } pub fn set_freq(&mut self, freq: Hertz) { @@ -106,22 +100,20 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - unsafe { self.inner.get_max_compare_value() } + self.inner.get_max_compare_value() } pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty < self.get_max_duty()); - unsafe { self.inner.set_compare_value(channel, duty) } + self.inner.set_compare_value(channel, duty) } /// Set the dead time as a proportion of max_duty pub fn set_dead_time(&mut self, value: u16) { let (ckd, value) = compute_dead_time_value(value); - unsafe { - self.inner.set_dead_time_clock_division(ckd); - self.inner.set_dead_time_value(value); - } + self.inner.set_dead_time_clock_division(ckd); + self.inner.set_dead_time_value(value); } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 0bef07089..5aba2663e 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -59,33 +59,33 @@ pub(crate) mod sealed { pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { /// Global output enable. Does not do anything on non-advanced timers. - unsafe fn enable_outputs(&mut self, enable: bool); + fn enable_outputs(&mut self, enable: bool); - unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - unsafe fn enable_channel(&mut self, channel: Channel, enable: bool); + fn enable_channel(&mut self, channel: Channel, enable: bool); - unsafe fn set_compare_value(&mut self, channel: Channel, value: u16); + fn set_compare_value(&mut self, channel: Channel, value: u16); - unsafe fn get_max_compare_value(&self) -> u16; + fn get_max_compare_value(&self) -> u16; } pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { - unsafe fn set_dead_time_clock_division(&mut self, value: Ckd); + fn set_dead_time_clock_division(&mut self, value: Ckd); - unsafe fn set_dead_time_value(&mut self, value: u8); + fn set_dead_time_value(&mut self, value: u8); - unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); } pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { - unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - unsafe fn enable_channel(&mut self, channel: Channel, enable: bool); + fn enable_channel(&mut self, channel: Channel, enable: bool); - unsafe fn set_compare_value(&mut self, channel: Channel, value: u32); + fn set_compare_value(&mut self, channel: Channel, value: u32); - unsafe fn get_max_compare_value(&self) -> u32; + fn get_max_compare_value(&self) -> u32; } } @@ -108,9 +108,9 @@ pub trait CaptureCompare32bitInstance: macro_rules! impl_compare_capable_16bit { ($inst:ident) => { impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - unsafe fn enable_outputs(&mut self, _enable: bool) {} + fn enable_outputs(&mut self, _enable: bool) {} - unsafe fn set_output_compare_mode(&mut self, channel: crate::pwm::Channel, mode: OutputCompareMode) { + fn set_output_compare_mode(&mut self, channel: crate::pwm::Channel, mode: OutputCompareMode) { use crate::timer::sealed::GeneralPurpose16bitInstance; let r = Self::regs_gp16(); let raw_channel: usize = channel.raw(); @@ -118,19 +118,19 @@ macro_rules! impl_compare_capable_16bit { .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } - unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) { + fn enable_channel(&mut self, channel: Channel, enable: bool) { use crate::timer::sealed::GeneralPurpose16bitInstance; Self::regs_gp16() .ccer() .modify(|w| w.set_cce(channel.raw(), enable)); } - unsafe fn set_compare_value(&mut self, channel: Channel, value: u16) { + fn set_compare_value(&mut self, channel: Channel, value: u16) { use crate::timer::sealed::GeneralPurpose16bitInstance; Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); } - unsafe fn get_max_compare_value(&self) -> u16 { + fn get_max_compare_value(&self) -> u16 { use crate::timer::sealed::GeneralPurpose16bitInstance; Self::regs_gp16().arr().read().arr() } @@ -150,7 +150,7 @@ foreach_interrupt! { ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { impl_compare_capable_16bit!($inst); impl crate::pwm::sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { - unsafe fn set_output_compare_mode( + fn set_output_compare_mode( &mut self, channel: crate::pwm::Channel, mode: OutputCompareMode, @@ -160,17 +160,17 @@ foreach_interrupt! { Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } - unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) { + fn enable_channel(&mut self, channel: Channel, enable: bool) { use crate::timer::sealed::GeneralPurpose32bitInstance; Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); } - unsafe fn set_compare_value(&mut self, channel: Channel, value: u32) { + fn set_compare_value(&mut self, channel: Channel, value: u32) { use crate::timer::sealed::GeneralPurpose32bitInstance; Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); } - unsafe fn get_max_compare_value(&self) -> u32 { + fn get_max_compare_value(&self) -> u32 { use crate::timer::sealed::GeneralPurpose32bitInstance; Self::regs_gp32().arr().read().arr() as u32 } @@ -185,13 +185,13 @@ foreach_interrupt! { ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - unsafe fn enable_outputs(&mut self, enable: bool) { + fn enable_outputs(&mut self, enable: bool) { use crate::timer::sealed::AdvancedControlInstance; let r = Self::regs_advanced(); r.bdtr().modify(|w| w.set_moe(enable)); } - unsafe fn set_output_compare_mode( + fn set_output_compare_mode( &mut self, channel: crate::pwm::Channel, mode: OutputCompareMode, @@ -203,21 +203,21 @@ foreach_interrupt! { .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } - unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) { + fn enable_channel(&mut self, channel: Channel, enable: bool) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced() .ccer() .modify(|w| w.set_cce(channel.raw(), enable)); } - unsafe fn set_compare_value(&mut self, channel: Channel, value: u16) { + fn set_compare_value(&mut self, channel: Channel, value: u16) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced() .ccr(channel.raw()) .modify(|w| w.set_ccr(value)); } - unsafe fn get_max_compare_value(&self) -> u16 { + fn get_max_compare_value(&self) -> u16 { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced().arr().read().arr() } @@ -228,17 +228,17 @@ foreach_interrupt! { } impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { - unsafe fn set_dead_time_clock_division(&mut self, value: Ckd) { + fn set_dead_time_clock_division(&mut self, value: Ckd) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); } - unsafe fn set_dead_time_value(&mut self, value: u8) { + fn set_dead_time_value(&mut self, value: u8) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); } - unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced() .ccer() diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/pwm/simple_pwm.rs index b045a2d78..995f59c23 100644 --- a/embassy-stm32/src/pwm/simple_pwm.rs +++ b/embassy-stm32/src/pwm/simple_pwm.rs @@ -24,7 +24,7 @@ macro_rules! channel_impl { impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); - critical_section::with(|_| unsafe { + critical_section::with(|_| { pin.set_low(); pin.set_as_af(pin.af_num(), AFType::OutputPushPull); #[cfg(gpio_v2)] @@ -71,31 +71,25 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { this.inner.set_frequency(freq); this.inner.start(); - unsafe { - this.inner.enable_outputs(true); + this.inner.enable_outputs(true); - this.inner - .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); - this.inner - .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); - } + this.inner + .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + this.inner + .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); this } pub fn enable(&mut self, channel: Channel) { - unsafe { - self.inner.enable_channel(channel, true); - } + self.inner.enable_channel(channel, true); } pub fn disable(&mut self, channel: Channel) { - unsafe { - self.inner.enable_channel(channel, false); - } + self.inner.enable_channel(channel, false); } pub fn set_freq(&mut self, freq: Hertz) { @@ -103,11 +97,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - unsafe { self.inner.get_max_compare_value() } + self.inner.get_max_compare_value() } pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty < self.get_max_duty()); - unsafe { self.inner.set_compare_value(channel, duty) } + self.inner.set_compare_value(channel, duty) } } diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index c3126b37f..e9db934bf 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -96,20 +96,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { ) -> Self { into_ref!(peri, d0, d1, d2, d3, sck, nss); - unsafe { - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af(nss.af_num(), AFType::OutputPushPull); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af(d0.af_num(), AFType::OutputPushPull); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af(d1.af_num(), AFType::OutputPushPull); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af(d2.af_num(), AFType::OutputPushPull); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af(d3.af_num(), AFType::OutputPushPull); - d3.set_speed(crate::gpio::Speed::VeryHigh); - } + sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af(nss.af_num(), AFType::OutputPushPull); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af(d0.af_num(), AFType::OutputPushPull); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af(d1.af_num(), AFType::OutputPushPull); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af(d2.af_num(), AFType::OutputPushPull); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af(d3.af_num(), AFType::OutputPushPull); + d3.set_speed(crate::gpio::Speed::VeryHigh); Self::new_inner( peri, @@ -138,21 +136,19 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { into_ref!(peri, dma); T::enable(); - unsafe { - T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); + T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); - while T::REGS.sr().read().busy() {} + while T::REGS.sr().read().busy() {} - T::REGS.cr().write(|w| { - w.set_prescaler(config.prescaler); - w.set_en(true); - }); - T::REGS.dcr().write(|w| { - w.set_fsize(config.memory_size.into()); - w.set_csht(config.cs_high_time.into()); - w.set_ckmode(false); - }); - } + T::REGS.cr().write(|w| { + w.set_prescaler(config.prescaler); + w.set_en(true); + }); + T::REGS.dcr().write(|w| { + w.set_fsize(config.memory_size.into()); + w.set_csht(config.cs_high_time.into()); + w.set_ckmode(false); + }); Self { _peri: peri, @@ -168,148 +164,140 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } pub fn command(&mut self, transaction: TransferConfig) { - unsafe { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); } pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { - unsafe { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); - if let Some(len) = transaction.data_len { - let current_ar = T::REGS.ar().read().address(); - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); + if let Some(len) = transaction.data_len { + let current_ar = T::REGS.ar().read().address(); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); - for idx in 0..len { - while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = *(T::REGS.dr().ptr() as *mut u8); - } + for idx in 0..len { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); } pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { - unsafe { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); - if let Some(len) = transaction.data_len { - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); + if let Some(len) = transaction.data_len { + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); - for idx in 0..len { - while !T::REGS.sr().read().ftf() {} - *(T::REGS.dr().ptr() as *mut u8) = buf[idx]; - } + for idx in 0..len { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); } pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) where Dma: QuadDma, { - unsafe { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - let current_ar = T::REGS.ar().read().address(); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + let current_ar = T::REGS.ar().read().address(); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); - let request = self.dma.request(); - let transfer = Transfer::new_read( + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( &mut self.dma, request, - T::REGS.dr().ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut u8, buf, Default::default(), - ); + ) + }; - T::REGS.cr().modify(|v| v.set_dmaen(true)); + T::REGS.cr().modify(|v| v.set_dmaen(true)); - transfer.blocking_wait(); - } + transfer.blocking_wait(); } pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) where Dma: QuadDma, { - unsafe { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); - let request = self.dma.request(); - let transfer = Transfer::new_write( + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( &mut self.dma, request, buf, - T::REGS.dr().ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut u8, Default::default(), - ); + ) + }; - T::REGS.cr().modify(|v| v.set_dmaen(true)); + T::REGS.cr().modify(|v| v.set_dmaen(true)); - transfer.blocking_wait(); - } + transfer.blocking_wait(); } fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { - unsafe { - T::REGS.fcr().modify(|v| { - v.set_csmf(true); - v.set_ctcf(true); - v.set_ctef(true); - v.set_ctof(true); + T::REGS.fcr().modify(|v| { + v.set_csmf(true); + v.set_ctcf(true); + v.set_ctef(true); + v.set_ctof(true); + }); + + while T::REGS.sr().read().busy() {} + + if let Some(len) = transaction.data_len { + T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); + } + + T::REGS.ccr().write(|v| { + v.set_fmode(fmode.into()); + v.set_imode(transaction.iwidth.into()); + v.set_instruction(transaction.instruction); + v.set_admode(transaction.awidth.into()); + v.set_adsize(self.config.address_size.into()); + v.set_dmode(transaction.dwidth.into()); + v.set_abmode(QspiWidth::NONE.into()); + v.set_dcyc(transaction.dummy.into()); + }); + + if let Some(addr) = transaction.address { + T::REGS.ar().write(|v| { + v.set_address(addr); }); - - while T::REGS.sr().read().busy() {} - - if let Some(len) = transaction.data_len { - T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); - } - - T::REGS.ccr().write(|v| { - v.set_fmode(fmode.into()); - v.set_imode(transaction.iwidth.into()); - v.set_instruction(transaction.instruction); - v.set_admode(transaction.awidth.into()); - v.set_adsize(self.config.address_size.into()); - v.set_dmode(transaction.dwidth.into()); - v.set_abmode(QspiWidth::NONE.into()); - v.set_dcyc(transaction.dummy.into()); - }); - - if let Some(addr) = transaction.address { - T::REGS.ar().write(|v| { - v.set_address(addr); - }); - } } } } diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index e0929ca49..bc430afb2 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -36,18 +36,18 @@ pub struct Config { } #[cfg(stm32f410)] -unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { +fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { None } // Not currently implemented, but will be in the future #[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] -unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { +fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { None } #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] -unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { +fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { let min_div = 2; let max_div = 7; let target = match plli2s { @@ -82,13 +82,7 @@ unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { Some(output) } -unsafe fn setup_pll( - pllsrcclk: u32, - use_hse: bool, - pllsysclk: Option, - plli2s: Option, - pll48clk: bool, -) -> PllResults { +fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, plli2s: Option, pll48clk: bool) -> PllResults { use crate::pac::rcc::vals::{Pllp, Pllsrc}; let sysclk = pllsysclk.unwrap_or(pllsrcclk); @@ -320,7 +314,7 @@ impl<'d, T: McoInstance> Mco<'d, T> { } } -unsafe fn flash_setup(sysclk: u32) { +fn flash_setup(sysclk: u32) { use crate::pac::flash::vals::Latency; // Be conservative with voltage ranges diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs index 2d21326a3..71215cac5 100644 --- a/embassy-stm32/src/rcc/f7.rs +++ b/embassy-stm32/src/rcc/f7.rs @@ -25,7 +25,7 @@ pub struct Config { pub pll48: bool, } -unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bool) -> PllResults { +fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bool) -> PllResults { use crate::pac::rcc::vals::{Pllp, Pllsrc}; let sysclk = pllsysclk.unwrap_or(pllsrcclk); @@ -97,7 +97,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48 } } -unsafe fn flash_setup(sysclk: u32) { +fn flash_setup(sysclk: u32) { use crate::pac::flash::vals::Latency; // Be conservative with voltage ranges diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 3e138c7ab..17c73c36b 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -245,7 +245,7 @@ impl Default for Config { } impl PllConfig { - pub(crate) unsafe fn init(self) -> u32 { + pub(crate) fn init(self) -> u32 { assert!(self.n >= 8 && self.n <= 86); let (src, input_freq) = match self.source { PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ.0), diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs index 17fbc6056..4025a4e05 100644 --- a/embassy-stm32/src/rcc/h5.rs +++ b/embassy-stm32/src/rcc/h5.rs @@ -462,7 +462,7 @@ struct PllOutput { r: Option, } -unsafe fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { +fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { let Some(config) = config else { // Stop PLL RCC.cr().modify(|w| w.set_pllon(num, false)); @@ -595,12 +595,9 @@ fn flash_setup(clk: Hertz, vos: VoltageScale) { defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq); - // NOTE(unsafe) Atomic write - unsafe { - FLASH.acr().write(|w| { - w.set_wrhighfreq(wrhighfreq); - w.set_latency(latency); - }); - while FLASH.acr().read().latency() != latency {} - } + FLASH.acr().write(|w| { + w.set_wrhighfreq(wrhighfreq); + w.set_latency(latency); + }); + while FLASH.acr().read().latency() != latency {} } diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index 0185f7ae8..daa1cd61f 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -253,14 +253,11 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { }, }; - // NOTE(unsafe) Atomic write - unsafe { - FLASH.acr().write(|w| { - w.set_wrhighfreq(progr_delay); - w.set_latency(wait_states) - }); - while FLASH.acr().read().latency() != wait_states {} - } + FLASH.acr().write(|w| { + w.set_wrhighfreq(progr_delay); + w.set_latency(wait_states) + }); + while FLASH.acr().read().latency() != wait_states {} } pub enum McoClock { @@ -474,7 +471,6 @@ pub(crate) unsafe fn init(mut config: Config) { // Configure traceclk from PLL if needed traceclk_setup(&mut config, sys_use_pll1_p); - // NOTE(unsafe) We have exclusive access to the RCC let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0); let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1); let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2); @@ -756,7 +752,7 @@ mod pll { /// # Safety /// /// Must have exclusive access to the RCC register block - unsafe fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults { + fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults { use crate::pac::rcc::vals::{Pllrge, Pllvcosel}; let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln); @@ -785,11 +781,7 @@ mod pll { /// # Safety /// /// Must have exclusive access to the RCC register block - pub(super) unsafe fn pll_setup( - pll_src: u32, - config: &PllConfig, - plln: usize, - ) -> (Option, Option, Option) { + pub(super) fn pll_setup(pll_src: u32, config: &PllConfig, plln: usize) -> (Option, Option, Option) { use crate::pac::rcc::vals::Divp; match config.p_ck { diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index c657bf70e..b2faec53d 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -34,40 +34,34 @@ impl<'d, T: Instance> Rng<'d, T> { pub fn reset(&mut self) { // rng_v2 locks up on seed error, needs reset #[cfg(rng_v2)] - if unsafe { T::regs().sr().read().seis() } { + if T::regs().sr().read().seis() { T::reset(); } - unsafe { - T::regs().cr().modify(|reg| { - reg.set_rngen(true); - reg.set_ie(true); - }); - T::regs().sr().modify(|reg| { - reg.set_seis(false); - reg.set_ceis(false); - }); - } + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + reg.set_ie(true); + }); + T::regs().sr().modify(|reg| { + reg.set_seis(false); + reg.set_ceis(false); + }); // Reference manual says to discard the first. let _ = self.next_u32(); } pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - unsafe { - T::regs().cr().modify(|reg| { - reg.set_rngen(true); - }) - } + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + }); for chunk in dest.chunks_mut(4) { poll_fn(|cx| { RNG_WAKER.register(cx.waker()); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_ie(true); - }); - } + T::regs().cr().modify(|reg| { + reg.set_ie(true); + }); - let bits = unsafe { T::regs().sr().read() }; + let bits = T::regs().sr().read(); if bits.drdy() { Poll::Ready(Ok(())) @@ -82,7 +76,7 @@ impl<'d, T: Instance> Rng<'d, T> { } }) .await?; - let random_bytes = unsafe { T::regs().dr().read() }.to_be_bytes(); + let random_bytes = T::regs().dr().read().to_be_bytes(); for (dest, src) in chunk.iter_mut().zip(random_bytes.iter()) { *dest = *src } @@ -95,11 +89,11 @@ impl<'d, T: Instance> Rng<'d, T> { impl<'d, T: Instance> RngCore for Rng<'d, T> { fn next_u32(&mut self) -> u32 { loop { - let sr = unsafe { T::regs().sr().read() }; + let sr = T::regs().sr().read(); if sr.seis() | sr.ceis() { self.reset(); } else if sr.drdy() { - return unsafe { T::regs().dr().read() }; + return T::regs().dr().read(); } } } diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index 0a590c1bb..a9c48d88d 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -154,29 +154,27 @@ pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) { let yr_offset = (yr - 1970_u16) as u8; let (yt, yu) = byte_to_bcd2(yr_offset); - unsafe { - use crate::pac::rtc::vals::Ampm; + use crate::pac::rtc::vals::Ampm; - rtc.tr().write(|w| { - w.set_ht(ht); - w.set_hu(hu); - w.set_mnt(mnt); - w.set_mnu(mnu); - w.set_st(st); - w.set_su(su); - w.set_pm(Ampm::AM); - }); + rtc.tr().write(|w| { + w.set_ht(ht); + w.set_hu(hu); + w.set_mnt(mnt); + w.set_mnu(mnu); + w.set_st(st); + w.set_su(su); + w.set_pm(Ampm::AM); + }); - rtc.dr().write(|w| { - w.set_dt(dt); - w.set_du(du); - w.set_mt(mt > 0); - w.set_mu(mu); - w.set_yt(yt); - w.set_yu(yu); - w.set_wdu(day_of_week_to_u8(t.day_of_week)); - }); - } + rtc.dr().write(|w| { + w.set_dt(dt); + w.set_du(du); + w.set_mt(mt > 0); + w.set_mu(mu); + w.set_yt(yt); + w.set_yu(yu); + w.set_wdu(day_of_week_to_u8(t.day_of_week)); + }); } pub(super) fn datetime( diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 962927fb1..12a2ac795 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -113,7 +113,7 @@ impl Default for RtcCalibrationCyclePeriod { impl<'d, T: Instance> Rtc<'d, T> { pub fn new(_rtc: impl Peripheral

+ 'd, rtc_config: RtcConfig) -> Self { - unsafe { T::enable_peripheral_clk() }; + T::enable_peripheral_clk(); let mut rtc_struct = Self { phantom: PhantomData, @@ -144,34 +144,32 @@ impl<'d, T: Instance> Rtc<'d, T> { /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. pub fn now(&self) -> Result { let r = T::regs(); - unsafe { - let tr = r.tr().read(); - let second = bcd2_to_byte((tr.st(), tr.su())); - let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); - let hour = bcd2_to_byte((tr.ht(), tr.hu())); - // Reading either RTC_SSR or RTC_TR locks the values in the higher-order - // calendar shadow registers until RTC_DR is read. - let dr = r.dr().read(); + let tr = r.tr().read(); + let second = bcd2_to_byte((tr.st(), tr.su())); + let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); + let hour = bcd2_to_byte((tr.ht(), tr.hu())); + // Reading either RTC_SSR or RTC_TR locks the values in the higher-order + // calendar shadow registers until RTC_DR is read. + let dr = r.dr().read(); - let weekday = dr.wdu(); - let day = bcd2_to_byte((dr.dt(), dr.du())); - let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); - let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; + let weekday = dr.wdu(); + let day = bcd2_to_byte((dr.dt(), dr.du())); + let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); + let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; - self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) - } + self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) } /// Check if daylight savings time is active. pub fn get_daylight_savings(&self) -> bool { - let cr = unsafe { T::regs().cr().read() }; + let cr = T::regs().cr().read(); cr.bkp() } /// Enable/disable daylight savings time. pub fn set_daylight_savings(&mut self, daylight_savings: bool) { self.write(true, |rtc| { - unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) }; + rtc.cr().modify(|w| w.set_bkp(daylight_savings)); }) } @@ -228,7 +226,7 @@ pub(crate) mod sealed { crate::pac::RTC } - unsafe fn enable_peripheral_clk() {} + fn enable_peripheral_clk() {} /// Read content of the backup register. /// diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index adaafe67a..e1615b34c 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -8,74 +8,72 @@ impl<'d, T: Instance> super::Rtc<'d, T> { /// It this changes the RTC clock source the time will be reset pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { // Unlock the backup domain - unsafe { - let clock_config = rtc_config.clock_config as u8; + let clock_config = rtc_config.clock_config as u8; - #[cfg(not(rtc_v2wb))] - use stm32_metapac::rcc::vals::Rtcsel; + #[cfg(not(rtc_v2wb))] + use stm32_metapac::rcc::vals::Rtcsel; - #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] - let cr = crate::pac::PWR.cr(); - #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] - let cr = crate::pac::PWR.cr1(); + #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] + let cr = crate::pac::PWR.cr(); + #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] + let cr = crate::pac::PWR.cr1(); - // TODO: Missing from PAC for l0 and f0? - #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] - { - cr.modify(|w| w.set_dbp(true)); - while !cr.read().dbp() {} - } - - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - let reg = crate::pac::RCC.bdcr().read(); - #[cfg(any(rtc_v2l0, rtc_v2l1))] - let reg = crate::pac::RCC.csr().read(); - - #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - - #[cfg(rtc_v2wb)] - let rtcsel = reg.rtcsel(); - #[cfg(not(rtc_v2wb))] - let rtcsel = reg.rtcsel().0; - - if !reg.rtcen() || rtcsel != clock_config { - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - let cr = crate::pac::RCC.bdcr(); - #[cfg(any(rtc_v2l0, rtc_v2l1))] - let cr = crate::pac::RCC.csr(); - - cr.modify(|w| { - // Reset - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] - w.set_bdrst(false); - - // Select RTC source - #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel(clock_config)); - #[cfg(rtc_v2wb)] - w.set_rtcsel(clock_config); - w.set_rtcen(true); - - // Restore bcdr - #[cfg(any(rtc_v2l4, rtc_v2wb))] - w.set_lscosel(reg.lscosel()); - #[cfg(any(rtc_v2l4, rtc_v2wb))] - w.set_lscoen(reg.lscoen()); - - w.set_lseon(reg.lseon()); - - #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } + // TODO: Missing from PAC for l0 and f0? + #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] + { + cr.modify(|w| w.set_dbp(true)); + while !cr.read().dbp() {} } - self.write(true, |rtc| unsafe { + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let reg = crate::pac::RCC.bdcr().read(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let reg = crate::pac::RCC.csr().read(); + + #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + #[cfg(rtc_v2wb)] + let rtcsel = reg.rtcsel(); + #[cfg(not(rtc_v2wb))] + let rtcsel = reg.rtcsel().0; + + if !reg.rtcen() || rtcsel != clock_config { + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let cr = crate::pac::RCC.bdcr(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let cr = crate::pac::RCC.csr(); + + cr.modify(|w| { + // Reset + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + w.set_bdrst(false); + + // Select RTC source + #[cfg(not(rtc_v2wb))] + w.set_rtcsel(Rtcsel(clock_config)); + #[cfg(rtc_v2wb)] + w.set_rtcsel(clock_config); + w.set_rtcen(true); + + // Restore bcdr + #[cfg(any(rtc_v2l4, rtc_v2wb))] + w.set_lscosel(reg.lscosel()); + #[cfg(any(rtc_v2l4, rtc_v2wb))] + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + + #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } + + self.write(true, |rtc| { rtc.cr().modify(|w| { #[cfg(rtc_v2f2)] w.set_fmt(false); @@ -117,47 +115,45 @@ impl<'d, T: Instance> super::Rtc<'d, T> { clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM; self.write(false, |rtc| { - unsafe { - rtc.calr().write(|w| { - match period { - super::RtcCalibrationCyclePeriod::Seconds8 => { - w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND); - } - super::RtcCalibrationCyclePeriod::Seconds16 => { - w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND); - } - super::RtcCalibrationCyclePeriod::Seconds32 => { - // Set neither `calw8` nor `calw16` to use 32 seconds - } + rtc.calr().write(|w| { + match period { + super::RtcCalibrationCyclePeriod::Seconds8 => { + w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND); } - - // Extra pulses during calibration cycle period: CALP * 512 - CALM - // - // CALP sets whether pulses are added or omitted. - // - // CALM contains how many pulses (out of 512) are masked in a - // given calibration cycle period. - if clock_drift > 0.0 { - // Maximum (about 512.2) rounds to 512. - clock_drift += 0.5; - - // When the offset is positive (0 to 512), the opposite of - // the offset (512 - offset) is masked, i.e. for the - // maximum offset (512), 0 pulses are masked. - w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ); - w.set_calm(512 - clock_drift as u16); - } else { - // Minimum (about -510.7) rounds to -511. - clock_drift -= 0.5; - - // When the offset is negative or zero (-511 to 0), - // the absolute offset is masked, i.e. for the minimum - // offset (-511), 511 pulses are masked. - w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE); - w.set_calm((clock_drift * -1.0) as u16); + super::RtcCalibrationCyclePeriod::Seconds16 => { + w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND); } - }); - } + super::RtcCalibrationCyclePeriod::Seconds32 => { + // Set neither `calw8` nor `calw16` to use 32 seconds + } + } + + // Extra pulses during calibration cycle period: CALP * 512 - CALM + // + // CALP sets whether pulses are added or omitted. + // + // CALM contains how many pulses (out of 512) are masked in a + // given calibration cycle period. + if clock_drift > 0.0 { + // Maximum (about 512.2) rounds to 512. + clock_drift += 0.5; + + // When the offset is positive (0 to 512), the opposite of + // the offset (512 - offset) is masked, i.e. for the + // maximum offset (512), 0 pulses are masked. + w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ); + w.set_calm(512 - clock_drift as u16); + } else { + // Minimum (about -510.7) rounds to -511. + clock_drift -= 0.5; + + // When the offset is negative or zero (-511 to 0), + // the absolute offset is masked, i.e. for the minimum + // offset (-511), 511 pulses are masked. + w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE); + w.set_calm((clock_drift * -1.0) as u16); + } + }); }) } @@ -168,31 +164,27 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let r = T::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. - unsafe { - r.wpr().write(|w| w.set_key(0xca)); - r.wpr().write(|w| w.set_key(0x53)); + r.wpr().write(|w| w.set_key(0xca)); + r.wpr().write(|w| w.set_key(0x53)); - // true if initf bit indicates RTC peripheral is in init mode - if init_mode && !r.isr().read().initf() { - // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode - r.isr().modify(|w| w.set_init(Init::INITMODE)); - // wait till init state entered - // ~2 RTCCLK cycles - while !r.isr().read().initf() {} - } + // true if initf bit indicates RTC peripheral is in init mode + if init_mode && !r.isr().read().initf() { + // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode + r.isr().modify(|w| w.set_init(Init::INITMODE)); + // wait till init state entered + // ~2 RTCCLK cycles + while !r.isr().read().initf() {} } let result = f(&r); - unsafe { - if init_mode { - r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode - } - - // Re-enable write protection. - // This is safe, as the field accepts the full range of 8-bit values. - r.wpr().write(|w| w.set_key(0xff)); + if init_mode { + r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode } + + // Re-enable write protection. + // This is safe, as the field accepts the full range of 8-bit values. + r.wpr().write(|w| w.set_key(0xff)); result } } @@ -200,7 +192,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { impl sealed::Instance for crate::peripherals::RTC { const BACKUP_REGISTER_COUNT: usize = 20; - unsafe fn enable_peripheral_clk() { + fn enable_peripheral_clk() { #[cfg(any(rtc_v2l4, rtc_v2wb))] { // enable peripheral clock for communication @@ -213,7 +205,7 @@ impl sealed::Instance for crate::peripherals::RTC { fn read_backup_register(rtc: &Rtc, register: usize) -> Option { if register < Self::BACKUP_REGISTER_COUNT { - Some(unsafe { rtc.bkpr(register).read().bkp() }) + Some(rtc.bkpr(register).read().bkp()) } else { None } @@ -221,7 +213,7 @@ impl sealed::Instance for crate::peripherals::RTC { fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { if register < Self::BACKUP_REGISTER_COUNT { - unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) } + rtc.bkpr(register).write(|w| w.set_bkp(value)); } } } diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 24f6496a6..7c91046a2 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -8,70 +8,66 @@ impl<'d, T: Instance> super::Rtc<'d, T> { /// It this changes the RTC clock source the time will be reset pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { // Unlock the backup domain - unsafe { - #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] - { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} - } - #[cfg(any(rcc_wl5, rcc_wle))] - { - use crate::pac::pwr::vals::Dbp; + #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] + { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + } + #[cfg(any(rcc_wl5, rcc_wle))] + { + use crate::pac::pwr::vals::Dbp; - crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); - while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} - } + crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); + while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} + } - let reg = crate::pac::RCC.bdcr().read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - let config_rtcsel = rtc_config.clock_config as u8; - #[cfg(not(any(rcc_wl5, rcc_wle)))] - let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel); + let config_rtcsel = rtc_config.clock_config as u8; + #[cfg(not(any(rcc_wl5, rcc_wle)))] + let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel); - if !reg.rtcen() || reg.rtcsel() != config_rtcsel { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + if !reg.rtcen() || reg.rtcsel() != config_rtcsel { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - crate::pac::RCC.bdcr().modify(|w| { - // Reset - w.set_bdrst(false); + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); - // Select RTC source - w.set_rtcsel(config_rtcsel); + // Select RTC source + w.set_rtcsel(config_rtcsel); - w.set_rtcen(true); + w.set_rtcen(true); - // Restore bcdr - w.set_lscosel(reg.lscosel()); - w.set_lscoen(reg.lscoen()); + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); - w.set_lseon(reg.lseon()); - w.set_lsedrv(reg.lsedrv()); - w.set_lsebyp(reg.lsebyp()); - }); - } + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); } self.write(true, |rtc| { - unsafe { - rtc.cr().modify(|w| { - w.set_fmt(Fmt::TWENTYFOURHOUR); - w.set_osel(Osel::DISABLED); - w.set_pol(Pol::HIGH); - }); + rtc.cr().modify(|w| { + w.set_fmt(Fmt::TWENTYFOURHOUR); + w.set_osel(Osel::DISABLED); + w.set_pol(Pol::HIGH); + }); - rtc.prer().modify(|w| { - w.set_prediv_s(rtc_config.sync_prescaler); - w.set_prediv_a(rtc_config.async_prescaler); - }); + rtc.prer().modify(|w| { + w.set_prediv_s(rtc_config.sync_prescaler); + w.set_prediv_a(rtc_config.async_prescaler); + }); - // TODO: configuration for output pins - rtc.cr().modify(|w| { - w.set_out2en(false); - w.set_tampalrm_type(TampalrmType::PUSHPULL); - w.set_tampalrm_pu(TampalrmPu::NOPULLUP); - }); - } + // TODO: configuration for output pins + rtc.cr().modify(|w| { + w.set_out2en(false); + w.set_tampalrm_type(TampalrmType::PUSHPULL); + w.set_tampalrm_pu(TampalrmPu::NOPULLUP); + }); }); self.rtc_config = rtc_config; @@ -99,47 +95,45 @@ impl<'d, T: Instance> super::Rtc<'d, T> { clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM; self.write(false, |rtc| { - unsafe { - rtc.calr().write(|w| { - match period { - RtcCalibrationCyclePeriod::Seconds8 => { - w.set_calw8(Calw8::EIGHTSECONDS); - } - RtcCalibrationCyclePeriod::Seconds16 => { - w.set_calw16(Calw16::SIXTEENSECONDS); - } - RtcCalibrationCyclePeriod::Seconds32 => { - // Set neither `calw8` nor `calw16` to use 32 seconds - } + rtc.calr().write(|w| { + match period { + RtcCalibrationCyclePeriod::Seconds8 => { + w.set_calw8(Calw8::EIGHTSECONDS); } - - // Extra pulses during calibration cycle period: CALP * 512 - CALM - // - // CALP sets whether pulses are added or omitted. - // - // CALM contains how many pulses (out of 512) are masked in a - // given calibration cycle period. - if clock_drift > 0.0 { - // Maximum (about 512.2) rounds to 512. - clock_drift += 0.5; - - // When the offset is positive (0 to 512), the opposite of - // the offset (512 - offset) is masked, i.e. for the - // maximum offset (512), 0 pulses are masked. - w.set_calp(Calp::INCREASEFREQ); - w.set_calm(512 - clock_drift as u16); - } else { - // Minimum (about -510.7) rounds to -511. - clock_drift -= 0.5; - - // When the offset is negative or zero (-511 to 0), - // the absolute offset is masked, i.e. for the minimum - // offset (-511), 511 pulses are masked. - w.set_calp(Calp::NOCHANGE); - w.set_calm((clock_drift * -1.0) as u16); + RtcCalibrationCyclePeriod::Seconds16 => { + w.set_calw16(Calw16::SIXTEENSECONDS); } - }); - } + RtcCalibrationCyclePeriod::Seconds32 => { + // Set neither `calw8` nor `calw16` to use 32 seconds + } + } + + // Extra pulses during calibration cycle period: CALP * 512 - CALM + // + // CALP sets whether pulses are added or omitted. + // + // CALM contains how many pulses (out of 512) are masked in a + // given calibration cycle period. + if clock_drift > 0.0 { + // Maximum (about 512.2) rounds to 512. + clock_drift += 0.5; + + // When the offset is positive (0 to 512), the opposite of + // the offset (512 - offset) is masked, i.e. for the + // maximum offset (512), 0 pulses are masked. + w.set_calp(Calp::INCREASEFREQ); + w.set_calm(512 - clock_drift as u16); + } else { + // Minimum (about -510.7) rounds to -511. + clock_drift -= 0.5; + + // When the offset is negative or zero (-511 to 0), + // the absolute offset is masked, i.e. for the minimum + // offset (-511), 511 pulses are masked. + w.set_calp(Calp::NOCHANGE); + w.set_calm((clock_drift * -1.0) as u16); + } + }); }) } @@ -150,29 +144,26 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let r = T::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. - unsafe { - r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); - r.wpr().write(|w| w.set_key(Key::DEACTIVATE2)); + r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); + r.wpr().write(|w| w.set_key(Key::DEACTIVATE2)); - if init_mode && !r.icsr().read().initf() { - r.icsr().modify(|w| w.set_init(Init::INITMODE)); - // wait till init state entered - // ~2 RTCCLK cycles - while !r.icsr().read().initf() {} - } + if init_mode && !r.icsr().read().initf() { + r.icsr().modify(|w| w.set_init(Init::INITMODE)); + // wait till init state entered + // ~2 RTCCLK cycles + while !r.icsr().read().initf() {} } let result = f(&r); - unsafe { - if init_mode { - r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode - } - - // Re-enable write protection. - // This is safe, as the field accepts the full range of 8-bit values. - r.wpr().write(|w| w.set_key(Key::ACTIVATE)); + if init_mode { + r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode } + + // Re-enable write protection. + // This is safe, as the field accepts the full range of 8-bit values. + r.wpr().write(|w| w.set_key(Key::ACTIVATE)); + result } } @@ -192,7 +183,7 @@ impl sealed::Instance for crate::peripherals::RTC { fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) { if register < Self::BACKUP_REGISTER_COUNT { // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC - //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) } + //self.rtc.bkpr()[register].write(|w| w.bits(value)) } } } diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 28eb49ab6..80a336a48 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -28,17 +28,14 @@ pub struct InterruptHandler { impl InterruptHandler { fn data_interrupts(enable: bool) { let regs = T::regs(); - // NOTE(unsafe) Atomic write - unsafe { - regs.maskr().write(|w| { - w.set_dcrcfailie(enable); - w.set_dtimeoutie(enable); - w.set_dataendie(enable); + regs.maskr().write(|w| { + w.set_dcrcfailie(enable); + w.set_dtimeoutie(enable); + w.set_dataendie(enable); - #[cfg(sdmmc_v2)] - w.set_dabortie(enable); - }); - } + #[cfg(sdmmc_v2)] + w.set_dabortie(enable); + }); } } @@ -285,7 +282,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { ) -> Self { into_ref!(clk, cmd, d0); - critical_section::with(|_| unsafe { + critical_section::with(|_| { clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None); cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up); @@ -322,7 +319,7 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { ) -> Self { into_ref!(clk, cmd, d0, d1, d2, d3); - critical_section::with(|_| unsafe { + critical_section::with(|_| { clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None); cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up); @@ -364,7 +361,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { ) -> Self { into_ref!(clk, cmd, d0); - critical_section::with(|_| unsafe { + critical_section::with(|_| { clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None); cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up); @@ -400,7 +397,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { ) -> Self { into_ref!(clk, cmd, d0, d1, d2, d3); - critical_section::with(|_| unsafe { + critical_section::with(|_| { clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None); cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up); @@ -451,26 +448,24 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { unsafe { T::Interrupt::enable() }; let regs = T::regs(); - unsafe { - regs.clkcr().write(|w| { - w.set_pwrsav(false); - w.set_negedge(false); + regs.clkcr().write(|w| { + w.set_pwrsav(false); + w.set_negedge(false); - // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. - // See chip erratas for more details. - #[cfg(sdmmc_v1)] - w.set_hwfc_en(false); - #[cfg(sdmmc_v2)] - w.set_hwfc_en(true); + // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. + // See chip erratas for more details. + #[cfg(sdmmc_v1)] + w.set_hwfc_en(false); + #[cfg(sdmmc_v2)] + w.set_hwfc_en(true); - #[cfg(sdmmc_v1)] - w.set_clken(true); - }); + #[cfg(sdmmc_v1)] + w.set_clken(true); + }); - // 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)); - } + // 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)); Self { _peri: sdmmc, @@ -495,14 +490,11 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { fn data_active() -> bool { let regs = T::regs(); - // NOTE(unsafe) Atomic read with no side-effects - unsafe { - let status = regs.star().read(); - #[cfg(sdmmc_v1)] - return status.rxact() || status.txact(); - #[cfg(sdmmc_v2)] - return status.dpsmact(); - } + let status = regs.star().read(); + #[cfg(sdmmc_v1)] + return status.rxact() || status.txact(); + #[cfg(sdmmc_v2)] + return status.dpsmact(); } /// Coammand transfer is in progress @@ -510,14 +502,11 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { fn cmd_active() -> bool { let regs = T::regs(); - // NOTE(unsafe) Atomic read with no side-effects - unsafe { - let status = regs.star().read(); - #[cfg(sdmmc_v1)] - return status.cmdact(); - #[cfg(sdmmc_v2)] - return status.cpsmact(); - } + let status = regs.star().read(); + #[cfg(sdmmc_v1)] + return status.cmdact(); + #[cfg(sdmmc_v2)] + return status.cpsmact(); } /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) @@ -542,44 +531,41 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::wait_idle(); Self::clear_interrupt_flags(); - // NOTE(unsafe) We have exclusive access to the regisers - unsafe { - regs.dtimer() - .write(|w| w.set_datatime(self.config.data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); + #[cfg(sdmmc_v1)] + let transfer = unsafe { + let request = self.dma.request(); + Transfer::new_read( + &mut self.dma, + request, + regs.fifor().as_ptr() as *mut u32, + buffer, + DMA_TRANSFER_OPTIONS, + ) + }; + #[cfg(sdmmc_v2)] + let transfer = { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + Transfer { + _dummy: core::marker::PhantomData, + } + }; + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(true); #[cfg(sdmmc_v1)] - let transfer = { - let request = self.dma.request(); - Transfer::new_read( - &mut self.dma, - request, - regs.fifor().ptr() as *mut u32, - buffer, - DMA_TRANSFER_OPTIONS, - ) - }; - #[cfg(sdmmc_v2)] - let transfer = { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - Transfer { - _dummy: core::marker::PhantomData, - } - }; + { + w.set_dmaen(true); + w.set_dten(true); + } + }); - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(true); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); - - transfer - } + transfer } /// # Safety @@ -598,59 +584,54 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::wait_idle(); Self::clear_interrupt_flags(); - // NOTE(unsafe) We have exclusive access to the regisers - unsafe { - regs.dtimer() - .write(|w| w.set_datatime(self.config.data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); + #[cfg(sdmmc_v1)] + let transfer = unsafe { + let request = self.dma.request(); + Transfer::new_write( + &mut self.dma, + request, + buffer, + regs.fifor().as_ptr() as *mut u32, + DMA_TRANSFER_OPTIONS, + ) + }; + #[cfg(sdmmc_v2)] + let transfer = { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + Transfer { + _dummy: core::marker::PhantomData, + } + }; + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(false); #[cfg(sdmmc_v1)] - let transfer = { - let request = self.dma.request(); - Transfer::new_write( - &mut self.dma, - request, - buffer, - regs.fifor().ptr() as *mut u32, - DMA_TRANSFER_OPTIONS, - ) - }; - #[cfg(sdmmc_v2)] - let transfer = { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - Transfer { - _dummy: core::marker::PhantomData, - } - }; + { + w.set_dmaen(true); + w.set_dten(true); + } + }); - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(false); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); - - transfer - } + transfer } /// Stops the DMA datapath fn stop_datapath() { let regs = T::regs(); - unsafe { - #[cfg(sdmmc_v1)] - regs.dctrl().modify(|w| { - w.set_dmaen(false); - w.set_dten(false); - }); - #[cfg(sdmmc_v2)] - regs.idmactrlr().modify(|w| w.set_idmaen(false)); - } + #[cfg(sdmmc_v1)] + regs.dctrl().modify(|w| { + w.set_dmaen(false); + w.set_dten(false); + }); + #[cfg(sdmmc_v2)] + regs.idmactrlr().modify(|w| w.set_idmaen(false)); } /// Sets the CLKDIV field in CLKCR. Updates clock field in self @@ -673,16 +654,13 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); self.clock = new_clock; - // NOTE(unsafe) We have exclusive access to the regblock - unsafe { - // CPSMACT and DPSMACT must be 0 to set CLKDIV - Self::wait_idle(); - regs.clkcr().modify(|w| { - w.set_clkdiv(clkdiv); - #[cfg(sdmmc_v1)] - w.set_bypass(_bypass); - }); - } + // CPSMACT and DPSMACT must be 0 to set CLKDIV + Self::wait_idle(); + regs.clkcr().modify(|w| { + w.set_clkdiv(clkdiv); + #[cfg(sdmmc_v1)] + w.set_bypass(_bypass); + }); Ok(()) } @@ -710,7 +688,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { // Arm `OnDrop` after the buffer, so it will be dropped first let regs = T::regs(); - let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = self.prepare_datapath_read(&mut status, 64, 6); InterruptHandler::::data_interrupts(true); @@ -718,7 +696,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let res = poll_fn(|cx| { T::state().register(cx.waker()); - let status = unsafe { regs.star().read() }; + let status = regs.star().read(); if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); @@ -769,8 +747,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::cmd(Cmd::card_status(rca << 16), false)?; // CMD13 - // NOTE(unsafe) Atomic read with no side-effects - let r1 = unsafe { regs.respr(0).read().cardstatus() }; + let r1 = regs.respr(0).read().cardstatus(); Ok(r1.into()) } @@ -786,7 +763,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { // Arm `OnDrop` after the buffer, so it will be dropped first let regs = T::regs(); - let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = self.prepare_datapath_read(&mut status, 64, 6); InterruptHandler::::data_interrupts(true); @@ -794,7 +771,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let res = poll_fn(|cx| { T::state().register(cx.waker()); - let status = unsafe { regs.star().read() }; + let status = regs.star().read(); if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); @@ -840,35 +817,32 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { #[inline(always)] fn clear_interrupt_flags() { let regs = T::regs(); - // NOTE(unsafe) Atomic write - unsafe { - regs.icr().write(|w| { - w.set_ccrcfailc(true); - w.set_dcrcfailc(true); - w.set_ctimeoutc(true); - w.set_dtimeoutc(true); - w.set_txunderrc(true); - w.set_rxoverrc(true); - w.set_cmdrendc(true); - w.set_cmdsentc(true); - w.set_dataendc(true); - w.set_dbckendc(true); - w.set_sdioitc(true); + regs.icr().write(|w| { + w.set_ccrcfailc(true); + w.set_dcrcfailc(true); + w.set_ctimeoutc(true); + w.set_dtimeoutc(true); + w.set_txunderrc(true); + w.set_rxoverrc(true); + w.set_cmdrendc(true); + w.set_cmdsentc(true); + w.set_dataendc(true); + w.set_dbckendc(true); + w.set_sdioitc(true); - #[cfg(sdmmc_v2)] - { - w.set_dholdc(true); - w.set_dabortc(true); - w.set_busyd0endc(true); - w.set_ackfailc(true); - w.set_acktimeoutc(true); - w.set_vswendc(true); - w.set_ckstopc(true); - w.set_idmatec(true); - w.set_idmabtcc(true); - } - }); - } + #[cfg(sdmmc_v2)] + { + w.set_dholdc(true); + w.set_dabortc(true); + w.set_busyd0endc(true); + w.set_ackfailc(true); + w.set_acktimeoutc(true); + w.set_vswendc(true); + w.set_ckstopc(true); + w.set_idmatec(true); + w.set_idmabtcc(true); + } + }); } async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { @@ -880,7 +854,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { // Arm `OnDrop` after the buffer, so it will be dropped first let regs = T::regs(); - let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = self.prepare_datapath_read(&mut scr[..], 8, 3); InterruptHandler::::data_interrupts(true); @@ -888,7 +862,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let res = poll_fn(|cx| { T::state().register(cx.waker()); - let status = unsafe { regs.star().read() }; + let status = regs.star().read(); if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); @@ -921,59 +895,53 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); Self::clear_interrupt_flags(); - // NOTE(safety) Atomic operations - unsafe { - // CP state machine must be idle - while Self::cmd_active() {} + // CP state machine must be idle + while Self::cmd_active() {} - // Command arg - regs.argr().write(|w| w.set_cmdarg(cmd.arg)); + // Command arg + regs.argr().write(|w| w.set_cmdarg(cmd.arg)); - // Command index and start CP State Machine - regs.cmdr().write(|w| { - w.set_waitint(false); - w.set_waitresp(cmd.resp as u8); - w.set_cmdindex(cmd.cmd); - w.set_cpsmen(true); + // Command index and start CP State Machine + regs.cmdr().write(|w| { + w.set_waitint(false); + w.set_waitresp(cmd.resp as u8); + w.set_cmdindex(cmd.cmd); + w.set_cpsmen(true); - #[cfg(sdmmc_v2)] - { - // Special mode in CP State Machine - // CMD12: Stop Transmission - let cpsm_stop_transmission = cmd.cmd == 12; - w.set_cmdstop(cpsm_stop_transmission); - w.set_cmdtrans(data); - } - }); - - let mut status; - if cmd.resp == Response::None { - // Wait for CMDSENT or a timeout - while { - status = regs.star().read(); - !(status.ctimeout() || status.cmdsent()) - } {} - } else { - // Wait for CMDREND or CCRCFAIL or a timeout - while { - status = regs.star().read(); - !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) - } {} + #[cfg(sdmmc_v2)] + { + // Special mode in CP State Machine + // CMD12: Stop Transmission + let cpsm_stop_transmission = cmd.cmd == 12; + w.set_cmdstop(cpsm_stop_transmission); + w.set_cmdtrans(data); } + }); - if status.ctimeout() { - return Err(Error::Timeout); - } else if status.ccrcfail() { - return Err(Error::Crc); - } - Ok(()) + let mut status; + if cmd.resp == Response::None { + // Wait for CMDSENT or a timeout + while { + status = regs.star().read(); + !(status.ctimeout() || status.cmdsent()) + } {} + } else { + // Wait for CMDREND or CCRCFAIL or a timeout + while { + status = regs.star().read(); + !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) + } {} } + + if status.ctimeout() { + return Err(Error::Timeout); + } else if status.ccrcfail() { + return Err(Error::Crc); + } + Ok(()) } - /// # Safety - /// - /// Ensure that `regs` has exclusive access to the regblocks - unsafe fn on_drop() { + fn on_drop() { let regs = T::regs(); if Self::data_active() { Self::clear_interrupt_flags(); @@ -1017,141 +985,138 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { false => BusWidth::One, }; - // 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 (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); - self.clock = init_clock; + // While the SD/SDIO card or eMMC is in identification mode, + // the SDMMC_CK frequency must be no more than 400 kHz. + let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); + self.clock = init_clock; - // CPSMACT and DPSMACT must be 0 to set WIDBUS - Self::wait_idle(); + // CPSMACT and DPSMACT must be 0 to set WIDBUS + Self::wait_idle(); - regs.clkcr().modify(|w| { - w.set_widbus(0); - w.set_clkdiv(clkdiv); - #[cfg(sdmmc_v1)] - w.set_bypass(_bypass); - }); + regs.clkcr().modify(|w| { + w.set_widbus(0); + w.set_clkdiv(clkdiv); + #[cfg(sdmmc_v1)] + w.set_bypass(_bypass); + }); - regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); - Self::cmd(Cmd::idle(), false)?; + regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); + Self::cmd(Cmd::idle(), false)?; - // Check if cards supports CMD8 (with pattern) - Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; - let r1 = regs.respr(0).read().cardstatus(); + // Check if cards supports CMD8 (with pattern) + Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; + let r1 = regs.respr(0).read().cardstatus(); - let mut card = if r1 == 0x1AA { - // Card echoed back the pattern. Must be at least v2 - Card::default() - } else { - return Err(Error::UnsupportedCardVersion); - }; + let mut card = if r1 == 0x1AA { + // Card echoed back the pattern. Must be at least v2 + Card::default() + } else { + return Err(Error::UnsupportedCardVersion); + }; - let ocr = loop { - // Signal that next command is a app command - Self::cmd(Cmd::app_cmd(0), false)?; // CMD55 + let ocr = loop { + // Signal that next command is a app command + Self::cmd(Cmd::app_cmd(0), false)?; // CMD55 - let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 - | CmdAppOper::HIGH_CAPACITY as u32 - | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; + let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 + | CmdAppOper::HIGH_CAPACITY as u32 + | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; - // Initialize card - match Self::cmd(Cmd::app_op_cmd(arg), false) { - // ACMD41 - Ok(_) => (), - Err(Error::Crc) => (), - Err(err) => return Err(err), - } - let ocr: OCR = regs.respr(0).read().cardstatus().into(); - if !ocr.is_busy() { - // Power up done - break ocr; - } - }; - - if ocr.high_capacity() { - // Card is SDHC or SDXC or SDUC - card.card_type = CardCapacity::SDHC; - } else { - card.card_type = CardCapacity::SDSC; + // Initialize card + match Self::cmd(Cmd::app_op_cmd(arg), false) { + // ACMD41 + Ok(_) => (), + Err(Error::Crc) => (), + Err(err) => return Err(err), } - card.ocr = ocr; - - Self::cmd(Cmd::all_send_cid(), false)?; // CMD2 - let cid0 = regs.respr(0).read().cardstatus() as u128; - let cid1 = regs.respr(1).read().cardstatus() as u128; - let cid2 = regs.respr(2).read().cardstatus() as u128; - let cid3 = regs.respr(3).read().cardstatus() as u128; - let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); - card.cid = cid.into(); - - Self::cmd(Cmd::send_rel_addr(), false)?; - card.rca = regs.respr(0).read().cardstatus() >> 16; - - Self::cmd(Cmd::send_csd(card.rca << 16), false)?; - let csd0 = regs.respr(0).read().cardstatus() as u128; - let csd1 = regs.respr(1).read().cardstatus() as u128; - let csd2 = regs.respr(2).read().cardstatus() as u128; - let csd3 = regs.respr(3).read().cardstatus() as u128; - let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); - card.csd = csd.into(); - - self.select_card(Some(&card))?; - - self.get_scr(&mut card).await?; - - // Set bus width - let (width, acmd_arg) = match bus_width { - BusWidth::Eight => unimplemented!(), - BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), - _ => (BusWidth::One, 0), - }; - Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; - Self::cmd(Cmd::cmd6(acmd_arg), false)?; - - // CPSMACT and DPSMACT must be 0 to set WIDBUS - Self::wait_idle(); - - regs.clkcr().modify(|w| { - w.set_widbus(match width { - BusWidth::One => 0, - BusWidth::Four => 1, - BusWidth::Eight => 2, - _ => panic!("Invalid Bus Width"), - }) - }); - - // Set Clock - if freq.0 <= 25_000_000 { - // Final clock frequency - self.clkcr_set_clkdiv(freq.0, width)?; - } else { - // Switch to max clock for SDR12 - self.clkcr_set_clkdiv(25_000_000, width)?; + let ocr: OCR = regs.respr(0).read().cardstatus().into(); + if !ocr.is_busy() { + // Power up done + break ocr; } + }; - self.card = Some(card); - - // Read status - self.read_sd_status().await?; - - if freq.0 > 25_000_000 { - // Switch to SDR25 - self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; - - if self.signalling == Signalling::SDR25 { - // Set final clock frequency - self.clkcr_set_clkdiv(freq.0, width)?; - - if self.read_status(&card)?.state() != CurrentState::Transfer { - return Err(Error::SignalingSwitchFailed); - } - } - } - // Read status after signalling change - self.read_sd_status().await?; + if ocr.high_capacity() { + // Card is SDHC or SDXC or SDUC + card.card_type = CardCapacity::SDHC; + } else { + card.card_type = CardCapacity::SDSC; } + card.ocr = ocr; + + Self::cmd(Cmd::all_send_cid(), false)?; // CMD2 + let cid0 = regs.respr(0).read().cardstatus() as u128; + let cid1 = regs.respr(1).read().cardstatus() as u128; + let cid2 = regs.respr(2).read().cardstatus() as u128; + let cid3 = regs.respr(3).read().cardstatus() as u128; + let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); + card.cid = cid.into(); + + Self::cmd(Cmd::send_rel_addr(), false)?; + card.rca = regs.respr(0).read().cardstatus() >> 16; + + Self::cmd(Cmd::send_csd(card.rca << 16), false)?; + let csd0 = regs.respr(0).read().cardstatus() as u128; + let csd1 = regs.respr(1).read().cardstatus() as u128; + let csd2 = regs.respr(2).read().cardstatus() as u128; + let csd3 = regs.respr(3).read().cardstatus() as u128; + let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); + card.csd = csd.into(); + + self.select_card(Some(&card))?; + + self.get_scr(&mut card).await?; + + // Set bus width + let (width, acmd_arg) = match bus_width { + BusWidth::Eight => unimplemented!(), + BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), + _ => (BusWidth::One, 0), + }; + Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; + Self::cmd(Cmd::cmd6(acmd_arg), false)?; + + // CPSMACT and DPSMACT must be 0 to set WIDBUS + Self::wait_idle(); + + regs.clkcr().modify(|w| { + w.set_widbus(match width { + BusWidth::One => 0, + BusWidth::Four => 1, + BusWidth::Eight => 2, + _ => panic!("Invalid Bus Width"), + }) + }); + + // Set Clock + if freq.0 <= 25_000_000 { + // Final clock frequency + self.clkcr_set_clkdiv(freq.0, width)?; + } else { + // Switch to max clock for SDR12 + self.clkcr_set_clkdiv(25_000_000, width)?; + } + + self.card = Some(card); + + // Read status + self.read_sd_status().await?; + + if freq.0 > 25_000_000 { + // Switch to SDR25 + self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; + + if self.signalling == Signalling::SDR25 { + // Set final clock frequency + self.clkcr_set_clkdiv(freq.0, width)?; + + if self.read_status(&card)?.state() != CurrentState::Transfer { + return Err(Error::SignalingSwitchFailed); + } + } + } + // Read status after signalling change + self.read_sd_status().await?; Ok(()) } @@ -1172,7 +1137,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); - let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + let on_drop = OnDrop::new(|| Self::on_drop()); let transfer = self.prepare_datapath_read(buffer, 512, 9); InterruptHandler::::data_interrupts(true); @@ -1180,7 +1145,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let res = poll_fn(|cx| { T::state().register(cx.waker()); - let status = unsafe { regs.star().read() }; + let status = regs.star().read(); if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); @@ -1217,7 +1182,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); - let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + let on_drop = OnDrop::new(|| Self::on_drop()); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes #[cfg(sdmmc_v1)] @@ -1231,7 +1196,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let res = poll_fn(|cx| { T::state().register(cx.waker()); - let status = unsafe { regs.star().read() }; + let status = regs.star().read(); if status.dcrcfail() { return Poll::Ready(Err(Error::Crc)); @@ -1289,9 +1254,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { impl<'d, T: Instance, Dma: SdmmcDma + 'd> Drop for Sdmmc<'d, T, Dma> { fn drop(&mut self) { T::Interrupt::disable(); - unsafe { Self::on_drop() }; + Self::on_drop(); - critical_section::with(|_| unsafe { + critical_section::with(|_| { self.clk.set_as_disconnected(); self.cmd.set_as_disconnected(); self.d0.set_as_disconnected(); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 580971e45..1cddac992 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -98,14 +98,12 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Polarity::IdleHigh => Pull::Up, }; - unsafe { - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode); - sck.set_speed(crate::gpio::Speed::VeryHigh); - mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - mosi.set_speed(crate::gpio::Speed::VeryHigh); - miso.set_as_af(miso.af_num(), AFType::Input); - miso.set_speed(crate::gpio::Speed::VeryHigh); - } + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode); + sck.set_speed(crate::gpio::Speed::VeryHigh); + mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); + mosi.set_speed(crate::gpio::Speed::VeryHigh); + miso.set_as_af(miso.af_num(), AFType::Input); + miso.set_speed(crate::gpio::Speed::VeryHigh); Self::new_inner( peri, @@ -129,12 +127,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { config: Config, ) -> Self { into_ref!(sck, miso); - unsafe { - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - miso.set_as_af(miso.af_num(), AFType::Input); - miso.set_speed(crate::gpio::Speed::VeryHigh); - } + sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_speed(crate::gpio::Speed::VeryHigh); + miso.set_as_af(miso.af_num(), AFType::Input); + miso.set_speed(crate::gpio::Speed::VeryHigh); Self::new_inner( peri, @@ -158,12 +154,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { config: Config, ) -> Self { into_ref!(sck, mosi); - unsafe { - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - mosi.set_speed(crate::gpio::Speed::VeryHigh); - } + sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_speed(crate::gpio::Speed::VeryHigh); + mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); + mosi.set_speed(crate::gpio::Speed::VeryHigh); Self::new_inner( peri, @@ -186,10 +180,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { config: Config, ) -> Self { into_ref!(mosi); - unsafe { - mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); - mosi.set_speed(crate::gpio::Speed::Medium); - } + mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); + mosi.set_speed(crate::gpio::Speed::Medium); Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) } @@ -247,7 +239,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::reset(); #[cfg(any(spi_v1, spi_f1))] - unsafe { + { T::REGS.cr2().modify(|w| { w.set_ssoe(false); }); @@ -270,7 +262,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); } #[cfg(spi_v2)] - unsafe { + { T::REGS.cr2().modify(|w| { let (ds, frxth) = ::CONFIG; w.set_frxth(frxth); @@ -292,7 +284,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); } #[cfg(any(spi_v3, spi_v4, spi_v5))] - unsafe { + { T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff); T::REGS.cfg2().modify(|w| { //w.set_ssoe(true); @@ -343,29 +335,25 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let lsbfirst = config.raw_byte_order(); #[cfg(any(spi_v1, spi_f1, spi_v2))] - unsafe { - T::REGS.cr1().modify(|w| { - w.set_cpha(cpha); - w.set_cpol(cpol); - w.set_lsbfirst(lsbfirst); - }); - } + T::REGS.cr1().modify(|w| { + w.set_cpha(cpha); + w.set_cpol(cpol); + w.set_lsbfirst(lsbfirst); + }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - unsafe { - T::REGS.cfg2().modify(|w| { - w.set_cpha(cpha); - w.set_cpol(cpol); - w.set_lsbfirst(lsbfirst); - }); - } + T::REGS.cfg2().modify(|w| { + w.set_cpha(cpha); + w.set_cpol(cpol); + w.set_lsbfirst(lsbfirst); + }); } pub fn get_current_config(&self) -> Config { #[cfg(any(spi_v1, spi_f1, spi_v2))] - let cfg = unsafe { T::REGS.cr1().read() }; + let cfg = T::REGS.cr1().read(); #[cfg(any(spi_v3, spi_v4, spi_v5))] - let cfg = unsafe { T::REGS.cfg2().read() }; + let cfg = T::REGS.cfg2().read(); let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow } else { @@ -395,7 +383,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } #[cfg(any(spi_v1, spi_f1))] - unsafe { + { T::REGS.cr1().modify(|reg| { reg.set_spe(false); reg.set_dff(word_size) @@ -405,7 +393,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); } #[cfg(spi_v2)] - unsafe { + { T::REGS.cr1().modify(|w| { w.set_spe(false); }); @@ -418,7 +406,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { }); } #[cfg(any(spi_v3, spi_v4, spi_v5))] - unsafe { + { T::REGS.cr1().modify(|w| { w.set_csusp(true); }); @@ -447,26 +435,22 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } self.set_word_size(W::CONFIG); - unsafe { - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - } + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; - unsafe { - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - } + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); tx_f.await; @@ -485,11 +469,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } self.set_word_size(W::CONFIG); - unsafe { - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - } + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] @@ -517,16 +499,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { ) }; - unsafe { - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - } + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); join(tx_f, rx_f).await; @@ -548,11 +528,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } self.set_word_size(W::CONFIG); - unsafe { - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - } + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] @@ -568,16 +546,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let tx_dst = T::REGS.tx_ptr(); let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) }; - unsafe { - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - } + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); join(tx_f, rx_f).await; @@ -603,7 +579,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { - unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } + T::REGS.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(T::REGS); self.set_word_size(W::CONFIG); for word in words.iter() { @@ -613,7 +589,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } pub fn blocking_read(&mut self, words: &mut [W]) -> Result<(), Error> { - unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } + T::REGS.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(T::REGS); self.set_word_size(W::CONFIG); for word in words.iter_mut() { @@ -623,7 +599,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } pub fn blocking_transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Error> { - unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } + T::REGS.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(T::REGS); self.set_word_size(W::CONFIG); for word in words.iter_mut() { @@ -633,7 +609,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } pub fn blocking_transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { - unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } + T::REGS.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(T::REGS); self.set_word_size(W::CONFIG); let len = read.len().max(write.len()); @@ -650,11 +626,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> { fn drop(&mut self) { - unsafe { - self.sck.as_ref().map(|x| x.set_as_disconnected()); - self.mosi.as_ref().map(|x| x.set_as_disconnected()); - self.miso.as_ref().map(|x| x.set_as_disconnected()); - } + self.sck.as_ref().map(|x| x.set_as_disconnected()); + self.mosi.as_ref().map(|x| x.set_as_disconnected()); + self.miso.as_ref().map(|x| x.set_as_disconnected()); } } @@ -690,7 +664,7 @@ impl RegsExt for Regs { let dr = self.dr(); #[cfg(any(spi_v3, spi_v4, spi_v5))] let dr = self.txdr(); - dr.ptr() as *mut W + dr.as_ptr() as *mut W } fn rx_ptr(&self) -> *mut W { @@ -698,7 +672,7 @@ impl RegsExt for Regs { let dr = self.dr(); #[cfg(any(spi_v3, spi_v4, spi_v5))] let dr = self.rxdr(); - dr.ptr() as *mut W + dr.as_ptr() as *mut W } } @@ -731,7 +705,7 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> { fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> { loop { - let sr = unsafe { regs.sr().read() }; + let sr = regs.sr().read(); check_error_flags(sr)?; @@ -748,7 +722,7 @@ fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> { fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { loop { - let sr = unsafe { regs.sr().read() }; + let sr = regs.sr().read(); check_error_flags(sr)?; @@ -764,72 +738,64 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { } fn flush_rx_fifo(regs: Regs) { - unsafe { - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - while regs.sr().read().rxne() { - let _ = regs.dr().read(); - } - #[cfg(any(spi_v3, spi_v4, spi_v5))] - while regs.sr().read().rxp() { - let _ = regs.rxdr().read(); - } + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + while regs.sr().read().rxne() { + let _ = regs.dr().read(); + } + #[cfg(any(spi_v3, spi_v4, spi_v5))] + while regs.sr().read().rxp() { + let _ = regs.rxdr().read(); } } fn set_txdmaen(regs: Regs, val: bool) { - unsafe { - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - regs.cr2().modify(|reg| { - reg.set_txdmaen(val); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - regs.cfg1().modify(|reg| { - reg.set_txdmaen(val); - }); - } + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + regs.cr2().modify(|reg| { + reg.set_txdmaen(val); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + regs.cfg1().modify(|reg| { + reg.set_txdmaen(val); + }); } fn set_rxdmaen(regs: Regs, val: bool) { - unsafe { - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - regs.cr2().modify(|reg| { - reg.set_rxdmaen(val); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - regs.cfg1().modify(|reg| { - reg.set_rxdmaen(val); - }); - } + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + regs.cr2().modify(|reg| { + reg.set_rxdmaen(val); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + regs.cfg1().modify(|reg| { + reg.set_rxdmaen(val); + }); } fn finish_dma(regs: Regs) { - unsafe { - #[cfg(spi_v2)] - while regs.sr().read().ftlvl() > 0 {} + #[cfg(spi_v2)] + while regs.sr().read().ftlvl() > 0 {} - #[cfg(any(spi_v3, spi_v4, spi_v5))] - while !regs.sr().read().txc() {} - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - while regs.sr().read().bsy() {} + #[cfg(any(spi_v3, spi_v4, spi_v5))] + while !regs.sr().read().txc() {} + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + while regs.sr().read().bsy() {} - // Disable the spi peripheral - regs.cr1().modify(|w| { - w.set_spe(false); - }); + // Disable the spi peripheral + regs.cr1().modify(|w| { + w.set_spe(false); + }); - // The peripheral automatically disables the DMA stream on completion without error, - // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - regs.cr2().modify(|reg| { - reg.set_txdmaen(false); - reg.set_rxdmaen(false); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - regs.cfg1().modify(|reg| { - reg.set_txdmaen(false); - reg.set_rxdmaen(false); - }); - } + // The peripheral automatically disables the DMA stream on completion without error, + // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + regs.cr2().modify(|reg| { + reg.set_txdmaen(false); + reg.set_rxdmaen(false); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + regs.cfg1().modify(|reg| { + reg.set_txdmaen(false); + reg.set_rxdmaen(false); + }); } fn transfer_word(regs: Regs, tx_word: W) -> Result { diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index e82501a45..2622442f4 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -155,8 +155,7 @@ impl RtcDriver { let timer_freq = T::frequency(); - // NOTE(unsafe) Critical section to use the unsafe methods - critical_section::with(|_| unsafe { + critical_section::with(|_| { r.cr1().modify(|w| w.set_cen(false)); r.cnt().write(|w| w.set_cnt(0)); @@ -184,7 +183,7 @@ impl RtcDriver { }); ::Interrupt::unpend(); - ::Interrupt::enable(); + unsafe { ::Interrupt::enable() }; r.cr1().modify(|w| w.set_cen(true)); }) @@ -193,9 +192,8 @@ impl RtcDriver { fn on_interrupt(&self) { let r = T::regs_gp16(); - // NOTE(unsafe) Use critical section to access the methods // XXX: reduce the size of this critical section ? - critical_section::with(|cs| unsafe { + critical_section::with(|cs| { let sr = r.sr().read(); let dier = r.dier().read(); @@ -228,7 +226,7 @@ impl RtcDriver { let period = self.period.fetch_add(1, Ordering::Relaxed) + 1; let t = (period as u64) << 15; - critical_section::with(move |cs| unsafe { + critical_section::with(move |cs| { r.dier().modify(move |w| { for n in 0..ALARM_COUNT { let alarm = &self.alarms.borrow(cs)[n]; @@ -269,8 +267,7 @@ impl Driver for RtcDriver { let period = self.period.load(Ordering::Relaxed); compiler_fence(Ordering::Acquire); - // NOTE(unsafe) Atomic read with no side-effects - let counter = unsafe { r.cnt().read().cnt() }; + let counter = r.cnt().read().cnt(); calc_now(period, counter) } @@ -310,7 +307,7 @@ impl Driver for RtcDriver { if timestamp <= t { // If alarm timestamp has passed the alarm will not fire. // Disarm the alarm and return `false` to indicate that. - unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) }; + r.dier().modify(|w| w.set_ccie(n + 1, false)); alarm.timestamp.set(u64::MAX); @@ -321,12 +318,11 @@ impl Driver for RtcDriver { // Write the CCR value regardless of whether we're going to enable it now or not. // This way, when we enable it later, the right value is already set. - unsafe { r.ccr(n + 1).write(|w| w.set_ccr(safe_timestamp as u16)) }; + r.ccr(n + 1).write(|w| w.set_ccr(safe_timestamp as u16)); // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. let diff = timestamp - t; - // NOTE(unsafe) We're in a critical section - unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) }; + r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); true }) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 14db97024..09b7a3776 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -60,25 +60,19 @@ macro_rules! impl_basic_16bit_timer { type Interrupt = crate::interrupt::typelevel::$irq; fn regs() -> crate::pac::timer::TimBasic { - crate::pac::timer::TimBasic(crate::pac::$inst.0) + unsafe { crate::pac::timer::TimBasic::from_ptr(crate::pac::$inst.as_ptr()) } } fn start(&mut self) { - unsafe { - Self::regs().cr1().modify(|r| r.set_cen(true)); - } + Self::regs().cr1().modify(|r| r.set_cen(true)); } fn stop(&mut self) { - unsafe { - Self::regs().cr1().modify(|r| r.set_cen(false)); - } + Self::regs().cr1().modify(|r| r.set_cen(false)); } fn reset(&mut self) { - unsafe { - Self::regs().cnt().write(|r| r.set_cnt(0)); - } + Self::regs().cnt().write(|r| r.set_cnt(0)); } fn set_frequency(&mut self, frequency: Hertz) { @@ -90,35 +84,29 @@ macro_rules! impl_basic_16bit_timer { let arr: u16 = unwrap!((pclk_ticks_per_timer_period / (u32::from(psc) + 1)).try_into()); let regs = Self::regs(); - unsafe { - regs.psc().write(|r| r.set_psc(psc)); - regs.arr().write(|r| r.set_arr(arr)); + regs.psc().write(|r| r.set_psc(psc)); + regs.arr().write(|r| r.set_arr(arr)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } + regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); } fn clear_update_interrupt(&mut self) -> bool { let regs = Self::regs(); - unsafe { - let sr = regs.sr().read(); - if sr.uif() { - regs.sr().modify(|r| { - r.set_uif(false); - }); - true - } else { - false - } + let sr = regs.sr().read(); + if sr.uif() { + regs.sr().modify(|r| { + r.set_uif(false); + }); + true + } else { + false } } fn enable_update_interrupt(&mut self, enable: bool) { - unsafe { - Self::regs().dier().write(|r| r.set_uie(enable)); - } + Self::regs().dier().write(|r| r.set_uie(enable)); } } }; @@ -141,14 +129,12 @@ macro_rules! impl_32bit_timer { let arr: u32 = unwrap!(((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into())); let regs = Self::regs_gp32(); - unsafe { - regs.psc().write(|r| r.set_psc(psc)); - regs.arr().write(|r| r.set_arr(arr)); + regs.psc().write(|r| r.set_psc(psc)); + regs.arr().write(|r| r.set_arr(arr)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } + regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); } } }; @@ -185,7 +171,7 @@ foreach_interrupt! { impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { - crate::pac::timer::TimGp16(crate::pac::$inst.0) + unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } } } @@ -206,7 +192,7 @@ foreach_interrupt! { impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { - crate::pac::timer::TimGp16(crate::pac::$inst.0) + unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 086196a2c..433ad299c 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -19,68 +19,64 @@ impl interrupt::typelevel::Handler for Interrupt let state = T::buffered_state(); // RX - unsafe { - let sr = sr(r).read(); - // On v1 & v2, reading DR clears the rxne, error and idle interrupt - // flags. Keep this close to the SR read to reduce the chance of a - // flag being set in-between. - let dr = if sr.rxne() || cfg!(any(usart_v1, usart_v2)) && (sr.ore() || sr.idle()) { - Some(rdr(r).read_volatile()) + let sr_val = sr(r).read(); + // On v1 & v2, reading DR clears the rxne, error and idle interrupt + // flags. Keep this close to the SR read to reduce the chance of a + // flag being set in-between. + let dr = if sr_val.rxne() || cfg!(any(usart_v1, usart_v2)) && (sr_val.ore() || sr_val.idle()) { + Some(rdr(r).read_volatile()) + } else { + None + }; + clear_interrupt_flags(r, sr_val); + + if sr_val.pe() { + warn!("Parity error"); + } + if sr_val.fe() { + warn!("Framing error"); + } + if sr_val.ne() { + warn!("Noise error"); + } + if sr_val.ore() { + warn!("Overrun error"); + } + if sr_val.rxne() { + let mut rx_writer = state.rx_buf.writer(); + let buf = rx_writer.push_slice(); + if !buf.is_empty() { + buf[0] = dr.unwrap(); + rx_writer.push_done(1); } else { - None - }; - clear_interrupt_flags(r, sr); - - if sr.pe() { - warn!("Parity error"); - } - if sr.fe() { - warn!("Framing error"); - } - if sr.ne() { - warn!("Noise error"); - } - if sr.ore() { - warn!("Overrun error"); - } - if sr.rxne() { - let mut rx_writer = state.rx_buf.writer(); - let buf = rx_writer.push_slice(); - if !buf.is_empty() { - buf[0] = dr.unwrap(); - rx_writer.push_done(1); - } else { - // FIXME: Should we disable any further RX interrupts when the buffer becomes full. - } - - if state.rx_buf.is_full() { - state.rx_waker.wake(); - } + // FIXME: Should we disable any further RX interrupts when the buffer becomes full. } - if sr.idle() { + if state.rx_buf.is_full() { state.rx_waker.wake(); } } + if sr_val.idle() { + state.rx_waker.wake(); + } + // TX - unsafe { - if sr(r).read().txe() { - let mut tx_reader = state.tx_buf.reader(); - let buf = tx_reader.pop_slice(); - if !buf.is_empty() { - r.cr1().modify(|w| { - w.set_txeie(true); - }); - tdr(r).write_volatile(buf[0].into()); - tx_reader.pop_done(1); - state.tx_waker.wake(); - } else { - // Disable interrupt until we have something to transmit again - r.cr1().modify(|w| { - w.set_txeie(false); - }); - } + if sr(r).read().txe() { + let mut tx_reader = state.tx_buf.reader(); + let buf = tx_reader.pop_slice(); + if !buf.is_empty() { + r.cr1().modify(|w| { + w.set_txeie(true); + }); + tdr(r).write_volatile(buf[0].into()); + tx_reader.pop_done(1); + state.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.cr1().modify(|w| { + w.set_txeie(false); + }); } } } @@ -150,14 +146,12 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::enable(); T::reset(); - unsafe { - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_rtse(true); - w.set_ctse(true); - }); - } + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) } @@ -178,12 +172,10 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { T::enable(); T::reset(); - unsafe { - de.set_as_af(de.af_num(), AFType::OutputPushPull); - T::regs().cr3().write(|w| { - w.set_dem(true); - }); - } + de.set_as_af(de.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_dem(true); + }); Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) } @@ -205,22 +197,18 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; let r = T::regs(); - unsafe { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); configure(r, &config, T::frequency(), T::KIND, true, true); - unsafe { - r.cr1().modify(|w| { - #[cfg(lpuart_v2)] - w.set_fifoen(true); + r.cr1().modify(|w| { + #[cfg(lpuart_v2)] + w.set_fifoen(true); - w.set_rxneie(true); - w.set_idleie(true); - }); - } + w.set_rxneie(true); + w.set_idleie(true); + }); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index da3644a81..47a79c187 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -36,35 +36,31 @@ impl interrupt::typelevel::Handler for Interrupt let r = T::regs(); let s = T::state(); - let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; + let (sr, cr1, cr3) = (sr(r).read(), r.cr1().read(), r.cr3().read()); let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); if has_errors { // clear all interrupts and DMA Rx Request - unsafe { - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // disable parity interrupt - w.set_peie(false); - // disable idle line interrupt - w.set_idleie(false); - }); - r.cr3().modify(|w| { - // disable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(false); - // disable DMA Rx Request - w.set_dmar(false); - }); - } + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); } else if cr1.idleie() && sr.idle() { // IDLE detected: no more data will come - unsafe { - r.cr1().modify(|w| { - // disable idle line detection - w.set_idleie(false); - }); - } + r.cr1().modify(|w| { + // disable idle line detection + w.set_idleie(false); + }); } else if cr1.rxneie() { // We cannot check the RXNE flag as it is auto-cleared by the DMA controller @@ -205,12 +201,10 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { T::enable(); T::reset(); - unsafe { - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_ctse(true); - }); - } + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_ctse(true); + }); Self::new_inner(peri, tx, tx_dma, config) } @@ -224,9 +218,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { let r = T::regs(); - unsafe { - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); configure(r, &config, T::frequency(), T::KIND, false, true); @@ -245,11 +237,9 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { { let ch = &mut self.tx_dma; let request = ch.request(); - unsafe { - T::regs().cr3().modify(|reg| { - reg.set_dmat(true); - }); - } + T::regs().cr3().modify(|reg| { + reg.set_dmat(true); + }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) }; @@ -258,21 +248,17 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { - unsafe { - let r = T::regs(); - for &b in buffer { - while !sr(r).read().txe() {} - tdr(r).write_volatile(b); - } + let r = T::regs(); + for &b in buffer { + while !sr(r).read().txe() {} + unsafe { tdr(r).write_volatile(b) }; } Ok(()) } pub fn blocking_flush(&mut self) -> Result<(), Error> { - unsafe { - let r = T::regs(); - while !sr(r).read().tc() {} - } + let r = T::regs(); + while !sr(r).read().tc() {} Ok(()) } } @@ -305,12 +291,10 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { T::enable(); T::reset(); - unsafe { - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - T::regs().cr3().write(|w| { - w.set_rtse(true); - }); - } + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_rtse(true); + }); Self::new_inner(peri, rx, rx_dma, config) } @@ -325,9 +309,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let r = T::regs(); - unsafe { - rx.set_as_af(rx.af_num(), AFType::Input); - } + rx.set_as_af(rx.af_num(), AFType::Input); configure(r, &config, T::frequency(), T::KIND, true, false); @@ -347,7 +329,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } #[cfg(any(usart_v1, usart_v2))] - unsafe fn check_rx_flags(&mut self) -> Result { + fn check_rx_flags(&mut self) -> Result { let r = T::regs(); loop { // Handle all buffered error flags. @@ -380,7 +362,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } #[cfg(any(usart_v3, usart_v4))] - unsafe fn check_rx_flags(&mut self) -> Result { + fn check_rx_flags(&mut self) -> Result { let r = T::regs(); let sr = r.isr().read(); if sr.pe() { @@ -410,22 +392,18 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { pub fn nb_read(&mut self) -> Result> { let r = T::regs(); - unsafe { - if self.check_rx_flags()? { - Ok(rdr(r).read_volatile()) - } else { - Err(nb::Error::WouldBlock) - } + if self.check_rx_flags()? { + Ok(unsafe { rdr(r).read_volatile() }) + } else { + Err(nb::Error::WouldBlock) } } pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - unsafe { - let r = T::regs(); - for b in buffer { - while !self.check_rx_flags()? {} - *b = rdr(r).read_volatile(); - } + let r = T::regs(); + for b in buffer { + while !self.check_rx_flags()? {} + unsafe { *b = rdr(r).read_volatile() } } Ok(()) } @@ -451,23 +429,20 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let on_drop = OnDrop::new(move || { // defmt::trace!("Clear all USART interrupts and DMA Read Request"); // clear all interrupts and DMA Rx Request - // SAFETY: only clears Rx related flags - unsafe { - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // disable parity interrupt - w.set_peie(false); - // disable idle line interrupt - w.set_idleie(false); - }); - r.cr3().modify(|w| { - // disable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(false); - // disable DMA Rx Request - w.set_dmar(false); - }); - } + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); }); let ch = &mut self.rx_dma; @@ -480,78 +455,74 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // future which will complete when DMA Read request completes let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) }; - // SAFETY: The only way we might have a problem is using split rx and tx - // here we only modify or read Rx related flags, interrupts and DMA channel - unsafe { - // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer - if !self.detect_previous_overrun { - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); + // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer + if !self.detect_previous_overrun { + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); + } + + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // enable parity interrupt if not ParityNone + w.set_peie(w.pce()); + }); + + r.cr3().modify(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + // enable DMA Rx Request + w.set_dmar(true); + }); + + compiler_fence(Ordering::SeqCst); + + // In case of errors already pending when reception started, interrupts may have already been raised + // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts + // have been disabled in interrupt handler and DMA Rx Request has been disabled. + + let cr3 = r.cr3().read(); + + if !cr3.dmar() { + // something went wrong + // because the only way to get this flag cleared is to have an interrupt + + // DMA will be stopped when transfer is dropped + + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); + + if sr.pe() { + return Err(Error::Parity); + } + if sr.fe() { + return Err(Error::Framing); + } + if sr.ne() { + return Err(Error::Noise); + } + if sr.ore() { + return Err(Error::Overrun); } + unreachable!(); + } + + if enable_idle_line_detection { + // clear idle flag + let sr = sr(r).read(); + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); + + // enable idle interrupt r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // enable parity interrupt if not ParityNone - w.set_peie(w.pce()); + w.set_idleie(true); }); - - r.cr3().modify(|w| { - // enable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(true); - // enable DMA Rx Request - w.set_dmar(true); - }); - - compiler_fence(Ordering::SeqCst); - - // In case of errors already pending when reception started, interrupts may have already been raised - // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts - // have been disabled in interrupt handler and DMA Rx Request has been disabled. - - let cr3 = r.cr3().read(); - - if !cr3.dmar() { - // something went wrong - // because the only way to get this flag cleared is to have an interrupt - - // DMA will be stopped when transfer is dropped - - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); - - if sr.pe() { - return Err(Error::Parity); - } - if sr.fe() { - return Err(Error::Framing); - } - if sr.ne() { - return Err(Error::Noise); - } - if sr.ore() { - return Err(Error::Overrun); - } - - unreachable!(); - } - - if enable_idle_line_detection { - // clear idle flag - let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); - - // enable idle interrupt - r.cr1().modify(|w| { - w.set_idleie(true); - }); - } } compiler_fence(Ordering::SeqCst); @@ -562,15 +533,11 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { s.rx_waker.register(cx.waker()); - // SAFETY: read only and we only use Rx related flags - let sr = unsafe { sr(r).read() }; + let sr = sr(r).read(); - // SAFETY: only clears Rx related flags - unsafe { - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); - } + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); compiler_fence(Ordering::SeqCst); @@ -677,14 +644,12 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable(); T::reset(); - unsafe { - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_rtse(true); - w.set_ctse(true); - }); - } + rts.set_as_af(rts.af_num(), AFType::OutputPushPull); + cts.set_as_af(cts.af_num(), AFType::Input); + T::regs().cr3().write(|w| { + w.set_rtse(true); + w.set_ctse(true); + }); Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) } @@ -704,12 +669,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { T::enable(); T::reset(); - unsafe { - de.set_as_af(de.af_num(), AFType::OutputPushPull); - T::regs().cr3().write(|w| { - w.set_dem(true); - }); - } + de.set_as_af(de.af_num(), AFType::OutputPushPull); + T::regs().cr3().write(|w| { + w.set_dem(true); + }); Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config) } @@ -725,10 +688,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let r = T::regs(); - unsafe { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); configure(r, &config, T::frequency(), T::KIND, true, true); @@ -847,11 +808,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { over8 = true; let div = div as u32; - unsafe { - r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); - } + r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); found = Some(div); break; } @@ -860,11 +819,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: if div < brr_max { let div = div as u32; - unsafe { - r.brr().write_value(regs::Brr(div)); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); - } + r.brr().write_value(regs::Brr(div)); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); found = Some(div); break; } @@ -883,44 +840,42 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: pclk_freq.0 / div ); - unsafe { - r.cr2().write(|w| { - w.set_stop(match config.stop_bits { - StopBits::STOP0P5 => vals::Stop::STOP0P5, - StopBits::STOP1 => vals::Stop::STOP1, - StopBits::STOP1P5 => vals::Stop::STOP1P5, - StopBits::STOP2 => vals::Stop::STOP2, - }); + r.cr2().write(|w| { + w.set_stop(match config.stop_bits { + StopBits::STOP0P5 => vals::Stop::STOP0P5, + StopBits::STOP1 => vals::Stop::STOP1, + StopBits::STOP1P5 => vals::Stop::STOP1P5, + StopBits::STOP2 => vals::Stop::STOP2, }); - r.cr1().write(|w| { - // enable uart - w.set_ue(true); - // enable transceiver - w.set_te(enable_tx); - // enable receiver - w.set_re(enable_rx); - // configure word size - w.set_m0(if config.parity != Parity::ParityNone { - vals::M0::BIT9 - } else { - vals::M0::BIT8 - }); - // configure parity - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => vals::Ps::ODD, - Parity::ParityEven => vals::Ps::EVEN, - _ => vals::Ps::EVEN, - }); - #[cfg(not(usart_v1))] - w.set_over8(vals::Over8(over8 as _)); + }); + r.cr1().write(|w| { + // enable uart + w.set_ue(true); + // enable transceiver + w.set_te(enable_tx); + // enable receiver + w.set_re(enable_rx); + // configure word size + w.set_m0(if config.parity != Parity::ParityNone { + vals::M0::BIT9 + } else { + vals::M0::BIT8 + }); + // configure parity + w.set_pce(config.parity != Parity::ParityNone); + w.set_ps(match config.parity { + Parity::ParityOdd => vals::Ps::ODD, + Parity::ParityEven => vals::Ps::EVEN, + _ => vals::Ps::EVEN, }); - #[cfg(not(usart_v1))] - r.cr3().modify(|w| { - w.set_onebit(config.assume_noise_free); - }); - } + w.set_over8(vals::Over8(over8 as _)); + }); + + #[cfg(not(usart_v1))] + r.cr3().modify(|w| { + w.set_onebit(config.assume_noise_free); + }); } mod eh02 { @@ -1111,12 +1066,12 @@ use self::sealed::Kind; #[cfg(any(usart_v1, usart_v2))] fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { - r.dr().ptr() as _ + r.dr().as_ptr() as _ } #[cfg(any(usart_v1, usart_v2))] fn rdr(r: crate::pac::usart::Usart) -> *mut u8 { - r.dr().ptr() as _ + r.dr().as_ptr() as _ } #[cfg(any(usart_v1, usart_v2))] @@ -1126,18 +1081,18 @@ fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg *mut u8 { - r.tdr().ptr() as _ + r.tdr().as_ptr() as _ } #[cfg(any(usart_v3, usart_v4))] fn rdr(r: Regs) -> *mut u8 { - r.rdr().ptr() as _ + r.rdr().as_ptr() as _ } #[cfg(any(usart_v3, usart_v4))] @@ -1147,7 +1102,7 @@ fn sr(r: Regs) -> crate::pac::common::Reg { #[cfg(any(usart_v3, usart_v4))] #[allow(unused)] -unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { +fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { r.icr().write(|w| *w = regs::Icr(sr.0)); } @@ -1214,7 +1169,7 @@ macro_rules! impl_usart { type Interrupt = crate::interrupt::typelevel::$irq; fn regs() -> Regs { - Regs(crate::pac::$inst.0) + unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) } } fn state() -> &'static crate::usart::sealed::State { diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 511b71c7f..c74d7e092 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -59,23 +59,20 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD let r = T::regs(); // clear all interrupts and DMA Rx Request - // SAFETY: only clears Rx related flags - unsafe { - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // enable parity interrupt if not ParityNone - w.set_peie(w.pce()); - // enable idle line interrupt - w.set_idleie(true); - }); - r.cr3().modify(|w| { - // enable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(true); - // enable DMA Rx Request - w.set_dmar(true); - }); - } + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // enable parity interrupt if not ParityNone + w.set_peie(w.pce()); + // enable idle line interrupt + w.set_idleie(true); + }); + r.cr3().modify(|w| { + // enable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(true); + // enable DMA Rx Request + w.set_dmar(true); + }); } /// Stop uart background receive @@ -84,23 +81,20 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD let r = T::regs(); // clear all interrupts and DMA Rx Request - // SAFETY: only clears Rx related flags - unsafe { - r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); - // disable parity interrupt - w.set_peie(false); - // disable idle line interrupt - w.set_idleie(false); - }); - r.cr3().modify(|w| { - // disable Error Interrupt: (Frame error, Noise error, Overrun error) - w.set_eie(false); - // disable DMA Rx Request - w.set_dmar(false); - }); - } + r.cr1().modify(|w| { + // disable RXNE interrupt + w.set_rxneie(false); + // disable parity interrupt + w.set_peie(false); + // disable idle line interrupt + w.set_idleie(false); + }); + r.cr3().modify(|w| { + // disable Error Interrupt: (Frame error, Noise error, Overrun error) + w.set_eie(false); + // disable DMA Rx Request + w.set_dmar(false); + }); compiler_fence(Ordering::SeqCst); } @@ -117,8 +111,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD let r = T::regs(); // Start background receive if it was not already started - // SAFETY: read only - match unsafe { r.cr3().read().dmar() } { + match r.cr3().read().dmar() { false => self.start()?, _ => {} }; @@ -213,19 +206,17 @@ fn check_for_errors(s: Sr) -> Result<(), Error> { /// Clear IDLE and return the Sr register fn clear_idle_flag(r: Regs) -> Sr { - unsafe { - // SAFETY: read only and we only use Rx related flags + // SAFETY: read only and we only use Rx related flags - let sr = sr(r).read(); + let sr = sr(r).read(); - // This read also clears the error and idle interrupt flags on v1. - rdr(r).read_volatile(); - clear_interrupt_flags(r, sr); + // This read also clears the error and idle interrupt flags on v1. + unsafe { rdr(r).read_volatile() }; + clear_interrupt_flags(r, sr); - r.cr1().modify(|w| w.set_idleie(true)); + r.cr1().modify(|w| w.set_idleie(true)); - sr - } + sr } #[cfg(all(feature = "unstable-traits", feature = "nightly"))] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 7486bd376..2367127e8 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -28,82 +28,80 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - unsafe { - let regs = T::regs(); - //let x = regs.istr().read().0; - //trace!("USB IRQ: {:08x}", x); + let regs = T::regs(); + //let x = regs.istr().read().0; + //trace!("USB IRQ: {:08x}", x); - let istr = regs.istr().read(); + let istr = regs.istr().read(); - if istr.susp() { - //trace!("USB IRQ: susp"); - IRQ_SUSPEND.store(true, Ordering::Relaxed); - regs.cntr().modify(|w| { - w.set_fsusp(true); - w.set_lpmode(true); - }); + if istr.susp() { + //trace!("USB IRQ: susp"); + IRQ_SUSPEND.store(true, Ordering::Relaxed); + regs.cntr().modify(|w| { + w.set_fsusp(true); + w.set_lpmode(true); + }); - // Write 0 to clear. - let mut clear = regs::Istr(!0); - clear.set_susp(false); - regs.istr().write_value(clear); + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_susp(false); + regs.istr().write_value(clear); - // Wake main thread. - BUS_WAKER.wake(); - } + // Wake main thread. + BUS_WAKER.wake(); + } - if istr.wkup() { - //trace!("USB IRQ: wkup"); - IRQ_RESUME.store(true, Ordering::Relaxed); - regs.cntr().modify(|w| { - w.set_fsusp(false); - w.set_lpmode(false); - }); + if istr.wkup() { + //trace!("USB IRQ: wkup"); + IRQ_RESUME.store(true, Ordering::Relaxed); + regs.cntr().modify(|w| { + w.set_fsusp(false); + w.set_lpmode(false); + }); - // Write 0 to clear. - let mut clear = regs::Istr(!0); - clear.set_wkup(false); - regs.istr().write_value(clear); + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_wkup(false); + regs.istr().write_value(clear); - // Wake main thread. - BUS_WAKER.wake(); - } + // Wake main thread. + BUS_WAKER.wake(); + } - if istr.reset() { - //trace!("USB IRQ: reset"); - IRQ_RESET.store(true, Ordering::Relaxed); + if istr.reset() { + //trace!("USB IRQ: reset"); + IRQ_RESET.store(true, Ordering::Relaxed); - // Write 0 to clear. - let mut clear = regs::Istr(!0); - clear.set_reset(false); - regs.istr().write_value(clear); + // Write 0 to clear. + let mut clear = regs::Istr(!0); + clear.set_reset(false); + regs.istr().write_value(clear); - // Wake main thread. - BUS_WAKER.wake(); - } + // Wake main thread. + BUS_WAKER.wake(); + } - if istr.ctr() { - let index = istr.ep_id() as usize; - let mut epr = regs.epr(index).read(); - if epr.ctr_rx() { - if index == 0 && epr.setup() { - EP0_SETUP.store(true, Ordering::Relaxed); - } - //trace!("EP {} RX, setup={}", index, epr.setup()); - EP_OUT_WAKERS[index].wake(); + if istr.ctr() { + let index = istr.ep_id() as usize; + let mut epr = regs.epr(index).read(); + if epr.ctr_rx() { + if index == 0 && epr.setup() { + EP0_SETUP.store(true, Ordering::Relaxed); } - if epr.ctr_tx() { - //trace!("EP {} TX", index); - EP_IN_WAKERS[index].wake(); - } - epr.set_dtog_rx(false); - epr.set_dtog_tx(false); - epr.set_stat_rx(Stat(0)); - epr.set_stat_tx(Stat(0)); - epr.set_ctr_rx(!epr.ctr_rx()); - epr.set_ctr_tx(!epr.ctr_tx()); - regs.epr(index).write_value(epr); + //trace!("EP {} RX, setup={}", index, epr.setup()); + EP_OUT_WAKERS[index].wake(); } + if epr.ctr_tx() { + //trace!("EP {} TX", index); + EP_IN_WAKERS[index].wake(); + } + epr.set_dtog_rx(false); + epr.set_dtog_tx(false); + epr.set_stat_rx(Stat(0)); + epr.set_stat_tx(Stat(0)); + epr.set_ctr_rx(!epr.ctr_rx()); + epr.set_ctr_tx(!epr.ctr_tx()); + regs.epr(index).write_value(epr); } } } @@ -168,20 +166,20 @@ fn calc_out_len(len: u16) -> (u16, u16) { mod btable { use super::*; - pub(super) unsafe fn write_in(index: usize, addr: u16) { + pub(super) fn write_in(index: usize, addr: u16) { USBRAM.mem(index * 4 + 0).write_value(addr); } - pub(super) unsafe fn write_in_len(index: usize, _addr: u16, len: u16) { + pub(super) fn write_in_len(index: usize, _addr: u16, len: u16) { USBRAM.mem(index * 4 + 1).write_value(len); } - pub(super) unsafe fn write_out(index: usize, addr: u16, max_len_bits: u16) { + pub(super) fn write_out(index: usize, addr: u16, max_len_bits: u16) { USBRAM.mem(index * 4 + 2).write_value(addr); USBRAM.mem(index * 4 + 3).write_value(max_len_bits); } - pub(super) unsafe fn read_out_len(index: usize) -> u16 { + pub(super) fn read_out_len(index: usize) -> u16 { USBRAM.mem(index * 4 + 3).read() } } @@ -189,19 +187,19 @@ mod btable { mod btable { use super::*; - pub(super) unsafe fn write_in(_index: usize, _addr: u16) {} + pub(super) fn write_in(_index: usize, _addr: u16) {} - pub(super) unsafe fn write_in_len(index: usize, addr: u16, len: u16) { + pub(super) fn write_in_len(index: usize, addr: u16, len: u16) { USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); } - pub(super) unsafe fn write_out(index: usize, addr: u16, max_len_bits: u16) { + pub(super) fn write_out(index: usize, addr: u16, max_len_bits: u16) { USBRAM .mem(index * 2 + 1) .write_value((addr as u32) | ((max_len_bits as u32) << 16)); } - pub(super) unsafe fn read_out_len(index: usize) -> u16 { + pub(super) fn read_out_len(index: usize) -> u16 { (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 } } @@ -216,7 +214,7 @@ impl EndpointBuffer { fn read(&mut self, buf: &mut [u8]) { assert!(buf.len() <= self.len as usize); for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN { - let val = unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read() }; + let val = USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read(); let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN); buf[i * USBRAM_ALIGN..][..n].copy_from_slice(&val.to_le_bytes()[..n]); } @@ -233,7 +231,7 @@ impl EndpointBuffer { let val = u16::from_le_bytes(val); #[cfg(usbram_32_2048)] let val = u32::from_le_bytes(val); - unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val) }; + USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val); } } } @@ -266,36 +264,32 @@ impl<'d, T: Instance> Driver<'d, T> { let regs = T::regs(); #[cfg(stm32l5)] - unsafe { + { crate::peripherals::PWR::enable(); crate::pac::PWR.cr2().modify(|w| w.set_usv(true)); } #[cfg(pwr_h5)] - unsafe { - crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)) - } + crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)); - unsafe { - ::enable(); - ::reset(); + ::enable(); + ::reset(); - regs.cntr().write(|w| { - w.set_pdwn(false); - w.set_fres(true); - }); + regs.cntr().write(|w| { + w.set_pdwn(false); + w.set_fres(true); + }); - #[cfg(time)] - embassy_time::block_for(embassy_time::Duration::from_millis(100)); - #[cfg(not(time))] - cortex_m::asm::delay(crate::rcc::get_freqs().sys.0 / 10); + #[cfg(time)] + embassy_time::block_for(embassy_time::Duration::from_millis(100)); + #[cfg(not(time))] + cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.0 / 10); - #[cfg(not(usb_v4))] - regs.btable().write(|w| w.set_btable(0)); + #[cfg(not(usb_v4))] + regs.btable().write(|w| w.set_btable(0)); - dp.set_as_af(dp.af_num(), AFType::OutputPushPull); - dm.set_as_af(dm.af_num(), AFType::OutputPushPull); - } + dp.set_as_af(dp.af_num(), AFType::OutputPushPull); + dm.set_as_af(dm.af_num(), AFType::OutputPushPull); // Initialize the bus so that it signals that power is available BUS_WAKER.wake(); @@ -363,7 +357,7 @@ impl<'d, T: Instance> Driver<'d, T> { let addr = self.alloc_ep_mem(len); trace!(" len_bits = {:04x}", len_bits); - unsafe { btable::write_out::(index, addr, len_bits) } + btable::write_out::(index, addr, len_bits); EndpointBuffer { addr, @@ -379,7 +373,7 @@ impl<'d, T: Instance> Driver<'d, T> { let addr = self.alloc_ep_mem(len); // ep_in_len is written when actually TXing packets. - unsafe { btable::write_in::(index, addr) } + btable::write_in::(index, addr); EndpointBuffer { addr, @@ -440,19 +434,17 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { let regs = T::regs(); - unsafe { - regs.cntr().write(|w| { - w.set_pdwn(false); - w.set_fres(false); - w.set_resetm(true); - w.set_suspm(true); - w.set_wkupm(true); - w.set_ctrm(true); - }); + regs.cntr().write(|w| { + w.set_pdwn(false); + w.set_fres(false); + w.set_resetm(true); + w.set_suspm(true); + w.set_wkupm(true); + w.set_ctrm(true); + }); - #[cfg(any(usb_v3, usb_v4))] - regs.bcdr().write(|w| w.set_dppu(true)) - } + #[cfg(any(usb_v3, usb_v4))] + regs.bcdr().write(|w| w.set_dppu(true)); trace!("enabled"); @@ -485,7 +477,7 @@ pub struct Bus<'d, T: Instance> { impl<'d, T: Instance> driver::Bus for Bus<'d, T> { async fn poll(&mut self) -> Event { - poll_fn(move |cx| unsafe { + poll_fn(move |cx| { BUS_WAKER.register(cx.waker()); if self.inited { @@ -548,7 +540,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { match ep_addr.direction() { Direction::In => { loop { - let r = unsafe { reg.read() }; + let r = reg.read(); match r.stat_tx() { Stat::DISABLED => break, // if disabled, stall does nothing. Stat::STALL => break, // done! @@ -559,7 +551,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { }; let mut w = invariant(r); w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); - unsafe { reg.write_value(w) }; + reg.write_value(w); } } } @@ -567,7 +559,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } Direction::Out => { loop { - let r = unsafe { reg.read() }; + let r = reg.read(); match r.stat_rx() { Stat::DISABLED => break, // if disabled, stall does nothing. Stat::STALL => break, // done! @@ -578,7 +570,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { }; let mut w = invariant(r); w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); - unsafe { reg.write_value(w) }; + reg.write_value(w); } } } @@ -589,7 +581,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { let regs = T::regs(); - let epr = unsafe { regs.epr(ep_addr.index() as _).read() }; + let epr = regs.epr(ep_addr.index() as _).read(); match ep_addr.direction() { Direction::In => epr.stat_tx() == Stat::STALL, Direction::Out => epr.stat_rx() == Stat::STALL, @@ -600,7 +592,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { trace!("set_enabled {:x} {}", ep_addr, enabled); // This can race, so do a retry loop. let reg = T::regs().epr(ep_addr.index() as _); - trace!("EPR before: {:04x}", unsafe { reg.read() }.0); + trace!("EPR before: {:04x}", reg.read().0); match ep_addr.direction() { Direction::In => { loop { @@ -608,13 +600,13 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { false => Stat::DISABLED, true => Stat::NAK, }; - let r = unsafe { reg.read() }; + let r = reg.read(); if r.stat_tx() == want_stat { break; } let mut w = invariant(r); w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); - unsafe { reg.write_value(w) }; + reg.write_value(w); } EP_IN_WAKERS[ep_addr.index()].wake(); } @@ -624,18 +616,18 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { false => Stat::DISABLED, true => Stat::VALID, }; - let r = unsafe { reg.read() }; + let r = reg.read(); if r.stat_rx() == want_stat { break; } let mut w = invariant(r); w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); - unsafe { reg.write_value(w) }; + reg.write_value(w); } EP_OUT_WAKERS[ep_addr.index()].wake(); } } - trace!("EPR after: {:04x}", unsafe { reg.read() }.0); + trace!("EPR after: {:04x}", reg.read().0); } async fn enable(&mut self) {} @@ -685,12 +677,12 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> { fn write_data(&mut self, buf: &[u8]) { let index = self.info.addr.index(); self.buf.write(buf); - unsafe { btable::write_in_len::(index, self.buf.addr, buf.len() as _) } + btable::write_in_len::(index, self.buf.addr, buf.len() as _); } fn read_data(&mut self, buf: &mut [u8]) -> Result { let index = self.info.addr.index(); - let rx_len = unsafe { btable::read_out_len::(index) as usize } & 0x3FF; + let rx_len = btable::read_out_len::(index) as usize & 0x3FF; trace!("READ DONE, rx_len = {}", rx_len); if rx_len > buf.len() { return Err(EndpointError::BufferOverflow); @@ -711,7 +703,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { poll_fn(|cx| { EP_OUT_WAKERS[index].register(cx.waker()); let regs = T::regs(); - if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED { + if regs.epr(index).read().stat_tx() == Stat::DISABLED { Poll::Pending } else { Poll::Ready(()) @@ -733,7 +725,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { poll_fn(|cx| { EP_OUT_WAKERS[index].register(cx.waker()); let regs = T::regs(); - if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED { + if regs.epr(index).read().stat_rx() == Stat::DISABLED { Poll::Pending } else { Poll::Ready(()) @@ -751,7 +743,7 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { 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(); + let stat = regs.epr(index).read().stat_rx(); if matches!(stat, Stat::NAK | Stat::DISABLED) { Poll::Ready(stat) } else { @@ -767,16 +759,14 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { 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 - }) - }; + 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) @@ -795,7 +785,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { 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(); + let stat = regs.epr(index).read().stat_tx(); if matches!(stat, Stat::NAK | Stat::DISABLED) { Poll::Ready(stat) } else { @@ -811,16 +801,14 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { 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 - }) - }; + 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"); @@ -889,22 +877,20 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { } // 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 - }) - } + 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()); 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 regs.epr(0).read().stat_rx() == Stat::NAK { Poll::Ready(()) } else { Poll::Pending @@ -919,19 +905,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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 - }) - }; + 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) } @@ -960,15 +944,13 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { } // 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 - }) - } + 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 + }); } trace!("WRITE WAITING"); @@ -976,7 +958,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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 { + if regs.epr(0).read().stat_tx() == Stat::NAK { Poll::Ready(()) } else { Poll::Pending @@ -992,15 +974,13 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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 - }) - }; + 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 + }); trace!("WRITE OK"); @@ -1014,16 +994,14 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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 - }); - } + 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. @@ -1031,7 +1009,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { poll_fn(|cx| { EP_IN_WAKERS[0].register(cx.waker()); let regs = T::regs(); - if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { + if regs.epr(0).read().stat_tx() == Stat::NAK { Poll::Ready(()) } else { Poll::Pending @@ -1047,16 +1025,14 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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 - }); - } + 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 + }); } async fn accept_set_address(&mut self, addr: u8) { @@ -1064,11 +1040,9 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { let regs = T::regs(); trace!("setting addr: {}", addr); - unsafe { - regs.daddr().write(|w| { - w.set_ef(true); - w.set_add(addr); - }) - } + regs.daddr().write(|w| { + w.set_ef(true); + w.set_add(addr); + }); } } diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs index 317264cbb..12e5f0e60 100644 --- a/embassy-stm32/src/usb_otg/mod.rs +++ b/embassy-stm32/src/usb_otg/mod.rs @@ -148,7 +148,7 @@ foreach_interrupt!( fn regs() -> crate::pac::otg::Otg { // OTG HS registers are a superset of FS registers - crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0) + unsafe { crate::pac::otg::Otg::from_ptr(crate::pac::USB_OTG_HS.as_ptr()) } } #[cfg(feature = "nightly")] diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 16cbf1a95..8af5c7bd5 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -30,19 +30,16 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let state = T::state(); - // SAFETY: atomic read/write - let ints = unsafe { r.gintsts().read() }; + let ints = r.gintsts().read(); if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() { // Mask interrupts and notify `Bus` to process them - unsafe { r.gintmsk().write(|_| {}) }; + r.gintmsk().write(|_| {}); T::state().bus_waker.wake(); } // Handle RX - // SAFETY: atomic read with no side effects - while unsafe { r.gintsts().read().rxflvl() } { - // SAFETY: atomic "pop" register - let status = unsafe { r.grxstsp().read() }; + while r.gintsts().read().rxflvl() { + let status = r.grxstsp().read(); let ep_num = status.epnum() as usize; let len = status.bcnt() as usize; @@ -57,21 +54,15 @@ impl interrupt::typelevel::Handler for InterruptHandl if state.ep0_setup_ready.load(Ordering::Relaxed) == false { // SAFETY: exclusive access ensured by atomic bool let data = unsafe { &mut *state.ep0_setup_data.get() }; - // SAFETY: FIFO reads are exclusive to this IRQ - unsafe { - data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); - } + data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); state.ep0_setup_ready.store(true, Ordering::Release); state.ep_out_wakers[0].wake(); } else { error!("received SETUP before previous finished processing"); // discard FIFO - // SAFETY: FIFO reads are exclusive to IRQ - unsafe { - r.fifo(0).read(); - r.fifo(0).read(); - } + r.fifo(0).read(); + r.fifo(0).read(); } } vals::Pktstsd::OUT_DATA_RX => { @@ -84,8 +75,7 @@ impl interrupt::typelevel::Handler for InterruptHandl for chunk in buf.chunks_mut(4) { // RX FIFO is shared so always read from fifo(0) - // SAFETY: FIFO reads are exclusive to IRQ - let data = unsafe { r.fifo(0).read().0 }; + let data = r.fifo(0).read().0; chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]); } @@ -97,8 +87,7 @@ impl interrupt::typelevel::Handler for InterruptHandl // discard FIFO data let len_words = (len + 3) / 4; for _ in 0..len_words { - // SAFETY: FIFO reads are exclusive to IRQ - unsafe { r.fifo(0).read().data() }; + r.fifo(0).read().data(); } } } @@ -114,24 +103,20 @@ impl interrupt::typelevel::Handler for InterruptHandl // IN endpoint interrupt if ints.iepint() { - // SAFETY: atomic read with no side effects - let mut ep_mask = unsafe { r.daint().read().iepint() }; + let mut ep_mask = r.daint().read().iepint(); let mut ep_num = 0; // Iterate over endpoints while there are non-zero bits in the mask while ep_mask != 0 { if ep_mask & 1 != 0 { - // SAFETY: atomic read with no side effects - let ep_ints = unsafe { r.diepint(ep_num).read() }; + let ep_ints = r.diepint(ep_num).read(); // clear all - // SAFETY: DIEPINT is exclusive to IRQ - unsafe { r.diepint(ep_num).write_value(ep_ints) }; + r.diepint(ep_num).write_value(ep_ints); // TXFE is cleared in DIEPEMPMSK if ep_ints.txfe() { - // SAFETY: DIEPEMPMSK is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { r.diepempmsk().modify(|w| { w.set_ineptxfem(w.ineptxfem() & !(1 << ep_num)); }); @@ -172,8 +157,7 @@ impl interrupt::typelevel::Handler for InterruptHandl macro_rules! config_ulpi_pins { ($($pin:ident),*) => { into_ref!($($pin),*); - // NOTE(unsafe) Exclusive access to the registers - critical_section::with(|_| unsafe { + critical_section::with(|_| { $( $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); #[cfg(gpio_v2)] @@ -298,10 +282,8 @@ impl<'d, T: Instance> Driver<'d, T> { ) -> Self { into_ref!(dp, dm); - unsafe { - dp.set_as_af(dp.af_num(), AFType::OutputPushPull); - dm.set_as_af(dm.af_num(), AFType::OutputPushPull); - } + dp.set_as_af(dp.af_num(), AFType::OutputPushPull); + dm.set_as_af(dm.af_num(), AFType::OutputPushPull); Self { phantom: PhantomData, @@ -508,18 +490,15 @@ pub struct Bus<'d, T: Instance> { impl<'d, T: Instance> Bus<'d, T> { fn restore_irqs() { - // SAFETY: atomic write - unsafe { - T::regs().gintmsk().write(|w| { - w.set_usbrst(true); - w.set_enumdnem(true); - w.set_usbsuspm(true); - w.set_wuim(true); - w.set_iepint(true); - w.set_oepint(true); - w.set_rxflvlm(true); - }); - } + T::regs().gintmsk().write(|w| { + w.set_usbrst(true); + w.set_enumdnem(true); + w.set_usbsuspm(true); + w.set_wuim(true); + w.set_iepint(true); + w.set_oepint(true); + w.set_rxflvlm(true); + }); } } @@ -533,8 +512,7 @@ impl<'d, T: Instance> Bus<'d, T> { let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); trace!("configuring rx fifo size={}", rx_fifo_size_words); - // SAFETY: register is exclusive to `Bus` with `&mut self` - unsafe { r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)) }; + r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); // Configure TX (USB in direction) fifo size for each endpoint let mut fifo_top = rx_fifo_size_words; @@ -549,13 +527,10 @@ impl<'d, T: Instance> Bus<'d, T> { let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; - // SAFETY: register is exclusive to `Bus` with `&mut self` - unsafe { - dieptxf.write(|w| { - w.set_fd(ep.fifo_size_words); - w.set_sa(fifo_top); - }); - } + dieptxf.write(|w| { + w.set_fd(ep.fifo_size_words); + w.set_sa(fifo_top); + }); fifo_top += ep.fifo_size_words; } @@ -575,8 +550,7 @@ impl<'d, T: Instance> Bus<'d, T> { // Configure IN endpoints for (index, ep) in self.ep_in.iter().enumerate() { if let Some(ep) = ep { - // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { r.diepctl(index).write(|w| { if index == 0 { w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); @@ -593,8 +567,7 @@ impl<'d, T: Instance> Bus<'d, T> { // Configure OUT endpoints for (index, ep) in self.ep_out.iter().enumerate() { if let Some(ep) = ep { - // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { r.doepctl(index).write(|w| { if index == 0 { w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); @@ -618,14 +591,11 @@ impl<'d, T: Instance> Bus<'d, T> { } // Enable IRQs for allocated endpoints - // SAFETY: register is exclusive to `Bus` with `&mut self` - unsafe { - r.daintmsk().modify(|w| { - w.set_iepm(ep_irq_mask(&self.ep_in)); - // OUT interrupts not used, handled in RXFLVL - // w.set_oepm(ep_irq_mask(&self.ep_out)); - }); - } + r.daintmsk().modify(|w| { + w.set_iepm(ep_irq_mask(&self.ep_in)); + // OUT interrupts not used, handled in RXFLVL + // w.set_oepm(ep_irq_mask(&self.ep_out)); + }); } fn disable(&mut self) { @@ -634,10 +604,8 @@ impl<'d, T: Instance> Bus<'d, T> { ::disable(); #[cfg(stm32l4)] - unsafe { - crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); - // Cannot disable PWR, because other peripherals might be using it - } + crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); + // Cannot disable PWR, because other peripherals might be using it } } @@ -653,7 +621,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { T::state().bus_waker.register(cx.waker()); - let ints = unsafe { r.gintsts().read() }; + let ints = r.gintsts().read(); if ints.usbrst() { trace!("reset"); @@ -661,34 +629,27 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { self.configure_endpoints(); // Reset address - // SAFETY: DCFG is shared with `ControlPipe` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { r.dcfg().modify(|w| { w.set_dad(0); }); }); - // SAFETY: atomic clear on rc_w1 register - unsafe { r.gintsts().write(|w| w.set_usbrst(true)) }; // clear + r.gintsts().write(|w| w.set_usbrst(true)); // clear Self::restore_irqs(); } if ints.enumdne() { trace!("enumdne"); - // SAFETY: atomic read with no side effects - let speed = unsafe { r.dsts().read().enumspd() }; + let speed = r.dsts().read().enumspd(); trace!(" speed={}", speed.0); - // SAFETY: register is only accessed by `Bus` under `&mut self` - unsafe { - r.gusbcfg().modify(|w| { - w.set_trdt(calculate_trdt(speed, T::frequency())); - }) - }; + r.gusbcfg().modify(|w| { + w.set_trdt(calculate_trdt(speed, T::frequency())); + }); - // SAFETY: atomic clear on rc_w1 register - unsafe { r.gintsts().write(|w| w.set_enumdne(true)) }; // clear + r.gintsts().write(|w| w.set_enumdne(true)); // clear Self::restore_irqs(); return Poll::Ready(Event::Reset); @@ -696,16 +657,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { if ints.usbsusp() { trace!("suspend"); - // SAFETY: atomic clear on rc_w1 register - unsafe { r.gintsts().write(|w| w.set_usbsusp(true)) }; // clear + r.gintsts().write(|w| w.set_usbsusp(true)); // clear Self::restore_irqs(); return Poll::Ready(Event::Suspend); } if ints.wkupint() { trace!("resume"); - // SAFETY: atomic clear on rc_w1 register - unsafe { r.gintsts().write(|w| w.set_wkupint(true)) }; // clear + r.gintsts().write(|w| w.set_wkupint(true)); // clear Self::restore_irqs(); return Poll::Ready(Event::Resume); } @@ -727,8 +686,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { let regs = T::regs(); match ep_addr.direction() { Direction::Out => { - // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { regs.doepctl(ep_addr.index()).modify(|w| { w.set_stall(stalled); }); @@ -737,8 +695,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { T::state().ep_out_wakers[ep_addr.index()].wake(); } Direction::In => { - // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { regs.diepctl(ep_addr.index()).modify(|w| { w.set_stall(stalled); }); @@ -758,10 +715,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { let regs = T::regs(); - // SAFETY: atomic read with no side effects match ep_addr.direction() { - Direction::Out => unsafe { regs.doepctl(ep_addr.index()).read().stall() }, - Direction::In => unsafe { regs.diepctl(ep_addr.index()).read().stall() }, + Direction::Out => regs.doepctl(ep_addr.index()).read().stall(), + Direction::In => regs.diepctl(ep_addr.index()).read().stall(), } } @@ -777,8 +733,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { let r = T::regs(); match ep_addr.direction() { Direction::Out => { - // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { // cancel transfer if active if !enabled && r.doepctl(ep_addr.index()).read().epena() { r.doepctl(ep_addr.index()).modify(|w| { @@ -796,8 +751,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { T::state().ep_out_wakers[ep_addr.index()].wake(); } Direction::In => { - // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { // cancel transfer if active if !enabled && r.diepctl(ep_addr.index()).read().epena() { r.diepctl(ep_addr.index()).modify(|w| { @@ -820,196 +774,193 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { async fn enable(&mut self) { trace!("enable"); - // SAFETY: registers are only accessed by `Bus` under `&mut self` - unsafe { - #[cfg(stm32l4)] - { - crate::peripherals::PWR::enable(); - critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); - } - - #[cfg(stm32f7)] - { - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpien(ulpien); - } else { - w.set_usb_otg_hsen(ulpien); - } - }); - - // Low power mode - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpilpen(ulpien); - } else { - w.set_usb_otg_hslpen(ulpien); - } - }); - }); - } - - #[cfg(stm32h7)] - { - // If true, VDD33USB is generated by internal regulator from VDD50USB - // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) - // TODO: unhardcode - let internal_regulator = false; - - // Enable USB power - critical_section::with(|_| { - crate::pac::PWR.cr3().modify(|w| { - w.set_usb33den(true); - w.set_usbregen(internal_regulator); - }) - }); - - // Wait for USB power to stabilize - while !crate::pac::PWR.cr3().read().usb33rdy() {} - - // Use internal 48MHz HSI clock. Should be enabled in RCC by default. - critical_section::with(|_| { - crate::pac::RCC - .d2ccip2r() - .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) - }); - - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpien(ulpien); - } else { - w.set_usb_otg_fs_ulpien(ulpien); - } - }); - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpilpen(ulpien); - } else { - w.set_usb_otg_fs_ulpilpen(ulpien); - } - }); - }); - } - - #[cfg(stm32u5)] - { - // Enable USB power - critical_section::with(|_| { - crate::pac::RCC.ahb3enr().modify(|w| { - w.set_pwren(true); - }); - cortex_m::asm::delay(2); - - crate::pac::PWR.svmcr().modify(|w| { - w.set_usv(true); - w.set_uvmen(true); - }); - }); - - // Wait for USB power to stabilize - while !crate::pac::PWR.svmsr().read().vddusbrdy() {} - - // Select HSI48 as USB clock source. - critical_section::with(|_| { - crate::pac::RCC.ccipr1().modify(|w| { - w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); - }) - }); - } - - ::enable(); - ::reset(); - - T::Interrupt::unpend(); - T::Interrupt::enable(); - - let r = T::regs(); - let core_id = r.cid().read().0; - info!("Core id {:08x}", core_id); - - // Wait for AHB ready. - while !r.grstctl().read().ahbidl() {} - - // Configure as device. - r.gusbcfg().write(|w| { - // Force device mode - w.set_fdmod(true); - // Enable internal full-speed PHY - w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); - }); - - // Configuring Vbus sense and SOF output - match core_id { - 0x0000_1200 | 0x0000_1100 => { - assert!(self.phy_type != PhyType::InternalHighSpeed); - - r.gccfg_v1().modify(|w| { - // Enable internal full-speed PHY, logic is inverted - w.set_pwrdwn(self.phy_type.internal()); - }); - - // F429-like chips have the GCCFG.NOVBUSSENS bit - r.gccfg_v1().modify(|w| { - w.set_novbussens(true); - w.set_vbusasen(false); - w.set_vbusbsen(false); - w.set_sofouten(false); - }); - } - 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { - // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning - r.gccfg_v2().modify(|w| { - // Enable internal full-speed PHY, logic is inverted - w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); - w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); - }); - - r.gccfg_v2().modify(|w| { - w.set_vbden(false); - }); - - // Force B-peripheral session - r.gotgctl().modify(|w| { - w.set_bvaloen(true); - w.set_bvaloval(true); - }); - } - _ => unimplemented!("Unknown USB core id {:X}", core_id), - } - - // Soft disconnect. - r.dctl().write(|w| w.set_sdis(true)); - - // Set speed. - r.dcfg().write(|w| { - w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); - w.set_dspd(self.phy_type.to_dspd()); - }); - - // Unmask transfer complete EP interrupt - r.diepmsk().write(|w| { - w.set_xfrcm(true); - }); - - // Unmask and clear core interrupts - Bus::::restore_irqs(); - r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); - - // Unmask global interrupt - r.gahbcfg().write(|w| { - w.set_gint(true); // unmask global interrupt - }); - - // Connect - r.dctl().write(|w| w.set_sdis(false)); + #[cfg(stm32l4)] + { + crate::peripherals::PWR::enable(); + critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); } + #[cfg(stm32f7)] + { + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpien(ulpien); + } else { + w.set_usb_otg_hsen(ulpien); + } + }); + + // Low power mode + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpilpen(ulpien); + } else { + w.set_usb_otg_hslpen(ulpien); + } + }); + }); + } + + #[cfg(stm32h7)] + { + // If true, VDD33USB is generated by internal regulator from VDD50USB + // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) + // TODO: unhardcode + let internal_regulator = false; + + // Enable USB power + critical_section::with(|_| { + crate::pac::PWR.cr3().modify(|w| { + w.set_usb33den(true); + w.set_usbregen(internal_regulator); + }) + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.cr3().read().usb33rdy() {} + + // Use internal 48MHz HSI clock. Should be enabled in RCC by default. + critical_section::with(|_| { + crate::pac::RCC + .d2ccip2r() + .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) + }); + + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpien(ulpien); + } else { + w.set_usb_otg_fs_ulpien(ulpien); + } + }); + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpilpen(ulpien); + } else { + w.set_usb_otg_fs_ulpilpen(ulpien); + } + }); + }); + } + + #[cfg(stm32u5)] + { + // Enable USB power + critical_section::with(|_| { + crate::pac::RCC.ahb3enr().modify(|w| { + w.set_pwren(true); + }); + cortex_m::asm::delay(2); + + crate::pac::PWR.svmcr().modify(|w| { + w.set_usv(true); + w.set_uvmen(true); + }); + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.svmsr().read().vddusbrdy() {} + + // Select HSI48 as USB clock source. + critical_section::with(|_| { + crate::pac::RCC.ccipr1().modify(|w| { + w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); + }) + }); + } + + ::enable(); + ::reset(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + let r = T::regs(); + let core_id = r.cid().read().0; + info!("Core id {:08x}", core_id); + + // Wait for AHB ready. + while !r.grstctl().read().ahbidl() {} + + // Configure as device. + r.gusbcfg().write(|w| { + // Force device mode + w.set_fdmod(true); + // Enable internal full-speed PHY + w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); + }); + + // Configuring Vbus sense and SOF output + match core_id { + 0x0000_1200 | 0x0000_1100 => { + assert!(self.phy_type != PhyType::InternalHighSpeed); + + r.gccfg_v1().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal()); + }); + + // F429-like chips have the GCCFG.NOVBUSSENS bit + r.gccfg_v1().modify(|w| { + w.set_novbussens(true); + w.set_vbusasen(false); + w.set_vbusbsen(false); + w.set_sofouten(false); + }); + } + 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { + // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning + r.gccfg_v2().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); + w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); + }); + + r.gccfg_v2().modify(|w| { + w.set_vbden(false); + }); + + // Force B-peripheral session + r.gotgctl().modify(|w| { + w.set_bvaloen(true); + w.set_bvaloval(true); + }); + } + _ => unimplemented!("Unknown USB core id {:X}", core_id), + } + + // Soft disconnect. + r.dctl().write(|w| w.set_sdis(true)); + + // Set speed. + r.dcfg().write(|w| { + w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); + w.set_dspd(self.phy_type.to_dspd()); + }); + + // Unmask transfer complete EP interrupt + r.diepmsk().write(|w| { + w.set_xfrcm(true); + }); + + // Unmask and clear core interrupts + Bus::::restore_irqs(); + r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); + + // Unmask global interrupt + r.gahbcfg().write(|w| { + w.set_gint(true); // unmask global interrupt + }); + + // Connect + r.dctl().write(|w| w.set_sdis(false)); + self.enabled = true; } @@ -1066,8 +1017,7 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> { T::state().ep_in_wakers[ep_index].register(cx.waker()); - // SAFETY: atomic read without side effects - if unsafe { T::regs().diepctl(ep_index).read().usbaep() } { + if T::regs().diepctl(ep_index).read().usbaep() { Poll::Ready(()) } else { Poll::Pending @@ -1088,8 +1038,7 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> { T::state().ep_out_wakers[ep_index].register(cx.waker()); - // SAFETY: atomic read without side effects - if unsafe { T::regs().doepctl(ep_index).read().usbaep() } { + if T::regs().doepctl(ep_index).read().usbaep() { Poll::Ready(()) } else { Poll::Pending @@ -1124,8 +1073,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { // Release buffer state.ep_out_size[index].store(EP_OUT_BUFFER_EMPTY, Ordering::Release); - // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Bus` so a critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { // Receive 1 packet T::regs().doeptsiz(index).modify(|w| { w.set_xfrsiz(self.info.max_packet_size as _); @@ -1163,8 +1111,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { poll_fn(|cx| { state.ep_in_wakers[index].register(cx.waker()); - // SAFETY: atomic read with no side effects - let diepctl = unsafe { r.diepctl(index).read() }; + let diepctl = r.diepctl(index).read(); if !diepctl.usbaep() { Poll::Ready(Err(EndpointError::Disabled)) } else if !diepctl.epena() { @@ -1181,12 +1128,10 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { let size_words = (buf.len() + 3) / 4; - // SAFETY: atomic read with no side effects - let fifo_space = unsafe { r.dtxfsts(index).read().ineptfsav() as usize }; + let fifo_space = r.dtxfsts(index).read().ineptfsav() as usize; if size_words > fifo_space { // Not enough space in fifo, enable tx fifo empty interrupt - // SAFETY: DIEPEMPMSK is shared with IRQ so critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { r.diepempmsk().modify(|w| { w.set_ineptxfem(w.ineptxfem() | (1 << index)); }); @@ -1202,18 +1147,14 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { .await } - // SAFETY: DIEPTSIZ is exclusive to this endpoint under `&mut self` - unsafe { - // Setup transfer size - r.dieptsiz(index).write(|w| { - w.set_mcnt(1); - w.set_pktcnt(1); - w.set_xfrsiz(buf.len() as _); - }); - } + // Setup transfer size + r.dieptsiz(index).write(|w| { + w.set_mcnt(1); + w.set_pktcnt(1); + w.set_xfrsiz(buf.len() as _); + }); - // SAFETY: DIEPCTL is shared with `Bus` so a critical section is needed for RMW - critical_section::with(|_| unsafe { + critical_section::with(|_| { // Enable endpoint r.diepctl(index).modify(|w| { w.set_cnak(true); @@ -1225,8 +1166,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { for chunk in buf.chunks(4) { let mut tmp = [0u8; 4]; tmp[0..chunk.len()].copy_from_slice(chunk); - // SAFETY: FIFO is exclusive to this endpoint under `&mut self` - unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) }; + r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); } trace!("write done ep={:?}", self.info.addr); @@ -1258,17 +1198,15 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { state.ep0_setup_ready.store(false, Ordering::Release); // EP0 should not be controlled by `Bus` so this RMW does not need a critical section - unsafe { - // Receive 1 SETUP packet - T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| { - w.set_rxdpid_stupcnt(1); - }); + // Receive 1 SETUP packet + T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| { + w.set_rxdpid_stupcnt(1); + }); - // Clear NAK to indicate we are ready to receive more data - T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { - w.set_cnak(true); - }); - } + // Clear NAK to indicate we are ready to receive more data + T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { + w.set_cnak(true); + }); trace!("SETUP received: {:?}", data); Poll::Ready(data) @@ -1313,20 +1251,18 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> { trace!("control: reject"); // EP0 should not be controlled by `Bus` so this RMW does not need a critical section - unsafe { - let regs = T::regs(); - regs.diepctl(self.ep_in.info.addr.index()).modify(|w| { - w.set_stall(true); - }); - regs.doepctl(self.ep_out.info.addr.index()).modify(|w| { - w.set_stall(true); - }); - } + let regs = T::regs(); + regs.diepctl(self.ep_in.info.addr.index()).modify(|w| { + w.set_stall(true); + }); + regs.doepctl(self.ep_out.info.addr.index()).modify(|w| { + w.set_stall(true); + }); } async fn accept_set_address(&mut self, addr: u8) { trace!("setting addr: {}", addr); - critical_section::with(|_| unsafe { + critical_section::with(|_| { T::regs().dcfg().modify(|w| { w.set_dad(addr); }); diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 18ebf97d8..5907a4e54 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -48,11 +48,9 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { let rl = reload_value(psc, timeout_us); let wdg = T::regs(); - unsafe { - wdg.kr().write(|w| w.set_key(Key::ENABLE)); - wdg.pr().write(|w| w.set_pr(Pr(pr))); - wdg.rlr().write(|w| w.set_rl(rl)); - } + wdg.kr().write(|w| w.set_key(Key::ENABLE)); + wdg.pr().write(|w| w.set_pr(Pr(pr))); + wdg.rlr().write(|w| w.set_rl(rl)); trace!( "Watchdog configured with {}us timeout, desired was {}us (PR={}, RL={})", @@ -67,11 +65,11 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { } } - pub unsafe fn unleash(&mut self) { + pub fn unleash(&mut self) { T::regs().kr().write(|w| w.set_key(Key::START)); } - pub unsafe fn pet(&mut self) { + pub fn pet(&mut self) { T::regs().kr().write(|w| w.set_key(Key::RESET)); } } diff --git a/examples/stm32f0/src/bin/wdg.rs b/examples/stm32f0/src/bin/wdg.rs index 80e76f901..a44b17528 100644 --- a/examples/stm32f0/src/bin/wdg.rs +++ b/examples/stm32f0/src/bin/wdg.rs @@ -16,10 +16,10 @@ async fn main(_spawner: Spawner) { let mut wdg = IndependentWatchdog::new(p.IWDG, 20_000_00); info!("Watchdog start"); - unsafe { wdg.unleash() }; + wdg.unleash(); loop { Timer::after(Duration::from_secs(1)).await; - unsafe { wdg.pet() }; + wdg.pet(); } } diff --git a/examples/stm32f4/src/bin/wdt.rs b/examples/stm32f4/src/bin/wdt.rs index b2c587fa1..e5d122af7 100644 --- a/examples/stm32f4/src/bin/wdt.rs +++ b/examples/stm32f4/src/bin/wdt.rs @@ -17,9 +17,7 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB7, Level::High, Speed::Low); let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000); - unsafe { - wdt.unleash(); - } + wdt.unleash(); let mut i = 0; @@ -36,9 +34,7 @@ async fn main(_spawner: Spawner) { // MCU should restart in 1 second after the last pet. if i < 5 { info!("Petting watchdog"); - unsafe { - wdt.pet(); - } + wdt.pet(); } i += 1; diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index ecbe3a6e6..c111a9787 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -38,9 +38,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - unsafe { - pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10)); - } + pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10)); let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 3912327e2..336eed644 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -45,11 +45,9 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - unsafe { - pac::RCC.ccipr4().write(|w| { - w.set_usbsel(pac::rcc::vals::Usbsel::HSI48); - }); - } + pac::RCC.ccipr4().write(|w| { + w.set_usbsel(pac::rcc::vals::Usbsel::HSI48); + }); // Create the driver, from the HAL. let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 1972f8ff2..d360df085 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -62,49 +62,39 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { T::enable(); ::reset(); - unsafe { - ch1.set_speed(Speed::VeryHigh); - ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull); - ch2.set_speed(Speed::VeryHigh); - ch2.set_as_af(ch1.af_num(), AFType::OutputPushPull); - ch3.set_speed(Speed::VeryHigh); - ch3.set_as_af(ch1.af_num(), AFType::OutputPushPull); - ch4.set_speed(Speed::VeryHigh); - ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull); - } + ch1.set_speed(Speed::VeryHigh); + ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull); + ch2.set_speed(Speed::VeryHigh); + ch2.set_as_af(ch1.af_num(), AFType::OutputPushPull); + ch3.set_speed(Speed::VeryHigh); + ch3.set_as_af(ch1.af_num(), AFType::OutputPushPull); + ch4.set_speed(Speed::VeryHigh); + ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull); let mut this = Self { inner: tim }; this.set_freq(freq); this.inner.start(); - unsafe { - T::regs_gp32() - .ccmr_output(0) - .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); - T::regs_gp32() - .ccmr_output(0) - .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); - T::regs_gp32() - .ccmr_output(1) - .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); - T::regs_gp32() - .ccmr_output(1) - .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); - } + let r = T::regs_gp32(); + r.ccmr_output(0) + .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); + r.ccmr_output(0) + .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); + r.ccmr_output(1) + .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); + r.ccmr_output(1) + .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into())); + this } pub fn enable(&mut self, channel: Channel) { - unsafe { - T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true)); - } + T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true)); } pub fn disable(&mut self, channel: Channel) { - unsafe { - T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false)); - } + T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false)); } pub fn set_freq(&mut self, freq: Hertz) { @@ -112,11 +102,11 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { } pub fn get_max_duty(&self) -> u32 { - unsafe { T::regs_gp32().arr().read().arr() } + T::regs_gp32().arr().read().arr() } pub fn set_duty(&mut self, channel: Channel, duty: u32) { defmt::assert!(duty < self.get_max_duty()); - unsafe { T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) } + T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) } } diff --git a/examples/stm32h7/src/bin/wdg.rs b/examples/stm32h7/src/bin/wdg.rs index 2b0301aad..9181dfd67 100644 --- a/examples/stm32h7/src/bin/wdg.rs +++ b/examples/stm32h7/src/bin/wdg.rs @@ -15,10 +15,10 @@ async fn main(_spawner: Spawner) { let mut wdg = IndependentWatchdog::new(p.IWDG1, 20_000_000); - unsafe { wdg.unleash() }; + wdg.unleash(); loop { Timer::after(Duration::from_secs(1)).await; - unsafe { wdg.pet() }; + wdg.pet(); } } diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 281346e5f..1771e5202 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -12,12 +12,10 @@ use {defmt_rtt as _, panic_probe as _}; fn main() -> ! { info!("Hello World!"); - unsafe { - pac::RCC.ccipr().modify(|w| { - w.set_adcsel(0b11); - }); - pac::RCC.ahb2enr().modify(|w| w.set_adcen(true)); - } + pac::RCC.ccipr().modify(|w| { + w.set_adcsel(0b11); + }); + pac::RCC.ahb2enr().modify(|w| w.set_adcen(true)); let p = embassy_stm32::init(Default::default()); diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index d6e744aa6..a36ed5d90 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -11,11 +11,9 @@ use {defmt_rtt as _, panic_probe as _}; fn main() -> ! { info!("Hello World!"); - unsafe { - pac::RCC.apb1enr1().modify(|w| { - w.set_dac1en(true); - }); - } + pac::RCC.apb1enr1().modify(|w| { + w.set_dac1en(true); + }); let p = embassy_stm32::init(Default::default()); diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index e179c5ca1..805d21418 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -35,7 +35,7 @@ async fn main(_spawner: Spawner) { config.rcc.enable_lsi = true; // enable RNG let p = embassy_stm32::init(config); - unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) } + pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)); let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2); diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index 182c607f9..d8562fca5 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs @@ -15,11 +15,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_lsi = true; //Needed for RNG to work let p = embassy_stm32::init(config); - unsafe { - pac::RCC.ccipr().modify(|w| { - w.set_rngsel(0b01); - }); - } + pac::RCC.ccipr().modify(|w| { + w.set_rngsel(0b01); + }); info!("Hello World!"); diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 32d35c42c..582df5753 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -24,10 +24,8 @@ async fn main(_spawner: Spawner) { info!("Starting LSI"); - unsafe { - pac::RCC.csr().modify(|w| w.set_lsion(true)); - while !pac::RCC.csr().read().lsirdy() {} - } + pac::RCC.csr().modify(|w| w.set_lsion(true)); + while !pac::RCC.csr().read().lsirdy() {} info!("Started LSI"); From bbc81146ecb3832e9ec1531ef6d48fec6c7c0d8c Mon Sep 17 00:00:00 2001 From: Catherine Date: Mon, 19 Jun 2023 09:06:41 +0000 Subject: [PATCH 1392/1575] BDMA: request stop after busy loop in blocking_wait(). Otherwise the channel cannot be used again, since CR.EN remains set and the DMA channel registers are read-only while it is set. --- embassy-stm32/src/dma/bdma.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index c0a503e25..e9b75d863 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -327,6 +327,7 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn blocking_wait(mut self) { while self.is_running() {} + self.request_stop(); // "Subsequent reads and writes cannot be moved ahead of preceding reads." fence(Ordering::SeqCst); From e0747e937f06ed280594e17c97d19784410b6e85 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:15:09 +0200 Subject: [PATCH 1393/1575] remove unsafe for circular dma reg access --- embassy-stm32/src/dma/bdma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index be4571440..3d315e20b 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -325,7 +325,7 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().ch(self.channel.num()); let en = ch.cr().read().en(); - let circular = unsafe { ch.cr().read() }.circ() == vals::Circ::ENABLED; + let circular = ch.cr().read().circ() == vals::Circ::ENABLED; let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; en && (circular || !tcif) } From fe7b72948ab5d3682f23e305f3eb7186cc308b1b Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:42:25 +0200 Subject: [PATCH 1394/1575] add ValueArray type and respective write functions --- embassy-stm32/src/dac/mod.rs | 206 +++++++++++++++++++++-------------- 1 file changed, 124 insertions(+), 82 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 525d45d72..4384a7c34 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -98,6 +98,14 @@ pub enum Value { Bit12(u16, Alignment), } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ValueArray<'a> { + Bit8(&'a [u8]), + Bit12Left(&'a [u16]), + Bit12Right(&'a [u16]), +} + pub struct Dac<'d, T: Instance, Tx> { channels: u8, txdma: PeripheralRef<'d, Tx>, @@ -129,21 +137,19 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { T::enable(); T::reset(); - unsafe { - T::regs().mcr().modify(|reg| { - for ch in 0..channels { - reg.set_mode(ch as usize, 0); - reg.set_mode(ch as usize, 0); - } - }); + T::regs().mcr().modify(|reg| { + for ch in 0..channels { + reg.set_mode(ch as usize, 0); + reg.set_mode(ch as usize, 0); + } + }); - T::regs().cr().modify(|reg| { - for ch in 0..channels { - reg.set_en(ch as usize, true); - reg.set_ten(ch as usize, true); - } - }); - } + T::regs().cr().modify(|reg| { + for ch in 0..channels { + reg.set_en(ch as usize, true); + reg.set_ten(ch as usize, true); + } + }); Self { channels, @@ -161,13 +167,12 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { } } + /// Set the enable register of the given channel fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { self.check_channel_exists(ch)?; - unsafe { - T::regs().cr().modify(|reg| { - reg.set_en(ch.index(), on); - }) - } + T::regs().cr().modify(|reg| { + reg.set_en(ch.index(), on); + }); Ok(()) } @@ -179,112 +184,149 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { self.set_channel_enable(ch, false) } + /// Performs all register accesses necessary to select a new trigger for CH1 pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch1)?; unwrap!(self.disable_channel(Channel::Ch1)); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_tsel1(trigger.tsel()); - }) - } + T::regs().cr().modify(|reg| { + reg.set_tsel1(trigger.tsel()); + }); Ok(()) } + /// Performs all register accesses necessary to select a new trigger for CH2 pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch2)?; unwrap!(self.disable_channel(Channel::Ch2)); - unsafe { - T::regs().cr().modify(|reg| { - reg.set_tsel2(trigger.tsel()); - }) - } + T::regs().cr().modify(|reg| { + reg.set_tsel2(trigger.tsel()); + }); Ok(()) } + /// Perform a software trigger on a given channel pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { self.check_channel_exists(ch)?; - unsafe { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(ch.index(), true); - }); - } + T::regs().swtrigr().write(|reg| { + reg.set_swtrig(ch.index(), true); + }); Ok(()) } + /// Perform a software trigger on all channels pub fn trigger_all(&mut self) { - unsafe { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(Channel::Ch1.index(), true); - reg.set_swtrig(Channel::Ch2.index(), true); - }) - } + T::regs().swtrigr().write(|reg| { + reg.set_swtrig(Channel::Ch1.index(), true); + reg.set_swtrig(Channel::Ch2.index(), true); + }); } pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { self.check_channel_exists(ch)?; match value { - Value::Bit8(v) => unsafe { - T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)); - }, - Value::Bit12(v, Alignment::Left) => unsafe { - T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)); - }, - Value::Bit12(v, Alignment::Right) => unsafe { - T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)); - }, + Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), } Ok(()) } + /// Write `data` to the DAC via DMA. + /// + /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. + /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// + /// ## Current limitations + /// - Only CH1 Supported + /// /// TODO: Allow an array of Value instead of only u16, right-aligned - pub async fn write(&mut self, data: &[u16], circular: bool) -> Result<(), Error> + pub async fn write_8bit(&mut self, data_ch1: &[u8], circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.write_inner(ValueArray::Bit8(data_ch1), circular).await + } + + pub async fn write_12bit_right_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.write_inner(ValueArray::Bit12Right(data_ch1), circular).await + } + + pub async fn write_12bit_left_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.write_inner(ValueArray::Bit12Left(data_ch1), circular).await + } + + async fn write_inner(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { // TODO: Make this a parameter or get it from the struct or so... const CHANNEL: usize = 0; - //debug!("Starting DAC"); - unsafe { - T::regs().cr().modify(|w| { - w.set_en(CHANNEL, true); - w.set_dmaen(CHANNEL, true); - }); - } + // Enable DAC and DMA + T::regs().cr().modify(|w| { + w.set_en(CHANNEL, true); + w.set_dmaen(CHANNEL, true); + }); let tx_request = self.txdma.request(); - // Use the 12 bit right-aligned register for now. TODO: distinguish values - let tx_dst = T::regs().dhr12r(CHANNEL).ptr() as *mut u16; - - let tx_f = unsafe { - Transfer::new_write( - &mut self.txdma, - tx_request, - data, - tx_dst, - TransferOptions { - circular, - halt_transfer_ir: false, - }, - ) + // Initiate the correct type of DMA transfer depending on what data is passed + let tx_f = match data_ch1 { + ValueArray::Bit8(buf) => unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + buf, + T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + buf, + T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + Transfer::new_write( + &mut self.txdma, + tx_request, + buf, + T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16, + TransferOptions { + circular, + halt_transfer_ir: false, + }, + ) + }, }; - //debug!("Awaiting tx_f"); - tx_f.await; // finish dma - unsafe { - // TODO: Do we need to check any status registers here? + // TODO: Do we need to check any status registers here? + T::regs().cr().modify(|w| { + // Disable the DAC peripheral + w.set_en(CHANNEL, false); + // Disable the DMA. TODO: Is this necessary? + w.set_dmaen(CHANNEL, false); + }); - T::regs().cr().modify(|w| { - // Disable the dac peripheral - w.set_en(CHANNEL, false); - // Disable the DMA. TODO: Is this necessary? - w.set_dmaen(CHANNEL, false); - }); - } Ok(()) } } From 218b102b2840c9786944aa6f613450c876b6110b Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:46:17 +0200 Subject: [PATCH 1395/1575] remove Alignment and make Value and Value array look the same --- embassy-stm32/src/dac/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 4384a7c34..0fbf67673 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -84,25 +84,25 @@ impl Ch2Trigger { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Alignment { - Left, - Right, -} - #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Value { + // 8 bit value Bit8(u8), - Bit12(u16, Alignment), + // 12 bit value stored in a u16, left-aligned + Bit12Left(u16), + // 12 bit value stored in a u16, right-aligned + Bit12Right(u16), } #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ValueArray<'a> { + // 8 bit values Bit8(&'a [u8]), + // 12 bit value stored in a u16, left-aligned Bit12Left(&'a [u16]), + // 12 bit values stored in a u16, right-aligned Bit12Right(&'a [u16]), } @@ -225,8 +225,8 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { self.check_channel_exists(ch)?; match value { Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12Left(v) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12Right(v) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), } Ok(()) } From 88052480b14b8dd38e7c4ba179b7035390f59618 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:50:17 +0200 Subject: [PATCH 1396/1575] fix typo, minor cleanup --- embassy-stm32/src/dac/mod.rs | 13 +++++++------ embassy-stm32/src/dma/bdma.rs | 7 +++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 0fbf67673..704b77b37 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -275,42 +275,43 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }); let tx_request = self.txdma.request(); + let channel = &mut self.txdma; // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data_ch1 { ValueArray::Bit8(buf) => unsafe { Transfer::new_write( - &mut self.txdma, + channel, tx_request, buf, T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8, TransferOptions { circular, - halt_transfer_ir: false, + half_transfer_ir: false, }, ) }, ValueArray::Bit12Left(buf) => unsafe { Transfer::new_write( - &mut self.txdma, + channel, tx_request, buf, T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16, TransferOptions { circular, - halt_transfer_ir: false, + half_transfer_ir: false, }, ) }, ValueArray::Bit12Right(buf) => unsafe { Transfer::new_write( - &mut self.txdma, + channel, tx_request, buf, T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16, TransferOptions { circular, - halt_transfer_ir: false, + half_transfer_ir: false, }, ) }, diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 3d315e20b..0ad20579a 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -1,7 +1,6 @@ #![macro_use] use core::future::Future; -use core::option; use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; @@ -24,14 +23,14 @@ use crate::pac::bdma::{regs, vals}; #[non_exhaustive] pub struct TransferOptions { pub circular: bool, - pub halt_transfer_ir: bool, + pub half_transfer_ir: bool, } impl Default for TransferOptions { fn default() -> Self { Self { circular: false, - halt_transfer_ir: false, + half_transfer_ir: false, } } } @@ -291,7 +290,7 @@ impl<'a, C: Channel> Transfer<'a, C> { w.set_dir(dir.into()); w.set_teie(true); w.set_tcie(true); - w.set_htie(options.halt_transfer_ir); + w.set_htie(options.half_transfer_ir); if options.circular { w.set_circ(vals::Circ::ENABLED); debug!("Setting circular mode"); From 56ab6d9f143ecc3041ac9726e621eedea729ca4d Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:54:22 +0200 Subject: [PATCH 1397/1575] remove write_X variants --- embassy-stm32/src/dac/mod.rs | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 704b77b37..f02adeed9 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -86,6 +86,7 @@ impl Ch2Trigger { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Single 8 or 12 bit value that can be output by the DAC pub enum Value { // 8 bit value Bit8(u8), @@ -97,6 +98,7 @@ pub enum Value { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Array variant of [`Value`] pub enum ValueArray<'a> { // 8 bit values Bit8(&'a [u8]), @@ -239,29 +241,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// ## Current limitations /// - Only CH1 Supported /// - /// TODO: Allow an array of Value instead of only u16, right-aligned - pub async fn write_8bit(&mut self, data_ch1: &[u8], circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.write_inner(ValueArray::Bit8(data_ch1), circular).await - } - - pub async fn write_12bit_right_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.write_inner(ValueArray::Bit12Right(data_ch1), circular).await - } - - pub async fn write_12bit_left_aligned(&mut self, data_ch1: &[u16], circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.write_inner(ValueArray::Bit12Left(data_ch1), circular).await - } - - async fn write_inner(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> + pub async fn write(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { From 990dd5e5db7134193259fcf350c0928b9a64df97 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 19 Jun 2023 22:38:27 +0200 Subject: [PATCH 1398/1575] tests/stm32: do multiple transfers to catch more bugs. --- tests/stm32/src/bin/usart_dma.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index 50dd2893e..c34d9574b 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -69,24 +69,27 @@ async fn main(_spawner: Spawner) { const LEN: usize = 128; let mut tx_buf = [0; LEN]; let mut rx_buf = [0; LEN]; - for i in 0..LEN { - tx_buf[i] = i as u8; - } let (mut tx, mut rx) = usart.split(); - let tx_fut = async { - tx.write(&tx_buf).await.unwrap(); - }; - let rx_fut = async { - rx.read(&mut rx_buf).await.unwrap(); - }; + for n in 0..42 { + for i in 0..LEN { + tx_buf[i] = (i ^ n) as u8; + } - // note: rx needs to be polled first, to workaround this bug: - // https://github.com/embassy-rs/embassy/issues/1426 - join(rx_fut, tx_fut).await; + let tx_fut = async { + tx.write(&tx_buf).await.unwrap(); + }; + let rx_fut = async { + rx.read(&mut rx_buf).await.unwrap(); + }; - assert_eq!(tx_buf, rx_buf); + // note: rx needs to be polled first, to workaround this bug: + // https://github.com/embassy-rs/embassy/issues/1426 + join(rx_fut, tx_fut).await; + + assert_eq!(tx_buf, rx_buf); + } info!("Test OK"); cortex_m::asm::bkpt(); From 76659d9003104f8edd2472a36149565e4a55c0e6 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 19 Jun 2023 22:37:23 +0200 Subject: [PATCH 1399/1575] Prevent accidental revert when using firmware updater This change prevents accidentally overwriting the previous firmware before the new one has been marked as booted. --- .../boot/src/firmware_updater/asynch.rs | 34 +++++++++++++++++-- .../boot/src/firmware_updater/blocking.rs | 32 +++++++++++++++-- embassy-boot/boot/src/firmware_updater/mod.rs | 3 ++ embassy-boot/boot/src/lib.rs | 12 +++++-- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index 0b3f88313..20731ee0a 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -56,6 +56,16 @@ impl FirmwareUpdater { } } + // Make sure we are running a booted firmware to avoid reverting to a bad state. + async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + if self.get_state(aligned).await? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + /// Obtain the current state. /// /// This is useful to check if the bootloader has just done a swap, in order @@ -98,6 +108,8 @@ impl FirmwareUpdater { assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); + self.verify_booted(_aligned).await?; + #[cfg(feature = "ed25519-dalek")] { use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; @@ -217,8 +229,16 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { + pub async fn write_firmware( + &mut self, + aligned: &mut [u8], + offset: usize, + data: &[u8], + ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + + self.verify_booted(aligned).await?; self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; @@ -232,7 +252,14 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.verify_booted(aligned).await?; + self.dfu.erase(0, self.dfu.capacity() as u32).await?; Ok(&mut self.dfu) @@ -255,13 +282,14 @@ mod tests { let flash = Mutex::::new(MemFlash::<131072, 4096, 8>::default()); let state = Partition::new(&flash, 0, 4096); let dfu = Partition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); + block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index 551150c4f..f03f53e4d 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -58,6 +58,16 @@ impl BlockingFirmwareUpdater { } } + // Make sure we are running a booted firmware to avoid reverting to a bad state. + fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + if self.get_state(aligned)? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + /// Obtain the current state. /// /// This is useful to check if the bootloader has just done a swap, in order @@ -100,6 +110,8 @@ impl BlockingFirmwareUpdater { assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); + self.verify_booted(_aligned)?; + #[cfg(feature = "ed25519-dalek")] { use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; @@ -219,8 +231,15 @@ impl BlockingFirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { + pub fn write_firmware( + &mut self, + aligned: &mut [u8], + offset: usize, + data: &[u8], + ) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.verify_booted(aligned)?; self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; @@ -234,7 +253,13 @@ impl BlockingFirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + self.verify_booted(aligned)?; self.dfu.erase(0, self.dfu.capacity() as u32)?; Ok(&mut self.dfu) @@ -264,7 +289,8 @@ mod tests { to_write[..7].copy_from_slice(update.as_slice()); let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - updater.write_firmware(0, to_write.as_slice()).unwrap(); + let mut aligned = [0; 8]; + updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; updater diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs index a37984a3a..55ce8f363 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs @@ -26,6 +26,8 @@ pub enum FirmwareUpdaterError { Flash(NorFlashErrorKind), /// Signature errors. Signature(signature::Error), + /// Bad state. + BadState, } #[cfg(feature = "defmt")] @@ -34,6 +36,7 @@ impl defmt::Format for FirmwareUpdaterError { match self { FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), + FirmwareUpdaterError::BadState => defmt::write!(fmt, "FirmwareUpdaterError::BadState"), } } } diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 45a87bd0e..016362b86 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -51,6 +51,8 @@ impl AsMut<[u8]> for AlignedBuffer { #[cfg(test)] mod tests { + #![allow(unused_imports)] + use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; #[cfg(feature = "nightly")] use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; @@ -120,9 +122,13 @@ mod tests { dfu: flash.dfu(), state: flash.state(), }); - block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); block_on(updater.mark_updated(&mut aligned)).unwrap(); + // Writing after marking updated is not allowed until marked as booted. + let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)); + assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState))); + let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { active: flash.active(), @@ -188,7 +194,7 @@ mod tests { dfu: flash.dfu(), state: flash.state(), }); - block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); block_on(updater.mark_updated(&mut aligned)).unwrap(); let flash = flash.into_blocking(); @@ -230,7 +236,7 @@ mod tests { dfu: flash.dfu(), state: flash.state(), }); - block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); block_on(updater.mark_updated(&mut aligned)).unwrap(); let flash = flash.into_blocking(); From 428a4ba3f98d81de28f8f72115ed6eff5401b46a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 19 Jun 2023 22:39:08 +0200 Subject: [PATCH 1400/1575] stm32/gpdma: clear all interrupts after reset. Reset doesn't clear them, this causes subsequent transfers to instantly complete because the TC flag was set from before. --- embassy-stm32/src/dma/gpdma.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index c600df92d..b7bcf7795 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -252,6 +252,7 @@ impl<'a, C: Channel> Transfer<'a, C> { super::dmamux::configure_dmamux(&mut *this.channel, request); ch.cr().write(|w| w.set_reset(true)); + ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs ch.llr().write(|_| {}); // no linked list ch.tr1().write(|w| { w.set_sdw(data_size.into()); From 09982214788def209316ef70cae08eead964e206 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 19 Jun 2023 16:05:59 -0500 Subject: [PATCH 1401/1575] stm32/can: update interrupts --- embassy-stm32/src/can/bxcan.rs | 59 ++++++++++++++++----------------- examples/stm32f4/src/bin/can.rs | 13 ++++++-- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 9cd40fd8b..e23ce6863 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -5,12 +5,11 @@ use core::task::Poll; pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; -use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::{into_ref, PeripheralRef}; use futures::FutureExt; use crate::gpio::sealed::AFType; -use crate::interrupt::InterruptExt; +use crate::interrupt::typelevel::Interrupt; use crate::pac::can::vals::{Lec, RirIde}; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -21,7 +20,7 @@ pub struct TxInterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for TxInterruptHandler { +impl interrupt::typelevel::Handler for TxInterruptHandler { unsafe fn on_interrupt() { T::regs().tsr().write(|v| { v.set_rqcp(0, true); @@ -37,7 +36,7 @@ pub struct Rx0InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for Rx0InterruptHandler { +impl interrupt::typelevel::Handler for Rx0InterruptHandler { unsafe fn on_interrupt() { // info!("rx0 irq"); Can::::receive_fifo(RxFifo::Fifo0); @@ -48,7 +47,7 @@ pub struct Rx1InterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for Rx1InterruptHandler { +impl interrupt::typelevel::Handler for Rx1InterruptHandler { unsafe fn on_interrupt() { // info!("rx1 irq"); Can::::receive_fifo(RxFifo::Fifo1); @@ -59,7 +58,7 @@ pub struct SceInterruptHandler { _phantom: PhantomData, } -impl interrupt::Handler for SceInterruptHandler { +impl interrupt::typelevel::Handler for SceInterruptHandler { unsafe fn on_interrupt() { // info!("sce irq"); let msr = T::regs().msr(); @@ -97,10 +96,10 @@ impl<'d, T: Instance> Can<'d, T> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - _irqs: impl interrupt::Binding> - + interrupt::Binding> - + interrupt::Binding> - + interrupt::Binding> + _irqs: impl interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + 'd, ) -> Self { into_ref!(peri, rx, tx); @@ -111,7 +110,7 @@ impl<'d, T: Instance> Can<'d, T> { T::enable(); T::reset(); - unsafe { + { use crate::pac::can::vals::{Errie, Fmpie, Tmeie}; T::regs().ier().write(|w| { @@ -127,21 +126,21 @@ impl<'d, T: Instance> Can<'d, T> { // Enable timestamps on rx messages w.set_ttcm(true); - }) + }); } unsafe { - T::TXInterrupt::steal().unpend(); - T::TXInterrupt::steal().enable(); + T::TXInterrupt::unpend(); + T::TXInterrupt::enable(); - T::RX0Interrupt::steal().unpend(); - T::RX0Interrupt::steal().enable(); + T::RX0Interrupt::unpend(); + T::RX0Interrupt::enable(); - T::RX1Interrupt::steal().unpend(); - T::RX1Interrupt::steal().enable(); + T::RX1Interrupt::unpend(); + T::RX1Interrupt::enable(); - T::SCEInterrupt::steal().unpend(); - T::SCEInterrupt::steal().enable(); + T::SCEInterrupt::unpend(); + T::SCEInterrupt::enable(); } rx.set_as_af(rx.af_num(), AFType::Input); @@ -169,7 +168,7 @@ impl<'d, T: Instance> Can<'d, T> { } pub async fn flush(&self, mb: bxcan::Mailbox) { - poll_fn(|cx| unsafe { + poll_fn(|cx| { if T::regs().tsr().read().tme(mb.index()) { return Poll::Ready(()); } @@ -194,7 +193,7 @@ impl<'d, T: Instance> Can<'d, T> { } fn curr_error(&self) -> Option { - let err = unsafe { T::regs().esr().read() }; + let err = { T::regs().esr().read() }; if err.boff() { return Some(BusError::BusOff); } else if err.epvf() { @@ -396,19 +395,19 @@ pub(crate) mod sealed { } pub trait TXInstance { - type TXInterrupt: crate::interrupt::Interrupt; + type TXInterrupt: crate::interrupt::typelevel::Interrupt; } pub trait RX0Instance { - type RX0Interrupt: crate::interrupt::Interrupt; + type RX0Interrupt: crate::interrupt::typelevel::Interrupt; } pub trait RX1Instance { - type RX1Interrupt: crate::interrupt::Interrupt; + type RX1Interrupt: crate::interrupt::typelevel::Interrupt; } pub trait SCEInstance { - type SCEInterrupt: crate::interrupt::Interrupt; + type SCEInterrupt: crate::interrupt::typelevel::Interrupt; } pub trait InterruptableInstance: TXInstance + RX0Instance + RX1Instance + SCEInstance {} @@ -440,22 +439,22 @@ foreach_peripheral!( foreach_interrupt!( ($inst,can,CAN,TX,$irq:ident) => { impl TXInstance for peripherals::$inst { - type TXInterrupt = crate::interrupt::$irq; + type TXInterrupt = crate::interrupt::typelevel::$irq; } }; ($inst,can,CAN,RX0,$irq:ident) => { impl RX0Instance for peripherals::$inst { - type RX0Interrupt = crate::interrupt::$irq; + type RX0Interrupt = crate::interrupt::typelevel::$irq; } }; ($inst,can,CAN,RX1,$irq:ident) => { impl RX1Instance for peripherals::$inst { - type RX1Interrupt = crate::interrupt::$irq; + type RX1Interrupt = crate::interrupt::typelevel::$irq; } }; ($inst,can,CAN,SCE,$irq:ident) => { impl SCEInstance for peripherals::$inst { - type SCEInterrupt = crate::interrupt::$irq; + type SCEInterrupt = crate::interrupt::typelevel::$irq; } }; ); diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index e8377b9a1..da8955053 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -4,12 +4,21 @@ use cortex_m_rt::entry; use defmt::*; +use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; -use embassy_stm32::can::Can; +use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; +use embassy_stm32::peripherals::CAN1; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + CAN1_RX0 => Rx0InterruptHandler; + CAN1_RX1 => Rx1InterruptHandler; + CAN1_SCE => SceInterruptHandler; + CAN1_TX => TxInterruptHandler; +}); + #[entry] fn main() -> ! { info!("Hello World!"); @@ -23,7 +32,7 @@ fn main() -> ! { let rx_pin = Input::new(&mut p.PA11, Pull::Up); core::mem::forget(rx_pin); - let mut can = Can::new(p.CAN1, p.PA11, p.PA12); + let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); From 5a075acc6a91f9eb05d065ee31551a4695cc4cdf Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 19 Jun 2023 16:11:01 -0500 Subject: [PATCH 1402/1575] stm32/tests: fix can --- tests/stm32/src/bin/can.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index f39f83e82..33d63d546 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -4,8 +4,9 @@ // required-features: can -#[path = "../example_common.rs"] -mod example_common; +#[path = "../common.rs"] +mod common; +use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; @@ -13,7 +14,6 @@ use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; -use example_common::*; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { From 161d3ce05c812f7ee951b6265735187b4994037a Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 19 Jun 2023 23:30:51 +0200 Subject: [PATCH 1403/1575] Add firmware updater examples to CI CI was not building the a.rs application due to the requirement of b.bin having been built first. Add a feature flag to examples so that CI can build them including a dummy application. Update a.rs application examples so that they compile again. --- ci.sh | 20 +++++++++---------- examples/boot/application/nrf/Cargo.toml | 1 + examples/boot/application/nrf/src/bin/a.rs | 7 +++++-- examples/boot/application/rp/Cargo.toml | 1 + examples/boot/application/rp/src/bin/a.rs | 6 +++++- examples/boot/application/stm32f3/Cargo.toml | 1 + .../boot/application/stm32f3/src/bin/a.rs | 7 +++++-- examples/boot/application/stm32f7/Cargo.toml | 2 ++ .../boot/application/stm32f7/src/bin/a.rs | 13 +++++++++--- examples/boot/application/stm32h7/Cargo.toml | 2 ++ .../boot/application/stm32h7/src/bin/a.rs | 13 +++++++++--- examples/boot/application/stm32l0/Cargo.toml | 1 + .../boot/application/stm32l0/src/bin/a.rs | 17 ++++++++++------ examples/boot/application/stm32l1/Cargo.toml | 1 + .../boot/application/stm32l1/src/bin/a.rs | 8 ++++++-- examples/boot/application/stm32l4/Cargo.toml | 1 + .../boot/application/stm32l4/src/bin/a.rs | 10 +++++++--- examples/boot/application/stm32wl/Cargo.toml | 1 + .../boot/application/stm32wl/src/bin/a.rs | 12 +++++++---- 19 files changed, 88 insertions(+), 36 deletions(-) diff --git a/ci.sh b/ci.sh index 760d6fbfb..3fe1b1ce8 100755 --- a/ci.sh +++ b/ci.sh @@ -133,16 +133,16 @@ cargo batch \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- 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 --features embassy-nrf/nrf52840 --out-dir out/examples/boot/nrf --bin b \ - --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns --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 \ - --- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/boot/stm32l0 --bin b \ - --- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/boot/stm32l1 --bin b \ - --- 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/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf \ + --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf \ + --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \ + --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \ + --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \ + --- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32h7 \ + --- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l0 \ + --- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \ + --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \ + --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 5939a43b1..b98f73f39 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -24,3 +24,4 @@ cortex-m-rt = "0.7.0" [features] ed25519-dalek = ["embassy-boot/ed25519-dalek"] ed25519-salty = ["embassy-boot/ed25519-salty"] +skip-include = [] diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 06c237781..021d77f3b 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -12,6 +12,9 @@ use embassy_nrf::wdt::{self, Watchdog}; use embassy_sync::mutex::Mutex; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] @@ -55,13 +58,13 @@ async fn main(_spawner: Spawner) { button.wait_for_any_edge().await; if button.is_low() { let mut offset = 0; + let mut magic = [0; 4]; for chunk in APP_B.chunks(4096) { let mut buf: [u8; 4096] = [0; 4096]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf).await.unwrap(); + updater.write_firmware(&mut magic, offset, &buf).await.unwrap(); offset += chunk.len(); } - let mut magic = [0; 4]; updater.mark_updated(&mut magic).await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 4a2c5dd8f..007b6839c 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -29,6 +29,7 @@ debug = [ "embassy-boot-rp/defmt", "panic-probe" ] +skip-include = [] [profile.release] debug = true diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index 69850069b..c8497494c 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -18,7 +18,11 @@ use panic_probe as _; #[cfg(feature = "panic-reset")] use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); + const FLASH_SIZE: usize = 2 * 1024 * 1024; #[embassy_executor::main] @@ -43,7 +47,7 @@ async fn main(_s: Spawner) { let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); defmt::info!("preparing update"); let writer = updater - .prepare_update() + .prepare_update(&mut buf.0[..1]) .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) .unwrap(); defmt::info!("writer created, starting write"); diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 24abd90d4..5b3faf8f8 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -26,3 +26,4 @@ defmt = [ "embassy-stm32/defmt", "embassy-boot-stm32/defmt", ] +skip-include = [] diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index c94676f09..c0a11d699 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -13,6 +13,9 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_sync::mutex::Mutex; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] @@ -31,13 +34,13 @@ async fn main(_spawner: Spawner) { let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; + let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf).await.unwrap(); + updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); offset += chunk.len(); } - let mut magic = AlignedBuffer([0; WRITE_SIZE]); updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 529a01aad..b6a6f9cd8 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -16,6 +16,7 @@ defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } +embedded-storage = "0.3.0" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -26,3 +27,4 @@ defmt = [ "embassy-stm32/defmt", "embassy-boot-stm32/defmt", ] +skip-include = [] diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index fc2702c91..dea682a96 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -2,6 +2,8 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::cell::RefCell; + #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; @@ -9,8 +11,13 @@ use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::blocking_mutex::Mutex; +use embedded_storage::nor_flash::NorFlash; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] @@ -27,16 +34,16 @@ async fn main(_spawner: Spawner) { let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); let mut updater = BlockingFirmwareUpdater::new(config); - let mut writer = updater.prepare_update().unwrap(); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let writer = updater.prepare_update(magic.as_mut()).unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); writer.write(offset, buf.as_ref()).unwrap(); - offset += chunk.len(); + offset += chunk.len() as u32; } - let mut magic = AlignedBuffer([0; WRITE_SIZE]); updater.mark_updated(magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index d7539a53f..0a7e19b1d 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -16,6 +16,7 @@ defmt = { version = "0.3", optional = true } defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } +embedded-storage = "0.3.0" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -26,3 +27,4 @@ defmt = [ "embassy-stm32/defmt", "embassy-boot-stm32/defmt", ] +skip-include = [] diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 1a54464d0..719176692 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -2,6 +2,8 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::cell::RefCell; + #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; @@ -9,8 +11,13 @@ use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::blocking_mutex::Mutex; +use embedded_storage::nor_flash::NorFlash; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] @@ -26,17 +33,17 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut updater = BlockingFirmwareUpdater::new(config); - let mut writer = updater.prepare_update().unwrap(); + let writer = updater.prepare_update(magic.as_mut()).unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); for chunk in APP_B.chunks(4096) { buf.as_mut()[..chunk.len()].copy_from_slice(chunk); writer.write(offset, buf.as_ref()).unwrap(); - offset += chunk.len(); + offset += chunk.len() as u32; } - let mut magic = AlignedBuffer([0; WRITE_SIZE]); updater.mark_updated(magic.as_mut()).unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index e90da259b..998df4dc0 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -26,3 +26,4 @@ defmt = [ "embassy-stm32/defmt", "embassy-boot-stm32/defmt", ] +skip-include = [] diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index 4033ac590..ce80056e6 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -4,22 +4,26 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::mutex::Mutex; use embassy_time::{Duration, Timer}; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = BlockingAsync::new(flash); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PB2, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI2); @@ -28,18 +32,19 @@ async fn main(_spawner: Spawner) { led.set_high(); - let mut updater = FirmwareUpdater::default(); + let config = FirmwareUpdaterConfig::from_linkerfile(&flash); + let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; let mut offset = 0; + let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf, &mut flash, 128).await.unwrap(); + updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); offset += chunk.len(); } - let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 8ac0fac85..10b58c172 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -26,3 +26,4 @@ defmt = [ "embassy-stm32/defmt", "embassy-boot-stm32/defmt", ] +skip-include = [] diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 00ddda636..1e9bf3cb9 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -10,9 +10,13 @@ use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::mutex::Mutex; use embassy_time::{Duration, Timer}; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] @@ -31,15 +35,15 @@ async fn main(_spawner: Spawner) { let config = FirmwareUpdaterConfig::from_linkerfile(&flash); let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; + let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut offset = 0; for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf).await.unwrap(); + updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); offset += chunk.len(); } - let mut magic = AlignedBuffer([0; WRITE_SIZE]); updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index ec79acdeb..713a6527e 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -26,3 +26,4 @@ defmt = [ "embassy-stm32/defmt", "embassy-boot-stm32/defmt", ] +skip-include = [] diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 54579e4ac..a514ab5be 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -10,8 +10,12 @@ use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::mutex::Mutex; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] @@ -29,15 +33,15 @@ async fn main(_spawner: Spawner) { let config = FirmwareUpdaterConfig::from_linkerfile(&flash); let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; + let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf).await.unwrap(); + updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); offset += chunk.len(); } - let mut magic = AlignedBuffer([0; WRITE_SIZE]); - updater.mark_updated(&mut flash, magic.as_mut()).await.unwrap(); + updater.mark_updated(magic.as_mut()).await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index dfaece6cf..4c8bbd73f 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -26,3 +26,4 @@ defmt = [ "embassy-stm32/defmt", "embassy-boot-stm32/defmt", ] +skip-include = [] diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 0c6fa05f9..52a197a5c 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -4,21 +4,25 @@ #[cfg(feature = "defmt-rtt")] use defmt_rtt::*; -use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater}; +use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_sync::mutex::Mutex; use panic_reset as _; +#[cfg(feature = "skip-include")] +static APP_B: &[u8] = &[0, 1, 2, 3]; +#[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); - let mut flash = Mutex::new(BlockingAsync::new(flash)); + let flash = Mutex::new(BlockingAsync::new(flash)); let button = Input::new(p.PA0, Pull::Up); let mut button = ExtiInput::new(button, p.EXTI0); @@ -30,15 +34,15 @@ async fn main(_spawner: Spawner) { let mut updater = FirmwareUpdater::new(config); button.wait_for_falling_edge().await; //defmt::info!("Starting update"); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); // defmt::info!("Writing chunk at 0x{:x}", offset); - updater.write_firmware(offset, &buf).await.unwrap(); + updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); offset += chunk.len(); } - let mut magic = AlignedBuffer([0; WRITE_SIZE]); updater.mark_updated(magic.as_mut()).await.unwrap(); //defmt::info!("Marked as updated"); led.set_low(); From 978e7b5e77f86dc82c393442702defa38b516f4b Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 19 Jun 2023 21:17:31 -0500 Subject: [PATCH 1404/1575] stm32/wpan: fix bugs --- embassy-stm32-wpan/src/evt.rs | 8 ++++---- embassy-stm32-wpan/src/mac.rs | 12 +++++++----- embassy-stm32-wpan/src/sys.rs | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index ee108ec88..47bdc49bf 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -45,15 +45,15 @@ pub struct AsynchEvt { payload: [u8; 1], } -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone)] #[repr(C, packed)] pub struct Evt { pub evt_code: u8, pub payload_len: u8, - pub payload: [u8; 1], + pub payload: [u8; 255], } -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone)] #[repr(C, packed)] pub struct EvtSerial { pub kind: u8, @@ -75,7 +75,7 @@ pub struct EvtStub { /// Be careful that the asynchronous events reported by the CPU2 on the system channel do /// include the header and shall use `EvtPacket` format. Only the command response format on the /// system channel is different. -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone)] #[repr(C, packed)] pub struct EvtPacket { pub header: PacketHeader, diff --git a/embassy-stm32-wpan/src/mac.rs b/embassy-stm32-wpan/src/mac.rs index 9fbf37473..d2be1b85c 100644 --- a/embassy-stm32-wpan/src/mac.rs +++ b/embassy-stm32-wpan/src/mac.rs @@ -8,14 +8,13 @@ use embassy_futures::poll_once; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; +use crate::channels; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; use crate::tables::{ Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE, }; -use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, EVT_QUEUE}; static MAC_WAKER: AtomicWaker = AtomicWaker::new(); static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); @@ -27,8 +26,6 @@ pub struct Mac { impl Mac { pub(crate) fn new() -> Self { unsafe { - LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); - TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), @@ -42,7 +39,12 @@ impl Mac { /// SAFETY: passing a pointer to something other than a managed event packet is UB pub(crate) unsafe fn drop_event_packet(_: *mut EvtPacket) { // Write the ack - CmdPacket::write_into(MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), TlPacketType::OtAck, 0, &[]); + CmdPacket::write_into( + MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _, + TlPacketType::OtAck, + 0, + &[], + ); // Clear the rx flag let _ = poll_once(Ipcc::receive::( diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs index 65a64e090..2b699b725 100644 --- a/embassy-stm32-wpan/src/sys.rs +++ b/embassy-stm32-wpan/src/sys.rs @@ -4,6 +4,7 @@ use core::ptr; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{CcEvt, EvtBox, EvtPacket}; +#[allow(unused_imports)] use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; use crate::tables::{SysTable, WirelessFwInfoTable}; use crate::unsafe_linked_list::LinkedListNode; From 0a551eb7c6646af1b5c7e79849594fafe877e99a Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 20 Jun 2023 17:39:00 -0500 Subject: [PATCH 1405/1575] stm32/can: fix time --- embassy-stm32/src/can/bxcan.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index e23ce6863..224c39ea3 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -158,10 +158,11 @@ impl<'d, T: Instance> Can<'d, T> { /// Queues the message to be sent but exerts backpressure pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); if let Ok(status) = self.can.transmit(frame) { return Poll::Ready(status); } - T::state().tx_waker.register(cx.waker()); + Poll::Pending }) .await @@ -169,10 +170,11 @@ impl<'d, T: Instance> Can<'d, T> { pub async fn flush(&self, mb: bxcan::Mailbox) { poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); if T::regs().tsr().read().tme(mb.index()) { return Poll::Ready(()); } - T::state().tx_waker.register(cx.waker()); + Poll::Pending }) .await; @@ -181,12 +183,13 @@ impl<'d, T: Instance> Can<'d, T> { /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { return Poll::Ready(Ok((time, frame))); } else if let Some(err) = self.curr_error() { return Poll::Ready(Err(err)); } - T::state().err_waker.register(cx.waker()); + Poll::Pending }) .await From 5247c1c795a8b37be485aeeaa99a79eece678fba Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 21 Jun 2023 16:34:56 -0500 Subject: [PATCH 1406/1575] stm32/wpan: fix data alignment --- embassy-stm32-wpan/src/cmd.rs | 2 +- embassy-stm32-wpan/src/tables.rs | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index edca82390..c8056aaa7 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -37,7 +37,7 @@ pub struct CmdSerialStub { } #[derive(Copy, Clone, Default)] -#[repr(C, packed)] +#[repr(C, packed(4))] pub struct CmdPacket { pub header: PacketHeader, pub cmdserial: CmdSerial, diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 2064910f0..3f26282c6 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -164,6 +164,9 @@ pub struct Mac802_15_4Table { pub evt_queue: *const u8, } +#[repr(C, align(4))] +pub struct AlignedData([u8; L]); + /// Reference table. Contains pointers to all other tables. #[derive(Debug, Copy, Clone)] #[repr(C)] @@ -219,9 +222,10 @@ pub static mut FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit #[link_section = "MB_MEM1"] pub static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); +const CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; + #[link_section = "MB_MEM2"] -pub static mut CS_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]> = - MaybeUninit::uninit(); +pub static mut CS_BUFFER: MaybeUninit> = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] pub static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); @@ -234,9 +238,12 @@ pub static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::unin #[link_section = "MB_MEM2"] pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); +#[cfg(feature = "mac")] +const MAC_802_15_4_NOTIF_RSP_EVT_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255; + #[cfg(feature = "mac")] #[link_section = "MB_MEM2"] -pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = +pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit> = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] @@ -245,17 +252,21 @@ pub static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] pub static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); +const SYS_SPARE_EVT_BUF_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255; + #[link_section = "MB_MEM2"] -pub static mut SYS_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = - MaybeUninit::uninit(); +pub static mut SYS_SPARE_EVT_BUF: MaybeUninit> = MaybeUninit::uninit(); #[link_section = "MB_MEM1"] pub static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); +const BLE_SPARE_EVT_BUF_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255; + #[link_section = "MB_MEM2"] -pub static mut BLE_SPARE_EVT_BUF: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]> = - MaybeUninit::uninit(); +pub static mut BLE_SPARE_EVT_BUF: MaybeUninit> = MaybeUninit::uninit(); + +const HCI_ACL_DATA_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + 5 + 251; #[link_section = "MB_MEM2"] // fuck these "magic" numbers from ST ---v---v -pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); +pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; HCI_ACL_DATA_BUFFER_SIZE]> = MaybeUninit::uninit(); From 8d0095c61808b88ad95349c7f24e91797d93dc83 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:43:45 +0200 Subject: [PATCH 1407/1575] add option to enable/disable complete transfer interrupt --- embassy-stm32/src/dma/bdma.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 162ca9adb..32b75bb68 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -22,8 +22,12 @@ use crate::pac::bdma::{regs, vals}; #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct TransferOptions { + /// Enable circular DMA pub circular: bool, + /// Enable half transfer interrupt pub half_transfer_ir: bool, + /// Enable transfer complete interrupt + pub complete_transfer_ir: bool, } impl Default for TransferOptions { @@ -31,6 +35,7 @@ impl Default for TransferOptions { Self { circular: false, half_transfer_ir: false, + complete_transfer_ir: false, } } } @@ -289,7 +294,7 @@ impl<'a, C: Channel> Transfer<'a, C> { } w.set_dir(dir.into()); w.set_teie(true); - w.set_tcie(true); + w.set_tcie(options.complete_transfer_ir); w.set_htie(options.half_transfer_ir); if options.circular { w.set_circ(vals::Circ::ENABLED); From 78736328a042e7c7f6565ed44ac301be22953f7a Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:44:08 +0200 Subject: [PATCH 1408/1575] update docs and update to new dma interface --- embassy-stm32/src/dac/mod.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index f02adeed9..42646d20d 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -115,6 +115,7 @@ pub struct Dac<'d, T: Instance, Tx> { } impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { + /// Create a new instance with one channel pub fn new_1ch( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, @@ -124,6 +125,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Self::new_inner(peri, 1, txdma) } + /// Create a new instance with two channels pub fn new_2ch( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, @@ -134,6 +136,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Self::new_inner(peri, 2, txdma) } + /// Perform initialisation steps for the DAC fn new_inner(peri: PeripheralRef<'d, T>, channels: u8, txdma: impl Peripheral

+ 'd) -> Self { into_ref!(txdma); T::enable(); @@ -178,15 +181,17 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } + /// Enable the DAC channel `ch` pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> { self.set_channel_enable(ch, true) } + /// Disable the DAC channel `ch` pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> { self.set_channel_enable(ch, false) } - /// Performs all register accesses necessary to select a new trigger for CH1 + /// Select a new trigger for CH1 (disables the channel) pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch1)?; unwrap!(self.disable_channel(Channel::Ch1)); @@ -196,7 +201,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } - /// Performs all register accesses necessary to select a new trigger for CH2 + /// Select a new trigger for CH2 (disables the channel) pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { self.check_channel_exists(Channel::Ch2)?; unwrap!(self.disable_channel(Channel::Ch2)); @@ -206,7 +211,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } - /// Perform a software trigger on a given channel + /// Perform a software trigger on `ch` pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { self.check_channel_exists(ch)?; T::regs().swtrigr().write(|reg| { @@ -223,6 +228,9 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }); } + /// Set a value to be output by the DAC on trigger. + /// + /// The `value` is written to the corresponding "data holding register" pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { self.check_channel_exists(ch)?; match value { @@ -237,6 +245,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// ## Current limitations /// - Only CH1 Supported @@ -255,7 +264,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }); let tx_request = self.txdma.request(); - let channel = &mut self.txdma; + let channel = &self.txdma; // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data_ch1 { @@ -268,6 +277,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { TransferOptions { circular, half_transfer_ir: false, + complete_transfer_ir: !circular, }, ) }, @@ -280,6 +290,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { TransferOptions { circular, half_transfer_ir: false, + complete_transfer_ir: !circular, }, ) }, @@ -292,6 +303,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { TransferOptions { circular, half_transfer_ir: false, + complete_transfer_ir: !circular, }, ) }, From cd4f8f13a2c533f4e752c2e446661a6d86b19fe6 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 22 Jun 2023 15:21:14 +0100 Subject: [PATCH 1409/1575] wpan: add BLE HCI --- embassy-stm32-wpan/Cargo.toml | 4 +- embassy-stm32-wpan/src/ble.rs | 19 +- embassy-stm32-wpan/src/cmd.rs | 2 +- embassy-stm32-wpan/src/evt.rs | 23 ++ embassy-stm32-wpan/src/lhci.rs | 111 +++++++++ embassy-stm32-wpan/src/lib.rs | 2 + examples/stm32wb/.cargo/config.toml | 2 +- examples/stm32wb/Cargo.toml | 6 +- examples/stm32wb/src/bin/eddystone_beacon.rs | 249 +++++++++++++++++++ 9 files changed, 412 insertions(+), 6 deletions(-) create mode 100644 embassy-stm32-wpan/src/lhci.rs create mode 100644 examples/stm32wb/src/bin/eddystone_beacon.rs diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index f9023ed79..fda4189ca 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -23,11 +23,13 @@ cortex-m = "0.7.6" heapless = "0.7.16" bit_field = "0.10.2" +stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } +bluetooth-hci-async = { version = "*", git = "https://github.com/OueslatiGhaith/bluetooth-hci", optional = true } [features] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] -ble = [] +ble = ["dep:bluetooth-hci-async"] mac = [] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 60e0cbdf8..297ee4cdf 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -1,6 +1,7 @@ use core::marker::PhantomData; use embassy_stm32::ipcc::Ipcc; +use hci::Opcode; use crate::channels; use crate::cmd::CmdPacket; @@ -29,7 +30,7 @@ impl Ble { Self { phantom: PhantomData } } /// `HW_IPCC_BLE_EvtNot` - pub async fn read(&self) -> EvtBox { + pub async fn tl_read(&self) -> EvtBox { Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { Some(EvtBox::new(node_ptr.cast())) @@ -41,7 +42,7 @@ impl Ble { } /// `TL_BLE_SendCmd` - pub async fn write(&self, opcode: u16, payload: &[u8]) { + pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe { CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); }) @@ -61,3 +62,17 @@ impl Ble { .await; } } + +pub extern crate bluetooth_hci_async as hci; + +impl hci::Controller for Ble { + async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) { + self.tl_write(opcode.0, payload).await; + } + + async fn controller_read(&self) -> &[u8] { + let evt_box = self.tl_read().await; + + evt_box.serial() + } +} diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index c8056aaa7..8428b6ffc 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -52,7 +52,7 @@ impl CmdPacket { p_cmd_serial, CmdSerialStub { ty: packet_type as u8, - cmd_code: cmd_code, + cmd_code, payload_len: payload.len() as u8, }, ); diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index 47bdc49bf..7a4738b7a 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -1,6 +1,7 @@ use core::{ptr, slice}; use super::PacketHeader; +use crate::consts::TL_EVT_HEADER_SIZE; /** * The payload of `Evt` for a command status event @@ -105,6 +106,14 @@ impl EvtBox { Self { ptr } } + pub fn evt<'a>(&self) -> &'a [u8] { + unsafe { + let evt_packet = &(*self.ptr); + + core::slice::from_raw_parts(evt_packet as *const _ as *const u8, core::mem::size_of::()) + } + } + /// Returns information about the event pub fn stub(&self) -> EvtStub { unsafe { @@ -124,6 +133,20 @@ impl EvtBox { slice::from_raw_parts(p_payload, payload_len as usize) } } + + /// writes an underlying [`EvtPacket`] into the provided buffer. + /// Returns the number of bytes that were written. + /// Returns an error if event kind is unknown or if provided buffer size is not enough. + pub fn serial<'a>(&self) -> &'a [u8] { + unsafe { + let evt_serial: *const EvtSerial = &(*self.ptr).evt_serial; + let evt_serial_buf: *const u8 = evt_serial.cast(); + + let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; + + slice::from_raw_parts(evt_serial_buf, len) + } + } } impl Drop for EvtBox { diff --git a/embassy-stm32-wpan/src/lhci.rs b/embassy-stm32-wpan/src/lhci.rs new file mode 100644 index 000000000..62116a695 --- /dev/null +++ b/embassy-stm32-wpan/src/lhci.rs @@ -0,0 +1,111 @@ +use crate::cmd::CmdPacket; +use crate::consts::{TlPacketType, TL_EVT_HEADER_SIZE}; +use crate::evt::{CcEvt, EvtPacket, EvtSerial}; +use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, WirelessFwInfoTable}; +use crate::TL_REF_TABLE; + +const TL_BLEEVT_CC_OPCODE: u8 = 0x0e; +const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62; + +const PACKAGE_DATA_PTR: *const u8 = 0x1FFF_7500 as _; +const UID64_PTR: *const u32 = 0x1FFF_7580 as _; + +#[derive(Debug, Copy, Clone)] +#[repr(C, packed)] +pub struct LhciC1DeviceInformationCcrp { + pub status: u8, + pub rev_id: u16, + pub dev_code_id: u16, + pub package_type: u8, + pub device_type_id: u8, + pub st_company_id: u32, + pub uid64: u32, + + pub uid96_0: u32, + pub uid96_1: u32, + pub uid96_2: u32, + + pub safe_boot_info_table: SafeBootInfoTable, + pub rss_info_table: RssInfoTable, + pub wireless_fw_info_table: WirelessFwInfoTable, + + pub app_fw_inf: u32, +} + +impl Default for LhciC1DeviceInformationCcrp { + fn default() -> Self { + let DeviceInfoTable { + safe_boot_info_table, + rss_info_table, + wireless_fw_info_table, + } = unsafe { &*(*TL_REF_TABLE.as_ptr()).device_info_table }.clone(); + + let device_id = stm32_device_signature::device_id(); + let uid96_0 = (device_id[3] as u32) << 24 + | (device_id[2] as u32) << 16 + | (device_id[1] as u32) << 8 + | device_id[0] as u32; + let uid96_1 = (device_id[7] as u32) << 24 + | (device_id[6] as u32) << 16 + | (device_id[5] as u32) << 8 + | device_id[4] as u32; + let uid96_2 = (device_id[11] as u32) << 24 + | (device_id[10] as u32) << 16 + | (device_id[9] as u32) << 8 + | device_id[8] as u32; + + let package_type = unsafe { *PACKAGE_DATA_PTR }; + let uid64 = unsafe { *UID64_PTR }; + let st_company_id = unsafe { *UID64_PTR.offset(1) } >> 8 & 0x00FF_FFFF; + let device_type_id = (unsafe { *UID64_PTR.offset(1) } & 0x000000FF) as u8; + + LhciC1DeviceInformationCcrp { + status: 0, + rev_id: 0, + dev_code_id: 0, + package_type, + device_type_id, + st_company_id, + uid64, + uid96_0, + uid96_1, + uid96_2, + safe_boot_info_table, + rss_info_table, + wireless_fw_info_table, + app_fw_inf: (1 << 8), // 0.0.1 + } + } +} + +impl LhciC1DeviceInformationCcrp { + pub fn new() -> Self { + Self::default() + } + + pub fn write(&self, cmd_packet: &mut CmdPacket) { + let self_size = core::mem::size_of::(); + + unsafe { + let cmd_packet_ptr: *mut CmdPacket = cmd_packet; + let evet_packet_ptr: *mut EvtPacket = cmd_packet_ptr.cast(); + + let evt_serial: *mut EvtSerial = &mut (*evet_packet_ptr).evt_serial; + let evt_payload = (*evt_serial).evt.payload.as_mut_ptr(); + let evt_cc: *mut CcEvt = evt_payload.cast(); + let evt_cc_payload_buf = (*evt_cc).payload.as_mut_ptr(); + + (*evt_serial).kind = TlPacketType::LocRsp as u8; + (*evt_serial).evt.evt_code = TL_BLEEVT_CC_OPCODE; + (*evt_serial).evt.payload_len = TL_EVT_HEADER_SIZE as u8 + self_size as u8; + + (*evt_cc).cmd_code = LHCI_OPCODE_C1_DEVICE_INF; + (*evt_cc).num_cmd = 1; + + let self_ptr: *const LhciC1DeviceInformationCcrp = self; + let self_buf = self_ptr.cast(); + + core::ptr::copy(self_buf, evt_cc_payload_buf, self_size); + } + } +} diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 5aec9933c..bf0f0466e 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![cfg_attr(feature = "ble", feature(async_fn_in_trait))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; @@ -21,6 +22,7 @@ pub mod channels; pub mod cmd; pub mod consts; pub mod evt; +pub mod lhci; #[cfg(feature = "mac")] pub mod mac; pub mod mm; diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index d23fdc513..35317a297 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list` -# runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" +# runner = "probe-rs-cli run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" runner = "teleprobe local run --chip STM32WB55RG --elf" [build] diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index e41424aad..726cd10d4 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -33,4 +33,8 @@ required-features = ["ble"] [[bin]] name = "tl_mbox_mac" -required-features = ["mac"] \ No newline at end of file +required-features = ["mac"] + +[[bin]] +name = "eddystone_beacon" +required-features = ["ble"] \ No newline at end of file diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs new file mode 100644 index 000000000..fdd5be4a2 --- /dev/null +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -0,0 +1,249 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::time::Duration; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::ble::hci::host::uart::UartHci; +use embassy_stm32_wpan::ble::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; +use embassy_stm32_wpan::ble::hci::types::AdvertisingType; +use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gap::{ + AdvertisingDataType, DiscoverableParameters, GapCommands, Role, +}; +use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gatt::GattCommands; +use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; +use embassy_stm32_wpan::ble::hci::BdAddr; +use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mut mbox = TlMbox::init(p.IPCC, Irqs, config); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + + info!("resetting BLE..."); + mbox.ble_subsystem.reset().await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("config public address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("config random address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::random_address(get_random_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("config identity root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::identity_root(&get_irk()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("config encryption root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("config tx power level..."); + mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("GATT init..."); + mbox.ble_subsystem.init_gatt().await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("GAP init..."); + mbox.ble_subsystem + .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + // info!("set scan response..."); + // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap(); + // let response = mbox.ble_subsystem.read().await.unwrap(); + // defmt::info!("{}", response); + + info!("set discoverable..."); + mbox.ble_subsystem + .set_discoverable(&DiscoverableParameters { + advertising_type: AdvertisingType::NonConnectableUndirected, + advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), + address_type: OwnAddressType::Public, + filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, + local_name: None, + advertising_data: &[], + conn_interval: (None, None), + }) + .await + .unwrap(); + + let response = mbox.ble_subsystem.read().await; + defmt::info!("{}", response); + + // remove some advertisement to decrease the packet size + info!("delete tx power ad type..."); + mbox.ble_subsystem + .delete_ad_type(AdvertisingDataType::TxPowerLevel) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("delete conn interval ad type..."); + mbox.ble_subsystem + .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("update advertising data..."); + mbox.ble_subsystem + .update_advertising_data(&eddystone_advertising_data()) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("update advertising data type..."); + mbox.ble_subsystem + .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + info!("update advertising data flags..."); + mbox.ble_subsystem + .update_advertising_data(&[ + 2, + AdvertisingDataType::Flags as u8, + (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support + ]) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await.unwrap(); + defmt::info!("{}", response); + + cortex_m::asm::wfi(); +} + +fn get_bd_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = lhci_info.device_type_id; + bytes[4] = (lhci_info.st_company_id & 0xff) as u8; + bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8; + + BdAddr(bytes) +} + +fn get_random_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = 0; + bytes[4] = 0x6E; + bytes[5] = 0xED; + + BdAddr(bytes) +} + +const BLE_CFG_IRK: [u8; 16] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, +]; +const BLE_CFG_ERK: [u8; 16] = [ + 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, +]; + +fn get_irk() -> EncryptionKey { + EncryptionKey(BLE_CFG_IRK) +} + +fn get_erk() -> EncryptionKey { + EncryptionKey(BLE_CFG_ERK) +} + +fn eddystone_advertising_data() -> [u8; 24] { + const EDDYSTONE_URL: &[u8] = b"www.rust-lang.com"; + + let mut service_data = [0u8; 24]; + let url_len = EDDYSTONE_URL.len(); + + service_data[0] = 6 + url_len as u8; + service_data[1] = AdvertisingDataType::ServiceData as u8; + + // 16-bit eddystone uuid + service_data[2] = 0xaa; + service_data[3] = 0xFE; + + service_data[4] = 0x10; // URL frame type + service_data[5] = 22_i8 as u8; // calibrated TX power at 0m + service_data[6] = 0x03; // eddystone url prefix = https + + service_data[7..(7 + url_len)].copy_from_slice(EDDYSTONE_URL); + + service_data +} From 810c6af77af037a658186f8b102980ee84164a05 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 22 Jun 2023 15:31:45 +0100 Subject: [PATCH 1410/1575] fix build --- examples/stm32wb/src/bin/tl_mbox_ble.rs | 4 ++-- examples/stm32wb/src/bin/tl_mbox_mac.rs | 6 +++--- tests/stm32/src/bin/tl_mbox.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 439bd01ac..a511e89aa 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -52,10 +52,10 @@ async fn main(_spawner: Spawner) { mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("starting ble..."); - mbox.ble_subsystem.write(0x0c, &[]).await; + mbox.ble_subsystem.tl_write(0x0c, &[]).await; info!("waiting for ble..."); - let ble_event = mbox.ble_subsystem.read().await; + let ble_event = mbox.ble_subsystem.tl_read().await; info!("ble event: {}", ble_event.payload()); diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index a42939bbd..6c8653cf4 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -46,16 +46,16 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mbox = TlMbox::init(p.IPCC, Irqs, config); - let sys_event = mbox.sys_subsystem.read().await; + let sys_event = mbox.sys_subsystem.tl_read().await; info!("sys event: {}", sys_event.payload()); mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; // // info!("starting ble..."); - // mbox.ble_subsystem.write(0x0c, &[]).await; + // mbox.ble_subsystem.t_write(0x0c, &[]).await; // // info!("waiting for ble..."); - // let ble_event = mbox.ble_subsystem.read().await; + // let ble_event = mbox.ble_subsystem.tl_read().await; // // info!("ble event: {}", ble_event.payload()); diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index f47a89b6f..f55c0292a 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -63,10 +63,10 @@ async fn main(spawner: Spawner) { info!("subsystem initialization: {}", result); info!("starting ble..."); - mbox.ble_subsystem.write(0x0c, &[]).await; + mbox.ble_subsystem.tl_write(0x0c, &[]).await; info!("waiting for ble..."); - let ble_event = mbox.ble_subsystem.read().await; + let ble_event = mbox.ble_subsystem.tl_read().await; info!("ble event {:x} : {:x}", ble_event.stub().kind, ble_event.payload()); From 3dbd58f40e07eb4b5d41a671aec0fe71ac8c4b34 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 22 Jun 2023 15:59:03 +0100 Subject: [PATCH 1411/1575] fix unsound access in `EvtBox` --- embassy-stm32-wpan/Cargo.toml | 2 +- embassy-stm32-wpan/src/ble.rs | 5 +++-- embassy-stm32-wpan/src/evt.rs | 10 +--------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index fda4189ca..3659d7135 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -24,7 +24,7 @@ heapless = "0.7.16" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -bluetooth-hci-async = { version = "*", git = "https://github.com/OueslatiGhaith/bluetooth-hci", optional = true } +bluetooth-hci-async = { version = "*", git = "https://github.com/OueslatiGhaith/bluetooth-hci", features = ["version-5-0"], optional = true } [features] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 297ee4cdf..04acf0aff 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -70,9 +70,10 @@ impl hci::Controller for Ble { self.tl_write(opcode.0, payload).await; } - async fn controller_read(&self) -> &[u8] { + async fn controller_read_into(&self, buf: &mut [u8]) { let evt_box = self.tl_read().await; + let evt_serial = evt_box.serial(); - evt_box.serial() + buf[..evt_serial.len()].copy_from_slice(evt_serial); } } diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index 7a4738b7a..25249a13a 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -106,14 +106,6 @@ impl EvtBox { Self { ptr } } - pub fn evt<'a>(&self) -> &'a [u8] { - unsafe { - let evt_packet = &(*self.ptr); - - core::slice::from_raw_parts(evt_packet as *const _ as *const u8, core::mem::size_of::()) - } - } - /// Returns information about the event pub fn stub(&self) -> EvtStub { unsafe { @@ -137,7 +129,7 @@ impl EvtBox { /// writes an underlying [`EvtPacket`] into the provided buffer. /// Returns the number of bytes that were written. /// Returns an error if event kind is unknown or if provided buffer size is not enough. - pub fn serial<'a>(&self) -> &'a [u8] { + pub fn serial<'a>(&'a self) -> &'a [u8] { unsafe { let evt_serial: *const EvtSerial = &(*self.ptr).evt_serial; let evt_serial_buf: *const u8 = evt_serial.cast(); From 5ecf9ec7bcd785555563d8f5006fd24414bbc17f Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Thu, 22 Jun 2023 17:17:51 +0200 Subject: [PATCH 1412/1575] split can --- embassy-stm32/src/can/bxcan.rs | 101 +++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 88eef528f..7b664a113 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; - +use core::cell::RefCell; pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -72,7 +72,7 @@ impl interrupt::typelevel::Handler for SceInterrup } pub struct Can<'d, T: Instance> { - can: bxcan::Can>, + pub can: RefCell>>, } #[derive(Debug)] @@ -147,19 +147,20 @@ impl<'d, T: Instance> Can<'d, T> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); - Self { can } + let can_ref_cell = RefCell::new(can); + Self { can: can_ref_cell } } pub fn set_bitrate(&mut self, bitrate: u32) { let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); - self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); + self.can.borrow_mut().modify_config().set_bit_timing(bit_timing).leave_disabled(); } /// Queues the message to be sent but exerts backpressure pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); - if let Ok(status) = self.can.transmit(frame) { + if let Ok(status) = self.can.borrow_mut().transmit(frame) { return Poll::Ready(status); } @@ -341,6 +342,74 @@ impl<'d, T: Instance> Can<'d, T> { // Pack into BTR register values Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1)) } + + pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) { + (CanTx { can: &self.can }, CanRx { can: &self.can }) + } +} + +pub struct CanTx<'c, 'd, T: Instance> { + can: &'c RefCell>>, +} + +impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { + pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if let Ok(status) = self.can.borrow_mut().transmit(frame) { + return Poll::Ready(status); + } + + Poll::Pending + }) + .await + } + + pub async fn flush(&self, mb: bxcan::Mailbox) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if T::regs().tsr().read().tme(mb.index()) { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } +} + +pub struct CanRx<'c, 'd, T: Instance> { + can: &'c RefCell>>, +} + +impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { + pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { + return Poll::Ready(Ok((time, frame))); + } else if let Some(err) = self.curr_error() { + return Poll::Ready(Err(err)); + } + + Poll::Pending + }) + .await + } + + fn curr_error(&self) -> Option { + let err = { T::regs().esr().read() }; + if err.boff() { + return Some(BusError::BusOff); + } else if err.epvf() { + return Some(BusError::BusPassive); + } else if err.ewgf() { + return Some(BusError::BusWarning); + } else if let Some(err) = err.lec().into_bus_err() { + return Some(err); + } + None + } } enum RxFifo { @@ -357,19 +426,19 @@ impl<'d, T: Instance> Drop for Can<'d, T> { } } -impl<'d, T: Instance> Deref for Can<'d, T> { - type Target = bxcan::Can>; +// impl<'d, T: Instance> Deref for Can<'d, T> { +// type Target = bxcan::Can>; - fn deref(&self) -> &Self::Target { - &self.can - } -} +// fn deref(&self) -> &Self::Target { +// self.can.borrow() +// } +// } -impl<'d, T: Instance> DerefMut for Can<'d, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.can - } -} +// impl<'d, T: Instance> DerefMut for Can<'d, T> { +// fn deref_mut(&mut self) -> &mut Self::Target { +// self.can.borrow_mut() +// } +// } pub(crate) mod sealed { use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; From f47a148f51c7c0de52a1c202fefe6f863c669854 Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Thu, 22 Jun 2023 17:18:55 +0200 Subject: [PATCH 1413/1575] add stm32f7 can example --- examples/stm32f7/src/bin/can.rs | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 examples/stm32f7/src/bin/can.rs diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs new file mode 100644 index 000000000..cf027cc3e --- /dev/null +++ b/examples/stm32f7/src/bin/can.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::borrow::BorrowMut; +use core::borrow::Borrow; +use cortex_m_rt::entry; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::can::bxcan::filter::Mask32; +use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId, Data}; +use embassy_stm32::can::{Can,CanTx,CanRx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; +use embassy_stm32::gpio::{Input, Pull}; +use embassy_stm32::peripherals::CAN3; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + CAN3_RX0 => Rx0InterruptHandler; + CAN3_RX1 => Rx1InterruptHandler; + CAN3_SCE => SceInterruptHandler; + CAN3_TX => TxInterruptHandler; +}); + +#[embassy_executor::task] +pub async fn send_can_message(tx: &'static mut CanTx<'static, 'static, CAN3>) { + loop { + let frame = Frame::new_data(unwrap!(StandardId::new(0 as _)), [0]); + tx.write(&frame).await; + embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let mut p = embassy_stm32::init(Default::default()); + + // The next two lines are a workaround for testing without transceiver. + // To synchronise to the bus the RX input needs to see a high level. + // Use `mem::forget()` to release the borrow on the pin but keep the + // pull-up resistor enabled. + let rx_pin = Input::new(&mut p.PA15, Pull::Up); + core::mem::forget(rx_pin); + + let CAN: &'static mut Can<'static,CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); + CAN.can.borrow_mut().modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + + CAN.can.borrow_mut() + .modify_config() + .set_bit_timing(0x001c0001) // http://www.bittiming.can-wiki.info/ + .enable(); + + let (tx, mut rx) = CAN.split(); + let TX: &'static mut CanTx<'static, 'static, CAN3> = static_cell::make_static!(tx); + spawner.spawn(send_can_message(TX)).unwrap(); + + loop { + let frame = rx.read().await.unwrap(); + println!("Received: {:?}", frame); + } +} From 76a334bd7c402f530825352b9c12f2a6d749a42c Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Thu, 22 Jun 2023 17:47:58 +0200 Subject: [PATCH 1414/1575] add as_mut & set loopback true in example --- examples/stm32f7/src/bin/can.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index cf027cc3e..d821039c2 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -45,14 +45,16 @@ async fn main(spawner: Spawner) { core::mem::forget(rx_pin); let CAN: &'static mut Can<'static,CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); - CAN.can.borrow_mut().modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + CAN.as_mut().modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - CAN.can.borrow_mut() + CAN.as_mut() .modify_config() .set_bit_timing(0x001c0001) // http://www.bittiming.can-wiki.info/ + .set_loopback(true) .enable(); let (tx, mut rx) = CAN.split(); + let TX: &'static mut CanTx<'static, 'static, CAN3> = static_cell::make_static!(tx); spawner.spawn(send_can_message(TX)).unwrap(); From 89fbb02979c0abfac99b32e3676c140055f31c1e Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Thu, 22 Jun 2023 17:49:33 +0200 Subject: [PATCH 1415/1575] add as_mut --- embassy-stm32/src/can/bxcan.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 7b664a113..4afbb5687 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -2,7 +2,9 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; +use core::cell::RefMut; use core::cell::RefCell; + pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -346,6 +348,10 @@ impl<'d, T: Instance> Can<'d, T> { pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) { (CanTx { can: &self.can }, CanRx { can: &self.can }) } + + pub fn as_mut(&self) -> RefMut<'_, bxcan::Can>> { + self.can.borrow_mut() + } } pub struct CanTx<'c, 'd, T: Instance> { From 6c123596b7d48ee66ea93e8b1515e91231e9bced Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Jun 2023 03:36:48 +0200 Subject: [PATCH 1416/1575] wip: esp-hosted net driver. --- embassy-net-esp-hosted/Cargo.toml | 20 + embassy-net-esp-hosted/src/control.rs | 141 +++++ .../src/esp_hosted_config.proto | 432 +++++++++++++ embassy-net-esp-hosted/src/fmt.rs | 257 ++++++++ embassy-net-esp-hosted/src/ioctl.rs | 119 ++++ embassy-net-esp-hosted/src/lib.rs | 300 +++++++++ embassy-net-esp-hosted/src/proto.rs | 598 ++++++++++++++++++ examples/nrf52840/Cargo.toml | 2 + examples/nrf52840/src/bin/wifi_esp_hosted.rs | 143 +++++ 9 files changed, 2012 insertions(+) create mode 100644 embassy-net-esp-hosted/Cargo.toml create mode 100644 embassy-net-esp-hosted/src/control.rs create mode 100644 embassy-net-esp-hosted/src/esp_hosted_config.proto create mode 100644 embassy-net-esp-hosted/src/fmt.rs create mode 100644 embassy-net-esp-hosted/src/ioctl.rs create mode 100644 embassy-net-esp-hosted/src/lib.rs create mode 100644 embassy-net-esp-hosted/src/proto.rs create mode 100644 examples/nrf52840/src/bin/wifi_esp_hosted.rs diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml new file mode 100644 index 000000000..a7e18ee09 --- /dev/null +++ b/embassy-net-esp-hosted/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "embassy-net-esp-hosted" +version = "0.1.0" +edition = "2021" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync"} +embassy-futures = { version = "0.1.0", path = "../embassy-futures"} +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} + +embedded-hal = { version = "1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } + +noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } +#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } +heapless = "0.7.16" diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs new file mode 100644 index 000000000..2381c6b84 --- /dev/null +++ b/embassy-net-esp-hosted/src/control.rs @@ -0,0 +1,141 @@ +use ch::driver::LinkState; +use defmt::Debug2Format; +use embassy_net_driver_channel as ch; +use heapless::String; + +use crate::ioctl::IoctlState; +use crate::proto::{self, CtrlMsg}; + +#[derive(Debug)] +pub struct Error { + pub status: u32, +} + +pub struct Control<'a> { + state_ch: ch::StateRunner<'a>, + ioctl_state: &'a IoctlState, +} + +enum WifiMode { + None = 0, + Sta = 1, + Ap = 2, + ApSta = 3, +} + +impl<'a> Control<'a> { + pub(crate) fn new(state_ch: ch::StateRunner<'a>, ioctl_state: &'a IoctlState) -> Self { + Self { state_ch, ioctl_state } + } + + pub async fn init(&mut self) { + debug!("set wifi mode"); + self.set_wifi_mode(WifiMode::Sta as _).await; + let mac_addr = self.get_mac_addr().await; + debug!("mac addr: {:02x}", mac_addr); + self.state_ch.set_ethernet_address(mac_addr); + } + + pub async fn join(&mut self, ssid: &str, password: &str) { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqConnectAp as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { + ssid: String::from(ssid), + pwd: String::from(password), + bssid: String::new(), + listen_interval: 3, + is_wpa3_supported: false, + })), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + debug!("======= {:?}", Debug2Format(&resp)); + assert_eq!(resp.resp, 0); + self.state_ch.set_link_state(LinkState::Up); + } + + async fn get_mac_addr(&mut self) -> [u8; 6] { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( + proto::CtrlMsgReqGetMacAddress { + mode: WifiMode::Sta as _, + }, + )), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + assert_eq!(resp.resp, 0); + + // WHY IS THIS A STRING? WHYYYY + fn nibble_from_hex(b: u8) -> u8 { + match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => b + 0xa - b'a', + b'A'..=b'F' => b + 0xa - b'A', + _ => panic!("invalid hex digit {}", b), + } + } + + let mac = resp.mac.as_bytes(); + let mut res = [0; 6]; + assert_eq!(mac.len(), 17); + for (i, b) in res.iter_mut().enumerate() { + *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) + } + res + } + + async fn get_wifi_mode(&mut self) -> u32 { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqGetWifiMode as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqGetWifiMode(proto::CtrlMsgReqGetMode {})), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespGetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + assert_eq!(resp.resp, 0); + resp.mode + } + + async fn set_wifi_mode(&mut self, mode: u32) { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + assert_eq!(resp.resp, 0); + } + + async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { + let mut buf = [0u8; 128]; + + let req_len = noproto::write(&req, &mut buf).unwrap(); + + struct CancelOnDrop<'a>(&'a IoctlState); + + impl CancelOnDrop<'_> { + fn defuse(self) { + core::mem::forget(self); + } + } + + impl Drop for CancelOnDrop<'_> { + fn drop(&mut self) { + self.0.cancel_ioctl(); + } + } + + let ioctl = CancelOnDrop(self.ioctl_state); + + let resp_len = ioctl.0.do_ioctl(&mut buf, req_len).await; + + ioctl.defuse(); + + noproto::read(&buf[..resp_len]).unwrap() + } +} diff --git a/embassy-net-esp-hosted/src/esp_hosted_config.proto b/embassy-net-esp-hosted/src/esp_hosted_config.proto new file mode 100644 index 000000000..aa1bfde64 --- /dev/null +++ b/embassy-net-esp-hosted/src/esp_hosted_config.proto @@ -0,0 +1,432 @@ +syntax = "proto3"; + +/* Enums similar to ESP IDF */ +enum Ctrl_VendorIEType { + Beacon = 0; + Probe_req = 1; + Probe_resp = 2; + Assoc_req = 3; + Assoc_resp = 4; +} + +enum Ctrl_VendorIEID { + ID_0 = 0; + ID_1 = 1; +} + +enum Ctrl_WifiMode { + NONE = 0; + STA = 1; + AP = 2; + APSTA = 3; +} + +enum Ctrl_WifiBw { + BW_Invalid = 0; + HT20 = 1; + HT40 = 2; +} + +enum Ctrl_WifiPowerSave { + PS_Invalid = 0; + MIN_MODEM = 1; + MAX_MODEM = 2; +} + +enum Ctrl_WifiSecProt { + Open = 0; + WEP = 1; + WPA_PSK = 2; + WPA2_PSK = 3; + WPA_WPA2_PSK = 4; + WPA2_ENTERPRISE = 5; + WPA3_PSK = 6; + WPA2_WPA3_PSK = 7; +} + +/* enums for Control path */ +enum Ctrl_Status { + Connected = 0; + Not_Connected = 1; + No_AP_Found = 2; + Connection_Fail = 3; + Invalid_Argument = 4; + Out_Of_Range = 5; +} + + +enum CtrlMsgType { + MsgType_Invalid = 0; + Req = 1; + Resp = 2; + Event = 3; + MsgType_Max = 4; +} + +enum CtrlMsgId { + MsgId_Invalid = 0; + + /** Request Msgs **/ + Req_Base = 100; + + Req_GetMACAddress = 101; + Req_SetMacAddress = 102; + Req_GetWifiMode = 103; + Req_SetWifiMode = 104; + + Req_GetAPScanList = 105; + Req_GetAPConfig = 106; + Req_ConnectAP = 107; + Req_DisconnectAP = 108; + + Req_GetSoftAPConfig = 109; + Req_SetSoftAPVendorSpecificIE = 110; + Req_StartSoftAP = 111; + Req_GetSoftAPConnectedSTAList = 112; + Req_StopSoftAP = 113; + + Req_SetPowerSaveMode = 114; + Req_GetPowerSaveMode = 115; + + Req_OTABegin = 116; + Req_OTAWrite = 117; + Req_OTAEnd = 118; + + Req_SetWifiMaxTxPower = 119; + Req_GetWifiCurrTxPower = 120; + + Req_ConfigHeartbeat = 121; + /* Add new control path command response before Req_Max + * and update Req_Max */ + Req_Max = 122; + + /** Response Msgs **/ + Resp_Base = 200; + + Resp_GetMACAddress = 201; + Resp_SetMacAddress = 202; + Resp_GetWifiMode = 203; + Resp_SetWifiMode = 204; + + Resp_GetAPScanList = 205; + Resp_GetAPConfig = 206; + Resp_ConnectAP = 207; + Resp_DisconnectAP = 208; + + Resp_GetSoftAPConfig = 209; + Resp_SetSoftAPVendorSpecificIE = 210; + Resp_StartSoftAP = 211; + Resp_GetSoftAPConnectedSTAList = 212; + Resp_StopSoftAP = 213; + + Resp_SetPowerSaveMode = 214; + Resp_GetPowerSaveMode = 215; + + Resp_OTABegin = 216; + Resp_OTAWrite = 217; + Resp_OTAEnd = 218; + + Resp_SetWifiMaxTxPower = 219; + Resp_GetWifiCurrTxPower = 220; + + Resp_ConfigHeartbeat = 221; + /* Add new control path command response before Resp_Max + * and update Resp_Max */ + Resp_Max = 222; + + /** Event Msgs **/ + Event_Base = 300; + Event_ESPInit = 301; + Event_Heartbeat = 302; + Event_StationDisconnectFromAP = 303; + Event_StationDisconnectFromESPSoftAP = 304; + /* Add new control path command notification before Event_Max + * and update Event_Max */ + Event_Max = 305; +} + +/* internal supporting structures for CtrlMsg */ +message ScanResult { + bytes ssid = 1; + uint32 chnl = 2; + int32 rssi = 3; + bytes bssid = 4; + Ctrl_WifiSecProt sec_prot = 5; +} + +message ConnectedSTAList { + bytes mac = 1; + int32 rssi = 2; +} + + +/* Control path structures */ +/** Req/Resp structure **/ +message CtrlMsg_Req_GetMacAddress { + int32 mode = 1; +} + +message CtrlMsg_Resp_GetMacAddress { + bytes mac = 1; + int32 resp = 2; +} + +message CtrlMsg_Req_GetMode { +} + +message CtrlMsg_Resp_GetMode { + int32 mode = 1; + int32 resp = 2; +} + +message CtrlMsg_Req_SetMode { + int32 mode = 1; +} + +message CtrlMsg_Resp_SetMode { + int32 resp = 1; +} + +message CtrlMsg_Req_GetStatus { +} + +message CtrlMsg_Resp_GetStatus { + int32 resp = 1; +} + +message CtrlMsg_Req_SetMacAddress { + bytes mac = 1; + int32 mode = 2; +} + +message CtrlMsg_Resp_SetMacAddress { + int32 resp = 1; +} + +message CtrlMsg_Req_GetAPConfig { +} + +message CtrlMsg_Resp_GetAPConfig { + bytes ssid = 1; + bytes bssid = 2; + int32 rssi = 3; + int32 chnl = 4; + Ctrl_WifiSecProt sec_prot = 5; + int32 resp = 6; +} + +message CtrlMsg_Req_ConnectAP { + string ssid = 1; + string pwd = 2; + string bssid = 3; + bool is_wpa3_supported = 4; + int32 listen_interval = 5; +} + +message CtrlMsg_Resp_ConnectAP { + int32 resp = 1; + bytes mac = 2; +} + +message CtrlMsg_Req_GetSoftAPConfig { +} + +message CtrlMsg_Resp_GetSoftAPConfig { + bytes ssid = 1; + bytes pwd = 2; + int32 chnl = 3; + Ctrl_WifiSecProt sec_prot = 4; + int32 max_conn = 5; + bool ssid_hidden = 6; + int32 bw = 7; + int32 resp = 8; +} + +message CtrlMsg_Req_StartSoftAP { + string ssid = 1; + string pwd = 2; + int32 chnl = 3; + Ctrl_WifiSecProt sec_prot = 4; + int32 max_conn = 5; + bool ssid_hidden = 6; + int32 bw = 7; +} + +message CtrlMsg_Resp_StartSoftAP { + int32 resp = 1; + bytes mac = 2; +} + +message CtrlMsg_Req_ScanResult { +} + +message CtrlMsg_Resp_ScanResult { + uint32 count = 1; + repeated ScanResult entries = 2; + int32 resp = 3; +} + +message CtrlMsg_Req_SoftAPConnectedSTA { +} + +message CtrlMsg_Resp_SoftAPConnectedSTA { + uint32 num = 1; + repeated ConnectedSTAList stations = 2; + int32 resp = 3; +} + +message CtrlMsg_Req_OTABegin { +} + +message CtrlMsg_Resp_OTABegin { + int32 resp = 1; +} + +message CtrlMsg_Req_OTAWrite { + bytes ota_data = 1; +} + +message CtrlMsg_Resp_OTAWrite { + int32 resp = 1; +} + +message CtrlMsg_Req_OTAEnd { +} + +message CtrlMsg_Resp_OTAEnd { + int32 resp = 1; +} + +message CtrlMsg_Req_VendorIEData { + int32 element_id = 1; + int32 length = 2; + bytes vendor_oui = 3; + int32 vendor_oui_type = 4; + bytes payload = 5; +} + +message CtrlMsg_Req_SetSoftAPVendorSpecificIE { + bool enable = 1; + Ctrl_VendorIEType type = 2; + Ctrl_VendorIEID idx = 3; + CtrlMsg_Req_VendorIEData vendor_ie_data = 4; +} + +message CtrlMsg_Resp_SetSoftAPVendorSpecificIE { + int32 resp = 1; +} + +message CtrlMsg_Req_SetWifiMaxTxPower { + int32 wifi_max_tx_power = 1; +} + +message CtrlMsg_Resp_SetWifiMaxTxPower { + int32 resp = 1; +} + +message CtrlMsg_Req_GetWifiCurrTxPower { +} + +message CtrlMsg_Resp_GetWifiCurrTxPower { + int32 wifi_curr_tx_power = 1; + int32 resp = 2; +} + +message CtrlMsg_Req_ConfigHeartbeat { + bool enable = 1; + int32 duration = 2; +} + +message CtrlMsg_Resp_ConfigHeartbeat { + int32 resp = 1; +} + +/** Event structure **/ +message CtrlMsg_Event_ESPInit { + bytes init_data = 1; +} + +message CtrlMsg_Event_Heartbeat { + int32 hb_num = 1; +} + +message CtrlMsg_Event_StationDisconnectFromAP { + int32 resp = 1; +} + +message CtrlMsg_Event_StationDisconnectFromESPSoftAP { + int32 resp = 1; + bytes mac = 2; +} + +message CtrlMsg { + /* msg_type could be req, resp or Event */ + CtrlMsgType msg_type = 1; + + /* msg id */ + CtrlMsgId msg_id = 2; + + /* union of all msg ids */ + oneof payload { + /** Requests **/ + CtrlMsg_Req_GetMacAddress req_get_mac_address = 101; + CtrlMsg_Req_SetMacAddress req_set_mac_address = 102; + CtrlMsg_Req_GetMode req_get_wifi_mode = 103; + CtrlMsg_Req_SetMode req_set_wifi_mode = 104; + + CtrlMsg_Req_ScanResult req_scan_ap_list = 105; + CtrlMsg_Req_GetAPConfig req_get_ap_config = 106; + CtrlMsg_Req_ConnectAP req_connect_ap = 107; + CtrlMsg_Req_GetStatus req_disconnect_ap = 108; + + CtrlMsg_Req_GetSoftAPConfig req_get_softap_config = 109; + CtrlMsg_Req_SetSoftAPVendorSpecificIE req_set_softap_vendor_specific_ie = 110; + CtrlMsg_Req_StartSoftAP req_start_softap = 111; + CtrlMsg_Req_SoftAPConnectedSTA req_softap_connected_stas_list = 112; + CtrlMsg_Req_GetStatus req_stop_softap = 113; + + CtrlMsg_Req_SetMode req_set_power_save_mode = 114; + CtrlMsg_Req_GetMode req_get_power_save_mode = 115; + + CtrlMsg_Req_OTABegin req_ota_begin = 116; + CtrlMsg_Req_OTAWrite req_ota_write = 117; + CtrlMsg_Req_OTAEnd req_ota_end = 118; + + CtrlMsg_Req_SetWifiMaxTxPower req_set_wifi_max_tx_power = 119; + CtrlMsg_Req_GetWifiCurrTxPower req_get_wifi_curr_tx_power = 120; + CtrlMsg_Req_ConfigHeartbeat req_config_heartbeat = 121; + + /** Responses **/ + CtrlMsg_Resp_GetMacAddress resp_get_mac_address = 201; + CtrlMsg_Resp_SetMacAddress resp_set_mac_address = 202; + CtrlMsg_Resp_GetMode resp_get_wifi_mode = 203; + CtrlMsg_Resp_SetMode resp_set_wifi_mode = 204; + + CtrlMsg_Resp_ScanResult resp_scan_ap_list = 205; + CtrlMsg_Resp_GetAPConfig resp_get_ap_config = 206; + CtrlMsg_Resp_ConnectAP resp_connect_ap = 207; + CtrlMsg_Resp_GetStatus resp_disconnect_ap = 208; + + CtrlMsg_Resp_GetSoftAPConfig resp_get_softap_config = 209; + CtrlMsg_Resp_SetSoftAPVendorSpecificIE resp_set_softap_vendor_specific_ie = 210; + CtrlMsg_Resp_StartSoftAP resp_start_softap = 211; + CtrlMsg_Resp_SoftAPConnectedSTA resp_softap_connected_stas_list = 212; + CtrlMsg_Resp_GetStatus resp_stop_softap = 213; + + CtrlMsg_Resp_SetMode resp_set_power_save_mode = 214; + CtrlMsg_Resp_GetMode resp_get_power_save_mode = 215; + + CtrlMsg_Resp_OTABegin resp_ota_begin = 216; + CtrlMsg_Resp_OTAWrite resp_ota_write = 217; + CtrlMsg_Resp_OTAEnd resp_ota_end = 218; + CtrlMsg_Resp_SetWifiMaxTxPower resp_set_wifi_max_tx_power = 219; + CtrlMsg_Resp_GetWifiCurrTxPower resp_get_wifi_curr_tx_power = 220; + CtrlMsg_Resp_ConfigHeartbeat resp_config_heartbeat = 221; + + /** Notifications **/ + CtrlMsg_Event_ESPInit event_esp_init = 301; + CtrlMsg_Event_Heartbeat event_heartbeat = 302; + CtrlMsg_Event_StationDisconnectFromAP event_station_disconnect_from_AP = 303; + CtrlMsg_Event_StationDisconnectFromESPSoftAP event_station_disconnect_from_ESP_SoftAP = 304; + } +} diff --git a/embassy-net-esp-hosted/src/fmt.rs b/embassy-net-esp-hosted/src/fmt.rs new file mode 100644 index 000000000..91984bde1 --- /dev/null +++ b/embassy-net-esp-hosted/src/fmt.rs @@ -0,0 +1,257 @@ +#![macro_use] +#![allow(unused_macros)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unreachable { + ($($x:tt)*) => { + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*); + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +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 + } +} + +pub struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs new file mode 100644 index 000000000..59bdabf37 --- /dev/null +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -0,0 +1,119 @@ +use core::cell::{Cell, RefCell}; +use core::future::poll_fn; +use core::task::{Poll, Waker}; + +use embassy_sync::waitqueue::WakerRegistration; + +use crate::fmt::Bytes; + +#[derive(Clone, Copy)] +pub struct PendingIoctl { + pub buf: *mut [u8], + pub req_len: usize, +} + +#[derive(Clone, Copy)] +enum IoctlStateInner { + Pending(PendingIoctl), + Sent { buf: *mut [u8] }, + Done { resp_len: usize }, +} + +struct Wakers { + control: WakerRegistration, + runner: WakerRegistration, +} + +impl Default for Wakers { + fn default() -> Self { + Self { + control: WakerRegistration::new(), + runner: WakerRegistration::new(), + } + } +} + +pub struct IoctlState { + state: Cell, + wakers: RefCell, +} + +impl IoctlState { + pub fn new() -> Self { + Self { + state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), + wakers: Default::default(), + } + } + + fn wake_control(&self) { + self.wakers.borrow_mut().control.wake(); + } + + fn register_control(&self, waker: &Waker) { + self.wakers.borrow_mut().control.register(waker); + } + + fn wake_runner(&self) { + self.wakers.borrow_mut().runner.wake(); + } + + fn register_runner(&self, waker: &Waker) { + self.wakers.borrow_mut().runner.register(waker); + } + + pub async fn wait_complete(&self) -> usize { + poll_fn(|cx| { + if let IoctlStateInner::Done { resp_len } = self.state.get() { + Poll::Ready(resp_len) + } else { + self.register_control(cx.waker()); + Poll::Pending + } + }) + .await + } + + pub async fn wait_pending(&self) -> PendingIoctl { + let pending = poll_fn(|cx| { + if let IoctlStateInner::Pending(pending) = self.state.get() { + Poll::Ready(pending) + } else { + self.register_runner(cx.waker()); + Poll::Pending + } + }) + .await; + + self.state.set(IoctlStateInner::Sent { buf: pending.buf }); + pending + } + + pub fn cancel_ioctl(&self) { + self.state.set(IoctlStateInner::Done { resp_len: 0 }); + } + + pub async fn do_ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { + debug!("IOCTL Request: {:02x}", Bytes(&buf[..req_len])); + + self.state.set(IoctlStateInner::Pending(PendingIoctl { buf, req_len })); + self.wake_runner(); + self.wait_complete().await + } + + pub fn ioctl_done(&self, response: &[u8]) { + if let IoctlStateInner::Sent { buf } = self.state.get() { + debug!("IOCTL Response: {:02x}", Bytes(response)); + + // TODO fix this + (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); + + self.state.set(IoctlStateInner::Done { + resp_len: response.len(), + }); + self.wake_control(); + } else { + warn!("IOCTL Response but no pending Ioctl"); + } + } +} diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs new file mode 100644 index 000000000..2cf05a7df --- /dev/null +++ b/embassy-net-esp-hosted/src/lib.rs @@ -0,0 +1,300 @@ +#![no_std] + +use control::Control; +use embassy_futures::select::{select3, Either3}; +use embassy_net_driver_channel as ch; +use embassy_time::{Duration, Instant, Timer}; +use embedded_hal::digital::{InputPin, OutputPin}; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiDevice; +use ioctl::IoctlState; + +use crate::ioctl::PendingIoctl; + +mod proto; + +// must be first +mod fmt; + +mod control; +mod ioctl; + +const MTU: usize = 1514; + +macro_rules! impl_bytes { + ($t:ident) => { + impl $t { + pub const SIZE: usize = core::mem::size_of::(); + + #[allow(unused)] + pub fn to_bytes(&self) -> [u8; Self::SIZE] { + unsafe { core::mem::transmute(*self) } + } + + #[allow(unused)] + pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self { + let alignment = core::mem::align_of::(); + assert_eq!( + bytes.as_ptr().align_offset(alignment), + 0, + "{} is not aligned", + core::any::type_name::() + ); + unsafe { core::mem::transmute(bytes) } + } + + #[allow(unused)] + pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self { + let alignment = core::mem::align_of::(); + assert_eq!( + bytes.as_ptr().align_offset(alignment), + 0, + "{} is not aligned", + core::any::type_name::() + ); + + unsafe { core::mem::transmute(bytes) } + } + } + }; +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug, Default)] +struct PayloadHeader { + /// InterfaceType on lower 4 bits, number on higher 4 bits. + if_type_and_num: u8, + + /// Flags. + /// + /// bit 0: more fragments. + flags: u8, + + len: u16, + offset: u16, + checksum: u16, + seq_num: u16, + reserved2: u8, + + /// Packet type for HCI or PRIV interface, reserved otherwise + hci_priv_packet_type: u8, +} +impl_bytes!(PayloadHeader); + +#[repr(u8)] +enum InterfaceType { + Sta = 0, + Ap = 1, + Serial = 2, + Hci = 3, + Priv = 4, + Test = 5, +} + +const MAX_SPI_BUFFER_SIZE: usize = 1600; + +pub struct State { + ioctl_state: IoctlState, + ch: ch::State, +} + +impl State { + pub fn new() -> Self { + Self { + ioctl_state: IoctlState::new(), + ch: ch::State::new(), + } + } +} + +pub type NetDriver<'a> = ch::Device<'a, MTU>; + +pub async fn new<'a, SPI, IN, OUT>( + state: &'a mut State, + spi: SPI, + handshake: IN, + ready: IN, + reset: OUT, +) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>) +where + SPI: SpiDevice, + IN: InputPin + Wait, + OUT: OutputPin, +{ + let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let state_ch = ch_runner.state_runner(); + + let mut runner = Runner { + ch: ch_runner, + ioctl_state: &state.ioctl_state, + next_seq: 1, + handshake, + ready, + reset, + spi, + }; + runner.init().await; + + (device, Control::new(state_ch, &state.ioctl_state), runner) +} + +pub struct Runner<'a, SPI, IN, OUT> { + ch: ch::Runner<'a, MTU>, + ioctl_state: &'a IoctlState, + + next_seq: u16, + + spi: SPI, + handshake: IN, + ready: IN, + reset: OUT, +} + +impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT> +where + SPI: SpiDevice, + IN: InputPin + Wait, + OUT: OutputPin, +{ + async fn init(&mut self) {} + + pub async fn run(mut self) -> ! { + debug!("resetting..."); + self.reset.set_low().unwrap(); + Timer::after(Duration::from_millis(100)).await; + self.reset.set_high().unwrap(); + Timer::after(Duration::from_millis(1000)).await; + + let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; + let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; + + loop { + self.handshake.wait_for_high().await.unwrap(); + + let ioctl = self.ioctl_state.wait_pending(); + let tx = self.ch.tx_buf(); + let ev = async { self.ready.wait_for_high().await.unwrap() }; + + match select3(ioctl, tx, ev).await { + Either3::First(PendingIoctl { buf, req_len }) => { + tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); + tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); + tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); + + let mut header = PayloadHeader { + if_type_and_num: InterfaceType::Serial as _, + len: (req_len + 14) as _, + offset: PayloadHeader::SIZE as _, + seq_num: self.next_seq, + ..Default::default() + }; + self.next_seq = self.next_seq.wrapping_add(1); + + // Calculate checksum + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + header.checksum = checksum(&tx_buf[..26 + req_len]); + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + + debug!("====== SENDING IOCTL"); + } + Either3::Second(packet) => { + tx_buf[12..][..packet.len()].copy_from_slice(packet); + + let mut header = PayloadHeader { + if_type_and_num: InterfaceType::Sta as _, + len: packet.len() as _, + offset: PayloadHeader::SIZE as _, + seq_num: self.next_seq, + ..Default::default() + }; + self.next_seq = self.next_seq.wrapping_add(1); + + // Calculate checksum + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + header.checksum = checksum(&tx_buf[..12 + packet.len()]); + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + + self.ch.tx_done(); + } + Either3::Third(()) => { + tx_buf[..PayloadHeader::SIZE].fill(0); + } + } + + if tx_buf[0] != 0 { + trace!("tx: {:02x}", &tx_buf[..40]); + } + + self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + let delay_until = Instant::now() + Duration::from_millis(1); + self.handle_rx(&mut rx_buf); + Timer::at(delay_until).await; + } + } + + fn handle_rx(&mut self, buf: &mut [u8]) { + trace!("rx: {:02x}", &buf[..40]); + + let buf_len = buf.len(); + let h = PayloadHeader::from_bytes_mut((&mut buf[..PayloadHeader::SIZE]).try_into().unwrap()); + + if h.len == 0 || h.offset as usize != PayloadHeader::SIZE { + return; + } + + let payload_len = h.len as usize; + if buf_len < PayloadHeader::SIZE + payload_len { + warn!("rx: len too big"); + return; + } + + let if_type_and_num = h.if_type_and_num; + let want_checksum = h.checksum; + h.checksum = 0; + let got_checksum = checksum(&buf[..PayloadHeader::SIZE + payload_len]); + if want_checksum != got_checksum { + warn!("rx: bad checksum. Got {:04x}, want {:04x}", got_checksum, want_checksum); + return; + } + + let payload = &mut buf[PayloadHeader::SIZE..][..payload_len]; + + match if_type_and_num & 0x0f { + // STA + 0 => match self.ch.try_rx_buf() { + Some(buf) => { + buf[..payload.len()].copy_from_slice(payload); + self.ch.rx_done(payload.len()) + } + None => warn!("failed to push rxd packet to the channel."), + }, + // serial + 2 => { + debug!("serial rx: {:02x}", payload); + if payload.len() < 14 { + warn!("serial rx: too short"); + return; + } + if &payload[..12] != b"\x01\x08\x00ctrlResp\x02" { + warn!("serial rx: bad tlv"); + return; + } + let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize; + if payload.len() < 14 + len { + warn!("serial rx: too short 2"); + return; + } + self.ioctl_state.ioctl_done(&payload[14..][..len]); + } + _ => warn!("unknown iftype {}", if_type_and_num), + } + } +} + +fn checksum(buf: &[u8]) -> u16 { + let mut res = 0u16; + for &b in buf { + res = res.wrapping_add(b as _); + } + res +} diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs new file mode 100644 index 000000000..e105e393c --- /dev/null +++ b/embassy-net-esp-hosted/src/proto.rs @@ -0,0 +1,598 @@ +use heapless::{String, Vec}; + +/// internal supporting structures for CtrlMsg + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct ScanResult { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub chnl: u32, + #[noproto(tag = "3")] + pub rssi: u32, + #[noproto(tag = "4")] + pub bssid: String<32>, + #[noproto(tag = "5")] + pub sec_prot: CtrlWifiSecProt, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct ConnectedStaList { + #[noproto(tag = "1")] + pub mac: String<32>, + #[noproto(tag = "2")] + pub rssi: u32, +} +/// * Req/Resp structure * + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetMacAddress { + #[noproto(tag = "1")] + pub mode: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetMacAddress { + #[noproto(tag = "1")] + pub mac: String<32>, + #[noproto(tag = "2")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetMode {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetMode { + #[noproto(tag = "1")] + pub mode: u32, + #[noproto(tag = "2")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetMode { + #[noproto(tag = "1")] + pub mode: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetMode { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetStatus {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetStatus { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetMacAddress { + #[noproto(tag = "1")] + pub mac: String<32>, + #[noproto(tag = "2")] + pub mode: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetMacAddress { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetApConfig {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetApConfig { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub bssid: String<32>, + #[noproto(tag = "3")] + pub rssi: u32, + #[noproto(tag = "4")] + pub chnl: u32, + #[noproto(tag = "5")] + pub sec_prot: CtrlWifiSecProt, + #[noproto(tag = "6")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqConnectAp { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub pwd: String<32>, + #[noproto(tag = "3")] + pub bssid: String<32>, + #[noproto(tag = "4")] + pub is_wpa3_supported: bool, + #[noproto(tag = "5")] + pub listen_interval: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespConnectAp { + #[noproto(tag = "1")] + pub resp: u32, + #[noproto(tag = "2")] + pub mac: String<32>, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetSoftApConfig {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetSoftApConfig { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub pwd: String<32>, + #[noproto(tag = "3")] + pub chnl: u32, + #[noproto(tag = "4")] + pub sec_prot: CtrlWifiSecProt, + #[noproto(tag = "5")] + pub max_conn: u32, + #[noproto(tag = "6")] + pub ssid_hidden: bool, + #[noproto(tag = "7")] + pub bw: u32, + #[noproto(tag = "8")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqStartSoftAp { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub pwd: String<32>, + #[noproto(tag = "3")] + pub chnl: u32, + #[noproto(tag = "4")] + pub sec_prot: CtrlWifiSecProt, + #[noproto(tag = "5")] + pub max_conn: u32, + #[noproto(tag = "6")] + pub ssid_hidden: bool, + #[noproto(tag = "7")] + pub bw: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespStartSoftAp { + #[noproto(tag = "1")] + pub resp: u32, + #[noproto(tag = "2")] + pub mac: String<32>, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqScanResult {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespScanResult { + #[noproto(tag = "1")] + pub count: u32, + #[noproto(repeated, tag = "2")] + pub entries: Vec, + #[noproto(tag = "3")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSoftApConnectedSta {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSoftApConnectedSta { + #[noproto(tag = "1")] + pub num: u32, + #[noproto(repeated, tag = "2")] + pub stations: Vec, + #[noproto(tag = "3")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqOtaBegin {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespOtaBegin { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqOtaWrite { + #[noproto(tag = "1")] + pub ota_data: Vec, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespOtaWrite { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqOtaEnd {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespOtaEnd { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqVendorIeData { + #[noproto(tag = "1")] + pub element_id: u32, + #[noproto(tag = "2")] + pub length: u32, + #[noproto(tag = "3")] + pub vendor_oui: Vec, + #[noproto(tag = "4")] + pub vendor_oui_type: u32, + #[noproto(tag = "5")] + pub payload: Vec, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetSoftApVendorSpecificIe { + #[noproto(tag = "1")] + pub enable: bool, + #[noproto(tag = "2")] + pub r#type: CtrlVendorIeType, + #[noproto(tag = "3")] + pub idx: CtrlVendorIeid, + #[noproto(optional, tag = "4")] + pub vendor_ie_data: Option, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetSoftApVendorSpecificIe { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetWifiMaxTxPower { + #[noproto(tag = "1")] + pub wifi_max_tx_power: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetWifiMaxTxPower { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetWifiCurrTxPower {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetWifiCurrTxPower { + #[noproto(tag = "1")] + pub wifi_curr_tx_power: u32, + #[noproto(tag = "2")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqConfigHeartbeat { + #[noproto(tag = "1")] + pub enable: bool, + #[noproto(tag = "2")] + pub duration: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespConfigHeartbeat { + #[noproto(tag = "1")] + pub resp: u32, +} +/// * Event structure * + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventEspInit { + #[noproto(tag = "1")] + pub init_data: Vec, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventHeartbeat { + #[noproto(tag = "1")] + pub hb_num: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventStationDisconnectFromAp { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventStationDisconnectFromEspSoftAp { + #[noproto(tag = "1")] + pub resp: u32, + #[noproto(tag = "2")] + pub mac: String<32>, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsg { + /// msg_type could be req, resp or Event + #[noproto(tag = "1")] + pub msg_type: CtrlMsgType, + /// msg id + #[noproto(tag = "2")] + pub msg_id: CtrlMsgId, + /// union of all msg ids + #[noproto( + oneof, + tags = "101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 301, 302, 303, 304" + )] + pub payload: Option, +} + +/// union of all msg ids +#[derive(Debug, Clone, Eq, PartialEq, noproto::Oneof)] +pub enum CtrlMsgPayload { + /// * Requests * + #[noproto(tag = "101")] + ReqGetMacAddress(CtrlMsgReqGetMacAddress), + #[noproto(tag = "102")] + ReqSetMacAddress(CtrlMsgReqSetMacAddress), + #[noproto(tag = "103")] + ReqGetWifiMode(CtrlMsgReqGetMode), + #[noproto(tag = "104")] + ReqSetWifiMode(CtrlMsgReqSetMode), + #[noproto(tag = "105")] + ReqScanApList(CtrlMsgReqScanResult), + #[noproto(tag = "106")] + ReqGetApConfig(CtrlMsgReqGetApConfig), + #[noproto(tag = "107")] + ReqConnectAp(CtrlMsgReqConnectAp), + #[noproto(tag = "108")] + ReqDisconnectAp(CtrlMsgReqGetStatus), + #[noproto(tag = "109")] + ReqGetSoftapConfig(CtrlMsgReqGetSoftApConfig), + #[noproto(tag = "110")] + ReqSetSoftapVendorSpecificIe(CtrlMsgReqSetSoftApVendorSpecificIe), + #[noproto(tag = "111")] + ReqStartSoftap(CtrlMsgReqStartSoftAp), + #[noproto(tag = "112")] + ReqSoftapConnectedStasList(CtrlMsgReqSoftApConnectedSta), + #[noproto(tag = "113")] + ReqStopSoftap(CtrlMsgReqGetStatus), + #[noproto(tag = "114")] + ReqSetPowerSaveMode(CtrlMsgReqSetMode), + #[noproto(tag = "115")] + ReqGetPowerSaveMode(CtrlMsgReqGetMode), + #[noproto(tag = "116")] + ReqOtaBegin(CtrlMsgReqOtaBegin), + #[noproto(tag = "117")] + ReqOtaWrite(CtrlMsgReqOtaWrite), + #[noproto(tag = "118")] + ReqOtaEnd(CtrlMsgReqOtaEnd), + #[noproto(tag = "119")] + ReqSetWifiMaxTxPower(CtrlMsgReqSetWifiMaxTxPower), + #[noproto(tag = "120")] + ReqGetWifiCurrTxPower(CtrlMsgReqGetWifiCurrTxPower), + #[noproto(tag = "121")] + ReqConfigHeartbeat(CtrlMsgReqConfigHeartbeat), + /// * Responses * + #[noproto(tag = "201")] + RespGetMacAddress(CtrlMsgRespGetMacAddress), + #[noproto(tag = "202")] + RespSetMacAddress(CtrlMsgRespSetMacAddress), + #[noproto(tag = "203")] + RespGetWifiMode(CtrlMsgRespGetMode), + #[noproto(tag = "204")] + RespSetWifiMode(CtrlMsgRespSetMode), + #[noproto(tag = "205")] + RespScanApList(CtrlMsgRespScanResult), + #[noproto(tag = "206")] + RespGetApConfig(CtrlMsgRespGetApConfig), + #[noproto(tag = "207")] + RespConnectAp(CtrlMsgRespConnectAp), + #[noproto(tag = "208")] + RespDisconnectAp(CtrlMsgRespGetStatus), + #[noproto(tag = "209")] + RespGetSoftapConfig(CtrlMsgRespGetSoftApConfig), + #[noproto(tag = "210")] + RespSetSoftapVendorSpecificIe(CtrlMsgRespSetSoftApVendorSpecificIe), + #[noproto(tag = "211")] + RespStartSoftap(CtrlMsgRespStartSoftAp), + #[noproto(tag = "212")] + RespSoftapConnectedStasList(CtrlMsgRespSoftApConnectedSta), + #[noproto(tag = "213")] + RespStopSoftap(CtrlMsgRespGetStatus), + #[noproto(tag = "214")] + RespSetPowerSaveMode(CtrlMsgRespSetMode), + #[noproto(tag = "215")] + RespGetPowerSaveMode(CtrlMsgRespGetMode), + #[noproto(tag = "216")] + RespOtaBegin(CtrlMsgRespOtaBegin), + #[noproto(tag = "217")] + RespOtaWrite(CtrlMsgRespOtaWrite), + #[noproto(tag = "218")] + RespOtaEnd(CtrlMsgRespOtaEnd), + #[noproto(tag = "219")] + RespSetWifiMaxTxPower(CtrlMsgRespSetWifiMaxTxPower), + #[noproto(tag = "220")] + RespGetWifiCurrTxPower(CtrlMsgRespGetWifiCurrTxPower), + #[noproto(tag = "221")] + RespConfigHeartbeat(CtrlMsgRespConfigHeartbeat), + /// * Notifications * + #[noproto(tag = "301")] + EventEspInit(CtrlMsgEventEspInit), + #[noproto(tag = "302")] + EventHeartbeat(CtrlMsgEventHeartbeat), + #[noproto(tag = "303")] + EventStationDisconnectFromAp(CtrlMsgEventStationDisconnectFromAp), + #[noproto(tag = "304")] + EventStationDisconnectFromEspSoftAp(CtrlMsgEventStationDisconnectFromEspSoftAp), +} + +/// Enums similar to ESP IDF +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlVendorIeType { + #[default] + Beacon = 0, + ProbeReq = 1, + ProbeResp = 2, + AssocReq = 3, + AssocResp = 4, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlVendorIeid { + #[default] + Id0 = 0, + Id1 = 1, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiMode { + #[default] + None = 0, + Sta = 1, + Ap = 2, + Apsta = 3, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiBw { + #[default] + BwInvalid = 0, + Ht20 = 1, + Ht40 = 2, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiPowerSave { + #[default] + PsInvalid = 0, + MinModem = 1, + MaxModem = 2, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiSecProt { + #[default] + Open = 0, + Wep = 1, + WpaPsk = 2, + Wpa2Psk = 3, + WpaWpa2Psk = 4, + Wpa2Enterprise = 5, + Wpa3Psk = 6, + Wpa2Wpa3Psk = 7, +} + +/// enums for Control path +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlStatus { + #[default] + Connected = 0, + NotConnected = 1, + NoApFound = 2, + ConnectionFail = 3, + InvalidArgument = 4, + OutOfRange = 5, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlMsgType { + #[default] + MsgTypeInvalid = 0, + Req = 1, + Resp = 2, + Event = 3, + MsgTypeMax = 4, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlMsgId { + #[default] + MsgIdInvalid = 0, + /// * Request Msgs * + ReqBase = 100, + ReqGetMacAddress = 101, + ReqSetMacAddress = 102, + ReqGetWifiMode = 103, + ReqSetWifiMode = 104, + ReqGetApScanList = 105, + ReqGetApConfig = 106, + ReqConnectAp = 107, + ReqDisconnectAp = 108, + ReqGetSoftApConfig = 109, + ReqSetSoftApVendorSpecificIe = 110, + ReqStartSoftAp = 111, + ReqGetSoftApConnectedStaList = 112, + ReqStopSoftAp = 113, + ReqSetPowerSaveMode = 114, + ReqGetPowerSaveMode = 115, + ReqOtaBegin = 116, + ReqOtaWrite = 117, + ReqOtaEnd = 118, + ReqSetWifiMaxTxPower = 119, + ReqGetWifiCurrTxPower = 120, + ReqConfigHeartbeat = 121, + /// Add new control path command response before Req_Max + /// and update Req_Max + ReqMax = 122, + /// * Response Msgs * + RespBase = 200, + RespGetMacAddress = 201, + RespSetMacAddress = 202, + RespGetWifiMode = 203, + RespSetWifiMode = 204, + RespGetApScanList = 205, + RespGetApConfig = 206, + RespConnectAp = 207, + RespDisconnectAp = 208, + RespGetSoftApConfig = 209, + RespSetSoftApVendorSpecificIe = 210, + RespStartSoftAp = 211, + RespGetSoftApConnectedStaList = 212, + RespStopSoftAp = 213, + RespSetPowerSaveMode = 214, + RespGetPowerSaveMode = 215, + RespOtaBegin = 216, + RespOtaWrite = 217, + RespOtaEnd = 218, + RespSetWifiMaxTxPower = 219, + RespGetWifiCurrTxPower = 220, + RespConfigHeartbeat = 221, + /// Add new control path command response before Resp_Max + /// and update Resp_Max + RespMax = 222, + /// * Event Msgs * + EventBase = 300, + EventEspInit = 301, + EventHeartbeat = 302, + EventStationDisconnectFromAp = 303, + EventStationDisconnectFromEspSoftAp = 304, + /// Add new control path command notification before Event_Max + /// and update Event_Max + EventMax = 305, +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 6627b7861..7c9d48bad 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -22,6 +22,7 @@ embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["ti lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } +embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -35,3 +36,4 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } +embedded-hal-async = "0.2.0-alpha.1" diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs new file mode 100644 index 000000000..401dbd33c --- /dev/null +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -0,0 +1,143 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap, warn}; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, StackResources}; +use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::{self, Spim}; +use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use static_cell::make_static; +use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn wifi_task( + runner: hosted::Runner< + 'static, + ExclusiveDevice, Output<'static, peripherals::P0_31>>, + Input<'static, AnyPin>, + Output<'static, peripherals::P1_03>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_nrf::init(Default::default()); + + let miso = p.P0_28; + let sck = p.P0_29; + let mosi = p.P0_30; + let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive); + let handshake = Input::new(p.P1_01.degrade(), Pull::Up); + let ready = Input::new(p.P1_02.degrade(), Pull::None); + let reset = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M1; + config.mode = spim::MODE_2; // !!! + let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); + let spi = ExclusiveDevice::new(spi, cs); + + let (device, mut control, runner) = embassy_net_esp_hosted::new( + make_static!(embassy_net_esp_hosted::State::new()), + spi, + handshake, + ready, + reset, + ) + .await; + + unwrap!(spawner.spawn(wifi_task(runner))); + + // TODO: wait for ESP_INIT event instead of hardcoding delay. + Timer::after(Duration::from_secs(3)).await; + + control.init().await; + control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} From ec2c095a76a0f55a031093e98a5283cff0daa576 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 18:13:19 +0200 Subject: [PATCH 1417/1575] esp-hosted: print events. --- embassy-net-esp-hosted/src/control.rs | 7 +++- embassy-net-esp-hosted/src/ioctl.rs | 4 +- embassy-net-esp-hosted/src/lib.rs | 36 ++++++++++++++---- embassy-net-esp-hosted/src/proto.rs | 54 +++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 2381c6b84..ec51933b3 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -112,6 +112,8 @@ impl<'a> Control<'a> { } async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { + debug!("ioctl req: {:?}", &req); + let mut buf = [0u8; 128]; let req_len = noproto::write(&req, &mut buf).unwrap(); @@ -136,6 +138,9 @@ impl<'a> Control<'a> { ioctl.defuse(); - noproto::read(&buf[..resp_len]).unwrap() + let res = noproto::read(&buf[..resp_len]).unwrap(); + debug!("ioctl resp: {:?}", &res); + + res } } diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 59bdabf37..689dd2a88 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -94,7 +94,7 @@ impl IoctlState { } pub async fn do_ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { - debug!("IOCTL Request: {:02x}", Bytes(&buf[..req_len])); + trace!("ioctl req bytes: {:02x}", Bytes(&buf[..req_len])); self.state.set(IoctlStateInner::Pending(PendingIoctl { buf, req_len })); self.wake_runner(); @@ -103,7 +103,7 @@ impl IoctlState { pub fn ioctl_done(&self, response: &[u8]) { if let IoctlStateInner::Sent { buf } = self.state.get() { - debug!("IOCTL Response: {:02x}", Bytes(response)); + trace!("ioctl resp bytes: {:02x}", Bytes(response)); // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 2cf05a7df..084009966 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -8,6 +8,7 @@ use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; use ioctl::IoctlState; +use proto::CtrlMsg; use crate::ioctl::PendingIoctl; @@ -194,8 +195,6 @@ where tx_buf[0..12].copy_from_slice(&header.to_bytes()); header.checksum = checksum(&tx_buf[..26 + req_len]); tx_buf[0..12].copy_from_slice(&header.to_bytes()); - - debug!("====== SENDING IOCTL"); } Either3::Second(packet) => { tx_buf[12..][..packet.len()].copy_from_slice(packet); @@ -270,25 +269,46 @@ where }, // serial 2 => { - debug!("serial rx: {:02x}", payload); + trace!("serial rx: {:02x}", payload); if payload.len() < 14 { warn!("serial rx: too short"); return; } - if &payload[..12] != b"\x01\x08\x00ctrlResp\x02" { - warn!("serial rx: bad tlv"); - return; - } + + let isEvent = match &payload[..12] { + b"\x01\x08\x00ctrlResp\x02" => false, + b"\x01\x08\x00ctrlEvnt\x02" => true, + _ => { + warn!("serial rx: bad tlv"); + return; + } + }; + let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize; if payload.len() < 14 + len { warn!("serial rx: too short 2"); return; } - self.ioctl_state.ioctl_done(&payload[14..][..len]); + let data = &payload[14..][..len]; + + if isEvent { + self.handle_event(data); + } else { + self.ioctl_state.ioctl_done(data); + } } _ => warn!("unknown iftype {}", if_type_and_num), } } + + fn handle_event(&self, data: &[u8]) { + let Ok(event) = noproto::read::(data) else { + warn!("failed to parse event"); + return + }; + + debug!("event: {:?}", &event); + } } fn checksum(buf: &[u8]) -> u16 { diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs index e105e393c..8ceb1579d 100644 --- a/embassy-net-esp-hosted/src/proto.rs +++ b/embassy-net-esp-hosted/src/proto.rs @@ -3,6 +3,7 @@ use heapless::{String, Vec}; /// internal supporting structures for CtrlMsg #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanResult { #[noproto(tag = "1")] pub ssid: String<32>, @@ -17,6 +18,7 @@ pub struct ScanResult { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ConnectedStaList { #[noproto(tag = "1")] pub mac: String<32>, @@ -26,12 +28,14 @@ pub struct ConnectedStaList { /// * Req/Resp structure * #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetMacAddress { #[noproto(tag = "1")] pub mode: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetMacAddress { #[noproto(tag = "1")] pub mac: String<32>, @@ -40,9 +44,11 @@ pub struct CtrlMsgRespGetMacAddress { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetMode {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetMode { #[noproto(tag = "1")] pub mode: u32, @@ -51,27 +57,32 @@ pub struct CtrlMsgRespGetMode { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetMode { #[noproto(tag = "1")] pub mode: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetMode { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetStatus {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetStatus { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetMacAddress { #[noproto(tag = "1")] pub mac: String<32>, @@ -80,15 +91,18 @@ pub struct CtrlMsgReqSetMacAddress { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetMacAddress { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetApConfig {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetApConfig { #[noproto(tag = "1")] pub ssid: String<32>, @@ -105,6 +119,7 @@ pub struct CtrlMsgRespGetApConfig { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqConnectAp { #[noproto(tag = "1")] pub ssid: String<32>, @@ -119,6 +134,7 @@ pub struct CtrlMsgReqConnectAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespConnectAp { #[noproto(tag = "1")] pub resp: u32, @@ -127,9 +143,11 @@ pub struct CtrlMsgRespConnectAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetSoftApConfig {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetSoftApConfig { #[noproto(tag = "1")] pub ssid: String<32>, @@ -150,6 +168,7 @@ pub struct CtrlMsgRespGetSoftApConfig { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqStartSoftAp { #[noproto(tag = "1")] pub ssid: String<32>, @@ -168,6 +187,7 @@ pub struct CtrlMsgReqStartSoftAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespStartSoftAp { #[noproto(tag = "1")] pub resp: u32, @@ -176,9 +196,11 @@ pub struct CtrlMsgRespStartSoftAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqScanResult {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespScanResult { #[noproto(tag = "1")] pub count: u32, @@ -189,9 +211,11 @@ pub struct CtrlMsgRespScanResult { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSoftApConnectedSta {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSoftApConnectedSta { #[noproto(tag = "1")] pub num: u32, @@ -202,36 +226,43 @@ pub struct CtrlMsgRespSoftApConnectedSta { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqOtaBegin {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespOtaBegin { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqOtaWrite { #[noproto(tag = "1")] pub ota_data: Vec, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespOtaWrite { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqOtaEnd {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespOtaEnd { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqVendorIeData { #[noproto(tag = "1")] pub element_id: u32, @@ -246,6 +277,7 @@ pub struct CtrlMsgReqVendorIeData { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetSoftApVendorSpecificIe { #[noproto(tag = "1")] pub enable: bool, @@ -258,27 +290,32 @@ pub struct CtrlMsgReqSetSoftApVendorSpecificIe { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetSoftApVendorSpecificIe { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetWifiMaxTxPower { #[noproto(tag = "1")] pub wifi_max_tx_power: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetWifiMaxTxPower { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetWifiCurrTxPower {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetWifiCurrTxPower { #[noproto(tag = "1")] pub wifi_curr_tx_power: u32, @@ -287,6 +324,7 @@ pub struct CtrlMsgRespGetWifiCurrTxPower { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqConfigHeartbeat { #[noproto(tag = "1")] pub enable: bool, @@ -295,6 +333,7 @@ pub struct CtrlMsgReqConfigHeartbeat { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespConfigHeartbeat { #[noproto(tag = "1")] pub resp: u32, @@ -302,24 +341,28 @@ pub struct CtrlMsgRespConfigHeartbeat { /// * Event structure * #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventEspInit { #[noproto(tag = "1")] pub init_data: Vec, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventHeartbeat { #[noproto(tag = "1")] pub hb_num: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventStationDisconnectFromAp { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventStationDisconnectFromEspSoftAp { #[noproto(tag = "1")] pub resp: u32, @@ -328,6 +371,7 @@ pub struct CtrlMsgEventStationDisconnectFromEspSoftAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsg { /// msg_type could be req, resp or Event #[noproto(tag = "1")] @@ -345,6 +389,7 @@ pub struct CtrlMsg { /// union of all msg ids #[derive(Debug, Clone, Eq, PartialEq, noproto::Oneof)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlMsgPayload { /// * Requests * #[noproto(tag = "101")] @@ -446,6 +491,7 @@ pub enum CtrlMsgPayload { /// Enums similar to ESP IDF #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlVendorIeType { #[default] Beacon = 0, @@ -457,6 +503,7 @@ pub enum CtrlVendorIeType { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlVendorIeid { #[default] Id0 = 0, @@ -465,6 +512,7 @@ pub enum CtrlVendorIeid { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiMode { #[default] None = 0, @@ -475,6 +523,7 @@ pub enum CtrlWifiMode { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiBw { #[default] BwInvalid = 0, @@ -484,6 +533,7 @@ pub enum CtrlWifiBw { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiPowerSave { #[default] PsInvalid = 0, @@ -493,6 +543,7 @@ pub enum CtrlWifiPowerSave { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiSecProt { #[default] Open = 0, @@ -508,6 +559,7 @@ pub enum CtrlWifiSecProt { /// enums for Control path #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlStatus { #[default] Connected = 0, @@ -520,6 +572,7 @@ pub enum CtrlStatus { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlMsgType { #[default] MsgTypeInvalid = 0, @@ -531,6 +584,7 @@ pub enum CtrlMsgType { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlMsgId { #[default] MsgIdInvalid = 0, From 082f1ab494587e02d405cad8f186d48943acf16d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 18:55:29 +0200 Subject: [PATCH 1418/1575] esp-hosted: nicer names for shared state struct. --- embassy-net-esp-hosted/src/control.rs | 16 ++--- embassy-net-esp-hosted/src/ioctl.rs | 93 +++++++++++---------------- embassy-net-esp-hosted/src/lib.rs | 16 ++--- 3 files changed, 53 insertions(+), 72 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index ec51933b3..c98d0ebf4 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -3,7 +3,7 @@ use defmt::Debug2Format; use embassy_net_driver_channel as ch; use heapless::String; -use crate::ioctl::IoctlState; +use crate::ioctl::Shared; use crate::proto::{self, CtrlMsg}; #[derive(Debug)] @@ -13,7 +13,7 @@ pub struct Error { pub struct Control<'a> { state_ch: ch::StateRunner<'a>, - ioctl_state: &'a IoctlState, + shared: &'a Shared, } enum WifiMode { @@ -24,8 +24,8 @@ enum WifiMode { } impl<'a> Control<'a> { - pub(crate) fn new(state_ch: ch::StateRunner<'a>, ioctl_state: &'a IoctlState) -> Self { - Self { state_ch, ioctl_state } + pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { + Self { state_ch, shared } } pub async fn init(&mut self) { @@ -118,7 +118,7 @@ impl<'a> Control<'a> { let req_len = noproto::write(&req, &mut buf).unwrap(); - struct CancelOnDrop<'a>(&'a IoctlState); + struct CancelOnDrop<'a>(&'a Shared); impl CancelOnDrop<'_> { fn defuse(self) { @@ -128,13 +128,13 @@ impl<'a> Control<'a> { impl Drop for CancelOnDrop<'_> { fn drop(&mut self) { - self.0.cancel_ioctl(); + self.0.ioctl_cancel(); } } - let ioctl = CancelOnDrop(self.ioctl_state); + let ioctl = CancelOnDrop(self.shared); - let resp_len = ioctl.0.do_ioctl(&mut buf, req_len).await; + let resp_len = ioctl.0.ioctl(&mut buf, req_len).await; ioctl.defuse(); diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 689dd2a88..7cbe80b2a 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -13,105 +13,86 @@ pub struct PendingIoctl { } #[derive(Clone, Copy)] -enum IoctlStateInner { +enum IoctlState { Pending(PendingIoctl), Sent { buf: *mut [u8] }, Done { resp_len: usize }, } -struct Wakers { - control: WakerRegistration, - runner: WakerRegistration, +pub struct Shared(RefCell); + +struct SharedInner { + ioctl: IoctlState, + control_waker: WakerRegistration, + runner_waker: WakerRegistration, } -impl Default for Wakers { - fn default() -> Self { - Self { - control: WakerRegistration::new(), - runner: WakerRegistration::new(), - } - } -} - -pub struct IoctlState { - state: Cell, - wakers: RefCell, -} - -impl IoctlState { +impl Shared { pub fn new() -> Self { - Self { - state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), - wakers: Default::default(), - } + Self(RefCell::new(SharedInner { + ioctl: IoctlState::Done { resp_len: 0 }, + control_waker: WakerRegistration::new(), + runner_waker: WakerRegistration::new(), + })) } - fn wake_control(&self) { - self.wakers.borrow_mut().control.wake(); - } - - fn register_control(&self, waker: &Waker) { - self.wakers.borrow_mut().control.register(waker); - } - - fn wake_runner(&self) { - self.wakers.borrow_mut().runner.wake(); - } - - fn register_runner(&self, waker: &Waker) { - self.wakers.borrow_mut().runner.register(waker); - } - - pub async fn wait_complete(&self) -> usize { + pub async fn ioctl_wait_complete(&self) -> usize { poll_fn(|cx| { - if let IoctlStateInner::Done { resp_len } = self.state.get() { + let mut this = self.0.borrow_mut(); + if let IoctlState::Done { resp_len } = this.ioctl { Poll::Ready(resp_len) } else { - self.register_control(cx.waker()); + this.control_waker.register(cx.waker()); Poll::Pending } }) .await } - pub async fn wait_pending(&self) -> PendingIoctl { + pub async fn ioctl_wait_pending(&self) -> PendingIoctl { let pending = poll_fn(|cx| { - if let IoctlStateInner::Pending(pending) = self.state.get() { + let mut this = self.0.borrow_mut(); + if let IoctlState::Pending(pending) = this.ioctl { Poll::Ready(pending) } else { - self.register_runner(cx.waker()); + this.runner_waker.register(cx.waker()); Poll::Pending } }) .await; - self.state.set(IoctlStateInner::Sent { buf: pending.buf }); + self.0.borrow_mut().ioctl = IoctlState::Sent { buf: pending.buf }; pending } - pub fn cancel_ioctl(&self) { - self.state.set(IoctlStateInner::Done { resp_len: 0 }); + pub fn ioctl_cancel(&self) { + self.0.borrow_mut().ioctl = IoctlState::Done { resp_len: 0 }; } - pub async fn do_ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { + pub async fn ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { trace!("ioctl req bytes: {:02x}", Bytes(&buf[..req_len])); - self.state.set(IoctlStateInner::Pending(PendingIoctl { buf, req_len })); - self.wake_runner(); - self.wait_complete().await + { + let mut this = self.0.borrow_mut(); + this.ioctl = IoctlState::Pending(PendingIoctl { buf, req_len }); + this.runner_waker.wake(); + } + + self.ioctl_wait_complete().await } pub fn ioctl_done(&self, response: &[u8]) { - if let IoctlStateInner::Sent { buf } = self.state.get() { + let mut this = self.0.borrow_mut(); + if let IoctlState::Sent { buf } = this.ioctl { trace!("ioctl resp bytes: {:02x}", Bytes(response)); // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); - self.state.set(IoctlStateInner::Done { + this.ioctl = IoctlState::Done { resp_len: response.len(), - }); - self.wake_control(); + }; + this.control_waker.wake(); } else { warn!("IOCTL Response but no pending Ioctl"); } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 084009966..700a5221c 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -7,7 +7,7 @@ use embassy_time::{Duration, Instant, Timer}; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use ioctl::IoctlState; +use ioctl::Shared; use proto::CtrlMsg; use crate::ioctl::PendingIoctl; @@ -95,14 +95,14 @@ enum InterfaceType { const MAX_SPI_BUFFER_SIZE: usize = 1600; pub struct State { - ioctl_state: IoctlState, + shared: Shared, ch: ch::State, } impl State { pub fn new() -> Self { Self { - ioctl_state: IoctlState::new(), + shared: Shared::new(), ch: ch::State::new(), } } @@ -127,7 +127,7 @@ where let mut runner = Runner { ch: ch_runner, - ioctl_state: &state.ioctl_state, + shared: &state.shared, next_seq: 1, handshake, ready, @@ -136,12 +136,12 @@ where }; runner.init().await; - (device, Control::new(state_ch, &state.ioctl_state), runner) + (device, Control::new(state_ch, &state.shared), runner) } pub struct Runner<'a, SPI, IN, OUT> { ch: ch::Runner<'a, MTU>, - ioctl_state: &'a IoctlState, + shared: &'a Shared, next_seq: u16, @@ -172,7 +172,7 @@ where loop { self.handshake.wait_for_high().await.unwrap(); - let ioctl = self.ioctl_state.wait_pending(); + let ioctl = self.shared.ioctl_wait_pending(); let tx = self.ch.tx_buf(); let ev = async { self.ready.wait_for_high().await.unwrap() }; @@ -294,7 +294,7 @@ where if isEvent { self.handle_event(data); } else { - self.ioctl_state.ioctl_done(data); + self.shared.ioctl_done(data); } } _ => warn!("unknown iftype {}", if_type_and_num), From 764b43e82c7b61c21621c1fd9f5fd2f6a3dc419c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 19:05:20 +0200 Subject: [PATCH 1419/1575] esp-hosted: wait for esp firmware init. --- embassy-net-esp-hosted/src/control.rs | 3 +++ embassy-net-esp-hosted/src/ioctl.rs | 23 ++++++++++++++++++++ embassy-net-esp-hosted/src/lib.rs | 11 ++++++++++ examples/nrf52840/src/bin/wifi_esp_hosted.rs | 3 --- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index c98d0ebf4..a2a65bc31 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -29,6 +29,9 @@ impl<'a> Control<'a> { } pub async fn init(&mut self) { + debug!("wait for init event..."); + self.shared.init_wait().await; + debug!("set wifi mode"); self.set_wifi_mode(WifiMode::Sta as _).await; let mac_addr = self.get_mac_addr().await; diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 7cbe80b2a..607b7b627 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -23,6 +23,7 @@ pub struct Shared(RefCell); struct SharedInner { ioctl: IoctlState, + is_init: bool, control_waker: WakerRegistration, runner_waker: WakerRegistration, } @@ -31,6 +32,7 @@ impl Shared { pub fn new() -> Self { Self(RefCell::new(SharedInner { ioctl: IoctlState::Done { resp_len: 0 }, + is_init: false, control_waker: WakerRegistration::new(), runner_waker: WakerRegistration::new(), })) @@ -97,4 +99,25 @@ impl Shared { warn!("IOCTL Response but no pending Ioctl"); } } + + // // // // // // // // // // // // // // // // // // // // + + pub fn init_done(&self) { + let mut this = self.0.borrow_mut(); + this.is_init = true; + this.control_waker.wake(); + } + + pub async fn init_wait(&self) { + poll_fn(|cx| { + let mut this = self.0.borrow_mut(); + if this.is_init { + Poll::Ready(()) + } else { + this.control_waker.register(cx.waker()); + Poll::Pending + } + }) + .await + } } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 700a5221c..ff7bed2b7 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -11,6 +11,7 @@ use ioctl::Shared; use proto::CtrlMsg; use crate::ioctl::PendingIoctl; +use crate::proto::CtrlMsgPayload; mod proto; @@ -308,6 +309,16 @@ where }; debug!("event: {:?}", &event); + + let Some(payload) = &event.payload else { + warn!("event without payload?"); + return + }; + + match payload { + CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), + _ => {} + } } } diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 401dbd33c..b75756f76 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -69,9 +69,6 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - // TODO: wait for ESP_INIT event instead of hardcoding delay. - Timer::after(Duration::from_secs(3)).await; - control.init().await; control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; From 1ed909ea74bfff75bd91b35739a44c5128271571 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 19:08:09 +0200 Subject: [PATCH 1420/1575] esp-hosted: fix warnings. --- embassy-net-esp-hosted/src/control.rs | 14 ++------------ embassy-net-esp-hosted/src/ioctl.rs | 4 ++-- embassy-net-esp-hosted/src/lib.rs | 5 +++-- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 1 - 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index a2a65bc31..fce82ade7 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -16,6 +16,7 @@ pub struct Control<'a> { shared: &'a Shared, } +#[allow(unused)] enum WifiMode { None = 0, Sta = 1, @@ -34,6 +35,7 @@ impl<'a> Control<'a> { debug!("set wifi mode"); self.set_wifi_mode(WifiMode::Sta as _).await; + let mac_addr = self.get_mac_addr().await; debug!("mac addr: {:02x}", mac_addr); self.state_ch.set_ethernet_address(mac_addr); @@ -91,18 +93,6 @@ impl<'a> Control<'a> { res } - async fn get_wifi_mode(&mut self) -> u32 { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqGetWifiMode as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqGetWifiMode(proto::CtrlMsgReqGetMode {})), - }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespGetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; - assert_eq!(resp.resp, 0); - resp.mode - } - async fn set_wifi_mode(&mut self, mode: u32) { let req = proto::CtrlMsg { msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 607b7b627..e2a6815aa 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -1,6 +1,6 @@ -use core::cell::{Cell, RefCell}; +use core::cell::RefCell; use core::future::poll_fn; -use core::task::{Poll, Waker}; +use core::task::Poll; use embassy_sync::waitqueue::WakerRegistration; diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index ff7bed2b7..2a4601ce4 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -83,6 +83,7 @@ struct PayloadHeader { } impl_bytes!(PayloadHeader); +#[allow(unused)] #[repr(u8)] enum InterfaceType { Sta = 0, @@ -276,7 +277,7 @@ where return; } - let isEvent = match &payload[..12] { + let is_event = match &payload[..12] { b"\x01\x08\x00ctrlResp\x02" => false, b"\x01\x08\x00ctrlEvnt\x02" => true, _ => { @@ -292,7 +293,7 @@ where } let data = &payload[14..][..len]; - if isEvent { + if is_event { self.handle_event(data); } else { self.shared.ioctl_done(data); diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index b75756f76..cea45c5c8 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -10,7 +10,6 @@ use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; -use embassy_time::{Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use static_cell::make_static; From 0d02298ea628b9d2154fd05db7975f62b8d12edb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Jun 2023 15:30:26 +0200 Subject: [PATCH 1421/1575] esp-hosted: fix build on stable. --- examples/nrf52840/Cargo.toml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 7c9d48bad..8c4175966 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,8 +6,24 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", "static_cell/nightly", - "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lora-phy", "lorawan-device", "lorawan"] +nightly = [ + "embedded-hal-async", + "embassy-executor/nightly", + "embassy-nrf/nightly", + "embassy-net/nightly", + "embassy-net-esp-hosted", + "embassy-nrf/unstable-traits", + "embassy-time/nightly", + "embassy-time/unstable-traits", + "static_cell/nightly", + "embassy-usb", + "embedded-io/async", + "embassy-net", + "embassy-lora", + "lora-phy", + "lorawan-device", + "lorawan", +] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -22,7 +38,7 @@ embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["ti lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } -embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } +embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -36,4 +52,4 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } -embedded-hal-async = "0.2.0-alpha.1" +embedded-hal-async = { version = "0.2.0-alpha.1", optional = true } From 6e65282f185d94a8c449374e6d4ed4eefa5793a4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Jun 2023 21:10:58 +0200 Subject: [PATCH 1422/1575] esp-hosted: smaller delay after transfer, makes slightly better perf. --- embassy-net-esp-hosted/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 2a4601ce4..44dfbe89c 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -227,7 +227,12 @@ where } self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); - let delay_until = Instant::now() + Duration::from_millis(1); + + // The esp-hosted firmware deasserts the HANSHAKE pin a few us AFTER ending the SPI transfer + // If we check it again too fast, we'll see it's high from the previous transfer, and if we send it + // data it will get lost. + // Make sure we check it after 100us at minimum. + let delay_until = Instant::now() + Duration::from_micros(100); self.handle_rx(&mut rx_buf); Timer::at(delay_until).await; } From 8bbfa6827cd68f67a50a89b13947542e632d5411 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Jun 2023 21:11:53 +0200 Subject: [PATCH 1423/1575] esp-hosted: add perf hil test. --- ci.sh | 2 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 8 +- tests/nrf/Cargo.toml | 4 + tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 270 +++++++++++++++++++ tests/rp/src/bin/cyw43-perf.rs | 9 +- 5 files changed, 280 insertions(+), 13 deletions(-) create mode 100644 tests/nrf/src/bin/wifi_esp_hosted_perf.rs diff --git a/ci.sh b/ci.sh index 3fe1b1ce8..a03efb856 100755 --- a/ci.sh +++ b/ci.sh @@ -3,7 +3,7 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings -export DEFMT_LOG=trace,cyw43=info,cyw43_pio=info,smoltcp=info +export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info # needed by wifi examples export WIFI_NETWORK=x diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index cea45c5c8..4eb31b105 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -26,7 +26,7 @@ async fn wifi_task( 'static, ExclusiveDevice, Output<'static, peripherals::P0_31>>, Input<'static, AnyPin>, - Output<'static, peripherals::P1_03>, + Output<'static, peripherals::P1_05>, >, ) -> ! { runner.run().await @@ -48,11 +48,11 @@ async fn main(spawner: Spawner) { let mosi = p.P0_30; let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive); let handshake = Input::new(p.P1_01.degrade(), Pull::Up); - let ready = Input::new(p.P1_02.degrade(), Pull::None); - let reset = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let ready = Input::new(p.P1_04.degrade(), Pull::None); + let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); let mut config = spim::Config::default(); - config.frequency = spim::Frequency::M1; + config.frequency = spim::Frequency::M32; config.mode = spim::MODE_2; // !!! let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); let spi = ExclusiveDevice::new(spi, cs); diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 9735c87d9..4f9ecc47a 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -13,6 +13,10 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } +embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } +embedded-hal-async = { version = "0.2.0-alpha.1" } +static_cell = { version = "1.1", features = [ "nightly" ] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs new file mode 100644 index 000000000..277b985c5 --- /dev/null +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -0,0 +1,270 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../common.rs"] +mod common; + +use defmt::{error, info, unwrap}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Config, Ipv4Address, Stack, StackResources}; +use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::{self, Spim}; +use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::{with_timeout, Duration, Timer}; +use embedded_hal_async::spi::ExclusiveDevice; +use static_cell::make_static; +use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; + +teleprobe_meta::timeout!(120); + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn wifi_task( + runner: hosted::Runner< + 'static, + ExclusiveDevice, Output<'static, peripherals::P0_31>>, + Input<'static, AnyPin>, + Output<'static, peripherals::P1_05>, + >, +) -> ! { + runner.run().await +} + +type MyDriver = hosted::NetDriver<'static>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_nrf::init(Default::default()); + + let miso = p.P0_28; + let sck = p.P0_29; + let mosi = p.P0_30; + let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive); + let handshake = Input::new(p.P1_01.degrade(), Pull::Up); + let ready = Input::new(p.P1_04.degrade(), Pull::None); + let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M32; + config.mode = spim::MODE_2; // !!! + let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); + let spi = ExclusiveDevice::new(spi, cs); + + let (device, mut control, runner) = embassy_net_esp_hosted::new( + make_static!(embassy_net_esp_hosted::State::new()), + spi, + handshake, + ready, + reset, + ) + .await; + + unwrap!(spawner.spawn(wifi_task(runner))); + + control.init().await; + control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + Config::dhcpv4(Default::default()), + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + info!("Waiting for DHCP up..."); + while stack.config_v4().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); + assert!(up > TEST_EXPECTED_UPLOAD_KBPS); + assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +// Test-only wifi network, no internet access! +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + +const TEST_DURATION: usize = 10; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 150; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 150; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &'static Stack) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &'static Stack) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &'static Stack) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 7a94ea191..9fc537a4b 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -63,20 +63,13 @@ async fn main(spawner: Spawner) { .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; - let config = Config::dhcpv4(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::Config { - // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), - // dns_servers: Vec::new(), - // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), - //}); - // Generate random seed let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. // Init network stack let stack = &*make_static!(Stack::new( net_device, - config, + Config::dhcpv4(Default::default()), make_static!(StackResources::<2>::new()), seed )); From 23c51a18741c4c500c46955b5cd028ec0eb7d53a Mon Sep 17 00:00:00 2001 From: Dietrich Beck Date: Thu, 22 Jun 2023 23:02:16 +0200 Subject: [PATCH 1424/1575] disable pull-up and down resistors for rp adc blocking_read --- embassy-rp/src/adc.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index b96d5a4a8..699a0d61d 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -112,8 +112,14 @@ impl<'d> Adc<'d> { r.result().read().result().into() } - pub fn blocking_read, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { + pub fn blocking_read, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { let r = Self::regs(); + pin.pad_ctrl().modify(|w| { + w.set_ie(true); + let (pu, pd) = (false, false); + w.set_pue(pu); + w.set_pde(pd); + }); r.cs().modify(|w| { w.set_ainsel(PIN::channel()); w.set_start_once(true) @@ -166,7 +172,7 @@ impl_pin!(PIN_29, 3); impl OneShot, WORD, PIN> for Adc<'static> where WORD: From, - PIN: Channel, ID = u8>, + PIN: Channel, ID = u8> + Pin, { type Error = (); fn read(&mut self, pin: &mut PIN) -> nb::Result { From 558247d8f60dbdebf5f3df3a632d9e32fe0f0277 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Thu, 22 Jun 2023 22:51:38 +0100 Subject: [PATCH 1425/1575] update hci crate name --- embassy-stm32-wpan/Cargo.toml | 4 ++-- embassy-stm32-wpan/src/ble.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 3659d7135..2977084ff 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -24,12 +24,12 @@ heapless = "0.7.16" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -bluetooth-hci-async = { version = "*", git = "https://github.com/OueslatiGhaith/bluetooth-hci", features = ["version-5-0"], optional = true } +stm32wb-hci = { version = "*", git = "https://github.com/OueslatiGhaith/bluetooth-hci", features = ["version-5-0"], optional = true } [features] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] -ble = ["dep:bluetooth-hci-async"] +ble = ["dep:stm32wb-hci"] mac = [] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs index 04acf0aff..619cd66a0 100644 --- a/embassy-stm32-wpan/src/ble.rs +++ b/embassy-stm32-wpan/src/ble.rs @@ -63,7 +63,7 @@ impl Ble { } } -pub extern crate bluetooth_hci_async as hci; +pub extern crate stm32wb_hci as hci; impl hci::Controller for Ble { async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) { From 64ff1a6b75aa13797cf64bb97e597f333a2ae9b6 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Thu, 22 Jun 2023 22:53:07 +0100 Subject: [PATCH 1426/1575] update hci crate git path --- embassy-stm32-wpan/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 2977084ff..ee60a22e5 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -24,7 +24,7 @@ heapless = "0.7.16" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -stm32wb-hci = { version = "*", git = "https://github.com/OueslatiGhaith/bluetooth-hci", features = ["version-5-0"], optional = true } +stm32wb-hci = { version = "*", git = "https://github.com/OueslatiGhaith/stm32wb-hci", features = ["version-5-0"], optional = true } [features] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] From caf63b9e7336ed3ddb0dc997d431f15a26ae7693 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 22 Jun 2023 21:05:51 -0500 Subject: [PATCH 1427/1575] stm32/tests: update ble test --- embassy-stm32-wpan/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 5 +- examples/stm32wb/src/bin/tl_mbox_mac.rs | 2 +- tests/stm32/Cargo.toml | 3 + tests/stm32/src/bin/tl_mbox.rs | 217 +++++++++++++++++++++--- 5 files changed, 205 insertions(+), 24 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index ee60a22e5..6d78ca577 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -24,7 +24,7 @@ heapless = "0.7.16" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -stm32wb-hci = { version = "*", git = "https://github.com/OueslatiGhaith/stm32wb-hci", features = ["version-5-0"], optional = true } +stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true } [features] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 726cd10d4..fbb2d918b 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -37,4 +37,7 @@ required-features = ["mac"] [[bin]] name = "eddystone_beacon" -required-features = ["ble"] \ No newline at end of file +required-features = ["ble"] + +[patch.crates-io] +stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"} \ No newline at end of file diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 6c8653cf4..afd319a41 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -46,7 +46,7 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mbox = TlMbox::init(p.IPCC, Irqs, config); - let sys_event = mbox.sys_subsystem.tl_read().await; + let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index fe646927a..c2422f7bc 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -46,6 +46,9 @@ rand_chacha = { version = "0.3", default-features = false } chrono = { version = "^0.4", default-features = false, optional = true} +[patch.crates-io] +stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"} + # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. [[bin]] diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index f55c0292a..76c736a5b 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -6,43 +6,49 @@ #[path = "../common.rs"] mod common; -use core::mem; +use core::time::Duration; use common::*; use embassy_executor::Spawner; -use embassy_futures::poll_once; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::ble::hci::host::uart::UartHci; +use embassy_stm32_wpan::ble::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; +use embassy_stm32_wpan::ble::hci::types::AdvertisingType; +use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gap::{ + AdvertisingDataType, DiscoverableParameters, GapCommands, Role, +}; +use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gatt::GattCommands; +use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; +use embassy_stm32_wpan::ble::hci::BdAddr; +use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; use embassy_stm32_wpan::{mm, TlMbox}; -use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ IPCC_C1_RX => ReceiveInterruptHandler; IPCC_C1_TX => TransmitInterruptHandler; }); +const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; + #[embassy_executor::task] async fn run_mm_queue(memory_manager: mm::MemoryManager) { memory_manager.run_queue().await; } #[embassy_executor::main] -async fn main(spawner: Spawner) { +async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mut mbox = TlMbox::init(p.IPCC, Irqs, config); - spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + // spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); - let ready_event = mbox.sys_subsystem.read().await; - let _ = poll_once(mbox.sys_subsystem.read()); // clear rx not - - info!("sys event {:x} : {:x}", ready_event.stub().kind, ready_event.payload()); - - // test memory manager - mem::drop(ready_event); + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap(); let version_major = fw_info.version_major(); @@ -57,19 +63,188 @@ async fn main(spawner: Spawner) { version_major, version_minor, subversion, sram2a_size, sram2b_size ); - Timer::after(Duration::from_millis(50)).await; + mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; - let result = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; - info!("subsystem initialization: {}", result); + info!("resetting BLE..."); + mbox.ble_subsystem.reset().await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); - info!("starting ble..."); - mbox.ble_subsystem.tl_write(0x0c, &[]).await; + info!("config public address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); - info!("waiting for ble..."); - let ble_event = mbox.ble_subsystem.tl_read().await; + info!("config random address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::random_address(get_random_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); - info!("ble event {:x} : {:x}", ble_event.stub().kind, ble_event.payload()); + info!("config identity root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::identity_root(&get_irk()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("config encryption root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("config tx power level..."); + mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("GATT init..."); + mbox.ble_subsystem.init_gatt().await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("GAP init..."); + mbox.ble_subsystem + .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + // info!("set scan response..."); + // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap(); + // let response = mbox.ble_subsystem.read().await.unwrap(); + // info!("{}", response); + + info!("set discoverable..."); + mbox.ble_subsystem + .set_discoverable(&DiscoverableParameters { + advertising_type: AdvertisingType::NonConnectableUndirected, + advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), + address_type: OwnAddressType::Public, + filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, + local_name: None, + advertising_data: &[], + conn_interval: (None, None), + }) + .await + .unwrap(); + + let response = mbox.ble_subsystem.read().await; + info!("{}", response); + + // remove some advertisement to decrease the packet size + info!("delete tx power ad type..."); + mbox.ble_subsystem + .delete_ad_type(AdvertisingDataType::TxPowerLevel) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("delete conn interval ad type..."); + mbox.ble_subsystem + .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) + .await; + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("update advertising data..."); + mbox.ble_subsystem + .update_advertising_data(&eddystone_advertising_data()) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("update advertising data type..."); + mbox.ble_subsystem + .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); + + info!("update advertising data flags..."); + mbox.ble_subsystem + .update_advertising_data(&[ + 2, + AdvertisingDataType::Flags as u8, + (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support + ]) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await.unwrap(); + info!("{}", response); info!("Test OK"); cortex_m::asm::bkpt(); } + +fn get_bd_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = lhci_info.device_type_id; + bytes[4] = (lhci_info.st_company_id & 0xff) as u8; + bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8; + + BdAddr(bytes) +} + +fn get_random_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = 0; + bytes[4] = 0x6E; + bytes[5] = 0xED; + + BdAddr(bytes) +} + +const BLE_CFG_IRK: [u8; 16] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, +]; +const BLE_CFG_ERK: [u8; 16] = [ + 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, +]; + +fn get_irk() -> EncryptionKey { + EncryptionKey(BLE_CFG_IRK) +} + +fn get_erk() -> EncryptionKey { + EncryptionKey(BLE_CFG_ERK) +} + +fn eddystone_advertising_data() -> [u8; 24] { + const EDDYSTONE_URL: &[u8] = b"www.rust-lang.com"; + + let mut service_data = [0u8; 24]; + let url_len = EDDYSTONE_URL.len(); + + service_data[0] = 6 + url_len as u8; + service_data[1] = AdvertisingDataType::ServiceData as u8; + + // 16-bit eddystone uuid + service_data[2] = 0xaa; + service_data[3] = 0xFE; + + service_data[4] = 0x10; // URL frame type + service_data[5] = 22_i8 as u8; // calibrated TX power at 0m + service_data[6] = 0x03; // eddystone url prefix = https + + service_data[7..(7 + url_len)].copy_from_slice(EDDYSTONE_URL); + + service_data +} From ea04a0277bb19719188e904a86e28c34f9801c96 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:14:26 +0200 Subject: [PATCH 1428/1575] change dma complete transfer IR default to true --- embassy-stm32/src/dma/bdma.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 32b75bb68..5a87888b7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -35,7 +35,7 @@ impl Default for TransferOptions { Self { circular: false, half_transfer_ir: false, - complete_transfer_ir: false, + complete_transfer_ir: true, } } } From 915f79c974ace037e914397b42eb9d2448bd5ca3 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:14:40 +0200 Subject: [PATCH 1429/1575] allow independent use of ch1 and ch2 on dac --- embassy-stm32/src/dac/mod.rs | 152 +++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 51 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 42646d20d..5b39758bf 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -22,7 +22,7 @@ pub enum Channel { } impl Channel { - fn index(&self) -> usize { + const fn index(&self) -> usize { match self { Channel::Ch1 => 0, Channel::Ch2 => 1, @@ -109,72 +109,100 @@ pub enum ValueArray<'a> { } pub struct Dac<'d, T: Instance, Tx> { - channels: u8, + ch1: bool, + ch2: bool, txdma: PeripheralRef<'d, Tx>, _peri: PeripheralRef<'d, T>, } impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { - /// Create a new instance with one channel - pub fn new_1ch( + pub fn new_ch1( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd, ) -> Self { into_ref!(peri); - Self::new_inner(peri, 1, txdma) + Self::new_inner(peri, true, false, txdma) } - /// Create a new instance with two channels - pub fn new_2ch( + pub fn new_ch2( + peri: impl Peripheral

+ 'd, + txdma: impl Peripheral

+ 'd, + _ch2: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri); + Self::new_inner(peri, false, true, txdma) + } + + pub fn new_ch1_and_ch2( peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, _ch1: impl Peripheral

> + 'd, _ch2: impl Peripheral

> + 'd, ) -> Self { into_ref!(peri); - Self::new_inner(peri, 2, txdma) + Self::new_inner(peri, true, true, txdma) } /// Perform initialisation steps for the DAC - fn new_inner(peri: PeripheralRef<'d, T>, channels: u8, txdma: impl Peripheral

+ 'd) -> Self { + fn new_inner(peri: PeripheralRef<'d, T>, ch1: bool, ch2: bool, txdma: impl Peripheral

+ 'd) -> Self { into_ref!(txdma); T::enable(); T::reset(); - T::regs().mcr().modify(|reg| { - for ch in 0..channels { - reg.set_mode(ch as usize, 0); - reg.set_mode(ch as usize, 0); - } - }); - - T::regs().cr().modify(|reg| { - for ch in 0..channels { - reg.set_en(ch as usize, true); - reg.set_ten(ch as usize, true); - } - }); - - Self { - channels, + let mut dac = Self { + ch1, + ch2, txdma, _peri: peri, + }; + + // Configure each activated channel. All results can be `unwrap`ed since they + // will only error if the channel is not configured (i.e. ch1, ch2 are false) + if ch1 { + dac.set_channel_mode(Channel::Ch1, 0).unwrap(); + dac.enable_channel(Channel::Ch1).unwrap(); + dac.set_trigger_enable(Channel::Ch1, true).unwrap(); } + if ch2 { + dac.set_channel_mode(Channel::Ch2, 0).unwrap(); + dac.enable_channel(Channel::Ch2).unwrap(); + dac.set_trigger_enable(Channel::Ch2, true).unwrap(); + } + + dac } /// Check the channel is configured - fn check_channel_exists(&self, ch: Channel) -> Result<(), Error> { - if ch == Channel::Ch2 && self.channels < 2 { + fn check_channel_configured(&self, ch: Channel) -> Result<(), Error> { + if (ch == Channel::Ch1 && !self.ch1) || (ch == Channel::Ch2 && !self.ch2) { Err(Error::UnconfiguredChannel) } else { Ok(()) } } - /// Set the enable register of the given channel + /// Enable trigger of the given channel + fn set_trigger_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { + self.check_channel_configured(ch)?; + T::regs().cr().modify(|reg| { + reg.set_ten(ch.index(), on); + }); + Ok(()) + } + + /// Set mode register of the given channel + fn set_channel_mode(&mut self, ch: Channel, val: u8) -> Result<(), Error> { + self.check_channel_configured(ch)?; + T::regs().mcr().modify(|reg| { + reg.set_mode(ch.index(), val); + }); + Ok(()) + } + + /// Set enable register of the given channel fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { - self.check_channel_exists(ch)?; + self.check_channel_configured(ch)?; T::regs().cr().modify(|reg| { reg.set_en(ch.index(), on); }); @@ -193,7 +221,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// Select a new trigger for CH1 (disables the channel) pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { - self.check_channel_exists(Channel::Ch1)?; + self.check_channel_configured(Channel::Ch1)?; unwrap!(self.disable_channel(Channel::Ch1)); T::regs().cr().modify(|reg| { reg.set_tsel1(trigger.tsel()); @@ -203,7 +231,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// Select a new trigger for CH2 (disables the channel) pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { - self.check_channel_exists(Channel::Ch2)?; + self.check_channel_configured(Channel::Ch2)?; unwrap!(self.disable_channel(Channel::Ch2)); T::regs().cr().modify(|reg| { reg.set_tsel2(trigger.tsel()); @@ -213,7 +241,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// Perform a software trigger on `ch` pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { - self.check_channel_exists(ch)?; + self.check_channel_configured(ch)?; T::regs().swtrigr().write(|reg| { reg.set_swtrig(ch.index(), true); }); @@ -232,7 +260,7 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// /// The `value` is written to the corresponding "data holding register" pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { - self.check_channel_exists(ch)?; + self.check_channel_configured(ch)?; match value { Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), Value::Bit12Left(v) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), @@ -241,39 +269,61 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { Ok(()) } - /// Write `data` to the DAC via DMA. + /// Write `data` to the DAC CH1 via DMA. /// /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. /// This will configure a circular DMA transfer that periodically outputs the `data`. /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// - /// ## Current limitations - /// - Only CH1 Supported - /// - pub async fn write(&mut self, data_ch1: ValueArray<'_>, circular: bool) -> Result<(), Error> + /// **Important:** Channel 1 has to be configured for the DAC instance! + pub async fn write_ch1(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { - // TODO: Make this a parameter or get it from the struct or so... - const CHANNEL: usize = 0; + self.check_channel_configured(Channel::Ch1)?; + self.write_inner(data, circular, Channel::Ch1).await + } + + /// Write `data` to the DAC CH2 via DMA. + /// + /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. + /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// + /// **Important:** Channel 2 has to be configured for the DAC instance! + pub async fn write_ch2(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + self.check_channel_configured(Channel::Ch2)?; + self.write_inner(data, circular, Channel::Ch2).await + } + + /// Performs the dma write for the given channel. + /// TODO: Should self be &mut? + async fn write_inner(&self, data_ch1: ValueArray<'_>, circular: bool, channel: Channel) -> Result<(), Error> + where + Tx: Dma, + { + let channel = channel.index(); // Enable DAC and DMA T::regs().cr().modify(|w| { - w.set_en(CHANNEL, true); - w.set_dmaen(CHANNEL, true); + w.set_en(channel, true); + w.set_dmaen(channel, true); }); let tx_request = self.txdma.request(); - let channel = &self.txdma; + let dma_channel = &self.txdma; // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data_ch1 { ValueArray::Bit8(buf) => unsafe { Transfer::new_write( - channel, + dma_channel, tx_request, buf, - T::regs().dhr8r(CHANNEL).as_ptr() as *mut u8, + T::regs().dhr8r(channel).as_ptr() as *mut u8, TransferOptions { circular, half_transfer_ir: false, @@ -283,10 +333,10 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }, ValueArray::Bit12Left(buf) => unsafe { Transfer::new_write( - channel, + dma_channel, tx_request, buf, - T::regs().dhr12l(CHANNEL).as_ptr() as *mut u16, + T::regs().dhr12l(channel).as_ptr() as *mut u16, TransferOptions { circular, half_transfer_ir: false, @@ -296,10 +346,10 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { }, ValueArray::Bit12Right(buf) => unsafe { Transfer::new_write( - channel, + dma_channel, tx_request, buf, - T::regs().dhr12r(CHANNEL).as_ptr() as *mut u16, + T::regs().dhr12r(channel).as_ptr() as *mut u16, TransferOptions { circular, half_transfer_ir: false, @@ -315,9 +365,9 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { // TODO: Do we need to check any status registers here? T::regs().cr().modify(|w| { // Disable the DAC peripheral - w.set_en(CHANNEL, false); + w.set_en(channel, false); // Disable the DMA. TODO: Is this necessary? - w.set_dmaen(CHANNEL, false); + w.set_dmaen(channel, false); }); Ok(()) From 29f32ce00ec0f50ef5e3b29c7e50f7f5479b4967 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 23 Jun 2023 17:54:06 -0500 Subject: [PATCH 1430/1575] stm32/wpan: reorg subsystems --- embassy-stm32-wpan/src/evt.rs | 4 +-- embassy-stm32-wpan/src/lhci.rs | 8 +++--- embassy-stm32-wpan/src/lib.rs | 26 +++++++++----------- embassy-stm32-wpan/src/{ => sub}/ble.rs | 0 embassy-stm32-wpan/src/{ => sub}/mac.rs | 0 embassy-stm32-wpan/src/{ => sub}/mm.rs | 0 embassy-stm32-wpan/src/sub/mod.rs | 6 +++++ embassy-stm32-wpan/src/{ => sub}/sys.rs | 0 examples/stm32wb/src/bin/eddystone_beacon.rs | 14 +++++------ tests/stm32/src/bin/tl_mbox.rs | 17 +++++++------ 10 files changed, 41 insertions(+), 34 deletions(-) rename embassy-stm32-wpan/src/{ => sub}/ble.rs (100%) rename embassy-stm32-wpan/src/{ => sub}/mac.rs (100%) rename embassy-stm32-wpan/src/{ => sub}/mm.rs (100%) create mode 100644 embassy-stm32-wpan/src/sub/mod.rs rename embassy-stm32-wpan/src/{ => sub}/sys.rs (100%) diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index 25249a13a..22f089037 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -145,14 +145,14 @@ impl Drop for EvtBox { fn drop(&mut self) { #[cfg(feature = "ble")] unsafe { - use crate::mm; + use crate::sub::mm; mm::MemoryManager::drop_event_packet(self.ptr) }; #[cfg(feature = "mac")] unsafe { - use crate::mac; + use crate::sub::mac; mac::Mac::drop_event_packet(self.ptr) } diff --git a/embassy-stm32-wpan/src/lhci.rs b/embassy-stm32-wpan/src/lhci.rs index 62116a695..284103705 100644 --- a/embassy-stm32-wpan/src/lhci.rs +++ b/embassy-stm32-wpan/src/lhci.rs @@ -1,7 +1,9 @@ +use core::ptr; + use crate::cmd::CmdPacket; use crate::consts::{TlPacketType, TL_EVT_HEADER_SIZE}; use crate::evt::{CcEvt, EvtPacket, EvtSerial}; -use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, WirelessFwInfoTable}; +use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, WirelessFwInfoTable, TL_DEVICE_INFO_TABLE}; use crate::TL_REF_TABLE; const TL_BLEEVT_CC_OPCODE: u8 = 0x0e; @@ -38,7 +40,7 @@ impl Default for LhciC1DeviceInformationCcrp { safe_boot_info_table, rss_info_table, wireless_fw_info_table, - } = unsafe { &*(*TL_REF_TABLE.as_ptr()).device_info_table }.clone(); + } = unsafe { ptr::read_volatile(TL_DEVICE_INFO_TABLE.as_ptr()) }; let device_id = stm32_device_signature::device_id(); let uid96_0 = (device_id[3] as u32) << 24 @@ -105,7 +107,7 @@ impl LhciC1DeviceInformationCcrp { let self_ptr: *const LhciC1DeviceInformationCcrp = self; let self_buf = self_ptr.cast(); - core::ptr::copy(self_buf, evt_cc_payload_buf, self_size); + ptr::copy(self_buf, evt_cc_payload_buf, self_size); } } } diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index bf0f0466e..99c610583 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -11,26 +11,24 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; -use mm::MemoryManager; -use sys::Sys; +use sub::mm::MemoryManager; +use sub::sys::Sys; use tables::*; use unsafe_linked_list::LinkedListNode; -#[cfg(feature = "ble")] -pub mod ble; pub mod channels; pub mod cmd; pub mod consts; pub mod evt; pub mod lhci; -#[cfg(feature = "mac")] -pub mod mac; -pub mod mm; pub mod shci; -pub mod sys; +pub mod sub; pub mod tables; pub mod unsafe_linked_list; +#[cfg(feature = "ble")] +pub use crate::sub::ble::hci; + type PacketHeader = LinkedListNode; pub struct TlMbox<'d> { @@ -39,9 +37,9 @@ pub struct TlMbox<'d> { pub sys_subsystem: Sys, pub mm_subsystem: MemoryManager, #[cfg(feature = "ble")] - pub ble_subsystem: ble::Ble, + pub ble_subsystem: sub::ble::Ble, #[cfg(feature = "mac")] - pub mac_subsystem: mac::Mac, + pub mac_subsystem: sub::mac::Mac, } impl<'d> TlMbox<'d> { @@ -128,12 +126,12 @@ impl<'d> TlMbox<'d> { Self { _ipcc: ipcc, - sys_subsystem: sys::Sys::new(), + sys_subsystem: sub::sys::Sys::new(), #[cfg(feature = "ble")] - ble_subsystem: ble::Ble::new(), + ble_subsystem: sub::ble::Ble::new(), #[cfg(feature = "mac")] - mac_subsystem: mac::Mac::new(), - mm_subsystem: mm::MemoryManager::new(), + mac_subsystem: sub::mac::Mac::new(), + mm_subsystem: sub::mm::MemoryManager::new(), } } } diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs similarity index 100% rename from embassy-stm32-wpan/src/ble.rs rename to embassy-stm32-wpan/src/sub/ble.rs diff --git a/embassy-stm32-wpan/src/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs similarity index 100% rename from embassy-stm32-wpan/src/mac.rs rename to embassy-stm32-wpan/src/sub/mac.rs diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs similarity index 100% rename from embassy-stm32-wpan/src/mm.rs rename to embassy-stm32-wpan/src/sub/mm.rs diff --git a/embassy-stm32-wpan/src/sub/mod.rs b/embassy-stm32-wpan/src/sub/mod.rs new file mode 100644 index 000000000..bee3dbdf1 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mod.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "ble")] +pub mod ble; +#[cfg(feature = "mac")] +pub mod mac; +pub mod mm; +pub mod sys; diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs similarity index 100% rename from embassy-stm32-wpan/src/sys.rs rename to embassy-stm32-wpan/src/sub/sys.rs diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index fdd5be4a2..b99f8cb2e 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -8,15 +8,15 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::ble::hci::host::uart::UartHci; -use embassy_stm32_wpan::ble::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; -use embassy_stm32_wpan::ble::hci::types::AdvertisingType; -use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gap::{ +use embassy_stm32_wpan::hci::host::uart::UartHci; +use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; +use embassy_stm32_wpan::hci::types::AdvertisingType; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{ AdvertisingDataType, DiscoverableParameters, GapCommands, Role, }; -use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gatt::GattCommands; -use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; -use embassy_stm32_wpan::ble::hci::BdAddr; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::GattCommands; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; +use embassy_stm32_wpan::hci::BdAddr; use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 76c736a5b..8880554de 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -12,17 +12,18 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::ble::hci::host::uart::UartHci; -use embassy_stm32_wpan::ble::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; -use embassy_stm32_wpan::ble::hci::types::AdvertisingType; -use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gap::{ +use embassy_stm32_wpan::hci::host::uart::UartHci; +use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; +use embassy_stm32_wpan::hci::types::AdvertisingType; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{ AdvertisingDataType, DiscoverableParameters, GapCommands, Role, }; -use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::gatt::GattCommands; -use embassy_stm32_wpan::ble::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; -use embassy_stm32_wpan::ble::hci::BdAddr; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::GattCommands; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; +use embassy_stm32_wpan::hci::BdAddr; use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; -use embassy_stm32_wpan::{mm, TlMbox}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ From f23b34951a20f569997bbe028048f3943d7e4c56 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 23 Jun 2023 17:55:47 -0500 Subject: [PATCH 1431/1575] rustfmt --- embassy-stm32-wpan/src/lhci.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32-wpan/src/lhci.rs b/embassy-stm32-wpan/src/lhci.rs index 284103705..89f204f99 100644 --- a/embassy-stm32-wpan/src/lhci.rs +++ b/embassy-stm32-wpan/src/lhci.rs @@ -4,7 +4,6 @@ use crate::cmd::CmdPacket; use crate::consts::{TlPacketType, TL_EVT_HEADER_SIZE}; use crate::evt::{CcEvt, EvtPacket, EvtSerial}; use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, WirelessFwInfoTable, TL_DEVICE_INFO_TABLE}; -use crate::TL_REF_TABLE; const TL_BLEEVT_CC_OPCODE: u8 = 0x0e; const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62; From 91fdd76053c747c569b0eefe0715522465fe0194 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 23 Jun 2023 18:08:42 -0500 Subject: [PATCH 1432/1575] stm32/wpan: use align to align data --- embassy-stm32-wpan/Cargo.toml | 1 + embassy-stm32-wpan/src/tables.rs | 34 ++++++++++++-------------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 6d78ca577..4b830cab3 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -21,6 +21,7 @@ embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } defmt = { version = "0.3", optional = true } cortex-m = "0.7.6" heapless = "0.7.16" +aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 3f26282c6..1b5dcdf2e 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -1,5 +1,6 @@ use core::mem::MaybeUninit; +use aligned::{Aligned, A4}; use bit_field::BitField; use crate::cmd::{AclDataPacket, CmdPacket}; @@ -164,9 +165,6 @@ pub struct Mac802_15_4Table { pub evt_queue: *const u8, } -#[repr(C, align(4))] -pub struct AlignedData([u8; L]); - /// Reference table. Contains pointers to all other tables. #[derive(Debug, Copy, Clone)] #[repr(C)] @@ -222,10 +220,9 @@ pub static mut FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit #[link_section = "MB_MEM1"] pub static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); -const CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; - #[link_section = "MB_MEM2"] -pub static mut CS_BUFFER: MaybeUninit> = MaybeUninit::uninit(); +pub static mut CS_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); #[link_section = "MB_MEM2"] pub static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); @@ -238,35 +235,30 @@ pub static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::unin #[link_section = "MB_MEM2"] pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); -#[cfg(feature = "mac")] -const MAC_802_15_4_NOTIF_RSP_EVT_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255; - #[cfg(feature = "mac")] #[link_section = "MB_MEM2"] -pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< + Aligned, +> = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] -pub static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); +pub static mut EVT_POOL: MaybeUninit> = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] pub static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); -const SYS_SPARE_EVT_BUF_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255; - #[link_section = "MB_MEM2"] -pub static mut SYS_SPARE_EVT_BUF: MaybeUninit> = MaybeUninit::uninit(); +pub static mut SYS_SPARE_EVT_BUF: MaybeUninit> = + MaybeUninit::uninit(); #[link_section = "MB_MEM1"] pub static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); -const BLE_SPARE_EVT_BUF_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255; - #[link_section = "MB_MEM2"] -pub static mut BLE_SPARE_EVT_BUF: MaybeUninit> = MaybeUninit::uninit(); - -const HCI_ACL_DATA_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + 5 + 251; +pub static mut BLE_SPARE_EVT_BUF: MaybeUninit> = + MaybeUninit::uninit(); #[link_section = "MB_MEM2"] // fuck these "magic" numbers from ST ---v---v -pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; HCI_ACL_DATA_BUFFER_SIZE]> = MaybeUninit::uninit(); +pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit> = + MaybeUninit::uninit(); From d43417e97c4de487b4ebf018830825e034c5394e Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 23 Jun 2023 19:59:48 -0500 Subject: [PATCH 1433/1575] stm32/wpan: implement mm pattern --- embassy-stm32-wpan/src/consts.rs | 4 +++ embassy-stm32-wpan/src/evt.rs | 33 ++++++++------------- embassy-stm32-wpan/src/sub/ble.rs | 25 +++++++++++++--- embassy-stm32-wpan/src/sub/mac.rs | 48 ++++++++++++++++--------------- embassy-stm32-wpan/src/sub/mm.rs | 23 ++++++++------- embassy-stm32-wpan/src/sub/sys.rs | 3 +- tests/stm32/src/bin/tl_mbox.rs | 4 +-- 7 files changed, 78 insertions(+), 62 deletions(-) diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs index 9a107306c..f234151d7 100644 --- a/embassy-stm32-wpan/src/consts.rs +++ b/embassy-stm32-wpan/src/consts.rs @@ -87,3 +87,7 @@ pub const fn divc(x: usize, y: usize) -> usize { pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; #[allow(dead_code)] pub const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; + +pub const TL_BLEEVT_CC_OPCODE: u8 = 0x0E; +pub const TL_BLEEVT_CS_OPCODE: u8 = 0x0F; +pub const TL_BLEEVT_VS_OPCODE: u8 = 0xFF; diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index 22f089037..c6528413d 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -1,3 +1,4 @@ +use core::marker::PhantomData; use core::{ptr, slice}; use super::PacketHeader; @@ -93,17 +94,22 @@ impl EvtPacket { } } +pub trait MemoryManager { + unsafe fn drop_event_packet(evt: *mut EvtPacket); +} + /// smart pointer to the [`EvtPacket`] that will dispose of [`EvtPacket`] buffer automatically /// on [`Drop`] #[derive(Debug)] -pub struct EvtBox { +pub struct EvtBox { ptr: *mut EvtPacket, + mm: PhantomData, } -unsafe impl Send for EvtBox {} -impl EvtBox { +unsafe impl Send for EvtBox {} +impl EvtBox { pub(super) fn new(ptr: *mut EvtPacket) -> Self { - Self { ptr } + Self { ptr, mm: PhantomData } } /// Returns information about the event @@ -126,9 +132,6 @@ impl EvtBox { } } - /// writes an underlying [`EvtPacket`] into the provided buffer. - /// Returns the number of bytes that were written. - /// Returns an error if event kind is unknown or if provided buffer size is not enough. pub fn serial<'a>(&'a self) -> &'a [u8] { unsafe { let evt_serial: *const EvtSerial = &(*self.ptr).evt_serial; @@ -141,20 +144,8 @@ impl EvtBox { } } -impl Drop for EvtBox { +impl Drop for EvtBox { fn drop(&mut self) { - #[cfg(feature = "ble")] - unsafe { - use crate::sub::mm; - - mm::MemoryManager::drop_event_packet(self.ptr) - }; - - #[cfg(feature = "mac")] - unsafe { - use crate::sub::mac; - - mac::Mac::drop_event_packet(self.ptr) - } + unsafe { T::drop_event_packet(self.ptr) }; } } diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs index 619cd66a0..cd32692e1 100644 --- a/embassy-stm32-wpan/src/sub/ble.rs +++ b/embassy-stm32-wpan/src/sub/ble.rs @@ -1,14 +1,16 @@ use core::marker::PhantomData; +use core::ptr; use embassy_stm32::ipcc::Ipcc; use hci::Opcode; -use crate::channels; use crate::cmd::CmdPacket; -use crate::consts::TlPacketType; -use crate::evt::EvtBox; +use crate::consts::{TlPacketType, TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE}; +use crate::evt::{EvtBox, EvtPacket, EvtStub}; +use crate::sub::mm; use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; use crate::unsafe_linked_list::LinkedListNode; +use crate::{channels, evt}; pub struct Ble { phantom: PhantomData, @@ -30,7 +32,7 @@ impl Ble { Self { phantom: PhantomData } } /// `HW_IPCC_BLE_EvtNot` - pub async fn tl_read(&self) -> EvtBox { + pub async fn tl_read(&self) -> EvtBox { Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { Some(EvtBox::new(node_ptr.cast())) @@ -63,6 +65,21 @@ impl Ble { } } +impl evt::MemoryManager for Ble { + /// SAFETY: passing a pointer to something other than a managed event packet is UB + unsafe fn drop_event_packet(evt: *mut EvtPacket) { + let stub = unsafe { + let p_evt_stub = &(*evt).evt_serial as *const _ as *const EvtStub; + + ptr::read_volatile(p_evt_stub) + }; + + if !(stub.evt_code == TL_BLEEVT_CS_OPCODE || stub.evt_code == TL_BLEEVT_CC_OPCODE) { + mm::MemoryManager::drop_event_packet(evt); + } + } +} + pub extern crate stm32wb_hci as hci; impl hci::Controller for Ble { diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index d2be1b85c..fd8af8609 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -8,13 +8,13 @@ use embassy_futures::poll_once; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; -use crate::channels; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; use crate::tables::{ Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE, }; +use crate::{channels, evt}; static MAC_WAKER: AtomicWaker = AtomicWaker::new(); static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); @@ -36,31 +36,10 @@ impl Mac { Self { phantom: PhantomData } } - /// SAFETY: passing a pointer to something other than a managed event packet is UB - pub(crate) unsafe fn drop_event_packet(_: *mut EvtPacket) { - // Write the ack - CmdPacket::write_into( - MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _, - TlPacketType::OtAck, - 0, - &[], - ); - - // Clear the rx flag - let _ = poll_once(Ipcc::receive::( - channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, - || None, - )); - - // Allow a new read call - MAC_EVT_OUT.store(false, Ordering::SeqCst); - MAC_WAKER.wake(); - } - /// `HW_IPCC_MAC_802_15_4_EvtNot` /// /// This function will stall if the previous `EvtBox` has not been dropped - pub async fn read(&self) -> EvtBox { + pub async fn read(&self) -> EvtBox { // Wait for the last event box to be dropped poll_fn(|cx| { MAC_WAKER.register(cx.waker()); @@ -109,3 +88,26 @@ impl Mac { .await; } } + +impl evt::MemoryManager for Mac { + /// SAFETY: passing a pointer to something other than a managed event packet is UB + unsafe fn drop_event_packet(_: *mut EvtPacket) { + // Write the ack + CmdPacket::write_into( + MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _, + TlPacketType::OtAck, + 0, + &[], + ); + + // Clear the rx flag + let _ = poll_once(Ipcc::receive::( + channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, + || None, + )); + + // Allow a new read call + MAC_EVT_OUT.store(false, Ordering::SeqCst); + MAC_WAKER.wake(); + } +} diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index 047fddcd4..1f2ecac2e 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs @@ -8,13 +8,13 @@ use cortex_m::interrupt; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; -use crate::channels; use crate::consts::POOL_SIZE; use crate::evt::EvtPacket; use crate::tables::{ MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, }; use crate::unsafe_linked_list::LinkedListNode; +use crate::{channels, evt}; static MM_WAKER: AtomicWaker = AtomicWaker::new(); static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); @@ -43,16 +43,6 @@ impl MemoryManager { Self { phantom: PhantomData } } - #[allow(dead_code)] - /// SAFETY: passing a pointer to something other than a managed event packet is UB - pub(crate) unsafe fn drop_event_packet(evt: *mut EvtPacket) { - interrupt::free(|_| unsafe { - LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _); - }); - - MM_WAKER.wake(); - } - pub async fn run_queue(&self) { loop { poll_fn(|cx| unsafe { @@ -77,3 +67,14 @@ impl MemoryManager { } } } + +impl evt::MemoryManager for MemoryManager { + /// SAFETY: passing a pointer to something other than a managed event packet is UB + unsafe fn drop_event_packet(evt: *mut EvtPacket) { + interrupt::free(|_| unsafe { + LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _); + }); + + MM_WAKER.wake(); + } +} diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index 2b699b725..af652860d 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -6,6 +6,7 @@ use crate::consts::TlPacketType; use crate::evt::{CcEvt, EvtBox, EvtPacket}; #[allow(unused_imports)] use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; +use crate::sub::mm; use crate::tables::{SysTable, WirelessFwInfoTable}; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; @@ -73,7 +74,7 @@ impl Sys { } /// `HW_IPCC_SYS_EvtNot` - pub async fn read(&self) -> EvtBox { + pub async fn read(&self) -> EvtBox { Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { Some(EvtBox::new(node_ptr.cast())) diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs index 8880554de..af3832709 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/tl_mbox.rs @@ -39,14 +39,14 @@ async fn run_mm_queue(memory_manager: mm::MemoryManager) { } #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); let config = Config::default(); let mut mbox = TlMbox::init(p.IPCC, Irqs, config); - // spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); From 49333ce6adf28ff6c3eb1a632c0d4860379ef3ef Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 23 Jun 2023 20:09:13 -0500 Subject: [PATCH 1434/1575] stm32/wpan: move linker file into pkg --- embassy-stm32-wpan/build.rs | 13 ++++++++++++- {embassy-stm32 => embassy-stm32-wpan}/tl_mbox.x.in | 0 embassy-stm32/build.rs | 10 ---------- 3 files changed, 12 insertions(+), 11 deletions(-) rename {embassy-stm32 => embassy-stm32-wpan}/tl_mbox.x.in (100%) diff --git a/embassy-stm32-wpan/build.rs b/embassy-stm32-wpan/build.rs index 4edf73d59..94aac070d 100644 --- a/embassy-stm32-wpan/build.rs +++ b/embassy-stm32-wpan/build.rs @@ -1,4 +1,5 @@ -use std::env; +use std::path::PathBuf; +use std::{env, fs}; fn main() { match env::vars() @@ -10,6 +11,16 @@ fn main() { Err(GetOneError::None) => panic!("No stm32xx Cargo feature enabled"), Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), } + + let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + // ======== + // stm32wb tl_mbox link sections + + let out_file = out_dir.join("tl_mbox.x").to_string_lossy().to_string(); + fs::write(out_file, fs::read_to_string("tl_mbox.x.in").unwrap()).unwrap(); + println!("cargo:rustc-link-search={}", out_dir.display()); + println!("cargo:rerun-if-changed=tl_mbox.x.in"); } enum GetOneError { diff --git a/embassy-stm32/tl_mbox.x.in b/embassy-stm32-wpan/tl_mbox.x.in similarity index 100% rename from embassy-stm32/tl_mbox.x.in rename to embassy-stm32-wpan/tl_mbox.x.in diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f71074bcf..40103d322 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -911,16 +911,6 @@ fn main() { println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]); } - // ======== - // stm32wb tl_mbox link sections - - if chip_name.starts_with("stm32wb") { - let out_file = out_dir.join("tl_mbox.x").to_string_lossy().to_string(); - fs::write(out_file, fs::read_to_string("tl_mbox.x.in").unwrap()).unwrap(); - println!("cargo:rustc-link-search={}", out_dir.display()); - println!("cargo:rerun-if-changed=tl_mbox.x.in"); - } - // ======= // Features for targeting groups of chips From 388d3e273d3d003e6a058b4bad9e2517dd33d626 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 24 Jun 2023 13:10:59 +0200 Subject: [PATCH 1435/1575] first attempt at fixing the 2nd channel problem --- embassy-stm32/src/dac/mod.rs | 386 +++++++++++++++++++++-------------- 1 file changed, 234 insertions(+), 152 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 5b39758bf..e87292b86 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -1,5 +1,7 @@ #![macro_use] +use core::marker::PhantomData; + use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::{Transfer, TransferOptions}; @@ -108,166 +110,108 @@ pub enum ValueArray<'a> { Bit12Right(&'a [u16]), } -pub struct Dac<'d, T: Instance, Tx> { - ch1: bool, - ch2: bool, - txdma: PeripheralRef<'d, Tx>, - _peri: PeripheralRef<'d, T>, -} - -impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { - pub fn new_ch1( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - _ch1: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(peri); - Self::new_inner(peri, true, false, txdma) - } - - pub fn new_ch2( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - _ch2: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(peri); - Self::new_inner(peri, false, true, txdma) - } - - pub fn new_ch1_and_ch2( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - _ch1: impl Peripheral

> + 'd, - _ch2: impl Peripheral

> + 'd, - ) -> Self { - into_ref!(peri); - Self::new_inner(peri, true, true, txdma) - } - - /// Perform initialisation steps for the DAC - fn new_inner(peri: PeripheralRef<'d, T>, ch1: bool, ch2: bool, txdma: impl Peripheral

+ 'd) -> Self { - into_ref!(txdma); - T::enable(); - T::reset(); - - let mut dac = Self { - ch1, - ch2, - txdma, - _peri: peri, - }; - - // Configure each activated channel. All results can be `unwrap`ed since they - // will only error if the channel is not configured (i.e. ch1, ch2 are false) - if ch1 { - dac.set_channel_mode(Channel::Ch1, 0).unwrap(); - dac.enable_channel(Channel::Ch1).unwrap(); - dac.set_trigger_enable(Channel::Ch1, true).unwrap(); - } - if ch2 { - dac.set_channel_mode(Channel::Ch2, 0).unwrap(); - dac.enable_channel(Channel::Ch2).unwrap(); - dac.set_trigger_enable(Channel::Ch2, true).unwrap(); - } - - dac - } - - /// Check the channel is configured - fn check_channel_configured(&self, ch: Channel) -> Result<(), Error> { - if (ch == Channel::Ch1 && !self.ch1) || (ch == Channel::Ch2 && !self.ch2) { - Err(Error::UnconfiguredChannel) - } else { - Ok(()) - } - } +pub trait DacChannel { + const CHANNEL: Channel; /// Enable trigger of the given channel - fn set_trigger_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> { T::regs().cr().modify(|reg| { - reg.set_ten(ch.index(), on); + reg.set_ten(Self::CHANNEL.index(), on); }); Ok(()) } /// Set mode register of the given channel - fn set_channel_mode(&mut self, ch: Channel, val: u8) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { T::regs().mcr().modify(|reg| { - reg.set_mode(ch.index(), val); + reg.set_mode(Self::CHANNEL.index(), val); }); Ok(()) } /// Set enable register of the given channel - fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> { T::regs().cr().modify(|reg| { - reg.set_en(ch.index(), on); + reg.set_en(Self::CHANNEL.index(), on); }); Ok(()) } /// Enable the DAC channel `ch` - pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> { - self.set_channel_enable(ch, true) + fn enable_channel(&mut self) -> Result<(), Error> { + self.set_channel_enable(true) } /// Disable the DAC channel `ch` - pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> { - self.set_channel_enable(ch, false) - } - - /// Select a new trigger for CH1 (disables the channel) - pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { - self.check_channel_configured(Channel::Ch1)?; - unwrap!(self.disable_channel(Channel::Ch1)); - T::regs().cr().modify(|reg| { - reg.set_tsel1(trigger.tsel()); - }); - Ok(()) - } - - /// Select a new trigger for CH2 (disables the channel) - pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { - self.check_channel_configured(Channel::Ch2)?; - unwrap!(self.disable_channel(Channel::Ch2)); - T::regs().cr().modify(|reg| { - reg.set_tsel2(trigger.tsel()); - }); - Ok(()) + fn disable_channel(&mut self) -> Result<(), Error> { + self.set_channel_enable(false) } /// Perform a software trigger on `ch` - pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn trigger(&mut self) -> Result<(), Error> { T::regs().swtrigr().write(|reg| { - reg.set_swtrig(ch.index(), true); + reg.set_swtrig(Self::CHANNEL.index(), true); }); Ok(()) } - /// Perform a software trigger on all channels - pub fn trigger_all(&mut self) { - T::regs().swtrigr().write(|reg| { - reg.set_swtrig(Channel::Ch1.index(), true); - reg.set_swtrig(Channel::Ch2.index(), true); - }); - } - /// Set a value to be output by the DAC on trigger. /// /// The `value` is written to the corresponding "data holding register" - pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> { - self.check_channel_configured(ch)?; + fn set(&mut self, value: Value) -> Result<(), Error> { match value { - Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12Left(v) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)), - Value::Bit12Right(v) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)), + Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), + Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), } Ok(()) } +} + +pub struct Dac<'d, T: Instance, Tx> { + ch1: DacCh1<'d, T, Tx>, + ch2: DacCh2<'d, T, Tx>, +} + +pub struct DacCh1<'d, T: Instance, Tx> { + _peri: PeripheralRef<'d, T>, + dma: PeripheralRef<'d, Tx>, +} + +pub struct DacCh2<'d, T: Instance, Tx> { + phantom: PhantomData<&'d mut T>, + dma: PeripheralRef<'d, Tx>, +} + +impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { + /// Perform initialisation steps for the DAC + pub fn new( + peri: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, + _pin: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, dma); + T::enable(); + T::reset(); + + let mut dac = Self { _peri: peri, dma }; + + // Configure each activated channel. All results can be `unwrap`ed since they + // will only error if the channel is not configured (i.e. ch1, ch2 are false) + dac.set_channel_mode(0).unwrap(); + dac.enable_channel().unwrap(); + dac.set_trigger_enable(true).unwrap(); + + dac + } + /// Select a new trigger for CH1 (disables the channel) + pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { + unwrap!(self.disable_channel()); + T::regs().cr().modify(|reg| { + reg.set_tsel1(trigger.tsel()); + }); + Ok(()) + } /// Write `data` to the DAC CH1 via DMA. /// @@ -276,36 +220,12 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 1 has to be configured for the DAC instance! - pub async fn write_ch1(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { - self.check_channel_configured(Channel::Ch1)?; - self.write_inner(data, circular, Channel::Ch1).await - } - - /// Write `data` to the DAC CH2 via DMA. - /// - /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. - /// This will configure a circular DMA transfer that periodically outputs the `data`. - /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. - /// - /// **Important:** Channel 2 has to be configured for the DAC instance! - pub async fn write_ch2(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - self.check_channel_configured(Channel::Ch2)?; - self.write_inner(data, circular, Channel::Ch2).await - } - - /// Performs the dma write for the given channel. - /// TODO: Should self be &mut? - async fn write_inner(&self, data_ch1: ValueArray<'_>, circular: bool, channel: Channel) -> Result<(), Error> - where - Tx: Dma, - { - let channel = channel.index(); + let channel = Channel::Ch1.index(); + debug!("Writing to channel {}", channel); // Enable DAC and DMA T::regs().cr().modify(|w| { @@ -313,11 +233,11 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { w.set_dmaen(channel, true); }); - let tx_request = self.txdma.request(); - let dma_channel = &self.txdma; + let tx_request = self.dma.request(); + let dma_channel = &self.dma; // Initiate the correct type of DMA transfer depending on what data is passed - let tx_f = match data_ch1 { + let tx_f = match data { ValueArray::Bit8(buf) => unsafe { Transfer::new_write( dma_channel, @@ -374,6 +294,168 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { } } +impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { + /// Perform initialisation steps for the DAC + pub fn new_ch2( + peri: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, + _pin: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, dma); + T::enable(); + T::reset(); + + let mut dac = Self { + phantom: PhantomData, + dma, + }; + + // Configure each activated channel. All results can be `unwrap`ed since they + // will only error if the channel is not configured (i.e. ch1, ch2 are false) + dac.set_channel_mode(0).unwrap(); + dac.enable_channel().unwrap(); + dac.set_trigger_enable(true).unwrap(); + + dac + } + + /// Select a new trigger for CH1 (disables the channel) + pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { + unwrap!(self.disable_channel()); + T::regs().cr().modify(|reg| { + reg.set_tsel2(trigger.tsel()); + }); + Ok(()) + } + + /// Write `data` to the DAC CH1 via DMA. + /// + /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. + /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// + /// **Important:** Channel 1 has to be configured for the DAC instance! + async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + let channel = Channel::Ch2.index(); + debug!("Writing to channel {}", channel); + + // Enable DAC and DMA + T::regs().cr().modify(|w| { + w.set_en(channel, true); + w.set_dmaen(channel, true); + }); + + let tx_request = self.dma.request(); + let dma_channel = &self.dma; + + // Initiate the correct type of DMA transfer depending on what data is passed + let tx_f = match data { + ValueArray::Bit8(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr8r(channel).as_ptr() as *mut u8, + TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + }, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12l(channel).as_ptr() as *mut u16, + TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + }, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12r(channel).as_ptr() as *mut u16, + TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + }, + ) + }, + }; + + tx_f.await; + + // finish dma + // TODO: Do we need to check any status registers here? + T::regs().cr().modify(|w| { + // Disable the DAC peripheral + w.set_en(channel, false); + // Disable the DMA. TODO: Is this necessary? + w.set_dmaen(channel, false); + }); + + Ok(()) + } +} + +impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { + pub fn new( + peri: impl Peripheral

+ 'd, + dma_ch1: impl Peripheral

+ 'd, + dma_ch2: impl Peripheral

+ 'd, + _pin_ch1: impl Peripheral

> + 'd, + _pin_ch2: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, dma_ch1, dma_ch2); + T::enable(); + T::reset(); + + let mut dac_ch1 = DacCh1 { + _peri: peri, + dma: dma_ch1, + }; + + let mut dac_ch2 = DacCh2 { + phantom: PhantomData, + dma: dma_ch2, + }; + + // Configure each activated channel. All results can be `unwrap`ed since they + // will only error if the channel is not configured (i.e. ch1, ch2 are false) + dac_ch1.set_channel_mode(0).unwrap(); + dac_ch1.enable_channel().unwrap(); + dac_ch1.set_trigger_enable(true).unwrap(); + + dac_ch1.set_channel_mode(0).unwrap(); + dac_ch1.enable_channel().unwrap(); + dac_ch1.set_trigger_enable(true).unwrap(); + + Self { + ch1: dac_ch1, + ch2: dac_ch2, + } + } +} + +impl<'d, T: Instance, Tx> DacChannel for DacCh1<'d, T, Tx> { + const CHANNEL: Channel = Channel::Ch1; +} + +impl<'d, T: Instance, Tx> DacChannel for DacCh2<'d, T, Tx> { + const CHANNEL: Channel = Channel::Ch2; +} + pub(crate) mod sealed { pub trait Instance { fn regs() -> &'static crate::pac::dac::Dac; From df944edeef738590f481d35ee9e2a1afb09601fa Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 25 Jun 2023 10:53:35 +0200 Subject: [PATCH 1436/1575] fix minor issues with splitting channels etc --- embassy-stm32/src/dac/mod.rs | 46 ++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index e87292b86..3dcd6b771 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -168,9 +168,9 @@ pub trait DacChannel { } } -pub struct Dac<'d, T: Instance, Tx> { - ch1: DacCh1<'d, T, Tx>, - ch2: DacCh2<'d, T, Tx>, +pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { + ch1: DacCh1<'d, T, TxCh1>, + ch2: DacCh2<'d, T, TxCh2>, } pub struct DacCh1<'d, T: Instance, Tx> { @@ -220,7 +220,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 1 has to be configured for the DAC instance! - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { @@ -297,11 +297,11 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// Perform initialisation steps for the DAC pub fn new_ch2( - peri: impl Peripheral

+ 'd, + _peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, _pin: impl Peripheral

> + 'd, ) -> Self { - into_ref!(peri, dma); + into_ref!(_peri, dma); T::enable(); T::reset(); @@ -335,7 +335,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 1 has to be configured for the DAC instance! - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: Dma, { @@ -409,11 +409,11 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { } } -impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { +impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { pub fn new( peri: impl Peripheral

+ 'd, - dma_ch1: impl Peripheral

+ 'd, - dma_ch2: impl Peripheral

+ 'd, + dma_ch1: impl Peripheral

+ 'd, + dma_ch2: impl Peripheral

+ 'd, _pin_ch1: impl Peripheral

> + 'd, _pin_ch2: impl Peripheral

> + 'd, ) -> Self { @@ -437,15 +437,35 @@ impl<'d, T: Instance, Tx> Dac<'d, T, Tx> { dac_ch1.enable_channel().unwrap(); dac_ch1.set_trigger_enable(true).unwrap(); - dac_ch1.set_channel_mode(0).unwrap(); - dac_ch1.enable_channel().unwrap(); - dac_ch1.set_trigger_enable(true).unwrap(); + dac_ch2.set_channel_mode(0).unwrap(); + dac_ch2.enable_channel().unwrap(); + dac_ch2.set_trigger_enable(true).unwrap(); Self { ch1: dac_ch1, ch2: dac_ch2, } } + + pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { + (self.ch1, self.ch2) + } + + pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { + &mut self.ch1 + } + + pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { + &mut self.ch2 + } + + pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { + &self.ch1 + } + + pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { + &self.ch2 + } } impl<'d, T: Instance, Tx> DacChannel for DacCh1<'d, T, Tx> { From 8cafaa1f3c9b75e8dba30a7f37f60d9fee6e65e2 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sun, 25 Jun 2023 11:54:25 +0200 Subject: [PATCH 1437/1575] add docs, cleanup --- embassy-stm32/src/dac/mod.rs | 322 +++++++++++++++++------------------ 1 file changed, 155 insertions(+), 167 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 3dcd6b771..d95674ff0 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -1,5 +1,6 @@ #![macro_use] +//! Provide access to the STM32 digital-to-analog converter (DAC). use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -11,6 +12,7 @@ use crate::{peripherals, Peripheral}; #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Curstom Errors pub enum Error { UnconfiguredChannel, InvalidValue, @@ -18,6 +20,7 @@ pub enum Error { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// DAC Channels pub enum Channel { Ch1, Ch2, @@ -34,6 +37,7 @@ impl Channel { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Trigger sources for CH1 pub enum Ch1Trigger { Tim6, Tim3, @@ -60,6 +64,7 @@ impl Ch1Trigger { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Trigger sources for CH2 pub enum Ch2Trigger { Tim6, Tim8, @@ -109,7 +114,7 @@ pub enum ValueArray<'a> { // 12 bit values stored in a u16, right-aligned Bit12Right(&'a [u16]), } - +/// Provide common functions for DAC channels pub trait DacChannel { const CHANNEL: Channel; @@ -157,7 +162,7 @@ pub trait DacChannel { /// Set a value to be output by the DAC on trigger. /// - /// The `value` is written to the corresponding "data holding register" + /// The `value` is written to the corresponding "data holding register". fn set(&mut self, value: Value) -> Result<(), Error> { match value { Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)), @@ -166,25 +171,51 @@ pub trait DacChannel { } Ok(()) } + + /// Write `data` to the DAC channel via DMA. + /// + /// `circular` sets the DMA to circular mode. + async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: Dma; } +/// Hold two DAC channels +/// +/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously. +/// +/// # Example for obtaining both DAC channels +/// +/// ```no_run +/// // DMA channels and pins may need to be changed for your controller +/// let (dac_ch1, dac_ch2) = +/// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); +/// ``` pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { ch1: DacCh1<'d, T, TxCh1>, ch2: DacCh2<'d, T, TxCh2>, } +/// DAC CH1 +/// +/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. pub struct DacCh1<'d, T: Instance, Tx> { + /// To consume T _peri: PeripheralRef<'d, T>, dma: PeripheralRef<'d, Tx>, } +/// DAC CH2 +/// +/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously. pub struct DacCh2<'d, T: Instance, Tx> { + /// Instead of PeripheralRef to consume T phantom: PhantomData<&'d mut T>, dma: PeripheralRef<'d, Tx>, } impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { - /// Perform initialisation steps for the DAC + /// Obtain DAC CH1 pub fn new( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, @@ -204,7 +235,8 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { dac } - /// Select a new trigger for CH1 (disables the channel) + + /// Select a new trigger for this channel pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { unwrap!(self.disable_channel()); T::regs().cr().modify(|reg| { @@ -212,91 +244,11 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { }); Ok(()) } - - /// Write `data` to the DAC CH1 via DMA. - /// - /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. - /// This will configure a circular DMA transfer that periodically outputs the `data`. - /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. - /// - /// **Important:** Channel 1 has to be configured for the DAC instance! - pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - let channel = Channel::Ch1.index(); - debug!("Writing to channel {}", channel); - - // Enable DAC and DMA - T::regs().cr().modify(|w| { - w.set_en(channel, true); - w.set_dmaen(channel, true); - }); - - let tx_request = self.dma.request(); - let dma_channel = &self.dma; - - // Initiate the correct type of DMA transfer depending on what data is passed - let tx_f = match data { - ValueArray::Bit8(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr8r(channel).as_ptr() as *mut u8, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12l(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12r(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - }; - - tx_f.await; - - // finish dma - // TODO: Do we need to check any status registers here? - T::regs().cr().modify(|w| { - // Disable the DAC peripheral - w.set_en(channel, false); - // Disable the DMA. TODO: Is this necessary? - w.set_dmaen(channel, false); - }); - - Ok(()) - } } impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { - /// Perform initialisation steps for the DAC - pub fn new_ch2( + /// Obtain DAC CH2 + pub fn new( _peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, _pin: impl Peripheral

> + 'd, @@ -319,7 +271,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { dac } - /// Select a new trigger for CH1 (disables the channel) + /// Select a new trigger for this channel pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> { unwrap!(self.disable_channel()); T::regs().cr().modify(|reg| { @@ -327,89 +279,12 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { }); Ok(()) } - - /// Write `data` to the DAC CH1 via DMA. - /// - /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. - /// This will configure a circular DMA transfer that periodically outputs the `data`. - /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. - /// - /// **Important:** Channel 1 has to be configured for the DAC instance! - pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - let channel = Channel::Ch2.index(); - debug!("Writing to channel {}", channel); - - // Enable DAC and DMA - T::regs().cr().modify(|w| { - w.set_en(channel, true); - w.set_dmaen(channel, true); - }); - - let tx_request = self.dma.request(); - let dma_channel = &self.dma; - - // Initiate the correct type of DMA transfer depending on what data is passed - let tx_f = match data { - ValueArray::Bit8(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr8r(channel).as_ptr() as *mut u8, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12l(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12r(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - }; - - tx_f.await; - - // finish dma - // TODO: Do we need to check any status registers here? - T::regs().cr().modify(|w| { - // Disable the DAC peripheral - w.set_en(channel, false); - // Disable the DMA. TODO: Is this necessary? - w.set_dmaen(channel, false); - }); - - Ok(()) - } } impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { + /// Create a new DAC instance with both channels. + /// + /// This is used to obtain two independent channels via `split()` for use e.g. with DMA. pub fn new( peri: impl Peripheral

+ 'd, dma_ch1: impl Peripheral

+ 'd, @@ -447,22 +322,27 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { } } + /// Split the DAC into CH1 and CH2 for independent use. pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) { (self.ch1, self.ch2) } + /// Get mutable reference to CH1 pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> { &mut self.ch1 } + /// Get mutable reference to CH2 pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> { &mut self.ch2 } + /// Get reference to CH1 pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> { &self.ch1 } + /// Get reference to CH2 pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> { &self.ch2 } @@ -470,10 +350,117 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { impl<'d, T: Instance, Tx> DacChannel for DacCh1<'d, T, Tx> { const CHANNEL: Channel = Channel::Ch1; + + /// Write `data` to the DAC CH1 via DMA. + /// + /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. + /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// + /// **Important:** Channel 1 has to be configured for the DAC instance! + async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + write_inner(Self::CHANNEL, &self.dma, data, circular).await + } } impl<'d, T: Instance, Tx> DacChannel for DacCh2<'d, T, Tx> { const CHANNEL: Channel = Channel::Ch2; + + /// Write `data` to the DAC CH2 via DMA. + /// + /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. + /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// + /// **Important:** Channel 2 has to be configured for the DAC instance! + async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: Dma, + { + write_inner(Self::CHANNEL, &self.dma, data, circular).await + } +} + +/// Shared utility function to perform the actual DMA config and write. +async fn write_inner( + ch: Channel, + dma: &PeripheralRef<'_, Tx>, + data: ValueArray<'_>, + circular: bool, +) -> Result<(), Error> +where + Tx: Dma, +{ + let channel = ch.index(); + debug!("Writing to channel {}", channel); + + // Enable DAC and DMA + T::regs().cr().modify(|w| { + w.set_en(channel, true); + w.set_dmaen(channel, true); + }); + + let tx_request = dma.request(); + let dma_channel = dma; + + // Initiate the correct type of DMA transfer depending on what data is passed + let tx_f = match data { + ValueArray::Bit8(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr8r(channel).as_ptr() as *mut u8, + TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + }, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12l(channel).as_ptr() as *mut u16, + TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + }, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12r(channel).as_ptr() as *mut u16, + TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + }, + ) + }, + }; + + tx_f.await; + + // finish dma + // TODO: Do we need to check any status registers here? + T::regs().cr().modify(|w| { + // Disable the DAC peripheral + w.set_en(channel, false); + // Disable the DMA. TODO: Is this necessary? + w.set_dmaen(channel, false); + }); + + Ok(()) } pub(crate) mod sealed { @@ -485,6 +472,7 @@ pub(crate) mod sealed { pub trait Instance: sealed::Instance + RccPeripheral + 'static {} dma_trait!(Dma, Instance); +/// Marks a pin that can be used with the DAC pub trait DacPin: crate::gpio::Pin + 'static {} foreach_peripheral!( From 018622f607a17037903ef7c5592dda762002f89b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 25 Jun 2023 11:38:48 -0500 Subject: [PATCH 1438/1575] stm32/wpan: update example --- examples/stm32wb/src/bin/tl_mbox_mac.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index afd319a41..f67be4682 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -49,7 +49,9 @@ async fn main(_spawner: Spawner) { let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); - mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + // // info!("starting ble..."); // mbox.ble_subsystem.t_write(0x0c, &[]).await; From aa0ab06645446bcb4b99a9407dc9c6c58030d8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 25 Jun 2023 22:24:48 +0200 Subject: [PATCH 1439/1575] Update darling --- embassy-macros/Cargo.toml | 4 ++-- embassy-macros/src/lib.rs | 37 ++++++++++++++++++++++--------- embassy-macros/src/macros/main.rs | 9 ++++---- embassy-macros/src/macros/task.rs | 5 +++-- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 781026b99..3b8fe8b44 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml @@ -12,9 +12,9 @@ categories = [ ] [dependencies] -syn = { version = "1.0.76", features = ["full", "extra-traits"] } +syn = { version = "2.0.15", features = ["full", "extra-traits"] } quote = "1.0.9" -darling = "0.13.0" +darling = "0.20.1" proc-macro2 = "1.0.29" [lib] diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index ba4f13b77..c9d58746a 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -1,11 +1,28 @@ #![doc = include_str!("../README.md")] extern crate proc_macro; +use darling::ast::NestedMeta; use proc_macro::TokenStream; mod macros; mod util; use macros::*; +use syn::parse::{Parse, ParseBuffer}; +use syn::punctuated::Punctuated; +use syn::Token; + +struct Args { + meta: Vec, +} + +impl Parse for Args { + fn parse(input: &ParseBuffer) -> syn::Result { + let meta = Punctuated::::parse_terminated(input)?; + Ok(Args { + meta: meta.into_iter().collect(), + }) + } +} /// 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. @@ -39,10 +56,10 @@ use macros::*; /// ``` #[proc_macro_attribute] pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let args = syn::parse_macro_input!(args as Args); let f = syn::parse_macro_input!(item as syn::ItemFn); - task::run(args, f).unwrap_or_else(|x| x).into() + task::run(&args.meta, 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. @@ -65,9 +82,9 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let args = syn::parse_macro_input!(args as Args); let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(args, f, main::cortex_m()).unwrap_or_else(|x| x).into() + main::run(&args.meta, 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. @@ -100,9 +117,9 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let args = syn::parse_macro_input!(args as Args); let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(args.clone(), f, main::riscv(args)) + main::run(&args.meta, f, main::riscv(&args.meta)) .unwrap_or_else(|x| x) .into() } @@ -127,9 +144,9 @@ pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let args = syn::parse_macro_input!(args as Args); let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(args, f, main::std()).unwrap_or_else(|x| x).into() + main::run(&args.meta, 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. @@ -152,7 +169,7 @@ pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as syn::AttributeArgs); + let args = syn::parse_macro_input!(args as Args); let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(args, f, main::wasm()).unwrap_or_else(|x| x).into() + main::run(&args.meta, f, main::wasm()).unwrap_or_else(|x| x).into() } diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs index 5c099f68a..7c4d55163 100644 --- a/embassy-macros/src/macros/main.rs +++ b/embassy-macros/src/macros/main.rs @@ -1,3 +1,4 @@ +use darling::export::NestedMeta; use darling::FromMeta; use proc_macro2::TokenStream; use quote::quote; @@ -11,8 +12,8 @@ struct Args { entry: Option, } -pub fn riscv(args: syn::AttributeArgs) -> TokenStream { - let maybe_entry = match Args::from_list(&args) { +pub fn riscv(args: &[NestedMeta]) -> TokenStream { + let maybe_entry = match Args::from_list(args) { Ok(args) => args.entry, Err(e) => return e.write_errors(), }; @@ -77,9 +78,9 @@ pub fn std() -> TokenStream { } } -pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Result { +pub fn run(args: &[NestedMeta], f: syn::ItemFn, main: TokenStream) -> Result { #[allow(unused_variables)] - let args = Args::from_list(&args).map_err(|e| e.write_errors())?; + let args = Args::from_list(args).map_err(|e| e.write_errors())?; let fargs = f.sig.inputs.clone(); diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 9f30cf43e..8c4bf7265 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs @@ -1,3 +1,4 @@ +use darling::export::NestedMeta; use darling::FromMeta; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -11,8 +12,8 @@ struct Args { pool_size: Option, } -pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result { - let args = Args::from_list(&args).map_err(|e| e.write_errors())?; +pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result { + let args = Args::from_list(args).map_err(|e| e.write_errors())?; let pool_size: usize = args.pool_size.unwrap_or(1); From 2809e926cf8a3a1556c674a7b17c8db4b926f243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 25 Jun 2023 22:32:24 +0200 Subject: [PATCH 1440/1575] Allow arbitrary expressions as pool_size --- embassy-macros/src/macros/task.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 8c4bf7265..1d30434e9 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs @@ -1,21 +1,24 @@ use darling::export::NestedMeta; use darling::FromMeta; -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; -use syn::{parse_quote, ItemFn, ReturnType, Type}; +use syn::{parse_quote, Expr, ExprLit, ItemFn, Lit, LitInt, ReturnType, Type}; use crate::util::ctxt::Ctxt; #[derive(Debug, FromMeta)] struct Args { #[darling(default)] - pool_size: Option, + pool_size: Option, } pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result { let args = Args::from_list(args).map_err(|e| e.write_errors())?; - let pool_size: usize = args.pool_size.unwrap_or(1); + let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit { + attrs: vec![], + lit: Lit::Int(LitInt::new("1", Span::call_site())), + })); let ctxt = Ctxt::new(); @@ -46,10 +49,6 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result Result ::embassy_executor::SpawnToken { type Fut = impl ::core::future::Future + 'static; - static POOL: ::embassy_executor::raw::TaskPool = ::embassy_executor::raw::TaskPool::new(); + const POOL_SIZE: usize = #pool_size; + static POOL: ::embassy_executor::raw::TaskPool = ::embassy_executor::raw::TaskPool::new(); unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } } }; From 12872ce49b3945c1a28ae362f78bcfb334a9eeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 25 Jun 2023 23:03:14 +0200 Subject: [PATCH 1441/1575] Modify an example --- examples/nrf52840/src/bin/self_spawn.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/nrf52840/src/bin/self_spawn.rs b/examples/nrf52840/src/bin/self_spawn.rs index 196255a52..31ea6c81e 100644 --- a/examples/nrf52840/src/bin/self_spawn.rs +++ b/examples/nrf52840/src/bin/self_spawn.rs @@ -7,7 +7,11 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; -#[embassy_executor::task(pool_size = 2)] +mod config { + pub const MY_TASK_POOL_SIZE: usize = 2; +} + +#[embassy_executor::task(pool_size = config::MY_TASK_POOL_SIZE)] async fn my_task(spawner: Spawner, n: u32) { Timer::after(Duration::from_secs(1)).await; info!("Spawning self! {}", n); From e7bc84dda8cc28835d1b7d3574a94b6142e29864 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Mon, 26 Jun 2023 09:42:25 +0200 Subject: [PATCH 1442/1575] fix issues when DAC2 present, add additional options to DMA (NOT YET WORKING with STM32H7A3ZI) --- embassy-stm32/build.rs | 4 +- embassy-stm32/src/dac/mod.rs | 267 ++++++++++++++++++++--------------- embassy-stm32/src/dma/dma.rs | 26 +++- 3 files changed, 178 insertions(+), 119 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f7a25743c..7fa4fae45 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -699,8 +699,8 @@ fn main() { // SDMMCv1 uses the same channel for both directions, so just implement for RX (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), - (("dac", "CH1"), quote!(crate::dac::Dma)), - (("dac", "CH2"), quote!(crate::dac::Dma)), + (("dac", "CH1"), quote!(crate::dac::DmaCh1)), + (("dac", "CH2"), quote!(crate::dac::DmaCh2)), ] .into(); diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index d95674ff0..6ead00e15 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -171,13 +171,6 @@ pub trait DacChannel { } Ok(()) } - - /// Write `data` to the DAC channel via DMA. - /// - /// `circular` sets the DMA to circular mode. - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma; } /// Hold two DAC channels @@ -244,6 +237,81 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { }); Ok(()) } + + /// Write `data` to the DAC CH1 via DMA. + /// + /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. + /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// + /// **Important:** Channel 1 has to be configured for the DAC instance! + pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: DmaCh1, + { + let channel = Channel::Ch1.index(); + debug!("Writing to channel {}", channel); + + // Enable DAC and DMA + T::regs().cr().modify(|w| { + w.set_en(channel, true); + w.set_dmaen(channel, true); + }); + + let tx_request = self.dma.request(); + let dma_channel = &self.dma; + + let tx_options = TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + ..Default::default() + }; + + // Initiate the correct type of DMA transfer depending on what data is passed + let tx_f = match data { + ValueArray::Bit8(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr8r(channel).as_ptr() as *mut u8, + tx_options, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12l(channel).as_ptr() as *mut u16, + tx_options, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12r(channel).as_ptr() as *mut u16, + tx_options, + ) + }, + }; + + tx_f.await; + + // finish dma + // TODO: Do we need to check any status registers here? + T::regs().cr().modify(|w| { + // Disable the DAC peripheral + w.set_en(channel, false); + // Disable the DMA. TODO: Is this necessary? + w.set_dmaen(channel, false); + }); + + Ok(()) + } } impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { @@ -279,6 +347,81 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { }); Ok(()) } + + /// Write `data` to the DAC CH2 via DMA. + /// + /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. + /// This will configure a circular DMA transfer that periodically outputs the `data`. + /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. + /// + /// **Important:** Channel 2 has to be configured for the DAC instance! + pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> + where + Tx: DmaCh2, + { + let channel = Channel::Ch2.index(); + debug!("Writing to channel {}", channel); + + // Enable DAC and DMA + T::regs().cr().modify(|w| { + w.set_en(channel, true); + w.set_dmaen(channel, true); + }); + + let tx_request = self.dma.request(); + let dma_channel = &self.dma; + + let tx_options = TransferOptions { + circular, + half_transfer_ir: false, + complete_transfer_ir: !circular, + ..Default::default() + }; + + // Initiate the correct type of DMA transfer depending on what data is passed + let tx_f = match data { + ValueArray::Bit8(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr8r(channel).as_ptr() as *mut u8, + tx_options, + ) + }, + ValueArray::Bit12Left(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12l(channel).as_ptr() as *mut u16, + tx_options, + ) + }, + ValueArray::Bit12Right(buf) => unsafe { + Transfer::new_write( + dma_channel, + tx_request, + buf, + T::regs().dhr12r(channel).as_ptr() as *mut u16, + tx_options, + ) + }, + }; + + tx_f.await; + + // finish dma + // TODO: Do we need to check any status registers here? + T::regs().cr().modify(|w| { + // Disable the DAC peripheral + w.set_en(channel, false); + // Disable the DMA. TODO: Is this necessary? + w.set_dmaen(channel, false); + }); + + Ok(()) + } } impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { @@ -350,117 +493,10 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { impl<'d, T: Instance, Tx> DacChannel for DacCh1<'d, T, Tx> { const CHANNEL: Channel = Channel::Ch1; - - /// Write `data` to the DAC CH1 via DMA. - /// - /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. - /// This will configure a circular DMA transfer that periodically outputs the `data`. - /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. - /// - /// **Important:** Channel 1 has to be configured for the DAC instance! - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - write_inner(Self::CHANNEL, &self.dma, data, circular).await - } } impl<'d, T: Instance, Tx> DacChannel for DacCh2<'d, T, Tx> { const CHANNEL: Channel = Channel::Ch2; - - /// Write `data` to the DAC CH2 via DMA. - /// - /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set. - /// This will configure a circular DMA transfer that periodically outputs the `data`. - /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. - /// - /// **Important:** Channel 2 has to be configured for the DAC instance! - async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> - where - Tx: Dma, - { - write_inner(Self::CHANNEL, &self.dma, data, circular).await - } -} - -/// Shared utility function to perform the actual DMA config and write. -async fn write_inner( - ch: Channel, - dma: &PeripheralRef<'_, Tx>, - data: ValueArray<'_>, - circular: bool, -) -> Result<(), Error> -where - Tx: Dma, -{ - let channel = ch.index(); - debug!("Writing to channel {}", channel); - - // Enable DAC and DMA - T::regs().cr().modify(|w| { - w.set_en(channel, true); - w.set_dmaen(channel, true); - }); - - let tx_request = dma.request(); - let dma_channel = dma; - - // Initiate the correct type of DMA transfer depending on what data is passed - let tx_f = match data { - ValueArray::Bit8(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr8r(channel).as_ptr() as *mut u8, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12l(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( - dma_channel, - tx_request, - buf, - T::regs().dhr12r(channel).as_ptr() as *mut u16, - TransferOptions { - circular, - half_transfer_ir: false, - complete_transfer_ir: !circular, - }, - ) - }, - }; - - tx_f.await; - - // finish dma - // TODO: Do we need to check any status registers here? - T::regs().cr().modify(|w| { - // Disable the DAC peripheral - w.set_en(channel, false); - // Disable the DMA. TODO: Is this necessary? - w.set_dmaen(channel, false); - }); - - Ok(()) } pub(crate) mod sealed { @@ -470,7 +506,8 @@ pub(crate) mod sealed { } pub trait Instance: sealed::Instance + RccPeripheral + 'static {} -dma_trait!(Dma, Instance); +dma_trait!(DmaCh1, Instance); +dma_trait!(DmaCh2, Instance); /// Marks a pin that can be used with the DAC pub trait DacPin: crate::gpio::Pin + 'static {} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 8abe541d3..a5f828948 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -29,6 +29,12 @@ pub struct TransferOptions { pub flow_ctrl: FlowControl, /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. pub fifo_threshold: Option, + /// Enable circular DMA + pub circular: bool, + /// Enable half transfer interrupt + pub half_transfer_ir: bool, + /// Enable transfer complete interrupt + pub complete_transfer_ir: bool, } impl Default for TransferOptions { @@ -38,6 +44,9 @@ impl Default for TransferOptions { mburst: Burst::Single, flow_ctrl: FlowControl::Dma, fifo_threshold: None, + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, } } } @@ -366,13 +375,20 @@ impl<'a, C: Channel> Transfer<'a, C> { }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_tcie(true); + w.set_tcie(options.complete_transfer_ir); + w.set_htie(options.half_transfer_ir); #[cfg(dma_v1)] w.set_trbuff(true); #[cfg(dma_v2)] w.set_chsel(_request); + if options.circular { + w.set_circ(vals::Circ::ENABLED); + debug!("Setting circular mode"); + } else { + w.set_circ(vals::Circ::DISABLED); + } w.set_pburst(options.pburst.into()); w.set_mburst(options.mburst.into()); w.set_pfctrl(options.flow_ctrl.into()); @@ -404,8 +420,14 @@ impl<'a, C: Channel> Transfer<'a, C> { } pub fn is_running(&mut self) -> bool { + //let ch = self.channel.regs().st(self.channel.num()); + //ch.cr().read().en() + let ch = self.channel.regs().st(self.channel.num()); - ch.cr().read().en() + let en = ch.cr().read().en(); + let circular = ch.cr().read().circ() == vals::Circ::ENABLED; + let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; + en && (circular || !tcif) } /// Gets the total remaining transfers for the channel From 64cba950e55dd5cdd7d6ef13c2dbb03825bc6d01 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Jun 2023 01:59:25 +0200 Subject: [PATCH 1443/1575] Update smoltcp. --- embassy-net/Cargo.toml | 4 ++-- embassy-net/src/device.rs | 3 ++- embassy-net/src/lib.rs | 18 +++++++++++++----- embassy-net/src/udp.rs | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 4ac572577..63947c261 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -38,10 +38,10 @@ igmp = ["smoltcp/proto-igmp"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -smoltcp = { version = "0.9.0", default-features = false, features = [ +smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev = "803840b5ccac01cc0f108993958f637835f0adbe", default-features = false, features = [ "socket", "async", -]} +] } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 583cdc87f..4513c86d3 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -51,8 +51,9 @@ where Medium::Ethernet => phy::Medium::Ethernet, #[cfg(feature = "medium-ip")] Medium::Ip => phy::Medium::Ip, + #[allow(unreachable_patterns)] _ => panic!( - "Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.", + "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", caps.medium ), }; diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 7e8f765f9..3e83da7aa 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -235,12 +235,19 @@ impl Stack { #[cfg(feature = "medium-ethernet")] let medium = device.capabilities().medium; - let mut iface_cfg = smoltcp::iface::Config::new(); + let hardware_addr = match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())), + #[cfg(feature = "medium-ip")] + Medium::Ip => HardwareAddress::Ip, + #[allow(unreachable_patterns)] + _ => panic!( + "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", + medium + ), + }; + let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr); iface_cfg.random_seed = random_seed; - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { - iface_cfg.hardware_addr = Some(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address()))); - } let iface = Interface::new( iface_cfg, @@ -248,6 +255,7 @@ impl Stack { inner: &mut device, cx: None, }, + instant_to_smoltcp(Instant::now()), ); let sockets = SocketSet::new(&mut resources.sockets[..]); diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index c9843cfe8..36f8d06f2 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -104,7 +104,7 @@ impl<'a> UdpSocket<'a> { pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { poll_fn(move |cx| { self.with_mut(|s, _| match s.recv_slice(buf) { - Ok(x) => Poll::Ready(Ok(x)), + Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))), // No data ready Err(udp::RecvError::Exhausted) => { s.register_recv_waker(cx.waker()); From 715bf20c4159f2b11f4edf0cd3b1b999ef19b56b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Jun 2023 20:13:55 +0200 Subject: [PATCH 1444/1575] Update smoltcp to 0.10 --- embassy-net/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 63947c261..cef8247eb 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -38,7 +38,7 @@ igmp = ["smoltcp/proto-igmp"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev = "803840b5ccac01cc0f108993958f637835f0adbe", default-features = false, features = [ +smoltcp = { version = "0.10.0", default-features = false, features = [ "socket", "async", ] } From 28fb492c402c74add5c650b9d65687816585c923 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 00:42:24 +0200 Subject: [PATCH 1445/1575] stm32/otg: flush fifos on reconfigure and on ep disable. --- embassy-stm32/src/usb_otg/usb.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 8af5c7bd5..532157e6c 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -540,6 +540,19 @@ impl<'d, T: Instance> Bus<'d, T> { fifo_top <= T::FIFO_DEPTH_WORDS, "FIFO allocations exceeded maximum capacity" ); + + // Flush fifos + r.grstctl().write(|w| { + w.set_rxfflsh(true); + w.set_txfflsh(true); + w.set_txfnum(0x10); + }); + loop { + let x = r.grstctl().read(); + if !x.rxfflsh() && !x.txfflsh() { + break; + } + } } fn configure_endpoints(&mut self) { @@ -744,7 +757,19 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { r.doepctl(ep_addr.index()).modify(|w| { w.set_usbaep(enabled); - }) + }); + + // Flush tx fifo + r.grstctl().write(|w| { + w.set_txfflsh(true); + w.set_txfnum(ep_addr.index() as _); + }); + loop { + let x = r.grstctl().read(); + if !x.txfflsh() { + break; + } + } }); // Wake `Endpoint::wait_enabled()` From a575e40a3503f4bf500b7ee3cadcce44727c7c6f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 02:12:06 +0200 Subject: [PATCH 1446/1575] stm32/otg: clear NAK bit on endpoint enable. --- embassy-stm32/src/usb_otg/usb.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 532157e6c..1a0d44fd2 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -780,13 +780,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { // cancel transfer if active if !enabled && r.diepctl(ep_addr.index()).read().epena() { r.diepctl(ep_addr.index()).modify(|w| { - w.set_snak(true); + w.set_snak(true); // set NAK w.set_epdis(true); }) } r.diepctl(ep_addr.index()).modify(|w| { w.set_usbaep(enabled); + w.set_cnak(enabled); // clear NAK that might've been set by SNAK above. }) }); From 80407aa930279a7d23bd1295c7703d0d9064aa58 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 02:12:33 +0200 Subject: [PATCH 1447/1575] stm32/otg: set tx fifo num in IN endpoints on configure. --- embassy-stm32/src/usb_otg/usb.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 1a0d44fd2..b2f7eb852 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -124,7 +124,7 @@ impl interrupt::typelevel::Handler for InterruptHandl } state.ep_in_wakers[ep_num].wake(); - trace!("in ep={} irq val={:b}", ep_num, ep_ints.0); + trace!("in ep={} irq val={:08x}", ep_num, ep_ints.0); } ep_mask >>= 1; @@ -144,7 +144,7 @@ impl interrupt::typelevel::Handler for InterruptHandl // // clear all // r.doepint(ep_num).write_value(ep_ints); // state.ep_out_wakers[ep_num].wake(); - // trace!("out ep={} irq val={=u32:b}", ep_num, ep_ints.0); + // trace!("out ep={} irq val={:08x}", ep_num, ep_ints.0); // } // ep_mask >>= 1; @@ -571,6 +571,8 @@ impl<'d, T: Instance> Bus<'d, T> { w.set_mpsiz(ep.max_packet_size); w.set_eptyp(to_eptyp(ep.ep_type)); w.set_sd0pid_sevnfrm(true); + w.set_txfnum(index as _); + w.set_snak(true); } }); }); From 5e6e18b310ef3c19fd4cdc42fa74dd8ed455e444 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 03:56:09 +0200 Subject: [PATCH 1448/1575] stm32/usb: add TODO: implement VBUS detection. --- embassy-stm32/src/usb/usb.rs | 95 ++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 2367127e8..01b158b17 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -480,56 +480,57 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { poll_fn(move |cx| { BUS_WAKER.register(cx.waker()); - if self.inited { - let regs = T::regs(); - - if IRQ_RESUME.load(Ordering::Acquire) { - IRQ_RESUME.store(false, Ordering::Relaxed); - return Poll::Ready(Event::Resume); - } - - if IRQ_RESET.load(Ordering::Acquire) { - IRQ_RESET.store(false, Ordering::Relaxed); - - trace!("RESET"); - regs.daddr().write(|w| { - w.set_ef(true); - w.set_add(0); - }); - - regs.epr(0).write(|w| { - w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat::NAK); - w.set_stat_tx(Stat::NAK); - }); - - for i in 1..EP_COUNT { - regs.epr(i).write(|w| { - w.set_ea(i as _); - w.set_ep_type(self.ep_types[i - 1]); - }) - } - - for w in &EP_IN_WAKERS { - w.wake() - } - for w in &EP_OUT_WAKERS { - w.wake() - } - - return Poll::Ready(Event::Reset); - } - - if IRQ_SUSPEND.load(Ordering::Acquire) { - IRQ_SUSPEND.store(false, Ordering::Relaxed); - return Poll::Ready(Event::Suspend); - } - - Poll::Pending - } else { + // TODO: implement VBUS detection. + if !self.inited { self.inited = true; return Poll::Ready(Event::PowerDetected); } + + let regs = T::regs(); + + if IRQ_RESUME.load(Ordering::Acquire) { + IRQ_RESUME.store(false, Ordering::Relaxed); + return Poll::Ready(Event::Resume); + } + + if IRQ_RESET.load(Ordering::Acquire) { + IRQ_RESET.store(false, Ordering::Relaxed); + + trace!("RESET"); + regs.daddr().write(|w| { + w.set_ef(true); + w.set_add(0); + }); + + regs.epr(0).write(|w| { + w.set_ep_type(EpType::CONTROL); + w.set_stat_rx(Stat::NAK); + w.set_stat_tx(Stat::NAK); + }); + + for i in 1..EP_COUNT { + regs.epr(i).write(|w| { + w.set_ea(i as _); + w.set_ep_type(self.ep_types[i - 1]); + }) + } + + for w in &EP_IN_WAKERS { + w.wake() + } + for w in &EP_OUT_WAKERS { + w.wake() + } + + return Poll::Ready(Event::Reset); + } + + if IRQ_SUSPEND.load(Ordering::Acquire) { + IRQ_SUSPEND.store(false, Ordering::Relaxed); + return Poll::Ready(Event::Suspend); + } + + Poll::Pending }) .await } From a2d1e7f02ca8d94c755ced56f1db11646ac2aa72 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 03:56:25 +0200 Subject: [PATCH 1449/1575] rp/usb: add TODO: implement VBUS detection. --- embassy-rp/src/usb.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 1900ab416..b3f3bd927 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -353,6 +353,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { poll_fn(move |cx| { BUS_WAKER.register(cx.waker()); + // TODO: implement VBUS detection. if !self.inited { self.inited = true; return Poll::Ready(Event::PowerDetected); From 219ef5b37a1dff5f6e770da252ec010d55c43257 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 08:42:51 +0200 Subject: [PATCH 1450/1575] stm32/otg: add VBUS detection. Fixes #1442. --- embassy-stm32/src/usb_otg/usb.rs | 479 +++++++++++++---------- examples/stm32f4/src/bin/usb_ethernet.rs | 4 +- examples/stm32f4/src/bin/usb_serial.rs | 4 +- examples/stm32f7/src/bin/usb_serial.rs | 4 +- examples/stm32h7/src/bin/usb_serial.rs | 4 +- examples/stm32l4/src/bin/usb_serial.rs | 4 +- examples/stm32u5/src/bin/usb_serial.rs | 4 +- 7 files changed, 297 insertions(+), 206 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index b2f7eb852..6c00c93d6 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -6,8 +6,8 @@ use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; use embassy_hal_common::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ - self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, - EndpointType, Event, Unsupported, + self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, + EndpointOut, EndpointType, Event, Unsupported, }; use futures::future::poll_fn; @@ -31,7 +31,7 @@ impl interrupt::typelevel::Handler for InterruptHandl let state = T::state(); let ints = r.gintsts().read(); - if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() { + if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() { // Mask interrupts and notify `Bus` to process them r.gintmsk().write(|_| {}); T::state().bus_waker.wake(); @@ -256,7 +256,34 @@ struct EndpointData { fifo_size_words: u16, } +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Config { + /// Enable VBUS detection. + /// + /// The USB spec requires USB devices monitor for USB cable plug/unplug and react accordingly. + /// This is done by checkihg whether there is 5V on the VBUS pin or not. + /// + /// If your device is bus-powered (powers itself from the USB host via VBUS), then this is optional. + /// (if there's no power in VBUS your device would be off anyway, so it's fine to always assume + /// there's power in VBUS, i.e. the USB cable is always plugged in.) + /// + /// If your device is self-powered (i.e. it gets power from a source other than the USB cable, and + /// therefore can stay powered through USB cable plug/unplug) then you MUST set this to true. + /// + /// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a + /// voltage divider. See ST application note AN4879 and the reference manual for more details. + pub vbus_detection: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { vbus_detection: true } + } +} + pub struct Driver<'d, T: Instance> { + config: Config, phantom: PhantomData<&'d mut T>, ep_in: [Option; MAX_EP_COUNT], ep_out: [Option; MAX_EP_COUNT], @@ -279,6 +306,7 @@ impl<'d, T: Instance> Driver<'d, T> { dp: impl Peripheral

> + 'd, dm: impl Peripheral

> + 'd, ep_out_buffer: &'d mut [u8], + config: Config, ) -> Self { into_ref!(dp, dm); @@ -286,6 +314,7 @@ impl<'d, T: Instance> Driver<'d, T> { dm.set_as_af(dm.af_num(), AFType::OutputPushPull); Self { + config, phantom: PhantomData, ep_in: [None; MAX_EP_COUNT], ep_out: [None; MAX_EP_COUNT], @@ -318,6 +347,7 @@ impl<'d, T: Instance> Driver<'d, T> { ulpi_d6: impl Peripheral

> + 'd, ulpi_d7: impl Peripheral

> + 'd, ep_out_buffer: &'d mut [u8], + config: Config, ) -> Self { assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB"); @@ -327,6 +357,7 @@ impl<'d, T: Instance> Driver<'d, T> { ); Self { + config, phantom: PhantomData, ep_in: [None; MAX_EP_COUNT], ep_out: [None; MAX_EP_COUNT], @@ -464,11 +495,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { ( Bus { + config: self.config, phantom: PhantomData, ep_in: self.ep_in, ep_out: self.ep_out, phy_type: self.phy_type, - enabled: false, + inited: false, }, ControlPipe { _phantom: PhantomData, @@ -481,11 +513,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> { } pub struct Bus<'d, T: Instance> { + config: Config, phantom: PhantomData<&'d mut T>, ep_in: [Option; MAX_EP_COUNT], ep_out: [Option; MAX_EP_COUNT], phy_type: PhyType, - enabled: bool, + inited: bool, } impl<'d, T: Instance> Bus<'d, T> { @@ -498,11 +531,202 @@ impl<'d, T: Instance> Bus<'d, T> { w.set_iepint(true); w.set_oepint(true); w.set_rxflvlm(true); + w.set_srqim(true); + w.set_otgint(true); }); } } impl<'d, T: Instance> Bus<'d, T> { + fn init(&mut self) { + #[cfg(stm32l4)] + { + crate::peripherals::PWR::enable(); + critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); + } + + #[cfg(stm32f7)] + { + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpien(ulpien); + } else { + w.set_usb_otg_hsen(ulpien); + } + }); + + // Low power mode + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hsulpilpen(ulpien); + } else { + w.set_usb_otg_hslpen(ulpien); + } + }); + }); + } + + #[cfg(stm32h7)] + { + // If true, VDD33USB is generated by internal regulator from VDD50USB + // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) + // TODO: unhardcode + let internal_regulator = false; + + // Enable USB power + critical_section::with(|_| { + crate::pac::PWR.cr3().modify(|w| { + w.set_usb33den(true); + w.set_usbregen(internal_regulator); + }) + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.cr3().read().usb33rdy() {} + + // Use internal 48MHz HSI clock. Should be enabled in RCC by default. + critical_section::with(|_| { + crate::pac::RCC + .d2ccip2r() + .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) + }); + + // Enable ULPI clock if external PHY is used + let ulpien = !self.phy_type.internal(); + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpien(ulpien); + } else { + w.set_usb_otg_fs_ulpien(ulpien); + } + }); + crate::pac::RCC.ahb1lpenr().modify(|w| { + if T::HIGH_SPEED { + w.set_usb_otg_hs_ulpilpen(ulpien); + } else { + w.set_usb_otg_fs_ulpilpen(ulpien); + } + }); + }); + } + + #[cfg(stm32u5)] + { + // Enable USB power + critical_section::with(|_| { + crate::pac::RCC.ahb3enr().modify(|w| { + w.set_pwren(true); + }); + cortex_m::asm::delay(2); + + crate::pac::PWR.svmcr().modify(|w| { + w.set_usv(true); + w.set_uvmen(true); + }); + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.svmsr().read().vddusbrdy() {} + + // Select HSI48 as USB clock source. + critical_section::with(|_| { + crate::pac::RCC.ccipr1().modify(|w| { + w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); + }) + }); + } + + ::enable(); + ::reset(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + let r = T::regs(); + let core_id = r.cid().read().0; + info!("Core id {:08x}", core_id); + + // Wait for AHB ready. + while !r.grstctl().read().ahbidl() {} + + // Configure as device. + r.gusbcfg().write(|w| { + // Force device mode + w.set_fdmod(true); + // Enable internal full-speed PHY + w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); + }); + + // Configuring Vbus sense and SOF output + match core_id { + 0x0000_1200 | 0x0000_1100 => { + assert!(self.phy_type != PhyType::InternalHighSpeed); + + r.gccfg_v1().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal()); + }); + + // F429-like chips have the GCCFG.NOVBUSSENS bit + r.gccfg_v1().modify(|w| { + w.set_novbussens(!self.config.vbus_detection); + w.set_vbusasen(false); + w.set_vbusbsen(self.config.vbus_detection); + w.set_sofouten(false); + }); + } + 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { + // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning + r.gccfg_v2().modify(|w| { + // Enable internal full-speed PHY, logic is inverted + w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); + w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); + }); + + r.gccfg_v2().modify(|w| { + w.set_vbden(self.config.vbus_detection); + }); + + // Force B-peripheral session + r.gotgctl().modify(|w| { + w.set_bvaloen(!self.config.vbus_detection); + w.set_bvaloval(true); + }); + } + _ => unimplemented!("Unknown USB core id {:X}", core_id), + } + + // Soft disconnect. + r.dctl().write(|w| w.set_sdis(true)); + + // Set speed. + r.dcfg().write(|w| { + w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); + w.set_dspd(self.phy_type.to_dspd()); + }); + + // Unmask transfer complete EP interrupt + r.diepmsk().write(|w| { + w.set_xfrcm(true); + }); + + // Unmask and clear core interrupts + Bus::::restore_irqs(); + r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); + + // Unmask global interrupt + r.gahbcfg().write(|w| { + w.set_gint(true); // unmask global interrupt + }); + + // Connect + r.dctl().write(|w| w.set_sdis(false)); + } + fn init_fifo(&mut self) { trace!("init_fifo"); @@ -613,6 +837,13 @@ impl<'d, T: Instance> Bus<'d, T> { }); } + fn disable_all_endpoints(&mut self) { + for i in 0..T::ENDPOINT_COUNT { + self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::In), false); + self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::Out), false); + } + } + fn disable(&mut self) { T::Interrupt::disable(); @@ -627,9 +858,14 @@ impl<'d, T: Instance> Bus<'d, T> { impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { async fn poll(&mut self) -> Event { poll_fn(move |cx| { - // TODO: implement VBUS detection - if !self.enabled { - return Poll::Ready(Event::PowerDetected); + if !self.inited { + self.init(); + self.inited = true; + + // If no vbus detection, just return a single PowerDetected event at startup. + if !self.config.vbus_detection { + return Poll::Ready(Event::PowerDetected); + } } let r = T::regs(); @@ -637,6 +873,32 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { T::state().bus_waker.register(cx.waker()); let ints = r.gintsts().read(); + + if ints.srqint() { + trace!("vbus detected"); + + r.gintsts().write(|w| w.set_srqint(true)); // clear + Self::restore_irqs(); + + if self.config.vbus_detection { + return Poll::Ready(Event::PowerDetected); + } + } + + if ints.otgint() { + let otgints = r.gotgint().read(); + r.gotgint().write_value(otgints); // clear all + Self::restore_irqs(); + + if otgints.sedet() { + trace!("vbus removed"); + if self.config.vbus_detection { + self.disable_all_endpoints(); + return Poll::Ready(Event::PowerRemoved); + } + } + } + if ints.usbrst() { trace!("reset"); @@ -801,203 +1063,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { async fn enable(&mut self) { trace!("enable"); - - #[cfg(stm32l4)] - { - crate::peripherals::PWR::enable(); - critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); - } - - #[cfg(stm32f7)] - { - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpien(ulpien); - } else { - w.set_usb_otg_hsen(ulpien); - } - }); - - // Low power mode - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hsulpilpen(ulpien); - } else { - w.set_usb_otg_hslpen(ulpien); - } - }); - }); - } - - #[cfg(stm32h7)] - { - // If true, VDD33USB is generated by internal regulator from VDD50USB - // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) - // TODO: unhardcode - let internal_regulator = false; - - // Enable USB power - critical_section::with(|_| { - crate::pac::PWR.cr3().modify(|w| { - w.set_usb33den(true); - w.set_usbregen(internal_regulator); - }) - }); - - // Wait for USB power to stabilize - while !crate::pac::PWR.cr3().read().usb33rdy() {} - - // Use internal 48MHz HSI clock. Should be enabled in RCC by default. - critical_section::with(|_| { - crate::pac::RCC - .d2ccip2r() - .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48)) - }); - - // Enable ULPI clock if external PHY is used - let ulpien = !self.phy_type.internal(); - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpien(ulpien); - } else { - w.set_usb_otg_fs_ulpien(ulpien); - } - }); - crate::pac::RCC.ahb1lpenr().modify(|w| { - if T::HIGH_SPEED { - w.set_usb_otg_hs_ulpilpen(ulpien); - } else { - w.set_usb_otg_fs_ulpilpen(ulpien); - } - }); - }); - } - - #[cfg(stm32u5)] - { - // Enable USB power - critical_section::with(|_| { - crate::pac::RCC.ahb3enr().modify(|w| { - w.set_pwren(true); - }); - cortex_m::asm::delay(2); - - crate::pac::PWR.svmcr().modify(|w| { - w.set_usv(true); - w.set_uvmen(true); - }); - }); - - // Wait for USB power to stabilize - while !crate::pac::PWR.svmsr().read().vddusbrdy() {} - - // Select HSI48 as USB clock source. - critical_section::with(|_| { - crate::pac::RCC.ccipr1().modify(|w| { - w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48); - }) - }); - } - - ::enable(); - ::reset(); - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - let r = T::regs(); - let core_id = r.cid().read().0; - info!("Core id {:08x}", core_id); - - // Wait for AHB ready. - while !r.grstctl().read().ahbidl() {} - - // Configure as device. - r.gusbcfg().write(|w| { - // Force device mode - w.set_fdmod(true); - // Enable internal full-speed PHY - w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed()); - }); - - // Configuring Vbus sense and SOF output - match core_id { - 0x0000_1200 | 0x0000_1100 => { - assert!(self.phy_type != PhyType::InternalHighSpeed); - - r.gccfg_v1().modify(|w| { - // Enable internal full-speed PHY, logic is inverted - w.set_pwrdwn(self.phy_type.internal()); - }); - - // F429-like chips have the GCCFG.NOVBUSSENS bit - r.gccfg_v1().modify(|w| { - w.set_novbussens(true); - w.set_vbusasen(false); - w.set_vbusbsen(false); - w.set_sofouten(false); - }); - } - 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => { - // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning - r.gccfg_v2().modify(|w| { - // Enable internal full-speed PHY, logic is inverted - w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed()); - w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed()); - }); - - r.gccfg_v2().modify(|w| { - w.set_vbden(false); - }); - - // Force B-peripheral session - r.gotgctl().modify(|w| { - w.set_bvaloen(true); - w.set_bvaloval(true); - }); - } - _ => unimplemented!("Unknown USB core id {:X}", core_id), - } - - // Soft disconnect. - r.dctl().write(|w| w.set_sdis(true)); - - // Set speed. - r.dcfg().write(|w| { - w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); - w.set_dspd(self.phy_type.to_dspd()); - }); - - // Unmask transfer complete EP interrupt - r.diepmsk().write(|w| { - w.set_xfrcm(true); - }); - - // Unmask and clear core interrupts - Bus::::restore_irqs(); - r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); - - // Unmask global interrupt - r.gahbcfg().write(|w| { - w.set_gint(true); // unmask global interrupt - }); - - // Connect - r.dctl().write(|w| w.set_sdis(false)); - - self.enabled = true; + // TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb } async fn disable(&mut self) { trace!("disable"); - Bus::disable(self); - - self.enabled = false; + // TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb + //Bus::disable(self); } async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { @@ -1140,11 +1213,16 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { state.ep_in_wakers[index].register(cx.waker()); let diepctl = r.diepctl(index).read(); + let dtxfsts = r.dtxfsts(index).read(); + info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0); if !diepctl.usbaep() { + trace!("write ep={:?} wait for prev: error disabled", self.info.addr); Poll::Ready(Err(EndpointError::Disabled)) } else if !diepctl.epena() { + trace!("write ep={:?} wait for prev: ready", self.info.addr); Poll::Ready(Ok(())) } else { + trace!("write ep={:?} wait for prev: pending", self.info.addr); Poll::Pending } }) @@ -1169,6 +1247,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { Poll::Pending } else { + trace!("write ep={:?} wait for fifo: ready", self.info.addr); Poll::Ready(()) } }) diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 953d99a45..b1f01417c 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -52,7 +52,9 @@ async fn main(spawner: Spawner) { // Create the driver, from the HAL. let ep_out_buffer = &mut make_static!([0; 256])[..]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index f8f5940a7..4ff6452ef 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 763309ce2..a2c76178b 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index c622f19f7..97291f60c 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 80811a43e..410d6891b 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index f36daf91b..9e47fb18a 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -31,7 +31,9 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); + let mut config = embassy_stm32::usb_otg::Config::default(); + config.vbus_detection = true; + let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); From afec1b439bb40b769c8ccd1c1b19d58edd034c3d Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 18:17:51 +0200 Subject: [PATCH 1451/1575] feature-gate dma write, make trigger not return a result --- embassy-stm32/src/dac/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 6ead00e15..3e48d558a 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -153,11 +153,10 @@ pub trait DacChannel { } /// Perform a software trigger on `ch` - fn trigger(&mut self) -> Result<(), Error> { + fn trigger(&mut self) { T::regs().swtrigr().write(|reg| { reg.set_swtrig(Self::CHANNEL.index(), true); }); - Ok(()) } /// Set a value to be output by the DAC on trigger. @@ -230,6 +229,8 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { } /// Select a new trigger for this channel + /// + /// **Important**: This disables the channel! pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> { unwrap!(self.disable_channel()); T::regs().cr().modify(|reg| { @@ -245,6 +246,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 1 has to be configured for the DAC instance! + #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh1, @@ -355,6 +357,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 2 has to be configured for the DAC instance! + #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh2, From 56dd22f0ac49be2b824e88026d38b69843b56972 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:23:47 +0200 Subject: [PATCH 1452/1575] feature-gate set_channel_mode, undo dma.rs changes --- embassy-stm32/src/dac/mod.rs | 17 ++++++++++------- embassy-stm32/src/dma/dma.rs | 18 +----------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 3e48d558a..b53083524 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -127,6 +127,7 @@ pub trait DacChannel { } /// Set mode register of the given channel + #[cfg(dac_v2)] fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { T::regs().mcr().modify(|reg| { reg.set_mode(Self::CHANNEL.index(), val); @@ -221,6 +222,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) + #[cfg(dac_v2)] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -334,6 +336,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) + #[cfg(dac_v2)] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -454,10 +457,12 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) + #[cfg(dac_v2)] dac_ch1.set_channel_mode(0).unwrap(); dac_ch1.enable_channel().unwrap(); dac_ch1.set_trigger_enable(true).unwrap(); + #[cfg(dac_v2)] dac_ch2.set_channel_mode(0).unwrap(); dac_ch2.enable_channel().unwrap(); dac_ch2.set_trigger_enable(true).unwrap(); @@ -521,27 +526,25 @@ foreach_peripheral!( #[cfg(rcc_h7)] impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { - crate::rcc::get_freqs().apb1 - }) + critical_section::with(|_| crate::rcc::get_freqs().apb1) } fn reset() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true)); crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false)); }) } fn enable() { - critical_section::with(|_| unsafe { + critical_section::with(|_| { crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); }) } fn disable() { - critical_section::with(|_| unsafe { - crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)); + critical_section::with(|_| { + crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false)) }) } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index a5f828948..0b7b60789 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -29,12 +29,6 @@ pub struct TransferOptions { pub flow_ctrl: FlowControl, /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. pub fifo_threshold: Option, - /// Enable circular DMA - pub circular: bool, - /// Enable half transfer interrupt - pub half_transfer_ir: bool, - /// Enable transfer complete interrupt - pub complete_transfer_ir: bool, } impl Default for TransferOptions { @@ -44,9 +38,6 @@ impl Default for TransferOptions { mburst: Burst::Single, flow_ctrl: FlowControl::Dma, fifo_threshold: None, - circular: false, - half_transfer_ir: false, - complete_transfer_ir: true, } } } @@ -375,20 +366,13 @@ impl<'a, C: Channel> Transfer<'a, C> { }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_tcie(options.complete_transfer_ir); - w.set_htie(options.half_transfer_ir); + w.set_tcie(true); #[cfg(dma_v1)] w.set_trbuff(true); #[cfg(dma_v2)] w.set_chsel(_request); - if options.circular { - w.set_circ(vals::Circ::ENABLED); - debug!("Setting circular mode"); - } else { - w.set_circ(vals::Circ::DISABLED); - } w.set_pburst(options.pburst.into()); w.set_mburst(options.mburst.into()); w.set_pfctrl(options.flow_ctrl.into()); From 60c54107ce5cc50f9d9365297d31973d96f00021 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:58:56 +0200 Subject: [PATCH 1453/1575] fix sdmmc bdma transferconfig fields --- embassy-stm32/src/sdmmc/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 80a336a48..698292bff 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -227,7 +227,11 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp fifo_threshold: Some(crate::dma::FifoThreshold::Full), }; #[cfg(all(sdmmc_v1, not(dma)))] -const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {}; +const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, +}; /// SDMMC configuration /// From 9c81d6315500b236adc7634d2d2d6ef776f984eb Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Tue, 27 Jun 2023 22:33:17 +0200 Subject: [PATCH 1454/1575] fix warnings --- embassy-stm32/src/dac/mod.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index b53083524..6686a387a 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -5,7 +5,6 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use crate::dma::{Transfer, TransferOptions}; use crate::pac::dac; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -195,6 +194,7 @@ pub struct Dac<'d, T: Instance, TxCh1, TxCh2> { pub struct DacCh1<'d, T: Instance, Tx> { /// To consume T _peri: PeripheralRef<'d, T>, + #[allow(unused)] // For chips whose DMA is not (yet) supported dma: PeripheralRef<'d, Tx>, } @@ -204,6 +204,7 @@ pub struct DacCh1<'d, T: Instance, Tx> { pub struct DacCh2<'d, T: Instance, Tx> { /// Instead of PeripheralRef to consume T phantom: PhantomData<&'d mut T>, + #[allow(unused)] // For chips whose DMA is not (yet) supported dma: PeripheralRef<'d, Tx>, } @@ -265,7 +266,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { let tx_request = self.dma.request(); let dma_channel = &self.dma; - let tx_options = TransferOptions { + let tx_options = crate::dma::TransferOptions { circular, half_transfer_ir: false, complete_transfer_ir: !circular, @@ -275,7 +276,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data { ValueArray::Bit8(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -284,7 +285,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { ) }, ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -293,7 +294,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { ) }, ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -377,7 +378,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { let tx_request = self.dma.request(); let dma_channel = &self.dma; - let tx_options = TransferOptions { + let tx_options = crate::dma::TransferOptions { circular, half_transfer_ir: false, complete_transfer_ir: !circular, @@ -387,7 +388,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { // Initiate the correct type of DMA transfer depending on what data is passed let tx_f = match data { ValueArray::Bit8(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -396,7 +397,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { ) }, ValueArray::Bit12Left(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -405,7 +406,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { ) }, ValueArray::Bit12Right(buf) => unsafe { - Transfer::new_write( + crate::dma::Transfer::new_write( dma_channel, tx_request, buf, @@ -526,7 +527,7 @@ foreach_peripheral!( #[cfg(rcc_h7)] impl crate::rcc::sealed::RccPeripheral for peripherals::$inst { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| crate::rcc::get_freqs().apb1) + critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 }) } fn reset() { From f5ca687e9bbeb81ce24f56db6cd7defbcb5c2db2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 23:49:12 +0200 Subject: [PATCH 1455/1575] sync/pipe: fix doc typos. --- embassy-sync/src/pipe.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index db6ebb08b..13bf4ef01 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -282,7 +282,7 @@ where /// returns the amount of bytes written. /// /// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full, - /// this method will wait until it is. See [`try_write`](Self::try_write) for a variant that + /// this method will wait until it isn't. See [`try_write`](Self::try_write) for a variant that /// returns an error instead of waiting. /// /// It is not guaranteed that all bytes in the buffer are written, even if there's enough @@ -319,7 +319,7 @@ where /// returns the amount of bytes read. /// /// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty, - /// this method will wait until it is. See [`try_read`](Self::try_read) for a variant that + /// this method will wait until it isn't. See [`try_read`](Self::try_read) for a variant that /// returns an error instead of waiting. /// /// It is not guaranteed that all bytes in the buffer are read, even if there's enough From ed493be869fa653dc14d31060375e17e2469ce11 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 27 Jun 2023 23:49:30 +0200 Subject: [PATCH 1456/1575] stm32: update metapac, includes fix for OTG with 9 endpoints (H7) --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/rcc/l0.rs | 4 ++-- examples/stm32g4/src/bin/usb_serial.rs | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3d9ee8261..f15c6d0b7 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "10" +stm32-metapac = "11" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "10", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "11", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs index 42a481a74..d53b61069 100644 --- a/embassy-stm32/src/rcc/l0.rs +++ b/embassy-stm32/src/rcc/l0.rs @@ -1,7 +1,7 @@ use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; use crate::pac::RCC; #[cfg(crs)] -use crate::pac::{CRS, SYSCFG}; +use crate::pac::{crs, CRS, SYSCFG}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -338,7 +338,7 @@ pub(crate) unsafe fn init(config: Config) { CRS.cfgr().write(|w| // Select LSE as synchronization source - w.set_syncsrc(0b01)); + w.set_syncsrc(crs::vals::Syncsrc::LSE)); CRS.cr().modify(|w| { w.set_autotrimen(true); w.set_cen(true); diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index c111a9787..289d0ed86 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -38,7 +38,9 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10)); + pac::RCC.ccipr().write(|w| { + w.set_clk48sel(pac::rcc::vals::Clk48sel::PLLQCLK); + }); let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); From 91c31d5e437b510af3c535f5e597881042563496 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:58:25 +0200 Subject: [PATCH 1457/1575] Update DAC examples, add DAC + DMA example --- embassy-stm32/src/dac/mod.rs | 2 +- examples/stm32f4/src/bin/dac.rs | 9 +- examples/stm32h7/src/bin/dac.rs | 9 +- examples/stm32l4/Cargo.toml | 2 + examples/stm32l4/src/bin/dac.rs | 16 ++- examples/stm32l4/src/bin/dac_dma.rs | 148 ++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 examples/stm32l4/src/bin/dac_dma.rs diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 6686a387a..1dc13949d 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -178,7 +178,7 @@ pub trait DacChannel { /// /// # Example for obtaining both DAC channels /// -/// ```no_run +/// ```ignore /// // DMA channels and pins may need to be changed for your controller /// let (dac_ch1, dac_ch2) = /// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index d97ae7082..3a6216712 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs @@ -4,7 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dac::{Channel, Dac, Value}; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,12 +13,12 @@ async fn main(_spawner: Spawner) -> ! { let p = embassy_stm32::init(Default::default()); info!("Hello World, dude!"); - let mut dac = Dac::new_1ch(p.DAC, p.PA4); + let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); loop { for v in 0..=255 { - unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); - unwrap!(dac.trigger(Channel::Ch1)); + unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.trigger(); } } } diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index f12716370..586b4154b 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -4,7 +4,8 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dac::{Channel, Dac, Value}; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; use embassy_stm32::time::mhz; use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; @@ -19,12 +20,12 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); - let mut dac = Dac::new_1ch(p.DAC1, p.PA4); + let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); loop { for v in 0..=255 { - unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); - unwrap!(dac.trigger(Channel::Ch1)); + unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.trigger(); } } } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 3bb473ef5..d2d228282 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -25,3 +25,5 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" + +static_cell = "1.0.0" diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index a36ed5d90..8aad27646 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -3,26 +3,22 @@ #![feature(type_alias_impl_trait)] use defmt::*; -use embassy_stm32::dac::{Channel, Dac, Value}; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; use embassy_stm32::pac; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] fn main() -> ! { + let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - pac::RCC.apb1enr1().modify(|w| { - w.set_dac1en(true); - }); - - let p = embassy_stm32::init(Default::default()); - - let mut dac = Dac::new_1ch(p.DAC1, p.PA4); + let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); loop { for v in 0..=255 { - unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); - unwrap!(dac.trigger(Channel::Ch1)); + unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); + dac.trigger(); } } } diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs new file mode 100644 index 000000000..81e6a58e4 --- /dev/null +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -0,0 +1,148 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dac::{DacChannel, ValueArray}; +use embassy_stm32::pac::timer::vals::{Mms, Opm}; +use embassy_stm32::peripherals::{TIM6, TIM7}; +use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::time::Hertz; +use embassy_stm32::timer::low_level::Basic16bitInstance; +use micromath::F32Ext; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +pub type Dac1Type<'d> = + embassy_stm32::dac::DacCh1<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; + +pub type Dac2Type<'d> = + embassy_stm32::dac::DacCh2<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let config = embassy_stm32::Config::default(); + + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config); + + // Obtain two independent channels (p.DAC1 can only be consumed once, though!) + let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); + + let dac1 = { + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init(dac_ch1) + }; + + let dac2 = { + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init(dac_ch2) + }; + + spawner.spawn(dac_task1(dac1)).ok(); + spawner.spawn(dac_task2(dac2)).ok(); +} + +#[embassy_executor::task] +async fn dac_task1(dac: &'static mut Dac1Type<'static>) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM6 frequency is {}", TIM6::frequency()); + const FREQUENCY: Hertz = Hertz::hz(200); + let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; + + // Depends on your clock and on the specific chip used, you may need higher or lower values here + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); + dac.enable_channel().unwrap(); + + TIM6::enable(); + TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM6::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + debug!( + "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM6::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + // Loop technically not necessary if DMA circular mode is enabled + loop { + info!("Loop DAC1"); + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } + } +} + +#[embassy_executor::task] +async fn dac_task2(dac: &'static mut Dac2Type<'static>) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM7 frequency is {}", TIM7::frequency()); + + const FREQUENCY: Hertz = Hertz::hz(600); + let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; + + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + TIM7::enable(); + TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM7::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); + + debug!( + "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM7::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } +} + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = 3.14 * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = 3.14 + 3.14 * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} + +fn calculate_array() -> [u8; N] { + let mut res = [0; N]; + let mut i = 0; + while i < N { + res[i] = to_sine_wave(i as u8); + i += 1; + } + res +} From 59f829c6cce3427f95b7c5b137f62f8af0c3c40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 28 Jun 2023 15:03:57 +0200 Subject: [PATCH 1458/1575] Make StackResources::new() const --- embassy-net/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 3e83da7aa..17a7a22a2 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -57,7 +57,7 @@ pub struct StackResources { impl StackResources { /// Create a new set of stack resources. - pub fn new() -> Self { + pub const fn new() -> Self { #[cfg(feature = "dns")] const INIT: Option = None; Self { From 27a89019adaebfd1916d3b71dde1db3a6a768883 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:21:24 +0200 Subject: [PATCH 1459/1575] add doc --- examples/stm32l4/src/bin/dac_dma.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 81e6a58e4..aefc8412f 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -52,6 +52,8 @@ async fn dac_task1(dac: &'static mut Dac1Type<'static>) { info!("TIM6 frequency is {}", TIM6::frequency()); const FREQUENCY: Hertz = Hertz::hz(200); + + // Compute the reload value such that we obtain the FREQUENCY for the sine let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; // Depends on your clock and on the specific chip used, you may need higher or lower values here From f2e7a23148f0c1f663744bfe47fbbb37d9552080 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:25:57 +0200 Subject: [PATCH 1460/1575] attempt at fixing ci --- examples/stm32l4/.cargo/config.toml | 3 ++- examples/stm32l4/memory.x | 8 ++++---- examples/stm32l4/src/bin/dac_dma.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index abf55eb2e..4ccdf121e 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,7 +2,8 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` #runner = "probe-rs-cli run --chip STM32L475VGT6" #runner = "probe-rs-cli run --chip STM32L475VG" -runner = "probe-rs-cli run --chip STM32L4S5VI" +#runner = "probe-rs-cli run --chip STM32L4S5VI" +runner = "probe-run --chip STM32L432KCUx" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l4/memory.x b/examples/stm32l4/memory.x index eb87d1b54..0cef526ae 100644 --- a/examples/stm32l4/memory.x +++ b/examples/stm32l4/memory.x @@ -1,7 +1,7 @@ MEMORY { - /* NOTE 1 K = 1 KiBi = 1024 bytes */ - /* These values correspond to the STM32L4S5 */ - FLASH : ORIGIN = 0x08000000, LENGTH = 1024K - RAM : ORIGIN = 0x20000000, LENGTH = 128K + /* NOTE K = KiBi = 1024 bytes */ + /* TODO Adjust these memory regions to match your device memory layout */ + FLASH : ORIGIN = 0x8000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K } diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index aefc8412f..7c0df835b 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -93,7 +93,7 @@ async fn dac_task1(dac: &'static mut Dac1Type<'static>) { #[embassy_executor::task] async fn dac_task2(dac: &'static mut Dac2Type<'static>) { let data: &[u8; 256] = &calculate_array::<256>(); - + info!("TIM7 frequency is {}", TIM7::frequency()); const FREQUENCY: Hertz = Hertz::hz(600); From 02f367f733591c9423731a86ea7726772a88dac0 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:28:10 +0200 Subject: [PATCH 1461/1575] attempt at fixing ci --- examples/stm32l4/src/bin/dac.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index 8aad27646..ade43eb35 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_stm32::dac::{DacCh1, DacChannel, Value}; use embassy_stm32::dma::NoDma; -use embassy_stm32::pac; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] From bf7e24e9d7abc5d31b75ef97418577920bd4600c Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:37:29 +0200 Subject: [PATCH 1462/1575] revert to STM32L4S5VI --- examples/stm32l4/.cargo/config.toml | 3 +-- examples/stm32l4/memory.x | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index 4ccdf121e..abf55eb2e 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,8 +2,7 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` #runner = "probe-rs-cli run --chip STM32L475VGT6" #runner = "probe-rs-cli run --chip STM32L475VG" -#runner = "probe-rs-cli run --chip STM32L4S5VI" -runner = "probe-run --chip STM32L432KCUx" +runner = "probe-rs-cli run --chip STM32L4S5VI" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l4/memory.x b/examples/stm32l4/memory.x index 0cef526ae..eb87d1b54 100644 --- a/examples/stm32l4/memory.x +++ b/examples/stm32l4/memory.x @@ -1,7 +1,7 @@ MEMORY { - /* NOTE K = KiBi = 1024 bytes */ - /* TODO Adjust these memory regions to match your device memory layout */ - FLASH : ORIGIN = 0x8000000, LENGTH = 256K - RAM : ORIGIN = 0x20000000, LENGTH = 64K + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the STM32L4S5 */ + FLASH : ORIGIN = 0x08000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 128K } From daedfbbd8756e921cc6343ad531401d309966eaa Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:39:36 +0200 Subject: [PATCH 1463/1575] add dma is_running change doc --- embassy-stm32/src/dma/dma.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 0b7b60789..9c03599eb 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -404,11 +404,9 @@ impl<'a, C: Channel> Transfer<'a, C> { } pub fn is_running(&mut self) -> bool { - //let ch = self.channel.regs().st(self.channel.num()); - //ch.cr().read().en() - let ch = self.channel.regs().st(self.channel.num()); let en = ch.cr().read().en(); + // Check if circular mode is enabled, if so it will still be running even if tcif == 1 let circular = ch.cr().read().circ() == vals::Circ::ENABLED; let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; en && (circular || !tcif) From d5898c11ebef63fa0ec6dba8381484f4cfabd65c Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Wed, 28 Jun 2023 16:40:50 +0200 Subject: [PATCH 1464/1575] remove need for StaticCell in dac_dma example for stm32l4 --- examples/stm32l4/Cargo.toml | 2 -- examples/stm32l4/src/bin/dac_dma.rs | 29 ++++++++--------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index d2d228282..3bb473ef5 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -25,5 +25,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" - -static_cell = "1.0.0" diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 7c0df835b..c27cc03e1 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -11,14 +11,13 @@ use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Basic16bitInstance; use micromath::F32Ext; -use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -pub type Dac1Type<'d> = - embassy_stm32::dac::DacCh1<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; +pub type Dac1Type = + embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; -pub type Dac2Type<'d> = - embassy_stm32::dac::DacCh2<'d, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; +pub type Dac2Type = + embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; #[embassy_executor::main] async fn main(spawner: Spawner) { @@ -30,24 +29,12 @@ async fn main(spawner: Spawner) { // Obtain two independent channels (p.DAC1 can only be consumed once, though!) let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); - let dac1 = { - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init(dac_ch1) - }; - - let dac2 = { - type T = impl Sized; - static STATIC_CELL: StaticCell = StaticCell::new(); - STATIC_CELL.init(dac_ch2) - }; - - spawner.spawn(dac_task1(dac1)).ok(); - spawner.spawn(dac_task2(dac2)).ok(); + spawner.spawn(dac_task1(dac_ch1)).ok(); + spawner.spawn(dac_task2(dac_ch2)).ok(); } #[embassy_executor::task] -async fn dac_task1(dac: &'static mut Dac1Type<'static>) { +async fn dac_task1(mut dac: Dac1Type) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM6 frequency is {}", TIM6::frequency()); @@ -91,7 +78,7 @@ async fn dac_task1(dac: &'static mut Dac1Type<'static>) { } #[embassy_executor::task] -async fn dac_task2(dac: &'static mut Dac2Type<'static>) { +async fn dac_task2(mut dac: Dac2Type) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM7 frequency is {}", TIM7::frequency()); From 5666c569033d59fc894230ed4161e6c686733b2d Mon Sep 17 00:00:00 2001 From: Kevin Lannen Date: Wed, 28 Jun 2023 13:05:39 -0600 Subject: [PATCH 1465/1575] STM32G4: Add CRS support to RCC Create working CRS USB Example --- embassy-stm32/src/rcc/g4.rs | 77 +++++++++++++++++++++++++- examples/stm32g4/src/bin/usb_serial.rs | 30 ++++++---- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 9401af4c3..ff8f97541 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -3,6 +3,7 @@ use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; use stm32_metapac::FLASH; use crate::pac::{PWR, RCC}; +use crate::rcc::sealed::RccPeripheral; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -316,6 +317,27 @@ impl Into for AHBPrescaler { } } +/// Sets the source for the 48MHz clock to the USB and RNG peripherals. +pub enum Clock48MhzSrc { + /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the + /// oscillator to comply with the USB specification for oscillator tolerance. + Hsi48(Option), + /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the + /// PLL needs to be using the HSE source to comply with the USB specification for oscillator + /// tolerance. + PllQ, +} + +/// Sets the sync source for the Clock Recovery System (CRS). +pub enum CrsSyncSource { + /// Use an external GPIO to sync the CRS. + Gpio, + /// Use the Low Speed External oscillator to sync the CRS. + Lse, + /// Use the USB SOF to sync the CRS. + Usb, +} + /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -326,6 +348,14 @@ pub struct Config { /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration /// MUST turn on the PLLR output. pub pll: Option, + /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. + pub clock_48mhz_src: Option, +} + +/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator. +pub struct CrsConfig { + /// Sync source for the CRS. + pub sync_src: CrsSyncSource, } impl Default for Config { @@ -338,6 +368,7 @@ impl Default for Config { apb2_pre: APBPrescaler::NotDivided, low_power_run: false, pll: None, + clock_48mhz_src: None, } } } @@ -430,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) { assert!(pll_freq.is_some()); assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); - let freq = pll_freq.unwrap().pll_r.unwrap().0; + let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0; assert!(freq <= 170_000_000); @@ -497,6 +528,50 @@ pub(crate) unsafe fn init(config: Config) { } }; + // Setup the 48 MHz clock if needed + if let Some(clock_48mhz_src) = config.clock_48mhz_src { + let source = match clock_48mhz_src { + Clock48MhzSrc::PllQ => { + // Make sure the PLLQ is enabled and running at 48Mhz + let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q); + assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000); + + crate::pac::rcc::vals::Clk48sel::PLLQCLK + } + Clock48MhzSrc::Hsi48(crs_config) => { + // Enable HSI48 + RCC.crrcr().modify(|w| w.set_hsi48on(true)); + // Wait for HSI48 to turn on + while RCC.crrcr().read().hsi48rdy() == false {} + + // Enable and setup CRS if needed + if let Some(crs_config) = crs_config { + crate::peripherals::CRS::enable(); + + let sync_src = match crs_config.sync_src { + CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO, + CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE, + CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB, + }; + + crate::pac::CRS.cfgr().modify(|w| { + w.set_syncsrc(sync_src); + }); + + // These are the correct settings for standard USB operation. If other settings + // are needed there will need to be additional config options for the CRS. + crate::pac::CRS.cr().modify(|w| { + w.set_autotrimen(true); + w.set_cen(true); + }); + } + crate::pac::rcc::vals::Clk48sel::HSI48 + } + }; + + RCC.ccipr().modify(|w| w.set_clk48sel(source)); + } + if config.low_power_run { assert!(sys_clk <= 2_000_000); PWR.cr1().modify(|w| w.set_lpr(true)); diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 289d0ed86..77cfa67d3 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -4,10 +4,10 @@ use defmt::{panic, *}; use embassy_executor::Spawner; -use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllQ, PllR, PllSrc}; +use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{self, Driver, Instance}; -use embassy_stm32::{bind_interrupts, pac, peripherals, Config}; +use embassy_stm32::{bind_interrupts, peripherals, Config}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; @@ -22,25 +22,35 @@ bind_interrupts!(struct Irqs { async fn main(_spawner: Spawner) { let mut config = Config::default(); + // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE. + const USE_HSI48: bool = true; + + let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) }; + config.rcc.pll = Some(Pll { - source: PllSrc::HSE(Hertz(8000000)), + source: PllSrc::HSE(Hertz(8_000_000)), prediv_m: PllM::Div2, mul_n: PllN::Mul72, div_p: None, - // USB and CAN at 48 MHz - div_q: Some(PllQ::Div6), + div_q: pllq_div, // Main system clock at 144 MHz div_r: Some(PllR::Div2), }); config.rcc.mux = ClockSrc::PLL; - let p = embassy_stm32::init(config); - info!("Hello World!"); + if USE_HSI48 { + // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. + config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig { + sync_src: CrsSyncSource::Usb, + }))); + } else { + config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); + } - pac::RCC.ccipr().write(|w| { - w.set_clk48sel(pac::rcc::vals::Clk48sel::PLLQCLK); - }); + let p = embassy_stm32::init(config); + + info!("Hello World!"); let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); From e892014b6572e74b58ea653cfed06614c496e6a5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Jun 2023 01:51:19 +0200 Subject: [PATCH 1466/1575] Update stm32-metapac, includes chiptool changes to use real Rust enums now. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/adc/v3.rs | 8 ++-- embassy-stm32/src/can/bxcan.rs | 8 ++-- embassy-stm32/src/eth/v1/rx_desc.rs | 2 +- embassy-stm32/src/pwm/complementary_pwm.rs | 2 +- embassy-stm32/src/rcc/c0.rs | 10 ++-- embassy-stm32/src/rcc/f0.rs | 23 +++++---- embassy-stm32/src/rcc/f1.rs | 36 +++++++------- embassy-stm32/src/rcc/f2.rs | 2 +- embassy-stm32/src/rcc/f4.rs | 12 ++--- embassy-stm32/src/rcc/f7.rs | 12 ++--- embassy-stm32/src/rcc/g0.rs | 10 ++-- embassy-stm32/src/rcc/h7.rs | 17 +++---- embassy-stm32/src/rcc/l0.rs | 6 +-- embassy-stm32/src/rcc/l1.rs | 6 +-- embassy-stm32/src/rcc/l4.rs | 6 +-- embassy-stm32/src/rcc/l5.rs | 6 +-- embassy-stm32/src/rcc/u5.rs | 2 +- embassy-stm32/src/rtc/v2.rs | 4 +- embassy-stm32/src/rtc/v3.rs | 2 +- embassy-stm32/src/spi/mod.rs | 4 +- embassy-stm32/src/usart/mod.rs | 2 +- embassy-stm32/src/usb/usb.rs | 56 +++++++++++----------- embassy-stm32/src/usb_otg/usb.rs | 4 +- embassy-stm32/src/wdg/mod.rs | 2 +- 25 files changed, 121 insertions(+), 125 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index f15c6d0b7..b3fe9c1f5 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "11" +stm32-metapac = "12" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "11", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "12", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 94cdc86cd..3a6e58cf6 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -211,10 +211,8 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(not(stm32g0))] fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { let sample_time = sample_time.into(); - if ch <= 9 { - T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time)); - } else { - T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); - } + T::regs() + .smpr(ch as usize / 10) + .modify(|reg| reg.set_smp(ch as usize % 10, sample_time)); } } diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 88eef528f..73861776a 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -116,10 +116,10 @@ impl<'d, T: Instance> Can<'d, T> { T::regs().ier().write(|w| { // TODO: fix metapac - w.set_errie(Errie(1)); - w.set_fmpie(0, Fmpie(1)); - w.set_fmpie(1, Fmpie(1)); - w.set_tmeie(Tmeie(1)); + w.set_errie(Errie::from_bits(1)); + w.set_fmpie(0, Fmpie::from_bits(1)); + w.set_fmpie(1, Fmpie::from_bits(1)); + w.set_tmeie(Tmeie::from_bits(1)); }); T::regs().mcr().write(|w| { diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index 01a073bb9..668378bea 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs @@ -174,7 +174,7 @@ impl<'a> RDesRing<'a> { // Receive descriptor unavailable Rps::SUSPENDED => RunningState::Stopped, // Closing receive descriptor - Rps(0b101) => RunningState::Running, + Rps::_RESERVED_5 => RunningState::Running, // Transferring the receive packet data from receive buffer to host memory Rps::RUNNINGWRITING => RunningState::Running, _ => RunningState::Unknown, diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index 0e153202e..4d64d005c 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -243,7 +243,7 @@ mod tests { for test_run in fn_results { let (ckd, bits) = compute_dead_time_value(test_run.value); - assert_eq!(ckd.0, test_run.ckd.0); + assert_eq!(ckd.to_bits(), test_run.ckd.to_bits()); assert_eq!(bits, test_run.bits); } } diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 6c7b36647..df6e9047c 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -126,7 +126,7 @@ pub(crate) unsafe fn init(config: Config) { }); while !RCC.cr().read().hsirdy() {} - (HSI_FREQ.0 >> div.0, Sw::HSI) + (HSI_FREQ.0 >> div.to_bits(), Sw::HSI) } ClockSrc::HSE(freq) => { // Enable HSE @@ -157,7 +157,7 @@ pub(crate) unsafe fn init(config: Config) { let mut set_flash_latency_after = false; FLASH.acr().modify(|w| { // Is the current flash latency less than what we need at the new SYSCLK? - if w.latency().0 <= target_flash_latency.0 { + if w.latency().to_bits() <= target_flash_latency.to_bits() { // We must increase the number of wait states now w.set_latency(target_flash_latency) } else { @@ -171,12 +171,12 @@ pub(crate) unsafe fn init(config: Config) { // > Flash memory. // // Enable flash prefetching if we have at least one wait state, and disable it otherwise. - w.set_prften(target_flash_latency.0 > 0); + w.set_prften(target_flash_latency.to_bits() > 0); }); if !set_flash_latency_after { // Spin until the effective flash latency is compatible with the clock change - while FLASH.acr().read().latency().0 < target_flash_latency.0 {} + while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {} } // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once @@ -218,7 +218,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/f0.rs b/embassy-stm32/src/rcc/f0.rs index eb62ab661..ca6eed284 100644 --- a/embassy-stm32/src/rcc/f0.rs +++ b/embassy-stm32/src/rcc/f0.rs @@ -1,3 +1,5 @@ +use stm32_metapac::flash::vals::Latency; + use super::{set_freqs, Clocks}; use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw}; use crate::pac::{FLASH, RCC}; @@ -85,14 +87,11 @@ pub(crate) unsafe fn init(config: Config) { let timer_mul = if ppre == 1 { 1 } else { 2 }; FLASH.acr().write(|w| { - let latency = if real_sysclk <= 24_000_000 { - 0 - } else if real_sysclk <= 48_000_000 { - 1 + w.set_latency(if real_sysclk <= 24_000_000 { + Latency::WS0 } else { - 2 - }; - w.latency().0 = latency; + Latency::WS1 + }); }); match (config.hse.is_some(), use_hsi48) { @@ -134,20 +133,20 @@ pub(crate) unsafe fn init(config: Config) { // TODO: Option to use CRS (Clock Recovery) if let Some(pllmul_bits) = pllmul_bits { - RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits))); + RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits))); RCC.cr().modify(|w| w.set_pllon(true)); while !RCC.cr().read().pllrdy() {} RCC.cfgr().modify(|w| { - w.set_ppre(Ppre(ppre_bits)); - w.set_hpre(Hpre(hpre_bits)); + w.set_ppre(Ppre::from_bits(ppre_bits)); + w.set_hpre(Hpre::from_bits(hpre_bits)); w.set_sw(Sw::PLL) }); } else { RCC.cfgr().modify(|w| { - w.set_ppre(Ppre(ppre_bits)); - w.set_hpre(Hpre(hpre_bits)); + w.set_ppre(Ppre::from_bits(ppre_bits)); + w.set_hpre(Hpre::from_bits(hpre_bits)); if config.hse.is_some() { w.set_sw(Sw::HSE); diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs index 4769b7059..b6200231e 100644 --- a/embassy-stm32/src/rcc/f1.rs +++ b/embassy-stm32/src/rcc/f1.rs @@ -106,11 +106,11 @@ pub(crate) unsafe fn init(config: Config) { // Only needed for stm32f103? FLASH.acr().write(|w| { w.set_latency(if real_sysclk <= 24_000_000 { - Latency(0b000) + Latency::WS0 } else if real_sysclk <= 48_000_000 { - Latency(0b001) + Latency::WS1 } else { - Latency(0b010) + Latency::WS2 }); }); @@ -147,12 +147,13 @@ pub(crate) unsafe fn init(config: Config) { if let Some(pllmul_bits) = pllmul_bits { let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 }; - RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(pllctpre_flag))); + RCC.cfgr() + .modify(|w| w.set_pllxtpre(Pllxtpre::from_bits(pllctpre_flag))); // enable PLL and wait for it to be ready RCC.cfgr().modify(|w| { - w.set_pllmul(Pllmul(pllmul_bits)); - w.set_pllsrc(Pllsrc(config.hse.is_some() as u8)); + w.set_pllmul(Pllmul::from_bits(pllmul_bits)); + w.set_pllsrc(Pllsrc::from_bits(config.hse.is_some() as u8)); }); RCC.cr().modify(|w| w.set_pllon(true)); @@ -161,22 +162,19 @@ pub(crate) unsafe fn init(config: Config) { // Only needed for stm32f103? RCC.cfgr().modify(|w| { - w.set_adcpre(Adcpre(apre_bits)); - w.set_ppre2(Ppre1(ppre2_bits)); - w.set_ppre1(Ppre1(ppre1_bits)); - w.set_hpre(Hpre(hpre_bits)); + w.set_adcpre(Adcpre::from_bits(apre_bits)); + w.set_ppre2(Ppre1::from_bits(ppre2_bits)); + w.set_ppre1(Ppre1::from_bits(ppre1_bits)); + w.set_hpre(Hpre::from_bits(hpre_bits)); #[cfg(not(rcc_f100))] - w.set_usbpre(Usbpre(usbpre as u8)); - w.set_sw(Sw(if pllmul_bits.is_some() { - // PLL - 0b10 + w.set_usbpre(Usbpre::from_bits(usbpre as u8)); + w.set_sw(if pllmul_bits.is_some() { + Sw::PLL } else if config.hse.is_some() { - // HSE - 0b1 + Sw::HSE } else { - // HSI - 0b0 - })); + Sw::HSI + }); }); set_freqs(Clocks { diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index bcae64d0f..1525cc3c3 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -485,7 +485,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_ppre1(config.apb1_pre.into()); w.set_ppre2(config.apb2_pre.into()); }); - while RCC.cfgr().read().sws() != sw.0 {} + while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} // Turn off HSI to save power if we don't need it if !config.hsi { diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index bc430afb2..b84470440 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -87,7 +87,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, plli2s: Opti let sysclk = pllsysclk.unwrap_or(pllsrcclk); if pllsysclk.is_none() && !pll48clk { - RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); + RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8))); return PllResults { use_pll: false, @@ -141,9 +141,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, plli2s: Opti RCC.pllcfgr().modify(|w| { w.set_pllm(pllm as u8); w.set_plln(plln as u16); - w.set_pllp(Pllp(pllp as u8)); + w.set_pllp(Pllp::from_bits(pllp as u8)); w.set_pllq(pllq as u8); - w.set_pllsrc(Pllsrc(use_hse as u8)); + w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); }); let real_pllsysclk = vco_in * plln / sysclk_div; @@ -323,7 +323,7 @@ fn flash_setup(sysclk: u32) { critical_section::with(|_| { FLASH .acr() - .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); + .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); }); } @@ -440,8 +440,8 @@ pub(crate) unsafe fn init(config: Config) { } RCC.cfgr().modify(|w| { - w.set_ppre2(Ppre(ppre2_bits)); - w.set_ppre1(Ppre(ppre1_bits)); + w.set_ppre2(Ppre::from_bits(ppre2_bits)); + w.set_ppre1(Ppre::from_bits(ppre1_bits)); w.set_hpre(hpre_bits); }); diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs index 71215cac5..85cb9c661 100644 --- a/embassy-stm32/src/rcc/f7.rs +++ b/embassy-stm32/src/rcc/f7.rs @@ -30,7 +30,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bo let sysclk = pllsysclk.unwrap_or(pllsrcclk); if pllsysclk.is_none() && !pll48clk { - RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); + RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8))); return PllResults { use_pll: false, @@ -83,9 +83,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bo RCC.pllcfgr().modify(|w| { w.set_pllm(pllm as u8); w.set_plln(plln as u16); - w.set_pllp(Pllp(pllp as u8)); + w.set_pllp(Pllp::from_bits(pllp as u8)); w.set_pllq(pllq as u8); - w.set_pllsrc(Pllsrc(use_hse as u8)); + w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); }); let real_pllsysclk = vco_in * plln / sysclk_div; @@ -106,7 +106,7 @@ fn flash_setup(sysclk: u32) { critical_section::with(|_| { FLASH .acr() - .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); + .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); }); } @@ -246,8 +246,8 @@ pub(crate) unsafe fn init(config: Config) { } RCC.cfgr().modify(|w| { - w.set_ppre2(Ppre(ppre2_bits)); - w.set_ppre1(Ppre(ppre1_bits)); + w.set_ppre2(Ppre::from_bits(ppre2_bits)); + w.set_ppre1(Ppre::from_bits(ppre1_bits)); w.set_hpre(hpre_bits); }); diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 17c73c36b..5e3a7911a 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -344,7 +344,7 @@ pub(crate) unsafe fn init(config: Config) { }); while !RCC.cr().read().hsirdy() {} - (HSI_FREQ.0 >> div.0, Sw::HSI) + (HSI_FREQ.0 >> div.to_bits(), Sw::HSI) } ClockSrc::HSE(freq) => { // Enable HSE @@ -381,7 +381,7 @@ pub(crate) unsafe fn init(config: Config) { let mut set_flash_latency_after = false; FLASH.acr().modify(|w| { // Is the current flash latency less than what we need at the new SYSCLK? - if w.latency().0 <= target_flash_latency.0 { + if w.latency().to_bits() <= target_flash_latency.to_bits() { // We must increase the number of wait states now w.set_latency(target_flash_latency) } else { @@ -395,12 +395,12 @@ pub(crate) unsafe fn init(config: Config) { // > Flash memory. // // Enable flash prefetching if we have at least one wait state, and disable it otherwise. - w.set_prften(target_flash_latency.0 > 0); + w.set_prften(target_flash_latency.to_bits() > 0); }); if !set_flash_latency_after { // Spin until the effective flash latency is compatible with the clock change - while FLASH.acr().read().latency().0 < target_flash_latency.0 {} + while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {} } // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once @@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index daa1cd61f..f3a98c794 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -601,22 +601,22 @@ pub(crate) unsafe fn init(mut config: Config) { // Core Prescaler / AHB Prescaler / APB3 Prescaler RCC.d1cfgr().modify(|w| { - w.set_d1cpre(Hpre(d1cpre_bits)); - w.set_d1ppre(Dppre(ppre3_bits)); + w.set_d1cpre(Hpre::from_bits(d1cpre_bits)); + w.set_d1ppre(Dppre::from_bits(ppre3_bits)); w.set_hpre(hpre_bits) }); // Ensure core prescaler value is valid before future lower // core voltage - while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {} + while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {} // APB1 / APB2 Prescaler RCC.d2cfgr().modify(|w| { - w.set_d2ppre1(Dppre(ppre1_bits)); - w.set_d2ppre2(Dppre(ppre2_bits)); + w.set_d2ppre1(Dppre::from_bits(ppre1_bits)); + w.set_d2ppre2(Dppre::from_bits(ppre2_bits)); }); // APB4 Prescaler - RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits))); + RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre::from_bits(ppre4_bits))); // Peripheral Clock (per_ck) RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel)); @@ -640,7 +640,7 @@ pub(crate) unsafe fn init(mut config: Config) { _ => Sw::HSI, }; RCC.cfgr().modify(|w| w.set_sw(sw)); - while RCC.cfgr().read().sws() != sw.0 {} + while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} // IO compensation cell - Requires CSI clock and SYSCFG assert!(RCC.cr().read().csirdy()); @@ -806,7 +806,8 @@ mod pll { RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false)); let vco_ck = ref_x_ck * pll_x_n; - RCC.plldivr(plln).modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8))); + RCC.plldivr(plln) + .modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8))); RCC.pllcfgr().modify(|w| w.set_divpen(plln, true)); // Calulate additional output dividers diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs index d53b61069..46a528e31 100644 --- a/embassy-stm32/src/rcc/l0.rs +++ b/embassy-stm32/src/rcc/l0.rs @@ -293,7 +293,7 @@ pub(crate) unsafe fn init(config: Config) { AHBPrescaler::NotDivided => sys_clk, pre => { let pre: Hpre = pre.into(); - let pre = 1 << (pre.0 as u32 - 7); + let pre = 1 << (pre.to_bits() as u32 - 7); sys_clk / pre } }; @@ -302,7 +302,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } @@ -312,7 +312,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs index c907fa88a..59a6eac8f 100644 --- a/embassy-stm32/src/rcc/l1.rs +++ b/embassy-stm32/src/rcc/l1.rs @@ -294,7 +294,7 @@ pub(crate) unsafe fn init(config: Config) { AHBPrescaler::NotDivided => sys_clk, pre => { let pre: Hpre = pre.into(); - let pre = 1 << (pre.0 as u32 - 7); + let pre = 1 << (pre.to_bits() as u32 - 7); sys_clk / pre } }; @@ -303,7 +303,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } @@ -313,7 +313,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index f8c1a6e06..20cb8c91c 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -635,7 +635,7 @@ pub(crate) unsafe fn init(config: Config) { AHBPrescaler::NotDivided => sys_clk, pre => { let pre: Hpre = pre.into(); - let pre = 1 << (pre.0 as u32 - 7); + let pre = 1 << (pre.to_bits() as u32 - 7); sys_clk / pre } }; @@ -644,7 +644,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } @@ -654,7 +654,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs index f56fce365..16da65d5e 100644 --- a/embassy-stm32/src/rcc/l5.rs +++ b/embassy-stm32/src/rcc/l5.rs @@ -461,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) { AHBPrescaler::NotDivided => sys_clk, pre => { let pre: Hpre = pre.into(); - let pre = 1 << (pre.0 as u32 - 7); + let pre = 1 << (pre.to_bits() as u32 - 7); sys_clk / pre } }; @@ -470,7 +470,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } @@ -480,7 +480,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 81507a4d6..cfc07f069 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -126,7 +126,7 @@ pub enum PllM { impl Into for PllM { fn into(self) -> Pllm { - Pllm(self as u8) + Pllm::from_bits(self as u8) } } diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index e1615b34c..a2eace6d3 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -36,7 +36,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(rtc_v2wb)] let rtcsel = reg.rtcsel(); #[cfg(not(rtc_v2wb))] - let rtcsel = reg.rtcsel().0; + let rtcsel = reg.rtcsel().to_bits(); if !reg.rtcen() || rtcsel != clock_config { #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] @@ -54,7 +54,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { // Select RTC source #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcsel(Rtcsel::from_bits(clock_config)); #[cfg(rtc_v2wb)] w.set_rtcsel(clock_config); w.set_rtcen(true); diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 7c91046a2..7e5c64d90 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -26,7 +26,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let config_rtcsel = rtc_config.clock_config as u8; #[cfg(not(any(rcc_wl5, rcc_wle)))] - let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel); + let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(config_rtcsel); if !reg.rtcen() || reg.rtcsel() != config_rtcsel { crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 1cddac992..c3224073d 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -650,7 +650,7 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { _ => 0b111, }; - Br(val) + Br::from_bits(val) } trait RegsExt { @@ -772,7 +772,7 @@ fn set_rxdmaen(regs: Regs, val: bool) { fn finish_dma(regs: Regs) { #[cfg(spi_v2)] - while regs.sr().read().ftlvl() > 0 {} + while regs.sr().read().ftlvl().to_bits() > 0 {} #[cfg(any(spi_v3, spi_v4, spi_v5))] while !regs.sr().read().txc() {} diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 47a79c187..c97efbf0a 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -869,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: _ => vals::Ps::EVEN, }); #[cfg(not(usart_v1))] - w.set_over8(vals::Over8(over8 as _)); + w.set_over8(vals::Over8::from_bits(over8 as _)); }); #[cfg(not(usart_v1))] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 01b158b17..ecdd1d0b8 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -97,8 +97,8 @@ impl interrupt::typelevel::Handler for InterruptHandl } epr.set_dtog_rx(false); epr.set_dtog_tx(false); - epr.set_stat_rx(Stat(0)); - epr.set_stat_tx(Stat(0)); + epr.set_stat_rx(Stat::from_bits(0)); + epr.set_stat_tx(Stat::from_bits(0)); epr.set_ctr_rx(!epr.ctr_rx()); epr.set_ctr_tx(!epr.ctr_tx()); regs.epr(index).write_value(epr); @@ -143,8 +143,8 @@ fn invariant(mut r: regs::Epr) -> regs::Epr { r.set_ctr_tx(true); // don't clear r.set_dtog_rx(false); // don't toggle r.set_dtog_tx(false); // don't toggle - r.set_stat_rx(Stat(0)); - r.set_stat_tx(Stat(0)); + r.set_stat_rx(Stat::from_bits(0)); + r.set_stat_tx(Stat::from_bits(0)); r } @@ -551,7 +551,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { true => Stat::STALL, }; let mut w = invariant(r); - w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); + w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits())); reg.write_value(w); } } @@ -570,7 +570,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { true => Stat::STALL, }; let mut w = invariant(r); - w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); + w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits())); reg.write_value(w); } } @@ -606,7 +606,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { break; } let mut w = invariant(r); - w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); + w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits())); reg.write_value(w); } EP_IN_WAKERS[ep_addr.index()].wake(); @@ -622,7 +622,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { break; } let mut w = invariant(r); - w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); + w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits())); reg.write_value(w); } EP_OUT_WAKERS[ep_addr.index()].wake(); @@ -763,8 +763,8 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { 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_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); + w.set_stat_tx(Stat::from_bits(0)); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }); @@ -805,8 +805,8 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { 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_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); + w.set_stat_rx(Stat::from_bits(0)); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }); @@ -869,19 +869,19 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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; + stat_rx ^= Stat::NAK.to_bits() ^ Stat::VALID.to_bits(); + stat_tx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits(); } if last { // change STALL -> VALID - stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0; + stat_tx ^= Stat::STALL.to_bits() ^ Stat::NAK.to_bits(); } // Note: if this is the first AND last transfer, the above effectively // changes stat_tx like NAK -> NAK, so noop. 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_stat_rx(Stat::from_bits(stat_rx)); + w.set_stat_tx(Stat::from_bits(stat_tx)); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }); @@ -908,11 +908,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { regs.epr(0).write(|w| { w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(match last { + w.set_stat_rx(Stat::from_bits(match last { // If last, set STAT_RX=STALL. - true => Stat::NAK.0 ^ Stat::STALL.0, + true => Stat::NAK.to_bits() ^ Stat::STALL.to_bits(), // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet. - false => Stat::NAK.0 ^ Stat::VALID.0, + false => Stat::NAK.to_bits() ^ Stat::VALID.to_bits(), })); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear @@ -937,17 +937,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { let mut stat_rx = 0; if first { // change NAK -> STALL - stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0; + stat_rx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits(); } if last { // change STALL -> VALID - stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0; + stat_rx ^= Stat::STALL.to_bits() ^ Stat::VALID.to_bits(); } // Note: if this is the first AND last transfer, the above effectively // does a change of NAK -> VALID. regs.epr(0).write(|w| { w.set_ep_type(EpType::CONTROL); - w.set_stat_rx(Stat(stat_rx)); + w.set_stat_rx(Stat::from_bits(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 @@ -977,7 +977,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { let regs = T::regs(); 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_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); 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 @@ -998,8 +998,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits())); + w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::VALID.to_bits())); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }); @@ -1029,8 +1029,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { 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_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits())); + w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::STALL.to_bits())); w.set_ctr_rx(true); // don't clear w.set_ctr_tx(true); // don't clear }); diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 6c00c93d6..6783db28d 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -97,7 +97,7 @@ impl interrupt::typelevel::Handler for InterruptHandl vals::Pktstsd::SETUP_DATA_DONE => { trace!("SETUP_DATA_DONE ep={}", ep_num); } - x => trace!("unknown PKTSTS: {}", x.0), + x => trace!("unknown PKTSTS: {}", x.to_bits()), } } @@ -920,7 +920,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { trace!("enumdne"); let speed = r.dsts().read().enumspd(); - trace!(" speed={}", speed.0); + trace!(" speed={}", speed.to_bits()); r.gusbcfg().modify(|w| { w.set_trdt(calculate_trdt(speed, T::frequency())); diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 5907a4e54..b03e81d6e 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -49,7 +49,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { let wdg = T::regs(); wdg.kr().write(|w| w.set_key(Key::ENABLE)); - wdg.pr().write(|w| w.set_pr(Pr(pr))); + wdg.pr().write(|w| w.set_pr(Pr::from_bits(pr))); wdg.rlr().write(|w| w.set_rl(rl)); trace!( From ce889900d63f9f32f1a6b7540834d6f9558239e4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Jun 2023 02:08:55 +0200 Subject: [PATCH 1467/1575] Update rp-pac. --- embassy-rp/Cargo.toml | 2 +- embassy-rp/src/clocks.rs | 92 ++++++++++++++++++++-------------------- embassy-rp/src/gpio.rs | 4 +- embassy-rp/src/pio.rs | 4 +- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 49aa6a4d5..66823771a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -76,7 +76,7 @@ embedded-storage = { version = "0.3" } rand_core = "0.6.4" fixed = "1.23.1" -rp-pac = { version = "5" } +rp-pac = { version = "6" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 4c6223107..ddd61d224 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -46,13 +46,13 @@ static CLOCKS: Clocks = Clocks { #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum PeriClkSrc { - Sys = ClkPeriCtrlAuxsrc::CLK_SYS.0, - PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS.0, - PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB.0, - Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH.0, - Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC.0, - // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0, - // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0, + Sys = ClkPeriCtrlAuxsrc::CLK_SYS as _, + PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS as _, + PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB as _, + Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _, + Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _, + // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ , + // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , } #[non_exhaustive] @@ -251,12 +251,12 @@ pub struct SysClkConfig { #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum UsbClkSrc { - PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB.0, - PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS.0, - Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH.0, - Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC.0, - // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0, - // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0, + PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _, + PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS as _, + Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _, + Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _, + // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ , + // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ , } pub struct UsbClkConfig { @@ -269,12 +269,12 @@ pub struct UsbClkConfig { #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum AdcClkSrc { - PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB.0, - PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS.0, - Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH.0, - Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC.0, - // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0, - // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0, + PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _, + PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS as _, + Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _, + Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _, + // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ , + // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } pub struct AdcClkConfig { @@ -287,12 +287,12 @@ pub struct AdcClkConfig { #[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum RtcClkSrc { - PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB.0, - PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS.0, - Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH.0, - Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC.0, - // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0, - // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0, + PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _, + PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS as _, + Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _, + Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _, + // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ , + // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ , } pub struct RtcClkConfig { @@ -396,7 +396,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_src(ref_src); w.set_auxsrc(ref_aux); }); - while c.clk_ref_selected().read() != 1 << ref_src.0 {} + while c.clk_ref_selected().read() != 1 << ref_src as u32 {} c.clk_ref_div().write(|w| { w.set_int(config.ref_clk.div); }); @@ -425,13 +425,13 @@ pub(crate) unsafe fn init(config: ClockConfig) { CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); if sys_src != ClkSysCtrlSrc::CLK_REF { c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); - while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} + while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF as u32 {} } c.clk_sys_ctrl().write(|w| { w.set_auxsrc(sys_aux); w.set_src(sys_src); }); - while c.clk_sys_selected().read() != 1 << sys_src.0 {} + while c.clk_sys_selected().read() != 1 << sys_src as u32 {} c.clk_sys_div().write(|w| { w.set_int(config.sys_clk.div_int); w.set_frac(config.sys_clk.div_frac); @@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { if let Some(src) = config.peri_clk_src { c.clk_peri_ctrl().write(|w| { w.set_enable(true); - w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); + w.set_auxsrc(ClkPeriCtrlAuxsrc::from_bits(src as _)); }); let peri_freq = match src { PeriClkSrc::Sys => clk_sys_freq, @@ -468,7 +468,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_usb_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); + w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _)); }); let usb_freq = match conf.src { UsbClkSrc::PllUsb => pll_usb_freq, @@ -491,7 +491,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_adc_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); + w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _)); }); let adc_in_freq = match conf.src { AdcClkSrc::PllUsb => pll_usb_freq, @@ -517,7 +517,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { c.clk_rtc_ctrl().write(|w| { w.set_phase(conf.phase); w.set_enable(true); - w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); + w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _)); }); let rtc_in_freq = match conf.src { RtcClkSrc::PllUsb => pll_usb_freq, @@ -718,7 +718,7 @@ impl<'d, T: Pin> Drop for Gpin<'d, T> { self.gpin .io() .ctrl() - .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); + .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _)); } } @@ -743,17 +743,17 @@ impl_gpoutpin!(PIN_25, 3); #[repr(u8)] pub enum GpoutSrc { - PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS.0, - // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0, - // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0, - PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB.0, - Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC.0, - Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC.0, - Sys = ClkGpoutCtrlAuxsrc::CLK_SYS.0, - Usb = ClkGpoutCtrlAuxsrc::CLK_USB.0, - Adc = ClkGpoutCtrlAuxsrc::CLK_ADC.0, - Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC.0, - Ref = ClkGpoutCtrlAuxsrc::CLK_REF.0, + PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _, + // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ , + // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ , + PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB as _, + Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC as _, + Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC as _, + Sys = ClkGpoutCtrlAuxsrc::CLK_SYS as _, + Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _, + Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _, + Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _, + Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _, } pub struct Gpout<'d, T: GpoutPin> { @@ -780,7 +780,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn set_src(&self, src: GpoutSrc) { let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { - w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _)); + w.set_auxsrc(ClkGpoutCtrlAuxsrc::from_bits(src as _)); }); } @@ -831,7 +831,7 @@ impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { self.gpout .io() .ctrl() - .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); + .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _)); } } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ce0d02557..f8048a4dd 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -452,7 +452,7 @@ impl<'d, T: Pin> Flex<'d, T> { }); pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0); + w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _); }); Self { pin } @@ -618,7 +618,7 @@ impl<'d, T: Pin> Drop for Flex<'d, T> { fn drop(&mut self) { self.pin.pad_ctrl().write(|_| {}); self.pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0); + w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _); }); } } diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 1b36e0a54..30648e8ea 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -834,7 +834,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { /// of [`Pio`] do not keep pin registrations alive.** pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); - pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); + pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); Pin { @@ -998,7 +998,7 @@ fn on_pio_drop() { let state = PIO::state(); if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { let used_pins = state.used_pins.load(Ordering::Relaxed); - let null = Gpio0ctrlFuncsel::NULL.0; + let null = Gpio0ctrlFuncsel::NULL as _; // we only have 30 pins. don't test the other two since gpio() asserts. for i in 0..30 { if used_pins & (1 << i) != 0 { From f7ec579c18b2ec925afcd4ab9a840719f4ce08cc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Jun 2023 02:39:28 +0200 Subject: [PATCH 1468/1575] Update probe-rs-cli -> probe-rs --- .vscode/settings.json | 6 +++--- cyw43/README.md | 12 ++++++------ embassy-nrf/src/lib.rs | 6 +++--- examples/boot/application/nrf/.cargo/config.toml | 4 ++-- examples/boot/application/rp/.cargo/config.toml | 2 +- examples/boot/application/stm32f3/.cargo/config.toml | 4 ++-- examples/boot/application/stm32f7/.cargo/config.toml | 4 ++-- examples/boot/application/stm32h7/.cargo/config.toml | 4 ++-- examples/boot/application/stm32h7/flash-boot.sh | 2 +- examples/boot/application/stm32l0/.cargo/config.toml | 4 ++-- examples/boot/application/stm32l1/.cargo/config.toml | 4 ++-- examples/boot/application/stm32l4/.cargo/config.toml | 4 ++-- examples/boot/application/stm32wl/.cargo/config.toml | 4 ++-- examples/boot/bootloader/nrf/.cargo/config.toml | 2 +- examples/boot/bootloader/rp/.cargo/config.toml | 2 +- examples/nrf-rtos-trace/.cargo/config.toml | 4 ++-- examples/nrf52840-rtic/.cargo/config.toml | 4 ++-- examples/nrf52840/.cargo/config.toml | 4 ++-- examples/nrf52840/src/bin/nvmc.rs | 2 +- examples/nrf52840/src/bin/wdt.rs | 2 +- examples/nrf5340/.cargo/config.toml | 4 ++-- examples/rp/.cargo/config.toml | 2 +- examples/rp/src/bin/wifi_ap_tcp_server.rs | 4 ++-- examples/rp/src/bin/wifi_blinky.rs | 4 ++-- examples/rp/src/bin/wifi_scan.rs | 4 ++-- examples/rp/src/bin/wifi_tcp_server.rs | 4 ++-- examples/stm32c0/.cargo/config.toml | 4 ++-- examples/stm32f0/.cargo/config.toml | 2 +- examples/stm32f1/.cargo/config.toml | 4 ++-- examples/stm32f2/.cargo/config.toml | 4 ++-- examples/stm32f3/.cargo/config.toml | 4 ++-- examples/stm32f4/.cargo/config.toml | 4 ++-- examples/stm32f7/.cargo/config.toml | 4 ++-- examples/stm32g0/.cargo/config.toml | 4 ++-- examples/stm32g4/.cargo/config.toml | 4 ++-- examples/stm32h5/.cargo/config.toml | 2 +- examples/stm32h7/.cargo/config.toml | 2 +- examples/stm32l0/.cargo/config.toml | 4 ++-- examples/stm32l1/.cargo/config.toml | 4 ++-- examples/stm32l4/.cargo/config.toml | 8 ++++---- examples/stm32l5/.cargo/config.toml | 4 ++-- examples/stm32u5/.cargo/config.toml | 4 ++-- examples/stm32wb/.cargo/config.toml | 4 ++-- examples/stm32wl/.cargo/config.toml | 4 ++-- tests/rp/src/bin/cyw43-perf.rs | 4 ++-- 45 files changed, 88 insertions(+), 88 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ef7fe1ce..725fb69d0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,16 +6,16 @@ "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + "rust-analyzer.cargo.target": "thumbv7m-none-eabi", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - "nightly", + ///"nightly", ], "rust-analyzer.linkedProjects": [ // Declare for the target you wish to develop // "embassy-executor/Cargo.toml", // "embassy-sync/Cargo.toml", - "examples/nrf52840/Cargo.toml", + "examples/stm32wl/Cargo.toml", // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", diff --git a/cyw43/README.md b/cyw43/README.md index defea489f..e4a81410d 100644 --- a/cyw43/README.md +++ b/cyw43/README.md @@ -1,6 +1,6 @@ # cyw43 -WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver). +Rust driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver). ## Current status @@ -19,18 +19,18 @@ Working: TODO: - Setting a custom MAC address. -- Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) +- Bus sleep (for power consumption optimization) ## Running the examples -- `cargo install probe-rs-cli` -- `cd examples/rpi-pico-w` +- `cargo install probe-rs --features cli` +- `cd examples/rp` ### Example 1: Scan the wifi stations - `cargo run --release --bin wifi_scan` ### Example 2: Create an access point (IP and credentials in the code) -- `cargo run --release --bin tcp_server_ap` +- `cargo run --release --bin wifi_ap_tcp_server` ### Example 3: Connect to an existing network and create a server -- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release` +- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server` After a few seconds, you should see that DHCP picks up an IP address like this ``` diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 691545662..d23759f9d 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -410,13 +410,13 @@ pub fn init(config: config::Config) -> Peripherals { warn!( "You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\ However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ - To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." + To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`." ); #[cfg(feature = "reset-pin-as-gpio")] warn!( "You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\ However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ - To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." + To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`." ); } } @@ -432,7 +432,7 @@ pub fn init(config: config::Config) -> Peripherals { warn!( "You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\ However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ - To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." + To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`." ); } } diff --git a/examples/boot/application/nrf/.cargo/config.toml b/examples/boot/application/nrf/.cargo/config.toml index 3872e7189..17616a054 100644 --- a/examples/boot/application/nrf/.cargo/config.toml +++ b/examples/boot/application/nrf/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/examples/boot/application/rp/.cargo/config.toml b/examples/boot/application/rp/.cargo/config.toml index 278c24a57..cd8d1ef02 100644 --- a/examples/boot/application/rp/.cargo/config.toml +++ b/examples/boot/application/rp/.cargo/config.toml @@ -3,7 +3,7 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-rs-cli run --chip RP2040" +runner = "probe-rs run --chip RP2040" [build] target = "thumbv6m-none-eabi" diff --git a/examples/boot/application/stm32f3/.cargo/config.toml b/examples/boot/application/stm32f3/.cargo/config.toml index 9fc2396e8..4a7ec0a5b 100644 --- a/examples/boot/application/stm32f3/.cargo/config.toml +++ b/examples/boot/application/stm32f3/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F303VCTx" +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F303VCTx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32f7/.cargo/config.toml b/examples/boot/application/stm32f7/.cargo/config.toml index 7d6c88a99..9088eea6e 100644 --- a/examples/boot/application/stm32f7/.cargo/config.toml +++ b/examples/boot/application/stm32f7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F767ZITx" +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F767ZITx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32h7/.cargo/config.toml b/examples/boot/application/stm32h7/.cargo/config.toml index 067a5c89b..caa0d3a93 100644 --- a/examples/boot/application/stm32h7/.cargo/config.toml +++ b/examples/boot/application/stm32h7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32H743ZITx" +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32H743ZITx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32h7/flash-boot.sh b/examples/boot/application/stm32h7/flash-boot.sh index a3003681a..4912a50b7 100755 --- a/examples/boot/application/stm32h7/flash-boot.sh +++ b/examples/boot/application/stm32h7/flash-boot.sh @@ -1,5 +1,5 @@ #!/bin/bash -probe-rs-cli erase --chip STM32H743ZITx +probe-rs erase --chip STM32H743ZITx mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x cp memory-bl.x ../../bootloader/stm32/memory.x diff --git a/examples/boot/application/stm32l0/.cargo/config.toml b/examples/boot/application/stm32l0/.cargo/config.toml index ce0e460bd..6099f015c 100644 --- a/examples/boot/application/stm32l0/.cargo/config.toml +++ b/examples/boot/application/stm32l0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32L072CZTx" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L072CZTx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/boot/application/stm32l1/.cargo/config.toml b/examples/boot/application/stm32l1/.cargo/config.toml index 1401500a0..9cabd14ba 100644 --- a/examples/boot/application/stm32l1/.cargo/config.toml +++ b/examples/boot/application/stm32l1/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32L151CBxxA" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L151CBxxA" [build] target = "thumbv7m-none-eabi" diff --git a/examples/boot/application/stm32l4/.cargo/config.toml b/examples/boot/application/stm32l4/.cargo/config.toml index 48ff3734b..c803215f6 100644 --- a/examples/boot/application/stm32l4/.cargo/config.toml +++ b/examples/boot/application/stm32l4/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32L475VG" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L475VG" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/application/stm32wl/.cargo/config.toml b/examples/boot/application/stm32wl/.cargo/config.toml index b49b582e0..4f8094ff2 100644 --- a/examples/boot/application/stm32wl/.cargo/config.toml +++ b/examples/boot/application/stm32wl/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32WLE5JCIx" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32WLE5JCIx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/boot/bootloader/nrf/.cargo/config.toml b/examples/boot/bootloader/nrf/.cargo/config.toml index d636b1d23..c292846aa 100644 --- a/examples/boot/bootloader/nrf/.cargo/config.toml +++ b/examples/boot/bootloader/nrf/.cargo/config.toml @@ -4,7 +4,7 @@ build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] #runner = "./fruitrunner" -runner = "probe-rs-cli run --chip nrf52840_xxAA" +runner = "probe-rs run --chip nrf52840_xxAA" rustflags = [ # Code-size optimizations. diff --git a/examples/boot/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml index 795ee043a..9d48ecdc9 100644 --- a/examples/boot/bootloader/rp/.cargo/config.toml +++ b/examples/boot/bootloader/rp/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-rs-cli run --chip RP2040" +runner = "probe-rs run --chip RP2040" [build] target = "thumbv6m-none-eabi" diff --git a/examples/nrf-rtos-trace/.cargo/config.toml b/examples/nrf-rtos-trace/.cargo/config.toml index 3872e7189..17616a054 100644 --- a/examples/nrf-rtos-trace/.cargo/config.toml +++ b/examples/nrf-rtos-trace/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/examples/nrf52840-rtic/.cargo/config.toml b/examples/nrf52840-rtic/.cargo/config.toml index 3872e7189..17616a054 100644 --- a/examples/nrf52840-rtic/.cargo/config.toml +++ b/examples/nrf52840-rtic/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/examples/nrf52840/.cargo/config.toml b/examples/nrf52840/.cargo/config.toml index 3872e7189..17616a054 100644 --- a/examples/nrf52840/.cargo/config.toml +++ b/examples/nrf52840/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip nRF52840_xxAA" +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF52840_xxAA" [build] target = "thumbv7em-none-eabi" diff --git a/examples/nrf52840/src/bin/nvmc.rs b/examples/nrf52840/src/bin/nvmc.rs index 33a44516d..31c6fe4b6 100644 --- a/examples/nrf52840/src/bin/nvmc.rs +++ b/examples/nrf52840/src/bin/nvmc.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Hello NVMC!"); - // probe-rs-cli run breaks without this, I'm not sure why. + // probe-rs run breaks without this, I'm not sure why. Timer::after(Duration::from_secs(1)).await; let mut f = Nvmc::new(p.NVMC); diff --git a/examples/nrf52840/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs index ccfd0e439..058746518 100644 --- a/examples/nrf52840/src/bin/wdt.rs +++ b/examples/nrf52840/src/bin/wdt.rs @@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.timeout_ticks = 32768 * 3; // 3 seconds - // This is needed for `probe-rs-cli run` to be able to catch the panic message + // This is needed for `probe-rs run` to be able to catch the panic message // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. config.run_during_debug_halt = false; diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml index d25355894..4c3cf3d32 100644 --- a/examples/nrf5340/.cargo/config.toml +++ b/examples/nrf5340/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace nRF5340_xxAA with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip nRF5340_xxAA" +# replace nRF5340_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF5340_xxAA" [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml index 2ee6fcb00..3d7d61740 100644 --- a/examples/rp/.cargo/config.toml +++ b/examples/rp/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-rs-cli run --chip RP2040" +runner = "probe-rs run --chip RP2040" [build] target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index e8197390c..310e84d92 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -42,8 +42,8 @@ async fn main(spawner: Spawner) { // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: - // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 - // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index be965807b..bbcb1b5ec 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -27,8 +27,8 @@ async fn main(spawner: Spawner) { // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: - // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 - // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index 79534f229..391e12282 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -39,8 +39,8 @@ async fn main(spawner: Spawner) { // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: - // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 - // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 026e056fa..e9d1079a6 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -42,8 +42,8 @@ async fn main(spawner: Spawner) { // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: - // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 - // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; diff --git a/examples/stm32c0/.cargo/config.toml b/examples/stm32c0/.cargo/config.toml index 517101fae..29a8be7e1 100644 --- a/examples/stm32c0/.cargo/config.toml +++ b/examples/stm32c0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --speed 100 --chip STM32c031c6tx" +# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --speed 100 --chip STM32c031c6tx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32f0/.cargo/config.toml b/examples/stm32f0/.cargo/config.toml index bd0c0cd97..def4c8c92 100644 --- a/examples/stm32f0/.cargo/config.toml +++ b/examples/stm32f0/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv6m-none-eabi] -runner = 'probe-rs-cli run --chip STM32F091RCTX' +runner = 'probe-rs run --chip STM32F091RCTX' [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32f1/.cargo/config.toml b/examples/stm32f1/.cargo/config.toml index 81199c5aa..ce6fef11b 100644 --- a/examples/stm32f1/.cargo/config.toml +++ b/examples/stm32f1/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F103C8 with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F103C8" +# replace STM32F103C8 with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F103C8" [build] target = "thumbv7m-none-eabi" diff --git a/examples/stm32f2/.cargo/config.toml b/examples/stm32f2/.cargo/config.toml index 5532779c8..1198fcab8 100644 --- a/examples/stm32f2/.cargo/config.toml +++ b/examples/stm32f2/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F207ZGTx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F207ZGTx" +# replace STM32F207ZGTx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F207ZGTx" [build] target = "thumbv7m-none-eabi" diff --git a/examples/stm32f3/.cargo/config.toml b/examples/stm32f3/.cargo/config.toml index 7f3fda529..cb8a7c5af 100644 --- a/examples/stm32f3/.cargo/config.toml +++ b/examples/stm32f3/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F303ZETx" +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F303ZETx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32f4/.cargo/config.toml b/examples/stm32f4/.cargo/config.toml index bed04b68f..16efa8e6f 100644 --- a/examples/stm32f4/.cargo/config.toml +++ b/examples/stm32f4/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F429ZITx" +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F429ZITx" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml index 7d6c88a99..9088eea6e 100644 --- a/examples/stm32f7/.cargo/config.toml +++ b/examples/stm32f7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F767ZITx" +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32F767ZITx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32g0/.cargo/config.toml b/examples/stm32g0/.cargo/config.toml index a7a5fbd84..35cca5412 100644 --- a/examples/stm32g0/.cargo/config.toml +++ b/examples/stm32g0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32G071RBTx" +# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32G071RBTx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32g4/.cargo/config.toml b/examples/stm32g4/.cargo/config.toml index 606d7d5a3..d28ad069e 100644 --- a/examples/stm32g4/.cargo/config.toml +++ b/examples/stm32g4/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32G484VETx" +# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32G484VETx" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32h5/.cargo/config.toml b/examples/stm32h5/.cargo/config.toml index c8b864b6c..478146142 100644 --- a/examples/stm32h5/.cargo/config.toml +++ b/examples/stm32h5/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv8m.main-none-eabihf] -runner = 'probe-rs-cli run --chip STM32H563ZITx' +runner = 'probe-rs run --chip STM32H563ZITx' [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml index f08f57a54..5f680dbce 100644 --- a/examples/stm32h7/.cargo/config.toml +++ b/examples/stm32h7/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv7em-none-eabihf] -runner = 'probe-rs-cli run --chip STM32H743ZITx' +runner = 'probe-rs run --chip STM32H743ZITx' [build] target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml index 526f5a1f7..b050334b2 100644 --- a/examples/stm32l0/.cargo/config.toml +++ b/examples/stm32l0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32L053R8Tx" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L053R8Tx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32l1/.cargo/config.toml b/examples/stm32l1/.cargo/config.toml index 1401500a0..9cabd14ba 100644 --- a/examples/stm32l1/.cargo/config.toml +++ b/examples/stm32l1/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32L151CBxxA" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L151CBxxA" [build] target = "thumbv7m-none-eabi" diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index abf55eb2e..36e74e5a5 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -1,8 +1,8 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -#runner = "probe-rs-cli run --chip STM32L475VGT6" -#runner = "probe-rs-cli run --chip STM32L475VG" -runner = "probe-rs-cli run --chip STM32L4S5VI" +# replace STM32F429ZITx with your chip as listed in `probe-rs chip list` +#runner = "probe-rs run --chip STM32L475VGT6" +#runner = "probe-rs run --chip STM32L475VG" +runner = "probe-rs run --chip STM32L4S5VI" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l5/.cargo/config.toml b/examples/stm32l5/.cargo/config.toml index 1dc3a6fb7..86a145a27 100644 --- a/examples/stm32l5/.cargo/config.toml +++ b/examples/stm32l5/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32L552ZETxQ with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32L552ZETxQ" +# replace STM32L552ZETxQ with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32L552ZETxQ" [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/stm32u5/.cargo/config.toml b/examples/stm32u5/.cargo/config.toml index cecd01938..36c5b63a6 100644 --- a/examples/stm32u5/.cargo/config.toml +++ b/examples/stm32u5/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32U585AIIx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32U585AIIx" +# replace STM32U585AIIx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32U585AIIx" [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 35317a297..8b6d6d754 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list` -# runner = "probe-rs-cli run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" +# replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` +# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" runner = "teleprobe local run --chip STM32WB55RG --elf" [build] diff --git a/examples/stm32wl/.cargo/config.toml b/examples/stm32wl/.cargo/config.toml index b49b582e0..4f8094ff2 100644 --- a/examples/stm32wl/.cargo/config.toml +++ b/examples/stm32wl/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32WLE5JCIx" +# replace your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32WLE5JCIx" [build] target = "thumbv7em-none-eabihf" diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 9fc537a4b..1ecaab266 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -44,8 +44,8 @@ async fn main(spawner: Spawner) { } // cyw43 firmware needs to be flashed manually: - // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000 - // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000 + // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000 let fw = unsafe { core::slice::from_raw_parts(0x101c0000 as *const u8, 224190) }; let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 4752) }; From 01101e3df0619dca85ab692be8b7c38f249b44e8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Jun 2023 03:11:22 +0200 Subject: [PATCH 1469/1575] Update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 315d247ee..b05e55aa5 100644 --- a/README.md +++ b/README.md @@ -99,10 +99,10 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer ### Running examples -- Install `probe-rs-cli` with defmt support. +- Install `probe-rs`. ```bash -cargo install probe-rs-cli +cargo install probe-rs --features cli ``` - Change directory to the sample's base directory. For example: From 96f1525ffe675b7e3ca26f038bc558488c03af9b Mon Sep 17 00:00:00 2001 From: Julian <20155974+JuliDi@users.noreply.github.com> Date: Thu, 29 Jun 2023 09:20:25 +0200 Subject: [PATCH 1470/1575] Revert changes to dma.rs --- embassy-stm32/src/dma/dma.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9c03599eb..8abe541d3 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -405,11 +405,7 @@ impl<'a, C: Channel> Transfer<'a, C> { pub fn is_running(&mut self) -> bool { let ch = self.channel.regs().st(self.channel.num()); - let en = ch.cr().read().en(); - // Check if circular mode is enabled, if so it will still be running even if tcif == 1 - let circular = ch.cr().read().circ() == vals::Circ::ENABLED; - let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; - en && (circular || !tcif) + ch.cr().read().en() } /// Gets the total remaining transfers for the channel From f0b17675d89f87350faa8c61b491be53ef666894 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Jun 2023 12:20:51 +0200 Subject: [PATCH 1471/1575] usb: add missing builder reexports. Fixes #1176 cc #1596 --- embassy-usb/src/class/cdc_ncm/mod.rs | 4 ++-- embassy-usb/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index d5556dd0b..fcfa0bfcd 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -11,8 +11,8 @@ //! - On Pixel 4a, it refused to work on Android 11, worked on Android 12. //! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), //! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. -//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 -//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 +//! This is due to regex spaghetti: +//! and this nonsense in the linux kernel: use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index d8563d59a..1180b9b66 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -23,7 +23,7 @@ mod config { use embassy_futures::select::{select, Either}; use heapless::Vec; -pub use crate::builder::{Builder, Config}; +pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder}; use crate::config::*; use crate::control::*; use crate::descriptor::*; From 837d3bcdbb87af69d13cb799a137ad88ee015538 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 29 Jun 2023 14:43:31 +0200 Subject: [PATCH 1472/1575] embassy-boot/nrf/README.md: typo fix --- embassy-boot/nrf/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/nrf/README.md b/embassy-boot/nrf/README.md index 7ce3c7021..fe581823d 100644 --- a/embassy-boot/nrf/README.md +++ b/embassy-boot/nrf/README.md @@ -6,7 +6,7 @@ An adaptation of `embassy-boot` for nRF. ## Features -* Load applications with our without the softdevice. +* Load applications with or without the softdevice. * Configure bootloader partitions based on linker script. * Using watchdog timer to detect application failure. From 2aa2b843ce6b96aacd72e52fe29b550e7ebc55b3 Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 29 Jun 2023 17:11:36 +0200 Subject: [PATCH 1473/1575] feature(1355): Add trigger to task, triggered + clear to Event --- embassy-nrf/src/ppi/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 7c18da6ee..ba95849e8 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -137,6 +137,11 @@ impl Task { Self(ptr) } + // Triggers this task. + pub unsafe fn trigger(&mut self) { + self.0.write(|w| unsafe { w.bits(1) }); + } + pub(crate) fn from_reg(reg: &T) -> Self { Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) } @@ -173,6 +178,16 @@ impl Event { Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) } + // Describes whether this Event is currently in a triggered state. + pub unsafe fn is_triggered(&self) -> bool { + self.0.read().bits() == 1 + } + + // Clear the current register's triggered state, reverting it to 0. + pub unsafe fn clear(&mut self) { + self.0.write(|w| unsafe { w.bits(0) }); + } + /// Address of publish register for this event. #[cfg(feature = "_dppi")] pub fn publish_reg(&self) -> *mut u32 { From e90f47aba31be9d99935c758d87941c7106cbece Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 29 Jun 2023 17:37:51 +0200 Subject: [PATCH 1474/1575] Fixed Pointer Updates --- embassy-nrf/src/ppi/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index ba95849e8..0789e2694 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -137,9 +137,9 @@ impl Task { Self(ptr) } - // Triggers this task. + /// Triggers this task. pub unsafe fn trigger(&mut self) { - self.0.write(|w| unsafe { w.bits(1) }); + *self.0.as_ptr() = 1; } pub(crate) fn from_reg(reg: &T) -> Self { @@ -178,14 +178,14 @@ impl Event { Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) } - // Describes whether this Event is currently in a triggered state. + /// Describes whether this Event is currently in a triggered state. pub unsafe fn is_triggered(&self) -> bool { - self.0.read().bits() == 1 + *self.0.as_ptr() == 1 } - // Clear the current register's triggered state, reverting it to 0. + /// Clear the current register's triggered state, reverting it to 0. pub unsafe fn clear(&mut self) { - self.0.write(|w| unsafe { w.bits(0) }); + *self.0.as_ptr() = 0; } /// Address of publish register for this event. From 3f19879f41e8c39c5c38e7905a180160b24807fc Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 29 Jun 2023 17:44:46 +0200 Subject: [PATCH 1475/1575] PR Fixes --- embassy-nrf/src/ppi/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 0789e2694..76757a248 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -138,8 +138,8 @@ impl Task { } /// Triggers this task. - pub unsafe fn trigger(&mut self) { - *self.0.as_ptr() = 1; + pub fn trigger(&mut self) { + unsafe { self.0.as_ptr().write_volatile(1) }; } pub(crate) fn from_reg(reg: &T) -> Self { @@ -179,13 +179,13 @@ impl Event { } /// Describes whether this Event is currently in a triggered state. - pub unsafe fn is_triggered(&self) -> bool { - *self.0.as_ptr() == 1 + pub fn is_triggered(&self) -> bool { + unsafe { self.0.as_ptr().read_volatile() == 1 } } /// Clear the current register's triggered state, reverting it to 0. - pub unsafe fn clear(&mut self) { - *self.0.as_ptr() = 0; + pub fn clear(&mut self) { + unsafe { self.0.as_ptr().write_volatile(0) }; } /// Address of publish register for this event. From 24e186e6849223ee076e0fabdfed07d2cd7f7baf Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 29 Jun 2023 18:09:26 +0200 Subject: [PATCH 1476/1575] feature(1354): Added lifetimes to Event + --- embassy-nrf/src/ppi/mod.rs | 29 ++++++++++++++++++----------- embassy-nrf/src/ppi/ppi.rs | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 76757a248..f6771c2a3 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -15,6 +15,7 @@ //! many tasks and events, but any single task or event can only be coupled with one channel. //! +use core::marker::PhantomData; use core::ptr::NonNull; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; @@ -125,16 +126,16 @@ const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::(); /// When a task is subscribed to a PPI channel, it will run when the channel is triggered by /// a published event. #[derive(PartialEq, Eq, Clone, Copy)] -pub struct Task(NonNull); +pub struct Task<'d>(NonNull, PhantomData<&'d ()>); -impl Task { +impl<'d> Task<'_> { /// Create a new `Task` from a task register pointer /// /// # Safety /// /// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral. pub unsafe fn new_unchecked(ptr: NonNull) -> Self { - Self(ptr) + Self(ptr, PhantomData) } /// Triggers this task. @@ -143,7 +144,10 @@ impl Task { } pub(crate) fn from_reg(reg: &T) -> Self { - Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) + Self( + unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }, + PhantomData, + ) } /// Address of subscription register for this task. @@ -156,26 +160,29 @@ impl Task { /// # Safety /// /// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core. -unsafe impl Send for Task {} +unsafe impl Send for Task<'_> {} /// Represents an event that a peripheral can publish. /// /// An event can be set to publish on a PPI channel when the event happens. #[derive(PartialEq, Eq, Clone, Copy)] -pub struct Event(NonNull); +pub struct Event<'d>(NonNull, PhantomData<&'d ()>); -impl Event { +impl<'d> Event<'_> { /// Create a new `Event` from an event register pointer /// /// # Safety /// /// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral. pub unsafe fn new_unchecked(ptr: NonNull) -> Self { - Self(ptr) + Self(ptr, PhantomData) } - pub(crate) fn from_reg(reg: &T) -> Self { - Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) + pub(crate) fn from_reg(reg: &'d T) -> Self { + Self( + unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }, + PhantomData, + ) } /// Describes whether this Event is currently in a triggered state. @@ -198,7 +205,7 @@ impl Event { /// # Safety /// /// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core. -unsafe impl Send for Event {} +unsafe impl Send for Event<'_> {} // ====================== // traits diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index f1eeaee1e..6e8a669d3 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -3,12 +3,12 @@ use embassy_hal_common::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task}; use crate::{pac, Peripheral}; -impl Task { +impl<'d> Task<'_> { fn reg_val(&self) -> u32 { self.0.as_ptr() as _ } } -impl Event { +impl<'d> Event<'_> { fn reg_val(&self) -> u32 { self.0.as_ptr() as _ } From fef338f5c2d21ad1f45247579b27ffdf33f1096f Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 29 Jun 2023 18:13:46 +0200 Subject: [PATCH 1477/1575] Lifetime groups --- embassy-nrf/src/ppi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index f6771c2a3..9092529ac 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -31,9 +31,9 @@ pub(crate) use _version::*; pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> { ch: PeripheralRef<'d, C>, #[cfg(feature = "_dppi")] - events: [Event; EVENT_COUNT], + events: [Event<'d>; EVENT_COUNT], #[cfg(feature = "_dppi")] - tasks: [Task; TASK_COUNT], + tasks: [Task<'d>; TASK_COUNT], } /// PPI channel group driver. From 2432cece38750220c04fe692d7153d1776220222 Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 29 Jun 2023 18:36:12 +0200 Subject: [PATCH 1478/1575] Lifetimes in dppi --- embassy-nrf/src/ppi/dppi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 3a1e7f170..36c1eb8e6 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -30,8 +30,8 @@ impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usi /// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires. pub fn new_many_to_many( ch: impl Peripheral

+ 'd, - events: [Event; EVENT_COUNT], - tasks: [Task; TASK_COUNT], + events: [Event<'d>; EVENT_COUNT], + tasks: [Task<'d>; TASK_COUNT], ) -> Self { into_ref!(ch); From 6eac49186d5a5da4c310027e59adcd0bf44ae514 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Jun 2023 19:51:16 +0200 Subject: [PATCH 1479/1575] Release embassy-net v0.1 --- embassy-net-driver-channel/Cargo.toml | 11 +++ embassy-net-driver-channel/README.md | 96 ++++++++++++++++++++ embassy-net-driver-channel/src/lib.rs | 1 + embassy-net-driver/Cargo.toml | 11 ++- embassy-net-driver/README.md | 16 ++++ embassy-net-w5500/src/lib.rs | 5 +- embassy-net/Cargo.toml | 9 +- embassy-net/README.md | 64 +++++++++---- embassy-net/src/lib.rs | 24 ++++- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 5 +- examples/std/README.md | 23 +++++ 11 files changed, 238 insertions(+), 27 deletions(-) create mode 100644 embassy-net-driver-channel/README.md create mode 100644 examples/std/README.md diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index e475551e1..bee2e3021 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -2,6 +2,14 @@ name = "embassy-net-driver-channel" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" +description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack." +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-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/" @@ -9,6 +17,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d features = ["defmt"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["defmt"] + [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-net-driver-channel/README.md b/embassy-net-driver-channel/README.md new file mode 100644 index 000000000..dd90e7ad2 --- /dev/null +++ b/embassy-net-driver-channel/README.md @@ -0,0 +1,96 @@ +# embassy-net-driver-channel + +This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a +higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly. + +The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by +hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net` +knows when to poll your driver again to make more progress. + +With `embassy-net-driver-channel` + +## A note about deadlocks + +When implementing a driver using this crate, it might be tempting to write it in the most straightforward way: + +```rust,ignore +loop { + // Wait for either.. + match select( + // ... the chip signaling an interrupt, indicating a packet is available to receive, or + irq_pin.wait_for_low(), + // ... a TX buffer becoming available, i.e. embassy-net wants to send a packet + tx_chan.tx_buf(), + ).await { + Either::First(_) => { + // a packet is ready to be received! + let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue + let n = receive_packet_over_spi(buf).await; + rx_chan.rx_done(n); + } + Either::Second(buf) => { + // a packet is ready to be sent! + send_packet_over_spi(buf).await; + tx_chan.tx_done(); + } + } +} +``` + +However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load. + +The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue. + +The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available": + +```rust,ignore +loop { + // Wait for either.. + match select( + async { + // ... the chip signaling an interrupt, indicating a packet is available to receive + irq_pin.wait_for_low().await; + // *AND* the buffer is ready... + rx_chan.rx_buf().await + }, + // ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet + tx_chan.tx_buf(), + ).await { + Either::First(buf) => { + // a packet is ready to be received! + let n = receive_packet_over_spi(buf).await; + rx_chan.rx_done(n); + } + Either::Second(buf) => { + // a packet is ready to be sent! + send_packet_over_spi(buf).await; + tx_chan.tx_done(); + } + } +} +``` + +## Examples + +These `embassy-net` drivers are implemented using this crate. You can look at them for inspiration. + +- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W +- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. +- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. + + +## Interoperability + +This crate can run on any executor. + + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 0c8dcc22b..02a4c00d6 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![doc = include_str!("../README.md")] // must go first! mod fmt; diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index ff6f29355..da6d9ad62 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -3,7 +3,13 @@ name = "embassy-net-driver" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" - +description = "Driver trait for the `embassy-net` async TCP/IP network stack." +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-net-driver-v$VERSION/embassy-net-driver/src/" @@ -11,5 +17,8 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d features = ["defmt"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["defmt"] + [dependencies] defmt = { version = "0.3", optional = true } \ No newline at end of file diff --git a/embassy-net-driver/README.md b/embassy-net-driver/README.md index 84f25492d..6a757380d 100644 --- a/embassy-net-driver/README.md +++ b/embassy-net-driver/README.md @@ -1,5 +1,21 @@ # embassy-net-driver +This crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support +for a new hardware platform. + +If you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate. + +If you are writing a driver, you should depend only on this crate, not on the main `embassy-net` crate. +This will allow your driver to continue working for newer `embassy-net` major versions, without needing an update, +if the driver trait has not had breaking changes. + +See also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API +to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via +packet queues for RX and TX. + +## Interoperability + +This crate can run on any executor. ## License diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 6821373e3..efd9bed66 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -1,5 +1,6 @@ +//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. #![no_std] -/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. + mod device; mod socket; mod spi; @@ -77,7 +78,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } } -/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net). +/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index cef8247eb..e89039daa 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -3,7 +3,13 @@ name = "embassy-net" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" - +description = "Async TCP/IP network stack for embedded systems" +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-net-v$VERSION/embassy-net/src/" @@ -44,7 +50,6 @@ smoltcp = { version = "0.10.0", default-features = false, features = [ ] } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-net/README.md b/embassy-net/README.md index 470926c58..48f9fd832 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -1,30 +1,56 @@ # embassy-net -embassy-net contains an async network API based on smoltcp and embassy, designed -for embedded systems. +`embassy-net` is a no-std no-alloc async network stack, designed for embedded systems. -## Running the example +It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated +API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and +memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience. -First, create the tap0 interface. You only need to do this once. +## Features -```sh -sudo ip tuntap add name tap0 mode tap user $USER -sudo ip link set tap0 up -sudo ip addr add 192.168.69.100/24 dev tap0 -sudo ip -6 addr add fe80::100/64 dev tap0 -sudo ip -6 addr add fdaa::100/64 dev tap0 -sudo ip -6 route add fe80::/64 dev tap0 -sudo ip -6 route add fdaa::/64 dev tap0 -``` +- IPv4, IPv6 +- Ethernet and bare-IP mediums. +- TCP, UDP, DNS, DHCPv4, IGMPv4 +- TCP sockets implement the `embedded-io` async traits. -Second, have something listening there. For example `nc -l 8000` +See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and +unimplemented features of the network protocols. -Then run the example located in the `examples` folder: +## Hardware support -```sh -cd $EMBASSY_ROOT/examples/std/ -cargo run --bin net -- --static-ip -``` +- [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif. +- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W +- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. +- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). +- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. + +## Examples + +- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`. +- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips. +- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin) + +## Adding support for new hardware + +To add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or +an Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) +traits. + +Alternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higer-level API +to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via +packet queues for RX and TX. + +Drivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate. +This allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver +trait has not had breaking changes. + +## Interoperability + +This crate can run on any executor. + +[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must +link an `embassy-time` driver in your project to use this crate. ## License diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 17a7a22a2..840d7a09a 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -419,7 +419,29 @@ impl Stack { }) .await?; - use embassy_hal_common::drop::OnDrop; + #[must_use = "to delay the drop handler invocation to the end of the scope"] + struct OnDrop { + f: core::mem::MaybeUninit, + } + + impl OnDrop { + fn new(f: F) -> Self { + Self { + f: core::mem::MaybeUninit::new(f), + } + } + + fn defuse(self) { + core::mem::forget(self) + } + } + + impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } + } + let drop = OnDrop::new(|| { self.with_mut(|s, i| { let socket = s.sockets.get_mut::(i.dns_socket); diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index bc79b3671..670709021 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -1,4 +1,5 @@ -//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class. +//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class. + use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; @@ -79,7 +80,7 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { - /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net). + /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](https://crates.io/crates/embassy-net). pub fn into_embassy_net_device( self, state: &'d mut State, diff --git a/examples/std/README.md b/examples/std/README.md new file mode 100644 index 000000000..adc795928 --- /dev/null +++ b/examples/std/README.md @@ -0,0 +1,23 @@ + +## Running the `embassy-net` examples + +First, create the tap0 interface. You only need to do this once. + +```sh +sudo ip tuntap add name tap0 mode tap user $USER +sudo ip link set tap0 up +sudo ip addr add 192.168.69.100/24 dev tap0 +sudo ip -6 addr add fe80::100/64 dev tap0 +sudo ip -6 addr add fdaa::100/64 dev tap0 +sudo ip -6 route add fe80::/64 dev tap0 +sudo ip -6 route add fdaa::/64 dev tap0 +``` + +Second, have something listening there. For example `nc -l 8000` + +Then run the example located in the `examples` folder: + +```sh +cd $EMBASSY_ROOT/examples/std/ +cargo run --bin net -- --static-ip +``` \ No newline at end of file From d6fde756a8c5f01e2557165fd2bba457143a3586 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 30 Jun 2023 11:32:11 +0200 Subject: [PATCH 1480/1575] Build failures --- embassy-nrf/src/ppi/dppi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 36c1eb8e6..af2267453 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -20,7 +20,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { /// Configure PPI channel to trigger both `task1` and `task2` on `event`. pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { - Ppi::new_many_to_many(ch, [event], [task1, task2]) + Ppi<'d>::new_many_to_many(ch, [event], [task1, task2]) } } From 4d23ea554b73f377c1a2c2aca15c4284b188231c Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 30 Jun 2023 11:34:13 +0200 Subject: [PATCH 1481/1575] Build failures --- embassy-nrf/src/ppi/dppi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index af2267453..c0d6f228e 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -19,8 +19,8 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { /// Configure PPI channel to trigger both `task1` and `task2` on `event`. - pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { - Ppi<'d>::new_many_to_many(ch, [event], [task1, task2]) + pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self { + Ppi::new_many_to_many(ch, [event], [task1, task2]) } } From c69f2929c0702701d49b3cd9dcc14d5a6664f5dc Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 30 Jun 2023 11:37:53 +0200 Subject: [PATCH 1482/1575] Build failures --- embassy-nrf/src/ppi/dppi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index c0d6f228e..40ccb2f09 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -12,7 +12,7 @@ pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock { impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { /// Configure PPI channel to trigger `task` on `event`. - pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event, task: Task) -> Self { + pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event<'d>, task: Task<'d>) -> Self { Ppi::new_many_to_many(ch, [event], [task]) } } From 81cbb0fc322c8dd23128a28e0500df1f185e1067 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 30 Jun 2023 11:47:20 +0200 Subject: [PATCH 1483/1575] Attempt to fix certain borrowing rule issues --- embassy-nrf/src/buffered_uarte.rs | 4 +++- embassy-nrf/src/saadc.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 9bc1c1e7a..c1300d7ef 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -352,11 +352,13 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); let mut ppi_group = PpiGroup::new(ppi_group); + let ppi_group_channel_disable_all_task = ppi_group.task_disable_all(); + let mut ppi_ch2 = Ppi::new_one_to_two( ppi_ch2, Event::from_reg(&r.events_endrx), Task::from_reg(&r.tasks_startrx), - ppi_group.task_disable_all(), + ppi_group_channel_disable_all_task ); ppi_ch2.disable(); ppi_group.add_channel(&ppi_ch2); diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index cf3fb9993..b6984b0f0 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -320,7 +320,9 @@ impl<'d, const N: usize> Saadc<'d, N> { timer.cc(0).write(sample_counter); timer.cc(0).short_compare_clear(); - let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer.cc(0).event_compare(), Task::from_reg(&r.tasks_sample)); + let compare_event = timer.cc(0).event_compare(); + + let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, compare_event, Task::from_reg(&r.tasks_sample)); timer.start(); From bca2c549482c05954b20021ec976609bb607eed3 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 30 Jun 2023 11:50:27 +0200 Subject: [PATCH 1484/1575] Adjusted build issue --- embassy-nrf/src/saadc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index b6984b0f0..23292924c 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -320,9 +320,9 @@ impl<'d, const N: usize> Saadc<'d, N> { timer.cc(0).write(sample_counter); timer.cc(0).short_compare_clear(); - let compare_event = timer.cc(0).event_compare(); + let timer_cc = timer.cc(0); - let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, compare_event, Task::from_reg(&r.tasks_sample)); + let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(&r.tasks_sample)); timer.start(); From 93caf97a04d1383ddeb11cff51b3ca34107a45c8 Mon Sep 17 00:00:00 2001 From: Cameron Date: Fri, 30 Jun 2023 11:54:37 +0200 Subject: [PATCH 1485/1575] Formatting stuff --- embassy-nrf/src/buffered_uarte.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c1300d7ef..8fc3b4b9b 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -352,13 +352,13 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); let mut ppi_group = PpiGroup::new(ppi_group); - let ppi_group_channel_disable_all_task = ppi_group.task_disable_all(); + let ppi_group_channel_disable_all_task = ppi_group.task_disable_all(); let mut ppi_ch2 = Ppi::new_one_to_two( ppi_ch2, Event::from_reg(&r.events_endrx), Task::from_reg(&r.tasks_startrx), - ppi_group_channel_disable_all_task + ppi_group_channel_disable_all_task, ); ppi_ch2.disable(); ppi_group.add_channel(&ppi_ch2); From d372df7ddb381571fd2964e32b486b6d1cd1ad03 Mon Sep 17 00:00:00 2001 From: Mathias Date: Sat, 1 Jul 2023 12:16:23 +0200 Subject: [PATCH 1486/1575] L4: Switch to MSI to prevent problems with PLL configuration, and enable power to AHB bus clock to allow RTC to run --- embassy-stm32/src/rcc/l4.rs | 23 +++++++++++++++ examples/stm32l4/src/bin/rtc.rs | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 examples/stm32l4/src/bin/rtc.rs diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index f8c1a6e06..f7f3b9046 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,6 +1,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; +use stm32_metapac::rcc::regs::Cfgr; use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; use crate::gpio::sealed::AFType; @@ -439,6 +440,26 @@ impl<'d, T: McoInstance> Mco<'d, T> { } pub(crate) unsafe fn init(config: Config) { + // Switch to MSI to prevent problems with PLL configuration. + if !RCC.cr().read().msion() { + // Turn on MSI and configure it to 4MHz. + RCC.cr().modify(|w| { + w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0]. + w.set_msirange(MSIRange::default().into()); + w.set_msipllen(false); + w.set_msion(true) + }); + + // Wait until MSI is running + while !RCC.cr().read().msirdy() {} + } + if RCC.cfgr().read().sws() != Sw::MSI { + // Set MSI as a clock source, reset prescalers. + RCC.cfgr().write_value(Cfgr::default()); + // Wait for clock switch status bits to change. + while RCC.cfgr().read().sws() != Sw::MSI {} + } + match config.rtc_mux { RtcClockSource::LSE32 => { // 1. Unlock the backup domain @@ -660,6 +681,8 @@ pub(crate) unsafe fn init(config: Config) { } }; + RCC.apb1enr1().modify(|w| w.set_pwren(true)); + set_freqs(Clocks { sys: Hertz(sys_clk), ahb1: Hertz(ahb_freq), diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs new file mode 100644 index 000000000..0de708950 --- /dev/null +++ b/examples/stm32l4/src/bin/rtc.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{self, ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::time::Hertz; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = { + let mut config = Config::default(); + config.rcc.mux = ClockSrc::PLL( + PLLSource::HSE(Hertz::mhz(8)), + PLLClkDiv::Div2, + PLLSrcDiv::Div1, + PLLMul::Mul20, + None, + ); + config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; + embassy_stm32::init(config) + }; + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new( + p.RTC, + RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), + ); + info!("Got RTC! {:?}", now.timestamp()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + // In reality the delay would be much longer + Timer::after(Duration::from_millis(20000)).await; + + let then: NaiveDateTime = rtc.now().unwrap().into(); + info!("Got RTC! {:?}", then.timestamp()); + +} From c9b9be5b819911aaf809df9b55be7e7efa3a1fba Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 2 Jul 2023 22:07:20 +0200 Subject: [PATCH 1487/1575] hal-common: require DerefMut for peripherals, not just Deref. Otherwise you can create multiple drivers on the same singleton like this: ```rust let mut input = Input::new(&pin, Pull::None); let mut output = Output::new(&pin, Level::Low, Speed::Low); input.is_high(); output.set_high(); input.is_high(); output.set_high(); ``` Thanks @pennae for reporting. --- embassy-hal-common/src/peripheral.rs | 2 +- embassy-stm32/src/dac/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-common/src/peripheral.rs index c7133bac6..38b4c452e 100644 --- a/embassy-hal-common/src/peripheral.rs +++ b/embassy-hal-common/src/peripheral.rs @@ -161,7 +161,7 @@ pub trait Peripheral: Sized { } } -impl<'b, T: Deref> Peripheral for T +impl<'b, T: DerefMut> Peripheral for T where T::Target: Peripheral, { diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 1dc13949d..31a2d8863 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -264,7 +264,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { }); let tx_request = self.dma.request(); - let dma_channel = &self.dma; + let dma_channel = &mut self.dma; let tx_options = crate::dma::TransferOptions { circular, @@ -376,7 +376,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { }); let tx_request = self.dma.request(); - let dma_channel = &self.dma; + let dma_channel = &mut self.dma; let tx_options = crate::dma::TransferOptions { circular, From af15b49bfe30d82b7fcb17511d7e1830b2216f03 Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Mon, 3 Jul 2023 22:57:33 +0200 Subject: [PATCH 1488/1575] fmt --- embassy-stm32/src/can/bxcan.rs | 31 +++++++++++++++++-------------- examples/stm32f7/src/bin/can.rs | 16 ++++++++++------ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 4afbb5687..260f567b4 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -1,9 +1,8 @@ +use core::cell::{RefCell, RefMut}; use core::future::poll_fn; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use core::cell::RefMut; -use core::cell::RefCell; pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; @@ -155,7 +154,11 @@ impl<'d, T: Instance> Can<'d, T> { pub fn set_bitrate(&mut self, bitrate: u32) { let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); - self.can.borrow_mut().modify_config().set_bit_timing(bit_timing).leave_disabled(); + self.can + .borrow_mut() + .modify_config() + .set_bit_timing(bit_timing) + .leave_disabled(); } /// Queues the message to be sent but exerts backpressure @@ -432,19 +435,19 @@ impl<'d, T: Instance> Drop for Can<'d, T> { } } -// impl<'d, T: Instance> Deref for Can<'d, T> { -// type Target = bxcan::Can>; +impl<'d, T: Instance> Deref for Can<'d, T> { + type Target = RefCell>>; -// fn deref(&self) -> &Self::Target { -// self.can.borrow() -// } -// } + fn deref(&self) -> &Self::Target { + &self.can + } +} -// impl<'d, T: Instance> DerefMut for Can<'d, T> { -// fn deref_mut(&mut self) -> &mut Self::Target { -// self.can.borrow_mut() -// } -// } +impl<'d, T: Instance> DerefMut for Can<'d, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.can + } +} pub(crate) mod sealed { use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index d821039c2..65af9845e 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -2,15 +2,17 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::borrow::BorrowMut; -use core::borrow::Borrow; +use core::borrow::{Borrow, BorrowMut}; + use cortex_m_rt::entry; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; -use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId, Data}; -use embassy_stm32::can::{Can,CanTx,CanRx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; +use embassy_stm32::can::bxcan::{Data, Fifo, Frame, StandardId}; +use embassy_stm32::can::{ + Can, CanRx, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, +}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN3; use {defmt_rtt as _, panic_probe as _}; @@ -44,8 +46,10 @@ async fn main(spawner: Spawner) { let rx_pin = Input::new(&mut p.PA15, Pull::Up); core::mem::forget(rx_pin); - let CAN: &'static mut Can<'static,CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); - CAN.as_mut().modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + let CAN: &'static mut Can<'static, CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); + CAN.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); CAN.as_mut() .modify_config() From a96f30edf45533cb0e41711556106d1583977081 Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Mon, 3 Jul 2023 23:48:07 +0200 Subject: [PATCH 1489/1575] allow deed code can rx & clippy --- embassy-stm32/src/can/bxcan.rs | 1 + examples/stm32f7/src/bin/can.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 260f567b4..fbb4113ed 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -387,6 +387,7 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { } } +#[allow(dead_code)] pub struct CanRx<'c, 'd, T: Instance> { can: &'c RefCell>>, } diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index 65af9845e..2383c23d6 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -11,7 +11,7 @@ use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; use embassy_stm32::can::bxcan::{Data, Fifo, Frame, StandardId}; use embassy_stm32::can::{ - Can, CanRx, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, + Can, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, }; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN3; @@ -46,21 +46,21 @@ async fn main(spawner: Spawner) { let rx_pin = Input::new(&mut p.PA15, Pull::Up); core::mem::forget(rx_pin); - let CAN: &'static mut Can<'static, CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); - CAN.as_mut() + let can: &'static mut Can<'static, CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); + can.as_mut() .modify_filters() .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - CAN.as_mut() + can.as_mut() .modify_config() .set_bit_timing(0x001c0001) // http://www.bittiming.can-wiki.info/ .set_loopback(true) .enable(); - let (tx, mut rx) = CAN.split(); + let (tx, mut rx) = can.split(); - let TX: &'static mut CanTx<'static, 'static, CAN3> = static_cell::make_static!(tx); - spawner.spawn(send_can_message(TX)).unwrap(); + let tx: &'static mut CanTx<'static, 'static, CAN3> = static_cell::make_static!(tx); + spawner.spawn(send_can_message(tx)).unwrap(); loop { let frame = rx.read().await.unwrap(); From e3e8d829330b58d7c6964e761d63cb718267f862 Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Mon, 3 Jul 2023 23:52:52 +0200 Subject: [PATCH 1490/1575] remove unused imports from example --- examples/stm32f7/src/bin/can.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index 2383c23d6..1b5b377ea 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -2,14 +2,11 @@ #![no_main] #![feature(type_alias_impl_trait)] -use core::borrow::{Borrow, BorrowMut}; - -use cortex_m_rt::entry; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; -use embassy_stm32::can::bxcan::{Data, Fifo, Frame, StandardId}; +use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; use embassy_stm32::can::{ Can, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, }; From 1869fe02ba8b7dfb503206977d8c70698038c3af Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Tue, 4 Jul 2023 00:21:08 +0200 Subject: [PATCH 1491/1575] make stm32f4 example work --- examples/stm32f4/src/bin/can.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index da8955053..08bed88db 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -2,8 +2,8 @@ #![no_main] #![feature(type_alias_impl_trait)] -use cortex_m_rt::entry; use defmt::*; +use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; @@ -19,8 +19,8 @@ bind_interrupts!(struct Irqs { CAN1_TX => TxInterruptHandler; }); -#[entry] -fn main() -> ! { +#[embassy_executor::main] +async fn main(_spawner: Spawner) { info!("Hello World!"); let mut p = embassy_stm32::init(Default::default()); @@ -34,9 +34,12 @@ fn main() -> ! { let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); - can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.modify_config() + can.as_mut() + .modify_config() .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ .set_loopback(true) // Receive own frames .set_silent(true) @@ -45,9 +48,8 @@ fn main() -> ! { let mut i: u8 = 0; loop { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); - unwrap!(nb::block!(can.transmit(&tx_frame))); - while !can.is_transmitter_idle() {} - let rx_frame = unwrap!(nb::block!(can.receive())); + can.write(&tx_frame).await; + let (_, rx_frame) = can.read().await.unwrap(); info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); i += 1; } From 8359d8c020fc44f795e2230b8ba12fe780f1960b Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Tue, 4 Jul 2023 00:34:24 +0200 Subject: [PATCH 1492/1575] make stm32 can test work --- tests/stm32/src/bin/can.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 33d63d546..05adfe89a 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -43,10 +43,13 @@ async fn main(_spawner: Spawner) { info!("Configuring can..."); - can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.set_bitrate(1_000_000); - can.modify_config() + can.as_mut().set_bitrate(1_000_000); + can.as_mut() + .modify_config() .set_loopback(true) // Receive own frames .set_silent(true) // .set_bit_timing(0x001c0003) From a088c4bee6e26947fd8e4d32a5604e47de293ba1 Mon Sep 17 00:00:00 2001 From: Philipp Scheff Date: Tue, 4 Jul 2023 00:39:10 +0200 Subject: [PATCH 1493/1575] fix stm32 can test --- tests/stm32/src/bin/can.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 05adfe89a..8bdd3c24f 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) { .modify_filters() .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.as_mut().set_bitrate(1_000_000); + can.set_bitrate(1_000_000); can.as_mut() .modify_config() .set_loopback(true) // Receive own frames From d9824dfd646051c1996bf15ef23304864ab9da6e Mon Sep 17 00:00:00 2001 From: William Yager Date: Mon, 3 Jul 2023 19:39:51 -0400 Subject: [PATCH 1494/1575] Add bank 1 16 bit --- embassy-stm32/src/fmc.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index a4f3b9686..26d1640be 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -86,6 +86,25 @@ macro_rules! fmc_sdram_constructor { } impl<'d, T: Instance> Fmc<'d, T> { + fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank1: ( + bank: stm32_fmc::SdramTargetBank::Bank1, + addr: [ + (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin) + ], + ba: [(ba0: BA0Pin), (ba1: BA1Pin)], + d: [ + (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), + (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) + ], + nbl: [ + (nbl0: NBL0Pin), (nbl1: NBL1Pin) + ], + ctrl: [ + (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) + ] + )); + + fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank1: ( bank: stm32_fmc::SdramTargetBank::Bank1, addr: [ From 0c4180cdd0308a7a5da2a564e1af3c275ecfd105 Mon Sep 17 00:00:00 2001 From: William Yager Date: Mon, 3 Jul 2023 19:39:58 -0400 Subject: [PATCH 1495/1575] fmt --- embassy-stm32/src/fmc.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 26d1640be..60d7a00ee 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -104,7 +104,6 @@ impl<'d, T: Instance> Fmc<'d, T> { ] )); - fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank1: ( bank: stm32_fmc::SdramTargetBank::Bank1, addr: [ From 582c721aec06702dcdb808b0b5e1d08ab8dd1353 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 4 Jul 2023 11:48:59 +0200 Subject: [PATCH 1496/1575] Add lifetimes to the functions --- embassy-nrf/src/spim.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 66bbd1a8f..166947936 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -521,11 +521,11 @@ mod eha { } impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { - async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { + async fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Result<(), Error> { self.transfer(rx, tx).await } - async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { + async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Error> { self.transfer_in_place(words).await } } From 40d25da793bd18528f3555d0a9205986018d01f0 Mon Sep 17 00:00:00 2001 From: cumthugo Date: Tue, 4 Jul 2023 21:13:31 +0800 Subject: [PATCH 1497/1575] time: fix queue size --- embassy-time/src/queue_generic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 4795eb2f3..77947ab29 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -16,7 +16,7 @@ const QUEUE_SIZE: usize = 16; #[cfg(feature = "generic-queue-32")] const QUEUE_SIZE: usize = 32; #[cfg(feature = "generic-queue-64")] -const QUEUE_SIZE: usize = 32; +const QUEUE_SIZE: usize = 64; #[cfg(feature = "generic-queue-128")] const QUEUE_SIZE: usize = 128; #[cfg(not(any( From a101d9078deb3ad576a40b6d5f4d6e81dcfd528e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 19:53:06 +0200 Subject: [PATCH 1498/1575] update embedded-hal crates. --- cyw43/Cargo.toml | 2 +- embassy-embedded-hal/Cargo.toml | 7 +- .../src/adapter/blocking_async.rs | 49 +++---- .../src/adapter/yielding_async.rs | 47 +++---- .../src/shared_bus/asynch/spi.rs | 126 ++---------------- .../src/shared_bus/blocking/spi.rs | 120 +++-------------- embassy-embedded-hal/src/shared_bus/mod.rs | 4 + embassy-lora/Cargo.toml | 7 +- embassy-net-esp-hosted/Cargo.toml | 4 +- embassy-net-w5500/Cargo.toml | 4 +- embassy-net-w5500/src/spi.rs | 8 +- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/spim.rs | 20 +-- embassy-rp/Cargo.toml | 6 +- embassy-rp/src/spi.rs | 20 +-- embassy-stm32/Cargo.toml | 6 +- embassy-stm32/src/spi/mod.rs | 23 +--- embassy-time/Cargo.toml | 4 +- examples/nrf52840/Cargo.toml | 5 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 5 +- examples/rp/Cargo.toml | 7 +- .../rp/src/bin/ethernet_w5500_multisocket.rs | 14 +- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 14 +- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 14 +- examples/rp/src/bin/ethernet_w5500_udp.rs | 13 +- examples/rp/src/bin/spi_display.rs | 8 +- examples/stm32h5/Cargo.toml | 4 +- examples/stm32h7/Cargo.toml | 4 +- examples/stm32l0/Cargo.toml | 3 + examples/stm32l4/Cargo.toml | 4 +- examples/stm32wl/Cargo.toml | 3 + tests/nrf/Cargo.toml | 4 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 6 +- tests/rp/Cargo.toml | 4 +- tests/stm32/Cargo.toml | 4 +- 35 files changed, 184 insertions(+), 393 deletions(-) diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 61caa0272..7d2f9dfd0 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -24,7 +24,7 @@ cortex-m = "0.7.6" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" } num_enum = { version = "0.5.7", default-features = false } [package.metadata.embassy_docs] diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 35c70bb63..2d11dc3c7 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -15,15 +15,18 @@ target = "x86_64-unknown-linux-gnu" std = [] # Enable nightly-only features nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"] +time = ["dep:embassy-time"] +default = ["time"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", ] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index b996d6a75..98ae2b02c 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -74,7 +74,21 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(data)?; + Ok(()) + } + + async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer(data)?; + Ok(()) + } + + async fn transfer(&mut self, read: &mut [u8], write: &[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(); @@ -83,38 +97,7 @@ where Ok(()) } - async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> { - todo!() - } -} - -impl embedded_hal_async::spi::SpiBusFlush for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusWrite for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { - self.wrapped.write(data)?; - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusRead for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { self.wrapped.transfer(data)?; Ok(()) } diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs index f51e4076f..fe9c9c341 100644 --- a/embassy-embedded-hal/src/adapter/yielding_async.rs +++ b/embassy-embedded-hal/src/adapter/yielding_async.rs @@ -69,54 +69,39 @@ where type Error = T::Error; } -impl embedded_hal_async::spi::SpiBus for YieldingAsync +impl embedded_hal_async::spi::SpiBus for YieldingAsync where - T: embedded_hal_async::spi::SpiBus, -{ - async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { - self.wrapped.transfer(read, write).await?; - yield_now().await; - Ok(()) - } - - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { - self.wrapped.transfer_in_place(words).await?; - yield_now().await; - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusFlush for YieldingAsync -where - T: embedded_hal_async::spi::SpiBusFlush, + T: embedded_hal_async::spi::SpiBus, { async fn flush(&mut self) -> Result<(), Self::Error> { self.wrapped.flush().await?; yield_now().await; Ok(()) } -} -impl embedded_hal_async::spi::SpiBusWrite for YieldingAsync -where - T: embedded_hal_async::spi::SpiBusWrite, -{ - async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> { self.wrapped.write(data).await?; yield_now().await; Ok(()) } -} -impl embedded_hal_async::spi::SpiBusRead for YieldingAsync -where - T: embedded_hal_async::spi::SpiBusRead, -{ - async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> { self.wrapped.read(data).await?; yield_now().await; Ok(()) } + + async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { + self.wrapped.transfer(read, write).await?; + yield_now().await; + Ok(()) + } + + async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + self.wrapped.transfer_in_place(words).await?; + yield_now().await; + Ok(()) + } } /// diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index b5549a6cd..030392183 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -56,62 +56,6 @@ where type Error = SpiDeviceError; } -impl spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> -where - M: RawMutex, - BUS: spi::SpiBusRead, - CS: OutputPin, -{ - async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { - let mut bus = self.bus.lock().await; - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res: Result<(), BUS::Error> = try { - for buf in operations { - bus.read(buf).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(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - - Ok(op_res) - } -} - -impl spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> -where - M: RawMutex, - BUS: spi::SpiBusWrite, - CS: OutputPin, -{ - async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { - let mut bus = self.bus.lock().await; - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res: Result<(), BUS::Error> = try { - for buf in operations { - bus.write(buf).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(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - - Ok(op_res) - } -} - impl spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, @@ -129,6 +73,12 @@ where Operation::Write(buf) => bus.write(buf).await?, Operation::Transfer(read, write) => bus.transfer(read, write).await?, Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await + } } } }; @@ -172,64 +122,6 @@ where type Error = SpiDeviceError; } -impl spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> -where - M: RawMutex, - BUS: spi::SpiBusWrite + SetConfig, - CS: OutputPin, -{ - async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res: Result<(), BUS::Error> = try { - for buf in operations { - bus.write(buf).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(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - - Ok(op_res) - } -} - -impl spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> -where - M: RawMutex, - BUS: spi::SpiBusRead + SetConfig, - CS: OutputPin, -{ - async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { - let mut bus = self.bus.lock().await; - bus.set_config(&self.config); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res: Result<(), BUS::Error> = try { - for buf in operations { - bus.read(buf).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(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - - Ok(op_res) - } -} - impl spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, @@ -248,6 +140,12 @@ where Operation::Write(buf) => bus.write(buf).await?, Operation::Transfer(read, write) => bus.transfer(read, write).await?, Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await + } } } }; diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 22e013be9..6d03d6263 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -22,7 +22,7 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; use embedded_hal_1::digital::OutputPin; -use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite}; +use embedded_hal_1::spi::{self, Operation, SpiBus}; use crate::shared_bus::SpiDeviceError; use crate::SetConfig; @@ -48,58 +48,6 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS> -where - M: RawMutex, - BUS: SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { - self.bus.lock(|bus| { - let mut bus = bus.borrow_mut(); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf)); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - - Ok(op_res) - }) - } -} - -impl embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS> -where - M: RawMutex, - BUS: SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { - self.bus.lock(|bus| { - let mut bus = bus.borrow_mut(); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res = operations.iter().try_for_each(|buf| bus.write(buf)); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - - Ok(op_res) - }) - } -} - impl embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, @@ -116,6 +64,13 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::block_for(embassy_time::Duration::from_micros(*us as _)); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. @@ -199,58 +154,6 @@ where type Error = SpiDeviceError; } -impl embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS> -where - M: RawMutex, - BUS: SpiBusRead + SetConfig, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { - self.bus.lock(|bus| { - let mut bus = bus.borrow_mut(); - bus.set_config(&self.config); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf)); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - Ok(op_res) - }) - } -} - -impl embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS> -where - M: RawMutex, - BUS: SpiBusWrite + SetConfig, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { - self.bus.lock(|bus| { - let mut bus = bus.borrow_mut(); - bus.set_config(&self.config); - self.cs.set_low().map_err(SpiDeviceError::Cs)?; - - let op_res = operations.iter().try_for_each(|buf| bus.write(buf)); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - let op_res = op_res.map_err(SpiDeviceError::Spi)?; - flush_res.map_err(SpiDeviceError::Spi)?; - cs_res.map_err(SpiDeviceError::Cs)?; - Ok(op_res) - }) - } -} - impl embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, @@ -268,6 +171,13 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::block_for(embassy_time::Duration::from_micros(*us as _)); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embassy-embedded-hal/src/shared_bus/mod.rs b/embassy-embedded-hal/src/shared_bus/mod.rs index 617d921e9..79a90bd52 100644 --- a/embassy-embedded-hal/src/shared_bus/mod.rs +++ b/embassy-embedded-hal/src/shared_bus/mod.rs @@ -30,11 +30,14 @@ where /// Error returned by SPI device implementations in this crate. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum SpiDeviceError { /// An operation on the inner SPI bus failed. Spi(BUS), /// Setting the value of the Chip Select (CS) pin failed. Cs(CS), + /// DelayUs operations are not supported when the `time` Cargo feature is not enabled. + DelayUsNotSupported, } impl spi::Error for SpiDeviceError @@ -46,6 +49,7 @@ where match self { Self::Spi(e) => e.kind(), Self::Cs(_) => spi::ErrorKind::Other, + Self::DelayUsNotSupported => spi::ErrorKind::Other, } } } diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 05b6fa2d0..dc44f96db 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -23,8 +23,8 @@ log = { version = "0.4.14", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } 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"] } @@ -32,3 +32,6 @@ bit_field = { version = "0.10" } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index a7e18ee09..a52570f5d 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -12,8 +12,8 @@ embassy-sync = { version = "0.2.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} -embedded-hal = { version = "1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal = { version = "1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 37d15c7ac..41d411117 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -embedded-hal = { version = "1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal = { version = "1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} embassy-time = { version = "0.1.0" } embassy-futures = { version = "0.1.0" } diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs index 6cd52c44d..07749d6be 100644 --- a/embassy-net-w5500/src/spi.rs +++ b/embassy-net-w5500/src/spi.rs @@ -22,7 +22,11 @@ impl SpiInterface { let address_phase = address.to_be_bytes(); let control_phase = [(block as u8) << 3 | 0b0000_0100]; let data_phase = data; - let operations = &[&address_phase[..], &control_phase, &data_phase]; - self.0.write_transaction(operations).await + let operations = &mut [ + Operation::Write(&address_phase[..]), + Operation::Write(&control_phase), + Operation::Write(&data_phase), + ]; + self.0.transaction(operations).await } } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 3e858f854..d33740cc8 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -98,8 +98,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} embedded-io = { version = "0.4.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 166947936..b7dc332e9 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -468,25 +468,19 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead for Spim<'d, T> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } - } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite for Spim<'d, T> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } - } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -502,30 +496,24 @@ mod eha { use super::*; - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { async fn flush(&mut self) -> Result<(), Error> { Ok(()) } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spim<'d, T> { 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> { 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> { - async fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Result<(), Error> { + async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { self.transfer(rx, tx).await } - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Error> { + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> { self.transfer_in_place(words).await } } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 66823771a..a06831a9a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -79,9 +79,9 @@ fixed = "1.23.1" rp-pac = { version = "6" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} paste = "1.0" pio-proc = {version= "0.2" } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index e817d074e..af101cf4a 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -545,25 +545,19 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, M> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } - } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, M> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } - } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -578,30 +572,24 @@ mod eh1 { mod eha { use super::*; - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> { + impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Async> { 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> { 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> { - async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.transfer(read, write).await } - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.transfer_in_place(words).await } } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b3fe9c1f5..045149636 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -40,9 +40,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c3224073d..d5f63f84e 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -852,25 +852,19 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - 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, 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, 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) } @@ -895,32 +889,25 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { use super::*; - impl<'d, T: Instance, Tx, Rx> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { + + impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> { 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> { 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> - { 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> { - async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> { + async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { self.transfer(read, write).await } - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Result<(), Self::Error> { + async fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.transfer_in_place(words).await } } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 857da5467..0213eef03 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} futures-util = { version = "0.3.17", default-features = false } atomic-polyfill = "1.0.1" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 8c4175966..2ccd5045f 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -52,4 +52,7 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } -embedded-hal-async = { version = "0.2.0-alpha.1", optional = true } +embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 4eb31b105..f7496703c 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -10,6 +10,7 @@ use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::Delay; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use static_cell::make_static; @@ -24,7 +25,7 @@ bind_interrupts!(struct Irqs { async fn wifi_task( runner: hosted::Runner< 'static, - ExclusiveDevice, Output<'static, peripherals::P0_31>>, + ExclusiveDevice, Output<'static, peripherals::P0_31>, Delay>, Input<'static, AnyPin>, Output<'static, peripherals::P1_05>, >, @@ -55,7 +56,7 @@ async fn main(spawner: Spawner) { config.frequency = spim::Frequency::M32; config.mode = spim::MODE_2; // !!! let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); - let spi = ExclusiveDevice::new(spi, cs); + let spi = ExclusiveDevice::new(spi, cs, Delay); let (device, mut control, runner) = embassy_net_esp_hosted::new( make_static!(embassy_net_esp_hosted::State::new()), diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 48f3a26bb..17ebea86f 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -41,8 +41,8 @@ byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" heapless = "0.7.15" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = "0.2.0-alpha.1" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = "0.2.0-alpha.2" embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} @@ -53,3 +53,6 @@ rand = { version = "0.8.5", default-features = false } [profile.release] debug = true + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 82568254a..e81da177b 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -15,7 +15,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; -use embassy_time::Duration; +use embassy_time::{Delay, Duration}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -26,7 +26,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -54,8 +54,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index d562defad..9dd7ae973 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -17,7 +17,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; -use embassy_time::{Duration, Timer}; +use embassy_time::{Delay, Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -28,7 +28,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -57,8 +57,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 7f521cdb4..db21c2b6f 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -16,7 +16,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; -use embassy_time::Duration; +use embassy_time::{Delay, Duration}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -26,7 +26,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -55,8 +55,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index ada86ae55..038432b17 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -16,6 +16,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::Delay; use embedded_hal_async::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; @@ -24,7 +25,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -52,8 +53,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 85a19ce07..2fd201595 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -175,7 +175,7 @@ mod touch { mod my_display_interface { use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi::SpiDeviceWrite; + use embedded_hal_1::spi::SpiDevice; /// SPI display interface. /// @@ -187,7 +187,7 @@ mod my_display_interface { impl SPIDeviceInterface where - SPI: SpiDeviceWrite, + SPI: SpiDevice, DC: OutputPin, { /// Create new SPI interface for communciation with a display driver @@ -198,7 +198,7 @@ mod my_display_interface { impl WriteOnlyDataCommand for SPIDeviceInterface where - SPI: SpiDeviceWrite, + SPI: SpiDevice, DC: OutputPin, { fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { @@ -218,7 +218,7 @@ mod my_display_interface { } } - fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { + fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { match words { DataFormat::U8(slice) => spi.write(slice), DataFormat::U16(slice) => { diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index ebe511347..789ef59cc 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "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.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 62ef5e9e4..04a2baab7 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "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.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 2ead714e4..988fd3a79 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -32,3 +32,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } embedded-hal = "0.2.6" static_cell = "1.1" + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 3bb473ef5..7d066bb82 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -18,8 +18,8 @@ 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.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } 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/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 260f9afa1..75a5f1c41 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -25,3 +25,6 @@ embedded-storage = "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 } + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 4f9ecc47a..247287e46 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -10,12 +10,12 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -embedded-hal-async = { version = "0.2.0-alpha.1" } +embedded-hal-async = { version = "0.2.0-alpha.2" } static_cell = { version = "1.1", features = [ "nightly" ] } defmt = "0.3" diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 277b985c5..398ab9d27 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -14,7 +14,7 @@ use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{with_timeout, Delay, Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; @@ -30,7 +30,7 @@ bind_interrupts!(struct Irqs { async fn wifi_task( runner: hosted::Runner< 'static, - ExclusiveDevice, Output<'static, peripherals::P0_31>>, + ExclusiveDevice, Output<'static, peripherals::P0_31>, Delay>, Input<'static, AnyPin>, Output<'static, peripherals::P1_05>, >, @@ -63,7 +63,7 @@ async fn main(spawner: Spawner) { config.frequency = spim::Frequency::M32; config.mode = spim::MODE_2; // !!! let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); - let spi = ExclusiveDevice::new(spi, cs); + let spi = ExclusiveDevice::new(spi, cs, Delay); let (device, mut control, runner) = embassy_net_esp_hosted::new( make_static!(embassy_net_esp_hosted::State::new()), diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 180d0ebbe..f1b48e747 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -22,8 +22,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index c2422f7bc..c69af6d5a 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -38,8 +38,8 @@ 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.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } rand_chacha = { version = "0.3", default-features = false } From 10c0174903e6414d8d88b2636c65604ea85f4a8e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 20:10:45 +0200 Subject: [PATCH 1499/1575] doc: upload statics too. --- .github/ci/doc.sh | 49 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 1402e742f..e4d83e4bc 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -15,30 +15,31 @@ export BUILDER_COMPRESS=true # which makes rustup very sad rustc --version > /dev/null -docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup -docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup -docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup -docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup -docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup -docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup -docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup -docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup -docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup -docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup -docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup -docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup -docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup -docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup -docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup -docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup -docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup -docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup -docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup -docserver-builder -i ./cyw43 -o crates/cyw43/git.zup -docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup -docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup -docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup +docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup +docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup +docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup +docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup +docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup +docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup +docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup +docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup +docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup +docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup +docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup +docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup +docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup +docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup +docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup +docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup +docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup +docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup +docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup +docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup +docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup +docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) -kubectl cp crates $POD:/data +kubectl cp webroot/crates $POD:/data +kubectl cp webroot/static $POD:/data \ No newline at end of file From 2a035a24a62c3534c09ef26e00a96f6e1f185ff7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 20:11:00 +0200 Subject: [PATCH 1500/1575] Update nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fd454db26..c201d8bdf 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-2023-05-18" +channel = "nightly-2023-07-03" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From 9c4df46c46bb4eb88dff6017bbecf5cf807c8cac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 21:34:55 +0200 Subject: [PATCH 1501/1575] rustfmt. --- cyw43/src/runner.rs | 20 +++++++++++++++----- embassy-net-esp-hosted/src/control.rs | 12 +++++++++--- embassy-net-esp-hosted/src/lib.rs | 4 ++-- embassy-stm32/src/rcc/h5.rs | 4 ++-- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs index 5706696b4..1c187faa5 100644 --- a/cyw43/src/runner.rs +++ b/cyw43/src/runner.rs @@ -345,7 +345,9 @@ where } fn rx(&mut self, packet: &mut [u8]) { - let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return }; + let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { + return; + }; self.update_credit(&sdpcm_header); @@ -353,7 +355,9 @@ where match channel { CHANNEL_TYPE_CONTROL => { - let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; }; + let Some((cdc_header, response)) = CdcHeader::parse(payload) else { + return; + }; trace!(" {:?}", cdc_header); if cdc_header.id == self.ioctl_id { @@ -417,8 +421,12 @@ where let status = event_packet.msg.status; let event_payload = match evt_type { Event::ESCAN_RESULT if status == EStatus::PARTIAL => { - let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return }; - let Some(bss_info) = BssInfo::parse(bss_info) else { return }; + let Some((_, bss_info)) = ScanResults::parse(evt_data) else { + return; + }; + let Some(bss_info) = BssInfo::parse(bss_info) else { + return; + }; events::Payload::BssInfo(*bss_info) } Event::ESCAN_RESULT => events::Payload::None, @@ -439,7 +447,9 @@ where } } CHANNEL_TYPE_DATA => { - let Some((_, packet)) = BdcHeader::parse(payload) else { return }; + let Some((_, packet)) = BdcHeader::parse(payload) else { + return; + }; trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); match self.ch.try_rx_buf() { diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index fce82ade7..79f8cde7b 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -54,7 +54,9 @@ impl<'a> Control<'a> { })), }; let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { + panic!("unexpected resp") + }; debug!("======= {:?}", Debug2Format(&resp)); assert_eq!(resp.resp, 0); self.state_ch.set_link_state(LinkState::Up); @@ -71,7 +73,9 @@ impl<'a> Control<'a> { )), }; let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { + panic!("unexpected resp") + }; assert_eq!(resp.resp, 0); // WHY IS THIS A STRING? WHYYYY @@ -100,7 +104,9 @@ impl<'a> Control<'a> { payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), }; let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { + panic!("unexpected resp") + }; assert_eq!(resp.resp, 0); } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 44dfbe89c..a35adfca0 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -311,14 +311,14 @@ where fn handle_event(&self, data: &[u8]) { let Ok(event) = noproto::read::(data) else { warn!("failed to parse event"); - return + return; }; debug!("event: {:?}", &event); let Some(payload) = &event.payload else { warn!("event without payload?"); - return + return; }; match payload { diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs index 4025a4e05..7e2f75ab7 100644 --- a/embassy-stm32/src/rcc/h5.rs +++ b/embassy-stm32/src/rcc/h5.rs @@ -473,11 +473,11 @@ fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { w.set_divm(0); }); - return PllOutput{ + return PllOutput { p: None, q: None, r: None, - } + }; }; assert!(1 <= config.prediv && config.prediv <= 63); From 953c745ed86341dba1ce96a15cc03348e1120766 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 4 Jul 2023 16:29:46 -0500 Subject: [PATCH 1502/1575] stm32/rcc: allow const-propagation --- embassy-stm32/build.rs | 4 +--- embassy-stm32/src/rcc/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index fa66da1f6..995ad1443 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -348,9 +348,7 @@ fn main() { g.extend(quote! { impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { - crate::rcc::get_freqs().#clk - }) + unsafe { crate::rcc::get_freqs().#clk } } fn enable() { critical_section::with(|_| { diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d6816d6a8..886fc0b93 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -83,12 +83,12 @@ static mut CLOCK_FREQS: MaybeUninit = MaybeUninit::uninit(); /// Safety: Sets a mutable global. pub(crate) unsafe fn set_freqs(freqs: Clocks) { debug!("rcc: {:?}", freqs); - CLOCK_FREQS.as_mut_ptr().write(freqs); + CLOCK_FREQS = MaybeUninit::new(freqs); } /// Safety: Reads a mutable global. pub(crate) unsafe fn get_freqs() -> &'static Clocks { - &*CLOCK_FREQS.as_ptr() + CLOCK_FREQS.assume_init_ref() } #[cfg(feature = "unstable-pac")] From 70c05c62e49b7b8c2d6d47a7eb567f88b22aa7dc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Jul 2023 00:35:22 +0200 Subject: [PATCH 1503/1575] nrf: build docs with `time` feature. --- embassy-nrf/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index d33740cc8..ae76af3e6 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"] +features = ["nightly", "time", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"] flavors = [ { regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" }, { regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" }, From 2c5146f19fad4344222dee916687b750896a7487 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 09:20:56 +0200 Subject: [PATCH 1504/1575] Fixed Lifetimes in Events & Tasks --- embassy-nrf/src/ppi/mod.rs | 4 ++-- embassy-nrf/src/ppi/ppi.rs | 4 ++-- embassy-nrf/src/timer.rs | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 9092529ac..c2bc0f580 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -96,7 +96,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// Get a reference to the "enable all" task. /// /// When triggered, it will enable all the channels in this group. - pub fn task_enable_all(&self) -> Task { + pub fn task_enable_all<'s: 'd>(&'d self) -> Task<'s> { let n = self.g.number(); Task::from_reg(®s().tasks_chg[n].en) } @@ -104,7 +104,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// Get a reference to the "disable all" task. /// /// When triggered, it will disable all the channels in this group. - pub fn task_disable_all(&self) -> Task { + pub fn task_disable_all<'s: 'd>(&self) -> Task<'s> { let n = self.g.number(); Task::from_reg(®s().tasks_chg[n].dis) } diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 6e8a669d3..d0a70b85d 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -34,7 +34,7 @@ impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> { impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { /// Configure PPI channel to trigger `task` on `event`. - pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event, task: Task) -> Self { + pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event<'d>, task: Task<'d>) -> Self { into_ref!(ch); let r = regs(); @@ -49,7 +49,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { #[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { /// Configure PPI channel to trigger both `task1` and `task2` on `event`. - pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { + pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self { into_ref!(ch); let r = regs(); diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index dc3757856..fed576c35 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -168,21 +168,21 @@ impl<'d, T: Instance> Timer<'d, T> { /// Returns the START task, for use with PPI. /// /// When triggered, this task starts the timer. - pub fn task_start(&self) -> Task { + pub fn task_start<'s: 'd>(&self) -> Task<'s> { Task::from_reg(&T::regs().tasks_start) } /// Returns the STOP task, for use with PPI. /// /// When triggered, this task stops the timer. - pub fn task_stop(&self) -> Task { + pub fn task_stop<'s: 'd>(&self) -> Task<'s> { Task::from_reg(&T::regs().tasks_stop) } /// Returns the CLEAR task, for use with PPI. /// /// When triggered, this task resets the timer's counter to 0. - pub fn task_clear(&self) -> Task { + pub fn task_clear<'s: 'd>(&self) -> Task<'s> { Task::from_reg(&T::regs().tasks_clear) } @@ -190,7 +190,7 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// When triggered, this task increments the timer's counter by 1. /// Only works in counter mode. - pub fn task_count(&self) -> Task { + pub fn task_count<'s: 'd>(&self) -> Task<'s> { Task::from_reg(&T::regs().tasks_count) } @@ -258,14 +258,14 @@ impl<'d, T: Instance> Cc<'d, T> { /// Returns this CC register's CAPTURE task, for use with PPI. /// /// When triggered, this task will capture the current value of the timer's counter in this register. - pub fn task_capture(&self) -> Task { + pub fn task_capture<'s: 'd>(&self) -> Task<'s> { Task::from_reg(&T::regs().tasks_capture) } /// Returns this CC register's COMPARE event, for use with PPI. /// /// This event will fire when the timer's counter reaches the value in this CC register. - pub fn event_compare(&self) -> Event { + pub fn event_compare<'s: 'd>(&self) -> Event<'s> { Event::from_reg(&T::regs().events_compare[self.n]) } From ab7fcf1d5bf0a73cd86cabcb599e9f9e8f0b08ce Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 09:23:39 +0200 Subject: [PATCH 1505/1575] Removed unnecessary changes --- embassy-nrf/src/buffered_uarte.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 8fc3b4b9b..b038fe592 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -352,13 +352,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); let mut ppi_group = PpiGroup::new(ppi_group); - let ppi_group_channel_disable_all_task = ppi_group.task_disable_all(); - + let mut ppi_ch2 = Ppi::new_one_to_two( ppi_ch2, Event::from_reg(&r.events_endrx), Task::from_reg(&r.tasks_startrx), - ppi_group_channel_disable_all_task, + ppi_group.task_disable_all(), ); ppi_ch2.disable(); ppi_group.add_channel(&ppi_ch2); From 7d3eb6463a03f16674237ed7aa0a085dc6070207 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 11:34:33 +0200 Subject: [PATCH 1506/1575] Removed unnecessary space --- embassy-nrf/src/buffered_uarte.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index b038fe592..9bc1c1e7a 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -352,7 +352,6 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); let mut ppi_group = PpiGroup::new(ppi_group); - let mut ppi_ch2 = Ppi::new_one_to_two( ppi_ch2, Event::from_reg(&r.events_endrx), From a0dc87d64ec75b6236abb60f93815f6d5c621de5 Mon Sep 17 00:00:00 2001 From: Rasmus Pedersen Date: Wed, 5 Jul 2023 14:07:05 +0200 Subject: [PATCH 1507/1575] Remove semicolon in time driver example struct declaration The semicolon is not allowed with struct declarations with braces. The doc test compiles fine for some reason!? --- embassy-time/src/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs index d6436369b..5fe7becaf 100644 --- a/embassy-time/src/driver.rs +++ b/embassy-time/src/driver.rs @@ -36,7 +36,7 @@ //! ``` //! use embassy_time::driver::{Driver, AlarmHandle}; //! -//! struct MyDriver{}; // not public! +//! struct MyDriver{} // not public! //! embassy_time::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); //! //! impl Driver for MyDriver { From 8ee2f50b8c10dd86ec97e2d51110c9042d563075 Mon Sep 17 00:00:00 2001 From: Cameron Date: Wed, 5 Jul 2023 19:01:28 +0200 Subject: [PATCH 1508/1575] Removed unnecessary lifetime naming --- embassy-nrf/src/gpiote.rs | 8 ++++---- embassy-nrf/src/ppi/mod.rs | 4 ++-- embassy-nrf/src/pwm.rs | 22 +++++++++++----------- embassy-nrf/src/timer.rs | 12 ++++++------ 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 21d0d9564..6550f2abd 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -221,7 +221,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { } /// Returns the IN event, for use with PPI. - pub fn event_in(&self) -> Event { + pub fn event_in(&self) -> Event<'d> { let g = regs(); Event::from_reg(&g.events_in[self.ch.number()]) } @@ -292,21 +292,21 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { } /// Returns the OUT task, for use with PPI. - pub fn task_out(&self) -> Task { + pub fn task_out(&self) -> Task<'d> { let g = regs(); Task::from_reg(&g.tasks_out[self.ch.number()]) } /// Returns the CLR task, for use with PPI. #[cfg(not(feature = "nrf51"))] - pub fn task_clr(&self) -> Task { + pub fn task_clr(&self) -> Task<'d> { let g = regs(); Task::from_reg(&g.tasks_clr[self.ch.number()]) } /// Returns the SET task, for use with PPI. #[cfg(not(feature = "nrf51"))] - pub fn task_set(&self) -> Task { + pub fn task_set(&self) -> Task<'d> { let g = regs(); Task::from_reg(&g.tasks_set[self.ch.number()]) } diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index c2bc0f580..3be965abe 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -96,7 +96,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// Get a reference to the "enable all" task. /// /// When triggered, it will enable all the channels in this group. - pub fn task_enable_all<'s: 'd>(&'d self) -> Task<'s> { + pub fn task_enable_all(&self) -> Task<'d> { let n = self.g.number(); Task::from_reg(®s().tasks_chg[n].en) } @@ -104,7 +104,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// Get a reference to the "disable all" task. /// /// When triggered, it will disable all the channels in this group. - pub fn task_disable_all<'s: 'd>(&self) -> Task<'s> { + pub fn task_disable_all(&self) -> Task<'d> { let n = self.g.number(); Task::from_reg(®s().tasks_chg[n].dis) } diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 363a255d5..c8c81fa01 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -181,7 +181,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Stopped` event endpoint for PPI. #[inline(always)] - pub fn event_stopped(&self) -> Event { + pub fn event_stopped(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_stopped) @@ -189,7 +189,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `LoopsDone` event endpoint for PPI. #[inline(always)] - pub fn event_loops_done(&self) -> Event { + pub fn event_loops_done(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_loopsdone) @@ -197,7 +197,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `PwmPeriodEnd` event endpoint for PPI. #[inline(always)] - pub fn event_pwm_period_end(&self) -> Event { + pub fn event_pwm_period_end(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_pwmperiodend) @@ -205,7 +205,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq0 End` event endpoint for PPI. #[inline(always)] - pub fn event_seq_end(&self) -> Event { + pub fn event_seq_end(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqend[0]) @@ -213,7 +213,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq1 End` event endpoint for PPI. #[inline(always)] - pub fn event_seq1_end(&self) -> Event { + pub fn event_seq1_end(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqend[1]) @@ -221,7 +221,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq0 Started` event endpoint for PPI. #[inline(always)] - pub fn event_seq0_started(&self) -> Event { + pub fn event_seq0_started(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqstarted[0]) @@ -229,7 +229,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq1 Started` event endpoint for PPI. #[inline(always)] - pub fn event_seq1_started(&self) -> Event { + pub fn event_seq1_started(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqstarted[1]) @@ -240,7 +240,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_start_seq0(&self) -> Task { + pub unsafe fn task_start_seq0(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_seqstart[0]) @@ -251,7 +251,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_start_seq1(&self) -> Task { + pub unsafe fn task_start_seq1(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_seqstart[1]) @@ -262,7 +262,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_next_step(&self) -> Task { + pub unsafe fn task_next_step(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_nextstep) @@ -273,7 +273,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_stop(&self) -> Task { + pub unsafe fn task_stop(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_stop) diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index fed576c35..04748238d 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -168,21 +168,21 @@ impl<'d, T: Instance> Timer<'d, T> { /// Returns the START task, for use with PPI. /// /// When triggered, this task starts the timer. - pub fn task_start<'s: 'd>(&self) -> Task<'s> { + pub fn task_start(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_start) } /// Returns the STOP task, for use with PPI. /// /// When triggered, this task stops the timer. - pub fn task_stop<'s: 'd>(&self) -> Task<'s> { + pub fn task_stop(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_stop) } /// Returns the CLEAR task, for use with PPI. /// /// When triggered, this task resets the timer's counter to 0. - pub fn task_clear<'s: 'd>(&self) -> Task<'s> { + pub fn task_clear(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_clear) } @@ -190,7 +190,7 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// When triggered, this task increments the timer's counter by 1. /// Only works in counter mode. - pub fn task_count<'s: 'd>(&self) -> Task<'s> { + pub fn task_count(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_count) } @@ -258,14 +258,14 @@ impl<'d, T: Instance> Cc<'d, T> { /// Returns this CC register's CAPTURE task, for use with PPI. /// /// When triggered, this task will capture the current value of the timer's counter in this register. - pub fn task_capture<'s: 'd>(&self) -> Task<'s> { + pub fn task_capture(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_capture) } /// Returns this CC register's COMPARE event, for use with PPI. /// /// This event will fire when the timer's counter reaches the value in this CC register. - pub fn event_compare<'s: 'd>(&self) -> Event<'s> { + pub fn event_compare(&self) -> Event<'d> { Event::from_reg(&T::regs().events_compare[self.n]) } From fb3e6a2b40abcd14dcaa1ba2ba91a46cbc1b0ed5 Mon Sep 17 00:00:00 2001 From: Cameron Harris Date: Wed, 5 Jul 2023 19:10:16 +0200 Subject: [PATCH 1509/1575] Update embassy-nrf/src/ppi/mod.rs Co-authored-by: Dario Nieuwenhuis --- embassy-nrf/src/ppi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 3be965abe..568f7641b 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -128,7 +128,7 @@ const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::(); #[derive(PartialEq, Eq, Clone, Copy)] pub struct Task<'d>(NonNull, PhantomData<&'d ()>); -impl<'d> Task<'_> { +impl<'d> Task<'d> { /// Create a new `Task` from a task register pointer /// /// # Safety From 67c4d165c7e9b7a490bf220246be783c7ada7f02 Mon Sep 17 00:00:00 2001 From: Cameron Harris Date: Wed, 5 Jul 2023 19:10:22 +0200 Subject: [PATCH 1510/1575] Update embassy-nrf/src/ppi/ppi.rs Co-authored-by: Dario Nieuwenhuis --- embassy-nrf/src/ppi/ppi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index d0a70b85d..2b9926e2a 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -8,7 +8,7 @@ impl<'d> Task<'_> { self.0.as_ptr() as _ } } -impl<'d> Event<'_> { +impl<'d> Event<'d> { fn reg_val(&self) -> u32 { self.0.as_ptr() as _ } From 082147939d7e123994485c8a87daedf577905002 Mon Sep 17 00:00:00 2001 From: Cameron Harris Date: Wed, 5 Jul 2023 19:10:30 +0200 Subject: [PATCH 1511/1575] Update embassy-nrf/src/ppi/ppi.rs Co-authored-by: Dario Nieuwenhuis --- embassy-nrf/src/ppi/ppi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 2b9926e2a..1fe898625 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -3,7 +3,7 @@ use embassy_hal_common::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task}; use crate::{pac, Peripheral}; -impl<'d> Task<'_> { +impl<'d> Task<'d> { fn reg_val(&self) -> u32 { self.0.as_ptr() as _ } From d7ecf6f59394ff1ca322a096b7405c8f552d4c44 Mon Sep 17 00:00:00 2001 From: Cameron Harris Date: Wed, 5 Jul 2023 19:10:43 +0200 Subject: [PATCH 1512/1575] Update embassy-nrf/src/ppi/mod.rs Co-authored-by: Dario Nieuwenhuis --- embassy-nrf/src/ppi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 568f7641b..ff6593bd5 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -168,7 +168,7 @@ unsafe impl Send for Task<'_> {} #[derive(PartialEq, Eq, Clone, Copy)] pub struct Event<'d>(NonNull, PhantomData<&'d ()>); -impl<'d> Event<'_> { +impl<'d> Event<'d> { /// Create a new `Event` from an event register pointer /// /// # Safety From d1711036dba286ef0b629a3ab7e249fd855ea772 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Jul 2023 19:13:46 +0200 Subject: [PATCH 1513/1575] stm32-wpan: fix wrong src_base --- embassy-stm32-wpan/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 4b830cab3..6d4027fcc 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src" +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" target = "thumbv7em-none-eabihf" features = ["stm32wb55rg"] From c6cd69887c64e22575442359040a890f32719295 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Jul 2023 19:14:11 +0200 Subject: [PATCH 1514/1575] Downgrade nightly. Newer nightlies have a bad perf regression https://github.com/rust-lang/rust/issues/113372 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c201d8bdf..5db74c7a9 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-2023-07-03" +channel = "nightly-2023-06-28" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From a42ac86f1b71700632b77196ad506587774ae976 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Jul 2023 19:16:45 +0200 Subject: [PATCH 1515/1575] Remove wifi envvars. They're annoying, they cause rust-analyzer errors when opening the examples. --- ci.sh | 4 ---- cyw43/README.md | 2 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 5 ++++- examples/rp/src/bin/wifi_tcp_server.rs | 7 +++++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ci.sh b/ci.sh index a03efb856..376cc8f44 100755 --- a/ci.sh +++ b/ci.sh @@ -5,10 +5,6 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info -# needed by wifi examples -export WIFI_NETWORK=x -export WIFI_PASSWORD=x - TARGET=$(rustc -vV | sed -n 's|host: ||p') BUILD_EXTRA="" diff --git a/cyw43/README.md b/cyw43/README.md index e4a81410d..5b8f3cf40 100644 --- a/cyw43/README.md +++ b/cyw43/README.md @@ -30,7 +30,7 @@ TODO: ### Example 2: Create an access point (IP and credentials in the code) - `cargo run --release --bin wifi_ap_tcp_server` ### Example 3: Connect to an existing network and create a server -- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server` +- `cargo run --release --bin wifi_tcp_server` After a few seconds, you should see that DHCP picks up an IP address like this ``` diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index f7496703c..112e41bcd 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -16,6 +16,9 @@ use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + bind_interrupts!(struct Irqs { SPIM3 => spim::InterruptHandler; RNG => embassy_nrf::rng::InterruptHandler; @@ -70,7 +73,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); control.init().await; - control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; + control.join(WIFI_NETWORK, WIFI_PASSWORD).await; let config = embassy_net::Config::dhcpv4(Default::default()); // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index e9d1079a6..197535f45 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -19,6 +19,9 @@ use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -82,8 +85,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(net_task(stack))); loop { - //control.join_open(env!("WIFI_NETWORK")).await; - match control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await { + //control.join_open(WIFI_NETWORK).await; + match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { Ok(_) => break, Err(err) => { info!("join failed with status={}", err.status); From d1372869813e268a19e83819c7f1a0631d2f24d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Jul 2023 23:54:45 +0200 Subject: [PATCH 1516/1575] Release embassy-time v0.1.2 --- cyw43/Cargo.toml | 2 +- embassy-embedded-hal/Cargo.toml | 2 +- embassy-executor/Cargo.toml | 4 ++-- embassy-lora/Cargo.toml | 2 +- embassy-net-esp-hosted/Cargo.toml | 2 +- embassy-net-w5500/Cargo.toml | 6 +++--- embassy-net/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 18 +++++++++--------- embassy-rp/Cargo.toml | 2 +- embassy-stm32-wpan/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-time/CHANGELOG.md | 5 +++++ embassy-time/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 51 files changed, 66 insertions(+), 61 deletions(-) diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 7d2f9dfd0..50fb7c5db 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -11,7 +11,7 @@ log = ["dep:log"] firmware-logs = [] [dependencies] -embassy-time = { version = "0.1.0", path = "../embassy-time"} +embassy-time = { version = "0.1.2", path = "../embassy-time"} embassy-sync = { version = "0.2.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 2d11dc3c7..bba3d48be 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -21,7 +21,7 @@ default = ["time"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", ] } diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 1e5494ef8..590718e3e 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -61,8 +61,8 @@ log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.2.0", path = "../embassy-macros" } -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} +embassy-macros = { version = "0.2.0", path = "../embassy-macros" } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" static_cell = "1.1" diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index dc44f96db..e4524af5b 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -20,7 +20,7 @@ defmt = ["dep:defmt", "lorawan-device/defmt"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index a52570f5d..26f5b40bd 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 41d411117..8972b814a 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -10,9 +10,9 @@ edition = "2021" [dependencies] embedded-hal = { version = "1.0.0-alpha.11" } embedded-hal-async = { version = "=0.2.0-alpha.2" } -embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} -embassy-time = { version = "0.1.0" } -embassy-futures = { version = "0.1.0" } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index e89039daa..6dc429ddc 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -50,7 +50,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [ ] } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } -embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index ae76af3e6..57dd22f1c 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -91,7 +91,7 @@ _dppi = [] _gpio-p1 = [] [dependencies] -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } @@ -106,7 +106,7 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -futures = { version = "0.3.17", default-features = false } +futures = { version = "0.3.17", default-features = false } critical-section = "1.1" rand_core = "0.6.3" fixed = "1.10.0" @@ -114,13 +114,13 @@ embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } cfg-if = "1.0.0" -nrf52805-pac = { version = "0.12.0", optional = true } -nrf52810-pac = { version = "0.12.0", optional = true } -nrf52811-pac = { version = "0.12.0", optional = true } -nrf52820-pac = { version = "0.12.0", optional = true } -nrf52832-pac = { version = "0.12.0", optional = true } -nrf52833-pac = { version = "0.12.0", optional = true } -nrf52840-pac = { version = "0.12.0", optional = true } +nrf52805-pac = { version = "0.12.0", optional = true } +nrf52810-pac = { version = "0.12.0", optional = true } +nrf52811-pac = { version = "0.12.0", optional = true } +nrf52820-pac = { version = "0.12.0", optional = true } +nrf52832-pac = { version = "0.12.0", optional = true } +nrf52833-pac = { version = "0.12.0", optional = true } +nrf52840-pac = { version = "0.12.0", optional = true } nrf5340-app-pac = { version = "0.12.0", optional = true } nrf5340-net-pac = { version = "0.12.0", optional = true } nrf9160-pac = { version = "0.12.0", optional = true } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index a06831a9a..8f3ed885d 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -56,7 +56,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 6d4027fcc..5141f9bd2 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -13,7 +13,7 @@ features = ["stm32wb55rg"] [dependencies] embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 045149636..ec934e8be 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -32,7 +32,7 @@ flavors = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index f4a7860e6..26640d930 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.1.2 - 2023-07-05 + +- Update `embedded-hal-async` to `0.2.0-alpha.2`. +- Update `embedded-hal v1` to `1.0.0-alpha.11`. (Note: v0.2 support is kept unchanged). + ## 0.1.1 - 2023-04-13 - Update `embedded-hal-async` to `0.2.0-alpha.1` (uses `async fn` in traits). diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0213eef03..0ff0e3beb 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-time" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "Instant and Duration for embedded no-std systems, with async timer support" repository = "https://github.com/embassy-rs/embassy" diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index b98f73f39..2a0cf7818 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf", features = ["nightly"] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 007b6839c..95b2da954 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } +embassy-time = { version = "0.1.2", 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", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 5b3faf8f8..3b0fc4d9d 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index b6a6f9cd8..323b4ab2c 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 0a7e19b1d..b2abdc891 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 998df4dc0..0b7e72d5e 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 10b58c172..5f3f365c1 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 713a6527e..44eb5aba8 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 4c8bbd73f..fdad55060 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index a3acc56b8..30b67b7b2 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -18,7 +18,7 @@ log = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time" } +embassy-time = { version = "0.1.2", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 0f9048b0f..ded3b7db8 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -9,7 +9,7 @@ rtic = { version = "2", features = ["thumbv7-backend"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nightly", "unstable-traits", "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "0.3" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 2ccd5045f..7b9c371bb 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -29,7 +29,7 @@ nightly = [ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index efb66bae6..f1d45f336 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -14,7 +14,7 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature "defmt", "integrated-timers", ] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = [ +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", ] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 17ebea86f..7c5a9dfbc 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 878ad8c5a..92933ab50 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 43f432520..26837abef 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 8d2248ed0..b7b5eaa99 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -14,6 +14,6 @@ defmt-rtt = "0.4" panic-probe = "0.3" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", 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", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index d34fd439a..29cad5b67 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 5e3e0d0f7..652210c7f 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 29ab2009c..489d0ff4c 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 7ecb64fce..c1c821364 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", 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", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 657251c50..84d7b79c5 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index c5245757b..c88282d91 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index fbfbc6408..18bd03c39 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 789ef59cc..227bc28b4 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", 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", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 04a2baab7..768702fa9 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", 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", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 988fd3a79..747cec7bf 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -12,7 +12,7 @@ nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstab [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } lora-phy = { version = "1", optional = true } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 93d48abeb..dcca1cc3d 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } defmt = "0.3" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index dca052c2f..c55558518 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 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", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 6035c291f..54911482e 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 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"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index e2318c3d6..835e32940 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index fbb2d918b..203ca1486 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 75a5f1c41..99f68387f 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 437e443a7..3679e3857 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" wasm-bindgen = "0.2" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 247287e46..7ce51aa5e 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -10,7 +10,7 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 81bfdfab6..61f886c0c 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" critical-section = { version = "1.1.1", features = ["restore-state-bool"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time" } +embassy-time = { version = "0.1.2", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } riscv-rt = "0.11" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index f1b48e747..368d4acf9 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1.1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index c69af6d5a..4fd4a6d0b 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -27,7 +27,7 @@ teleprobe-meta = "1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] } From 47305c2bf24c05b4956d9491478dfa53df09c47f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Jul 2023 02:32:49 +0200 Subject: [PATCH 1517/1575] ci: build doc with 4 threads instead of 6, to avoid running out of disk space. --- .github/ci/doc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index e4d83e4bc..9e9c78a42 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -6,7 +6,7 @@ set -euo pipefail export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target -export BUILDER_THREADS=6 +export BUILDER_THREADS=4 export BUILDER_COMPRESS=true # force rustup to download the toolchain before starting building. From ffeb40ff43ea52d0df5d5e6e807ecb87eafdf0c8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 6 Jul 2023 13:49:19 +0200 Subject: [PATCH 1518/1575] stm32/otg: change some info logs to trace. --- embassy-stm32/src/usb_otg/usb.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 6783db28d..492b77585 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -648,7 +648,7 @@ impl<'d, T: Instance> Bus<'d, T> { let r = T::regs(); let core_id = r.cid().read().0; - info!("Core id {:08x}", core_id); + trace!("Core id {:08x}", core_id); // Wait for AHB ready. while !r.grstctl().read().ahbidl() {} @@ -1214,7 +1214,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { let diepctl = r.diepctl(index).read(); let dtxfsts = r.dtxfsts(index).read(); - info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0); + trace!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0); if !diepctl.usbaep() { trace!("write ep={:?} wait for prev: error disabled", self.info.addr); Poll::Ready(Err(EndpointError::Disabled)) From f8d608093f51ab6fe97b32c7a23bf131a10b6f9c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 7 Jul 2023 03:41:27 +0200 Subject: [PATCH 1519/1575] stm32/otg: implement `EndpointError::Disabled` for reads. It was implemented only for writes. --- embassy-stm32/src/usb_otg/usb.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 492b77585..d0284746c 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -1154,14 +1154,22 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { trace!("read start len={}", buf.len()); poll_fn(|cx| { + let r = T::regs(); let index = self.info.addr.index(); let state = T::state(); state.ep_out_wakers[index].register(cx.waker()); + let doepctl = r.doepctl(index).read(); + trace!("read ep={:?}: doepctl {:08x}", self.info.addr, doepctl.0,); + if !doepctl.usbaep() { + trace!("read ep={:?} error disabled", self.info.addr); + return Poll::Ready(Err(EndpointError::Disabled)); + } + let len = state.ep_out_size[index].load(Ordering::Relaxed); if len != EP_OUT_BUFFER_EMPTY { - trace!("read done len={}", len); + trace!("read ep={:?} done len={}", self.info.addr, len); if len as usize > buf.len() { return Poll::Ready(Err(EndpointError::BufferOverflow)); @@ -1214,7 +1222,12 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { let diepctl = r.diepctl(index).read(); let dtxfsts = r.dtxfsts(index).read(); - trace!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0); + trace!( + "write ep={:?}: diepctl {:08x} ftxfsts {:08x}", + self.info.addr, + diepctl.0, + dtxfsts.0 + ); if !diepctl.usbaep() { trace!("write ep={:?} wait for prev: error disabled", self.info.addr); Poll::Ready(Err(EndpointError::Disabled)) From 4b63829110b8ef314d22d78c160f54e6ae98634c Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 7 Jul 2023 04:30:46 +0200 Subject: [PATCH 1520/1575] rp/pio: use bind_interrupts for irqs closes #1338 --- embassy-rp/src/lib.rs | 1 - embassy-rp/src/pio.rs | 78 ++++++++++------------- examples/rp/src/bin/pio_async.rs | 9 ++- examples/rp/src/bin/pio_dma.rs | 11 +++- examples/rp/src/bin/pio_hd44780.rs | 15 +++-- examples/rp/src/bin/pio_ws2812.rs | 13 +++- examples/rp/src/bin/wifi_ap_tcp_server.rs | 9 ++- examples/rp/src/bin/wifi_blinky.rs | 9 ++- examples/rp/src/bin/wifi_scan.rs | 9 ++- examples/rp/src/bin/wifi_tcp_server.rs | 9 ++- tests/rp/Cargo.toml | 2 + tests/rp/src/bin/cyw43-perf.rs | 10 ++- tests/rp/src/bin/pio_irq.rs | 55 ++++++++++++++++ 13 files changed, 160 insertions(+), 70 deletions(-) create mode 100644 tests/rp/src/bin/pio_irq.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 4fd3cb46a..4f205a16e 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -252,7 +252,6 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "time-driver")] timer::init(); dma::init(); - pio::init(); gpio::init(); } diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 30648e8ea..72a2f44ed 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -16,12 +16,12 @@ use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; -use crate::interrupt::InterruptExt; +use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; use crate::pac::dma::vals::TreqSel; use crate::relocate::RelocatedProgram; -use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; +use crate::{pac, peripherals, pio_instr_util, RegExt}; -struct Wakers([AtomicWaker; 12]); +pub struct Wakers([AtomicWaker; 12]); impl Wakers { #[inline(always)] @@ -38,10 +38,6 @@ impl Wakers { } } -const NEW_AW: AtomicWaker = AtomicWaker::new(); -const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); -static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; - #[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -85,42 +81,20 @@ const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; -#[cfg(feature = "rt")] -#[interrupt] -fn PIO0_IRQ_0() { - use crate::pac; - let ints = pac::PIO0.irqs(0).ints().read().0; - for bit in 0..12 { - if ints & (1 << bit) != 0 { - WAKERS[0].0[bit].wake(); - } - } - pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints); +pub struct InterruptHandler { + _pio: PhantomData, } -#[cfg(feature = "rt")] -#[interrupt] -fn PIO1_IRQ_0() { - use crate::pac; - let ints = pac::PIO1.irqs(0).ints().read().0; - for bit in 0..12 { - if ints & (1 << bit) != 0 { - WAKERS[1].0[bit].wake(); +impl Handler for InterruptHandler { + unsafe fn on_interrupt() { + let ints = PIO::PIO.irqs(0).ints().read().0; + for bit in 0..12 { + if ints & (1 << bit) != 0 { + PIO::wakers().0[bit].wake(); + } } + PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints); } - pac::PIO1.irqs(0).inte().write_clear(|m| m.0 = ints); -} - -pub(crate) unsafe fn init() { - interrupt::PIO0_IRQ_0.disable(); - interrupt::PIO0_IRQ_0.set_priority(interrupt::Priority::P3); - pac::PIO0.irqs(0).inte().write(|m| m.0 = 0); - interrupt::PIO0_IRQ_0.enable(); - - interrupt::PIO1_IRQ_0.disable(); - interrupt::PIO1_IRQ_0.set_priority(interrupt::Priority::P3); - pac::PIO1.irqs(0).inte().write(|m| m.0 = 0); - interrupt::PIO1_IRQ_0.enable(); } /// Future that waits for TX-FIFO to become writable @@ -144,7 +118,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI if self.get_mut().sm_tx.try_push(value) { Poll::Ready(()) } else { - WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker()); + PIO::wakers().fifo_out()[SM].register(cx.waker()); PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = TXNFULL_MASK << SM; }); @@ -181,7 +155,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO if let Some(v) = self.sm_rx.try_pull() { Poll::Ready(v) } else { - WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker()); + PIO::wakers().fifo_in()[SM].register(cx.waker()); PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = RXNEMPTY_MASK << SM; }); @@ -217,7 +191,7 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { return Poll::Ready(()); } - WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker()); + PIO::wakers().irq()[self.irq_no as usize].register(cx.waker()); PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = SMIRQ_MASK << self.irq_no; }); @@ -949,9 +923,11 @@ pub struct Pio<'d, PIO: Instance> { } impl<'d, PIO: Instance> Pio<'d, PIO> { - pub fn new(_pio: impl Peripheral

+ 'd) -> Self { + pub fn new(_pio: impl Peripheral

+ 'd, _irq: impl Binding>) -> Self { PIO::state().users.store(5, Ordering::Release); PIO::state().used_pins.store(0, Ordering::Release); + PIO::Interrupt::unpend(); + unsafe { PIO::Interrupt::enable() }; Self { common: Common { instructions_used: 0, @@ -1017,6 +993,15 @@ mod sealed { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; + type Interrupt: crate::interrupt::typelevel::Interrupt; + + #[inline] + fn wakers() -> &'static Wakers { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + static WAKERS: Wakers = Wakers([NEW_AW; 12]); + + &WAKERS + } #[inline] fn state() -> &'static State { @@ -1033,18 +1018,19 @@ mod sealed { pub trait Instance: sealed::Instance + Sized + Unpin {} macro_rules! impl_pio { - ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { + ($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => { impl sealed::Instance for peripherals::$name { const PIO_NO: u8 = $pio; const PIO: &'static pac::pio::Pio = &pac::$pac; const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; + type Interrupt = crate::interrupt::typelevel::$irq; } impl Instance for peripherals::$name {} }; } -impl_pio!(PIO0, 0, PIO0, PIO0_0); -impl_pio!(PIO1, 1, PIO1, PIO1_0); +impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0); +impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0); pub trait PioPin: sealed::PioPin + gpio::Pin {} diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 79eda1a09..69034c92a 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -3,13 +3,18 @@ #![feature(type_alias_impl_trait)] use defmt::info; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { // Setup sm0 @@ -110,7 +115,7 @@ async fn main(spawner: Spawner) { mut sm1, mut sm2, .. - } = Pio::new(pio); + } = Pio::new(pio, Irqs); setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0); setup_pio_task_sm1(&mut common, &mut sm1); diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 05c0ebb16..80c963556 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,13 +4,18 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::Peripheral; +use embassy_rp::{bind_interrupts, Peripheral}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + fn swap_nibbles(v: u32) -> u32 { let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; @@ -25,7 +30,7 @@ async fn main(_spawner: Spawner) { mut common, sm0: mut sm, .. - } = Pio::new(pio); + } = Pio::new(pio, Irqs); let prg = pio_proc::pio_asm!( ".origin 0", diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index bfc6c9908..0a4514a66 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -7,13 +7,19 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::pio::{ + Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, +}; use embassy_rp::pwm::{self, Pwm}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::{into_ref, Peripheral, PeripheralRef}; +use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(pub struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { // this test assumes a 2x16 HD44780 display attached as follow: @@ -37,7 +43,7 @@ async fn main(_spawner: Spawner) { }); let mut hd = HD44780::new( - p.PIO0, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, + p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, ) .await; @@ -72,6 +78,7 @@ pub struct HD44780<'l> { impl<'l> HD44780<'l> { pub async fn new( pio: impl Peripheral

+ 'l, + irq: Irqs, dma: impl Peripheral

+ 'l, rs: impl PioPin, rw: impl PioPin, @@ -88,7 +95,7 @@ impl<'l> HD44780<'l> { mut irq0, mut sm0, .. - } = Pio::new(pio); + } = Pio::new(pio, irq); // takes command words ( <0:4>) let prg = pio_proc::pio_asm!( diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 26422421f..4a111e7aa 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -5,15 +5,22 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; -use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{ + Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, +}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::{clocks, into_ref, Peripheral, PeripheralRef}; +use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use fixed::types::U24F8; use fixed_macro::fixed; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { dma: PeripheralRef<'d, AnyChannel>, sm: StateMachine<'d, P, S>, @@ -123,7 +130,7 @@ async fn main(_spawner: Spawner) { info!("Start"); let p = embassy_rp::init(Default::default()); - let Pio { mut common, sm0, .. } = Pio::new(p.PIO0); + let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit // feather boards for the 2040 both have one built in. diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 310e84d92..3e41f83be 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -11,14 +11,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::Duration; use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -49,7 +54,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index bbcb1b5ec..6eb207af6 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -5,13 +5,18 @@ use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::{Duration, Timer}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -34,7 +39,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index 391e12282..aef18aa24 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -10,12 +10,17 @@ use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; use embassy_net::Stack; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -46,7 +51,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 197535f45..4fce74a66 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -11,14 +11,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::Duration; use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + const WIFI_NETWORK: &str = "EmbassyTest"; const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; @@ -52,7 +57,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 368d4acf9..f2c902787 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -29,6 +29,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa embedded-io = { version = "0.4.0", features = ["async"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} +pio = "0.2" +pio-proc = "0.2" [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 1ecaab266..bc127e2e5 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -12,12 +12,16 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Stack, StackResources}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; -use embassy_rp::rom_data; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::{bind_interrupts, rom_data}; use embassy_time::{with_timeout, Duration, Timer}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + teleprobe_meta::timeout!(120); #[embassy_executor::task] @@ -51,7 +55,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs new file mode 100644 index 000000000..45004424a --- /dev/null +++ b/tests/rp/src/bin/pio_irq.rs @@ -0,0 +1,55 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{Config, InterruptHandler, Pio}; +use embassy_rp::relocate::RelocatedProgram; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + let Pio { + mut common, + sm0: mut sm, + irq_flags, + .. + } = Pio::new(pio, Irqs); + + let prg = pio_proc::pio_asm!( + "irq set 0", + "irq wait 0", + "irq set 1", + // pause execution here + "irq wait 1", + ); + + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[]); + sm.set_config(&cfg); + sm.set_enable(true); + + // not using the wait futures on purpose because they clear the irq bits, + // and we want to see in which order they are set. + while !irq_flags.check(0) {} + cortex_m::asm::nop(); + assert!(!irq_flags.check(1)); + irq_flags.clear(0); + cortex_m::asm::nop(); + assert!(irq_flags.check(1)); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 151557fec38a454d0ff29de0e32914fed12869fd Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Fri, 7 Jul 2023 16:38:56 +0200 Subject: [PATCH 1521/1575] Re-export smoltcp::wire::IpEndpoint --- embassy-net/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 840d7a09a..0d0a986f6 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -34,7 +34,7 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig}; pub use smoltcp::wire::IpListenEndpoint; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; -pub use smoltcp::wire::{IpAddress, IpCidr}; +pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] From 972cdd4265b24efb101c6b9df373466fcbccac5d Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 5 Jul 2023 20:05:34 +0200 Subject: [PATCH 1522/1575] rp/adc: rewrite the module - don't require an irq binding for blocking-only adc - abstract adc pins into an AnyPin like interface, erasing the actual peripheral type at runtime. - add pull-up/pull-down functions for adc pins - add a test (mostly a copy of the example, to be honest) - configure adc pads according to datasheet - report conversion errors (although they seem exceedingly rare?) - drop embedded-hal interfaces. embedded-hal channels can do neither AnyPin nor pullup/pulldown without encoding both into the type --- embassy-rp/src/adc.rs | 227 +++++++++++++++++++++++-------------- embassy-rp/src/gpio.rs | 2 +- examples/rp/src/bin/adc.rs | 17 +-- tests/rp/src/bin/adc.rs | 86 ++++++++++++++ 4 files changed, 238 insertions(+), 94 deletions(-) create mode 100644 tests/rp/src/bin/adc.rs diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 699a0d61d..dfa1b877a 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -3,22 +3,17 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use embedded_hal_02::adc::{Channel, OneShot}; -use crate::gpio::Pin; +use crate::gpio::sealed::Pin as GpioPin; +use crate::gpio::{self, AnyPin, Pull}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; use crate::peripherals::ADC; use crate::{interrupt, 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 -} +static WAKER: AtomicWaker = AtomicWaker::new(); #[non_exhaustive] pub struct Config {} @@ -28,11 +23,65 @@ impl Default for Config { Self {} } } -pub struct Adc<'d> { - phantom: PhantomData<&'d ADC>, + +pub struct Pin<'p> { + pin: PeripheralRef<'p, AnyPin>, } -impl<'d> Adc<'d> { +impl<'p> Pin<'p> { + pub fn new(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { + into_ref!(pin); + pin.pad_ctrl().modify(|w| { + // manual says: + // + // > When using an ADC input shared with a GPIO pin, the pin’s + // > digital functions must be disabled by setting IE low and OD + // > high in the pin’s pad control register + w.set_ie(false); + w.set_od(true); + w.set_pue(pull == Pull::Up); + w.set_pde(pull == Pull::Down); + }); + Self { pin: pin.map_into() } + } + + fn channel(&self) -> u8 { + // this requires adc pins to be sequential and matching the adc channels, + // which is the case for rp2040 + self.pin._pin() - 26 + } +} + +impl<'d> Drop for Pin<'d> { + fn drop(&mut self) { + self.pin.pad_ctrl().modify(|w| { + w.set_ie(true); + w.set_od(false); + w.set_pue(false); + w.set_pde(true); + }); + } +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + ConversionFailed, +} + +pub trait Mode {} + +pub struct Async; +impl Mode for Async {} + +pub struct Blocking; +impl Mode for Blocking {} + +pub struct Adc<'d, M: Mode> { + phantom: PhantomData<(&'d ADC, M)>, +} + +impl<'d, M: Mode> Adc<'d, M> { #[inline] fn regs() -> pac::adc::Adc { pac::ADC @@ -45,11 +94,7 @@ impl<'d> Adc<'d> { ret } - pub fn new( - _inner: impl Peripheral

+ 'd, - _irq: impl Binding, - _config: Config, - ) -> Self { + fn setup() { let reset = Self::reset(); crate::reset::reset(reset); crate::reset::unreset_wait(reset); @@ -58,6 +103,43 @@ impl<'d> Adc<'d> { r.cs().write(|w| w.set_en(true)); // Wait for ADC ready while !r.cs().read().ready() {} + } + + fn sample_blocking(channel: u8) -> Result { + let r = Self::regs(); + r.cs().modify(|w| { + w.set_ainsel(channel); + w.set_start_once(true); + w.set_err(true); + }); + while !r.cs().read().ready() {} + match r.cs().read().err() { + true => Err(Error::ConversionFailed), + false => Ok(r.result().read().result().into()), + } + } + + pub fn blocking_read(&mut self, pin: &mut Pin) -> Result { + Self::sample_blocking(pin.channel()) + } + + pub fn blocking_read_temperature(&mut self) -> Result { + let r = Self::regs(); + r.cs().modify(|w| w.set_ts_en(true)); + while !r.cs().read().ready() {} + let result = Self::sample_blocking(4); + r.cs().modify(|w| w.set_ts_en(false)); + result + } +} + +impl<'d> Adc<'d, Async> { + pub fn new( + _inner: impl Peripheral

+ 'd, + _irq: impl Binding, + _config: Config, + ) -> Self { + Self::setup(); // Setup IRQ interrupt::ADC_IRQ_FIFO.unpend(); @@ -80,76 +162,42 @@ impl<'d> Adc<'d> { .await; } - pub async fn read, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { + async fn sample_async(channel: u8) -> Result { let r = Self::regs(); - // disable pull-down and pull-up resistors - // pull-down resistors are enabled by default - pin.pad_ctrl().modify(|w| { - w.set_ie(true); - let (pu, pd) = (false, false); - w.set_pue(pu); - w.set_pde(pd); - }); r.cs().modify(|w| { - w.set_ainsel(PIN::channel()); - w.set_start_once(true) + w.set_ainsel(channel); + w.set_start_once(true); + w.set_err(true); }); Self::wait_for_ready().await; - r.result().read().result().into() + match r.cs().read().err() { + true => Err(Error::ConversionFailed), + false => Ok(r.result().read().result().into()), + } } - pub async fn read_temperature(&mut self) -> u16 { + pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result { + Self::sample_async(pin.channel()).await + } + + pub async fn read_temperature(&mut self) -> Result { let r = Self::regs(); 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> + Pin>(&mut self, pin: &mut PIN) -> u16 { - let r = Self::regs(); - pin.pad_ctrl().modify(|w| { - w.set_ie(true); - let (pu, pd) = (false, false); - w.set_pue(pu); - w.set_pde(pd); - }); - 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(); - 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() + let result = Self::sample_async(4).await; + r.cs().modify(|w| w.set_ts_en(false)); + result } } -macro_rules! impl_pin { - ($pin:ident, $channel:expr) => { - impl Channel> for peripherals::$pin { - type ID = u8; - fn channel() -> u8 { - $channel - } - } - }; +impl<'d> Adc<'d, Blocking> { + pub fn new_blocking(_inner: impl Peripheral

+ 'd, _config: Config) -> Self { + Self::setup(); + + Self { phantom: PhantomData } + } } pub struct InterruptHandler { @@ -158,24 +206,33 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let r = Adc::regs(); + let r = Adc::::regs(); r.inte().write(|w| w.set_fifo(false)); WAKER.wake(); } } +mod sealed { + pub trait AdcPin: crate::gpio::sealed::Pin { + fn channel(&mut self) -> u8; + } +} + +pub trait AdcPin: sealed::AdcPin + gpio::Pin {} + +macro_rules! impl_pin { + ($pin:ident, $channel:expr) => { + impl sealed::AdcPin for peripherals::$pin { + fn channel(&mut self) -> u8 { + $channel + } + } + + impl AdcPin for peripherals::$pin {} + }; +} + 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> + Pin, -{ - type Error = (); - fn read(&mut self, pin: &mut PIN) -> nb::Result { - Ok(self.blocking_read(pin).into()) - } -} diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index f8048a4dd..d18fb909c 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -41,7 +41,7 @@ impl From for bool { } /// Represents a pull setting for an input. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Pull { None, Up, diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 7c2ca19f7..65069cde1 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -4,8 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler}; +use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; use embassy_rp::bind_interrupts; +use embassy_rp::gpio::Pull; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -18,18 +19,18 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut adc = Adc::new(p.ADC, Irqs, Config::default()); - let mut p26 = p.PIN_26; - let mut p27 = p.PIN_27; - let mut p28 = p.PIN_28; + let mut p26 = Pin::new(p.PIN_26, Pull::None); + let mut p27 = Pin::new(p.PIN_27, Pull::None); + let mut p28 = Pin::new(p.PIN_28, Pull::None); loop { - let level = adc.read(&mut p26).await; + let level = adc.read(&mut p26).await.unwrap(); info!("Pin 26 ADC: {}", level); - let level = adc.read(&mut p27).await; + let level = adc.read(&mut p27).await.unwrap(); info!("Pin 27 ADC: {}", level); - let level = adc.read(&mut p28).await; + let level = adc.read(&mut p28).await.unwrap(); info!("Pin 28 ADC: {}", level); - let temp = adc.read_temperature().await; + let temp = adc.read_temperature().await.unwrap(); info!("Temp: {} degrees", convert_to_celsius(temp)); Timer::after(Duration::from_secs(1)).await; } diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs new file mode 100644 index 000000000..e659844ae --- /dev/null +++ b/tests/rp/src/bin/adc.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::Pull; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC_IRQ_FIFO => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_rp::init(Default::default()); + let mut adc = Adc::new(p.ADC, Irqs, Config::default()); + + { + { + let mut p = Pin::new(&mut p.PIN_26, Pull::Down); + defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); + defmt::assert!(adc.read(&mut p).await.unwrap() < 0b01_0000_0000); + } + { + let mut p = Pin::new(&mut p.PIN_26, Pull::Up); + defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); + defmt::assert!(adc.read(&mut p).await.unwrap() > 0b11_0000_0000); + } + } + // not bothering with async reads from now on + { + { + let mut p = Pin::new(&mut p.PIN_27, Pull::Down); + defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); + } + { + let mut p = Pin::new(&mut p.PIN_27, Pull::Up); + defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); + } + } + { + { + let mut p = Pin::new(&mut p.PIN_28, Pull::Down); + defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); + } + { + let mut p = Pin::new(&mut p.PIN_28, Pull::Up); + defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); + } + } + { + // gp29 is connected to vsys through a 200k/100k divider, + // adding pulls should change the value + let low = { + let mut p = Pin::new(&mut p.PIN_29, Pull::Down); + adc.blocking_read(&mut p).unwrap() + }; + let none = { + let mut p = Pin::new(&mut p.PIN_29, Pull::None); + adc.blocking_read(&mut p).unwrap() + }; + let up = { + let mut p = Pin::new(&mut p.PIN_29, Pull::Up); + adc.blocking_read(&mut p).unwrap() + }; + defmt::assert!(low < none); + defmt::assert!(none < up); + } + + let temp = convert_to_celsius(adc.read_temperature().await.unwrap()); + defmt::assert!(temp > 0.0); + defmt::assert!(temp < 60.0); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +fn convert_to_celsius(raw_temp: u16) -> f32 { + // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet + 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32 +} From 69b4e898b3ac9e21deef56cb1dde34aa860e3f00 Mon Sep 17 00:00:00 2001 From: David Purser <60577982+davidpurser@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:52:44 -0500 Subject: [PATCH 1523/1575] Correctly calculate target VCO frequency from multipliers --- embassy-stm32/src/rcc/h7.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index f3a98c794..7e5cd0d1a 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -740,7 +740,7 @@ mod pll { } }; - let vco_ck = output + pll_x_p; + let vco_ck = output * pll_x_p; assert!(pll_x_p < 128); assert!(vco_ck >= VCO_MIN); From 05c524a7db1ee1ecc5397570a5dd00faf4b7316a Mon Sep 17 00:00:00 2001 From: James Waples Date: Sat, 8 Jul 2023 11:20:26 +0100 Subject: [PATCH 1524/1575] Enable `critical-section/std` when using `std` feature of `embassy-time` --- embassy-time/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0ff0e3beb..0afb1103d 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -23,7 +23,7 @@ target = "x86_64-unknown-linux-gnu" features = ["nightly", "defmt", "unstable-traits", "std"] [features] -std = ["tick-hz-1_000_000"] +std = ["tick-hz-1_000_000", "critical-section/std"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] # Enable nightly-only features From 735d676a725999eda3869e7323c6264c2e8c2cb9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 9 Jul 2023 15:50:01 -0500 Subject: [PATCH 1525/1575] wpan: update alignment control --- embassy-stm32-wpan/src/cmd.rs | 2 +- embassy-stm32-wpan/src/consts.rs | 1 + embassy-stm32-wpan/src/lib.rs | 40 ++++++++----- embassy-stm32-wpan/src/sub/mm.rs | 12 ++-- embassy-stm32-wpan/src/tables.rs | 96 ++++++++++++++++++-------------- 5 files changed, 90 insertions(+), 61 deletions(-) diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index 8428b6ffc..928357384 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -37,7 +37,7 @@ pub struct CmdSerialStub { } #[derive(Copy, Clone, Default)] -#[repr(C, packed(4))] +#[repr(C, packed)] pub struct CmdPacket { pub header: PacketHeader, pub cmdserial: CmdSerial, diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs index f234151d7..1c84addb1 100644 --- a/embassy-stm32-wpan/src/consts.rs +++ b/embassy-stm32-wpan/src/consts.rs @@ -79,6 +79,7 @@ pub const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; pub const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE; pub const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); +pub const C_SIZE_CMD_STRING: usize = 256; pub const fn divc(x: usize, y: usize) -> usize { (x + y - 1) / y diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 99c610583..3a45c5978 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -60,9 +60,9 @@ impl<'d> TlMbox<'d> { mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), traces_table: TL_TRACES_TABLE.as_ptr(), mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), - // zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), - // lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), - // ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), + zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), + lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), + ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), }); TL_SYS_TABLE @@ -87,15 +87,15 @@ impl<'d> TlMbox<'d> { TL_MAC_802_15_4_TABLE .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); - // TL_ZIGBEE_TABLE - // .as_mut_ptr() - // .write_volatile(MaybeUninit::zeroed().assume_init()); - // TL_LLD_TESTS_TABLE - // .as_mut_ptr() - // .write_volatile(MaybeUninit::zeroed().assume_init()); - // TL_BLE_LLD_TABLE - // .as_mut_ptr() - // .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_ZIGBEE_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_LLD_TESTS_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_BLE_LLD_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); EVT_POOL .as_mut_ptr() @@ -103,18 +103,30 @@ impl<'d> TlMbox<'d> { SYS_SPARE_EVT_BUF .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); - BLE_SPARE_EVT_BUF + CS_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); + #[cfg(feature = "ble")] { + BLE_SPARE_EVT_BUF + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + BLE_CMD_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); HCI_ACL_DATA_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); - CS_BUFFER + } + + #[cfg(feature = "mac")] + { + MAC_802_15_4_CMD_BUFFER + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + MAC_802_15_4_NOTIF_RSP_EVT_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); } diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index 1f2ecac2e..da05ad1dd 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs @@ -4,20 +4,21 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; use core::task::Poll; +use aligned::{Aligned, A4}; use cortex_m::interrupt; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; use crate::consts::POOL_SIZE; use crate::evt::EvtPacket; -use crate::tables::{ - MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, -}; +#[cfg(feature = "ble")] +use crate::tables::BLE_SPARE_EVT_BUF; +use crate::tables::{MemManagerTable, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, evt}; static MM_WAKER: AtomicWaker = AtomicWaker::new(); -static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +static mut LOCAL_FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); pub struct MemoryManager { phantom: PhantomData, @@ -30,7 +31,10 @@ impl MemoryManager { LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { + #[cfg(feature = "ble")] spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), + #[cfg(not(feature = "ble"))] + spare_ble_buffer: core::ptr::null(), spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), blepool: EVT_POOL.as_ptr().cast(), blepoolsize: POOL_SIZE as u32, diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 1b5dcdf2e..f2c250527 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -4,6 +4,8 @@ use aligned::{Aligned, A4}; use bit_field::BitField; use crate::cmd::{AclDataPacket, CmdPacket}; +#[cfg(feature = "mac")] +use crate::consts::C_SIZE_CMD_STRING; use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; use crate::unsafe_linked_list::LinkedListNode; @@ -80,7 +82,7 @@ impl WirelessFwInfoTable { } #[derive(Debug, Clone)] -#[repr(C, align(4))] +#[repr(C)] pub struct DeviceInfoTable { pub safe_boot_info_table: SafeBootInfoTable, pub rss_info_table: RssInfoTable, @@ -88,7 +90,7 @@ pub struct DeviceInfoTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct BleTable { pub pcmd_buffer: *mut CmdPacket, pub pcs_buffer: *const u8, @@ -97,16 +99,15 @@ pub struct BleTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct ThreadTable { pub nostack_buffer: *const u8, pub clicmdrsp_buffer: *const u8, pub otcmdrsp_buffer: *const u8, } -// TODO: use later #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct LldTestsTable { pub clicmdrsp_buffer: *const u8, pub m0cmd_buffer: *const u8, @@ -114,7 +115,7 @@ pub struct LldTestsTable { // TODO: use later #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct BleLldTable { pub cmdrsp_buffer: *const u8, pub m0cmd_buffer: *const u8, @@ -122,7 +123,7 @@ pub struct BleLldTable { // TODO: use later #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct ZigbeeTable { pub notif_m0_to_m4_buffer: *const u8, pub appli_cmd_m4_to_m0_bufer: *const u8, @@ -130,14 +131,14 @@ pub struct ZigbeeTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct SysTable { pub pcmd_buffer: *mut CmdPacket, pub sys_queue: *const LinkedListNode, } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct MemManagerTable { pub spare_ble_buffer: *const u8, pub spare_sys_buffer: *const u8, @@ -152,13 +153,13 @@ pub struct MemManagerTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct TracesTable { pub traces_queue: *const u8, } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct Mac802_15_4Table { pub p_cmdrsp_buffer: *const u8, pub p_notack_buffer: *const u8, @@ -176,6 +177,9 @@ pub struct RefTable { pub mem_manager_table: *const MemManagerTable, pub traces_table: *const TracesTable, pub mac_802_15_4_table: *const Mac802_15_4Table, + pub zigbee_table: *const ZigbeeTable, + pub lld_tests_table: *const LldTestsTable, + pub ble_lld_table: *const BleLldTable, } // --------------------- ref table --------------------- @@ -183,57 +187,57 @@ pub struct RefTable { pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM1"] -pub static mut TL_DEVICE_INFO_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_DEVICE_INFO_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_BLE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); - -// #[link_section = "MB_MEM1"] -// pub static mut TL_LLD_TESTS_TABLE: MaybeUninit = MaybeUninit::uninit(); - -// #[link_section = "MB_MEM1"] -// pub static mut TL_BLE_LLD_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_THREAD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_LLD_TESTS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_MEM_MANAGER_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_BLE_LLD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_SYS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_MEM_MANAGER_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -// #[link_section = "MB_MEM1"] -// pub static mut TL_ZIGBEE_TABLE: MaybeUninit = MaybeUninit::uninit(); +#[link_section = "MB_MEM1"] +pub static mut TL_TRACES_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); + +#[link_section = "MB_MEM1"] +pub static mut TL_MAC_802_15_4_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); + +#[link_section = "MB_MEM1"] +pub static mut TL_ZIGBEE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- tables --------------------- #[link_section = "MB_MEM1"] -pub static mut FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[allow(dead_code)] #[link_section = "MB_MEM1"] -pub static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TRACES_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut CS_BUFFER: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut CS_BUFFER: Aligned> = + Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut SYSTEM_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- app tables --------------------- #[cfg(feature = "mac")] #[link_section = "MB_MEM2"] -pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); +pub static mut MAC_802_15_4_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] #[link_section = "MB_MEM2"] @@ -242,23 +246,31 @@ pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< > = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] -pub static mut EVT_POOL: MaybeUninit> = MaybeUninit::uninit(); +pub static mut EVT_POOL: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); +pub static mut SYS_CMD_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut SYS_SPARE_EVT_BUF: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut SYS_SPARE_EVT_BUF: Aligned> = + Aligned(MaybeUninit::uninit()); +#[cfg(feature = "mac")] +#[link_section = "MB_MEM2"] +pub static mut MAC_802_15_4_CNFINDNOT: Aligned> = + Aligned(MaybeUninit::uninit()); + +#[cfg(feature = "ble")] #[link_section = "MB_MEM1"] -pub static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); +pub static mut BLE_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); +#[cfg(feature = "ble")] #[link_section = "MB_MEM2"] -pub static mut BLE_SPARE_EVT_BUF: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut BLE_SPARE_EVT_BUF: Aligned> = + Aligned(MaybeUninit::uninit()); +#[cfg(feature = "ble")] #[link_section = "MB_MEM2"] // fuck these "magic" numbers from ST ---v---v -pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut HCI_ACL_DATA_BUFFER: Aligned> = + Aligned(MaybeUninit::uninit()); From c1bf5aee247060a0251fe13eefcb3c7369f44eb9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 9 Jul 2023 16:01:13 -0500 Subject: [PATCH 1526/1575] mac: move table initialization after sys ready --- embassy-stm32-wpan/src/sub/mac.rs | 8 -------- embassy-stm32-wpan/src/sub/sys.rs | 19 +++++++++++++++++++ examples/stm32wb/src/bin/tl_mbox_mac.rs | 12 +++++++++++- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index fd8af8609..62612dbfb 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -25,14 +25,6 @@ pub struct Mac { impl Mac { pub(crate) fn new() -> Self { - unsafe { - TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { - p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), - p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), - evt_queue: ptr::null_mut(), - }); - } - Self { phantom: PhantomData } } diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index af652860d..caa4845f2 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -65,6 +65,25 @@ impl Sys { #[cfg(feature = "mac")] pub async fn shci_c2_mac_802_15_4_init(&self) -> SchiCommandStatus { + use crate::tables::{ + Mac802_15_4Table, TracesTable, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, + TL_MAC_802_15_4_TABLE, TL_TRACES_TABLE, TRACES_EVT_QUEUE, + }; + + unsafe { + LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _); + + TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable { + traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _, + }); + + TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { + p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), + p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), + evt_queue: core::ptr::null_mut(), + }); + }; + self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await } diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index f67be4682..5931c392b 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -14,8 +15,13 @@ bind_interrupts!(struct Irqs{ IPCC_C1_TX => TransmitInterruptHandler; }); +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { /* How to make this work: @@ -46,9 +52,13 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mbox = TlMbox::init(p.IPCC, Irqs, config); + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); + core::mem::drop(sys_event); + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); From bf4493dbdfe6f1d71e10af08b2094576c8fa7499 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 9 Jul 2023 16:08:39 -0500 Subject: [PATCH 1527/1575] rustfmt --- embassy-stm32-wpan/src/sub/mac.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index 62612dbfb..f7a59b0e4 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -11,9 +11,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; -use crate::tables::{ - Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE, -}; +use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; static MAC_WAKER: AtomicWaker = AtomicWaker::new(); From 4aca7c8811b70e420280893784cdad2acbe326f9 Mon Sep 17 00:00:00 2001 From: goueslati Date: Mon, 10 Jul 2023 16:54:48 +0100 Subject: [PATCH 1528/1575] wip --- embassy-stm32-wpan/Cargo.toml | 1 + embassy-stm32-wpan/src/sub/mac/commands.rs | 83 +++++++++++++ .../src/sub/{mac.rs => mac/mod.rs} | 18 +++ embassy-stm32-wpan/src/sub/mac/opcodes.rs | 27 ++++ examples/stm32wb/.cargo/config.toml | 4 +- examples/stm32wb/Cargo.toml | 6 +- examples/stm32wb/src/bin/tl_mbox_mac_2.rs | 117 ++++++++++++++++++ rust-toolchain.toml | 4 +- 8 files changed, 255 insertions(+), 5 deletions(-) create mode 100644 embassy-stm32-wpan/src/sub/mac/commands.rs rename embassy-stm32-wpan/src/sub/{mac.rs => mac/mod.rs} (88%) create mode 100644 embassy-stm32-wpan/src/sub/mac/opcodes.rs create mode 100644 examples/stm32wb/src/bin/tl_mbox_mac_2.rs diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 5141f9bd2..4b5dcdd29 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -28,6 +28,7 @@ stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true } [features] +default = ["stm32wb55rg", "mac", "ble"] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] ble = ["dep:stm32wb-hci"] diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/sub/mac/commands.rs new file mode 100644 index 000000000..75a31d2fc --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/commands.rs @@ -0,0 +1,83 @@ +use bit_field::BitField; + +use super::opcodes::OpcodeM4ToM0; + +pub trait MacCommand { + type Response; + const OPCODE: OpcodeM4ToM0; + const SIZE: usize; + + fn copy_into_slice(&self, buf: &mut [u8]); +} + +pub struct ResetRequest { + /// MAC PIB attributes are set to their default values or not during reset + pub set_default_pib: bool, +} + +impl MacCommand for ResetRequest { + type Response = (); + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq; + const SIZE: usize = 4; + + fn copy_into_slice(&self, buf: &mut [u8]) { + buf[0] = self.set_default_pib as u8; + } +} + +#[repr(C)] +pub struct SetRequest { + pub pib_attribute_ptr: *const u8, + pub pib_attribute: u8, + pub stuffing: [u8; 3], +} + +impl MacCommand for SetRequest { + type Response = (); + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq; + const SIZE: usize = 8; + + fn copy_into_slice(&self, buf: &mut [u8]) { + let address = self.pib_attribute_ptr as usize; + + // 68 ff 2 20 6f + + let a = unsafe { core::slice::from_raw_parts(&self as *const _ as *const u8, Self::SIZE) }; + debug!("{:#04x}", a); + + unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, 8) }; + + // buf[0] = self.pib_attribute_ptr as u8; + // buf[1] = self.pib_attribute; + } +} + +pub struct AssociateRequest { + pub channel_number: u8, + pub channel_page: u8, + pub coord_addr_mode: u8, + pub capability_information: u8, + pub coord_pan_id: [u8; 2], + pub security_level: u8, + pub key_id_mode: u8, + pub key_source: [u8; 8], + pub coord_address: MacAddress, + pub key_index: u8, +} + +impl MacCommand for AssociateRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq; + const SIZE: usize = 25; + type Response = (); + + fn copy_into_slice(&self, buf: &mut [u8]) { + let a = unsafe { core::slice::from_raw_parts(&self as *const _ as *const u8, core::mem::size_of::()) }; + + buf[..a.len()].copy_from_slice(a); + } +} + +pub union MacAddress { + pub short: [u8; 2], + pub extended: [u8; 8], +} diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs similarity index 88% rename from embassy-stm32-wpan/src/sub/mac.rs rename to embassy-stm32-wpan/src/sub/mac/mod.rs index f7a59b0e4..83970a881 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -8,12 +8,16 @@ use embassy_futures::poll_once; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; +use self::commands::MacCommand; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; +pub mod commands; +mod opcodes; + static MAC_WAKER: AtomicWaker = AtomicWaker::new(); static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); @@ -77,8 +81,22 @@ impl Mac { }) .await; } + + pub async fn send_command(&self, cmd: T) -> u8 + where + T: MacCommand, + { + let mut payload = [0u8; MAX_PACKET_SIZE]; + cmd.copy_into_slice(&mut payload); + + debug!("sending {:#x}", payload[..T::SIZE]); + + self.write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]).await + } } +const MAX_PACKET_SIZE: usize = 255; + impl evt::MemoryManager for Mac { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(_: *mut EvtPacket) { diff --git a/embassy-stm32-wpan/src/sub/mac/opcodes.rs b/embassy-stm32-wpan/src/sub/mac/opcodes.rs new file mode 100644 index 000000000..511b78157 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/opcodes.rs @@ -0,0 +1,27 @@ +const ST_VENDOR_OGF: u16 = 0x3F; +const MAC_802_15_4_CMD_OPCODE_OFFSET: u16 = 0x280; + +const fn opcode(ocf: u16) -> isize { + ((ST_VENDOR_OGF << 9) | (MAC_802_15_4_CMD_OPCODE_OFFSET + ocf)) as isize +} + +pub enum OpcodeM4ToM0 { + MlmeAssociateReq = opcode(0x00), + MlmeAssociateRes = opcode(0x01), + MlmeDisassociateReq = opcode(0x02), + MlmeGetReq = opcode(0x03), + MlmeGtsReq = opcode(0x04), + MlmeOrphanRes = opcode(0x05), + MlmeResetReq = opcode(0x06), + MlmeRxEnableReq = opcode(0x07), + MlmeScanReq = opcode(0x08), + MlmeSetReq = opcode(0x09), + MlmeStartReq = opcode(0x0A), + MlmeSyncReq = opcode(0x0B), + MlmePollReq = opcode(0x0C), + MlmeDpsReq = opcode(0x0D), + MlmeSoundingReq = opcode(0x0E), + MlmeCalibrateReq = opcode(0x0F), + McpsDataReq = opcode(0x10), + McpsPurgeReq = opcode(0x11), +} diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 8b6d6d754..cf62a10a0 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,7 +1,7 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` -# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" -runner = "teleprobe local run --chip STM32WB55RG --elf" +runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" +# runner = "teleprobe local run --chip STM32WB55RG --elf" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 203ca1486..109734546 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -23,7 +23,7 @@ heapless = { version = "0.7.5", default-features = false } [features] -default = ["ble"] +default = ["ble", "mac"] mac = ["embassy-stm32-wpan/mac"] ble = ["embassy-stm32-wpan/ble"] @@ -35,6 +35,10 @@ required-features = ["ble"] name = "tl_mbox_mac" required-features = ["mac"] +[[bin]] +name = "tl_mbox_mac_2" +required-features = ["mac"] + [[bin]] name = "eddystone_beacon" required-features = ["ble"] diff --git a/examples/stm32wb/src/bin/tl_mbox_mac_2.rs b/examples/stm32wb/src/bin/tl_mbox_mac_2.rs new file mode 100644 index 000000000..e069adf87 --- /dev/null +++ b/examples/stm32wb/src/bin/tl_mbox_mac_2.rs @@ -0,0 +1,117 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, MacAddress, ResetRequest, SetRequest}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + let response = mbox + .mac_subsystem + .send_command(ResetRequest { set_default_pib: true }) + .await; + info!("{}", response); + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000001; + defmt::debug!("{}", &extended_address as *const _ as *const u8); + let response = mbox + .mac_subsystem + .send_command(SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: 0x6F, + stuffing: [0; 3], + }) + .await; + info!("{}", response); + + // info!("association request"); + // mbox.mac_subsystem + // .send_command(AssociateRequest { + // channel_number: 16, + // channel_page: 0, + // coord_addr_mode: 2, + // coord_address: MacAddress { short: [0x22, 0x11] }, + // capability_information: 0x80, + // coord_pan_id: [0xAA, 0x1A], + // security_level: 0, + + // key_id_mode: 0, + // key_index: 0, + // key_source: [0; 8], + // }) + // .await; + // info!("reading"); + // let result = mbox.mac_subsystem.read().await; + // info!("{}", result.payload()); + + // + // info!("starting ble..."); + // mbox.ble_subsystem.t_write(0x0c, &[]).await; + // + // info!("waiting for ble..."); + // let ble_event = mbox.ble_subsystem.tl_read().await; + // + // info!("ble event: {}", ble_event.payload()); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5db74c7a9..bad6d3a42 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,8 +1,8 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-06-28" -components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] +channel = "nightly" +components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", "thumbv7m-none-eabi", From 29f3d5b68d3b5949de35227359bf631661613b14 Mon Sep 17 00:00:00 2001 From: shakencodes Date: Mon, 10 Jul 2023 16:40:18 -0700 Subject: [PATCH 1529/1575] Ensure I2C master_stop() called after error --- embassy-stm32/src/i2c/v2.rs | 52 +++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 1f036d55c..208d1527d 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -382,13 +382,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // I2C start // // ST SAD+W - Self::master_write( + if let Err(err) = Self::master_write( address, write.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout, - )?; + ) { + if send_stop { + self.master_stop(); + } + return Err(err); + } for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { @@ -399,18 +404,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe(&check_timeout)?; + if let Err(err) = self.wait_txe(&check_timeout) { + if send_stop { + self.master_stop(); + } + return Err(err); + } T::regs().txdr().write(|w| w.set_txdata(*byte)); } } // Wait until the write finishes - self.wait_tc(&check_timeout)?; - + let result = self.wait_tc(&check_timeout); if send_stop { self.master_stop(); } - Ok(()) + result } async fn write_dma_internal( @@ -707,13 +716,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let first_length = write[0].len(); let last_slice_index = write.len() - 1; - Self::master_write( + if let Err(err) = Self::master_write( address, first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), &check_timeout, - )?; + ) { + self.master_stop(); + return Err(err); + } for (idx, slice) in write.iter().enumerate() { let slice_len = slice.len(); @@ -726,27 +738,36 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let last_chunk_idx = total_chunks.saturating_sub(1); if idx != 0 { - Self::master_continue( + if let Err(err) = Self::master_continue( slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), &check_timeout, - )?; + ) { + self.master_stop(); + return Err(err); + } } for (number, chunk) in slice.chunks(255).enumerate() { if number != 0 { - Self::master_continue( + if let Err(err) = Self::master_continue( chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), &check_timeout, - )?; + ) { + self.master_stop(); + return Err(err); + } } for byte in chunk { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe(&check_timeout)?; + if let Err(err) = self.wait_txe(&check_timeout) { + self.master_stop(); + return Err(err); + } // Put byte on the wire //self.i2c.txdr.write(|w| w.txdata().bits(*byte)); @@ -755,10 +776,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc(&check_timeout)?; + let result = self.wait_tc(&check_timeout); self.master_stop(); - - Ok(()) + result } pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { From 91c1d17f166541c5dafc765e952486f21841e120 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 11 Jul 2023 12:38:53 +0200 Subject: [PATCH 1530/1575] rp/gpio: fix is_set_high/is_set_low, expand tests. --- embassy-rp/src/gpio.rs | 4 ++-- tests/rp/src/bin/gpio.rs | 36 ++++++++++++++++++++++++++++++++++-- tests/stm32/src/bin/gpio.rs | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index d18fb909c..a3d330cdc 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -566,13 +566,13 @@ impl<'d, T: Pin> Flex<'d, T> { /// Is the output level high? #[inline] pub fn is_set_high(&self) -> bool { - (self.pin.sio_out().value().read() & self.bit()) == 0 + !self.is_set_low() } /// Is the output level low? #[inline] pub fn is_set_low(&self) -> bool { - !self.is_set_high() + (self.pin.sio_out().value().read() & self.bit()) == 0 } /// What level output is set to diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index 51112d319..946b7dc88 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -21,14 +21,46 @@ async fn main(_spawner: Spawner) { let b = Input::new(&mut b, Pull::None); { - let _a = Output::new(&mut a, Level::Low); + let a = Output::new(&mut a, Level::Low); delay(); assert!(b.is_low()); + assert!(!b.is_high()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); } { - let _a = Output::new(&mut a, Level::High); + let mut a = Output::new(&mut a, Level::High); + delay(); + assert!(!b.is_low()); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test is_set_low / is_set_high + a.set_low(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.set_high(); delay(); assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test toggle + a.toggle(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.toggle(); + delay(); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); } } diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 67f44317e..aad174431 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -40,14 +40,46 @@ async fn main(_spawner: Spawner) { let b = Input::new(&mut b, Pull::None); { - let _a = Output::new(&mut a, Level::Low, Speed::Low); + let a = Output::new(&mut a, Level::Low, Speed::Low); delay(); assert!(b.is_low()); + assert!(!b.is_high()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); } { - let _a = Output::new(&mut a, Level::High, Speed::Low); + let mut a = Output::new(&mut a, Level::High, Speed::Low); + delay(); + assert!(!b.is_low()); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test is_set_low / is_set_high + a.set_low(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.set_high(); delay(); assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test toggle + a.toggle(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.toggle(); + delay(); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); } } From 6f4172fbc1280fdd9190ccddcf3cf6f25788c7be Mon Sep 17 00:00:00 2001 From: goueslati Date: Tue, 11 Jul 2023 16:07:33 +0100 Subject: [PATCH 1531/1575] wip: added MAC commands --- embassy-stm32-wpan/src/consts.rs | 2 + embassy-stm32-wpan/src/sub/mac/commands.rs | 426 +++++++++++++++--- embassy-stm32-wpan/src/sub/mac/mod.rs | 45 +- embassy-stm32-wpan/src/sub/mac/responses.rs | 0 embassy-stm32-wpan/src/sub/mac/typedefs.rs | 97 ++++ examples/stm32wb/Cargo.toml | 2 +- examples/stm32wb/src/bin/mac_ffd.rs | 174 +++++++ .../src/bin/{tl_mbox_mac_2.rs => mac_rfd.rs} | 63 ++- 8 files changed, 718 insertions(+), 91 deletions(-) create mode 100644 embassy-stm32-wpan/src/sub/mac/responses.rs create mode 100644 embassy-stm32-wpan/src/sub/mac/typedefs.rs create mode 100644 examples/stm32wb/src/bin/mac_ffd.rs rename examples/stm32wb/src/bin/{tl_mbox_mac_2.rs => mac_rfd.rs} (72%) diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs index 1c84addb1..bd70851ea 100644 --- a/embassy-stm32-wpan/src/consts.rs +++ b/embassy-stm32-wpan/src/consts.rs @@ -6,6 +6,8 @@ use crate::PacketHeader; #[derive(Debug)] #[repr(C)] pub enum TlPacketType { + MacCmd = 0x00, + BleCmd = 0x01, AclData = 0x02, BleEvt = 0x04, diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/sub/mac/commands.rs index 75a31d2fc..3c234a3ce 100644 --- a/embassy-stm32-wpan/src/sub/mac/commands.rs +++ b/embassy-stm32-wpan/src/sub/mac/commands.rs @@ -1,13 +1,99 @@ -use bit_field::BitField; - use super::opcodes::OpcodeM4ToM0; +use super::typedefs::{AddressMode, GtsCharacteristics, MacAddress, PibId}; pub trait MacCommand { - type Response; const OPCODE: OpcodeM4ToM0; const SIZE: usize; - fn copy_into_slice(&self, buf: &mut [u8]); + fn copy_into_slice(&self, buf: &mut [u8]) { + unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, 8) }; + } +} + +/// MLME ASSOCIATE Request used to request an association +pub struct AssociateRequest { + /// the logical channel on which to attempt association + pub channel_number: u8, + /// the channel page on which to attempt association + pub channel_page: u8, + /// coordinator addressing mode + pub coord_addr_mode: AddressMode, + /// operational capabilities of the associating device + pub capability_information: u8, + /// the identifier of the PAN with which to associate + pub coord_pan_id: [u8; 2], + /// the security level to be used + pub security_level: u8, + /// the mode used to identify the key to be used + pub key_id_mode: u8, + /// the originator of the key to be used + pub key_source: [u8; 8], + /// Coordinator address + pub coord_address: MacAddress, + /// the index of the key to be used + pub key_index: u8, +} + +impl MacCommand for AssociateRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq; + const SIZE: usize = 25; +} + +/// MLME DISASSOCIATE Request sed to request a disassociation +pub struct DisassociateRequest { + /// device addressing mode used + pub device_addr_mode: AddressMode, + /// the identifier of the PAN of the device + pub device_pan_id: [u8; 2], + /// the reason for the disassociation + pub disassociate_reason: u8, + /// device address + pub device_address: MacAddress, + /// `true` if the disassociation notification command is to be sent indirectly + pub tx_indirect: bool, + /// the security level to be used + pub security_level: u8, + /// the mode to be used to indetify the key to be used + pub key_id_mode: u8, + /// the index of the key to be used + pub key_index: u8, + /// the originator of the key to be used + pub key_source: [u8; 8], +} + +impl MacCommand for DisassociateRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq; + const SIZE: usize = 24; +} + +/// MLME GET Request used to request a PIB value +pub struct GetRequest { + /// the name of the PIB attribute to read + pub pib_attribute: PibId, +} + +impl MacCommand for GetRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; + const SIZE: usize = 4; +} + +/// MLME GTS Request used to request and maintain GTSs +pub struct GtsRequest { + /// the characteristics of the GTS + pub characteristics: GtsCharacteristics, + /// the security level to be used + pub security_level: u8, + /// the mode used to identify the key to be used + pub key_id_mode: u8, + /// the index of the key to be used + pub key_index: u8, + /// the originator of the key to be used + pub key_source: [u8; 8], +} + +impl MacCommand for GtsRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; + const SIZE: usize = 12; } pub struct ResetRequest { @@ -16,68 +102,302 @@ pub struct ResetRequest { } impl MacCommand for ResetRequest { - type Response = (); const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq; const SIZE: usize = 4; +} + +/// MLME RX ENABLE Request used to request that the receiver is either enabled +/// for a finite period of time or disabled +pub struct RxEnableRequest { + /// the request operation can be deferred or not + pub defer_permit: bool, + /// configure the transceiver to RX with ranging for a value of + /// RANGING_ON or to not enable ranging for RANGING_OFF + pub ranging_rx_control: u8, + /// number of symbols measured before the receiver is to be enabled or disabled + pub rx_on_time: [u8; 4], + /// number of symbols for which the receiver is to be enabled + pub rx_on_duration: [u8; 4], +} + +impl MacCommand for RxEnableRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq; + const SIZE: usize = 12; fn copy_into_slice(&self, buf: &mut [u8]) { - buf[0] = self.set_default_pib as u8; + buf[0] = self.defer_permit as u8; + buf[1] = self.ranging_rx_control as u8; + + // stuffing to keep 32bit alignment + buf[2] = 0; + buf[3] = 0; + + buf[4..8].copy_from_slice(&self.rx_on_time); + buf[8..12].copy_from_slice(&self.rx_on_duration); } } -#[repr(C)] -pub struct SetRequest { - pub pib_attribute_ptr: *const u8, - pub pib_attribute: u8, - pub stuffing: [u8; 3], -} - -impl MacCommand for SetRequest { - type Response = (); - const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq; - const SIZE: usize = 8; - - fn copy_into_slice(&self, buf: &mut [u8]) { - let address = self.pib_attribute_ptr as usize; - - // 68 ff 2 20 6f - - let a = unsafe { core::slice::from_raw_parts(&self as *const _ as *const u8, Self::SIZE) }; - debug!("{:#04x}", a); - - unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, 8) }; - - // buf[0] = self.pib_attribute_ptr as u8; - // buf[1] = self.pib_attribute; - } -} - -pub struct AssociateRequest { - pub channel_number: u8, +/// MLME SCAN Request used to initiate a channel scan over a given list of channels +pub struct ScanRequest { + /// the type of scan to be performed + pub scan_type: u8, + /// the time spent on scanning each channel + pub scan_duration: u8, + /// channel page on which to perform the scan pub channel_page: u8, - pub coord_addr_mode: u8, - pub capability_information: u8, - pub coord_pan_id: [u8; 2], + /// security level to be used pub security_level: u8, - pub key_id_mode: u8, + /// indicate which channels are to be scanned + pub scan_channels: [u8; 4], + /// originator the key to be used pub key_source: [u8; 8], - pub coord_address: MacAddress, + /// mode used to identify the key to be used + pub key_id_mode: u8, + /// index of the key to be used pub key_index: u8, } -impl MacCommand for AssociateRequest { - const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq; - const SIZE: usize = 25; - type Response = (); - - fn copy_into_slice(&self, buf: &mut [u8]) { - let a = unsafe { core::slice::from_raw_parts(&self as *const _ as *const u8, core::mem::size_of::()) }; - - buf[..a.len()].copy_from_slice(a); - } +impl MacCommand for ScanRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq; + const SIZE: usize = 20; } -pub union MacAddress { - pub short: [u8; 2], - pub extended: [u8; 8], +/// MLME SET Request used to attempt to write the given value to the indicated PIB attribute +#[repr(C)] +pub struct SetRequest { + /// the pointer to the value of the PIB attribute to set + pub pib_attribute_ptr: *const u8, + /// the name of the PIB attribute to set + pub pib_attribute: PibId, +} + +impl MacCommand for SetRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq; + const SIZE: usize = 8; +} + +/// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe +/// configuration +#[derive(Default)] +pub struct StartRequest { + /// PAN indentifier to used by the device + pub pan_id: [u8; 2], + /// logical channel on which to begin + pub channel_number: u8, + /// channel page on which to begin + pub channel_page: u8, + /// time at which to begin transmitting beacons + pub start_time: [u8; 4], + /// indicated how often the beacon is to be transmitted + pub beacon_order: u8, + /// length of the active portion of the superframe + pub superframe_order: u8, + /// indicated wheter the device is a PAN coordinator or not + pub pan_coordinator: bool, + /// indicates if the receiver of the beaconing device is disabled or not + pub battery_life_extension: bool, + /// indicated if the coordinator realignment command is to be trasmitted + pub coord_realignment: u8, + /// indicated if the coordinator realignment command is to be trasmitted + pub coord_realign_security_level: u8, + /// index of the key to be used + pub coord_realign_key_id_index: u8, + /// originator of the key to be used + pub coord_realign_key_source: [u8; 8], + /// security level to be used for beacon frames + pub beacon_security_level: u8, + /// mode used to identify the key to be used + pub beacon_key_id_mode: u8, + /// index of the key to be used + pub beacon_key_index: u8, + /// originator of the key to be used + pub beacon_key_source: [u8; 8], +} + +impl MacCommand for StartRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq; + const SIZE: usize = 35; +} + +/// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if +/// specified, tracking its beacons +pub struct SyncRequest { + /// the channel number on which to attempt coordinator synchronization + pub channel_number: u8, + /// the channel page on which to attempt coordinator synchronization + pub channel_page: u8, + /// `true` if the MLME is to synchronize with the next beacon and attempts + /// to track all future beacons. + /// + /// `false` if the MLME is to synchronize with only the next beacon + pub track_beacon: bool, +} + +impl MacCommand for SyncRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq; + const SIZE: usize = 4; +} + +/// MLME POLL Request propmts the device to request data from the coordinator +pub struct PollRequest { + /// addressing mode of the coordinator + pub coord_addr_mode: AddressMode, + /// security level to be used + pub security_level: u8, + /// mode used to identify the key to be used + pub key_id_mode: u8, + /// index of the key to be used + pub key_index: u8, + /// coordinator address + pub coord_address: MacAddress, + /// originator of the key to be used + pub key_source: [u8; 8], + /// PAN identifier of the coordinator + pub coord_pan_id: [u8; 2], +} + +impl MacCommand for PollRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq; + const SIZE: usize = 24; +} + +/// MLME DPS Request allows the next higher layer to request that the PHY utilize a +/// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration +pub struct DpsRequest { + /// the index value for the transmitter + tx_dps_index: u8, + /// the index value of the receiver + rx_dps_index: u8, + /// the number of symbols for which the transmitter and receiver will utilize the + /// respective DPS indices + dps_index_duration: u8, +} + +impl MacCommand for DpsRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq; + const SIZE: usize = 4; +} + +/// MLME SOUNDING request primitive which is used by the next higher layer to request that +/// the PHY respond with channel sounding information +pub struct SoundingRequest; + +impl MacCommand for SoundingRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq; + const SIZE: usize = 4; +} + +/// MLME CALIBRATE request primitive which used to obtain the results of a ranging +/// calibration request from an RDEV +pub struct CalibrateRequest; + +impl MacCommand for CalibrateRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq; + const SIZE: usize = 4; +} + +/// MCPS DATA Request used for MAC data related requests from the application +pub struct DataRequest { + /// the handle assocated with the MSDU to be transmitted + pub msdu_ptr: *const u8, + /// source addressing mode used + pub src_addr_mode: AddressMode, + /// destination addressing mode used + pub dst_addr_mode: AddressMode, + /// destination PAN Id + pub dst_pan_id: [u8; 2], + /// destination address + pub dst_address: MacAddress, + /// the number of octets contained in the MSDU + pub msdu_length: u8, + /// the handle assocated with the MSDU to be transmitted + pub msdu_handle: u8, + /// the ACK transmittion options for the MSDU + pub ack_tx: u8, + /// `true` if a GTS is to be used for transmission + /// + /// `false` indicates that the CAP will be used + pub gts_tx: bool, + /// the pending bit transmission options for the MSDU + pub indirect_tx: u8, + /// the security level to be used + pub security_level: u8, + /// the mode used to indentify the key to be used + pub key_id_mode: u8, + /// the index of the key to be used + pub key_index: u8, + /// the originator of the key to be used + pub key_source: [u8; 8], + /// 2011 - the pulse repitition value + pub uwbprf: u8, + /// 2011 - the ranging configuration + pub ranging: u8, + /// 2011 - the preamble symbol repititions + pub uwb_preamble_symbol_repetitions: u8, + /// 2011 - indicates the data rate + pub datrate: u8, +} + +impl MacCommand for DataRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq; + const SIZE: usize = 40; +} + +/// for MCPS PURGE Request used to purge an MSDU from the transaction queue +pub struct PurgeRequest { + /// the handle associated with the MSDU to be purged from the transaction + /// queue + pub msdu_handle: u8, +} + +impl MacCommand for PurgeRequest { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq; + const SIZE: usize = 4; +} + +/// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication +pub struct AssociateResponse { + /// extended address of the device requesting association + pub device_address: [u8; 8], + /// 16-bitshort device address allocated by the coordinator on successful + /// association + pub assoc_short_address: [u8; 2], + /// status of the association attempt + pub status: u8, + /// security level to be used + pub security_level: u8, + /// the originator of the key to be used + pub key_source: [u8; 8], + /// the mode used to identify the key to be used + pub key_id_mode: u8, + /// the index of the key to be used + pub key_index: u8, +} + +impl MacCommand for AssociateResponse { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes; + const SIZE: usize = 24; +} + +/// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication +pub struct OrphanResponse { + /// extended address of the orphaned device + pub orphan_address: [u8; 8], + /// short address allocated to the orphaned device + pub short_address: [u8; 2], + /// if the orphaned device is associated with coordinator or not + pub associated_member: bool, + /// security level to be used + pub security_level: u8, + /// the originator of the key to be used + pub key_source: [u8; 8], + /// the mode used to identify the key to be used + pub key_id_mode: u8, + /// the index of the key to be used + pub key_index: u8, +} + +impl MacCommand for OrphanResponse { + const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes; + const SIZE: usize = 24; } diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs index 83970a881..8e117d978 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -9,14 +9,17 @@ use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; use self::commands::MacCommand; +use self::typedefs::MacStatus; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; -pub mod commands; mod opcodes; +pub mod commands; +pub mod responses; +pub mod typedefs; static MAC_WAKER: AtomicWaker = AtomicWaker::new(); static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); @@ -59,12 +62,25 @@ impl Mac { /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { self.write(opcode, payload).await; - Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; + Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await; unsafe { let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8; + let evt_serial = (MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket) + .read_volatile() + .evt_serial; + let kind = (evt_serial).kind; + let evt_code = evt_serial.evt.evt_code; + let payload_len = evt_serial.evt.payload_len; + let payload = evt_serial.evt.payload; + + debug!( + "evt kind {} evt_code {} len {} payload {}", + kind, evt_code, payload_len, payload + ); + ptr::read_volatile(p_mac_rsp_evt) } } @@ -74,15 +90,32 @@ impl Mac { Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe { CmdPacket::write_into( MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), - TlPacketType::OtCmd, + TlPacketType::MacCmd, opcode, payload, ); }) .await; + + unsafe { + let typ = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.ty; + let cmd_code = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.cmd.cmd_code; + let payload_len = MAC_802_15_4_CMD_BUFFER + .as_ptr() + .read_volatile() + .cmdserial + .cmd + .payload_len; + let payload = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.cmd.payload; + + debug!( + "serial type {} cmd_code {} len {} payload {}", + typ, cmd_code, payload_len, payload + ); + } } - pub async fn send_command(&self, cmd: T) -> u8 + pub async fn send_command(&self, cmd: T) -> Result where T: MacCommand, { @@ -91,7 +124,9 @@ impl Mac { debug!("sending {:#x}", payload[..T::SIZE]); - self.write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]).await + let response = self.write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]).await; + + MacStatus::try_from(response) } } diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs new file mode 100644 index 000000000..e69de29bb diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/sub/mac/typedefs.rs new file mode 100644 index 000000000..d43d6e0cf --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/typedefs.rs @@ -0,0 +1,97 @@ +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacStatus { + Success = 0x00, + Error = 0x01, + NotImplemented = 0x02, + NotSupported = 0x03, + HardwareNotSupported = 0x04, + Undefined = 0x05, +} + +impl TryFrom for MacStatus { + type Error = (); + + fn try_from(value: u8) -> Result>::Error> { + match value { + 0x00 => Ok(Self::Success), + 0x01 => Ok(Self::Error), + 0x02 => Ok(Self::NotImplemented), + 0x03 => Ok(Self::NotSupported), + 0x04 => Ok(Self::HardwareNotSupported), + 0x05 => Ok(Self::Undefined), + _ => Err(()), + } + } +} + +/// this enum contains all the MAC PIB Ids +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PibId { + // PHY + CurrentChannel = 0x00, + ChannelsSupported = 0x01, + TransmitPower = 0x02, + CCAMode = 0x03, + CurrentPage = 0x04, + MaxFrameDuration = 0x05, + SHRDuration = 0x06, + SymbolsPerOctet = 0x07, + + // MAC + AckWaitDuration = 0x40, + AssociationPermit = 0x41, + AutoRequest = 0x42, + BeaconPayload = 0x45, + BeaconPayloadLength = 0x46, + BeaconOrder = 0x47, + Bsn = 0x49, + CoordExtendedAdddress = 0x4A, + CoordShortAddress = 0x4B, + Dsn = 0x4C, + MaxFrameTotalWaitTime = 0x58, + MaxFrameRetries = 0x59, + PanId = 0x50, + ResponseWaitTime = 0x5A, + RxOnWhenIdle = 0x52, + SecurityEnabled = 0x5D, + ShortAddress = 0x53, + SuperframeOrder = 0x54, + TimestampSupported = 0x5C, + TransactionPersistenceTime = 0x55, + MaxBe = 0x57, + LifsPeriod = 0x5E, + SifsPeriod = 0x5F, + MaxCsmaBackoffs = 0x4E, + MinBe = 0x4F, + PanCoordinator = 0x10, + AssocPanCoordinator = 0x11, + ExtendedAddress = 0x6F, + AclEntryDescriptor = 0x70, + AclEntryDescriptorSize = 0x71, + DefaultSecurity = 0x72, + DefaultSecurityMaterialLength = 0x73, + DefaultSecurityMaterial = 0x74, + DefaultSecuritySuite = 0x75, + SecurityMode = 0x76, + CurrentAclEntries = 0x80, + DefaultSecurityExtendedAddress = 0x81, + AssociatedPanCoordinator = 0x56, + PromiscuousMode = 0x51, +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AddressMode { + NoAddress = 0x00, + Reserved = 0x01, + Short = 0x02, + Extended = 0x03, +} + +pub union MacAddress { + pub short: [u8; 2], + pub extended: [u8; 8], +} + +pub struct GtsCharacteristics { + pub fields: u8, +} diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 109734546..f23c8afa6 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -36,7 +36,7 @@ name = "tl_mbox_mac" required-features = ["mac"] [[bin]] -name = "tl_mbox_mac_2" +name = "mac_ffd" required-features = ["mac"] [[bin]] diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs new file mode 100644 index 000000000..4100d1ac5 --- /dev/null +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -0,0 +1,174 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::sub::mac::commands::{ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::sub::mac::typedefs::PibId; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + let response = mbox + .mac_subsystem + .send_command(ResetRequest { set_default_pib: true }) + .await; + info!("{}", response); + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000001; + let response = mbox + .mac_subsystem + .send_command(SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await; + info!("{}", response); + + info!("setting short address"); + let short_address: u16 = 0x1122; + let response = mbox + .mac_subsystem + .send_command(SetRequest { + pib_attribute_ptr: &short_address as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await; + info!("{}", response); + + info!("setting association permit"); + let association_permit: bool = true; + let response = mbox + .mac_subsystem + .send_command(SetRequest { + pib_attribute_ptr: &association_permit as *const _ as *const u8, + pib_attribute: PibId::AssociationPermit, + }) + .await; + info!("{}", response); + + info!("setting TX power"); + let transmit_power: i8 = 2; + let response = mbox + .mac_subsystem + .send_command(SetRequest { + pib_attribute_ptr: &transmit_power as *const _ as *const u8, + pib_attribute: PibId::TransmitPower, + }) + .await; + info!("{}", response); + + info!("starting FFD device"); + let response = mbox + .mac_subsystem + .send_command(StartRequest { + channel_number: 16, + beacon_order: 0x0F, + superframe_order: 0x0F, + pan_coordinator: true, + battery_life_extension: false, + ..Default::default() + }) + .await; + info!("{}", response); + + info!("setting RX on when idle"); + let rx_on_while_idle: bool = true; + let response = mbox + .mac_subsystem + .send_command(SetRequest { + pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, + pib_attribute: PibId::RxOnWhenIdle, + }) + .await; + info!("{}", response); + + // info!("association request"); + // mbox.mac_subsystem + // .send_command(AssociateRequest { + // channel_number: 16, + // channel_page: 0, + // coord_addr_mode: 2, + // coord_address: MacAddress { short: [0x22, 0x11] }, + // capability_information: 0x80, + // coord_pan_id: [0xAA, 0x1A], + // security_level: 0, + + // key_id_mode: 0, + // key_index: 0, + // key_source: [0; 8], + // }) + // .await; + // info!("reading"); + // let result = mbox.mac_subsystem.read().await; + // info!("{}", result.payload()); + + // + // info!("starting ble..."); + // mbox.ble_subsystem.t_write(0x0c, &[]).await; + // + // info!("waiting for ble..."); + // let ble_event = mbox.ble_subsystem.tl_read().await; + // + // info!("ble event: {}", ble_event.payload()); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32wb/src/bin/tl_mbox_mac_2.rs b/examples/stm32wb/src/bin/mac_rfd.rs similarity index 72% rename from examples/stm32wb/src/bin/tl_mbox_mac_2.rs rename to examples/stm32wb/src/bin/mac_rfd.rs index e069adf87..938fe754f 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac_2.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,7 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, MacAddress, ResetRequest, SetRequest}; +use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::sub::mac::typedefs::{AddressMode, MacAddress, PibId}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -71,46 +72,44 @@ async fn main(spawner: Spawner) { info!("{}", response); info!("setting extended address"); - let extended_address: u64 = 0xACDE480000000001; - defmt::debug!("{}", &extended_address as *const _ as *const u8); + let extended_address: u64 = 0xACDE480000000002; let response = mbox .mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, - pib_attribute: 0x6F, - stuffing: [0; 3], + pib_attribute: PibId::ExtendedAddress, }) .await; info!("{}", response); - // info!("association request"); - // mbox.mac_subsystem - // .send_command(AssociateRequest { - // channel_number: 16, - // channel_page: 0, - // coord_addr_mode: 2, - // coord_address: MacAddress { short: [0x22, 0x11] }, - // capability_information: 0x80, - // coord_pan_id: [0xAA, 0x1A], - // security_level: 0, + info!("assocation request"); + let response = mbox + .mac_subsystem + .send_command(AssociateRequest { + channel_number: 16, + channel_page: 0, + coord_addr_mode: AddressMode::Short, + coord_address: MacAddress { short: [0x22, 0x11] }, + capability_information: 0x80, + coord_pan_id: [0xAA, 0x1A], + security_level: 0x00, + key_id_mode: 0, + key_source: [0; 8], + key_index: 0, + }) + .await; + info!("{}", response); - // key_id_mode: 0, - // key_index: 0, - // key_source: [0; 8], - // }) - // .await; - // info!("reading"); - // let result = mbox.mac_subsystem.read().await; - // info!("{}", result.payload()); - - // - // info!("starting ble..."); - // mbox.ble_subsystem.t_write(0x0c, &[]).await; - // - // info!("waiting for ble..."); - // let ble_event = mbox.ble_subsystem.tl_read().await; - // - // info!("ble event: {}", ble_event.payload()); + info!("setting short address"); + let short: u64 = 0xACDE480000000002; + let response = mbox + .mac_subsystem + .send_command(SetRequest { + pib_attribute_ptr: &short as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await; + info!("{}", response); info!("Test OK"); cortex_m::asm::bkpt(); From 67b14e6e7a98abc538adda45d6434ad44d700283 Mon Sep 17 00:00:00 2001 From: goueslati Date: Tue, 11 Jul 2023 16:54:48 +0100 Subject: [PATCH 1532/1575] wip: added MAC responses --- embassy-stm32-wpan/src/sub/mac/commands.rs | 17 ++ embassy-stm32-wpan/src/sub/mac/consts.rs | 3 + embassy-stm32-wpan/src/sub/mac/mod.rs | 3 +- embassy-stm32-wpan/src/sub/mac/responses.rs | 168 ++++++++++++++++++++ embassy-stm32-wpan/src/sub/mac/typedefs.rs | 27 ++++ 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32-wpan/src/sub/mac/consts.rs diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/sub/mac/commands.rs index 3c234a3ce..4965a46eb 100644 --- a/embassy-stm32-wpan/src/sub/mac/commands.rs +++ b/embassy-stm32-wpan/src/sub/mac/commands.rs @@ -11,6 +11,7 @@ pub trait MacCommand { } /// MLME ASSOCIATE Request used to request an association +#[repr(C)] pub struct AssociateRequest { /// the logical channel on which to attempt association pub channel_number: u8, @@ -40,6 +41,7 @@ impl MacCommand for AssociateRequest { } /// MLME DISASSOCIATE Request sed to request a disassociation +#[repr(C)] pub struct DisassociateRequest { /// device addressing mode used pub device_addr_mode: AddressMode, @@ -67,6 +69,7 @@ impl MacCommand for DisassociateRequest { } /// MLME GET Request used to request a PIB value +#[repr(C)] pub struct GetRequest { /// the name of the PIB attribute to read pub pib_attribute: PibId, @@ -78,6 +81,7 @@ impl MacCommand for GetRequest { } /// MLME GTS Request used to request and maintain GTSs +#[repr(C)] pub struct GtsRequest { /// the characteristics of the GTS pub characteristics: GtsCharacteristics, @@ -96,6 +100,7 @@ impl MacCommand for GtsRequest { const SIZE: usize = 12; } +#[repr(C)] pub struct ResetRequest { /// MAC PIB attributes are set to their default values or not during reset pub set_default_pib: bool, @@ -108,6 +113,7 @@ impl MacCommand for ResetRequest { /// MLME RX ENABLE Request used to request that the receiver is either enabled /// for a finite period of time or disabled +#[repr(C)] pub struct RxEnableRequest { /// the request operation can be deferred or not pub defer_permit: bool, @@ -138,6 +144,7 @@ impl MacCommand for RxEnableRequest { } /// MLME SCAN Request used to initiate a channel scan over a given list of channels +#[repr(C)] pub struct ScanRequest { /// the type of scan to be performed pub scan_type: u8, @@ -179,6 +186,7 @@ impl MacCommand for SetRequest { /// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe /// configuration #[derive(Default)] +#[repr(C)] pub struct StartRequest { /// PAN indentifier to used by the device pub pan_id: [u8; 2], @@ -221,6 +229,7 @@ impl MacCommand for StartRequest { /// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if /// specified, tracking its beacons +#[repr(C)] pub struct SyncRequest { /// the channel number on which to attempt coordinator synchronization pub channel_number: u8, @@ -239,6 +248,7 @@ impl MacCommand for SyncRequest { } /// MLME POLL Request propmts the device to request data from the coordinator +#[repr(C)] pub struct PollRequest { /// addressing mode of the coordinator pub coord_addr_mode: AddressMode, @@ -263,6 +273,7 @@ impl MacCommand for PollRequest { /// MLME DPS Request allows the next higher layer to request that the PHY utilize a /// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration +#[repr(C)] pub struct DpsRequest { /// the index value for the transmitter tx_dps_index: u8, @@ -280,6 +291,7 @@ impl MacCommand for DpsRequest { /// MLME SOUNDING request primitive which is used by the next higher layer to request that /// the PHY respond with channel sounding information +#[repr(C)] pub struct SoundingRequest; impl MacCommand for SoundingRequest { @@ -289,6 +301,7 @@ impl MacCommand for SoundingRequest { /// MLME CALIBRATE request primitive which used to obtain the results of a ranging /// calibration request from an RDEV +#[repr(C)] pub struct CalibrateRequest; impl MacCommand for CalibrateRequest { @@ -297,6 +310,7 @@ impl MacCommand for CalibrateRequest { } /// MCPS DATA Request used for MAC data related requests from the application +#[repr(C)] pub struct DataRequest { /// the handle assocated with the MSDU to be transmitted pub msdu_ptr: *const u8, @@ -344,6 +358,7 @@ impl MacCommand for DataRequest { } /// for MCPS PURGE Request used to purge an MSDU from the transaction queue +#[repr(C)] pub struct PurgeRequest { /// the handle associated with the MSDU to be purged from the transaction /// queue @@ -356,6 +371,7 @@ impl MacCommand for PurgeRequest { } /// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication +#[repr(C)] pub struct AssociateResponse { /// extended address of the device requesting association pub device_address: [u8; 8], @@ -380,6 +396,7 @@ impl MacCommand for AssociateResponse { } /// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication +#[repr(C)] pub struct OrphanResponse { /// extended address of the orphaned device pub orphan_address: [u8; 8], diff --git a/embassy-stm32-wpan/src/sub/mac/consts.rs b/embassy-stm32-wpan/src/sub/mac/consts.rs new file mode 100644 index 000000000..dfbbadc67 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/consts.rs @@ -0,0 +1,3 @@ +pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16; +pub const MAX_PAN_DESC_SUPPORTED: usize = 6; +pub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6; diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs index 8e117d978..6a3a057f4 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -16,8 +16,9 @@ use crate::evt::{EvtBox, EvtPacket}; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; -mod opcodes; pub mod commands; +mod consts; +mod opcodes; pub mod responses; pub mod typedefs; diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index e69de29bb..7b240f370 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -0,0 +1,168 @@ +use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED}; +use super::typedefs::{AddressMode, MacAddress, PanDescriptor}; + +pub trait MacResponse { + const SIZE: usize; + + fn parse(buf: &[u8]) -> Self; +} + +/// MLME ASSOCIATE Confirm used to inform of the initiating device whether +/// its request to associate was successful or unsuccessful +pub struct AssociateConfirm { + /// short address allocated by the coordinator on successful association + pub assoc_short_address: [u8; 2], + /// status of the association request + pub status: u8, + /// security level to be used + pub security_level: u8, + /// the originator of the key to be used + pub key_source: [u8; 8], + /// the mode used to identify the key to be used + pub key_id_mode: u8, + /// the index of the key to be used + pub key_index: u8, +} + +/// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. +pub struct DisassociateConfirm { + /// status of the disassociation attempt + pub status: u8, + /// device addressing mode used + pub device_addr_mode: AddressMode, + /// the identifier of the PAN of the device + pub device_pan_id: [u8; 2], + /// device address + pub device_address: MacAddress, +} + +/// MLME GET Confirm which requests information about a given PIB attribute +pub struct GetConfirm { + /// The pointer to the value of the PIB attribute attempted to read + pub pib_attribute_value_ptr: *const u8, + /// Status of the GET attempt + pub status: u8, + /// The name of the PIB attribute attempted to read + pub pib_attribute: u8, + /// The lenght of the PIB attribute Value return + pub pib_attribute_value_len: u8, +} + +/// MLME GTS Confirm which eports the results of a request to allocate a new GTS +/// or to deallocate an existing GTS +pub struct GtsConfirm { + /// The characteristics of the GTS + pub gts_characteristics: u8, + /// The status of the GTS reques + pub status: u8, +} + +/// MLME RESET Confirm which is used to report the results of the reset operation +pub struct ResetConfirm { + /// The result of the reset operation + status: u8, +} + +/// MLME RX ENABLE Confirm which is used to report the results of the attempt +/// to enable or disable the receiver +pub struct RxEnableConfirm { + /// Result of the request to enable or disable the receiver + status: u8, +} + +/// MLME SCAN Confirm which is used to report the result of the channel scan request +pub struct ScanConfirm { + /// Status of the scan request + pub status: u8, + /// The type of scan performed + pub scan_type: u8, + /// Channel page on which the scan was performed + pub channel_page: u8, + /// Channels given in the request which were not scanned + pub unscanned_channels: [u8; 4], + /// Number of elements returned in the appropriate result lists + pub result_list_size: u8, + /// List of energy measurements + pub energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], + /// List of PAN descriptors + pub pan_descriptor_list: [PanDescriptor; MAX_PAN_DESC_SUPPORTED], + /// Categorization of energy detected in channel + pub detected_category: u8, + /// For UWB PHYs, the list of energy measurements taken + pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], +} + +/// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute +pub struct SetConfirm { + /// The result of the set operation + pub status: u8, + /// The name of the PIB attribute that was written + pub pin_attribute: u8, +} + +/// MLME START Confirm which is used to report the results of the attempt to +/// start using a new superframe configuration +pub struct StartConfirm { + /// Result of the attempt to start using an updated superframe configuration + pub status: u8, +} + +/// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data +pub struct PollConfirm { + /// The status of the data request + pub status: u8, +} + +/// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide +/// channel sounding information +pub struct SoundingConfirm { + /// Results of the sounding measurement + sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], +} + +/// MLME CALIBRATE Confirm which reports the result of a request to the PHY +/// to provide internal propagation path information +pub struct CalibrateConfirm { + /// The status of the attempt to return sounding data + pub status: u8, + /// A count of the propagation time from the ranging counter + /// to the transmit antenna + pub cal_tx_rmaker_offset: u32, + /// A count of the propagation time from the receive antenna + /// to the ranging counter + pub cal_rx_rmaker_offset: u32, +} + +/// MCPS DATA Confirm which will be used for reporting the results of +/// MAC data related requests from the application +pub struct DataConfirm { + /// The handle associated with the MSDU being confirmed + pub msdu_handle: u8, + /// The time, in symbols, at which the data were transmitted + pub a_time_stamp: [u8; 4], + /// ranging status + pub ranging_received: u8, + /// The status of the last MSDU transmission + pub status: u8, + /// time units corresponding to an RMARKER at the antenna at + /// the beginning of a ranging exchange + pub ranging_counter_start: u32, + /// time units corresponding to an RMARKER at the antenna + /// at the end of a ranging exchange + pub ranging_counter_stop: u32, + /// time units in a message exchange over which the tracking offset was measured + pub ranging_tracking_interval: u32, + /// time units slipped or advanced by the radio tracking system + pub ranging_offset: u32, + /// The FoM characterizing the ranging measurement + pub ranging_fom: u8, +} + +/// MCPS PURGE Confirm which will be used by the MAC to notify the application of +/// the status of its request to purge an MSDU from the transaction queue +pub struct PurgeConfirm { + /// Handle associated with the MSDU requested to be purged from the transaction queue + pub msdu_handle: u8, + /// The status of the request + pub status: u8, +} diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/sub/mac/typedefs.rs index d43d6e0cf..7f0dd75c0 100644 --- a/embassy-stm32-wpan/src/sub/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/sub/mac/typedefs.rs @@ -95,3 +95,30 @@ pub union MacAddress { pub struct GtsCharacteristics { pub fields: u8, } + +/// MAC PAN Descriptor which contains the network details of the device from +/// which the beacon is received +pub struct PanDescriptor { + /// PAN identifier of the coordinator + pub a_coord_pan_id: [u8; 2], + /// Coordinator addressing mode + pub coord_addr_mode: AddressMode, + /// The current logical channel occupied by the network + pub logical_channel: u8, + /// Coordinator address + pub coord_addr: MacAddress, + /// The current channel page occupied by the network + pub channel_page: u8, + /// PAN coordinator is accepting GTS requests or not + pub gts_permit: bool, + /// Superframe specification as specified in the received beacon frame + pub a_superframe_spec: [u8; 2], + /// The time at which the beacon frame was received, in symbols + pub a_time_stamp: [u8; 4], + /// The LQI at which the network beacon was received + pub link_quality: u8, + /// Security level purportedly used by the received beacon frame + pub security_level: u8, + /// Byte Stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], +} From fbddfcbfb7f732db593eecd5383742d9ce7308e7 Mon Sep 17 00:00:00 2001 From: goueslati Date: Tue, 11 Jul 2023 17:19:32 +0100 Subject: [PATCH 1533/1575] wip: added MAC indications --- embassy-stm32-wpan/src/sub/mac/consts.rs | 1 + embassy-stm32-wpan/src/sub/mac/indications.rs | 205 ++++++++++++++++++ embassy-stm32-wpan/src/sub/mac/mod.rs | 1 + embassy-stm32-wpan/src/sub/mac/responses.rs | 14 ++ 4 files changed, 221 insertions(+) create mode 100644 embassy-stm32-wpan/src/sub/mac/indications.rs diff --git a/embassy-stm32-wpan/src/sub/mac/consts.rs b/embassy-stm32-wpan/src/sub/mac/consts.rs index dfbbadc67..892d533b4 100644 --- a/embassy-stm32-wpan/src/sub/mac/consts.rs +++ b/embassy-stm32-wpan/src/sub/mac/consts.rs @@ -1,3 +1,4 @@ pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16; pub const MAX_PAN_DESC_SUPPORTED: usize = 6; pub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6; +pub const MAX_PENDING_ADDRESS: usize = 7; diff --git a/embassy-stm32-wpan/src/sub/mac/indications.rs b/embassy-stm32-wpan/src/sub/mac/indications.rs new file mode 100644 index 000000000..ebca16f72 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/indications.rs @@ -0,0 +1,205 @@ +use super::consts::MAX_PENDING_ADDRESS; +use super::typedefs::{AddressMode, MacAddress, PanDescriptor}; + +/// MLME ASSOCIATE Indication which will be used by the MAC +/// to indicate the reception of an association request command +#[repr(C)] +pub struct AssociateIndication { + /// Extended address of the device requesting association + pub device_address: [u8; 8], + /// Operational capabilities of the device requesting association + pub capability_information: u8, + /// Security level purportedly used by the received MAC command frame + pub security_level: u8, + /// The mode used to identify the key used by the originator of frame + pub key_id_mode: u8, + /// Index of the key used by the originator of the received frame + pub key_index: u8, + /// The originator of the key used by the originator of the received frame + pub key_source: [u8; 8], +} + +/// MLME DISASSOCIATE indication which will be used to send +/// disassociation indication to the application. +#[repr(C)] +pub struct DisassociateIndication { + /// Extended address of the device requesting association + pub device_address: [u8; 8], + /// The reason for the disassociation + pub disassociate_reason: u8, + /// The security level to be used + pub security_level: u8, + /// The mode used to identify the key to be used + pub key_id_mode: u8, + /// The index of the key to be used + pub key_index: u8, + /// The originator of the key to be used + pub key_source: [u8; 8], +} + +/// MLME BEACON NOTIIFY Indication which is used to send parameters contained +/// within a beacon frame received by the MAC to the application +#[repr(C)] +pub struct BeaconNotifyIndication { + /// he set of octets comprising the beacon payload to be transferred + /// from the MAC sublayer entity to the next higher layer + pub sdu_ptr: *const u8, + /// The PAN Descriptor for the received beacon + pub pan_descriptor: PanDescriptor, + /// The list of addresses of the devices + pub addr_list: [MacAddress; MAX_PENDING_ADDRESS], + /// Beacon Sequence Number + pub bsn: u8, + /// The beacon pending address specification + pub pend_addr_spec: u8, + /// Number of octets contained in the beacon payload of the beacon frame + pub sdu_length: u8, +} + +/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status +#[repr(C)] +pub struct CommStatusIndication { + /// The 16-bit PAN identifier of the device from which the frame + /// was received or to which the frame was being sent + pub pan_id: [u8; 2], + /// Source addressing mode + pub src_addr_mode: AddressMode, + /// Destination addressing mode + pub dst_addr_mode: AddressMode, + /// Source address + pub src_address: MacAddress, + /// Destination address + pub dst_address: MacAddress, + /// The communications status + pub status: u8, + /// Security level to be used + pub security_level: u8, + /// Mode used to identify the key to be used + pub key_id_mode: u8, + /// Index of the key to be used + pub key_index: u8, + /// Originator of the key to be used + pub key_source: [u8; 8], +} + +/// MLME GTS Indication indicates that a GTS has been allocated or that a +/// previously allocated GTS has been deallocated +#[repr(C)] +pub struct GtsIndication { + /// The short address of the device that has been allocated or deallocated a GTS + pub device_address: [u8; 2], + /// The characteristics of the GTS + pub gts_characteristics: u8, + /// Security level to be used + pub security_level: u8, + /// Mode used to identify the key to be used + pub key_id_mode: u8, + /// Index of the key to be used + pub key_index: u8, + /// Originator of the key to be used + pub key_source: [u8; 8], +} + +/// MLME ORPHAN Indication which is used by the coordinator to notify the +/// application of the presence of an orphaned device +#[repr(C)] +pub struct OrphanIndication { + /// Extended address of the orphaned device + pub orphan_address: [u8; 8], + /// Originator of the key used by the originator of the received frame + pub key_source: [u8; 8], + /// Security level purportedly used by the received MAC command frame + pub security_level: u8, + /// Mode used to identify the key used by originator of received frame + pub key_id_mode: u8, + /// Index of the key used by the originator of the received frame + pub key_index: u8, +} + +/// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss +/// of synchronization with the coordinator +#[repr(C)] +pub struct SyncLossIndication { + /// The PAN identifier with which the device lost synchronization or to which it was realigned + pub pan_id: [u8; 2], + /// The reason that synchronization was lost + pub loss_reason: u8, + /// The logical channel on which the device lost synchronization or to whi + pub channel_number: u8, + /// The channel page on which the device lost synchronization or to which + pub channel_page: u8, + /// The security level used by the received MAC frame + pub security_level: u8, + /// Mode used to identify the key used by originator of received frame + pub key_id_mode: u8, + /// Index of the key used by the originator of the received frame + pub key_index: u8, + /// Originator of the key used by the originator of the received frame + pub key_source: [u8; 8], +} + +/// MLME DPS Indication which indicates the expiration of the DPSIndexDuration +/// and the resetting of the DPS values in the PHY +pub struct DpsIndication; + +#[repr(C)] +pub struct DataIndication { + /// Pointer to the set of octets forming the MSDU being indicated + pub msdu_ptr: *const u8, + /// Source addressing mode used + pub src_addr_mode: u8, + /// Source PAN ID + pub src_pan_id: [u8; 2], + /// Source address + pub src_address: MacAddress, + /// Destination addressing mode used + pub dst_addr_mode: AddressMode, + /// Destination PAN ID + pub dst_pan_id: [u8; 2], + /// Destination address + pub dst_address: MacAddress, + /// The number of octets contained in the MSDU being indicated + pub msdu_length: u8, + /// QI value measured during reception of the MPDU + pub mpdu_link_quality: u8, + /// The data sequence number of the received data frame + pub dsn: u8, + /// The time, in symbols, at which the data were received + pub time_stamp: [u8; 4], + /// The security level purportedly used by the received data frame + pub security_level: u8, + /// Mode used to identify the key used by originator of received frame + pub key_id_mode: u8, + /// The originator of the key + pub key_source: [u8; 8], + /// The index of the key + pub key_index: u8, + /// he pulse repetition value of the received PPDU + pub uwbprf: u8, + /// The preamble symbol repetitions of the UWB PHY frame + pub uwn_preamble_symbol_repetitions: u8, + /// Indicates the data rate + pub datrate: u8, + /// time units corresponding to an RMARKER at the antenna at the end of a ranging exchange, + pub ranging_received: u8, + pub ranging_counter_start: u32, + pub ranging_counter_stop: u32, + /// ime units in a message exchange over which the tracking offset was measured + pub ranging_tracking_interval: u32, + /// time units slipped or advanced by the radio tracking system + pub ranging_offset: u32, + /// The FoM characterizing the ranging measurement + pub ranging_fom: u8, + /// The Received Signal Strength Indicator measured + pub rssi: u8, +} + +/// MLME POLL Indication which will be used for indicating the Data Request +/// reception to upper layer as defined in Zigbee r22 - D.8.2 +#[repr(C)] +pub struct PollIndication { + /// addressing mode used + pub addr_mode: u8, + /// Poll requester address + pub request_address: MacAddress, +} diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs index 6a3a057f4..756d7d5b1 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -18,6 +18,7 @@ use crate::{channels, evt}; pub mod commands; mod consts; +pub mod indications; mod opcodes; pub mod responses; pub mod typedefs; diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index 7b240f370..8c30a1823 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -9,6 +9,7 @@ pub trait MacResponse { /// MLME ASSOCIATE Confirm used to inform of the initiating device whether /// its request to associate was successful or unsuccessful +#[repr(C)] pub struct AssociateConfirm { /// short address allocated by the coordinator on successful association pub assoc_short_address: [u8; 2], @@ -25,6 +26,7 @@ pub struct AssociateConfirm { } /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. +#[repr(C)] pub struct DisassociateConfirm { /// status of the disassociation attempt pub status: u8, @@ -37,6 +39,7 @@ pub struct DisassociateConfirm { } /// MLME GET Confirm which requests information about a given PIB attribute +#[repr(C)] pub struct GetConfirm { /// The pointer to the value of the PIB attribute attempted to read pub pib_attribute_value_ptr: *const u8, @@ -50,6 +53,7 @@ pub struct GetConfirm { /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS +#[repr(C)] pub struct GtsConfirm { /// The characteristics of the GTS pub gts_characteristics: u8, @@ -58,6 +62,7 @@ pub struct GtsConfirm { } /// MLME RESET Confirm which is used to report the results of the reset operation +#[repr(C)] pub struct ResetConfirm { /// The result of the reset operation status: u8, @@ -65,12 +70,14 @@ pub struct ResetConfirm { /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver +#[repr(C)] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver status: u8, } /// MLME SCAN Confirm which is used to report the result of the channel scan request +#[repr(C)] pub struct ScanConfirm { /// Status of the scan request pub status: u8, @@ -93,6 +100,7 @@ pub struct ScanConfirm { } /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute +#[repr(C)] pub struct SetConfirm { /// The result of the set operation pub status: u8, @@ -102,12 +110,14 @@ pub struct SetConfirm { /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration +#[repr(C)] pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration pub status: u8, } /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data +#[repr(C)] pub struct PollConfirm { /// The status of the data request pub status: u8, @@ -115,6 +125,7 @@ pub struct PollConfirm { /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information +#[repr(C)] pub struct SoundingConfirm { /// Results of the sounding measurement sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], @@ -122,6 +133,7 @@ pub struct SoundingConfirm { /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information +#[repr(C)] pub struct CalibrateConfirm { /// The status of the attempt to return sounding data pub status: u8, @@ -135,6 +147,7 @@ pub struct CalibrateConfirm { /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application +#[repr(C)] pub struct DataConfirm { /// The handle associated with the MSDU being confirmed pub msdu_handle: u8, @@ -160,6 +173,7 @@ pub struct DataConfirm { /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue +#[repr(C)] pub struct PurgeConfirm { /// Handle associated with the MSDU requested to be purged from the transaction queue pub msdu_handle: u8, From f54e1cea90527871f65ea449e58dc57fcf7cdb37 Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Wed, 12 Jul 2023 11:32:02 +0200 Subject: [PATCH 1534/1575] Add poll functions on UdpSocket. --- embassy-net/src/udp.rs | 47 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 36f8d06f2..bd7a32f7c 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -102,37 +102,42 @@ impl<'a> UdpSocket<'a> { /// /// Returns the number of bytes received and the remote endpoint. pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { - poll_fn(move |cx| { - self.with_mut(|s, _| match s.recv_slice(buf) { - Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))), - // No data ready - Err(udp::RecvError::Exhausted) => { - s.register_recv_waker(cx.waker()); - Poll::Pending - } - }) + poll_fn(move |cx| self.poll_recv_from(buf, cx)).await + } + + pub fn poll_recv_from(&self, buf: &mut[u8], cx: Context) -> Poll> { + self.with_mut(|s, _| match s.recv_slice(buf) { + Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))), + // No data ready + Err(udp::RecvError::Exhausted) => { + s.register_recv_waker(cx.waker()); + Poll::Pending + } }) - .await } /// Send a datagram to the specified remote endpoint. pub async fn send_to(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> + where + T: Into, + { + poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await + } + + pub fn poll_send_to(&self, buf: &[u8], remote_endpoint: T, cx: Context) -> Poll> where T: Into, { let remote_endpoint = remote_endpoint.into(); - poll_fn(move |cx| { - self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) { - // Entire datagram has been sent - Ok(()) => Poll::Ready(Ok(())), - Err(udp::SendError::BufferFull) => { - s.register_send_waker(cx.waker()); - Poll::Pending - } - Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)), - }) + self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) { + // Entire datagram has been sent + Ok(()) => Poll::Ready(Ok(())), + Err(udp::SendError::BufferFull) => { + s.register_send_waker(cx.waker()); + Poll::Pending + } + Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)), }) - .await } /// Returns the local endpoint of the socket. From b81c14f442839de61f2217cf88e09d390d465f3d Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Wed, 12 Jul 2023 11:32:02 +0200 Subject: [PATCH 1535/1575] Add polling fn's for send_to and recv_from in UdpSocket. --- embassy-net/src/udp.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index bd7a32f7c..16f48bcb0 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; use core::future::poll_fn; use core::mem; -use core::task::Poll; +use core::task::{Poll, Context}; use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; @@ -105,7 +105,14 @@ impl<'a> UdpSocket<'a> { poll_fn(move |cx| self.poll_recv_from(buf, cx)).await } - pub fn poll_recv_from(&self, buf: &mut[u8], cx: Context) -> Poll> { + /// Receive a datagram. + /// + /// When no datagram is available, this method will return `Poll::Pending` and + /// register the current task to be notified when a datagram is received. + /// + /// When a datagram is received, this method will return `Poll::Ready` with the + /// number of bytes received and the remote endpoint. + pub fn poll_recv_from(&self, buf: &mut[u8], cx: &mut Context<'_>) -> Poll> { self.with_mut(|s, _| match s.recv_slice(buf) { Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))), // No data ready @@ -117,18 +124,30 @@ impl<'a> UdpSocket<'a> { } /// Send a datagram to the specified remote endpoint. + /// + /// This method will wait until the datagram has been sent. + /// + /// When the remote endpoint is not reachable, this method will return `Err(Error::NoRoute)` pub async fn send_to(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> where T: Into, { + let remote_endpoint: IpEndpoint = remote_endpoint.into(); poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await } - pub fn poll_send_to(&self, buf: &[u8], remote_endpoint: T, cx: Context) -> Poll> + /// Send a datagram to the specified remote endpoint. + /// + /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`. + /// + /// When the socket's send buffer is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the buffer has space available. + /// + /// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`. + pub fn poll_send_to(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll> where T: Into, { - let remote_endpoint = remote_endpoint.into(); self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) { // Entire datagram has been sent Ok(()) => Poll::Ready(Ok(())), From f192f440187c705ce2674731c4d595dc327f08a3 Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Wed, 12 Jul 2023 11:32:02 +0200 Subject: [PATCH 1536/1575] fmt --- embassy-net/src/udp.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 16f48bcb0..0d97b6db1 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; use core::future::poll_fn; use core::mem; -use core::task::{Poll, Context}; +use core::task::{Context, Poll}; use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; @@ -106,13 +106,13 @@ impl<'a> UdpSocket<'a> { } /// Receive a datagram. - /// + /// /// When no datagram is available, this method will return `Poll::Pending` and /// register the current task to be notified when a datagram is received. - /// + /// /// When a datagram is received, this method will return `Poll::Ready` with the /// number of bytes received and the remote endpoint. - pub fn poll_recv_from(&self, buf: &mut[u8], cx: &mut Context<'_>) -> Poll> { + pub fn poll_recv_from(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll> { self.with_mut(|s, _| match s.recv_slice(buf) { Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))), // No data ready @@ -124,9 +124,9 @@ impl<'a> UdpSocket<'a> { } /// Send a datagram to the specified remote endpoint. - /// + /// /// This method will wait until the datagram has been sent. - /// + /// /// When the remote endpoint is not reachable, this method will return `Err(Error::NoRoute)` pub async fn send_to(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> where @@ -137,12 +137,12 @@ impl<'a> UdpSocket<'a> { } /// Send a datagram to the specified remote endpoint. - /// + /// /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`. - /// + /// /// When the socket's send buffer is full, this method will return `Poll::Pending` /// and register the current task to be notified when the buffer has space available. - /// + /// /// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`. pub fn poll_send_to(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll> where From 55a5e9b3a51b537b7de80221d501be423af43303 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Thu, 22 Jun 2023 22:37:24 +0200 Subject: [PATCH 1537/1575] RP: Add RTC example to rp2040. --- examples/rp/src/bin/rtc.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 examples/rp/src/bin/rtc.rs diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs new file mode 100644 index 000000000..a49c8f627 --- /dev/null +++ b/examples/rp/src/bin/rtc.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::rtc::{DateTime, DayOfWeek, RealTimeClock}; +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()); + info!("Hello World!"); + + let now = DateTime { + year: 2020, + month: 5, + day: 15, + day_of_week: DayOfWeek::Monday, + hour: 10, + minute: 30, + second: 50, + }; + + let rtc_result = RealTimeClock::new(p.RTC, now); + if let Ok(rtc) = rtc_result { + // In reality the delay would be much longer + Timer::after(Duration::from_millis(20000)).await; + + let _then: DateTime = rtc.now().unwrap(); + } +} From 029b156563e70e00cf0ffdf9d5ec23964e5ecc77 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Mon, 26 Jun 2023 08:48:04 +0200 Subject: [PATCH 1538/1575] RP: Add scratchN registers to watchdog. Add Clone and Debug to DateTime --- embassy-rp/src/rtc/datetime_no_deps.rs | 1 + embassy-rp/src/watchdog.rs | 96 ++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs index 92770e984..ea899c339 100644 --- a/embassy-rp/src/rtc/datetime_no_deps.rs +++ b/embassy-rp/src/rtc/datetime_no_deps.rs @@ -25,6 +25,7 @@ pub enum Error { } /// Structure containing date and time information +#[derive(Clone, Debug)] pub struct DateTime { /// 0..4095 pub year: u16, diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index d37795cc9..7b36bb5a8 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -107,4 +107,100 @@ impl Watchdog { w.set_trigger(true); }) } + + pub fn set_scratch0(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch0().write(|w| { + *w = value; + }) + } + + pub fn get_scratch0(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch0().read() + } + + pub fn set_scratch1(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch1().write(|w| { + *w = value; + }) + } + + pub fn get_scratch1(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch1().read() + } + + pub fn set_scratch2(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch2().write(|w| { + *w = value; + }) + } + + pub fn get_scratch2(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch2().read() + } + + pub fn set_scratch3(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch3().write(|w| { + *w = value; + }) + } + + pub fn get_scratch3(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch3().read() + } + + pub fn set_scratch4(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch4().write(|w| { + *w = value; + }) + } + + pub fn get_scratch4(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch4().read() + } + + pub fn set_scratch5(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch5().write(|w| { + *w = value; + }) + } + + pub fn get_scratch5(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch5().read() + } + + pub fn set_scratch6(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch6().write(|w| { + *w = value; + }) + } + + pub fn get_scratch6(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch6().read() + } + + pub fn set_scratch7(&mut self, value: u32) { + let watchdog = pac::WATCHDOG; + watchdog.scratch7().write(|w| { + *w = value; + }) + } + + pub fn get_scratch7(&mut self) -> u32 { + let watchdog = pac::WATCHDOG; + watchdog.scratch7().read() + } } From a93714327eb85b02c7c4a419f2a76df579258975 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Tue, 11 Jul 2023 18:41:45 +0200 Subject: [PATCH 1539/1575] RP: Rename Rtc to match STM32 impl. Remove setting RTC in new(). --- embassy-rp/src/rtc/mod.rs | 20 +++++++++----------- examples/rp/src/bin/rtc.rs | 14 ++++++++++---- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index b18f12fc4..90b796a9c 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -12,26 +12,24 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; use crate::clocks::clk_rtc_freq; /// A reference to the real time clock of the system -pub struct RealTimeClock<'d, T: Instance> { +pub struct Rtc<'d, T: Instance> { inner: PeripheralRef<'d, T>, } -impl<'d, T: Instance> RealTimeClock<'d, T> { +impl<'d, T: Instance> Rtc<'d, T> { /// Create a new instance of the real time clock, with the given date as an initial value. /// /// # Errors /// /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. - pub fn new(inner: impl Peripheral

+ 'd, initial_date: DateTime) -> Result { + pub fn new(inner: impl Peripheral

+ 'd) -> Self { into_ref!(inner); // Set the RTC divider inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); - let mut result = Self { inner }; - result.set_leap_year_check(true); // should be on by default, make sure this is the case. - result.set_datetime(initial_date)?; - Ok(result) + let result = Self { inner }; + result } /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. @@ -43,7 +41,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { }); } - /// Checks to see if this RealTimeClock is running + /// Checks to see if this Rtc is running pub fn is_running(&self) -> bool { self.inner.regs().ctrl().read().rtc_active() } @@ -113,8 +111,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { /// # fn main() { } /// # #[cfg(not(feature = "chrono"))] /// # fn main() { - /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; - /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; + /// # use embassy_rp::rtc::{Rtc, DateTimeFilter}; + /// # let mut real_time_clock: Rtc = unsafe { core::mem::zeroed() }; /// let now = real_time_clock.now().unwrap(); /// real_time_clock.schedule_alarm( /// DateTimeFilter::default() @@ -150,7 +148,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { } } -/// Errors that can occur on methods on [RealTimeClock] +/// Errors that can occur on methods on [Rtc] #[derive(Clone, Debug, PartialEq, Eq)] pub enum RtcError { /// An invalid DateTime was given or stored on the hardware. diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs index a49c8f627..6300950d4 100644 --- a/examples/rp/src/bin/rtc.rs +++ b/examples/rp/src/bin/rtc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::rtc::{DateTime, DayOfWeek, RealTimeClock}; +use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -23,11 +23,17 @@ async fn main(_spawner: Spawner) { second: 50, }; - let rtc_result = RealTimeClock::new(p.RTC, now); - if let Ok(rtc) = rtc_result { + let mut rtc = Rtc::new(p.RTC); + if rtc.set_datetime(now).is_ok() { // In reality the delay would be much longer Timer::after(Duration::from_millis(20000)).await; - let _then: DateTime = rtc.now().unwrap(); + if let Ok(dt) = rtc.now() { + info!( + "Now: {}-{}-{} {}:{}:{}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + ); + } } + info!("Done."); } From 466a391b52836f79fafad5780ad2eb0a07d82513 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Wed, 12 Jul 2023 14:13:19 +0200 Subject: [PATCH 1540/1575] RP: Add save/restore to Rtc. Example use. --- embassy-rp/src/rtc/mod.rs | 30 +++++++++++++++++++++ examples/rp/src/bin/rtc.rs | 55 +++++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 90b796a9c..1b33fdf8d 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -41,6 +41,36 @@ impl<'d, T: Instance> Rtc<'d, T> { }); } + /// Set the time from internal format + pub fn restore(&mut self, ymd: rp_pac::rtc::regs::Rtc1, hms: rp_pac::rtc::regs::Rtc0) { + // disable RTC while we configure it + self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); + while self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + + self.inner.regs().setup_0().write(|w| { + *w = rp_pac::rtc::regs::Setup0(ymd.0); + }); + self.inner.regs().setup_1().write(|w| { + *w = rp_pac::rtc::regs::Setup1(hms.0); + }); + + // Load the new datetime and re-enable RTC + self.inner.regs().ctrl().write(|w| w.set_load(true)); + self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); + while !self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + } + + /// Get the time in internal format + pub fn save(&mut self) -> (rp_pac::rtc::regs::Rtc1, rp_pac::rtc::regs::Rtc0) { + let rtc_0: rp_pac::rtc::regs::Rtc0 = self.inner.regs().rtc_0().read(); + let rtc_1 = self.inner.regs().rtc_1().read(); + (rtc_1, rtc_0) + } + /// Checks to see if this Rtc is running pub fn is_running(&self) -> bool { self.inner.regs().ctrl().read().rtc_active() diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs index 6300950d4..2ddde3257 100644 --- a/examples/rp/src/bin/rtc.rs +++ b/examples/rp/src/bin/rtc.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::pac::rtc::regs::{Rtc0, Rtc1}; use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -11,29 +12,41 @@ use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); - info!("Hello World!"); - - let now = DateTime { - year: 2020, - month: 5, - day: 15, - day_of_week: DayOfWeek::Monday, - hour: 10, - minute: 30, - second: 50, - }; + info!("Wait for 20s"); + let mut watchdog = embassy_rp::watchdog::Watchdog::new(p.WATCHDOG); let mut rtc = Rtc::new(p.RTC); - if rtc.set_datetime(now).is_ok() { - // In reality the delay would be much longer - Timer::after(Duration::from_millis(20000)).await; - if let Ok(dt) = rtc.now() { - info!( - "Now: {}-{}-{} {}:{}:{}", - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, - ); - } + let rtc0 = Rtc0(watchdog.get_scratch0()); + let rtc1 = Rtc1(watchdog.get_scratch1()); + if rtc1.year() >= 2020 { + rtc.restore(rtc1, rtc0); + } else { + let now = DateTime { + year: 2020, + month: 5, + day: 15, + day_of_week: DayOfWeek::Monday, + hour: 10, + minute: 30, + second: 50, + }; + rtc.set_datetime(now).unwrap(); } - info!("Done."); + + Timer::after(Duration::from_millis(20000)).await; + + if let Ok(dt) = rtc.now() { + info!( + "Now: {}-{:02}-{:02} {}:{:02}:{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + ); + let (rtc1, rtc0) = rtc.save(); + watchdog.set_scratch0(rtc0.0); + watchdog.set_scratch1(rtc1.0); + } + + info!("Reboot."); + Timer::after(Duration::from_millis(200)).await; + cortex_m::peripheral::SCB::sys_reset(); } From 6d402fe3932ac04ff939379e6520322476f683dc Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Wed, 12 Jul 2023 15:16:56 +0200 Subject: [PATCH 1541/1575] RP: Don't reset RTC in Clock::init. Updated example. --- embassy-rp/src/clocks.rs | 2 ++ examples/rp/src/bin/rtc.rs | 26 +++++++++----------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index ddd61d224..acb21dce5 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -308,6 +308,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // - QSPI (we're using it to run this code!) // - PLLs (it may be suicide if that's what's clocking us) // - USB, SYSCFG (breaks usb-to-swd on core1) + // - RTC (else there would be no more time...) let mut peris = reset::ALL_PERIPHERALS; peris.set_io_qspi(false); // peris.set_io_bank0(false); // might be suicide if we're clocked from gpin @@ -317,6 +318,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // TODO investigate if usb should be unreset here peris.set_usbctrl(false); peris.set_syscfg(false); + peris.set_rtc(false); reset::reset(peris); // Disable resus that may be enabled from previous software diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs index 2ddde3257..d569f598f 100644 --- a/examples/rp/src/bin/rtc.rs +++ b/examples/rp/src/bin/rtc.rs @@ -4,7 +4,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::pac::rtc::regs::{Rtc0, Rtc1}; use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,22 +13,18 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); info!("Wait for 20s"); - let mut watchdog = embassy_rp::watchdog::Watchdog::new(p.WATCHDOG); let mut rtc = Rtc::new(p.RTC); - let rtc0 = Rtc0(watchdog.get_scratch0()); - let rtc1 = Rtc1(watchdog.get_scratch1()); - if rtc1.year() >= 2020 { - rtc.restore(rtc1, rtc0); - } else { + if !rtc.is_running() { + info!("Start RTC"); let now = DateTime { - year: 2020, - month: 5, - day: 15, - day_of_week: DayOfWeek::Monday, - hour: 10, - minute: 30, - second: 50, + year: 2000, + month: 1, + day: 1, + day_of_week: DayOfWeek::Saturday, + hour: 0, + minute: 0, + second: 0, }; rtc.set_datetime(now).unwrap(); } @@ -41,9 +36,6 @@ async fn main(_spawner: Spawner) { "Now: {}-{:02}-{:02} {}:{:02}:{:02}", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, ); - let (rtc1, rtc0) = rtc.save(); - watchdog.set_scratch0(rtc0.0); - watchdog.set_scratch1(rtc1.0); } info!("Reboot."); From d5a4457b5e3a95a12f249315fb1d9b6a577307f2 Mon Sep 17 00:00:00 2001 From: goueslati Date: Wed, 12 Jul 2023 15:06:56 +0100 Subject: [PATCH 1542/1575] parsing MAC structs --- embassy-stm32-wpan/Cargo.toml | 7 +- embassy-stm32-wpan/src/sub/mac/commands.rs | 55 +-- embassy-stm32-wpan/src/sub/mac/event.rs | 94 +++++ embassy-stm32-wpan/src/sub/mac/helpers.rs | 7 + embassy-stm32-wpan/src/sub/mac/indications.rs | 295 ++++++++++++-- embassy-stm32-wpan/src/sub/mac/macros.rs | 32 ++ embassy-stm32-wpan/src/sub/mac/mod.rs | 63 ++- embassy-stm32-wpan/src/sub/mac/opcodes.rs | 63 +++ embassy-stm32-wpan/src/sub/mac/responses.rs | 302 +++++++++++++-- embassy-stm32-wpan/src/sub/mac/typedefs.rs | 359 ++++++++++++++---- examples/stm32wb/src/bin/mac_ffd.rs | 99 ++--- examples/stm32wb/src/bin/mac_rfd.rs | 14 +- 12 files changed, 1107 insertions(+), 283 deletions(-) create mode 100644 embassy-stm32-wpan/src/sub/mac/event.rs create mode 100644 embassy-stm32-wpan/src/sub/mac/helpers.rs create mode 100644 embassy-stm32-wpan/src/sub/mac/macros.rs diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 4b5dcdd29..1325faed9 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -26,13 +26,14 @@ aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true } +bitflags = { version = "2.3.3", optional = true } [features] -default = ["stm32wb55rg", "mac", "ble"] +default = ["stm32wb55rg", "mac", "ble", "defmt"] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] ble = ["dep:stm32wb-hci"] -mac = [] +mac = ["dep:bitflags"] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] @@ -49,4 +50,4 @@ stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ] stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ] stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ] stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ] -stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] \ No newline at end of file +stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/sub/mac/commands.rs index 4965a46eb..d8a4e3ee1 100644 --- a/embassy-stm32-wpan/src/sub/mac/commands.rs +++ b/embassy-stm32-wpan/src/sub/mac/commands.rs @@ -1,5 +1,8 @@ use super::opcodes::OpcodeM4ToM0; -use super::typedefs::{AddressMode, GtsCharacteristics, MacAddress, PibId}; +use super::typedefs::{ + AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, PibId, + ScanType, SecurityLevel, +}; pub trait MacCommand { const OPCODE: OpcodeM4ToM0; @@ -14,19 +17,19 @@ pub trait MacCommand { #[repr(C)] pub struct AssociateRequest { /// the logical channel on which to attempt association - pub channel_number: u8, + pub channel_number: MacChannel, /// the channel page on which to attempt association pub channel_page: u8, /// coordinator addressing mode pub coord_addr_mode: AddressMode, /// operational capabilities of the associating device - pub capability_information: u8, + pub capability_information: Capabilities, /// the identifier of the PAN with which to associate pub coord_pan_id: [u8; 2], /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the originator of the key to be used pub key_source: [u8; 8], /// Coordinator address @@ -48,15 +51,15 @@ pub struct DisassociateRequest { /// the identifier of the PAN of the device pub device_pan_id: [u8; 2], /// the reason for the disassociation - pub disassociate_reason: u8, + pub disassociation_reason: DisassociationReason, /// device address pub device_address: MacAddress, /// `true` if the disassociation notification command is to be sent indirectly pub tx_indirect: bool, /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode to be used to indetify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, /// the originator of the key to be used @@ -86,9 +89,9 @@ pub struct GtsRequest { /// the characteristics of the GTS pub characteristics: GtsCharacteristics, /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, /// the originator of the key to be used @@ -147,19 +150,19 @@ impl MacCommand for RxEnableRequest { #[repr(C)] pub struct ScanRequest { /// the type of scan to be performed - pub scan_type: u8, + pub scan_type: ScanType, /// the time spent on scanning each channel pub scan_duration: u8, /// channel page on which to perform the scan pub channel_page: u8, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// indicate which channels are to be scanned pub scan_channels: [u8; 4], /// originator the key to be used pub key_source: [u8; 8], /// mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// index of the key to be used pub key_index: u8, } @@ -191,7 +194,7 @@ pub struct StartRequest { /// PAN indentifier to used by the device pub pan_id: [u8; 2], /// logical channel on which to begin - pub channel_number: u8, + pub channel_number: MacChannel, /// channel page on which to begin pub channel_page: u8, /// time at which to begin transmitting beacons @@ -207,15 +210,15 @@ pub struct StartRequest { /// indicated if the coordinator realignment command is to be trasmitted pub coord_realignment: u8, /// indicated if the coordinator realignment command is to be trasmitted - pub coord_realign_security_level: u8, + pub coord_realign_security_level: SecurityLevel, /// index of the key to be used pub coord_realign_key_id_index: u8, /// originator of the key to be used pub coord_realign_key_source: [u8; 8], /// security level to be used for beacon frames - pub beacon_security_level: u8, + pub beacon_security_level: SecurityLevel, /// mode used to identify the key to be used - pub beacon_key_id_mode: u8, + pub beacon_key_id_mode: KeyIdMode, /// index of the key to be used pub beacon_key_index: u8, /// originator of the key to be used @@ -232,7 +235,7 @@ impl MacCommand for StartRequest { #[repr(C)] pub struct SyncRequest { /// the channel number on which to attempt coordinator synchronization - pub channel_number: u8, + pub channel_number: MacChannel, /// the channel page on which to attempt coordinator synchronization pub channel_page: u8, /// `true` if the MLME is to synchronize with the next beacon and attempts @@ -253,9 +256,9 @@ pub struct PollRequest { /// addressing mode of the coordinator pub coord_addr_mode: AddressMode, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// index of the key to be used pub key_index: u8, /// coordinator address @@ -335,9 +338,9 @@ pub struct DataRequest { /// the pending bit transmission options for the MSDU pub indirect_tx: u8, /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode used to indentify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, /// the originator of the key to be used @@ -381,11 +384,11 @@ pub struct AssociateResponse { /// status of the association attempt pub status: u8, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the originator of the key to be used pub key_source: [u8; 8], /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, } @@ -405,11 +408,11 @@ pub struct OrphanResponse { /// if the orphaned device is associated with coordinator or not pub associated_member: bool, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the originator of the key to be used pub key_source: [u8; 8], /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, } diff --git a/embassy-stm32-wpan/src/sub/mac/event.rs b/embassy-stm32-wpan/src/sub/mac/event.rs new file mode 100644 index 000000000..aaf965565 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/event.rs @@ -0,0 +1,94 @@ +use super::helpers::to_u16; +use super::indications::{ + AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, + DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, +}; +use super::responses::{ + AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, + PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, +}; +use crate::sub::mac::opcodes::OpcodeM0ToM4; + +pub trait ParseableMacEvent { + const SIZE: usize; + + fn validate(buf: &[u8]) -> Result<(), ()> { + if buf.len() < Self::SIZE { + return Err(()); + } + + Ok(()) + } + + fn try_parse(buf: &[u8]) -> Result + where + Self: Sized; +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacEvent { + MlmeAssociateCnf(AssociateConfirm), + MlmeDisassociateCnf(DisassociateConfirm), + MlmeGetCnf(GetConfirm), + MlmeGtsCnf(GtsConfirm), + MlmeResetCnf(ResetConfirm), + MlmeRxEnableCnf(RxEnableConfirm), + MlmeScanCnf(ScanConfirm), + MlmeSetCnf(SetConfirm), + MlmeStartCnf(StartConfirm), + MlmePollCnf(PollConfirm), + MlmeDpsCnf(DpsConfirm), + MlmeSoundingCnf(SoundingConfirm), + MlmeCalibrateCnf(CalibrateConfirm), + McpsDataCnf(DataConfirm), + McpsPurgeCnf(PurgeConfirm), + MlmeAssociateInd(AssociateIndication), + MlmeDisassociateInd(DisassociateIndication), + MlmeBeaconNotifyInd(BeaconNotifyIndication), + MlmeCommStatusInd(CommStatusIndication), + MlmeGtsInd(GtsIndication), + MlmeOrphanInd(OrphanIndication), + MlmeSyncLossInd(SyncLossIndication), + MlmeDpsInd(DpsIndication), + McpsDataInd(DataIndication), + MlmePollInd(PollIndication), +} + +impl TryFrom<&[u8]> for MacEvent { + type Error = (); + + fn try_from(value: &[u8]) -> Result { + let opcode = to_u16(&value[0..2]); + let opcode = OpcodeM0ToM4::try_from(opcode)?; + + let buf = &value[2..]; + + match opcode { + OpcodeM0ToM4::MlmeAssociateCnf => Ok(Self::MlmeAssociateCnf(AssociateConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDisassociateCnf => Ok(Self::MlmeDisassociateCnf(DisassociateConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeGetCnf => Ok(Self::MlmeGetCnf(GetConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeGtsCnf => Ok(Self::MlmeGtsCnf(GtsConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeResetCnf => Ok(Self::MlmeResetCnf(ResetConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeRxEnableCnf => Ok(Self::MlmeRxEnableCnf(RxEnableConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeScanCnf => Ok(Self::MlmeScanCnf(ScanConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeSetCnf => Ok(Self::MlmeSetCnf(SetConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeStartCnf => Ok(Self::MlmeStartCnf(StartConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmePollCnf => Ok(Self::MlmePollCnf(PollConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDpsCnf => Ok(Self::MlmeDpsCnf(DpsConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeSoundingCnf => Ok(Self::MlmeSoundingCnf(SoundingConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeCalibrateCnf => Ok(Self::MlmeCalibrateCnf(CalibrateConfirm::try_parse(buf)?)), + OpcodeM0ToM4::McpsDataCnf => Ok(Self::McpsDataCnf(DataConfirm::try_parse(buf)?)), + OpcodeM0ToM4::McpsPurgeCnf => Ok(Self::McpsPurgeCnf(PurgeConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeAssociateInd => Ok(Self::MlmeAssociateInd(AssociateIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDisassociateInd => Ok(Self::MlmeDisassociateInd(DisassociateIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(Self::MlmeBeaconNotifyInd(BeaconNotifyIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeCommStatusInd => Ok(Self::MlmeCommStatusInd(CommStatusIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeGtsInd => Ok(Self::MlmeGtsInd(GtsIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeOrphanInd => Ok(Self::MlmeOrphanInd(OrphanIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeSyncLossInd => Ok(Self::MlmeSyncLossInd(SyncLossIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDpsInd => Ok(Self::MlmeDpsInd(DpsIndication::try_parse(buf)?)), + OpcodeM0ToM4::McpsDataInd => Ok(Self::McpsDataInd(DataIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmePollInd => Ok(Self::MlmePollInd(PollIndication::try_parse(buf)?)), + } + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/helpers.rs b/embassy-stm32-wpan/src/sub/mac/helpers.rs new file mode 100644 index 000000000..5a5bf8a85 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/helpers.rs @@ -0,0 +1,7 @@ +pub fn to_u16(buf: &[u8]) -> u16 { + ((buf[1] as u16) << 8) | buf[0] as u16 +} + +pub fn to_u32(buf: &[u8]) -> u32 { + ((buf[0] as u32) << 0) + ((buf[1] as u32) << 8) + ((buf[2] as u32) << 16) + ((buf[3] as u32) << 24) +} diff --git a/embassy-stm32-wpan/src/sub/mac/indications.rs b/embassy-stm32-wpan/src/sub/mac/indications.rs index ebca16f72..dc5ae4c44 100644 --- a/embassy-stm32-wpan/src/sub/mac/indications.rs +++ b/embassy-stm32-wpan/src/sub/mac/indications.rs @@ -1,45 +1,84 @@ use super::consts::MAX_PENDING_ADDRESS; -use super::typedefs::{AddressMode, MacAddress, PanDescriptor}; +use super::event::ParseableMacEvent; +use super::helpers::to_u32; +use super::typedefs::{ + AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, + SecurityLevel, +}; /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateIndication { /// Extended address of the device requesting association pub device_address: [u8; 8], /// Operational capabilities of the device requesting association - pub capability_information: u8, + pub capability_information: Capabilities, /// Security level purportedly used by the received MAC command frame - pub security_level: u8, + pub security_level: SecurityLevel, /// The mode used to identify the key used by the originator of frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, /// The originator of the key used by the originator of the received frame pub key_source: [u8; 8], } +impl ParseableMacEvent for AssociateIndication { + const SIZE: usize = 20; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], + capability_information: Capabilities::from_bits(buf[8]).ok_or(())?, + security_level: SecurityLevel::try_from(buf[9])?, + key_id_mode: KeyIdMode::try_from(buf[10])?, + key_index: buf[11], + key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], + }) + } +} + /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateIndication { /// Extended address of the device requesting association pub device_address: [u8; 8], /// The reason for the disassociation - pub disassociate_reason: u8, + pub disassociation_reason: DisassociationReason, /// The security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// The mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// The index of the key to be used pub key_index: u8, /// The originator of the key to be used pub key_source: [u8; 8], } +impl ParseableMacEvent for DisassociateIndication { + const SIZE: usize = 20; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], + disassociation_reason: DisassociationReason::try_from(buf[8])?, + security_level: SecurityLevel::try_from(buf[9])?, + key_id_mode: KeyIdMode::try_from(buf[10])?, + key_index: buf[11], + key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], + }) + } +} + /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BeaconNotifyIndication { /// he set of octets comprising the beacon payload to be transferred /// from the MAC sublayer entity to the next higher layer @@ -56,8 +95,18 @@ pub struct BeaconNotifyIndication { pub sdu_length: u8, } +impl ParseableMacEvent for BeaconNotifyIndication { + const SIZE: usize = 12; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + todo!() + } +} + /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CommStatusIndication { /// The 16-bit PAN identifier of the device from which the frame /// was received or to which the frame was being sent @@ -71,83 +120,190 @@ pub struct CommStatusIndication { /// Destination address pub dst_address: MacAddress, /// The communications status - pub status: u8, + pub status: MacStatus, /// Security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key to be used pub key_index: u8, /// Originator of the key to be used pub key_source: [u8; 8], } +impl ParseableMacEvent for CommStatusIndication { + const SIZE: usize = 32; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let src_addr_mode = AddressMode::try_from(buf[2])?; + let dst_addr_mode = AddressMode::try_from(buf[3])?; + + let src_address = match src_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), + AddressMode::Extended => { + MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) + } + }; + + let dst_address = match dst_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[12], buf[13]]), + AddressMode::Extended => { + MacAddress::Extended([buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]]) + } + }; + + Ok(Self { + pan_id: [buf[0], buf[1]], + src_addr_mode, + dst_addr_mode, + src_address, + dst_address, + status: MacStatus::try_from(buf[20])?, + security_level: SecurityLevel::try_from(buf[21])?, + key_id_mode: KeyIdMode::try_from(buf[22])?, + key_index: buf[23], + key_source: [buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]], + }) + } +} + /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsIndication { /// The short address of the device that has been allocated or deallocated a GTS pub device_address: [u8; 2], /// The characteristics of the GTS pub gts_characteristics: u8, /// Security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key to be used pub key_index: u8, /// Originator of the key to be used pub key_source: [u8; 8], } +impl ParseableMacEvent for GtsIndication { + const SIZE: usize = 16; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + device_address: [buf[0], buf[1]], + gts_characteristics: buf[2], + security_level: SecurityLevel::try_from(buf[3])?, + key_id_mode: KeyIdMode::try_from(buf[4])?, + key_index: buf[5], + // 2 byte stuffing + key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], + }) + } +} + /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OrphanIndication { /// Extended address of the orphaned device pub orphan_address: [u8; 8], /// Originator of the key used by the originator of the received frame pub key_source: [u8; 8], /// Security level purportedly used by the received MAC command frame - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, } +impl ParseableMacEvent for OrphanIndication { + const SIZE: usize = 20; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + orphan_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], + key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], + security_level: SecurityLevel::try_from(buf[16])?, + key_id_mode: KeyIdMode::try_from(buf[17])?, + key_index: buf[18], + // 1 byte stuffing + }) + } +} + /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncLossIndication { /// The PAN identifier with which the device lost synchronization or to which it was realigned pub pan_id: [u8; 2], /// The reason that synchronization was lost pub loss_reason: u8, /// The logical channel on which the device lost synchronization or to whi - pub channel_number: u8, + pub channel_number: MacChannel, /// The channel page on which the device lost synchronization or to which pub channel_page: u8, /// The security level used by the received MAC frame - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, /// Originator of the key used by the originator of the received frame pub key_source: [u8; 8], } +impl ParseableMacEvent for SyncLossIndication { + const SIZE: usize = 16; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + pan_id: [buf[0], buf[1]], + loss_reason: buf[2], + channel_number: MacChannel::try_from(buf[3])?, + channel_page: buf[4], + security_level: SecurityLevel::try_from(buf[5])?, + key_id_mode: KeyIdMode::try_from(buf[6])?, + key_index: buf[7], + key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], + }) + } +} + /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsIndication; -#[repr(C)] +impl ParseableMacEvent for DpsIndication { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self) + } +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, /// Source addressing mode used - pub src_addr_mode: u8, + pub src_addr_mode: AddressMode, /// Source PAN ID pub src_pan_id: [u8; 2], /// Source address @@ -167,9 +323,9 @@ pub struct DataIndication { /// The time, in symbols, at which the data were received pub time_stamp: [u8; 4], /// The security level purportedly used by the received data frame - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// The originator of the key pub key_source: [u8; 8], /// The index of the key @@ -194,12 +350,91 @@ pub struct DataIndication { pub rssi: u8, } +impl ParseableMacEvent for DataIndication { + const SIZE: usize = 72; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let src_addr_mode = AddressMode::try_from(buf[4])?; + let src_address = match src_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[7], buf[8]]), + AddressMode::Extended => { + MacAddress::Extended([buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14]]) + } + }; + + let dst_addr_mode = AddressMode::try_from(buf[15])?; + let dst_address = match dst_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[18], buf[19]]), + AddressMode::Extended => { + MacAddress::Extended([buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25]]) + } + }; + + Ok(Self { + msdu_ptr: to_u32(&buf[0..4]) as *const u8, + src_addr_mode, + src_pan_id: [buf[5], buf[6]], + src_address, + dst_addr_mode, + dst_pan_id: [buf[16], buf[17]], + dst_address, + msdu_length: buf[26], + mpdu_link_quality: buf[27], + dsn: buf[28], + time_stamp: [buf[29], buf[30], buf[31], buf[32]], + security_level: SecurityLevel::try_from(buf[33])?, + key_id_mode: KeyIdMode::try_from(buf[34])?, + key_source: [buf[35], buf[36], buf[37], buf[38], buf[39], buf[40], buf[41], buf[42]], + key_index: buf[43], + uwbprf: buf[44], + uwn_preamble_symbol_repetitions: buf[45], + datrate: buf[46], + ranging_received: buf[47], + ranging_counter_start: to_u32(&buf[58..52]), + ranging_counter_stop: to_u32(&buf[52..56]), + ranging_tracking_interval: to_u32(&buf[56..60]), + ranging_offset: to_u32(&buf[60..64]), + ranging_fom: buf[65], + rssi: buf[66], + }) + } +} + /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollIndication { /// addressing mode used - pub addr_mode: u8, + pub addr_mode: AddressMode, /// Poll requester address pub request_address: MacAddress, } + +impl ParseableMacEvent for PollIndication { + const SIZE: usize = 9; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let addr_mode = AddressMode::try_from(buf[0])?; + let request_address = match addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[1], buf[2]]), + AddressMode::Extended => { + MacAddress::Extended([buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]]) + } + }; + + Ok(Self { + addr_mode, + request_address, + }) + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/macros.rs b/embassy-stm32-wpan/src/sub/mac/macros.rs new file mode 100644 index 000000000..1a988a779 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/macros.rs @@ -0,0 +1,32 @@ +#[macro_export] +macro_rules! numeric_enum { + (#[repr($repr:ident)] + $(#$attrs:tt)* $vis:vis enum $name:ident { + $($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)? + } ) => { + #[repr($repr)] + $(#$attrs)* + $vis enum $name { + $($(#$enum_attrs)* $enum = $constant),* + } + + impl ::core::convert::TryFrom<$repr> for $name { + type Error = (); + + fn try_from(value: $repr) -> ::core::result::Result { + match value { + $($constant => Ok( $name :: $enum ),)* + _ => Err(()) + } + } + } + + impl ::core::convert::From<$name> for $repr { + fn from(value: $name) -> $repr { + match value { + $($name :: $enum => $constant,)* + } + } + } + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs index 756d7d5b1..0524f135a 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -9,7 +9,8 @@ use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; use self::commands::MacCommand; -use self::typedefs::MacStatus; +use self::event::MacEvent; +use self::typedefs::MacError; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; @@ -18,7 +19,10 @@ use crate::{channels, evt}; pub mod commands; mod consts; +pub mod event; +mod helpers; pub mod indications; +mod macros; mod opcodes; pub mod responses; pub mod typedefs; @@ -38,7 +42,7 @@ impl Mac { /// `HW_IPCC_MAC_802_15_4_EvtNot` /// /// This function will stall if the previous `EvtBox` has not been dropped - pub async fn read(&self) -> EvtBox { + pub async fn tl_read(&self) -> EvtBox { // Wait for the last event box to be dropped poll_fn(|cx| { MAC_WAKER.register(cx.waker()); @@ -62,33 +66,20 @@ impl Mac { } /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` - pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { - self.write(opcode, payload).await; + pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { + self.tl_write(opcode, payload).await; Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await; unsafe { let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8; - let evt_serial = (MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket) - .read_volatile() - .evt_serial; - let kind = (evt_serial).kind; - let evt_code = evt_serial.evt.evt_code; - let payload_len = evt_serial.evt.payload_len; - let payload = evt_serial.evt.payload; - - debug!( - "evt kind {} evt_code {} len {} payload {}", - kind, evt_code, payload_len, payload - ); - ptr::read_volatile(p_mac_rsp_evt) } } /// `TL_MAC_802_15_4_SendCmd` - pub async fn write(&self, opcode: u16, payload: &[u8]) { + pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe { CmdPacket::write_into( MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), @@ -98,37 +89,31 @@ impl Mac { ); }) .await; - - unsafe { - let typ = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.ty; - let cmd_code = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.cmd.cmd_code; - let payload_len = MAC_802_15_4_CMD_BUFFER - .as_ptr() - .read_volatile() - .cmdserial - .cmd - .payload_len; - let payload = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.cmd.payload; - - debug!( - "serial type {} cmd_code {} len {} payload {}", - typ, cmd_code, payload_len, payload - ); - } } - pub async fn send_command(&self, cmd: T) -> Result + pub async fn send_command(&self, cmd: T) -> Result<(), MacError> where T: MacCommand, { let mut payload = [0u8; MAX_PACKET_SIZE]; cmd.copy_into_slice(&mut payload); - debug!("sending {:#x}", payload[..T::SIZE]); + let response = self + .tl_write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]) + .await; - let response = self.write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]).await; + if response == 0x00 { + Ok(()) + } else { + Err(MacError::from(response)) + } + } - MacStatus::try_from(response) + pub async fn read(&self) -> Result { + let evt_box = self.tl_read().await; + let payload = evt_box.payload(); + + MacEvent::try_from(payload) } } diff --git a/embassy-stm32-wpan/src/sub/mac/opcodes.rs b/embassy-stm32-wpan/src/sub/mac/opcodes.rs index 511b78157..c9a07d6af 100644 --- a/embassy-stm32-wpan/src/sub/mac/opcodes.rs +++ b/embassy-stm32-wpan/src/sub/mac/opcodes.rs @@ -25,3 +25,66 @@ pub enum OpcodeM4ToM0 { McpsDataReq = opcode(0x10), McpsPurgeReq = opcode(0x11), } + +pub enum OpcodeM0ToM4 { + MlmeAssociateCnf = 0x00, + MlmeDisassociateCnf, + MlmeGetCnf, + MlmeGtsCnf, + MlmeResetCnf, + MlmeRxEnableCnf, + MlmeScanCnf, + MlmeSetCnf, + MlmeStartCnf, + MlmePollCnf, + MlmeDpsCnf, + MlmeSoundingCnf, + MlmeCalibrateCnf, + McpsDataCnf, + McpsPurgeCnf, + MlmeAssociateInd, + MlmeDisassociateInd, + MlmeBeaconNotifyInd, + MlmeCommStatusInd, + MlmeGtsInd, + MlmeOrphanInd, + MlmeSyncLossInd, + MlmeDpsInd, + McpsDataInd, + MlmePollInd, +} + +impl TryFrom for OpcodeM0ToM4 { + type Error = (); + + fn try_from(value: u16) -> Result { + match value { + 0 => Ok(Self::MlmeAssociateCnf), + 1 => Ok(Self::MlmeDisassociateCnf), + 2 => Ok(Self::MlmeGetCnf), + 3 => Ok(Self::MlmeGtsCnf), + 4 => Ok(Self::MlmeResetCnf), + 5 => Ok(Self::MlmeRxEnableCnf), + 6 => Ok(Self::MlmeScanCnf), + 7 => Ok(Self::MlmeSetCnf), + 8 => Ok(Self::MlmeStartCnf), + 9 => Ok(Self::MlmePollCnf), + 10 => Ok(Self::MlmeDpsCnf), + 11 => Ok(Self::MlmeSoundingCnf), + 12 => Ok(Self::MlmeCalibrateCnf), + 13 => Ok(Self::McpsDataCnf), + 14 => Ok(Self::McpsPurgeCnf), + 15 => Ok(Self::MlmeAssociateInd), + 16 => Ok(Self::MlmeDisassociateInd), + 17 => Ok(Self::MlmeBeaconNotifyInd), + 18 => Ok(Self::MlmeCommStatusInd), + 19 => Ok(Self::MlmeGtsInd), + 20 => Ok(Self::MlmeOrphanInd), + 21 => Ok(Self::MlmeSyncLossInd), + 22 => Ok(Self::MlmeDpsInd), + 23 => Ok(Self::McpsDataInd), + 24 => Ok(Self::MlmePollInd), + _ => Err(()), + } + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index 8c30a1823..ce2ca2fb2 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -1,35 +1,50 @@ use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED}; -use super::typedefs::{AddressMode, MacAddress, PanDescriptor}; - -pub trait MacResponse { - const SIZE: usize; - - fn parse(buf: &[u8]) -> Self; -} +use super::event::ParseableMacEvent; +use super::helpers::to_u32; +use super::typedefs::{ + AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PibId, ScanType, SecurityLevel, +}; /// MLME ASSOCIATE Confirm used to inform of the initiating device whether /// its request to associate was successful or unsuccessful -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateConfirm { /// short address allocated by the coordinator on successful association pub assoc_short_address: [u8; 2], /// status of the association request - pub status: u8, + pub status: AssociationStatus, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the originator of the key to be used pub key_source: [u8; 8], /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, } +impl ParseableMacEvent for AssociateConfirm { + const SIZE: usize = 16; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + assoc_short_address: [buf[0], buf[1]], + status: AssociationStatus::try_from(buf[2])?, + security_level: SecurityLevel::try_from(buf[3])?, + key_source: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], + key_id_mode: KeyIdMode::try_from(buf[12])?, + key_index: buf[13], + }) + } +} + /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateConfirm { /// status of the disassociation attempt - pub status: u8, + pub status: MacStatus, /// device addressing mode used pub device_addr_mode: AddressMode, /// the identifier of the PAN of the device @@ -38,51 +53,130 @@ pub struct DisassociateConfirm { pub device_address: MacAddress, } +impl ParseableMacEvent for DisassociateConfirm { + const SIZE: usize = 12; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let device_addr_mode = AddressMode::try_from(buf[1])?; + let device_address = match device_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), + AddressMode::Extended => { + MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) + } + }; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + device_addr_mode, + device_pan_id: [buf[2], buf[3]], + device_address, + }) + } +} + /// MLME GET Confirm which requests information about a given PIB attribute -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetConfirm { /// The pointer to the value of the PIB attribute attempted to read pub pib_attribute_value_ptr: *const u8, /// Status of the GET attempt - pub status: u8, + pub status: MacStatus, /// The name of the PIB attribute attempted to read - pub pib_attribute: u8, + pub pib_attribute: PibId, /// The lenght of the PIB attribute Value return pub pib_attribute_value_len: u8, } +impl ParseableMacEvent for GetConfirm { + const SIZE: usize = 8; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let address = to_u32(&buf[0..4]); + + Ok(Self { + pib_attribute_value_ptr: address as *const u8, + status: MacStatus::try_from(buf[4])?, + pib_attribute: PibId::try_from(buf[5])?, + pib_attribute_value_len: buf[6], + }) + } +} + /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsConfirm { /// The characteristics of the GTS pub gts_characteristics: u8, /// The status of the GTS reques - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for GtsConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + gts_characteristics: buf[0], + status: MacStatus::try_from(buf[1])?, + }) + } } /// MLME RESET Confirm which is used to report the results of the reset operation -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation - status: u8, + status: MacStatus, +} + +impl ParseableMacEvent for ResetConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver - status: u8, + status: MacStatus, +} + +impl ParseableMacEvent for RxEnableConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME SCAN Confirm which is used to report the result of the channel scan request -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanConfirm { /// Status of the scan request - pub status: u8, + pub status: MacStatus, /// The type of scan performed - pub scan_type: u8, + pub scan_type: ScanType, /// Channel page on which the scan was performed pub channel_page: u8, /// Channels given in the request which were not scanned @@ -99,44 +193,124 @@ pub struct ScanConfirm { pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], } +impl ParseableMacEvent for ScanConfirm { + const SIZE: usize = 9; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + todo!() + } +} + /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SetConfirm { /// The result of the set operation - pub status: u8, + pub status: MacStatus, /// The name of the PIB attribute that was written - pub pin_attribute: u8, + pub pin_attribute: PibId, +} + +impl ParseableMacEvent for SetConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + pin_attribute: PibId::try_from(buf[1])?, + }) + } } /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for StartConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + debug!("{:#x}", buf); + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for PollConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } +} + +/// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DpsConfirm { + /// The status of the DPS request + pub status: MacStatus, +} + +impl ParseableMacEvent for DpsConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingConfirm { /// Results of the sounding measurement sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], } +impl ParseableMacEvent for SoundingConfirm { + const SIZE: usize = 1; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let mut sounding_list = [0u8; MAX_SOUNDING_LIST_SUPPORTED]; + sounding_list[..buf.len()].copy_from_slice(buf); + + Ok(Self { sounding_list }) + } +} + /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CalibrateConfirm { /// The status of the attempt to return sounding data - pub status: u8, + pub status: MacStatus, /// A count of the propagation time from the ranging counter /// to the transmit antenna pub cal_tx_rmaker_offset: u32, @@ -145,18 +319,33 @@ pub struct CalibrateConfirm { pub cal_rx_rmaker_offset: u32, } +impl ParseableMacEvent for CalibrateConfirm { + const SIZE: usize = 12; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + // 3 byte stuffing + cal_tx_rmaker_offset: to_u32(&buf[4..8]), + cal_rx_rmaker_offset: to_u32(&buf[8..12]), + }) + } +} + /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataConfirm { /// The handle associated with the MSDU being confirmed pub msdu_handle: u8, /// The time, in symbols, at which the data were transmitted - pub a_time_stamp: [u8; 4], + pub time_stamp: [u8; 4], /// ranging status pub ranging_received: u8, /// The status of the last MSDU transmission - pub status: u8, + pub status: MacStatus, /// time units corresponding to an RMARKER at the antenna at /// the beginning of a ranging exchange pub ranging_counter_start: u32, @@ -171,12 +360,45 @@ pub struct DataConfirm { pub ranging_fom: u8, } +impl ParseableMacEvent for DataConfirm { + const SIZE: usize = 28; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + msdu_handle: buf[0], + time_stamp: [buf[1], buf[2], buf[3], buf[4]], + ranging_received: buf[5], + status: MacStatus::try_from(buf[6])?, + ranging_counter_start: to_u32(&buf[7..11]), + ranging_counter_stop: to_u32(&buf[11..15]), + ranging_tracking_interval: to_u32(&buf[15..19]), + ranging_offset: to_u32(&buf[19..23]), + ranging_fom: buf[24], + }) + } +} + /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PurgeConfirm { /// Handle associated with the MSDU requested to be purged from the transaction queue pub msdu_handle: u8, /// The status of the request - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for PurgeConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + msdu_handle: buf[0], + status: MacStatus::try_from(buf[1])?, + }) + } } diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/sub/mac/typedefs.rs index 7f0dd75c0..fe79b3a79 100644 --- a/embassy-stm32-wpan/src/sub/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/sub/mac/typedefs.rs @@ -1,6 +1,8 @@ +use crate::numeric_enum; + +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MacStatus { - Success = 0x00, +pub enum MacError { Error = 0x01, NotImplemented = 0x02, NotSupported = 0x03, @@ -8,88 +10,115 @@ pub enum MacStatus { Undefined = 0x05, } -impl TryFrom for MacStatus { - type Error = (); - - fn try_from(value: u8) -> Result>::Error> { +impl From for MacError { + fn from(value: u8) -> Self { match value { - 0x00 => Ok(Self::Success), - 0x01 => Ok(Self::Error), - 0x02 => Ok(Self::NotImplemented), - 0x03 => Ok(Self::NotSupported), - 0x04 => Ok(Self::HardwareNotSupported), - 0x05 => Ok(Self::Undefined), - _ => Err(()), + 0x01 => Self::Error, + 0x02 => Self::NotImplemented, + 0x03 => Self::NotSupported, + 0x04 => Self::HardwareNotSupported, + 0x05 => Self::Undefined, + _ => Self::Undefined, } } } -/// this enum contains all the MAC PIB Ids -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PibId { - // PHY - CurrentChannel = 0x00, - ChannelsSupported = 0x01, - TransmitPower = 0x02, - CCAMode = 0x03, - CurrentPage = 0x04, - MaxFrameDuration = 0x05, - SHRDuration = 0x06, - SymbolsPerOctet = 0x07, - - // MAC - AckWaitDuration = 0x40, - AssociationPermit = 0x41, - AutoRequest = 0x42, - BeaconPayload = 0x45, - BeaconPayloadLength = 0x46, - BeaconOrder = 0x47, - Bsn = 0x49, - CoordExtendedAdddress = 0x4A, - CoordShortAddress = 0x4B, - Dsn = 0x4C, - MaxFrameTotalWaitTime = 0x58, - MaxFrameRetries = 0x59, - PanId = 0x50, - ResponseWaitTime = 0x5A, - RxOnWhenIdle = 0x52, - SecurityEnabled = 0x5D, - ShortAddress = 0x53, - SuperframeOrder = 0x54, - TimestampSupported = 0x5C, - TransactionPersistenceTime = 0x55, - MaxBe = 0x57, - LifsPeriod = 0x5E, - SifsPeriod = 0x5F, - MaxCsmaBackoffs = 0x4E, - MinBe = 0x4F, - PanCoordinator = 0x10, - AssocPanCoordinator = 0x11, - ExtendedAddress = 0x6F, - AclEntryDescriptor = 0x70, - AclEntryDescriptorSize = 0x71, - DefaultSecurity = 0x72, - DefaultSecurityMaterialLength = 0x73, - DefaultSecurityMaterial = 0x74, - DefaultSecuritySuite = 0x75, - SecurityMode = 0x76, - CurrentAclEntries = 0x80, - DefaultSecurityExtendedAddress = 0x81, - AssociatedPanCoordinator = 0x56, - PromiscuousMode = 0x51, +numeric_enum! { + #[repr(u8)] + #[derive(Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum MacStatus { + Success = 0x00, + Error = 0x01, + NotImplemented = 0x02, + NotSupported = 0x03, + HardwareNotSupported = 0x04, + Undefined = 0x05, + } } -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AddressMode { - NoAddress = 0x00, - Reserved = 0x01, - Short = 0x02, - Extended = 0x03, +numeric_enum! { + #[repr(u8)] + /// this enum contains all the MAC PIB Ids + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum PibId { + // PHY + CurrentChannel = 0x00, + ChannelsSupported = 0x01, + TransmitPower = 0x02, + CCAMode = 0x03, + CurrentPage = 0x04, + MaxFrameDuration = 0x05, + SHRDuration = 0x06, + SymbolsPerOctet = 0x07, + + // MAC + AckWaitDuration = 0x40, + AssociationPermit = 0x41, + AutoRequest = 0x42, + BeaconPayload = 0x45, + BeaconPayloadLength = 0x46, + BeaconOrder = 0x47, + Bsn = 0x49, + CoordExtendedAdddress = 0x4A, + CoordShortAddress = 0x4B, + Dsn = 0x4C, + MaxFrameTotalWaitTime = 0x58, + MaxFrameRetries = 0x59, + PanId = 0x50, + ResponseWaitTime = 0x5A, + RxOnWhenIdle = 0x52, + SecurityEnabled = 0x5D, + ShortAddress = 0x53, + SuperframeOrder = 0x54, + TimestampSupported = 0x5C, + TransactionPersistenceTime = 0x55, + MaxBe = 0x57, + LifsPeriod = 0x5E, + SifsPeriod = 0x5F, + MaxCsmaBackoffs = 0x4E, + MinBe = 0x4F, + PanCoordinator = 0x10, + AssocPanCoordinator = 0x11, + ExtendedAddress = 0x6F, + AclEntryDescriptor = 0x70, + AclEntryDescriptorSize = 0x71, + DefaultSecurity = 0x72, + DefaultSecurityMaterialLength = 0x73, + DefaultSecurityMaterial = 0x74, + DefaultSecuritySuite = 0x75, + SecurityMode = 0x76, + CurrentAclEntries = 0x80, + DefaultSecurityExtendedAddress = 0x81, + AssociatedPanCoordinator = 0x56, + PromiscuousMode = 0x51, + } } -pub union MacAddress { - pub short: [u8; 2], - pub extended: [u8; 8], +numeric_enum! { + #[repr(u8)] + #[derive(Default, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum AddressMode { + #[default] + NoAddress = 0x00, + Reserved = 0x01, + Short = 0x02, + Extended = 0x03, +} +} + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacAddress { + Short([u8; 2]), + Extended([u8; 8]), +} + +impl Default for MacAddress { + fn default() -> Self { + Self::Short([0, 0]) + } } pub struct GtsCharacteristics { @@ -98,13 +127,15 @@ pub struct GtsCharacteristics { /// MAC PAN Descriptor which contains the network details of the device from /// which the beacon is received +#[derive(Default, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PanDescriptor { /// PAN identifier of the coordinator - pub a_coord_pan_id: [u8; 2], + pub coord_pan_id: [u8; 2], /// Coordinator addressing mode pub coord_addr_mode: AddressMode, /// The current logical channel occupied by the network - pub logical_channel: u8, + pub logical_channel: MacChannel, /// Coordinator address pub coord_addr: MacAddress, /// The current channel page occupied by the network @@ -112,13 +143,179 @@ pub struct PanDescriptor { /// PAN coordinator is accepting GTS requests or not pub gts_permit: bool, /// Superframe specification as specified in the received beacon frame - pub a_superframe_spec: [u8; 2], + pub superframe_spec: [u8; 2], /// The time at which the beacon frame was received, in symbols - pub a_time_stamp: [u8; 4], + pub time_stamp: [u8; 4], /// The LQI at which the network beacon was received pub link_quality: u8, /// Security level purportedly used by the received beacon frame pub security_level: u8, - /// Byte Stuffing to keep 32 bit alignment - pub a_stuffing: [u8; 2], +} + +impl TryFrom<&[u8]> for PanDescriptor { + type Error = (); + + fn try_from(buf: &[u8]) -> Result { + const SIZE: usize = 24; + if buf.len() < SIZE { + return Err(()); + } + + let coord_addr_mode = AddressMode::try_from(buf[2])?; + let coord_addr = match coord_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), + AddressMode::Extended => { + MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) + } + }; + + Ok(Self { + coord_pan_id: [buf[0], buf[1]], + coord_addr_mode, + logical_channel: MacChannel::try_from(buf[3])?, + coord_addr, + channel_page: buf[12], + gts_permit: buf[13] != 0, + superframe_spec: [buf[14], buf[15]], + time_stamp: [buf[16], buf[17], buf[18], buf[19]], + link_quality: buf[20], + security_level: buf[21], + // 2 byte stuffing + }) + } +} + +numeric_enum! { + #[repr(u8)] + #[derive(Default, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + /// Building wireless applications with STM32WB series MCUs - Application note 13.10.3 + pub enum MacChannel { + Channel11 = 0x0B, + Channel12 = 0x0C, + Channel13 = 0x0D, + Channel14 = 0x0E, + Channel15 = 0x0F, + #[default] + Channel16 = 0x10, + Channel17 = 0x11, + Channel18 = 0x12, + Channel19 = 0x13, + Channel20 = 0x14, + Channel21 = 0x15, + Channel22 = 0x16, + Channel23 = 0x17, + Channel24 = 0x18, + Channel25 = 0x19, + Channel26 = 0x1A, + } +} + +#[cfg(not(feature = "defmt"))] +bitflags::bitflags! { + pub struct Capabilities: u8 { + /// 1 if the device is capabaleof becoming a PAN coordinator + const IS_COORDINATOR_CAPABLE = 0b00000001; + /// 1 if the device is an FFD, 0 if it is an RFD + const IS_FFD = 0b00000010; + /// 1 if the device is receiving power from mains, 0 if it is battery-powered + const IS_MAINS_POWERED = 0b00000100; + /// 1 if the device does not disable its receiver to conserver power during idle periods + const RECEIVER_ON_WHEN_IDLE = 0b00001000; + // 0b00010000 reserved + // 0b00100000 reserved + /// 1 if the device is capable of sending and receiving secured MAC frames + const IS_SECURE = 0b01000000; + /// 1 if the device wishes the coordinator to allocate a short address as a result of the association + const ALLOCATE_ADDRESS = 0b10000000; + } +} + +#[cfg(feature = "defmt")] +defmt::bitflags! { + pub struct Capabilities: u8 { + /// 1 if the device is capabaleof becoming a PAN coordinator + const IS_COORDINATOR_CAPABLE = 0b00000001; + /// 1 if the device is an FFD, 0 if it is an RFD + const IS_FFD = 0b00000010; + /// 1 if the device is receiving power from mains, 0 if it is battery-powered + const IS_MAINS_POWERED = 0b00000100; + /// 1 if the device does not disable its receiver to conserver power during idle periods + const RECEIVER_ON_WHEN_IDLE = 0b00001000; + // 0b00010000 reserved + // 0b00100000 reserved + /// 1 if the device is capable of sending and receiving secured MAC frames + const IS_SECURE = 0b01000000; + /// 1 if the device wishes the coordinator to allocate a short address as a result of the association + const ALLOCATE_ADDRESS = 0b10000000; + } +} + +numeric_enum! { + #[repr(u8)] + #[derive(Default)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum KeyIdMode { + #[default] + /// the key is determined implicitly from the originator and recipient(s) of the frame + Implicite = 0x00, + /// the key is determined explicitly using a 1 bytes key source and a 1 byte key index + Explicite1Byte = 0x01, + /// the key is determined explicitly using a 4 bytes key source and a 1 byte key index + Explicite4Byte = 0x02, + /// the key is determined explicitly using a 8 bytes key source and a 1 byte key index + Explicite8Byte = 0x03, + } +} + +numeric_enum! { + #[repr(u8)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum AssociationStatus { + /// Association successful + Success = 0x00, + /// PAN at capacity + PanAtCapacity = 0x01, + /// PAN access denied + PanAccessDenied = 0x02 + } +} + +numeric_enum! { + #[repr(u8)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum DisassociationReason { + /// The coordinator wishes the device to leave the PAN. + CoordRequested = 0x01, + /// The device wishes to leave the PAN. + DeviceRequested = 0x02, + } +} + +numeric_enum! { + #[repr(u8)] + #[derive(Default)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SecurityLevel { + /// MAC Unsecured Mode Security + #[default] + Unsecure = 0x00, + /// MAC ACL Mode Security + AclMode = 0x01, + /// MAC Secured Mode Security + Secured = 0x02, + } +} + +numeric_enum! { + #[repr(u8)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum ScanType { + EdScan = 0x00, + Active = 0x01, + Passive = 0x02, + Orphan = 0x03 + } } diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 4100d1ac5..18b29362b 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32_wpan::sub::mac::commands::{ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::sub::mac::typedefs::PibId; +use embassy_stm32_wpan::sub::mac::typedefs::{MacChannel, PibId}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -65,109 +65,92 @@ async fn main(spawner: Spawner) { info!("initialized mac: {}", result); info!("resetting"); - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(ResetRequest { set_default_pib: true }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting short address"); let short_address: u16 = 0x1122; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &short_address as *const _ as *const u8, pib_attribute: PibId::ShortAddress, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting association permit"); let association_permit: bool = true; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &association_permit as *const _ as *const u8, pib_attribute: PibId::AssociationPermit, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting TX power"); let transmit_power: i8 = 2; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &transmit_power as *const _ as *const u8, pib_attribute: PibId::TransmitPower, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("starting FFD device"); - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(StartRequest { - channel_number: 16, + channel_number: MacChannel::Channel16, beacon_order: 0x0F, superframe_order: 0x0F, pan_coordinator: true, battery_life_extension: false, ..Default::default() }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting RX on when idle"); let rx_on_while_idle: bool = true; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, pib_attribute: PibId::RxOnWhenIdle, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); - // info!("association request"); - // mbox.mac_subsystem - // .send_command(AssociateRequest { - // channel_number: 16, - // channel_page: 0, - // coord_addr_mode: 2, - // coord_address: MacAddress { short: [0x22, 0x11] }, - // capability_information: 0x80, - // coord_pan_id: [0xAA, 0x1A], - // security_level: 0, - - // key_id_mode: 0, - // key_index: 0, - // key_source: [0; 8], - // }) - // .await; - // info!("reading"); - // let result = mbox.mac_subsystem.read().await; - // info!("{}", result.payload()); - - // - // info!("starting ble..."); - // mbox.ble_subsystem.t_write(0x0c, &[]).await; - // - // info!("waiting for ble..."); - // let ble_event = mbox.ble_subsystem.tl_read().await; - // - // info!("ble event: {}", ble_event.payload()); + loop { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); + } info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 938fe754f..8042a3704 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -7,7 +7,9 @@ use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::sub::mac::typedefs::{AddressMode, MacAddress, PibId}; +use embassy_stm32_wpan::sub::mac::typedefs::{ + AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PibId, SecurityLevel, +}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -86,14 +88,14 @@ async fn main(spawner: Spawner) { let response = mbox .mac_subsystem .send_command(AssociateRequest { - channel_number: 16, + channel_number: MacChannel::Channel16, channel_page: 0, coord_addr_mode: AddressMode::Short, - coord_address: MacAddress { short: [0x22, 0x11] }, - capability_information: 0x80, + coord_address: MacAddress::Short([0x22, 0x11]), + capability_information: Capabilities::ALLOCATE_ADDRESS, coord_pan_id: [0xAA, 0x1A], - security_level: 0x00, - key_id_mode: 0, + security_level: SecurityLevel::Unsecure, + key_id_mode: KeyIdMode::Implicite, key_source: [0; 8], key_index: 0, }) From ff2daaff679ab79c856164d19b1bd15c7526991f Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Wed, 12 Jul 2023 16:41:35 +0200 Subject: [PATCH 1543/1575] RP: Watchdog scratch set/get with index: usize. --- embassy-rp/src/watchdog.rs | 116 +++++++++---------------------------- 1 file changed, 26 insertions(+), 90 deletions(-) diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index 7b36bb5a8..f1e986ec7 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -108,99 +108,35 @@ impl Watchdog { }) } - pub fn set_scratch0(&mut self, value: u32) { + /// Store data in scratch register + pub fn set_scratch(&mut self, index: usize, value: u32) { let watchdog = pac::WATCHDOG; - watchdog.scratch0().write(|w| { - *w = value; - }) + match index { + 0 => watchdog.scratch0().write(|w| *w = value), + 1 => watchdog.scratch1().write(|w| *w = value), + 2 => watchdog.scratch2().write(|w| *w = value), + 3 => watchdog.scratch3().write(|w| *w = value), + 4 => watchdog.scratch4().write(|w| *w = value), + 5 => watchdog.scratch5().write(|w| *w = value), + 6 => watchdog.scratch6().write(|w| *w = value), + 7 => watchdog.scratch7().write(|w| *w = value), + _ => panic!("Invalid watchdog scratch index"), + } } - pub fn get_scratch0(&mut self) -> u32 { + /// Read data from scratch register + pub fn get_scratch(&mut self, index: usize) -> u32 { let watchdog = pac::WATCHDOG; - watchdog.scratch0().read() - } - - pub fn set_scratch1(&mut self, value: u32) { - let watchdog = pac::WATCHDOG; - watchdog.scratch1().write(|w| { - *w = value; - }) - } - - pub fn get_scratch1(&mut self) -> u32 { - let watchdog = pac::WATCHDOG; - watchdog.scratch1().read() - } - - pub fn set_scratch2(&mut self, value: u32) { - let watchdog = pac::WATCHDOG; - watchdog.scratch2().write(|w| { - *w = value; - }) - } - - pub fn get_scratch2(&mut self) -> u32 { - let watchdog = pac::WATCHDOG; - watchdog.scratch2().read() - } - - pub fn set_scratch3(&mut self, value: u32) { - let watchdog = pac::WATCHDOG; - watchdog.scratch3().write(|w| { - *w = value; - }) - } - - pub fn get_scratch3(&mut self) -> u32 { - let watchdog = pac::WATCHDOG; - watchdog.scratch3().read() - } - - pub fn set_scratch4(&mut self, value: u32) { - let watchdog = pac::WATCHDOG; - watchdog.scratch4().write(|w| { - *w = value; - }) - } - - pub fn get_scratch4(&mut self) -> u32 { - let watchdog = pac::WATCHDOG; - watchdog.scratch4().read() - } - - pub fn set_scratch5(&mut self, value: u32) { - let watchdog = pac::WATCHDOG; - watchdog.scratch5().write(|w| { - *w = value; - }) - } - - pub fn get_scratch5(&mut self) -> u32 { - let watchdog = pac::WATCHDOG; - watchdog.scratch5().read() - } - - pub fn set_scratch6(&mut self, value: u32) { - let watchdog = pac::WATCHDOG; - watchdog.scratch6().write(|w| { - *w = value; - }) - } - - pub fn get_scratch6(&mut self) -> u32 { - let watchdog = pac::WATCHDOG; - watchdog.scratch6().read() - } - - pub fn set_scratch7(&mut self, value: u32) { - let watchdog = pac::WATCHDOG; - watchdog.scratch7().write(|w| { - *w = value; - }) - } - - pub fn get_scratch7(&mut self) -> u32 { - let watchdog = pac::WATCHDOG; - watchdog.scratch7().read() + match index { + 0 => watchdog.scratch0().read(), + 1 => watchdog.scratch1().read(), + 2 => watchdog.scratch2().read(), + 3 => watchdog.scratch3().read(), + 4 => watchdog.scratch4().read(), + 5 => watchdog.scratch5().read(), + 6 => watchdog.scratch6().read(), + 7 => watchdog.scratch7().read(), + _ => panic!("Invalid watchdog scratch index"), + } } } From a2501bd5c1004362368d962b206b6de8f4962837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 12 Jul 2023 16:52:52 +0200 Subject: [PATCH 1544/1575] Allow clearing finished task from timer queue --- embassy-executor/src/raw/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index bd0cff26b..f3760f589 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -165,6 +165,9 @@ impl TaskStorage { Poll::Ready(_) => { this.future.drop_in_place(); this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel); + + #[cfg(feature = "integrated-timers")] + this.raw.expires_at.set(Instant::MAX); } Poll::Pending => {} } From eccd2ecebf01753e70705a6ca1e21bc83b2c204c Mon Sep 17 00:00:00 2001 From: goueslati Date: Wed, 12 Jul 2023 16:49:37 +0100 Subject: [PATCH 1545/1575] change MacAddress to a union instead of an enum --- embassy-stm32-wpan/src/sub/mac/commands.rs | 20 +++- embassy-stm32-wpan/src/sub/mac/indications.rs | 70 ++++++++------ embassy-stm32-wpan/src/sub/mac/mod.rs | 4 +- embassy-stm32-wpan/src/sub/mac/responses.rs | 17 ++-- embassy-stm32-wpan/src/sub/mac/typedefs.rs | 43 ++++++--- examples/stm32wb/src/bin/mac_ffd.rs | 18 ++-- examples/stm32wb/src/bin/mac_rfd.rs | 92 ++++++++++++------- 7 files changed, 168 insertions(+), 96 deletions(-) diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/sub/mac/commands.rs index d8a4e3ee1..7ccf10026 100644 --- a/embassy-stm32-wpan/src/sub/mac/commands.rs +++ b/embassy-stm32-wpan/src/sub/mac/commands.rs @@ -9,12 +9,13 @@ pub trait MacCommand { const SIZE: usize; fn copy_into_slice(&self, buf: &mut [u8]) { - unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, 8) }; + unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, Self::SIZE) }; } } /// MLME ASSOCIATE Request used to request an association #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateRequest { /// the logical channel on which to attempt association pub channel_number: MacChannel, @@ -45,6 +46,7 @@ impl MacCommand for AssociateRequest { /// MLME DISASSOCIATE Request sed to request a disassociation #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateRequest { /// device addressing mode used pub device_addr_mode: AddressMode, @@ -73,6 +75,7 @@ impl MacCommand for DisassociateRequest { /// MLME GET Request used to request a PIB value #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetRequest { /// the name of the PIB attribute to read pub pib_attribute: PibId, @@ -85,6 +88,7 @@ impl MacCommand for GetRequest { /// MLME GTS Request used to request and maintain GTSs #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsRequest { /// the characteristics of the GTS pub characteristics: GtsCharacteristics, @@ -104,6 +108,7 @@ impl MacCommand for GtsRequest { } #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetRequest { /// MAC PIB attributes are set to their default values or not during reset pub set_default_pib: bool, @@ -117,6 +122,7 @@ impl MacCommand for ResetRequest { /// MLME RX ENABLE Request used to request that the receiver is either enabled /// for a finite period of time or disabled #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableRequest { /// the request operation can be deferred or not pub defer_permit: bool, @@ -148,6 +154,7 @@ impl MacCommand for RxEnableRequest { /// MLME SCAN Request used to initiate a channel scan over a given list of channels #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanRequest { /// the type of scan to be performed pub scan_type: ScanType, @@ -174,6 +181,7 @@ impl MacCommand for ScanRequest { /// MLME SET Request used to attempt to write the given value to the indicated PIB attribute #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SetRequest { /// the pointer to the value of the PIB attribute to set pub pib_attribute_ptr: *const u8, @@ -190,6 +198,7 @@ impl MacCommand for SetRequest { /// configuration #[derive(Default)] #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartRequest { /// PAN indentifier to used by the device pub pan_id: [u8; 2], @@ -233,6 +242,7 @@ impl MacCommand for StartRequest { /// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if /// specified, tracking its beacons #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncRequest { /// the channel number on which to attempt coordinator synchronization pub channel_number: MacChannel, @@ -252,6 +262,7 @@ impl MacCommand for SyncRequest { /// MLME POLL Request propmts the device to request data from the coordinator #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollRequest { /// addressing mode of the coordinator pub coord_addr_mode: AddressMode, @@ -277,6 +288,7 @@ impl MacCommand for PollRequest { /// MLME DPS Request allows the next higher layer to request that the PHY utilize a /// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsRequest { /// the index value for the transmitter tx_dps_index: u8, @@ -295,6 +307,7 @@ impl MacCommand for DpsRequest { /// MLME SOUNDING request primitive which is used by the next higher layer to request that /// the PHY respond with channel sounding information #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingRequest; impl MacCommand for SoundingRequest { @@ -305,6 +318,7 @@ impl MacCommand for SoundingRequest { /// MLME CALIBRATE request primitive which used to obtain the results of a ranging /// calibration request from an RDEV #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CalibrateRequest; impl MacCommand for CalibrateRequest { @@ -314,6 +328,7 @@ impl MacCommand for CalibrateRequest { /// MCPS DATA Request used for MAC data related requests from the application #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataRequest { /// the handle assocated with the MSDU to be transmitted pub msdu_ptr: *const u8, @@ -362,6 +377,7 @@ impl MacCommand for DataRequest { /// for MCPS PURGE Request used to purge an MSDU from the transaction queue #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PurgeRequest { /// the handle associated with the MSDU to be purged from the transaction /// queue @@ -375,6 +391,7 @@ impl MacCommand for PurgeRequest { /// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateResponse { /// extended address of the device requesting association pub device_address: [u8; 8], @@ -400,6 +417,7 @@ impl MacCommand for AssociateResponse { /// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OrphanResponse { /// extended address of the orphaned device pub orphan_address: [u8; 8], diff --git a/embassy-stm32-wpan/src/sub/mac/indications.rs b/embassy-stm32-wpan/src/sub/mac/indications.rs index dc5ae4c44..4695f24ef 100644 --- a/embassy-stm32-wpan/src/sub/mac/indications.rs +++ b/embassy-stm32-wpan/src/sub/mac/indications.rs @@ -141,21 +141,25 @@ impl ParseableMacEvent for CommStatusIndication { let dst_addr_mode = AddressMode::try_from(buf[3])?; let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress::Short([0, 0]), - AddressMode::Reserved => MacAddress::Short([0, 0]), - AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), - AddressMode::Extended => { - MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) - } + AddressMode::NoAddress => MacAddress { short: [0, 0] }, + AddressMode::Reserved => MacAddress { short: [0, 0] }, + AddressMode::Short => MacAddress { + short: [buf[4], buf[5]], + }, + AddressMode::Extended => MacAddress { + extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], + }, }; let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress::Short([0, 0]), - AddressMode::Reserved => MacAddress::Short([0, 0]), - AddressMode::Short => MacAddress::Short([buf[12], buf[13]]), - AddressMode::Extended => { - MacAddress::Extended([buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]]) - } + AddressMode::NoAddress => MacAddress { short: [0, 0] }, + AddressMode::Reserved => MacAddress { short: [0, 0] }, + AddressMode::Short => MacAddress { + short: [buf[12], buf[13]], + }, + AddressMode::Extended => MacAddress { + extended: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], + }, }; Ok(Self { @@ -358,22 +362,26 @@ impl ParseableMacEvent for DataIndication { let src_addr_mode = AddressMode::try_from(buf[4])?; let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress::Short([0, 0]), - AddressMode::Reserved => MacAddress::Short([0, 0]), - AddressMode::Short => MacAddress::Short([buf[7], buf[8]]), - AddressMode::Extended => { - MacAddress::Extended([buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14]]) - } + AddressMode::NoAddress => MacAddress { short: [0, 0] }, + AddressMode::Reserved => MacAddress { short: [0, 0] }, + AddressMode::Short => MacAddress { + short: [buf[7], buf[8]], + }, + AddressMode::Extended => MacAddress { + extended: [buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14]], + }, }; let dst_addr_mode = AddressMode::try_from(buf[15])?; let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress::Short([0, 0]), - AddressMode::Reserved => MacAddress::Short([0, 0]), - AddressMode::Short => MacAddress::Short([buf[18], buf[19]]), - AddressMode::Extended => { - MacAddress::Extended([buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25]]) - } + AddressMode::NoAddress => MacAddress { short: [0, 0] }, + AddressMode::Reserved => MacAddress { short: [0, 0] }, + AddressMode::Short => MacAddress { + short: [buf[18], buf[19]], + }, + AddressMode::Extended => MacAddress { + extended: [buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25]], + }, }; Ok(Self { @@ -424,12 +432,14 @@ impl ParseableMacEvent for PollIndication { let addr_mode = AddressMode::try_from(buf[0])?; let request_address = match addr_mode { - AddressMode::NoAddress => MacAddress::Short([0, 0]), - AddressMode::Reserved => MacAddress::Short([0, 0]), - AddressMode::Short => MacAddress::Short([buf[1], buf[2]]), - AddressMode::Extended => { - MacAddress::Extended([buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]]) - } + AddressMode::NoAddress => MacAddress { short: [0, 0] }, + AddressMode::Reserved => MacAddress { short: [0, 0] }, + AddressMode::Short => MacAddress { + short: [buf[1], buf[2]], + }, + AddressMode::Extended => MacAddress { + extended: [buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]], + }, }; Ok(Self { diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs index 0524f135a..26358bf81 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -91,13 +91,15 @@ impl Mac { .await; } - pub async fn send_command(&self, cmd: T) -> Result<(), MacError> + pub async fn send_command(&self, cmd: &T) -> Result<(), MacError> where T: MacCommand, { let mut payload = [0u8; MAX_PACKET_SIZE]; cmd.copy_into_slice(&mut payload); + debug!("sending {}", &payload[..T::SIZE]); + let response = self .tl_write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]) .await; diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index ce2ca2fb2..37271ec28 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -27,6 +27,8 @@ impl ParseableMacEvent for AssociateConfirm { const SIZE: usize = 16; fn try_parse(buf: &[u8]) -> Result { + debug!("{}", buf); + Self::validate(buf)?; Ok(Self { @@ -61,12 +63,14 @@ impl ParseableMacEvent for DisassociateConfirm { let device_addr_mode = AddressMode::try_from(buf[1])?; let device_address = match device_addr_mode { - AddressMode::NoAddress => MacAddress::Short([0, 0]), - AddressMode::Reserved => MacAddress::Short([0, 0]), - AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), - AddressMode::Extended => { - MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) - } + AddressMode::NoAddress => MacAddress { short: [0, 0] }, + AddressMode::Reserved => MacAddress { short: [0, 0] }, + AddressMode::Short => MacAddress { + short: [buf[4], buf[5]], + }, + AddressMode::Extended => MacAddress { + extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], + }, }; Ok(Self { @@ -238,7 +242,6 @@ impl ParseableMacEvent for StartConfirm { fn try_parse(buf: &[u8]) -> Result { Self::validate(buf)?; - debug!("{:#x}", buf); Ok(Self { status: MacStatus::try_from(buf[0])?, diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/sub/mac/typedefs.rs index fe79b3a79..1a4c30cbc 100644 --- a/embassy-stm32-wpan/src/sub/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/sub/mac/typedefs.rs @@ -109,18 +109,32 @@ numeric_enum! { } #[derive(Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MacAddress { - Short([u8; 2]), - Extended([u8; 8]), +pub union MacAddress { + pub short: [u8; 2], + pub extended: [u8; 8], +} + +#[cfg(feature = "defmt")] +impl defmt::Format for MacAddress { + fn format(&self, fmt: defmt::Formatter) { + unsafe { + defmt::write!( + fmt, + "MacAddress {{ short: {}, extended: {} }}", + self.short, + self.extended + ) + } + } } impl Default for MacAddress { fn default() -> Self { - Self::Short([0, 0]) + Self { short: [0, 0] } } } +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsCharacteristics { pub fields: u8, } @@ -163,12 +177,14 @@ impl TryFrom<&[u8]> for PanDescriptor { let coord_addr_mode = AddressMode::try_from(buf[2])?; let coord_addr = match coord_addr_mode { - AddressMode::NoAddress => MacAddress::Short([0, 0]), - AddressMode::Reserved => MacAddress::Short([0, 0]), - AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), - AddressMode::Extended => { - MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) - } + AddressMode::NoAddress => MacAddress { short: [0, 0] }, + AddressMode::Reserved => MacAddress { short: [0, 0] }, + AddressMode::Short => MacAddress { + short: [buf[4], buf[5]], + }, + AddressMode::Extended => MacAddress { + extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], + }, }; Ok(Self { @@ -255,7 +271,7 @@ defmt::bitflags! { numeric_enum! { #[repr(u8)] - #[derive(Default)] + #[derive(Default, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum KeyIdMode { #[default] @@ -285,6 +301,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] + #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DisassociationReason { /// The coordinator wishes the device to leave the PAN. @@ -296,7 +313,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Default)] + #[derive(Default, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SecurityLevel { /// MAC Unsecured Mode Security diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 18b29362b..4e2578a21 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -66,7 +66,7 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { set_default_pib: true }) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; @@ -75,7 +75,7 @@ async fn main(spawner: Spawner) { info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; mbox.mac_subsystem - .send_command(SetRequest { + .send_command(&SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, }) @@ -87,7 +87,7 @@ async fn main(spawner: Spawner) { info!("setting short address"); let short_address: u16 = 0x1122; mbox.mac_subsystem - .send_command(SetRequest { + .send_command(&SetRequest { pib_attribute_ptr: &short_address as *const _ as *const u8, pib_attribute: PibId::ShortAddress, }) @@ -99,7 +99,7 @@ async fn main(spawner: Spawner) { info!("setting association permit"); let association_permit: bool = true; mbox.mac_subsystem - .send_command(SetRequest { + .send_command(&SetRequest { pib_attribute_ptr: &association_permit as *const _ as *const u8, pib_attribute: PibId::AssociationPermit, }) @@ -111,7 +111,7 @@ async fn main(spawner: Spawner) { info!("setting TX power"); let transmit_power: i8 = 2; mbox.mac_subsystem - .send_command(SetRequest { + .send_command(&SetRequest { pib_attribute_ptr: &transmit_power as *const _ as *const u8, pib_attribute: PibId::TransmitPower, }) @@ -122,7 +122,8 @@ async fn main(spawner: Spawner) { info!("starting FFD device"); mbox.mac_subsystem - .send_command(StartRequest { + .send_command(&StartRequest { + pan_id: [0xAA, 0x1A], channel_number: MacChannel::Channel16, beacon_order: 0x0F, superframe_order: 0x0F, @@ -138,7 +139,7 @@ async fn main(spawner: Spawner) { info!("setting RX on when idle"); let rx_on_while_idle: bool = true; mbox.mac_subsystem - .send_command(SetRequest { + .send_command(&SetRequest { pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, pib_attribute: PibId::RxOnWhenIdle, }) @@ -151,7 +152,4 @@ async fn main(spawner: Spawner) { let evt = mbox.mac_subsystem.read().await; defmt::info!("{:#x}", evt); } - - info!("Test OK"); - cortex_m::asm::bkpt(); } diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 8042a3704..e5f8d54c9 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,7 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; +use embassy_stm32_wpan::sub::mac::event::MacEvent; use embassy_stm32_wpan::sub::mac::typedefs::{ AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PibId, SecurityLevel, }; @@ -67,52 +68,75 @@ async fn main(spawner: Spawner) { info!("initialized mac: {}", result); info!("resetting"); - let response = mbox - .mac_subsystem - .send_command(ResetRequest { set_default_pib: true }) - .await; - info!("{}", response); + mbox.mac_subsystem + .send_command(&ResetRequest { set_default_pib: true }) + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; - let response = mbox - .mac_subsystem - .send_command(SetRequest { + mbox.mac_subsystem + .send_command(&SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + + info!("getting extended address"); + mbox.mac_subsystem + .send_command(&GetRequest { + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + + if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } + } info!("assocation request"); - let response = mbox - .mac_subsystem - .send_command(AssociateRequest { - channel_number: MacChannel::Channel16, - channel_page: 0, - coord_addr_mode: AddressMode::Short, - coord_address: MacAddress::Short([0x22, 0x11]), - capability_information: Capabilities::ALLOCATE_ADDRESS, - coord_pan_id: [0xAA, 0x1A], - security_level: SecurityLevel::Unsecure, - key_id_mode: KeyIdMode::Implicite, - key_source: [0; 8], - key_index: 0, - }) - .await; - info!("{}", response); + let a = AssociateRequest { + channel_number: MacChannel::Channel16, + channel_page: 0, + coord_addr_mode: AddressMode::Short, + coord_address: MacAddress { short: [34, 17] }, + capability_information: Capabilities::ALLOCATE_ADDRESS, + coord_pan_id: [0xAA, 0x1A], + security_level: SecurityLevel::Unsecure, + key_id_mode: KeyIdMode::Implicite, + key_source: [0; 8], + key_index: 152, + }; + info!("{}", a); + mbox.mac_subsystem.send_command(&a).await.unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); info!("setting short address"); let short: u64 = 0xACDE480000000002; - let response = mbox - .mac_subsystem - .send_command(SetRequest { + mbox.mac_subsystem + .send_command(&SetRequest { pib_attribute_ptr: &short as *const _ as *const u8, pib_attribute: PibId::ShortAddress, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); - info!("Test OK"); - cortex_m::asm::bkpt(); + loop { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + } } From dff9bd9711205fd4cd5a91384072ab6aa2335d18 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 18:30:43 +0200 Subject: [PATCH 1546/1575] Remove trivial to remove uses of atomic-polyfill. --- embassy-stm32/src/dma/dma.rs | 3 +-- embassy-stm32/src/flash/asynch.rs | 2 +- embassy-stm32/src/flash/common.rs | 2 +- embassy-stm32/src/flash/f0.rs | 3 +-- embassy-stm32/src/flash/f3.rs | 3 +-- embassy-stm32/src/flash/f4.rs | 3 +-- embassy-stm32/src/flash/h7.rs | 3 +-- embassy-stm32/src/flash/l.rs | 3 +-- embassy-stm32/src/ipcc.rs | 3 +-- embassy-stm32/src/usb_otg/usb.rs | 2 +- embassy-time/src/driver_std.rs | 2 +- embassy-time/src/driver_wasm.rs | 2 +- 12 files changed, 12 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 8abe541d3..58d438af8 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,10 +1,9 @@ use core::future::Future; use core::marker::PhantomData; use core::pin::Pin; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{fence, AtomicUsize, Ordering}; use core::task::{Context, Poll, Waker}; -use atomic_polyfill::AtomicUsize; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 70a5ded62..f175349cd 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; +use core::sync::atomic::{fence, Ordering}; -use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index c6cdc574b..2a374733d 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; +use core::sync::atomic::{fence, Ordering}; -use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 02bd4cc1f..ec8343e7c 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index b093a7837..40335d643 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 242d99278..4cb39e033 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,8 +1,7 @@ use core::convert::TryInto; use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{fence, AtomicBool, Ordering}; -use atomic_polyfill::AtomicBool; use embassy_sync::waitqueue::AtomicWaker; use pac::flash::regs::Sr; use pac::FLASH_SIZE; diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 9baf059ee..bf17b5b18 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index deefd05ed..243c8b51d 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,6 +1,5 @@ use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 37f840c73..a24cba9f0 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,8 +1,7 @@ use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use atomic_polyfill::{compiler_fence, Ordering}; - use self::sealed::Instance; use crate::interrupt; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index d0284746c..fd0e22adf 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; use core::marker::PhantomData; +use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use core::task::Poll; -use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; use embassy_hal_common::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 9f8c57b5c..32db47a37 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -1,10 +1,10 @@ +use core::sync::atomic::{AtomicU8, Ordering}; use std::cell::{RefCell, UnsafeCell}; use std::mem::MaybeUninit; use std::sync::{Condvar, Mutex, Once}; use std::time::{Duration as StdDuration, Instant as StdInstant}; use std::{mem, ptr, thread}; -use atomic_polyfill::{AtomicU8, Ordering}; use critical_section::Mutex as CsMutex; use crate::driver::{AlarmHandle, Driver}; diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index 63d049897..0f672dc75 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -1,9 +1,9 @@ +use core::sync::atomic::{AtomicU8, Ordering}; use std::cell::UnsafeCell; use std::mem::MaybeUninit; use std::ptr; use std::sync::{Mutex, Once}; -use atomic_polyfill::{AtomicU8, Ordering}; use wasm_bindgen::prelude::*; use wasm_timer::Instant as StdInstant; From 588c0479f5dacd21e8654463eb3ca3d847d5dcd9 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Thu, 13 Jul 2023 11:16:11 +0200 Subject: [PATCH 1547/1575] Add descriptions to all RP2040 examples. Some need hardware that was not specified. --- examples/rp/src/bin/adc.rs | 5 ++++- examples/rp/src/bin/blinky.rs | 4 ++++ examples/rp/src/bin/button.rs | 4 ++++ examples/rp/src/bin/flash.rs | 2 ++ examples/rp/src/bin/gpio_async.rs | 6 ++++-- examples/rp/src/bin/gpout.rs | 4 ++++ examples/rp/src/bin/i2c_async.rs | 5 +++++ examples/rp/src/bin/i2c_blocking.rs | 5 +++++ examples/rp/src/bin/lora_lorawan.rs | 1 + examples/rp/src/bin/lora_p2p_receive.rs | 1 + examples/rp/src/bin/lora_p2p_send.rs | 1 + examples/rp/src/bin/lora_p2p_send_multicore.rs | 1 + examples/rp/src/bin/multicore.rs | 4 ++++ examples/rp/src/bin/pio_async.rs | 11 ++++++++++- examples/rp/src/bin/pio_dma.rs | 2 ++ examples/rp/src/bin/pio_hd44780.rs | 3 +++ examples/rp/src/bin/pio_ws2812.rs | 3 +++ examples/rp/src/bin/pwm.rs | 4 ++++ examples/rp/src/bin/rtc.rs | 2 ++ examples/rp/src/bin/spi.rs | 4 ++++ examples/rp/src/bin/spi_async.rs | 3 +++ examples/rp/src/bin/spi_display.rs | 5 +++++ examples/rp/src/bin/uart.rs | 6 ++++++ examples/rp/src/bin/uart_buffered_split.rs | 6 ++++++ examples/rp/src/bin/uart_unidir.rs | 6 +++++- examples/rp/src/bin/usb_ethernet.rs | 4 ++++ examples/rp/src/bin/usb_logger.rs | 4 ++++ examples/rp/src/bin/usb_serial.rs | 4 ++++ examples/rp/src/bin/watchdog.rs | 4 ++++ examples/rp/src/bin/wifi_ap_tcp_server.rs | 3 +++ examples/rp/src/bin/wifi_blinky.rs | 4 ++++ examples/rp/src/bin/wifi_scan.rs | 3 +++ examples/rp/src/bin/wifi_tcp_server.rs | 3 +++ 33 files changed, 122 insertions(+), 5 deletions(-) diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 65069cde1..c0cbe0172 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -1,3 +1,6 @@ +//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28. +//! It also reads the temperature sensor in the chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -38,5 +41,5 @@ async fn main(_spawner: Spawner) { fn convert_to_celsius(raw_temp: u16) -> f32 { // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet - 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32 + 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 } diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index 7aa36a19f..295b000f3 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs @@ -1,3 +1,7 @@ +//! This example test the RP Pico on board LED. +//! +//! It does not work with the RP Pico W board. See wifi_blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index 0d246c093..d7aa89410 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs @@ -1,3 +1,7 @@ +//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board. +//! +//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 19076150c..4c4982acc 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -1,3 +1,5 @@ +//! This example test the flash connected to the RP2040 chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/gpio_async.rs b/examples/rp/src/bin/gpio_async.rs index 52d13a9d5..bf58044d5 100644 --- a/examples/rp/src/bin/gpio_async.rs +++ b/examples/rp/src/bin/gpio_async.rs @@ -1,3 +1,7 @@ +//! This example shows how async gpio can be used with a RP2040. +//! +//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -9,8 +13,6 @@ use embassy_time::{Duration, Timer}; use gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_probe as _}; -/// This example shows how async gpio can be used with a RP2040. -/// /// It requires an external signal to be manually triggered on PIN 16. For /// example, this could be accomplished using an external power source with a /// button so that it is possible to toggle the signal from low to high. diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs index 64461fc5f..0a3b5fa98 100644 --- a/examples/rp/src/bin/gpout.rs +++ b/examples/rp/src/bin/gpout.rs @@ -1,3 +1,7 @@ +//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin. +//! +//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs index cf3cf742c..93224bc43 100644 --- a/examples/rp/src/bin/i2c_async.rs +++ b/examples/rp/src/bin/i2c_async.rs @@ -1,3 +1,8 @@ +//! This example shows how to communicate asynchronous using i2c with external chips. +//! +//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. +//! (https://www.microchip.com/en-us/product/mcp23017) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs index 7623e33c8..1c8c2039d 100644 --- a/examples/rp/src/bin/i2c_blocking.rs +++ b/examples/rp/src/bin/i2c_blocking.rs @@ -1,3 +1,8 @@ +//! This example shows how to communicate using i2c with external chips. +//! +//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. +//! (https://www.microchip.com/en-us/product/mcp23017) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/lora_lorawan.rs b/examples/rp/src/bin/lora_lorawan.rs index a9c84bf95..d631fafa1 100644 --- a/examples/rp/src/bin/lora_lorawan.rs +++ b/examples/rp/src/bin/lora_lorawan.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LoRaWAN join functionality. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/lora_p2p_receive.rs b/examples/rp/src/bin/lora_p2p_receive.rs index 250419202..396d669de 100644 --- a/examples/rp/src/bin/lora_p2p_receive.rs +++ b/examples/rp/src/bin/lora_p2p_receive.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/lora_p2p_send.rs b/examples/rp/src/bin/lora_p2p_send.rs index 3a0544b17..a0f70fa5c 100644 --- a/examples/rp/src/bin/lora_p2p_send.rs +++ b/examples/rp/src/bin/lora_p2p_send.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LORA P2P send functionality. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs index eef2f7a53..89a62818d 100644 --- a/examples/rp/src/bin/lora_p2p_send_multicore.rs +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 57278dd6c..893b724bf 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -1,3 +1,7 @@ +//! This example shows how to send messages between the two cores in the RP2040 chip. +//! +//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 69034c92a..c001d6440 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -1,3 +1,5 @@ +//! This example shows powerful PIO module in the RP2040 chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -54,7 +56,14 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, // Setupm sm1 // Read 0b10101 repeatedly until ISR is full - let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); + let prg = pio_proc::pio_asm!( + // + ".origin 8", + "set x, 0x15", + ".wrap_target", + "in x, 5 [31]", + ".wrap", + ); let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 80c963556..9ab72e1f3 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -1,3 +1,5 @@ +//! This example shows powerful PIO module in the RP2040 chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 0a4514a66..8aedd24b6 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -1,3 +1,6 @@ +//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display. +//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 4a111e7aa..3de2bd48d 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -1,3 +1,6 @@ +//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules. +//! See (https://www.sparkfun.com/categories/tags/ws2812) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 2b3d5d97a..9d919287c 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -1,3 +1,7 @@ +//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. +//! +//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs index d569f598f..15aa8243f 100644 --- a/examples/rp/src/bin/rtc.rs +++ b/examples/rp/src/bin/rtc.rs @@ -1,3 +1,5 @@ +//! This example shows how to use RTC (Real Time Clock) in the RP2040 chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index a830a17a2..602348f7a 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs @@ -1,3 +1,7 @@ +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! +//! Example for resistive touch sensor in Waveshare Pico-ResTouch + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs index 671a9caaf..328074e8b 100644 --- a/examples/rp/src/bin/spi_async.rs +++ b/examples/rp/src/bin/spi_async.rs @@ -1,3 +1,6 @@ +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 2fd201595..26c258e1c 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -1,3 +1,8 @@ +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! +//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch +//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/uart.rs b/examples/rp/src/bin/uart.rs index 05177a6b4..451c3c396 100644 --- a/examples/rp/src/bin/uart.rs +++ b/examples/rp/src/bin/uart.rs @@ -1,3 +1,9 @@ +//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. +//! +//! No specific hardware is specified in this example. Only output on pin 0 is tested. +//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used +//! with its UART port. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index 9df99bd58..735201718 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -1,3 +1,9 @@ +//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. +//! +//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back. +//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used +//! with its UART port. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index c0943a1b8..c1515a911 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs @@ -1,5 +1,9 @@ -//! test TX-only and RX-only UARTs. You need to connect GPIO0 to GPIO5 for +//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. +//! +//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for //! this to work +//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used +//! with its UART port. #![no_std] #![no_main] diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 91d1ec8e7..0a08f667e 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -1,3 +1,7 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This is a CDC-NCM class implementation, aka Ethernet over USB. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs index 7c90d0ca3..9c5e6897d 100644 --- a/examples/rp/src/bin/usb_logger.rs +++ b/examples/rp/src/bin/usb_logger.rs @@ -1,3 +1,7 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates the possibility to send log::info/warn/error/debug! to USB serial port. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index ca728536c..164e2052d 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -1,3 +1,7 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates a USB serial port that echos. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs index ece5cfe38..fe5eaf926 100644 --- a/examples/rp/src/bin/watchdog.rs +++ b/examples/rp/src/bin/watchdog.rs @@ -1,3 +1,7 @@ +//! This example shows how to use Watchdog in the RP2040 chip. +//! +//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 3e41f83be..e3e393445 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -1,3 +1,6 @@ +//! This example uses the RP Pico W board Wifi chip (cyw43). +//! Creates an Access point Wifi network and creates a TCP endpoint on port 1234. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index 6eb207af6..33d43788c 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -1,3 +1,7 @@ +//! This example test the RP Pico W on board LED. +//! +//! It does not work with the RP Pico board. See blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index aef18aa24..743fab617 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -1,3 +1,6 @@ +//! This example uses the RP Pico W board Wifi chip (cyw43). +//! Scans Wifi for ssid names. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 4fce74a66..0223a3636 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -1,3 +1,6 @@ +//! This example uses the RP Pico W board Wifi chip (cyw43). +//! Connects to specified Wifi network and creates a TCP endpoint on port 1234. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] From 3f0c8bafb060fdf81a677f0ec37d4db11e732266 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 13 Jul 2023 15:20:50 +0100 Subject: [PATCH 1548/1575] make it work, disgustingly --- embassy-stm32-wpan/src/sub/mac/commands.rs | 42 +++++++++++++++---- embassy-stm32-wpan/src/sub/mac/consts.rs | 2 +- embassy-stm32-wpan/src/sub/mac/indications.rs | 27 ++++++------ embassy-stm32-wpan/src/sub/mac/opcodes.rs | 2 + embassy-stm32-wpan/src/sub/mac/responses.rs | 7 ++-- embassy-stm32-wpan/src/sub/mac/typedefs.rs | 26 ++++++++---- examples/stm32wb/src/bin/mac_ffd.rs | 30 +++++++++++-- examples/stm32wb/src/bin/mac_rfd.rs | 38 ++++++++++++++--- 8 files changed, 133 insertions(+), 41 deletions(-) diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/sub/mac/commands.rs index 7ccf10026..8cfa0a054 100644 --- a/embassy-stm32-wpan/src/sub/mac/commands.rs +++ b/embassy-stm32-wpan/src/sub/mac/commands.rs @@ -1,7 +1,7 @@ use super::opcodes::OpcodeM4ToM0; use super::typedefs::{ - AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, PibId, - ScanType, SecurityLevel, + AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, + PanId, PibId, ScanType, SecurityLevel, }; pub trait MacCommand { @@ -26,7 +26,7 @@ pub struct AssociateRequest { /// operational capabilities of the associating device pub capability_information: Capabilities, /// the identifier of the PAN with which to associate - pub coord_pan_id: [u8; 2], + pub coord_pan_id: PanId, /// the security level to be used pub security_level: SecurityLevel, /// the mode used to identify the key to be used @@ -51,7 +51,7 @@ pub struct DisassociateRequest { /// device addressing mode used pub device_addr_mode: AddressMode, /// the identifier of the PAN of the device - pub device_pan_id: [u8; 2], + pub device_pan_id: PanId, /// the reason for the disassociation pub disassociation_reason: DisassociationReason, /// device address @@ -201,7 +201,7 @@ impl MacCommand for SetRequest { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartRequest { /// PAN indentifier to used by the device - pub pan_id: [u8; 2], + pub pan_id: PanId, /// logical channel on which to begin pub channel_number: MacChannel, /// channel page on which to begin @@ -277,7 +277,7 @@ pub struct PollRequest { /// originator of the key to be used pub key_source: [u8; 8], /// PAN identifier of the coordinator - pub coord_pan_id: [u8; 2], + pub coord_pan_id: PanId, } impl MacCommand for PollRequest { @@ -337,7 +337,7 @@ pub struct DataRequest { /// destination addressing mode used pub dst_addr_mode: AddressMode, /// destination PAN Id - pub dst_pan_id: [u8; 2], + pub dst_pan_id: PanId, /// destination address pub dst_address: MacAddress, /// the number of octets contained in the MSDU @@ -370,6 +370,31 @@ pub struct DataRequest { pub datrate: u8, } +impl Default for DataRequest { + fn default() -> Self { + Self { + msdu_ptr: 0 as *const u8, + src_addr_mode: AddressMode::NoAddress, + dst_addr_mode: AddressMode::NoAddress, + dst_pan_id: PanId([0, 0]), + dst_address: MacAddress { short: [0, 0] }, + msdu_length: 0, + msdu_handle: 0, + ack_tx: 0, + gts_tx: false, + indirect_tx: 0, + security_level: SecurityLevel::Unsecure, + key_id_mode: KeyIdMode::Implicite, + key_index: 0, + key_source: [0u8; 8], + uwbprf: 0, + ranging: 0, + uwb_preamble_symbol_repetitions: 0, + datrate: 0, + } + } +} + impl MacCommand for DataRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq; const SIZE: usize = 40; @@ -391,6 +416,7 @@ impl MacCommand for PurgeRequest { /// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateResponse { /// extended address of the device requesting association @@ -399,7 +425,7 @@ pub struct AssociateResponse { /// association pub assoc_short_address: [u8; 2], /// status of the association attempt - pub status: u8, + pub status: MacStatus, /// security level to be used pub security_level: SecurityLevel, /// the originator of the key to be used diff --git a/embassy-stm32-wpan/src/sub/mac/consts.rs b/embassy-stm32-wpan/src/sub/mac/consts.rs index 892d533b4..56903d980 100644 --- a/embassy-stm32-wpan/src/sub/mac/consts.rs +++ b/embassy-stm32-wpan/src/sub/mac/consts.rs @@ -1,4 +1,4 @@ -pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16; pub const MAX_PAN_DESC_SUPPORTED: usize = 6; pub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6; pub const MAX_PENDING_ADDRESS: usize = 7; +pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16; diff --git a/embassy-stm32-wpan/src/sub/mac/indications.rs b/embassy-stm32-wpan/src/sub/mac/indications.rs index 4695f24ef..b67f0a686 100644 --- a/embassy-stm32-wpan/src/sub/mac/indications.rs +++ b/embassy-stm32-wpan/src/sub/mac/indications.rs @@ -3,7 +3,7 @@ use super::event::ParseableMacEvent; use super::helpers::to_u32; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, - SecurityLevel, + PanId, SecurityLevel, }; /// MLME ASSOCIATE Indication which will be used by the MAC @@ -110,7 +110,7 @@ impl ParseableMacEvent for BeaconNotifyIndication { pub struct CommStatusIndication { /// The 16-bit PAN identifier of the device from which the frame /// was received or to which the frame was being sent - pub pan_id: [u8; 2], + pub pan_id: PanId, /// Source addressing mode pub src_addr_mode: AddressMode, /// Destination addressing mode @@ -163,7 +163,7 @@ impl ParseableMacEvent for CommStatusIndication { }; Ok(Self { - pan_id: [buf[0], buf[1]], + pan_id: PanId([buf[0], buf[1]]), src_addr_mode, dst_addr_mode, src_address, @@ -251,7 +251,7 @@ impl ParseableMacEvent for OrphanIndication { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncLossIndication { /// The PAN identifier with which the device lost synchronization or to which it was realigned - pub pan_id: [u8; 2], + pub pan_id: PanId, /// The reason that synchronization was lost pub loss_reason: u8, /// The logical channel on which the device lost synchronization or to whi @@ -275,7 +275,7 @@ impl ParseableMacEvent for SyncLossIndication { Self::validate(buf)?; Ok(Self { - pan_id: [buf[0], buf[1]], + pan_id: PanId([buf[0], buf[1]]), loss_reason: buf[2], channel_number: MacChannel::try_from(buf[3])?, channel_page: buf[4], @@ -303,19 +303,20 @@ impl ParseableMacEvent for DpsIndication { } #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(C, align(8))] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, /// Source addressing mode used pub src_addr_mode: AddressMode, /// Source PAN ID - pub src_pan_id: [u8; 2], + pub src_pan_id: PanId, /// Source address pub src_address: MacAddress, /// Destination addressing mode used pub dst_addr_mode: AddressMode, /// Destination PAN ID - pub dst_pan_id: [u8; 2], + pub dst_pan_id: PanId, /// Destination address pub dst_address: MacAddress, /// The number of octets contained in the MSDU being indicated @@ -355,7 +356,7 @@ pub struct DataIndication { } impl ParseableMacEvent for DataIndication { - const SIZE: usize = 72; + const SIZE: usize = 68; fn try_parse(buf: &[u8]) -> Result { Self::validate(buf)?; @@ -387,24 +388,24 @@ impl ParseableMacEvent for DataIndication { Ok(Self { msdu_ptr: to_u32(&buf[0..4]) as *const u8, src_addr_mode, - src_pan_id: [buf[5], buf[6]], + src_pan_id: PanId([buf[5], buf[6]]), src_address, dst_addr_mode, - dst_pan_id: [buf[16], buf[17]], + dst_pan_id: PanId([buf[16], buf[17]]), dst_address, msdu_length: buf[26], mpdu_link_quality: buf[27], dsn: buf[28], time_stamp: [buf[29], buf[30], buf[31], buf[32]], - security_level: SecurityLevel::try_from(buf[33])?, - key_id_mode: KeyIdMode::try_from(buf[34])?, + security_level: SecurityLevel::try_from(buf[33]).unwrap_or(SecurityLevel::Unsecure), // TODO: this is totaly wrong, but I'm too smol brain to fix it + key_id_mode: KeyIdMode::try_from(buf[34]).unwrap_or(KeyIdMode::Implicite), // TODO: this is totaly wrong, but I'm too smol brain to fix it key_source: [buf[35], buf[36], buf[37], buf[38], buf[39], buf[40], buf[41], buf[42]], key_index: buf[43], uwbprf: buf[44], uwn_preamble_symbol_repetitions: buf[45], datrate: buf[46], ranging_received: buf[47], - ranging_counter_start: to_u32(&buf[58..52]), + ranging_counter_start: to_u32(&buf[48..52]), ranging_counter_stop: to_u32(&buf[52..56]), ranging_tracking_interval: to_u32(&buf[56..60]), ranging_offset: to_u32(&buf[60..64]), diff --git a/embassy-stm32-wpan/src/sub/mac/opcodes.rs b/embassy-stm32-wpan/src/sub/mac/opcodes.rs index c9a07d6af..fd7011873 100644 --- a/embassy-stm32-wpan/src/sub/mac/opcodes.rs +++ b/embassy-stm32-wpan/src/sub/mac/opcodes.rs @@ -5,6 +5,7 @@ const fn opcode(ocf: u16) -> isize { ((ST_VENDOR_OGF << 9) | (MAC_802_15_4_CMD_OPCODE_OFFSET + ocf)) as isize } +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OpcodeM4ToM0 { MlmeAssociateReq = opcode(0x00), MlmeAssociateRes = opcode(0x01), @@ -26,6 +27,7 @@ pub enum OpcodeM4ToM0 { McpsPurgeReq = opcode(0x11), } +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OpcodeM0ToM4 { MlmeAssociateCnf = 0x00, MlmeDisassociateCnf, diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index 37271ec28..2b90ccdc6 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -2,7 +2,8 @@ use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_S use super::event::ParseableMacEvent; use super::helpers::to_u32; use super::typedefs::{ - AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PibId, ScanType, SecurityLevel, + AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType, + SecurityLevel, }; /// MLME ASSOCIATE Confirm used to inform of the initiating device whether @@ -50,7 +51,7 @@ pub struct DisassociateConfirm { /// device addressing mode used pub device_addr_mode: AddressMode, /// the identifier of the PAN of the device - pub device_pan_id: [u8; 2], + pub device_pan_id: PanId, /// device address pub device_address: MacAddress, } @@ -76,7 +77,7 @@ impl ParseableMacEvent for DisassociateConfirm { Ok(Self { status: MacStatus::try_from(buf[0])?, device_addr_mode, - device_pan_id: [buf[2], buf[3]], + device_pan_id: PanId([buf[2], buf[3]]), device_address, }) } diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/sub/mac/typedefs.rs index 1a4c30cbc..5ff051c97 100644 --- a/embassy-stm32-wpan/src/sub/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/sub/mac/typedefs.rs @@ -25,15 +25,12 @@ impl From for MacError { numeric_enum! { #[repr(u8)] - #[derive(Debug)] + #[derive(Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MacStatus { + #[default] Success = 0x00, - Error = 0x01, - NotImplemented = 0x02, - NotSupported = 0x03, - HardwareNotSupported = 0x04, - Undefined = 0x05, + Failure = 0xFF } } @@ -134,6 +131,10 @@ impl Default for MacAddress { } } +impl MacAddress { + pub const BROADCAST: Self = Self { short: [0xFF, 0xFF] }; +} + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsCharacteristics { pub fields: u8, @@ -145,7 +146,7 @@ pub struct GtsCharacteristics { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PanDescriptor { /// PAN identifier of the coordinator - pub coord_pan_id: [u8; 2], + pub coord_pan_id: PanId, /// Coordinator addressing mode pub coord_addr_mode: AddressMode, /// The current logical channel occupied by the network @@ -188,7 +189,7 @@ impl TryFrom<&[u8]> for PanDescriptor { }; Ok(Self { - coord_pan_id: [buf[0], buf[1]], + coord_pan_id: PanId([buf[0], buf[1]]), coord_addr_mode, logical_channel: MacChannel::try_from(buf[3])?, coord_addr, @@ -336,3 +337,12 @@ numeric_enum! { Orphan = 0x03 } } + +/// newtype for Pan Id +#[derive(Default, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PanId(pub [u8; 2]); + +impl PanId { + pub const BROADCAST: Self = Self([0xFF, 0xFF]); +} diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 4e2578a21..37d36fcdd 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -6,8 +6,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::sub::mac::commands::{ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::sub::mac::typedefs::{MacChannel, PibId}; +use embassy_stm32_wpan::sub::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::sub::mac::event::MacEvent; +use embassy_stm32_wpan::sub::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -123,7 +124,7 @@ async fn main(spawner: Spawner) { info!("starting FFD device"); mbox.mac_subsystem .send_command(&StartRequest { - pan_id: [0xAA, 0x1A], + pan_id: PanId([0x1A, 0xAA]), channel_number: MacChannel::Channel16, beacon_order: 0x0F, superframe_order: 0x0F, @@ -151,5 +152,28 @@ async fn main(spawner: Spawner) { loop { let evt = mbox.mac_subsystem.read().await; defmt::info!("{:#x}", evt); + + if let Ok(evt) = evt { + match evt { + MacEvent::MlmeAssociateInd(association) => mbox + .mac_subsystem + .send_command(&AssociateResponse { + device_address: association.device_address, + assoc_short_address: [0x33, 0x44], + status: MacStatus::Success, + security_level: SecurityLevel::Unsecure, + ..Default::default() + }) + .await + .unwrap(), + MacEvent::McpsDataInd(data_ind) => { + let data_addr = data_ind.msdu_ptr; + let mut a = [0u8; 256]; + unsafe { data_addr.copy_to(&mut a as *mut _, data_ind.msdu_length as usize) } + info!("{}", a[..data_ind.msdu_length as usize]) + } + _ => {} + } + } } } diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index e5f8d54c9..756709132 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,10 +6,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; +use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::sub::mac::event::MacEvent; use embassy_stm32_wpan::sub::mac::typedefs::{ - AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PibId, SecurityLevel, + AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, }; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; @@ -112,7 +112,7 @@ async fn main(spawner: Spawner) { coord_addr_mode: AddressMode::Short, coord_address: MacAddress { short: [34, 17] }, capability_information: Capabilities::ALLOCATE_ADDRESS, - coord_pan_id: [0xAA, 0x1A], + coord_pan_id: PanId([0x1A, 0xAA]), security_level: SecurityLevel::Unsecure, key_id_mode: KeyIdMode::Implicite, key_source: [0; 8], @@ -123,11 +123,16 @@ async fn main(spawner: Spawner) { let evt = mbox.mac_subsystem.read().await; info!("{:#x}", evt); + let short_addr = if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt { + conf.assoc_short_address + } else { + defmt::panic!() + }; + info!("setting short address"); - let short: u64 = 0xACDE480000000002; mbox.mac_subsystem .send_command(&SetRequest { - pib_attribute_ptr: &short as *const _ as *const u8, + pib_attribute_ptr: &short_addr as *const _ as *const u8, pib_attribute: PibId::ShortAddress, }) .await @@ -135,6 +140,29 @@ async fn main(spawner: Spawner) { let evt = mbox.mac_subsystem.read().await; info!("{:#x}", evt); + info!("sending data"); + let mut data_buffer = [0u8; 256]; + let data = b"Hello from embassy!"; + data_buffer[..data.len()].copy_from_slice(data); + mbox.mac_subsystem + .send_command(&DataRequest { + src_addr_mode: AddressMode::Short, + dst_addr_mode: AddressMode::Short, + dst_pan_id: PanId::BROADCAST, + dst_address: MacAddress::BROADCAST, + msdu_handle: 0x02, + ack_tx: 0x00, + gts_tx: false, + msdu_ptr: &data_buffer as *const _ as *const u8, + msdu_length: data.len() as u8, + security_level: SecurityLevel::Unsecure, + ..Default::default() + }) + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + loop { let evt = mbox.mac_subsystem.read().await; info!("{:#x}", evt); From 68792bb9188b69c1e7629425a0d39110c602b9b2 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 13 Jul 2023 15:48:15 +0100 Subject: [PATCH 1549/1575] final structs unchecked --- embassy-stm32-wpan/src/sub/mac/indications.rs | 23 ++++++++++++-- embassy-stm32-wpan/src/sub/mac/responses.rs | 31 +++++++++++++++++-- embassy-stm32-wpan/src/sub/mac/typedefs.rs | 17 +++++++++- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/embassy-stm32-wpan/src/sub/mac/indications.rs b/embassy-stm32-wpan/src/sub/mac/indications.rs index b67f0a686..6df4aa23a 100644 --- a/embassy-stm32-wpan/src/sub/mac/indications.rs +++ b/embassy-stm32-wpan/src/sub/mac/indications.rs @@ -96,12 +96,31 @@ pub struct BeaconNotifyIndication { } impl ParseableMacEvent for BeaconNotifyIndication { - const SIZE: usize = 12; + const SIZE: usize = 88; fn try_parse(buf: &[u8]) -> Result { + // TODO: this is unchecked + Self::validate(buf)?; - todo!() + let addr_list = [ + MacAddress::try_from(&buf[26..34])?, + MacAddress::try_from(&buf[34..42])?, + MacAddress::try_from(&buf[42..50])?, + MacAddress::try_from(&buf[50..58])?, + MacAddress::try_from(&buf[58..66])?, + MacAddress::try_from(&buf[66..74])?, + MacAddress::try_from(&buf[74..82])?, + ]; + + Ok(Self { + sdu_ptr: to_u32(&buf[0..4]) as *const u8, + pan_descriptor: PanDescriptor::try_from(&buf[4..26])?, + addr_list, + bsn: buf[82], + pend_addr_spec: buf[83], + sdu_length: buf[83], + }) } } diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index 2b90ccdc6..0d3c09869 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -199,12 +199,39 @@ pub struct ScanConfirm { } impl ParseableMacEvent for ScanConfirm { - const SIZE: usize = 9; + const SIZE: usize = 185; fn try_parse(buf: &[u8]) -> Result { + // TODO: this is unchecked + Self::validate(buf)?; - todo!() + let mut energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; + energy_detect_list.copy_from_slice(&buf[8..24]); + + let pan_descriptor_list = [ + PanDescriptor::try_from(&buf[24..46])?, + PanDescriptor::try_from(&buf[46..68])?, + PanDescriptor::try_from(&buf[68..90])?, + PanDescriptor::try_from(&buf[90..102])?, + PanDescriptor::try_from(&buf[102..124])?, + PanDescriptor::try_from(&buf[124..146])?, + ]; + + let mut uwb_energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; + uwb_energy_detect_list.copy_from_slice(&buf[147..163]); + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + scan_type: ScanType::try_from(buf[1])?, + channel_page: buf[2], + unscanned_channels: [buf[3], buf[4], buf[5], buf[6]], + result_list_size: buf[7], + energy_detect_list, + pan_descriptor_list, + detected_category: buf[146], + uwb_energy_detect_list, + }) } } diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/sub/mac/typedefs.rs index 5ff051c97..30c7731b2 100644 --- a/embassy-stm32-wpan/src/sub/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/sub/mac/typedefs.rs @@ -135,6 +135,21 @@ impl MacAddress { pub const BROADCAST: Self = Self { short: [0xFF, 0xFF] }; } +impl TryFrom<&[u8]> for MacAddress { + type Error = (); + + fn try_from(buf: &[u8]) -> Result { + const SIZE: usize = 8; + if buf.len() < SIZE { + return Err(()); + } + + Ok(Self { + extended: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], + }) + } +} + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsCharacteristics { pub fields: u8, @@ -171,7 +186,7 @@ impl TryFrom<&[u8]> for PanDescriptor { type Error = (); fn try_from(buf: &[u8]) -> Result { - const SIZE: usize = 24; + const SIZE: usize = 22; if buf.len() < SIZE { return Err(()); } From f90b170dad91848d5a0ff746d873bd8a4ce7e91f Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 13 Jul 2023 16:29:29 +0100 Subject: [PATCH 1550/1575] cleanup --- embassy-stm32-wpan/Cargo.toml | 2 +- embassy-stm32-wpan/src/sub/mac/mod.rs | 2 -- embassy-stm32-wpan/src/sub/mac/responses.rs | 2 -- embassy-stm32-wpan/src/sub/sys.rs | 8 ++++---- examples/stm32wb/.cargo/config.toml | 4 ++-- examples/stm32wb/src/bin/mac_ffd.rs | 10 +++++++--- examples/stm32wb/src/bin/mac_rfd.rs | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 1325faed9..91540321f 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -29,7 +29,7 @@ stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true } bitflags = { version = "2.3.3", optional = true } [features] -default = ["stm32wb55rg", "mac", "ble", "defmt"] +default = [] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] ble = ["dep:stm32wb-hci"] diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs index 26358bf81..ab39f89c2 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -98,8 +98,6 @@ impl Mac { let mut payload = [0u8; MAX_PACKET_SIZE]; cmd.copy_into_slice(&mut payload); - debug!("sending {}", &payload[..T::SIZE]); - let response = self .tl_write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]) .await; diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index 0d3c09869..2f6f5bf58 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -28,8 +28,6 @@ impl ParseableMacEvent for AssociateConfirm { const SIZE: usize = 16; fn try_parse(buf: &[u8]) -> Result { - debug!("{}", buf); - Self::validate(buf)?; Ok(Self { diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index caa4845f2..c17fd531d 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -50,7 +50,7 @@ impl Sys { } /// `HW_IPCC_SYS_CmdEvtNot` - pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> SchiCommandStatus { + pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> Result { self.write(opcode, payload).await; Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; @@ -59,12 +59,12 @@ impl Sys { let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt; let p_payload = &((*p_command_event).payload) as *const u8; - ptr::read_volatile(p_payload).try_into().unwrap() + ptr::read_volatile(p_payload).try_into() } } #[cfg(feature = "mac")] - pub async fn shci_c2_mac_802_15_4_init(&self) -> SchiCommandStatus { + pub async fn shci_c2_mac_802_15_4_init(&self) -> Result { use crate::tables::{ Mac802_15_4Table, TracesTable, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE, TL_TRACES_TABLE, TRACES_EVT_QUEUE, @@ -88,7 +88,7 @@ impl Sys { } #[cfg(feature = "ble")] - pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> SchiCommandStatus { + pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result { self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await } diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index cf62a10a0..51c499ee7 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,7 +1,7 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` -runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" -# runner = "teleprobe local run --chip STM32WB55RG --elf" +# runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" +runner = "teleprobe local run --chip STM32WB55RG --elf" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 37d36fcdd..689a28353 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -168,9 +168,13 @@ async fn main(spawner: Spawner) { .unwrap(), MacEvent::McpsDataInd(data_ind) => { let data_addr = data_ind.msdu_ptr; - let mut a = [0u8; 256]; - unsafe { data_addr.copy_to(&mut a as *mut _, data_ind.msdu_length as usize) } - info!("{}", a[..data_ind.msdu_length as usize]) + let mut data = [0u8; 256]; + unsafe { data_addr.copy_to(&mut data as *mut _, data_ind.msdu_length as usize) } + info!("{}", data[..data_ind.msdu_length as usize]); + + if &data[..data_ind.msdu_length as usize] == b"Hello from embassy!" { + info!("success"); + } } _ => {} } diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 756709132..ea349f9a8 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -148,7 +148,7 @@ async fn main(spawner: Spawner) { .send_command(&DataRequest { src_addr_mode: AddressMode::Short, dst_addr_mode: AddressMode::Short, - dst_pan_id: PanId::BROADCAST, + dst_pan_id: PanId([0x1A, 0xAA]), dst_address: MacAddress::BROADCAST, msdu_handle: 0x02, ack_tx: 0x00, From 460cdc9e0f51979e27dbd0a8faaad2738760cdf3 Mon Sep 17 00:00:00 2001 From: Ben Simms Date: Thu, 13 Jul 2023 19:29:09 +0100 Subject: [PATCH 1551/1575] Check intrstatus before signalling suspended --- embassy-rp/src/usb.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index b3f3bd927..4ab881f6e 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -361,6 +361,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let regs = T::regs(); let siestatus = regs.sie_status().read(); + let intrstatus = regs.intr().read(); if siestatus.resume() { regs.sie_status().write(|w| w.set_resume(true)); @@ -389,7 +390,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { return Poll::Ready(Event::Reset); } - if siestatus.suspended() { + if siestatus.suspended() && intrstatus.dev_suspend() { regs.sie_status().write(|w| w.set_suspended(true)); return Poll::Ready(Event::Suspend); } From 56ca1794759a21d9d5397e5bd4aa8226f6ef9385 Mon Sep 17 00:00:00 2001 From: Henrik Berg Date: Thu, 13 Jul 2023 22:47:03 +0200 Subject: [PATCH 1552/1575] Round temp to make more sense. --- examples/rp/src/bin/adc.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index c0cbe0172..81a8b8340 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -41,5 +41,8 @@ async fn main(_spawner: Spawner) { fn convert_to_celsius(raw_temp: u16) -> f32 { // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet - 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 + let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721; + let sign = if temp < 0.0 { -1.0 } else { 1.0 }; + let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16; + (rounded_temp_x10 as f32) / 10.0 } From 3bae53306683a57020ba751afaf631ec169deeed Mon Sep 17 00:00:00 2001 From: Phil Markgraf Date: Sat, 15 Jul 2023 04:40:23 -0700 Subject: [PATCH 1553/1575] Enable RTC on STM32WL chips (#1645) * Add clippy allow to not report if same then branch * Support enabling RTC clock on STM32WL * Add clippy allow to not report if same then branch * Support enabling RTC clock on STM32WL * Add rtc example for stm32wl * Address code review feedback --- embassy-stm32/src/rcc/wl.rs | 61 ++++++++++++++++++++++++++++- embassy-stm32/src/rtc/v3.rs | 1 + examples/stm32wl/.cargo/config.toml | 2 +- examples/stm32wl/Cargo.toml | 5 ++- examples/stm32wl/src/bin/rtc.rs | 43 ++++++++++++++++++++ 5 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 examples/stm32wl/src/bin/rtc.rs diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 7072db984..6b69bb1cb 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,4 +1,5 @@ -use crate::pac::{FLASH, RCC}; +use crate::pac::pwr::vals::Dbp; +use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -184,6 +185,8 @@ pub struct Config { pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, pub enable_lsi: bool, + pub enable_rtc_apb: bool, + pub rtc_mux: RtcClockSource, } impl Default for Config { @@ -196,10 +199,25 @@ impl Default for Config { apb1_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided, enable_lsi: false, + enable_rtc_apb: false, + rtc_mux: RtcClockSource::LSI32, } } } +pub enum RtcClockSource { + LSE32, + LSI32, +} + +#[repr(u8)] +pub enum Lsedrv { + Low = 0, + MediumLow = 1, + MediumHigh = 2, + High = 3, +} + pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw, vos) = match config.mux { ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2), @@ -266,6 +284,32 @@ pub(crate) unsafe fn init(config: Config) { while FLASH.acr().read().latency() != ws {} + match config.rtc_mux { + RtcClockSource::LSE32 => { + // 1. Unlock the backup domain + PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); + + // 2. Setup the LSE + RCC.bdcr().modify(|w| { + // Enable LSE + w.set_lseon(true); + // Max drive strength + // TODO: should probably be settable + w.set_lsedrv(Lsedrv::High as u8); //---// PAM - should not be commented + }); + + // Wait until LSE is running + while !RCC.bdcr().read().lserdy() {} + } + RtcClockSource::LSI32 => { + // Turn on the internal 32 kHz LSI oscillator + RCC.csr().modify(|w| w.set_lsion(true)); + + // Wait until LSI is running + while !RCC.csr().read().lsirdy() {} + } + } + match config.mux { ClockSrc::HSI16 => { // Enable HSI16 @@ -287,11 +331,26 @@ pub(crate) unsafe fn init(config: Config) { w.set_msirgsel(true); w.set_msirange(range.into()); w.set_msion(true); + + if let RtcClockSource::LSE32 = config.rtc_mux { + // If LSE is enabled, enable calibration of MSI + w.set_msipllen(true); + } else { + w.set_msipllen(false); + } }); while !RCC.cr().read().msirdy() {} } } + if config.enable_rtc_apb { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); + } + RCC.extcfgr().modify(|w| { if config.shd_ahb_pre == AHBPrescaler::NotDivided { w.set_shdhpre(0); diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 7e5c64d90..8ef0ec51d 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -172,6 +172,7 @@ impl sealed::Instance for crate::peripherals::RTC { const BACKUP_REGISTER_COUNT: usize = 32; fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { + #[allow(clippy::if_same_then_else)] if register < Self::BACKUP_REGISTER_COUNT { //Some(rtc.bkpr()[register].read().bits()) None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC diff --git a/examples/stm32wl/.cargo/config.toml b/examples/stm32wl/.cargo/config.toml index 4f8094ff2..ee416fcbc 100644 --- a/examples/stm32wl/.cargo/config.toml +++ b/examples/stm32wl/.cargo/config.toml @@ -3,7 +3,7 @@ runner = "probe-rs run --chip STM32WLE5JCIx" [build] -target = "thumbv7em-none-eabihf" +target = "thumbv7em-none-eabi" [env] DEFMT_LOG = "trace" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 99f68387f..e2c66f456 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } -embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } @@ -25,6 +25,7 @@ embedded-storage = "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 } +chrono = { version = "^0.4", default-features = false } [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs new file mode 100644 index 000000000..e11825499 --- /dev/null +++ b/examples/stm32wl/src/bin/rtc.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{self, ClockSrc}; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = { + let mut config = Config::default(); + config.rcc.mux = ClockSrc::HSE32; + config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; + config.rcc.enable_rtc_apb = true; + embassy_stm32::init(config) + }; + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new( + p.RTC, + RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), + ); + info!("Got RTC! {:?}", now.timestamp()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + // In reality the delay would be much longer + Timer::after(Duration::from_millis(20000)).await; + + let then: NaiveDateTime = rtc.now().unwrap().into(); + info!("Got RTC! {:?}", then.timestamp()); +} From 5b076cb0ddb55e60ae24d1a0ae02b27baa5d84e5 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 15 Jul 2023 13:33:10 +0100 Subject: [PATCH 1554/1575] wpan: update `stm32wb-hci` to version 0.1.3 --- embassy-stm32-wpan/Cargo.toml | 4 ++-- examples/stm32wb/Cargo.toml | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 5141f9bd2..6142fd7d1 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -25,10 +25,10 @@ aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true } +stm32wb-hci = { version = "0.1.3", optional = true } [features] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci/defmt"] ble = ["dep:stm32wb-hci"] mac = [] diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 203ca1486..26e72991f 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -37,7 +37,4 @@ required-features = ["mac"] [[bin]] name = "eddystone_beacon" -required-features = ["ble"] - -[patch.crates-io] -stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"} \ No newline at end of file +required-features = ["ble"] \ No newline at end of file From 283ec756a958cffa0ba145af6c9aea8c862430fa Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 15 Jul 2023 13:37:41 +0100 Subject: [PATCH 1555/1575] stm32wb: add gatt server example --- examples/stm32wb/Cargo.toml | 4 + examples/stm32wb/src/bin/gatt_server.rs | 397 ++++++++++++++++++++++++ 2 files changed, 401 insertions(+) create mode 100644 examples/stm32wb/src/bin/gatt_server.rs diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 26e72991f..d8d5f0ec7 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -37,4 +37,8 @@ required-features = ["mac"] [[bin]] name = "eddystone_beacon" +required-features = ["ble"] + +[[bin]] +name = "gatt_server" required-features = ["ble"] \ No newline at end of file diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs new file mode 100644 index 000000000..7621efb11 --- /dev/null +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -0,0 +1,397 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::time::Duration; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::hci::event::command::{CommandComplete, ReturnParameters}; +use embassy_stm32_wpan::hci::host::uart::{Packet, UartHci}; +use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; +use embassy_stm32_wpan::hci::types::AdvertisingType; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{ + AddressType, AuthenticationRequirements, DiscoverableParameters, GapCommands, IoCapability, LocalName, Pin, Role, + SecureConnectionSupport, +}; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::{ + AddCharacteristicParameters, AddServiceParameters, CharacteristicEvent, CharacteristicPermission, + CharacteristicProperty, EncryptionKeySize, GattCommands, ServiceType, UpdateCharacteristicValueParameters, Uuid, + WriteResponseParameters, +}; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; +use embassy_stm32_wpan::hci::vendor::stm32wb::event::{self, AttributeHandle, Stm32Wb5xEvent}; +use embassy_stm32_wpan::hci::{BdAddr, Event}; +use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; +use embassy_stm32_wpan::sub::ble::Ble; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mut mbox = TlMbox::init(p.IPCC, Irqs, config); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + + info!("resetting BLE..."); + mbox.ble_subsystem.reset().await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config public address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config random address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::random_address(get_random_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config identity root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::identity_root(&get_irk()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config encryption root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config tx power level..."); + mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("GATT init..."); + mbox.ble_subsystem.init_gatt().await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("GAP init..."); + mbox.ble_subsystem + .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set IO capabilities..."); + mbox.ble_subsystem.set_io_capability(IoCapability::DisplayConfirm).await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set authentication requirements..."); + mbox.ble_subsystem + .set_authentication_requirement(&AuthenticationRequirements { + bonding_required: false, + keypress_notification_support: false, + mitm_protection_required: false, + encryption_key_size_range: (8, 16), + fixed_pin: Pin::Requested, + identity_address_type: AddressType::Public, + secure_connection_support: SecureConnectionSupport::Optional, + }) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set scan response data..."); + mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set scan response data..."); + mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + defmt::info!("initializing services and characteristics..."); + let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); + defmt::info!("{}", ble_context); + + let discovery_params = DiscoverableParameters { + advertising_type: AdvertisingType::ConnectableUndirected, + advertising_interval: Some((Duration::from_millis(100), Duration::from_millis(100))), + address_type: OwnAddressType::Public, + filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, + local_name: Some(LocalName::Complete(b"TXTX")), + advertising_data: &[], + conn_interval: (None, None), + }; + + info!("set discoverable..."); + mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + loop { + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + if let Ok(Packet::Event(event)) = response { + match event { + Event::LeConnectionComplete(_) => { + defmt::info!("connected"); + } + Event::DisconnectionComplete(_) => { + defmt::info!("disconnected"); + ble_context.is_subscribed = false; + mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); + } + Event::Vendor(vendor_event) => match vendor_event { + Stm32Wb5xEvent::AttReadPermitRequest(read_req) => { + defmt::info!("read request received {}, allowing", read_req); + mbox.ble_subsystem.allow_read(read_req.conn_handle).await + } + Stm32Wb5xEvent::AttWritePermitRequest(write_req) => { + defmt::info!("write request received {}, allowing", write_req); + mbox.ble_subsystem + .write_response(&WriteResponseParameters { + conn_handle: write_req.conn_handle, + attribute_handle: write_req.attribute_handle, + status: Ok(()), + value: write_req.value(), + }) + .await + .unwrap() + } + Stm32Wb5xEvent::GattAttributeModified(attribute) => { + defmt::info!("{}", ble_context); + if attribute.attr_handle.0 == ble_context.chars.notify.0 + 2 { + if attribute.data()[0] == 0x01 { + defmt::info!("subscribed"); + ble_context.is_subscribed = true; + } else { + defmt::info!("unsubscribed"); + ble_context.is_subscribed = false; + } + } + } + _ => {} + }, + _ => {} + } + } + } +} + +fn get_bd_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = lhci_info.device_type_id; + bytes[4] = (lhci_info.st_company_id & 0xff) as u8; + bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8; + + BdAddr(bytes) +} + +fn get_random_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = 0; + bytes[4] = 0x6E; + bytes[5] = 0xED; + + BdAddr(bytes) +} + +const BLE_CFG_IRK: [u8; 16] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, +]; +const BLE_CFG_ERK: [u8; 16] = [ + 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, +]; + +fn get_irk() -> EncryptionKey { + EncryptionKey(BLE_CFG_IRK) +} + +fn get_erk() -> EncryptionKey { + EncryptionKey(BLE_CFG_ERK) +} + +#[derive(defmt::Format)] +pub struct BleContext { + pub service_handle: AttributeHandle, + pub chars: CharHandles, + pub is_subscribed: bool, +} + +#[derive(defmt::Format)] +pub struct CharHandles { + pub read: AttributeHandle, + pub write: AttributeHandle, + pub notify: AttributeHandle, +} + +pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result { + let service_handle = gatt_add_service(ble_subsystem, Uuid::Uuid16(0x500)).await?; + + let read = gatt_add_char( + ble_subsystem, + service_handle, + Uuid::Uuid16(0x501), + CharacteristicProperty::READ, + Some(b"Hello from embassy!"), + ) + .await?; + + let write = gatt_add_char( + ble_subsystem, + service_handle, + Uuid::Uuid16(0x502), + CharacteristicProperty::WRITE_WITHOUT_RESPONSE | CharacteristicProperty::WRITE | CharacteristicProperty::READ, + None, + ) + .await?; + + let notify = gatt_add_char( + ble_subsystem, + service_handle, + Uuid::Uuid16(0x503), + CharacteristicProperty::NOTIFY | CharacteristicProperty::READ, + None, + ) + .await?; + + Ok(BleContext { + service_handle, + is_subscribed: false, + chars: CharHandles { read, write, notify }, + }) +} + +async fn gatt_add_service(ble_subsystem: &mut Ble, uuid: Uuid) -> Result { + ble_subsystem + .add_service(&AddServiceParameters { + uuid, + service_type: ServiceType::Primary, + max_attribute_records: 8, + }) + .await; + let response = ble_subsystem.read().await; + defmt::debug!("{}", response); + + if let Ok(Packet::Event(Event::CommandComplete(CommandComplete { + return_params: + ReturnParameters::Vendor(event::command::ReturnParameters::GattAddService(event::command::GattService { + service_handle, + .. + })), + .. + }))) = response + { + Ok(service_handle) + } else { + Err(()) + } +} + +async fn gatt_add_char( + ble_subsystem: &mut Ble, + service_handle: AttributeHandle, + characteristic_uuid: Uuid, + characteristic_properties: CharacteristicProperty, + default_value: Option<&[u8]>, +) -> Result { + ble_subsystem + .add_characteristic(&AddCharacteristicParameters { + service_handle, + characteristic_uuid, + characteristic_properties, + characteristic_value_len: 32, + security_permissions: CharacteristicPermission::empty(), + gatt_event_mask: CharacteristicEvent::all(), + encryption_key_size: EncryptionKeySize::with_value(7).unwrap(), + is_variable: true, + }) + .await; + let response = ble_subsystem.read().await; + defmt::debug!("{}", response); + + if let Ok(Packet::Event(Event::CommandComplete(CommandComplete { + return_params: + ReturnParameters::Vendor(event::command::ReturnParameters::GattAddCharacteristic( + event::command::GattCharacteristic { + characteristic_handle, .. + }, + )), + .. + }))) = response + { + if let Some(value) = default_value { + ble_subsystem + .update_characteristic_value(&UpdateCharacteristicValueParameters { + service_handle, + characteristic_handle, + offset: 0, + value, + }) + .await + .unwrap(); + + let response = ble_subsystem.read().await; + defmt::debug!("{}", response); + } + Ok(characteristic_handle) + } else { + Err(()) + } +} From 0628dd997f872d49ba10e299d250f4877434d6f1 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sat, 15 Jul 2023 13:52:04 +0100 Subject: [PATCH 1556/1575] fix test --- tests/stm32/Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 4fd4a6d0b..d94bd730b 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -46,9 +46,6 @@ rand_chacha = { version = "0.3", default-features = false } chrono = { version = "^0.4", default-features = false, optional = true} -[patch.crates-io] -stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"} - # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. [[bin]] From 7ec7d1bbccac07de6053e1a152b6c417693e919b Mon Sep 17 00:00:00 2001 From: Ghaith Oueslati <73850124+OueslatiGhaith@users.noreply.github.com> Date: Sat, 15 Jul 2023 14:56:26 +0100 Subject: [PATCH 1557/1575] fix ci issue --- embassy-stm32-wpan/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 6142fd7d1..f1242ea10 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -28,7 +28,7 @@ stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } stm32wb-hci = { version = "0.1.3", optional = true } [features] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"] ble = ["dep:stm32wb-hci"] mac = [] @@ -48,4 +48,4 @@ stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ] stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ] stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ] stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ] -stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] \ No newline at end of file +stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] From 48b37aa2bf83b8fccb293fcda7f51149a4ec1a24 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 09:32:36 -0500 Subject: [PATCH 1558/1575] stm32/eth: refactor genericsmi --- embassy-stm32/src/eth/generic_smi.rs | 10 +++++++--- embassy-stm32/src/eth/mod.rs | 10 ++++------ embassy-stm32/src/eth/v1/mod.rs | 27 ++++++++++++++++++--------- embassy-stm32/src/eth/v2/mod.rs | 27 ++++++++++++++++++--------- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 968256046..1d83cec35 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -1,5 +1,7 @@ //! Generic SMI Ethernet PHY +use futures::task::Context; + use super::{StationManagement, PHY}; #[allow(dead_code)] @@ -40,13 +42,13 @@ pub struct GenericSMI; unsafe impl PHY for GenericSMI { /// Reset PHY and wait for it to come out of reset. - fn phy_reset(sm: &mut S) { + fn phy_reset(&mut self, sm: &mut S) { sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET); while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} } /// PHY initialisation. - fn phy_init(sm: &mut S) { + fn phy_init(&mut self, sm: &mut S) { // Clear WU CSR Self::smi_write_ext(sm, PHY_REG_WUCSR, 0); @@ -54,7 +56,9 @@ unsafe impl PHY for GenericSMI { sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M); } - fn poll_link(sm: &mut S) -> bool { + fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool { + cx.waker().wake_by_ref(); + let bsr = sm.smi_read(PHY_REG_BSR); // No link without autonegotiate diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 4989e17c7..1687cb319 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -81,9 +81,7 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> } fn link_state(&mut self, cx: &mut Context) -> LinkState { - // TODO: wake cx.waker on link state change - cx.waker().wake_by_ref(); - if P::poll_link(self) { + if self.phy.poll_link(&mut self.station_management, cx) { LinkState::Up } else { LinkState::Down @@ -148,11 +146,11 @@ pub unsafe trait StationManagement { /// The methods cannot move S pub unsafe trait PHY { /// Reset PHY and wait for it to come out of reset. - fn phy_reset(sm: &mut S); + fn phy_reset(&mut self, sm: &mut S); /// PHY initialisation. - fn phy_init(sm: &mut S); + fn phy_init(&mut self, sm: &mut S); /// Poll link to see if it is up and FD with 100Mbps - fn poll_link(sm: &mut S) -> bool; + fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool; } pub(crate) mod sealed { diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index b53c2d0fa..2a6ea35ff 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -3,6 +3,7 @@ mod rx_desc; mod tx_desc; +use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -48,9 +49,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], - _phy: P, - clock_range: Cr, - phy_addr: u8, + pub(crate) phy: P, + pub(crate) station_management: EthernetStationManagement, pub(crate) mac_addr: [u8; 6], } @@ -224,9 +224,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { let mut this = Self { _peri: peri, pins, - _phy: phy, - clock_range, - phy_addr, + phy: phy, + station_management: EthernetStationManagement { + peri: PhantomData, + clock_range: clock_range, + phy_addr: phy_addr, + }, mac_addr, tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), @@ -256,8 +259,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_tie(true); }); - P::phy_reset(&mut this); - P::phy_init(&mut this); + this.phy.phy_reset(&mut this.station_management); + this.phy.phy_init(&mut this.station_management); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; @@ -266,7 +269,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { } } -unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { +pub struct EthernetStationManagement { + peri: PhantomData, + clock_range: Cr, + phy_addr: u8, +} + +unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, reg: u8) -> u16 { let mac = ETH.ethernet_mac(); diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 600e1d3bc..bb681c42b 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -1,5 +1,6 @@ mod descriptors; +use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -40,9 +41,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { pub(crate) tx: TDesRing<'d>, pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], - _phy: P, - clock_range: u8, - phy_addr: u8, + pub(crate) phy: P, + pub(crate) station_management: EthernetStationManagement, pub(crate) mac_addr: [u8; 6], } @@ -201,9 +201,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), pins, - _phy: phy, - clock_range, - phy_addr, + phy: phy, + station_management: EthernetStationManagement { + peri: PhantomData, + clock_range: clock_range, + phy_addr: phy_addr, + }, mac_addr, }; @@ -229,8 +232,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_tie(true); }); - P::phy_reset(&mut this); - P::phy_init(&mut this); + this.phy.phy_reset(&mut this.station_management); + this.phy.phy_init(&mut this.station_management); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; @@ -239,7 +242,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { } } -unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { +pub struct EthernetStationManagement { + peri: PhantomData, + clock_range: u8, + phy_addr: u8, +} + +unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, reg: u8) -> u16 { let mac = ETH.ethernet_mac(); From bb24cfd1e8d359332acbc3c659cb9041d993483f Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 09:32:44 -0500 Subject: [PATCH 1559/1575] stm32/eth: add f4 example --- examples/stm32f4/src/bin/eth.rs | 111 ++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 examples/stm32f4/src/bin/eth.rs diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs new file mode 100644 index 000000000..c32d886d0 --- /dev/null +++ b/examples/stm32f4/src/bin/eth.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_stm32::eth::generic_smi::GenericSMI; +use embassy_stm32::eth::{Ethernet, PacketQueue}; +use embassy_stm32::peripherals::ETH; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::mhz; +use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::Write; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; +}); + +type Device = Ethernet<'static, ETH, GenericSMI>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.sys_ck = Some(mhz(200)); + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + // Generate random seed. + let mut rng = Rng::new(p.RNG); + let mut seed = [0; 8]; + let _ = rng.async_fill_bytes(&mut seed).await; + let seed = u64::from_le_bytes(seed); + + let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + + let device = Ethernet::new( + make_static!(PacketQueue::<16, 16>::new()), + p.ETH, + Irqs, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI, + mac_addr, + 0, + ); + + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Network task initialized"); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); + info!("connecting..."); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + continue; + } + info!("connected!"); + let buf = [0; 1024]; + loop { + let r = socket.write_all(&buf).await; + if let Err(e) = r { + info!("write error: {:?}", e); + continue; + } + Timer::after(Duration::from_secs(1)).await; + } + } +} From c3774607a55141ce55d8ba462a2ebe18f80056de Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 09:37:25 -0500 Subject: [PATCH 1560/1575] stm32/eth: convert static metho --- embassy-stm32/src/eth/generic_smi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 1d83cec35..5c7856437 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -50,7 +50,7 @@ unsafe impl PHY for GenericSMI { /// PHY initialisation. fn phy_init(&mut self, sm: &mut S) { // Clear WU CSR - Self::smi_write_ext(sm, PHY_REG_WUCSR, 0); + self.smi_write_ext(sm, PHY_REG_WUCSR, 0); // Enable auto-negotiation sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M); @@ -78,7 +78,7 @@ unsafe impl PHY for GenericSMI { /// Public functions for the PHY impl GenericSMI { // Writes a value to an extended PHY register in MMD address space - fn smi_write_ext(sm: &mut S, reg_addr: u16, reg_data: u16) { + fn smi_write_ext(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { sm.smi_write(PHY_REG_CTL, 0x0003); // set address sm.smi_write(PHY_REG_ADDAR, reg_addr); sm.smi_write(PHY_REG_CTL, 0x4003); // set data From 975a780efe73b20d3ba63a116792b28f9a6edada Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 09:57:09 -0500 Subject: [PATCH 1561/1575] stm32/eth: impl. poll interval --- embassy-stm32/src/eth/generic_smi.rs | 25 ++++++++++++++++++++++++- examples/stm32f4/src/bin/eth.rs | 2 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32h5/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 5c7856437..22631c2d1 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -1,6 +1,10 @@ //! Generic SMI Ethernet PHY +#[cfg(feature = "time")] +use embassy_time::{Duration, Timer}; use futures::task::Context; +#[cfg(feature = "time")] +use futures::FutureExt; use super::{StationManagement, PHY}; @@ -38,7 +42,22 @@ mod phy_consts { use self::phy_consts::*; /// Generic SMI Ethernet PHY -pub struct GenericSMI; +pub struct GenericSMI { + #[cfg(feature = "time")] + poll_interval: Duration, +} + +impl GenericSMI { + #[cfg(feature = "time")] + pub fn new(poll_interval: Duration) -> Self { + Self { poll_interval } + } + + #[cfg(not(feature = "time"))] + pub fn new() -> Self { + Self {} + } +} unsafe impl PHY for GenericSMI { /// Reset PHY and wait for it to come out of reset. @@ -57,8 +76,12 @@ unsafe impl PHY for GenericSMI { } fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool { + #[cfg(not(feature = "time"))] cx.waker().wake_by_ref(); + #[cfg(feature = "time")] + let _ = Timer::after(self.poll_interval).poll_unpin(cx); + let bsr = sm.smi_read(PHY_REG_BSR); // No link without autonegotiate diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index c32d886d0..15390592a 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -56,7 +56,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(Duration::from_millis(500)), mac_addr, 0, ); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index fde6a7576..c1baa5848 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -57,7 +57,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(Duration::from_millis(500)), mac_addr, 0, ); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 78c8282a6..3b33265ac 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -76,7 +76,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB15, p.PG11, - GenericSMI, + GenericSMI::new(Duration::from_millis(500)), mac_addr, 0, ); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 12d37f7a4..9203708ad 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -58,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(Duration::from_millis(500)), mac_addr, 0, ); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 6078fc3fe..8abc41095 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -59,7 +59,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(Duration::from_millis(500)), mac_addr, 0, ); From 17d5e1c47046ea69ef7ac6041ae3cf3be587221b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 12:02:08 -0500 Subject: [PATCH 1562/1575] stm32/eth: add set_poll_interval --- embassy-stm32/src/eth/generic_smi.rs | 10 ++++++++-- examples/stm32f4/src/bin/eth.rs | 2 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32h5/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 22631c2d1..90631b175 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -49,8 +49,10 @@ pub struct GenericSMI { impl GenericSMI { #[cfg(feature = "time")] - pub fn new(poll_interval: Duration) -> Self { - Self { poll_interval } + pub fn new() -> Self { + Self { + poll_interval: Duration::from_millis(500), + } } #[cfg(not(feature = "time"))] @@ -100,6 +102,10 @@ unsafe impl PHY for GenericSMI { /// Public functions for the PHY impl GenericSMI { + pub fn set_poll_interval(&mut self, poll_interval: Duration) { + self.poll_interval = poll_interval + } + // Writes a value to an extended PHY register in MMD address space fn smi_write_ext(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { sm.smi_write(PHY_REG_CTL, 0x0003); // set address diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 15390592a..d0b164393 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -56,7 +56,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI::new(Duration::from_millis(500)), + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index c1baa5848..c6b2ba45c 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -57,7 +57,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI::new(Duration::from_millis(500)), + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 3b33265ac..0bff85ed8 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -76,7 +76,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB15, p.PG11, - GenericSMI::new(Duration::from_millis(500)), + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 9203708ad..cfafcaed1 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -58,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI::new(Duration::from_millis(500)), + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 8abc41095..4ed737578 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -59,7 +59,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI::new(Duration::from_millis(500)), + GenericSMI::new(), mac_addr, 0, ); From d6dd5ea5d3ab7309bd5b4bec28afaee68d20b4ae Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 14:19:32 -0500 Subject: [PATCH 1563/1575] revert toolchain changes --- rust-toolchain.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index bad6d3a42..179ed1d6a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,8 +1,8 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly" -components = [ "rust-src", "rustfmt", "llvm-tools" ] +channel = "nightly-2023-06-28" +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", "thumbv7m-none-eabi", @@ -11,4 +11,4 @@ targets = [ "thumbv8m.main-none-eabihf", "riscv32imac-unknown-none-elf", "wasm32-unknown-unknown", -] +] \ No newline at end of file From d11a94e2a7c030dac7a7c4d6967f614104111d5a Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 14:28:42 -0500 Subject: [PATCH 1564/1575] wpan: add mac test --- tests/stm32/Cargo.toml | 20 ++-- tests/stm32/src/bin/rtc.rs | 2 + .../stm32/src/bin/{tl_mbox.rs => wpan_ble.rs} | 2 +- tests/stm32/src/bin/wpan_mac.rs | 108 ++++++++++++++++++ 4 files changed, 124 insertions(+), 8 deletions(-) rename tests/stm32/src/bin/{tl_mbox.rs => wpan_ble.rs} (99%) create mode 100644 tests/stm32/src/bin/wpan_mac.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index d94bd730b..b3a805e5a 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,14 +12,15 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble" ] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "mac" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] chrono = ["embassy-stm32/chrono", "dep:chrono"] can = [] -ble = ["dep:embassy-stm32-wpan"] +ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] +mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] not-gpdma = [] [dependencies] @@ -48,11 +49,6 @@ chrono = { version = "^0.4", default-features = false, optional = true} # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. -[[bin]] -name = "tl_mbox" -path = "src/bin/tl_mbox.rs" -required-features = [ "ble",] - [[bin]] name = "can" path = "src/bin/can.rs" @@ -103,6 +99,16 @@ name = "usart_rx_ringbuffered" path = "src/bin/usart_rx_ringbuffered.rs" required-features = [ "not-gpdma",] +[[bin]] +name = "wpan_ble" +path = "src/bin/wpan_ble.rs" +required-features = [ "ble",] + +[[bin]] +name = "wpan_mac" +path = "src/bin/wpan_mac.rs" +required-features = [ "mac",] + # END TESTS [profile.dev] diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 582df5753..194b153d5 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -1,3 +1,5 @@ +// required-features: chrono + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/wpan_ble.rs similarity index 99% rename from tests/stm32/src/bin/tl_mbox.rs rename to tests/stm32/src/bin/wpan_ble.rs index af3832709..3ad8aca4e 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/wpan_ble.rs @@ -64,7 +64,7 @@ async fn main(spawner: Spawner) { version_major, version_minor, subversion, sram2a_size, sram2b_size ); - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); mbox.ble_subsystem.reset().await; diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs new file mode 100644 index 000000000..d97a4d404 --- /dev/null +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -0,0 +1,108 @@ +// required-features: mac + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; +use embassy_stm32_wpan::sub::mac::event::MacEvent; +use embassy_stm32_wpan::sub::mac::typedefs::{ + AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, +}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(config()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + mbox.mac_subsystem + .send_command(&ResetRequest { set_default_pib: true }) + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000002; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + + info!("getting extended address"); + mbox.mac_subsystem + .send_command(&GetRequest { + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + + if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } + } + + info!("assocation request"); + let a = AssociateRequest { + channel_number: MacChannel::Channel16, + channel_page: 0, + coord_addr_mode: AddressMode::Short, + coord_address: MacAddress { short: [34, 17] }, + capability_information: Capabilities::ALLOCATE_ADDRESS, + coord_pan_id: PanId([0x1A, 0xAA]), + security_level: SecurityLevel::Unsecure, + key_id_mode: KeyIdMode::Implicite, + key_source: [0; 8], + key_index: 152, + }; + info!("{}", a); + mbox.mac_subsystem.send_command(&a).await.unwrap(); + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 1f63fdbb15b2f8fb94167e12428901357de15c11 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 14:31:35 -0500 Subject: [PATCH 1565/1575] stm32/tests: fix cargo --- tests/stm32/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index b3a805e5a..3007cd1e6 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -21,6 +21,7 @@ chrono = ["embassy-stm32/chrono", "dep:chrono"] can = [] ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] +embassy-stm32-wpan = [] not-gpdma = [] [dependencies] From 3705b4f40d206490a5165a287791206ac70573d9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 14:38:02 -0500 Subject: [PATCH 1566/1575] rustfmt --- examples/stm32wb/src/bin/eddystone_beacon.rs | 2 +- examples/stm32wb/src/bin/gatt_server.rs | 2 +- examples/stm32wb/src/bin/tl_mbox_ble.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index b99f8cb2e..451bd7d29 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) { let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); mbox.ble_subsystem.reset().await; diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 7621efb11..0f6419d45 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -71,7 +71,7 @@ async fn main(_spawner: Spawner) { let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); mbox.ble_subsystem.reset().await; diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index a511e89aa..90349422e 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -49,7 +49,7 @@ async fn main(_spawner: Spawner) { let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("starting ble..."); mbox.ble_subsystem.tl_write(0x0c, &[]).await; From 4db4200c37c191f4ec018dd318e805aa805d9cc3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 14:47:34 -0500 Subject: [PATCH 1567/1575] wpan: factor mac logic into other mod --- embassy-stm32-wpan/src/lib.rs | 3 +++ embassy-stm32-wpan/src/{sub => }/mac/commands.rs | 0 embassy-stm32-wpan/src/{sub => }/mac/consts.rs | 0 embassy-stm32-wpan/src/{sub => }/mac/event.rs | 2 +- embassy-stm32-wpan/src/{sub => }/mac/helpers.rs | 0 .../src/{sub => }/mac/indications.rs | 0 embassy-stm32-wpan/src/{sub => }/mac/macros.rs | 0 embassy-stm32-wpan/src/mac/mod.rs | 9 +++++++++ embassy-stm32-wpan/src/{sub => }/mac/opcodes.rs | 0 .../src/{sub => }/mac/responses.rs | 0 embassy-stm32-wpan/src/{sub => }/mac/typedefs.rs | 0 .../src/sub/{mac/mod.rs => mac.rs} | 16 +++------------- examples/stm32wb/src/bin/mac_ffd.rs | 6 +++--- examples/stm32wb/src/bin/mac_rfd.rs | 6 +++--- tests/stm32/src/bin/wpan_mac.rs | 6 +++--- 15 files changed, 25 insertions(+), 23 deletions(-) rename embassy-stm32-wpan/src/{sub => }/mac/commands.rs (100%) rename embassy-stm32-wpan/src/{sub => }/mac/consts.rs (100%) rename embassy-stm32-wpan/src/{sub => }/mac/event.rs (99%) rename embassy-stm32-wpan/src/{sub => }/mac/helpers.rs (100%) rename embassy-stm32-wpan/src/{sub => }/mac/indications.rs (100%) rename embassy-stm32-wpan/src/{sub => }/mac/macros.rs (100%) create mode 100644 embassy-stm32-wpan/src/mac/mod.rs rename embassy-stm32-wpan/src/{sub => }/mac/opcodes.rs (100%) rename embassy-stm32-wpan/src/{sub => }/mac/responses.rs (100%) rename embassy-stm32-wpan/src/{sub => }/mac/typedefs.rs (100%) rename embassy-stm32-wpan/src/sub/{mac/mod.rs => mac.rs} (94%) diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 3a45c5978..57f0dc4fa 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -26,6 +26,9 @@ pub mod sub; pub mod tables; pub mod unsafe_linked_list; +#[cfg(feature = "mac")] +pub mod mac; + #[cfg(feature = "ble")] pub use crate::sub::ble::hci; diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/commands.rs rename to embassy-stm32-wpan/src/mac/commands.rs diff --git a/embassy-stm32-wpan/src/sub/mac/consts.rs b/embassy-stm32-wpan/src/mac/consts.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/consts.rs rename to embassy-stm32-wpan/src/mac/consts.rs diff --git a/embassy-stm32-wpan/src/sub/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs similarity index 99% rename from embassy-stm32-wpan/src/sub/mac/event.rs rename to embassy-stm32-wpan/src/mac/event.rs index aaf965565..dfce21fea 100644 --- a/embassy-stm32-wpan/src/sub/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -7,7 +7,7 @@ use super::responses::{ AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, }; -use crate::sub::mac::opcodes::OpcodeM0ToM4; +use crate::mac::opcodes::OpcodeM0ToM4; pub trait ParseableMacEvent { const SIZE: usize; diff --git a/embassy-stm32-wpan/src/sub/mac/helpers.rs b/embassy-stm32-wpan/src/mac/helpers.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/helpers.rs rename to embassy-stm32-wpan/src/mac/helpers.rs diff --git a/embassy-stm32-wpan/src/sub/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/indications.rs rename to embassy-stm32-wpan/src/mac/indications.rs diff --git a/embassy-stm32-wpan/src/sub/mac/macros.rs b/embassy-stm32-wpan/src/mac/macros.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/macros.rs rename to embassy-stm32-wpan/src/mac/macros.rs diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs new file mode 100644 index 000000000..1af8fe6ba --- /dev/null +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -0,0 +1,9 @@ +pub mod commands; +mod consts; +pub mod event; +mod helpers; +pub mod indications; +mod macros; +mod opcodes; +pub mod responses; +pub mod typedefs; diff --git a/embassy-stm32-wpan/src/sub/mac/opcodes.rs b/embassy-stm32-wpan/src/mac/opcodes.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/opcodes.rs rename to embassy-stm32-wpan/src/mac/opcodes.rs diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/responses.rs rename to embassy-stm32-wpan/src/mac/responses.rs diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs similarity index 100% rename from embassy-stm32-wpan/src/sub/mac/typedefs.rs rename to embassy-stm32-wpan/src/mac/typedefs.rs diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac.rs similarity index 94% rename from embassy-stm32-wpan/src/sub/mac/mod.rs rename to embassy-stm32-wpan/src/sub/mac.rs index ab39f89c2..4893cb47b 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -8,25 +8,15 @@ use embassy_futures::poll_once; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; -use self::commands::MacCommand; -use self::event::MacEvent; -use self::typedefs::MacError; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; +use crate::mac::commands::MacCommand; +use crate::mac::event::MacEvent; +use crate::mac::typedefs::MacError; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; -pub mod commands; -mod consts; -pub mod event; -mod helpers; -pub mod indications; -mod macros; -mod opcodes; -pub mod responses; -pub mod typedefs; - static MAC_WAKER: AtomicWaker = AtomicWaker::new(); static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 689a28353..e4d81997e 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -6,9 +6,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::sub::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::sub::mac::event::MacEvent; -use embassy_stm32_wpan::sub::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; +use embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::mac::event::MacEvent; +use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index ea349f9a8..b2dac72cc 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,9 +6,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; -use embassy_stm32_wpan::sub::mac::event::MacEvent; -use embassy_stm32_wpan::sub::mac::typedefs::{ +use embassy_stm32_wpan::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; +use embassy_stm32_wpan::mac::event::MacEvent; +use embassy_stm32_wpan::mac::typedefs::{ AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, }; use embassy_stm32_wpan::sub::mm; diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index d97a4d404..cfa0aca3b 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -10,9 +10,9 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; -use embassy_stm32_wpan::sub::mac::event::MacEvent; -use embassy_stm32_wpan::sub::mac::typedefs::{ +use embassy_stm32_wpan::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; +use embassy_stm32_wpan::mac::event::MacEvent; +use embassy_stm32_wpan::mac::typedefs::{ AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, }; use embassy_stm32_wpan::sub::mm; From 88d1976e812af995028025407872bf64264c4f94 Mon Sep 17 00:00:00 2001 From: maximedeboeck <119764381+maximedeboeck@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:31:56 +0200 Subject: [PATCH 1568/1575] Added usb-hid keyboard example for rp pico. --- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/usb_hid_keyboard.rs | 188 ++++++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 examples/rp/src/bin/usb_hid_keyboard.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 7c5a9dfbc..c812cb3ee 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -40,6 +40,7 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" heapless = "0.7.15" +usbd-hid = "0.6.1" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-async = "0.2.0-alpha.2" diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..99af1f02f --- /dev/null +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -0,0 +1,188 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::{Input, Pull}; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::control::OutResponse; +use embassy_usb::{Builder, Config, Handler}; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("HID keyboard example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + // You can also add a Microsoft OS descriptor. + // let mut msos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + let request_handler = MyRequestHandler {}; + let mut device_handler = MyDeviceHandler::new(); + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + // &mut msos_descriptor, + &mut control_buf, + ); + + builder.handler(&mut device_handler); + + // Create classes on the builder. + let config = embassy_usb::class::hid::Config { + report_descriptor: KeyboardReport::desc(), + request_handler: Some(&request_handler), + poll_ms: 60, + max_packet_size: 64, + }; + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Set up the signal pin that will be used to trigger the keyboard. + let mut signal_pin = Input::new(p.PIN_16, Pull::None); + + let (reader, mut writer) = hid.split(); + + // Do stuff with the class! + let in_fut = async { + loop { + info!("Waiting for HIGH on pin 16"); + signal_pin.wait_for_high().await; + info!("HIGH DETECTED"); + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + signal_pin.wait_for_low().await; + info!("LOW DETECTED"); + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + }; + + let out_fut = async { + reader.run(false, &request_handler).await; + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, join(in_fut, out_fut)).await; +} + +struct MyRequestHandler {} + +impl RequestHandler for MyRequestHandler { + fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { + info!("Set report for {:?}: {=[u8]}", id, data); + OutResponse::Accepted + } + + fn set_idle_ms(&self, id: Option, dur: u32) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle_ms(&self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} + +struct MyDeviceHandler { + configured: AtomicBool, +} + +impl MyDeviceHandler { + fn new() -> Self { + MyDeviceHandler { + configured: AtomicBool::new(false), + } + } +} + +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { + self.configured.store(false, Ordering::Relaxed); + if enabled { + info!("Device enabled"); + } else { + info!("Device disabled"); + } + } + + fn reset(&mut self) { + self.configured.store(false, Ordering::Relaxed); + info!("Bus reset, the Vbus current limit is 100mA"); + } + + fn addressed(&mut self, addr: u8) { + self.configured.store(false, Ordering::Relaxed); + info!("USB address set to: {}", addr); + } + + fn configured(&mut self, configured: bool) { + self.configured.store(configured, Ordering::Relaxed); + if configured { + info!("Device configured, it may now draw up to the configured current limit from Vbus.") + } else { + info!("Device is no longer configured, the Vbus current limit is 100mA."); + } + } +} From e95a7dc555f367534d3b8bc7a9b6f2d361b0d951 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 12:41:57 -0500 Subject: [PATCH 1569/1575] wpan/mac: use slice view to avoid copy --- embassy-stm32-wpan/src/mac/commands.rs | 83 ++++++++++++++------------ embassy-stm32-wpan/src/mac/typedefs.rs | 2 + embassy-stm32-wpan/src/sub/mac.rs | 9 +-- examples/stm32wb/src/bin/mac_ffd.rs | 5 +- examples/stm32wb/src/bin/mac_rfd.rs | 35 ++++++----- tests/stm32/src/bin/wpan_mac.rs | 6 +- 6 files changed, 77 insertions(+), 63 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index 8cfa0a054..8acae24bb 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -1,15 +1,16 @@ +use core::{mem, slice}; + use super::opcodes::OpcodeM4ToM0; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, PanId, PibId, ScanType, SecurityLevel, }; -pub trait MacCommand { +pub trait MacCommand: Sized { const OPCODE: OpcodeM4ToM0; - const SIZE: usize; - fn copy_into_slice(&self, buf: &mut [u8]) { - unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, Self::SIZE) }; + fn payload<'a>(&'a self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::()) } } } @@ -41,7 +42,6 @@ pub struct AssociateRequest { impl MacCommand for AssociateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq; - const SIZE: usize = 25; } /// MLME DISASSOCIATE Request sed to request a disassociation @@ -70,20 +70,22 @@ pub struct DisassociateRequest { impl MacCommand for DisassociateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq; - const SIZE: usize = 24; } /// MLME GET Request used to request a PIB value #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetRequest { /// the name of the PIB attribute to read pub pib_attribute: PibId, + + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for GetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; - const SIZE: usize = 4; } /// MLME GTS Request used to request and maintain GTSs @@ -104,19 +106,20 @@ pub struct GtsRequest { impl MacCommand for GtsRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; - const SIZE: usize = 12; } #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetRequest { /// MAC PIB attributes are set to their default values or not during reset pub set_default_pib: bool, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for ResetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq; - const SIZE: usize = 4; } /// MLME RX ENABLE Request used to request that the receiver is either enabled @@ -129,6 +132,8 @@ pub struct RxEnableRequest { /// configure the transceiver to RX with ranging for a value of /// RANGING_ON or to not enable ranging for RANGING_OFF pub ranging_rx_control: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], /// number of symbols measured before the receiver is to be enabled or disabled pub rx_on_time: [u8; 4], /// number of symbols for which the receiver is to be enabled @@ -137,19 +142,6 @@ pub struct RxEnableRequest { impl MacCommand for RxEnableRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq; - const SIZE: usize = 12; - - fn copy_into_slice(&self, buf: &mut [u8]) { - buf[0] = self.defer_permit as u8; - buf[1] = self.ranging_rx_control as u8; - - // stuffing to keep 32bit alignment - buf[2] = 0; - buf[3] = 0; - - buf[4..8].copy_from_slice(&self.rx_on_time); - buf[8..12].copy_from_slice(&self.rx_on_duration); - } } /// MLME SCAN Request used to initiate a channel scan over a given list of channels @@ -172,11 +164,12 @@ pub struct ScanRequest { pub key_id_mode: KeyIdMode, /// index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for ScanRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq; - const SIZE: usize = 20; } /// MLME SET Request used to attempt to write the given value to the indicated PIB attribute @@ -191,13 +184,12 @@ pub struct SetRequest { impl MacCommand for SetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq; - const SIZE: usize = 8; } /// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe /// configuration -#[derive(Default)] #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartRequest { /// PAN indentifier to used by the device @@ -236,7 +228,6 @@ pub struct StartRequest { impl MacCommand for StartRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq; - const SIZE: usize = 35; } /// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if @@ -253,11 +244,12 @@ pub struct SyncRequest { /// /// `false` if the MLME is to synchronize with only the next beacon pub track_beacon: bool, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 1], } impl MacCommand for SyncRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq; - const SIZE: usize = 4; } /// MLME POLL Request propmts the device to request data from the coordinator @@ -278,11 +270,12 @@ pub struct PollRequest { pub key_source: [u8; 8], /// PAN identifier of the coordinator pub coord_pan_id: PanId, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for PollRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq; - const SIZE: usize = 24; } /// MLME DPS Request allows the next higher layer to request that the PHY utilize a @@ -297,33 +290,38 @@ pub struct DpsRequest { /// the number of symbols for which the transmitter and receiver will utilize the /// respective DPS indices dps_index_duration: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 1], } impl MacCommand for DpsRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq; - const SIZE: usize = 4; } /// MLME SOUNDING request primitive which is used by the next higher layer to request that /// the PHY respond with channel sounding information #[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SoundingRequest; +pub struct SoundingRequest { + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 4], +} impl MacCommand for SoundingRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq; - const SIZE: usize = 4; } /// MLME CALIBRATE request primitive which used to obtain the results of a ranging /// calibration request from an RDEV #[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CalibrateRequest; +pub struct CalibrateRequest { + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 4], +} impl MacCommand for CalibrateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq; - const SIZE: usize = 4; } /// MCPS DATA Request used for MAC data related requests from the application @@ -370,6 +368,15 @@ pub struct DataRequest { pub datrate: u8, } +impl DataRequest { + pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self { + self.msdu_ptr = &buf as *const _ as *const u8; + self.msdu_length = buf.len() as u8; + + self + } +} + impl Default for DataRequest { fn default() -> Self { Self { @@ -397,7 +404,6 @@ impl Default for DataRequest { impl MacCommand for DataRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq; - const SIZE: usize = 40; } /// for MCPS PURGE Request used to purge an MSDU from the transaction queue @@ -407,11 +413,12 @@ pub struct PurgeRequest { /// the handle associated with the MSDU to be purged from the transaction /// queue pub msdu_handle: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for PurgeRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq; - const SIZE: usize = 4; } /// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication @@ -434,11 +441,12 @@ pub struct AssociateResponse { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for AssociateResponse { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes; - const SIZE: usize = 24; } /// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication @@ -459,9 +467,10 @@ pub struct OrphanResponse { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for OrphanResponse { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes; - const SIZE: usize = 24; } diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 30c7731b2..98c67c86b 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -37,9 +37,11 @@ numeric_enum! { numeric_enum! { #[repr(u8)] /// this enum contains all the MAC PIB Ids + #[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PibId { // PHY + #[default] CurrentChannel = 0x00, ChannelsSupported = 0x01, TransmitPower = 0x02, diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index 4893cb47b..d30ed2f11 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -85,12 +85,7 @@ impl Mac { where T: MacCommand, { - let mut payload = [0u8; MAX_PACKET_SIZE]; - cmd.copy_into_slice(&mut payload); - - let response = self - .tl_write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]) - .await; + let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await; if response == 0x00 { Ok(()) @@ -107,8 +102,6 @@ impl Mac { } } -const MAX_PACKET_SIZE: usize = 255; - impl evt::MemoryManager for Mac { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(_: *mut EvtPacket) { diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index e4d81997e..f8c8ba288 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -67,7 +67,10 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index b2dac72cc..b0eb91061 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -69,7 +69,10 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; @@ -91,6 +94,7 @@ async fn main(spawner: Spawner) { mbox.mac_subsystem .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, + ..Default::default() }) .await .unwrap(); @@ -141,23 +145,22 @@ async fn main(spawner: Spawner) { info!("{:#x}", evt); info!("sending data"); - let mut data_buffer = [0u8; 256]; let data = b"Hello from embassy!"; - data_buffer[..data.len()].copy_from_slice(data); mbox.mac_subsystem - .send_command(&DataRequest { - src_addr_mode: AddressMode::Short, - dst_addr_mode: AddressMode::Short, - dst_pan_id: PanId([0x1A, 0xAA]), - dst_address: MacAddress::BROADCAST, - msdu_handle: 0x02, - ack_tx: 0x00, - gts_tx: false, - msdu_ptr: &data_buffer as *const _ as *const u8, - msdu_length: data.len() as u8, - security_level: SecurityLevel::Unsecure, - ..Default::default() - }) + .send_command( + DataRequest { + src_addr_mode: AddressMode::Short, + dst_addr_mode: AddressMode::Short, + dst_pan_id: PanId([0x1A, 0xAA]), + dst_address: MacAddress::BROADCAST, + msdu_handle: 0x02, + ack_tx: 0x00, + gts_tx: false, + security_level: SecurityLevel::Unsecure, + ..Default::default() + } + .set_buffer(data), + ) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index cfa0aca3b..2fc15dc9d 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -49,7 +49,10 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; @@ -71,6 +74,7 @@ async fn main(spawner: Spawner) { mbox.mac_subsystem .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, + ..Default::default() }) .await .unwrap(); From 7c465465c1a97234c3fbeb18154bfd7f79ab07f2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 13:59:15 -0500 Subject: [PATCH 1570/1575] wpan: use builtin conversion methods --- embassy-stm32-wpan/src/mac/event.rs | 4 ++-- embassy-stm32-wpan/src/mac/helpers.rs | 7 ------- embassy-stm32-wpan/src/mac/indications.rs | 13 ++++++------- embassy-stm32-wpan/src/mac/mod.rs | 1 - embassy-stm32-wpan/src/mac/responses.rs | 15 +++++++-------- 5 files changed, 15 insertions(+), 25 deletions(-) delete mode 100644 embassy-stm32-wpan/src/mac/helpers.rs diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index dfce21fea..c2bdc7e11 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,4 +1,3 @@ -use super::helpers::to_u16; use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, @@ -58,7 +57,8 @@ impl TryFrom<&[u8]> for MacEvent { type Error = (); fn try_from(value: &[u8]) -> Result { - let opcode = to_u16(&value[0..2]); + let opcode = u16::from_le_bytes(value[0..2].try_into().unwrap()); + let opcode = OpcodeM0ToM4::try_from(opcode)?; let buf = &value[2..]; diff --git a/embassy-stm32-wpan/src/mac/helpers.rs b/embassy-stm32-wpan/src/mac/helpers.rs deleted file mode 100644 index 5a5bf8a85..000000000 --- a/embassy-stm32-wpan/src/mac/helpers.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub fn to_u16(buf: &[u8]) -> u16 { - ((buf[1] as u16) << 8) | buf[0] as u16 -} - -pub fn to_u32(buf: &[u8]) -> u32 { - ((buf[0] as u32) << 0) + ((buf[1] as u32) << 8) + ((buf[2] as u32) << 16) + ((buf[3] as u32) << 24) -} diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 6df4aa23a..436b9ac9c 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -1,6 +1,5 @@ use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; -use super::helpers::to_u32; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, PanId, SecurityLevel, @@ -114,7 +113,7 @@ impl ParseableMacEvent for BeaconNotifyIndication { ]; Ok(Self { - sdu_ptr: to_u32(&buf[0..4]) as *const u8, + sdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, pan_descriptor: PanDescriptor::try_from(&buf[4..26])?, addr_list, bsn: buf[82], @@ -405,7 +404,7 @@ impl ParseableMacEvent for DataIndication { }; Ok(Self { - msdu_ptr: to_u32(&buf[0..4]) as *const u8, + msdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, src_addr_mode, src_pan_id: PanId([buf[5], buf[6]]), src_address, @@ -424,10 +423,10 @@ impl ParseableMacEvent for DataIndication { uwn_preamble_symbol_repetitions: buf[45], datrate: buf[46], ranging_received: buf[47], - ranging_counter_start: to_u32(&buf[48..52]), - ranging_counter_stop: to_u32(&buf[52..56]), - ranging_tracking_interval: to_u32(&buf[56..60]), - ranging_offset: to_u32(&buf[60..64]), + ranging_counter_start: u32::from_le_bytes(buf[48..52].try_into().unwrap()), + ranging_counter_stop: u32::from_le_bytes(buf[52..56].try_into().unwrap()), + ranging_tracking_interval: u32::from_le_bytes(buf[56..60].try_into().unwrap()), + ranging_offset: u32::from_le_bytes(buf[60..64].try_into().unwrap()), ranging_fom: buf[65], rssi: buf[66], }) diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index 1af8fe6ba..8d5edad6b 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -1,7 +1,6 @@ pub mod commands; mod consts; pub mod event; -mod helpers; pub mod indications; mod macros; mod opcodes; diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index 2f6f5bf58..d29257f84 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -1,6 +1,5 @@ use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED}; use super::event::ParseableMacEvent; -use super::helpers::to_u32; use super::typedefs::{ AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType, SecurityLevel, @@ -100,7 +99,7 @@ impl ParseableMacEvent for GetConfirm { fn try_parse(buf: &[u8]) -> Result { Self::validate(buf)?; - let address = to_u32(&buf[0..4]); + let address = u32::from_le_bytes(buf[0..4].try_into().unwrap()); Ok(Self { pib_attribute_value_ptr: address as *const u8, @@ -357,8 +356,8 @@ impl ParseableMacEvent for CalibrateConfirm { Ok(Self { status: MacStatus::try_from(buf[0])?, // 3 byte stuffing - cal_tx_rmaker_offset: to_u32(&buf[4..8]), - cal_rx_rmaker_offset: to_u32(&buf[8..12]), + cal_tx_rmaker_offset: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + cal_rx_rmaker_offset: u32::from_le_bytes(buf[8..12].try_into().unwrap()), }) } } @@ -400,10 +399,10 @@ impl ParseableMacEvent for DataConfirm { time_stamp: [buf[1], buf[2], buf[3], buf[4]], ranging_received: buf[5], status: MacStatus::try_from(buf[6])?, - ranging_counter_start: to_u32(&buf[7..11]), - ranging_counter_stop: to_u32(&buf[11..15]), - ranging_tracking_interval: to_u32(&buf[15..19]), - ranging_offset: to_u32(&buf[19..23]), + ranging_counter_start: u32::from_le_bytes(buf[7..11].try_into().unwrap()), + ranging_counter_stop: u32::from_le_bytes(buf[11..15].try_into().unwrap()), + ranging_tracking_interval: u32::from_le_bytes(buf[15..19].try_into().unwrap()), + ranging_offset: u32::from_le_bytes(buf[19..23].try_into().unwrap()), ranging_fom: buf[24], }) } From 28b419d65ede6ff29c79dbcaa27145f1c3458a57 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 15:09:30 -0500 Subject: [PATCH 1571/1575] wpan/mac: use lifetimes to control events --- embassy-stm32-wpan/src/mac/event.rs | 169 ++++++++------ embassy-stm32-wpan/src/mac/indications.rs | 269 ++------------------- embassy-stm32-wpan/src/mac/responses.rs | 271 ++++------------------ embassy-stm32-wpan/src/sub/mac.rs | 9 +- examples/stm32wb/src/bin/mac_ffd.rs | 46 ++-- examples/stm32wb/src/bin/mac_rfd.rs | 55 +++-- tests/stm32/src/bin/wpan_mac.rs | 32 ++- 7 files changed, 243 insertions(+), 608 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index c2bdc7e11..67f207d57 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,3 +1,5 @@ +use core::mem; + use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, @@ -6,89 +8,110 @@ use super::responses::{ AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, }; +use crate::evt::EvtBox; use crate::mac::opcodes::OpcodeM0ToM4; +use crate::sub::mac::Mac; -pub trait ParseableMacEvent { - const SIZE: usize; - - fn validate(buf: &[u8]) -> Result<(), ()> { - if buf.len() < Self::SIZE { - return Err(()); +pub(crate) trait ParseableMacEvent: Sized { + fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { + if buf.len() < mem::size_of::() { + Err(()) + } else { + Ok(unsafe { &*(buf as *const _ as *const Self) }) } + } +} - Ok(()) +pub struct Event { + event_box: EvtBox, +} + +impl Event { + pub(crate) fn new(event_box: EvtBox) -> Self { + Self { event_box } } - fn try_parse(buf: &[u8]) -> Result - where - Self: Sized; -} - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MacEvent { - MlmeAssociateCnf(AssociateConfirm), - MlmeDisassociateCnf(DisassociateConfirm), - MlmeGetCnf(GetConfirm), - MlmeGtsCnf(GtsConfirm), - MlmeResetCnf(ResetConfirm), - MlmeRxEnableCnf(RxEnableConfirm), - MlmeScanCnf(ScanConfirm), - MlmeSetCnf(SetConfirm), - MlmeStartCnf(StartConfirm), - MlmePollCnf(PollConfirm), - MlmeDpsCnf(DpsConfirm), - MlmeSoundingCnf(SoundingConfirm), - MlmeCalibrateCnf(CalibrateConfirm), - McpsDataCnf(DataConfirm), - McpsPurgeCnf(PurgeConfirm), - MlmeAssociateInd(AssociateIndication), - MlmeDisassociateInd(DisassociateIndication), - MlmeBeaconNotifyInd(BeaconNotifyIndication), - MlmeCommStatusInd(CommStatusIndication), - MlmeGtsInd(GtsIndication), - MlmeOrphanInd(OrphanIndication), - MlmeSyncLossInd(SyncLossIndication), - MlmeDpsInd(DpsIndication), - McpsDataInd(DataIndication), - MlmePollInd(PollIndication), -} - -impl TryFrom<&[u8]> for MacEvent { - type Error = (); - - fn try_from(value: &[u8]) -> Result { - let opcode = u16::from_le_bytes(value[0..2].try_into().unwrap()); + pub fn mac_event<'a>(&'a self) -> Result, ()> { + let payload = self.event_box.payload(); + let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); let opcode = OpcodeM0ToM4::try_from(opcode)?; - let buf = &value[2..]; - match opcode { - OpcodeM0ToM4::MlmeAssociateCnf => Ok(Self::MlmeAssociateCnf(AssociateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDisassociateCnf => Ok(Self::MlmeDisassociateCnf(DisassociateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGetCnf => Ok(Self::MlmeGetCnf(GetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGtsCnf => Ok(Self::MlmeGtsCnf(GtsConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeResetCnf => Ok(Self::MlmeResetCnf(ResetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeRxEnableCnf => Ok(Self::MlmeRxEnableCnf(RxEnableConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeScanCnf => Ok(Self::MlmeScanCnf(ScanConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSetCnf => Ok(Self::MlmeSetCnf(SetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeStartCnf => Ok(Self::MlmeStartCnf(StartConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmePollCnf => Ok(Self::MlmePollCnf(PollConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDpsCnf => Ok(Self::MlmeDpsCnf(DpsConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSoundingCnf => Ok(Self::MlmeSoundingCnf(SoundingConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeCalibrateCnf => Ok(Self::MlmeCalibrateCnf(CalibrateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::McpsDataCnf => Ok(Self::McpsDataCnf(DataConfirm::try_parse(buf)?)), - OpcodeM0ToM4::McpsPurgeCnf => Ok(Self::McpsPurgeCnf(PurgeConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeAssociateInd => Ok(Self::MlmeAssociateInd(AssociateIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDisassociateInd => Ok(Self::MlmeDisassociateInd(DisassociateIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(Self::MlmeBeaconNotifyInd(BeaconNotifyIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeCommStatusInd => Ok(Self::MlmeCommStatusInd(CommStatusIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGtsInd => Ok(Self::MlmeGtsInd(GtsIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeOrphanInd => Ok(Self::MlmeOrphanInd(OrphanIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSyncLossInd => Ok(Self::MlmeSyncLossInd(SyncLossIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDpsInd => Ok(Self::MlmeDpsInd(DpsIndication::try_parse(buf)?)), - OpcodeM0ToM4::McpsDataInd => Ok(Self::McpsDataInd(DataIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmePollInd => Ok(Self::MlmePollInd(PollIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeAssociateCnf => Ok(MacEvent::MlmeAssociateCnf(AssociateConfirm::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeDisassociateCnf => Ok(MacEvent::MlmeDisassociateCnf(DisassociateConfirm::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeGetCnf => Ok(MacEvent::MlmeGetCnf(GetConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeGtsCnf => Ok(MacEvent::MlmeGtsCnf(GtsConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeResetCnf => Ok(MacEvent::MlmeResetCnf(ResetConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeRxEnableCnf => { + Ok(MacEvent::MlmeRxEnableCnf(RxEnableConfirm::from_buffer(&payload[2..])?)) + } + OpcodeM0ToM4::MlmeScanCnf => Ok(MacEvent::MlmeScanCnf(ScanConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeSetCnf => Ok(MacEvent::MlmeSetCnf(SetConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeStartCnf => Ok(MacEvent::MlmeStartCnf(StartConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmePollCnf => Ok(MacEvent::MlmePollCnf(PollConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeDpsCnf => Ok(MacEvent::MlmeDpsCnf(DpsConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeSoundingCnf => { + Ok(MacEvent::MlmeSoundingCnf(SoundingConfirm::from_buffer(&payload[2..])?)) + } + OpcodeM0ToM4::MlmeCalibrateCnf => Ok(MacEvent::MlmeCalibrateCnf(CalibrateConfirm::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::McpsDataCnf => Ok(MacEvent::McpsDataCnf(DataConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::McpsPurgeCnf => Ok(MacEvent::McpsPurgeCnf(PurgeConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeAssociateInd => Ok(MacEvent::MlmeAssociateInd(AssociateIndication::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeDisassociateInd => Ok(MacEvent::MlmeDisassociateInd( + DisassociateIndication::from_buffer(&payload[2..])?, + )), + OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(MacEvent::MlmeBeaconNotifyInd( + BeaconNotifyIndication::from_buffer(&payload[2..])?, + )), + OpcodeM0ToM4::MlmeCommStatusInd => Ok(MacEvent::MlmeCommStatusInd(CommStatusIndication::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeGtsInd => Ok(MacEvent::MlmeGtsInd(GtsIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeOrphanInd => Ok(MacEvent::MlmeOrphanInd(OrphanIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeSyncLossInd => Ok(MacEvent::MlmeSyncLossInd(SyncLossIndication::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeDpsInd => Ok(MacEvent::MlmeDpsInd(DpsIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::McpsDataInd => Ok(MacEvent::McpsDataInd(DataIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmePollInd => Ok(MacEvent::MlmePollInd(PollIndication::from_buffer(&payload[2..])?)), } } } + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacEvent<'a> { + MlmeAssociateCnf(&'a AssociateConfirm), + MlmeDisassociateCnf(&'a DisassociateConfirm), + MlmeGetCnf(&'a GetConfirm), + MlmeGtsCnf(&'a GtsConfirm), + MlmeResetCnf(&'a ResetConfirm), + MlmeRxEnableCnf(&'a RxEnableConfirm), + MlmeScanCnf(&'a ScanConfirm), + MlmeSetCnf(&'a SetConfirm), + MlmeStartCnf(&'a StartConfirm), + MlmePollCnf(&'a PollConfirm), + MlmeDpsCnf(&'a DpsConfirm), + MlmeSoundingCnf(&'a SoundingConfirm), + MlmeCalibrateCnf(&'a CalibrateConfirm), + McpsDataCnf(&'a DataConfirm), + McpsPurgeCnf(&'a PurgeConfirm), + MlmeAssociateInd(&'a AssociateIndication), + MlmeDisassociateInd(&'a DisassociateIndication), + MlmeBeaconNotifyInd(&'a BeaconNotifyIndication), + MlmeCommStatusInd(&'a CommStatusIndication), + MlmeGtsInd(&'a GtsIndication), + MlmeOrphanInd(&'a OrphanIndication), + MlmeSyncLossInd(&'a SyncLossIndication), + MlmeDpsInd(&'a DpsIndication), + McpsDataInd(&'a DataIndication), + MlmePollInd(&'a PollIndication), +} diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 436b9ac9c..c7e9be84a 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -23,22 +23,7 @@ pub struct AssociateIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for AssociateIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - capability_information: Capabilities::from_bits(buf[8]).ok_or(())?, - security_level: SecurityLevel::try_from(buf[9])?, - key_id_mode: KeyIdMode::try_from(buf[10])?, - key_index: buf[11], - key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }) - } -} +impl ParseableMacEvent for AssociateIndication {} /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. @@ -58,22 +43,7 @@ pub struct DisassociateIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for DisassociateIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - disassociation_reason: DisassociationReason::try_from(buf[8])?, - security_level: SecurityLevel::try_from(buf[9])?, - key_id_mode: KeyIdMode::try_from(buf[10])?, - key_index: buf[11], - key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }) - } -} +impl ParseableMacEvent for DisassociateIndication {} /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application @@ -94,34 +64,7 @@ pub struct BeaconNotifyIndication { pub sdu_length: u8, } -impl ParseableMacEvent for BeaconNotifyIndication { - const SIZE: usize = 88; - - fn try_parse(buf: &[u8]) -> Result { - // TODO: this is unchecked - - Self::validate(buf)?; - - let addr_list = [ - MacAddress::try_from(&buf[26..34])?, - MacAddress::try_from(&buf[34..42])?, - MacAddress::try_from(&buf[42..50])?, - MacAddress::try_from(&buf[50..58])?, - MacAddress::try_from(&buf[58..66])?, - MacAddress::try_from(&buf[66..74])?, - MacAddress::try_from(&buf[74..82])?, - ]; - - Ok(Self { - sdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, - pan_descriptor: PanDescriptor::try_from(&buf[4..26])?, - addr_list, - bsn: buf[82], - pend_addr_spec: buf[83], - sdu_length: buf[83], - }) - } -} +impl ParseableMacEvent for BeaconNotifyIndication {} /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -149,51 +92,7 @@ pub struct CommStatusIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for CommStatusIndication { - const SIZE: usize = 32; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let src_addr_mode = AddressMode::try_from(buf[2])?; - let dst_addr_mode = AddressMode::try_from(buf[3])?; - - let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[4], buf[5]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - }, - }; - - let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[12], buf[13]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }, - }; - - Ok(Self { - pan_id: PanId([buf[0], buf[1]]), - src_addr_mode, - dst_addr_mode, - src_address, - dst_address, - status: MacStatus::try_from(buf[20])?, - security_level: SecurityLevel::try_from(buf[21])?, - key_id_mode: KeyIdMode::try_from(buf[22])?, - key_index: buf[23], - key_source: [buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]], - }) - } -} +impl ParseableMacEvent for CommStatusIndication {} /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated @@ -209,27 +108,13 @@ pub struct GtsIndication { pub key_id_mode: KeyIdMode, /// Index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], /// Originator of the key to be used pub key_source: [u8; 8], } -impl ParseableMacEvent for GtsIndication { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1]], - gts_characteristics: buf[2], - security_level: SecurityLevel::try_from(buf[3])?, - key_id_mode: KeyIdMode::try_from(buf[4])?, - key_index: buf[5], - // 2 byte stuffing - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - }) - } -} +impl ParseableMacEvent for GtsIndication {} /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device @@ -245,24 +130,11 @@ pub struct OrphanIndication { pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 1], } -impl ParseableMacEvent for OrphanIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - orphan_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - security_level: SecurityLevel::try_from(buf[16])?, - key_id_mode: KeyIdMode::try_from(buf[17])?, - key_index: buf[18], - // 1 byte stuffing - }) - } -} +impl ParseableMacEvent for OrphanIndication {} /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator @@ -286,42 +158,20 @@ pub struct SyncLossIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for SyncLossIndication { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - pan_id: PanId([buf[0], buf[1]]), - loss_reason: buf[2], - channel_number: MacChannel::try_from(buf[3])?, - channel_page: buf[4], - security_level: SecurityLevel::try_from(buf[5])?, - key_id_mode: KeyIdMode::try_from(buf[6])?, - key_index: buf[7], - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - }) - } -} +impl ParseableMacEvent for SyncLossIndication {} /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DpsIndication; - -impl ParseableMacEvent for DpsIndication { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self) - } +pub struct DpsIndication { + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 4], } +impl ParseableMacEvent for DpsIndication {} + #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C, align(8))] +#[repr(C)] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, @@ -373,65 +223,7 @@ pub struct DataIndication { pub rssi: u8, } -impl ParseableMacEvent for DataIndication { - const SIZE: usize = 68; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let src_addr_mode = AddressMode::try_from(buf[4])?; - let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[7], buf[8]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14]], - }, - }; - - let dst_addr_mode = AddressMode::try_from(buf[15])?; - let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[18], buf[19]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25]], - }, - }; - - Ok(Self { - msdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, - src_addr_mode, - src_pan_id: PanId([buf[5], buf[6]]), - src_address, - dst_addr_mode, - dst_pan_id: PanId([buf[16], buf[17]]), - dst_address, - msdu_length: buf[26], - mpdu_link_quality: buf[27], - dsn: buf[28], - time_stamp: [buf[29], buf[30], buf[31], buf[32]], - security_level: SecurityLevel::try_from(buf[33]).unwrap_or(SecurityLevel::Unsecure), // TODO: this is totaly wrong, but I'm too smol brain to fix it - key_id_mode: KeyIdMode::try_from(buf[34]).unwrap_or(KeyIdMode::Implicite), // TODO: this is totaly wrong, but I'm too smol brain to fix it - key_source: [buf[35], buf[36], buf[37], buf[38], buf[39], buf[40], buf[41], buf[42]], - key_index: buf[43], - uwbprf: buf[44], - uwn_preamble_symbol_repetitions: buf[45], - datrate: buf[46], - ranging_received: buf[47], - ranging_counter_start: u32::from_le_bytes(buf[48..52].try_into().unwrap()), - ranging_counter_stop: u32::from_le_bytes(buf[52..56].try_into().unwrap()), - ranging_tracking_interval: u32::from_le_bytes(buf[56..60].try_into().unwrap()), - ranging_offset: u32::from_le_bytes(buf[60..64].try_into().unwrap()), - ranging_fom: buf[65], - rssi: buf[66], - }) - } -} +impl ParseableMacEvent for DataIndication {} /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 @@ -443,27 +235,4 @@ pub struct PollIndication { pub request_address: MacAddress, } -impl ParseableMacEvent for PollIndication { - const SIZE: usize = 9; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let addr_mode = AddressMode::try_from(buf[0])?; - let request_address = match addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[1], buf[2]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]], - }, - }; - - Ok(Self { - addr_mode, - request_address, - }) - } -} +impl ParseableMacEvent for PollIndication {} diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index d29257f84..e0376a7f5 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -21,24 +21,11 @@ pub struct AssociateConfirm { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for AssociateConfirm { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - assoc_short_address: [buf[0], buf[1]], - status: AssociationStatus::try_from(buf[2])?, - security_level: SecurityLevel::try_from(buf[3])?, - key_source: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - key_id_mode: KeyIdMode::try_from(buf[12])?, - key_index: buf[13], - }) - } -} +impl ParseableMacEvent for AssociateConfirm {} /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -53,32 +40,7 @@ pub struct DisassociateConfirm { pub device_address: MacAddress, } -impl ParseableMacEvent for DisassociateConfirm { - const SIZE: usize = 12; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let device_addr_mode = AddressMode::try_from(buf[1])?; - let device_address = match device_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[4], buf[5]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - }, - }; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - device_addr_mode, - device_pan_id: PanId([buf[2], buf[3]]), - device_address, - }) - } -} +impl ParseableMacEvent for DisassociateConfirm {} /// MLME GET Confirm which requests information about a given PIB attribute #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -91,24 +53,11 @@ pub struct GetConfirm { pub pib_attribute: PibId, /// The lenght of the PIB attribute Value return pub pib_attribute_value_len: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 1], } -impl ParseableMacEvent for GetConfirm { - const SIZE: usize = 8; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let address = u32::from_le_bytes(buf[0..4].try_into().unwrap()); - - Ok(Self { - pib_attribute_value_ptr: address as *const u8, - status: MacStatus::try_from(buf[4])?, - pib_attribute: PibId::try_from(buf[5])?, - pib_attribute_value_len: buf[6], - }) - } -} +impl ParseableMacEvent for GetConfirm {} /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS @@ -118,39 +67,22 @@ pub struct GtsConfirm { pub gts_characteristics: u8, /// The status of the GTS reques pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for GtsConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - gts_characteristics: buf[0], - status: MacStatus::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for GtsConfirm {} /// MLME RESET Confirm which is used to report the results of the reset operation #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for ResetConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for ResetConfirm {} /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver @@ -158,19 +90,11 @@ impl ParseableMacEvent for ResetConfirm { pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for RxEnableConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for RxEnableConfirm {} /// MLME SCAN Confirm which is used to report the result of the channel scan request #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -195,42 +119,7 @@ pub struct ScanConfirm { pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], } -impl ParseableMacEvent for ScanConfirm { - const SIZE: usize = 185; - - fn try_parse(buf: &[u8]) -> Result { - // TODO: this is unchecked - - Self::validate(buf)?; - - let mut energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; - energy_detect_list.copy_from_slice(&buf[8..24]); - - let pan_descriptor_list = [ - PanDescriptor::try_from(&buf[24..46])?, - PanDescriptor::try_from(&buf[46..68])?, - PanDescriptor::try_from(&buf[68..90])?, - PanDescriptor::try_from(&buf[90..102])?, - PanDescriptor::try_from(&buf[102..124])?, - PanDescriptor::try_from(&buf[124..146])?, - ]; - - let mut uwb_energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; - uwb_energy_detect_list.copy_from_slice(&buf[147..163]); - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - scan_type: ScanType::try_from(buf[1])?, - channel_page: buf[2], - unscanned_channels: [buf[3], buf[4], buf[5], buf[6]], - result_list_size: buf[7], - energy_detect_list, - pan_descriptor_list, - detected_category: buf[146], - uwb_energy_detect_list, - }) - } -} +impl ParseableMacEvent for ScanConfirm {} /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -239,20 +128,11 @@ pub struct SetConfirm { pub status: MacStatus, /// The name of the PIB attribute that was written pub pin_attribute: PibId, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for SetConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - pin_attribute: PibId::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for SetConfirm {} /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration @@ -260,57 +140,33 @@ impl ParseableMacEvent for SetConfirm { pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for StartConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for StartConfirm {} /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for PollConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for PollConfirm {} /// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsConfirm { /// The status of the DPS request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for DpsConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for DpsConfirm {} /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information @@ -318,20 +174,11 @@ impl ParseableMacEvent for DpsConfirm { pub struct SoundingConfirm { /// Results of the sounding measurement sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], + + status: u8, } -impl ParseableMacEvent for SoundingConfirm { - const SIZE: usize = 1; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let mut sounding_list = [0u8; MAX_SOUNDING_LIST_SUPPORTED]; - sounding_list[..buf.len()].copy_from_slice(buf); - - Ok(Self { sounding_list }) - } -} +impl ParseableMacEvent for SoundingConfirm {} /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information @@ -339,6 +186,8 @@ impl ParseableMacEvent for SoundingConfirm { pub struct CalibrateConfirm { /// The status of the attempt to return sounding data pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], /// A count of the propagation time from the ranging counter /// to the transmit antenna pub cal_tx_rmaker_offset: u32, @@ -347,20 +196,7 @@ pub struct CalibrateConfirm { pub cal_rx_rmaker_offset: u32, } -impl ParseableMacEvent for CalibrateConfirm { - const SIZE: usize = 12; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - // 3 byte stuffing - cal_tx_rmaker_offset: u32::from_le_bytes(buf[4..8].try_into().unwrap()), - cal_rx_rmaker_offset: u32::from_le_bytes(buf[8..12].try_into().unwrap()), - }) - } -} +impl ParseableMacEvent for CalibrateConfirm {} /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application @@ -386,27 +222,11 @@ pub struct DataConfirm { pub ranging_offset: u32, /// The FoM characterizing the ranging measurement pub ranging_fom: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for DataConfirm { - const SIZE: usize = 28; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - msdu_handle: buf[0], - time_stamp: [buf[1], buf[2], buf[3], buf[4]], - ranging_received: buf[5], - status: MacStatus::try_from(buf[6])?, - ranging_counter_start: u32::from_le_bytes(buf[7..11].try_into().unwrap()), - ranging_counter_stop: u32::from_le_bytes(buf[11..15].try_into().unwrap()), - ranging_tracking_interval: u32::from_le_bytes(buf[15..19].try_into().unwrap()), - ranging_offset: u32::from_le_bytes(buf[19..23].try_into().unwrap()), - ranging_fom: buf[24], - }) - } -} +impl ParseableMacEvent for DataConfirm {} /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue @@ -416,17 +236,8 @@ pub struct PurgeConfirm { pub msdu_handle: u8, /// The status of the request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for PurgeConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - msdu_handle: buf[0], - status: MacStatus::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for PurgeConfirm {} diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index d30ed2f11..d9bf4c909 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -12,7 +12,7 @@ use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; use crate::mac::commands::MacCommand; -use crate::mac::event::MacEvent; +use crate::mac::event::Event; use crate::mac::typedefs::MacError; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; @@ -94,11 +94,8 @@ impl Mac { } } - pub async fn read(&self) -> Result { - let evt_box = self.tl_read().await; - let payload = evt_box.payload(); - - MacEvent::try_from(payload) + pub async fn read(&self) -> Event { + Event::new(self.tl_read().await) } } diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index f8c8ba288..86413ea0f 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -73,8 +73,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; @@ -85,8 +87,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting short address"); let short_address: u16 = 0x1122; @@ -97,8 +101,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting association permit"); let association_permit: bool = true; @@ -109,8 +115,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting TX power"); let transmit_power: i8 = 2; @@ -121,8 +129,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("starting FFD device"); mbox.mac_subsystem @@ -137,8 +147,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting RX on when idle"); let rx_on_while_idle: bool = true; @@ -149,14 +161,16 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } loop { let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", evt.mac_event()); - if let Ok(evt) = evt { + if let Ok(evt) = evt.mac_event() { match evt { MacEvent::MlmeAssociateInd(association) => mbox .mac_subsystem diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index b0eb91061..7cb401d89 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -75,8 +75,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; @@ -87,8 +89,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("getting extended address"); mbox.mac_subsystem @@ -98,14 +102,17 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { - if evt.pib_attribute_value_len == 8 { - let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); - info!("value {:#x}", value) + if let Ok(MacEvent::MlmeGetCnf(evt)) = evt.mac_event() { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } } } @@ -124,13 +131,15 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + let short_addr = { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); - let short_addr = if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt { - conf.assoc_short_address - } else { - defmt::panic!() + if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt.mac_event() { + conf.assoc_short_address + } else { + defmt::panic!() + } }; info!("setting short address"); @@ -141,8 +150,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("sending data"); let data = b"Hello from embassy!"; @@ -163,11 +174,13 @@ async fn main(spawner: Spawner) { ) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } loop { let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + info!("{:#x}", evt.mac_event()); } } diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index 2fc15dc9d..d64a5ef81 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -55,8 +55,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; @@ -67,8 +69,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("getting extended address"); mbox.mac_subsystem @@ -78,14 +82,16 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { - if evt.pib_attribute_value_len == 8 { - let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + if let Ok(MacEvent::MlmeGetCnf(evt)) = evt.mac_event() { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; - info!("value {:#x}", value) + info!("value {:#x}", value) + } } } @@ -104,8 +110,10 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("Test OK"); cortex_m::asm::bkpt(); From a0515ca7ac9f4aebeadede7c6a2fa312b60b5d55 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 16:16:56 -0500 Subject: [PATCH 1572/1575] wpan: add repr(c) to mac responses --- embassy-stm32-wpan/src/mac/indications.rs | 11 ++++++++++- embassy-stm32-wpan/src/mac/responses.rs | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index c7e9be84a..66819dc9d 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -7,6 +7,7 @@ use super::typedefs::{ /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateIndication { /// Extended address of the device requesting association @@ -27,6 +28,7 @@ impl ParseableMacEvent for AssociateIndication {} /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateIndication { /// Extended address of the device requesting association @@ -47,6 +49,7 @@ impl ParseableMacEvent for DisassociateIndication {} /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BeaconNotifyIndication { /// he set of octets comprising the beacon payload to be transferred @@ -67,6 +70,7 @@ pub struct BeaconNotifyIndication { impl ParseableMacEvent for BeaconNotifyIndication {} /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CommStatusIndication { /// The 16-bit PAN identifier of the device from which the frame @@ -96,6 +100,7 @@ impl ParseableMacEvent for CommStatusIndication {} /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsIndication { /// The short address of the device that has been allocated or deallocated a GTS @@ -118,6 +123,7 @@ impl ParseableMacEvent for GtsIndication {} /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OrphanIndication { /// Extended address of the orphaned device @@ -138,6 +144,7 @@ impl ParseableMacEvent for OrphanIndication {} /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncLossIndication { /// The PAN identifier with which the device lost synchronization or to which it was realigned @@ -162,6 +169,7 @@ impl ParseableMacEvent for SyncLossIndication {} /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsIndication { /// byte stuffing to keep 32 bit alignment @@ -170,8 +178,8 @@ pub struct DpsIndication { impl ParseableMacEvent for DpsIndication {} -#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, @@ -227,6 +235,7 @@ impl ParseableMacEvent for DataIndication {} /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollIndication { /// addressing mode used diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index e0376a7f5..5d203084c 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -7,6 +7,7 @@ use super::typedefs::{ /// MLME ASSOCIATE Confirm used to inform of the initiating device whether /// its request to associate was successful or unsuccessful +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateConfirm { /// short address allocated by the coordinator on successful association @@ -28,6 +29,7 @@ pub struct AssociateConfirm { impl ParseableMacEvent for AssociateConfirm {} /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateConfirm { /// status of the disassociation attempt @@ -43,6 +45,7 @@ pub struct DisassociateConfirm { impl ParseableMacEvent for DisassociateConfirm {} /// MLME GET Confirm which requests information about a given PIB attribute +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetConfirm { /// The pointer to the value of the PIB attribute attempted to read @@ -61,6 +64,7 @@ impl ParseableMacEvent for GetConfirm {} /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsConfirm { /// The characteristics of the GTS @@ -74,6 +78,7 @@ pub struct GtsConfirm { impl ParseableMacEvent for GtsConfirm {} /// MLME RESET Confirm which is used to report the results of the reset operation +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation @@ -86,6 +91,7 @@ impl ParseableMacEvent for ResetConfirm {} /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver @@ -97,6 +103,7 @@ pub struct RxEnableConfirm { impl ParseableMacEvent for RxEnableConfirm {} /// MLME SCAN Confirm which is used to report the result of the channel scan request +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanConfirm { /// Status of the scan request @@ -122,6 +129,7 @@ pub struct ScanConfirm { impl ParseableMacEvent for ScanConfirm {} /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SetConfirm { /// The result of the set operation @@ -136,6 +144,7 @@ impl ParseableMacEvent for SetConfirm {} /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration @@ -147,6 +156,7 @@ pub struct StartConfirm { impl ParseableMacEvent for StartConfirm {} /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request @@ -158,6 +168,7 @@ pub struct PollConfirm { impl ParseableMacEvent for PollConfirm {} /// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsConfirm { /// The status of the DPS request @@ -170,6 +181,7 @@ impl ParseableMacEvent for DpsConfirm {} /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingConfirm { /// Results of the sounding measurement @@ -182,6 +194,7 @@ impl ParseableMacEvent for SoundingConfirm {} /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CalibrateConfirm { /// The status of the attempt to return sounding data @@ -200,6 +213,7 @@ impl ParseableMacEvent for CalibrateConfirm {} /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataConfirm { /// The handle associated with the MSDU being confirmed @@ -230,6 +244,7 @@ impl ParseableMacEvent for DataConfirm {} /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PurgeConfirm { /// Handle associated with the MSDU requested to be purged from the transaction queue From 34217ea797c6bbea6219bb2bc2b611a99212e14b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 17:28:34 -0500 Subject: [PATCH 1573/1575] wpan: add slice data view --- embassy-stm32-wpan/src/mac/event.rs | 73 ++++++++++------------- embassy-stm32-wpan/src/mac/indications.rs | 8 +++ examples/stm32wb/src/bin/mac_ffd.rs | 22 ++++--- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 67f207d57..a2bb79222 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -36,53 +36,40 @@ impl Event { let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); let opcode = OpcodeM0ToM4::try_from(opcode)?; + let buf = &payload[2..]; match opcode { - OpcodeM0ToM4::MlmeAssociateCnf => Ok(MacEvent::MlmeAssociateCnf(AssociateConfirm::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeDisassociateCnf => Ok(MacEvent::MlmeDisassociateCnf(DisassociateConfirm::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeGetCnf => Ok(MacEvent::MlmeGetCnf(GetConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeGtsCnf => Ok(MacEvent::MlmeGtsCnf(GtsConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeResetCnf => Ok(MacEvent::MlmeResetCnf(ResetConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeRxEnableCnf => { - Ok(MacEvent::MlmeRxEnableCnf(RxEnableConfirm::from_buffer(&payload[2..])?)) + OpcodeM0ToM4::MlmeAssociateCnf => Ok(MacEvent::MlmeAssociateCnf(AssociateConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDisassociateCnf => { + Ok(MacEvent::MlmeDisassociateCnf(DisassociateConfirm::from_buffer(buf)?)) } - OpcodeM0ToM4::MlmeScanCnf => Ok(MacEvent::MlmeScanCnf(ScanConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeSetCnf => Ok(MacEvent::MlmeSetCnf(SetConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeStartCnf => Ok(MacEvent::MlmeStartCnf(StartConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmePollCnf => Ok(MacEvent::MlmePollCnf(PollConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeDpsCnf => Ok(MacEvent::MlmeDpsCnf(DpsConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeSoundingCnf => { - Ok(MacEvent::MlmeSoundingCnf(SoundingConfirm::from_buffer(&payload[2..])?)) + OpcodeM0ToM4::MlmeGetCnf => Ok(MacEvent::MlmeGetCnf(GetConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeGtsCnf => Ok(MacEvent::MlmeGtsCnf(GtsConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeResetCnf => Ok(MacEvent::MlmeResetCnf(ResetConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeRxEnableCnf => Ok(MacEvent::MlmeRxEnableCnf(RxEnableConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeScanCnf => Ok(MacEvent::MlmeScanCnf(ScanConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeSetCnf => Ok(MacEvent::MlmeSetCnf(SetConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeStartCnf => Ok(MacEvent::MlmeStartCnf(StartConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmePollCnf => Ok(MacEvent::MlmePollCnf(PollConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDpsCnf => Ok(MacEvent::MlmeDpsCnf(DpsConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeSoundingCnf => Ok(MacEvent::MlmeSoundingCnf(SoundingConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeCalibrateCnf => Ok(MacEvent::MlmeCalibrateCnf(CalibrateConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::McpsDataCnf => Ok(MacEvent::McpsDataCnf(DataConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::McpsPurgeCnf => Ok(MacEvent::McpsPurgeCnf(PurgeConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeAssociateInd => Ok(MacEvent::MlmeAssociateInd(AssociateIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDisassociateInd => { + Ok(MacEvent::MlmeDisassociateInd(DisassociateIndication::from_buffer(buf)?)) } - OpcodeM0ToM4::MlmeCalibrateCnf => Ok(MacEvent::MlmeCalibrateCnf(CalibrateConfirm::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::McpsDataCnf => Ok(MacEvent::McpsDataCnf(DataConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::McpsPurgeCnf => Ok(MacEvent::McpsPurgeCnf(PurgeConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeAssociateInd => Ok(MacEvent::MlmeAssociateInd(AssociateIndication::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeDisassociateInd => Ok(MacEvent::MlmeDisassociateInd( - DisassociateIndication::from_buffer(&payload[2..])?, - )), - OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(MacEvent::MlmeBeaconNotifyInd( - BeaconNotifyIndication::from_buffer(&payload[2..])?, - )), - OpcodeM0ToM4::MlmeCommStatusInd => Ok(MacEvent::MlmeCommStatusInd(CommStatusIndication::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeGtsInd => Ok(MacEvent::MlmeGtsInd(GtsIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeOrphanInd => Ok(MacEvent::MlmeOrphanInd(OrphanIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeSyncLossInd => Ok(MacEvent::MlmeSyncLossInd(SyncLossIndication::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeDpsInd => Ok(MacEvent::MlmeDpsInd(DpsIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::McpsDataInd => Ok(MacEvent::McpsDataInd(DataIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmePollInd => Ok(MacEvent::MlmePollInd(PollIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeBeaconNotifyInd => { + Ok(MacEvent::MlmeBeaconNotifyInd(BeaconNotifyIndication::from_buffer(buf)?)) + } + OpcodeM0ToM4::MlmeCommStatusInd => Ok(MacEvent::MlmeCommStatusInd(CommStatusIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeGtsInd => Ok(MacEvent::MlmeGtsInd(GtsIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeOrphanInd => Ok(MacEvent::MlmeOrphanInd(OrphanIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeSyncLossInd => Ok(MacEvent::MlmeSyncLossInd(SyncLossIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDpsInd => Ok(MacEvent::MlmeDpsInd(DpsIndication::from_buffer(buf)?)), + OpcodeM0ToM4::McpsDataInd => Ok(MacEvent::McpsDataInd(DataIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmePollInd => Ok(MacEvent::MlmePollInd(PollIndication::from_buffer(buf)?)), } } } diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 66819dc9d..cf795dfa0 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -1,3 +1,5 @@ +use core::slice; + use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; use super::typedefs::{ @@ -233,6 +235,12 @@ pub struct DataIndication { impl ParseableMacEvent for DataIndication {} +impl DataIndication { + pub fn payload<'a>(&'a self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.msdu_ptr as *const _ as *const u8, self.msdu_length as usize) } + } +} + /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 #[repr(C)] diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 86413ea0f..bc71e29aa 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -168,9 +168,10 @@ async fn main(spawner: Spawner) { loop { let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); - if let Ok(evt) = evt.mac_event() { + defmt::info!("parsed mac event"); + defmt::info!("{:#x}", evt); + match evt { MacEvent::MlmeAssociateInd(association) => mbox .mac_subsystem @@ -184,17 +185,22 @@ async fn main(spawner: Spawner) { .await .unwrap(), MacEvent::McpsDataInd(data_ind) => { - let data_addr = data_ind.msdu_ptr; - let mut data = [0u8; 256]; - unsafe { data_addr.copy_to(&mut data as *mut _, data_ind.msdu_length as usize) } - info!("{}", data[..data_ind.msdu_length as usize]); + let payload = data_ind.payload(); + let ref_payload = b"Hello from embassy!"; + info!("{}", payload); - if &data[..data_ind.msdu_length as usize] == b"Hello from embassy!" { + if payload == ref_payload { info!("success"); + } else { + info!("ref payload: {}", ref_payload); } } - _ => {} + _ => { + defmt::info!("other mac event"); + } } + } else { + defmt::info!("failed to parse mac event"); } } } From fe1e7c4d7660163457226316ffbf30f6f3c8ddc5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 18:07:05 -0500 Subject: [PATCH 1574/1575] wpan: fix datarequest --- embassy-stm32-wpan/src/mac/commands.rs | 2 +- embassy-stm32-wpan/src/mac/indications.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index 8acae24bb..8f6dcbbbc 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -370,7 +370,7 @@ pub struct DataRequest { impl DataRequest { pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self { - self.msdu_ptr = &buf as *const _ as *const u8; + self.msdu_ptr = buf as *const _ as *const u8; self.msdu_length = buf.len() as u8; self diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index cf795dfa0..98826e662 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -237,7 +237,7 @@ impl ParseableMacEvent for DataIndication {} impl DataIndication { pub fn payload<'a>(&'a self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.msdu_ptr as *const _ as *const u8, self.msdu_length as usize) } + unsafe { slice::from_raw_parts(self.msdu_ptr, self.msdu_length as usize) } } } From 7b34f5e866958f2ff28d7deb6888666690fe2837 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 18:54:11 -0500 Subject: [PATCH 1575/1575] wpan: make dataind fields private --- embassy-stm32-wpan/src/mac/indications.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 98826e662..5445fb4af 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -206,9 +206,9 @@ pub struct DataIndication { /// The time, in symbols, at which the data were received pub time_stamp: [u8; 4], /// The security level purportedly used by the received data frame - pub security_level: SecurityLevel, + security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: KeyIdMode, + key_id_mode: KeyIdMode, /// The originator of the key pub key_source: [u8; 8], /// The index of the key